|
|
@ -53,6 +53,7 @@ INCLUDE_DATA |
|
|
|
using A=Attribute; |
|
|
|
using A=Attribute; |
|
|
|
|
|
|
|
|
|
|
|
void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy){ |
|
|
|
void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy){ |
|
|
|
|
|
|
|
|
|
|
|
switch(m.phase){ |
|
|
|
switch(m.phase){ |
|
|
|
case 0:{ |
|
|
|
case 0:{ |
|
|
|
m.phase=ConfigInt("StartPhase"); |
|
|
|
m.phase=ConfigInt("StartPhase"); |
|
|
@ -74,7 +75,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy |
|
|
|
if(m.GetRemainingHPPct()<=ConfigFloat("Phase 2.Change")/100.f){ |
|
|
|
if(m.GetRemainingHPPct()<=ConfigFloat("Phase 2.Change")/100.f){ |
|
|
|
//before moving to Phase 2, we need to make sure we're in Phase 0 of the bear AI.
|
|
|
|
//before moving to Phase 2, we need to make sure we're in Phase 0 of the bear AI.
|
|
|
|
if(m.overlaySpriteTransparency<210U){ |
|
|
|
if(m.overlaySpriteTransparency<210U){ |
|
|
|
if(m.I(A::PHASE)!=0.f)goto bear; |
|
|
|
if(m.I(A::PHASE)!=0)goto bear; |
|
|
|
else{ |
|
|
|
else{ |
|
|
|
if(m.F(A::RUN_AWAY_TIMER)==0.f)m.F(A::RUN_AWAY_TIMER)=ConfigFloat("Phase 1.Fur Change Color Time"); |
|
|
|
if(m.F(A::RUN_AWAY_TIMER)==0.f)m.F(A::RUN_AWAY_TIMER)=ConfigFloat("Phase 1.Fur Change Color Time"); |
|
|
|
else{ |
|
|
|
else{ |
|
|
@ -226,6 +227,39 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy |
|
|
|
m.F(A::CHARGE_COOLDOWN)=std::max(0.f,m.F(A::CHARGE_COOLDOWN)-fElapsedTime); |
|
|
|
m.F(A::CHARGE_COOLDOWN)=std::max(0.f,m.F(A::CHARGE_COOLDOWN)-fElapsedTime); |
|
|
|
|
|
|
|
|
|
|
|
if(m.I(A::PHASE)!=0)goto bear2; //Prevent doing anything else if a part of bear AI is still running.
|
|
|
|
if(m.I(A::PHASE)!=0)goto bear2; //Prevent doing anything else if a part of bear AI is still running.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(m.GetRemainingHPPct()<=ConfigFloat("Phase 4.Change")/100.f){ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto TransitionToPhase5=[&](){ |
|
|
|
|
|
|
|
m.phase=5; |
|
|
|
|
|
|
|
m.PerformOtherAnimation(1); |
|
|
|
|
|
|
|
m.I(A::PHASE_REPEAT_COUNT)=ConfigInt("Phase 4.Wisp Pattern Spawn Count"); |
|
|
|
|
|
|
|
SoundEffect::PlaySFX("Ursule Phase Transition",SoundEffect::CENTERED); |
|
|
|
|
|
|
|
m.F(A::ENVIRONMENT_TIMER)=ConfigFloat("Phase 4.Environment Fade-out Time"); |
|
|
|
|
|
|
|
m.I(A::ENVIRONMENT_PHASE)=0; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//We also need to move to the center of the map.
|
|
|
|
|
|
|
|
if(m.F(A::RUN_AWAY_TIMER)==0.f)m.F(A::RUN_AWAY_TIMER)=ConfigFloat("Phase 4.Run to Center Time"); |
|
|
|
|
|
|
|
else{ |
|
|
|
|
|
|
|
m.F(A::RUN_AWAY_TIMER)=std::max(0.f,m.F(A::RUN_AWAY_TIMER)-fElapsedTime); |
|
|
|
|
|
|
|
if(m.F(A::RUN_AWAY_TIMER)==0.f){ |
|
|
|
|
|
|
|
TransitionToPhase5(); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
vf2d mapCenter=game->GetCurrentMap().MapData.MapSize*vf2d{float(game->GetCurrentMap().MapData.tilewidth),float(game->GetCurrentMap().MapData.tileheight)}/2.0f; |
|
|
|
|
|
|
|
float distToCenter=geom2d::line<float>(m.GetPos(),mapCenter).length(); |
|
|
|
|
|
|
|
if(distToCenter>4.0f){ |
|
|
|
|
|
|
|
m.targetAcquireTimer=20.f; |
|
|
|
|
|
|
|
m.target=mapCenter; |
|
|
|
|
|
|
|
RUN_TOWARDS(m,fElapsedTime,"Run Towards"); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
}else{ //Now we're finally good for phase 2.
|
|
|
|
|
|
|
|
TransitionToPhase5(); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(distToPlayer>=ConfigFloat("Phase 3.Charge Range")/100.f*24.f&&m.F(A::CHARGE_COOLDOWN)==0.f){ |
|
|
|
if(distToPlayer>=ConfigFloat("Phase 3.Charge Range")/100.f*24.f&&m.F(A::CHARGE_COOLDOWN)==0.f){ |
|
|
|
if(ConfigFloat("Phase 3.Charge Cast Time")!=0.f){ |
|
|
|
if(ConfigFloat("Phase 3.Charge Cast Time")!=0.f){ |
|
|
@ -250,6 +284,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy |
|
|
|
BEAR(m,fElapsedTime,"Bear"); |
|
|
|
BEAR(m,fElapsedTime,"Bear"); |
|
|
|
}break; |
|
|
|
}break; |
|
|
|
case 4:{ //A charging phase.
|
|
|
|
case 4:{ //A charging phase.
|
|
|
|
|
|
|
|
|
|
|
|
if(geom2d::line(m.pos,m.target).length()>100*fElapsedTime*m.GetMoveSpdMult()){ |
|
|
|
if(geom2d::line(m.pos,m.target).length()>100*fElapsedTime*m.GetMoveSpdMult()){ |
|
|
|
vf2d newPos=m.pos+geom2d::line(m.pos,m.target).vector().norm()*100*fElapsedTime*m.GetMoveSpdMult(); |
|
|
|
vf2d newPos=m.pos+geom2d::line(m.pos,m.target).vector().norm()*100*fElapsedTime*m.GetMoveSpdMult(); |
|
|
|
m.SetPos(newPos); |
|
|
|
m.SetPos(newPos); |
|
|
@ -265,6 +300,66 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy |
|
|
|
m.PerformIdleAnimation(); |
|
|
|
m.PerformIdleAnimation(); |
|
|
|
} |
|
|
|
} |
|
|
|
}break; |
|
|
|
}break; |
|
|
|
|
|
|
|
case 5:{ //Final boss phase.
|
|
|
|
|
|
|
|
#pragma region Environment Color Change Handling |
|
|
|
|
|
|
|
m.F(A::ENVIRONMENT_TIMER)=std::max(0.f,m.F(A::ENVIRONMENT_TIMER)-fElapsedTime); |
|
|
|
|
|
|
|
switch(m.I(A::ENVIRONMENT_PHASE)){ |
|
|
|
|
|
|
|
case 0:{ //Fade out. Use the phase 3 environment fade-in color as the previous color to lerp out from.
|
|
|
|
|
|
|
|
game->SetWorldColor(ConfigPixel("Phase 3.Environment Fade-In Color")*util::lerp(0.f,1.0f,m.F(A::ENVIRONMENT_TIMER)/ConfigFloat("Phase 4.Environment Fade-out Time"))); |
|
|
|
|
|
|
|
if(m.F(A::ENVIRONMENT_TIMER)==0.f){ |
|
|
|
|
|
|
|
game->SetWorldColor({0,0,0,255}); |
|
|
|
|
|
|
|
m.F(A::ENVIRONMENT_TIMER)=ConfigFloat("Phase 4.Environment Fade-in Time"); |
|
|
|
|
|
|
|
m.I(A::ENVIRONMENT_PHASE)++; |
|
|
|
|
|
|
|
game->SetWorldColorFunc([&](vi2d pos){ |
|
|
|
|
|
|
|
float fadeInRange=DATA["MonsterStrategy"]["Ursule"]["Phase 4"]["Environment Fade-In Range"].GetReal()/100.f*24.f; |
|
|
|
|
|
|
|
float distToPlayer=geom2d::line<float>(game->GetPlayer()->GetPos(),pos).length(); |
|
|
|
|
|
|
|
return game->GetWorldColor()*std::min(1.0f,fadeInRange/distToPlayer); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}break; |
|
|
|
|
|
|
|
case 1:{ //Fade in.
|
|
|
|
|
|
|
|
Pixel fadeInCol=ConfigPixel("Phase 4.Environment Fade-In Color"); |
|
|
|
|
|
|
|
game->SetWorldColor(fadeInCol*util::lerp(1.f,0.f,m.F(A::ENVIRONMENT_TIMER)/ConfigFloat("Phase 4.Environment Fade-in Time"))); |
|
|
|
|
|
|
|
if(m.F(A::ENVIRONMENT_TIMER)==0.f){ |
|
|
|
|
|
|
|
game->SetWorldColor(fadeInCol); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#pragma endregion |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(m.F(A::SHOOT_TIMER)==0.f){ |
|
|
|
|
|
|
|
uint8_t wispPatternCount=ConfigInt("Wisp Pattern Count"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t wispPattern=util::random()%wispPatternCount; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(ConfigString("Phase 4.Wisp Pattern Random Selection")=="Bag"){ |
|
|
|
|
|
|
|
if(m.VEC(A::WISP_PATTERN_LIST).size()==0){ |
|
|
|
|
|
|
|
for(uint8_t numb=0;numb<wispPatternCount;numb++){ //Numbers are randomly inserted into the list.
|
|
|
|
|
|
|
|
m.VEC(A::WISP_PATTERN_LIST).insert(m.VEC(A::WISP_PATTERN_LIST).begin()+(util::random()%(m.VEC(A::WISP_PATTERN_LIST).size()+1)),numb); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
wispPattern=std::any_cast<uint8_t>(m.VEC(A::WISP_PATTERN_LIST).back()); |
|
|
|
|
|
|
|
m.VEC(A::WISP_PATTERN_LIST).pop_back(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m.F(A::SHOOT_TIMER)=ConfigFloat("Phase 4.Wisp Pattern Spawn Wait Time"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vi2d wispSize={ConfigIntArr("Phase 4.Wisp Size",0),ConfigIntArr("Phase 4.Wisp Size",1)}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float rowWidth=ConfigString(std::format("Wisp Pattern {}.Row[0]",wispPattern)).length()*wispSize.x; // Width of a wisp set in pixels.
|
|
|
|
|
|
|
|
float mapWidth=game->GetCurrentMap().MapData.MapSize.x*float(game->GetCurrentMap().MapData.tilewidth); |
|
|
|
|
|
|
|
int rowCount=Config(std::format("Wisp Pattern {}",wispPattern)).GetKeys().size(); |
|
|
|
|
|
|
|
for(float x=0;x<mapWidth;x+=wispSize.x){ |
|
|
|
|
|
|
|
for(int y=0;y<rowCount;y++){ |
|
|
|
|
|
|
|
std::string_view row=ConfigString(std::format("Wisp Pattern {}.Row[{}]",wispPattern,y)); |
|
|
|
|
|
|
|
if(row[int(x/wispSize.x)%row.length()]!='.'){ |
|
|
|
|
|
|
|
float ySpawn=ConfigInt("Phase 4.Wisp Pattern Spawn Y")+y*wispSize.y; |
|
|
|
|
|
|
|
BULLET_LIST.push_back(std::make_unique<Wisp>(vf2d{x,ySpawn},vf2d{0,ConfigFloat("Phase 4.Wisp Speed")},wispSize.x/3.f,m.GetAttack(),m.OnUpperLevel(),false,ConfigPixel("Phase 4.Wisp Color"))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}break; |
|
|
|
default:{ |
|
|
|
default:{ |
|
|
|
ERR(std::format("WARNING! Unknown phase {} for {} reached!",m.phase,m.GetName())); |
|
|
|
ERR(std::format("WARNING! Unknown phase {} for {} reached!",m.phase,m.GetName())); |
|
|
|
} |
|
|
|
} |
|
|
|