// AI_monster_boss_guardian_seeker.cpp // Native state-machine replacement for ai_monster_boss_guardian_seeker.script. #include "precompiled.h" #pragma hdrstop #include "../Game_local.h" rvmMonsterBossGuardianSeeker::rvmMonsterBossGuardianSeeker() { painNextTime = 0.0f; nextEnemyCheck = 0.0f; getCloserEndTime = 0.0f; retreatEndTime = 0.0f; guardian = NULL; } void rvmMonsterBossGuardianSeeker::Init( idAI* aiOwner ) { rvmSeekerBase::Init( aiOwner ); SetMoveType( MOVETYPE_FLY ); SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 ); SetState( "state_Inactive" ); } stateResult_t rvmMonsterBossGuardianSeeker::RunStateByName( const char* stateName, stateParms_t* parms ) { AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle ); AI_DISPATCH_STATE( "Torso_Fly", Torso_Fly ); AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain ); AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack ); AI_DISPATCH_STATE( "Torso_Death", Torso_Death ); AI_DISPATCH_STATE( "see_enemy", see_enemy ); AI_DISPATCH_STATE( "lost_enemy", lost_enemy ); AI_DISPATCH_STATE( "respawnSeeker", respawnSeeker ); AI_DISPATCH_STATE( "state_Respawn", state_Respawn ); AI_DISPATCH_STATE( "state_Inactive", state_Inactive ); AI_DISPATCH_STATE( "state_Idle", state_Idle ); AI_DISPATCH_STATE( "state_Killed", state_Killed ); AI_DISPATCH_STATE( "state_WatchPlayer", state_WatchPlayer ); AI_DISPATCH_STATE( "state_GetCloser", state_GetCloser ); AI_DISPATCH_STATE( "state_FindPlayer", state_FindPlayer ); AI_DISPATCH_STATE( "state_Retreat", state_Retreat ); AI_DISPATCH_STATE( "state_RunAway", state_RunAway ); return rvmSeekerBase::RunStateByName( stateName, parms ); } stateResult_t rvmMonsterBossGuardianSeeker::Torso_Idle( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; } if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; } if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Fly", 4 ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardianSeeker::Torso_Fly( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "fly" ); parms->stage = 1; } if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; } if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardianSeeker::Torso_Pain( stateParms_t* parms ) { enum { STAGE_START, STAGE_RUNNING }; switch ( parms->stage ) { case STAGE_START: PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() ); painNextTime = DelayTime( 0.25f ); parms->stage = STAGE_RUNNING; return SRESULT_WAIT; case STAGE_RUNNING: if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; } if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) { FinishAction( "pain" ); SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 ); return SRESULT_DONE; } return SRESULT_WAIT; } return SRESULT_ERROR; } stateResult_t rvmMonsterBossGuardianSeeker::Torso_MeleeAttack( stateParms_t* parms ) { enum { STAGE_START, STAGE_RUNNING }; switch ( parms->stage ) { case STAGE_START: FaceEnemy(); PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" ); parms->stage = STAGE_RUNNING; return SRESULT_WAIT; case STAGE_RUNNING: if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) { FinishAction( "melee_attack" ); SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 ); return SRESULT_DONE; } return SRESULT_WAIT; } return SRESULT_ERROR; } stateResult_t rvmMonsterBossGuardianSeeker::Torso_Death( stateParms_t* parms ) { enum { STAGE_START, STAGE_RUNNING }; switch ( parms->stage ) { case STAGE_START: PlayAnim( ANIMCHANNEL_TORSO, "death" ); parms->stage = STAGE_RUNNING; return SRESULT_WAIT; case STAGE_RUNNING: if ( AnimDone( ANIMCHANNEL_TORSO, 0 ) ) { FinishAction( "death" ); SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 ); return SRESULT_DONE; } return SRESULT_WAIT; } return SRESULT_ERROR; } stateResult_t rvmMonsterBossGuardianSeeker::see_enemy( stateParms_t* parms ) { LookAtEnemy( SEEKER_SIGHTTIME ); return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardianSeeker::lost_enemy( stateParms_t* parms ) { LookAtEnemy( 0.1f ); return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardianSeeker::respawnSeeker( stateParms_t* parms ) { SetState( "state_Respawn" ); return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardianSeeker::state_Respawn( stateParms_t* parms ) { enum { STAGE_START, STAGE_SOLID }; switch ( parms->stage ) { case STAGE_START: ShowOwner(); AllowDamage(); SetHealth( GetFloatKey( "health", "100" ) ); StartSoundKey( "snd_respawn", SND_CHANNEL_VOICE, false ); scratchEndTime = DelayTime( 0.2f ); parms->stage = STAGE_SOLID; return SRESULT_WAIT; case STAGE_SOLID: if ( Time() < scratchEndTime ) { return SRESULT_WAIT; } BecomeSolid(); SetState( "state_Idle" ); return SRESULT_DONE; } return SRESULT_ERROR; } stateResult_t rvmMonsterBossGuardianSeeker::state_Inactive( stateParms_t* parms ) { if ( parms->stage == 0 ) { HideOwner(); IgnoreDamage(); StopMove(); parms->stage = 1; } if ( Activated() || Pain() ) { SetState( "state_Respawn" ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardianSeeker::state_Idle( stateParms_t* parms ) { enum { STAGE_START, STAGE_WAIT }; switch ( parms->stage ) { case STAGE_START: StartAction( "wait_for_enemy" ); parms->stage = STAGE_WAIT; return SRESULT_WAIT; case STAGE_WAIT: if ( InAction() ) { return SRESULT_WAIT; } SetState( "state_WatchPlayer" ); return SRESULT_DONE; } return SRESULT_ERROR; } stateResult_t rvmMonsterBossGuardianSeeker::state_Killed( stateParms_t* parms ) { if ( parms->stage == 0 ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 0 ); BecomeNonSolid(); parms->stage = 1; return SRESULT_WAIT; } if ( InAnimState( ANIMCHANNEL_TORSO, "Torso_Death" ) ) { return SRESULT_WAIT; } HideOwner(); SetState( "state_Inactive" ); return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardianSeeker::state_WatchPlayer( stateParms_t* parms ) { const float range = EnemyRange(); if ( !EnemyVisible() ) { SetState( "state_FindPlayer" ); return SRESULT_DONE; } if ( CheckForMelee() ) { waitActionName = "melee_attack"; SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 2 ); return SRESULT_WAIT; } if ( range < SEEKER_TOOCLOSE_RANGE ) { SetState( "state_Retreat" ); return SRESULT_DONE; } if ( range > SEEKER_TOOFAR_RANGE ) { SetState( "state_GetCloser" ); return SRESULT_DONE; } FaceEnemy(); MoveToEnemyHeight(); return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardianSeeker::state_GetCloser( stateParms_t* parms ) { enum { STAGE_START, STAGE_MOVE }; switch ( parms->stage ) { case STAGE_START: MoveToEnemy(); getCloserEndTime = DelayTime( SEEKER_GETCLOSER_TIME ); parms->stage = STAGE_MOVE; return SRESULT_WAIT; case STAGE_MOVE: if ( Time() >= getCloserEndTime || EnemyRange() <= SEEKER_IDEAL_RANGE || MoveDone() ) { SetState( "state_WatchPlayer" ); return SRESULT_DONE; } return SRESULT_WAIT; } return SRESULT_ERROR; } stateResult_t rvmMonsterBossGuardianSeeker::state_FindPlayer( stateParms_t* parms ) { enum { STAGE_START, STAGE_SEEK }; switch ( parms->stage ) { case STAGE_START: StartAction( "lost_enemy" ); MoveToEnemy(); scratchEndTime = DelayTime( SEEKER_SIGHTTIME ); parms->stage = STAGE_SEEK; return SRESULT_WAIT; case STAGE_SEEK: if ( EnemyVisible() ) { StartAction( "see_enemy" ); SetState( "state_WatchPlayer" ); return SRESULT_DONE; } if ( Time() >= scratchEndTime || MoveDone() ) { SetState( "state_WatchPlayer" ); return SRESULT_DONE; } return SRESULT_WAIT; } return SRESULT_ERROR; } stateResult_t rvmMonsterBossGuardianSeeker::state_Retreat( stateParms_t* parms ) { enum { STAGE_START, STAGE_MOVE }; switch ( parms->stage ) { case STAGE_START: MoveOutOfRange( GetEnemy(), SEEKER_IDEAL_RANGE ); retreatEndTime = DelayTime( SEEKER_RUNTIME ); parms->stage = STAGE_MOVE; return SRESULT_WAIT; case STAGE_MOVE: if ( Time() >= retreatEndTime || MoveDone() || EnemyRange() >= SEEKER_IDEAL_RANGE ) { SetState( "state_WatchPlayer" ); return SRESULT_DONE; } return SRESULT_WAIT; } return SRESULT_ERROR; } stateResult_t rvmMonsterBossGuardianSeeker::state_RunAway( stateParms_t* parms ) { enum { STAGE_START, STAGE_RUN }; switch ( parms->stage ) { case STAGE_START: SetFlySpeed( SEEKER_ESCAPE_SPEED ); MoveOutOfRange( GetEnemy(), SEEKER_MAXIMUM_RETREAT_RANGE ); scratchEndTime = DelayTime( SEEKER_RUNTIME ); parms->stage = STAGE_RUN; return SRESULT_WAIT; case STAGE_RUN: if ( Time() >= scratchEndTime || MoveDone() ) { ClearFlyOffset(); SetState( "state_WatchPlayer" ); return SRESULT_DONE; } return SRESULT_WAIT; } return SRESULT_ERROR; }