Added final phase behaviors and actions to Pirate Captain Boss. Release Build 12251.
All checks were successful
Emscripten Build / Build_and_Deploy_Web_Build (push) Successful in 6m43s

This commit is contained in:
sigonasr2 2025-08-05 13:14:08 -05:00
parent e780d2b48e
commit f5c80b20d5
6 changed files with 67 additions and 6 deletions

View File

@ -56,6 +56,7 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std
GHOSTSABER_SLASH=999,
TOSS_COIN,
HIDING,
FINAL, //The final phase of the fight.
};
enum CannonShotType{
@ -248,16 +249,21 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std
m.SetPos(m.V(A::HIDING_POS));
m.SetVelocity({});
m.arrowIndicator=false; //While the boss is hiding, the indicator will not show up.
m.SetStrategyOnHitFunction([&m,&OnLastCursePhase](const HurtDamageInfo damageData,Monster&monster,const StrategyName&strategyName)->void{
m.SetPhase(strategyName,NORMAL);
m.arrowIndicator=true;
m.SetStrategyOnHitFunction({});
m.SetStrategyOnHitFunction([&OnLastCursePhase,&m,&strategy](const HurtDamageInfo damageData,Monster&monster,const StrategyName&strategyName)->void{
monster.SetPhase(strategyName,OnLastCursePhase?FINAL:NORMAL);
monster.arrowIndicator=true;
monster.SetStrategyOnHitFunction({});
if(OnLastCursePhase){
const std::string_view PIRATES_TREASURE{"Pirate's Treasure"};
for(std::shared_ptr<Monster>&m:MONSTER_LIST
|std::views::filter([&PIRATES_TREASURE](std::shared_ptr<Monster>&m){return m->GetStrategyName()==PIRATES_TREASURE;})){
m->SetPhase("Pirate's Treasure",1); //HARDCODED to the LOCKED phase for the Pirate's Treasure
}
for(int i:std::ranges::iota_view(0,ConfigInt("Final Ghost Saber Count"))){
const float playerToMonsterAngle{util::pointTo(game->GetPlayer()->GetPos(),m.GetPos()).polar().y};
const int rotationMult=((i%2==0)?1:-1);
CreateBullet(GhostSaber)(m.GetPos(),m.GetWeakPointer(),ConfigFloat("Ghost Saber Lifetime"),ConfigFloat("Ghost Saber Distance")+i*ConfigFloat("Final Ghost Saber Separation Amount"),ConfigFloat("Ghost Saber Knockback Amt"),playerToMonsterAngle,ConfigFloat("Ghost Saber Radius"),0.f,ConfigInt("Ghost Saber Damage"),m.OnUpperLevel(),util::degToRad(ConfigFloat("Ghost Saber Rotation Spd")*rotationMult))EndBullet;
}
}
});
SETPHASE(HIDING);
@ -306,5 +312,51 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std
m.F(A::SHRAPNEL_CANNON_TIMER)=0.f;
}
}break;
case FINAL:{
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
m.target=ConfigVec("Final Standing Spot");
m.PerformAnimation("SLASH",Direction::SOUTH);
m.F(A::CANNON_TIMER)+=fElapsedTime;
if(m.F(A::CANNON_TIMER)>=ConfigFloat("Final Cannon Timer")){
switch(m.I(A::CANNON_SHOT_TYPE)){
case BOMBARDMENT:{
const float randomAng{util::random_range(0,2*PI)};
const float range{util::random_range(0,ConfigPixels("Bombardment Max Distance"))};
const vf2d targetPos{game->GetPlayer()->GetPos()+vf2d{range,randomAng}.cart()};
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
}break;
case PRECISE_BOMBARDMENT:{
const float randomAng{util::random_range(0,2*PI)};
const float range{util::random_range(0,ConfigPixels("Precise Bombardment Max Distance"))};
const vf2d targetPos{game->GetPlayer()->GetPos()+vf2d{range,randomAng}.cart()};
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
}break;
case LINE:{
//Draw a line from one side of the screen to the other, drawing through the middle.
if(m.I(A::CANNON_SHOT_COUNT)==0)m.F(A::LINE_SHOT_ANG)=util::random_range(0,2*PI);
const vf2d targetPos{geom2d::line{game->GetPlayer()->GetPos()+vf2d{float(game->ScreenHeight()),m.F(A::LINE_SHOT_ANG)}.cart(),game->GetPlayer()->GetPos()+vf2d{float(game->ScreenHeight()),m.F(A::LINE_SHOT_ANG)+PI}.cart()}.upoint(float(m.I(A::CANNON_SHOT_COUNT))/TOTAL_CANNON_SHOTS)};
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
}break;
case SHARPSHOOTER:{
if(m.I(A::CANNON_SHOT_COUNT)%2==0)CreateBullet(FallingBullet)("cannonball.png",game->GetPlayer()->GetPos(),ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
}break;
case PREDICTION:{
LOG(std::format("Previous Pos: {} Current: {}",game->GetPlayer()->GetPreviousPos().str(),game->GetPlayer()->GetPos().str()));
const float angle{util::angleTo(game->GetPlayer()->GetPreviousPos(),game->GetPlayer()->GetPos())};
const float range{util::random_range(0,100.f*game->GetPlayer()->GetMoveSpdMult())*ConfigFloat("Cannon Shot Impact Time")};
LOG(std::format("Range/Angle: {}",vf2d{range,angle}.str()));
const vf2d targetPos{game->GetPlayer()->GetPos()+vf2d{range,angle}.cart()};
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
}break;
}
AdvanceCannonPhase();
m.I(A::CANNON_SHOT_COUNT)++;
}
if(m.F(A::SHRAPNEL_CANNON_TIMER)>=ConfigFloat("Final Cannon Timer")){
m.I(A::SHRAPNEL_SHOT_COUNT)=ConfigInt("Shrapnel Shot Bullet Count");
m.F(A::SHRAPNEL_SHOT_FALL_TIMER)=ConfigFloat("Shrapnel Shot Bullet Separation");
m.F(A::SHRAPNEL_CANNON_TIMER)=0.f+util::random(0.5f); // A little randomness to offset the two timers if they happen to occur at once.
}
}break;
}
}

View File

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 3
#define VERSION_PATCH 0
#define VERSION_BUILD 12250
#define VERSION_BUILD 12251
#define stringify(a) stringify_(a)
#define stringify_(a) #a

View File

@ -1360,6 +1360,15 @@ MonsterStrategy
Coin Toss Rise Amount = 72px
Shrapnel Hiding Shot Delay = 5.0s
# FINAL PHASE SETTINGS
# The coordinates on the map indicating where the boss stands for the final portion of the fight.
Final Standing Spot = 888,1104
Final Ghost Saber Count = 10
# Amount of separation between each ghost saber
Final Ghost Saber Separation Amount = 24px
# How often the cannons fire during the final phase.
Final Cannon Timer = 5s
}
Pirate's Treasure
{

View File

@ -1850,7 +1850,7 @@ Monsters
SLASHING = 4, 0.1, OneShot
DEATH = 4, 0.25, OneShot
STABBING = 3, 0.1, OneShot
SLASH = 1, 0.1, OneShot
SLASH = 2, 0.1, OneShot
STAB = 1, 0.1, OneShot
SHOOTING = 3, 0.2, OneShot
SHOOT = 1, 0.1, OneShot