# pragma region License
/*
License ( OLC - 3 )
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
Copyright 2024 Joshua Sigona < sigonasr2 @ gmail . com >
Redistribution and use in source and binary forms , with or without modification ,
are permitted provided that the following conditions are met :
1. Redistributions or derivations of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice . This list of conditions and the following disclaimer must be
reproduced in the documentation and / or other materials provided with the distribution .
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS " AND ANY
EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT ,
INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED
TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR
BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE .
Portions of this software are copyright <EFBFBD> 2024 The FreeType
Project ( www . freetype . org ) . Please see LICENSE_FT . txt for more information .
All rights reserved .
*/
# pragma endregion
# include "AdventuresInLestoria.h"
# include "DEFINES.h"
# include "Monster.h"
# include "MonsterStrategyHelpers.h"
# include "BulletTypes.h"
# include "util.h"
INCLUDE_game
INCLUDE_MONSTER_DATA
using A = Attribute ;
void Monster : : STRATEGY : : ZEPHY ( Monster & m , float fElapsedTime , std : : string strategy ) {
enum Phase {
INITIALIZE ,
IDLE ,
FLY_ACROSS_PREPARE ,
FLY_ACROSS ,
TORNADO_ATTACK_PREPARE ,
TORNADO_ATTACK ,
WIND_ATTACK ,
HALFHEALTH_PHASE ,
} ;
enum class AttackChoice {
RIGHT ,
LEFT
} ;
if ( m . phase ! = HALFHEALTH_PHASE ) m . F ( A : : SPAWNER_TIMER ) - = fElapsedTime ;
if ( m . F ( A : : SPAWNER_TIMER ) < = 0.f ) {
const float randomDir = util : : random ( 2 * PI ) ;
game - > SpawnMonster ( m . GetPos ( ) + vf2d { ConfigFloat ( " Basic Hawk Spawn Radius " ) , randomDir } . cart ( ) , MONSTER_DATA . at ( " Hawk_NOXP " ) , m . OnUpperLevel ( ) ) ;
m . F ( A : : SPAWNER_TIMER ) = ConfigFloat ( " Basic Hawk Spawn Time " ) ;
}
switch ( m . phase ) {
case INITIALIZE : {
m . F ( A : : SPAWNER_TIMER ) = ConfigFloat ( " Basic Hawk Spawn Time " ) ;
m . phase = IDLE ;
} break ;
case IDLE : {
const int randomAttackChoice = util : : random ( ) % 2 ;
switch ( randomAttackChoice ) {
case 0 : {
m . I ( A : : ATTACK_CHOICE ) = util : : random ( ) % 2 ;
const bool RightDirectionChosen = m . I ( A : : ATTACK_CHOICE ) = = int ( AttackChoice : : RIGHT ) ;
if ( RightDirectionChosen ) m . target = ConfigVec ( " Fly Across Attack.Right Edge Start Pos " ) ;
else m . target = ConfigVec ( " Fly Across Attack.Left Edge Start Pos " ) ;
m . phase = FLY_ACROSS_PREPARE ;
} break ;
case 1 : {
m . phase = TORNADO_ATTACK_PREPARE ;
m . target = ConfigVec ( " Tornado Attack.Arena Center " ) ;
} break ;
case 2 : {
m . phase = WIND_ATTACK ;
} break ;
case 3 : {
m . phase = HALFHEALTH_PHASE ;
} break ;
}
} break ;
case FLY_ACROSS_PREPARE : {
m . targetAcquireTimer = 20.f ;
RUN_TOWARDS ( m , fElapsedTime , " Run Towards " ) ;
if ( m . ReachedTargetPos ( ) ) {
const bool RightDirectionChosen = m . I ( A : : ATTACK_CHOICE ) = = int ( AttackChoice : : RIGHT ) ;
//We're choosing the opposite side of the field to direct the boss towards for this attack.
if ( RightDirectionChosen ) m . target = ConfigVec ( " Fly Across Attack.Left Edge Start Pos " ) ;
else m . target = ConfigVec ( " Fly Across Attack.Right Edge Start Pos " ) ;
m . phase = FLY_ACROSS ;
m . AddBuff ( BuffType : : SPEEDBOOST , INFINITY , ConfigFloat ( " Fly Across Attack.Move Speed Multiplier " ) - 1.f ) ;
}
} break ;
case FLY_ACROSS : {
m . targetAcquireTimer = 20.f ;
RUN_TOWARDS ( m , fElapsedTime , " Run Towards " ) ;
m . F ( A : : SHOOT_TIMER ) - = fElapsedTime ;
if ( m . F ( A : : SHOOT_TIMER ) < = 0.f ) {
CreateBullet ( Bullet ) ( m . GetPos ( ) , vf2d { 0.f , ConfigFloat ( " Fly Across Attack.Attack Y Speed " ) } , 4 , ConfigInt ( " Fly Across Attack.Poop Damage " ) , " birdpoop.png " , m . OnUpperLevel ( ) , false , INFINITY , false , false , WHITE , vf2d { 1.f , 1.25f } )
. SetIframeTimeOnHit ( 0.25f ) EndBullet ;
const int extraPoopBitsCount = util : : random ( ) % 6 ;
for ( int i = 0 ; i < extraPoopBitsCount ; i + + ) {
const bool RightDirection = util : : random ( ) % 2 ;
float xOffset { 0.f } ;
if ( RightDirection ) xOffset = 1.f ;
else xOffset = - 1.f ;
xOffset * = 9.f ;
CreateBullet ( Bullet ) ( m . GetPos ( ) + vf2d { xOffset + util : : random_range ( - 3.f , 3.f ) , - util : : random ( 10.f ) - 4.f } , vf2d { 0.f , ConfigFloat ( " Fly Across Attack.Attack Y Speed " ) } , 1 , ConfigInt ( " Fly Across Attack.Poop Damage " ) , " birdpoop.png " , m . OnUpperLevel ( ) , false , INFINITY , false , false , WHITE , vf2d { util : : random_range ( 0.2f , 0.3f ) , util : : random_range ( 0.2f , 0.3f ) } , util : : random ( 2 * PI ) )
. SetIframeTimeOnHit ( 0.25f ) EndBullet ;
}
m . F ( A : : SHOOT_TIMER ) = ConfigFloat ( " Fly Across Attack.Landing Area " ) ;
}
if ( m . ReachedTargetPos ( ) ) {
m . phase = IDLE ;
m . RemoveBuff ( BuffType : : SPEEDBOOST ) ;
m . targetAcquireTimer = 0.f ;
}
} break ;
case TORNADO_ATTACK_PREPARE : {
m . targetAcquireTimer = 20.f ;
RUN_TOWARDS ( m , fElapsedTime , " Run Towards " ) ;
if ( m . ReachedTargetPos ( ) ) {
m . phase = TORNADO_ATTACK ;
m . PerformAnimation ( " ATTACK " , Direction : : SOUTH ) ;
m . targetAcquireTimer = 0.f ;
m . F ( A : : CASTING_TIMER ) = ConfigFloat ( " Tornado Attack.Attack Duration " ) ;
const int tornadoRingCount = Config ( " Tornado Attack.Tornados " ) . GetKeys ( ) . size ( ) ;
for ( int tornadoRingId = 1 ; tornadoRingId < = tornadoRingCount ; tornadoRingId + + ) {
//From strategy documentation:
//# For each Ring: Distance from Boss in Units, # of tornados, Rotation Speed (degrees/sec).
const float tornadoDistance = ConfigPixelsArr ( std : : format ( " Tornado Attack.Tornados.Ring{} " , tornadoRingId ) , 0 ) ;
const int tornadoCount = ConfigIntArr ( std : : format ( " Tornado Attack.Tornados.Ring{} " , tornadoRingId ) , 1 ) ;
const float tornadoRotSpd = util : : degToRad ( ConfigFloatArr ( std : : format ( " Tornado Attack.Tornados.Ring{} " , tornadoRingId ) , 2 ) ) ; //It's in degrees, let's convert it to radians now.
const float randomRotDir = util : : random ( 2 * PI ) ;
CreateBullet ( Tornado ) ( m . GetPos ( ) , tornadoDistance , randomRotDir , tornadoRotSpd , m . GetAttack ( ) , ConfigFloat ( " Tornado Attack.Knockup Duration " ) , ConfigFloat ( " Tornado Attack.Knockback Amount " ) , ConfigFloat ( " Tornado Attack.Attack Duration " ) , m . OnUpperLevel ( ) , false , WHITE ) EndBullet ;
BULLET_LIST . back ( ) - > SetFadeinTime ( ConfigFloat ( " Tornado Fade-In Time " ) ) ;
}
}
} break ;
case TORNADO_ATTACK : {
} break ;
case WIND_ATTACK : {
} break ;
case HALFHEALTH_PHASE : {
} break ;
}
}