@ -55,6 +55,7 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std
AFTERIMAGE_FADEIN ,
GHOSTSABER_SLASH = 999 ,
TOSS_COIN ,
HIDING ,
} ;
enum CannonShotType {
@ -67,6 +68,7 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std
static const uint8_t PHASE_COUNT { uint8_t ( DATA . GetProperty ( " MonsterStrategy.Ghost of Pirate Captain.Cannon Cycle " ) . GetValueCount ( ) ) } ;
static uint8_t TOTAL_CANNON_SHOTS { 0 } ;
const bool IsHiding { m . V ( A : : HIDING_POS ) ! = vf2d { } } ;
const auto AdvanceCannonPhase { [ & m , & strategy ] ( ) {
m . GetFloat ( A : : CANNON_TIMER ) = 0.f ;
@ -99,29 +101,6 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std
}
}
m . F ( A : : GHOST_SABER_TIMER ) - = fElapsedTime ;
m . F ( A : : LAST_COLLISION_TIMER ) - = fElapsedTime ;
if ( m . B ( A : : FIRST_WAVE_COMPLETE ) ) {
if ( m . F ( A : : LAST_COLLISION_TIMER ) < = 0.f ) {
m . UpdateFacingDirection ( m . GetFacingDirectionToTarget ( game - > GetPlayer ( ) - > GetPos ( ) ) ) ;
m . SetVelocity ( util : : pointTo ( m . GetPos ( ) , game - > GetPlayer ( ) - > GetPos ( ) ) * 100.f * m . GetMoveSpdMult ( ) ) ;
const float distToPlayer { util : : distance ( m . GetPos ( ) , game - > GetPlayer ( ) - > GetPos ( ) ) } ;
if ( m . F ( A : : GHOST_SABER_TIMER ) < = 0.f & & distToPlayer < = ConfigPixels ( " Ghost Saber Activation Range " ) ) {
m . F ( A : : GHOST_SABER_TIMER ) = ConfigFloat ( " Ghost Saber Cooldown " ) ;
const float playerToMonsterAngle { util : : pointTo ( game - > GetPlayer ( ) - > GetPos ( ) , m . GetPos ( ) ) . polar ( ) . y } ;
CreateBullet ( GhostSaber ) ( m . GetPos ( ) , m . GetWeakPointer ( ) , ConfigFloat ( " Ghost Saber Lifetime " ) , ConfigFloat ( " Ghost Saber Distance " ) , ConfigFloat ( " Ghost Saber Knockback Amt " ) , playerToMonsterAngle , ConfigFloat ( " Ghost Saber Radius " ) , ConfigFloat ( " Ghost Saber Expand Spd " ) , ConfigInt ( " Ghost Saber Damage " ) , m . OnUpperLevel ( ) , util : : degToRad ( ConfigFloat ( " Ghost Saber Rotation Spd " ) ) ) EndBullet ;
}
}
if ( m . B ( A : : COLLIDED_WITH_PLAYER ) ) {
m . PerformAnimation ( " SLASHING " ) ;
m . F ( A : : GHOST_SABER_SLASH_ANIMATION_TIMER ) = m . GetCurrentAnimation ( ) . GetTotalAnimationDuration ( ) ;
m . F ( A : : LAST_COLLISION_TIMER ) = ConfigFloat ( " Collision Recovery Time " ) ;
m . I ( A : : PREVIOUS_PHASE ) = PHASE ( ) ;
SETPHASE ( GHOSTSABER_SLASH ) ;
}
}
switch ( PHASE ( ) ) {
enum CannonPhaseType {
CANNON_SHOT ,
@ -147,6 +126,7 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std
} break ;
case NORMAL : {
m . F ( A : : CANNON_TIMER ) + = fElapsedTime ;
m . F ( A : : SHRAPNEL_CANNON_TIMER ) + = fElapsedTime ;
const int phase { std : : any_cast < int > ( m . VEC ( A : : CANNON_PHASES ) [ m . I ( A : : CANNON_PHASE ) ] ) } ;
switch ( phase ) {
case CANNON_SHOT : { //Normal Cannon Shot. Takes on one of five varieties.
@ -204,8 +184,37 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std
m . F ( A : : TOSS_COIN_WAIT_TIMER ) = ConfigFloat ( " Coin Toss Pause Time " ) ;
m . V ( A : : TOSS_COIN_TARGET ) = game - > GetPlayer ( ) - > GetPos ( ) ;
game - > AddEffect ( std : : make_unique < FlipCoinEffect > ( Oscillator < vf2d > { m . GetPos ( ) , m . V ( A : : TOSS_COIN_TARGET ) , 1.f / m . F ( A : : TOSS_COIN_WAIT_TIMER ) / 2.f } , ConfigFloat ( " Coin Toss Rise Amount " ) , m . F ( A : : TOSS_COIN_WAIT_TIMER ) , " coin.png " , m . OnUpperLevel ( ) , 3.f ) ) ;
# pragma region Determine a hiding spot
const auto & hidingSpots { game - > GetZones ( ) . at ( " Hiding Spot " ) } ;
if ( hidingSpots . size ( ) = = 0 ) ERR ( " WARNING! Could not find any zones with the name \" Hiding Spot \" on the map!! THIS SHOULD NOT BE HAPPENING! " )
m . V ( A : : HIDING_POS ) = hidingSpots [ util : : random ( ) % hidingSpots . size ( ) ] . zone . middle ( ) ;
# pragma endregion
SETPHASE ( TOSS_COIN ) ;
}
m . F ( A : : GHOST_SABER_TIMER ) - = fElapsedTime ;
m . F ( A : : LAST_COLLISION_TIMER ) - = fElapsedTime ;
if ( m . B ( A : : FIRST_WAVE_COMPLETE ) ) {
if ( m . F ( A : : LAST_COLLISION_TIMER ) < = 0.f ) {
m . UpdateFacingDirection ( m . GetFacingDirectionToTarget ( game - > GetPlayer ( ) - > GetPos ( ) ) ) ;
m . SetVelocity ( util : : pointTo ( m . GetPos ( ) , game - > GetPlayer ( ) - > GetPos ( ) ) * 100.f * m . GetMoveSpdMult ( ) ) ;
const float distToPlayer { util : : distance ( m . GetPos ( ) , game - > GetPlayer ( ) - > GetPos ( ) ) } ;
if ( m . F ( A : : GHOST_SABER_TIMER ) < = 0.f & & distToPlayer < = ConfigPixels ( " Ghost Saber Activation Range " ) ) {
m . F ( A : : GHOST_SABER_TIMER ) = ConfigFloat ( " Ghost Saber Cooldown " ) ;
const float playerToMonsterAngle { util : : pointTo ( game - > GetPlayer ( ) - > GetPos ( ) , m . GetPos ( ) ) . polar ( ) . y } ;
CreateBullet ( GhostSaber ) ( m . GetPos ( ) , m . GetWeakPointer ( ) , ConfigFloat ( " Ghost Saber Lifetime " ) , ConfigFloat ( " Ghost Saber Distance " ) , ConfigFloat ( " Ghost Saber Knockback Amt " ) , playerToMonsterAngle , ConfigFloat ( " Ghost Saber Radius " ) , ConfigFloat ( " Ghost Saber Expand Spd " ) , ConfigInt ( " Ghost Saber Damage " ) , m . OnUpperLevel ( ) , util : : degToRad ( ConfigFloat ( " Ghost Saber Rotation Spd " ) ) ) EndBullet ;
}
}
if ( m . B ( A : : COLLIDED_WITH_PLAYER ) ) {
m . PerformAnimation ( " SLASHING " ) ;
m . F ( A : : GHOST_SABER_SLASH_ANIMATION_TIMER ) = m . GetCurrentAnimation ( ) . GetTotalAnimationDuration ( ) ;
m . F ( A : : LAST_COLLISION_TIMER ) = ConfigFloat ( " Collision Recovery Time " ) ;
m . I ( A : : PREVIOUS_PHASE ) = PHASE ( ) ;
SETPHASE ( GHOSTSABER_SLASH ) ;
}
}
} break ;
case AFTERIMAGE_FADEIN : {
m . F ( A : : CASTING_TIMER ) - = fElapsedTime ;
@ -229,7 +238,59 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std
attachedTarget - > AddBuff ( BuffType : : PIRATE_GHOST_CAPTAIN_CURSE_DOT , BuffRestorationType : : OVER_TIME , BuffOverTimeType : : HP_PCT_DAMAGE_OVER_TIME , INFINITY , curseDmgPctOverTime , 1.f ) ;
} ) ;
game - > SpawnMonster ( m . V ( A : : TOSS_COIN_TARGET ) , MONSTER_DATA [ " Pirate's Coin " ] , m . OnUpperLevel ( ) ) ;
SETPHASE ( NORMAL ) ;
m . SetupAfterImage ( ) ;
m . afterImagePos = m . GetPos ( ) ;
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 ] ( const HurtDamageInfo damageData , Monster & monster , const StrategyName & strategyName ) - > void {
m . SetPhase ( strategyName , NORMAL ) ;
m . arrowIndicator = true ;
} ) ;
SETPHASE ( HIDING ) ;
}
} break ;
case HIDING : {
m . F ( A : : CANNON_TIMER ) + = fElapsedTime ;
if ( m . F ( A : : CANNON_TIMER ) > = ConfigFloat ( " Cannon Shot Delay " ) ) {
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 ( " Shrapnel Hiding Shot Delay " ) ) {
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 ;
}
} break ;
}