// AI_monster_boss_guardian.cpp #include "precompiled.h" #pragma hdrstop #include "../Game_local.h" rvmSeekerBase::rvmSeekerBase() {} void rvmSeekerBase::Init( idAI* aiOwner ) { rvmMonsterBase::Init( aiOwner ); } stateResult_t rvmSeekerBase::RunStateByName( const char* stateName, stateParms_t* parms ) { AI_DISPATCH_STATE( "respawnSeeker", respawnSeeker ); return rvmMonsterBase::RunStateByName( stateName, parms ); } bool rvmSeekerBase::isAlive() const { return owner && owner->health > 0 && !owner->AI_DEAD; } stateResult_t rvmSeekerBase::respawnSeeker( stateParms_t* parms ) { return SRESULT_DONE; } rvmMonsterBossGuardianSpawner::rvmMonsterBossGuardianSpawner() { guardian = NULL; inactive_time = active_time = pain_time = 0.0f; } void rvmMonsterBossGuardianSpawner::Init( idAI* aiOwner ) { rvmMonsterBase::Init( aiOwner ); inactive_time = GetFloatKey( "inactive_time", SPAWNER_FADEOUT ); active_time = GetFloatKey( "active_time", SPAWNER_FADEIN_TIME ); pain_time = GetFloatKey( "pain_time", SPAWNER_PAIN_FADEOUT ); SetState( "state_Inactive" ); } stateResult_t rvmMonsterBossGuardianSpawner::RunStateByName( const char* stateName, stateParms_t* parms ) { AI_DISPATCH_STATE( "update_effects", update_effects ); AI_DISPATCH_STATE( "setGuardian", setGuardian ); AI_DISPATCH_STATE( "state_Inactive", state_Inactive ); AI_DISPATCH_STATE( "state_Active", state_Active ); AI_DISPATCH_STATE( "state_Killed", state_Killed ); return rvmMonsterBase::RunStateByName( stateName, parms ); } stateResult_t rvmMonsterBossGuardianSpawner::update_effects( stateParms_t* parms ) { return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardianSpawner::setGuardian( stateParms_t* parms ) { guardian = scratchEntity.GetEntity(); return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardianSpawner::state_Inactive( stateParms_t* parms ) { if ( parms->stage == 0 ) { HideOwner(); IgnoreDamage(); parms->stage = 1; } if ( Pain() || Activated() ) { ShowOwner(); AllowDamage(); SetState( "state_Active" ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardianSpawner::state_Active( stateParms_t* parms ) { if ( parms->stage == 0 ) { ShowOwner(); AllowDamage(); scratchEndTime = DelayTime( active_time ); parms->stage = 1; return SRESULT_WAIT; } if ( Time() < scratchEndTime ) { return SRESULT_WAIT; } SetState( "state_Inactive" ); return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardianSpawner::state_Killed( stateParms_t* parms ) { return rvmMonsterBase::state_Killed( parms ); } rvmMonsterBossGuardian::rvmMonsterBossGuardian() { nextAttack = 0.0f; painNextTime = 0.0f; in_melee = false; rightfoot = false; charge = false; seeker1 = seeker2 = seeker3 = spawner = NULL; } void rvmMonsterBossGuardian::Init( idAI* aiOwner ) { rvmMonsterBase::Init( aiOwner ); SetMoveType( MOVETYPE_ANIM ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 ); SetState( "state_Idle" ); } stateResult_t rvmMonsterBossGuardian::RunStateByName( const char* stateName, stateParms_t* parms ) { AI_DISPATCH_STATE( "Legs_Sight", Legs_Sight ); AI_DISPATCH_STATE( "Legs_Death", Legs_Death ); AI_DISPATCH_STATE( "Legs_Pain", Legs_Pain ); AI_DISPATCH_STATE( "Legs_RangeAttack", Legs_RangeAttack ); AI_DISPATCH_STATE( "Legs_MeleeAttack", Legs_MeleeAttack ); AI_DISPATCH_STATE( "Legs_Walk_Melee_Left", Legs_Walk_Melee_Left ); AI_DISPATCH_STATE( "Legs_Walk_Melee_Right", Legs_Walk_Melee_Right ); AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk ); AI_DISPATCH_STATE( "Legs_Run_Melee_Left", Legs_Run_Melee_Left ); AI_DISPATCH_STATE( "Legs_Run_Melee_Right", Legs_Run_Melee_Right ); AI_DISPATCH_STATE( "Legs_Charge", Legs_Charge ); AI_DISPATCH_STATE( "Legs_Lost", Legs_Lost ); AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle ); AI_DISPATCH_STATE( "Legs_SpawnSeeker", Legs_SpawnSeeker ); AI_DISPATCH_STATE( "addSeeker", addSeeker ); AI_DISPATCH_STATE( "sightEnemy", sightEnemy ); AI_DISPATCH_STATE( "state_Idle", state_Idle ); AI_DISPATCH_STATE( "state_Killed", state_Killed ); AI_DISPATCH_STATE( "state_Combat", state_Combat ); AI_DISPATCH_STATE( "combat_melee", combat_melee ); AI_DISPATCH_STATE( "combat_range", combat_range ); AI_DISPATCH_STATE( "combat_charge", combat_charge ); AI_DISPATCH_STATE( "pound_impact", pound_impact ); AI_DISPATCH_STATE( "pound_attack", pound_attack ); AI_DISPATCH_STATE( "pound_attack_left", pound_attack_left ); AI_DISPATCH_STATE( "pound_attack_right", pound_attack_right ); AI_DISPATCH_STATE( "smash_ground", smash_ground ); return rvmMonsterBase::RunStateByName( stateName, parms ); } stateResult_t rvmMonsterBossGuardian::Legs_Idle( stateParms_t* parms ) { if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "idle" ); parms->stage = 1; } if ( Pain() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Pain", GUARDIAN_IDLE_TO_PAIN ); return SRESULT_DONE; } if ( charge ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Charge", GUARDIAN_IDLE_TO_CHARGE ); return SRESULT_DONE; } if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", GUARDIAN_IDLE_TO_WALK ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_Walk( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; } if ( Pain() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Pain", GUARDIAN_WALK_TO_PAIN ); return SRESULT_DONE; } if ( charge ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Charge", GUARDIAN_WALK_TO_CHARGE ); return SRESULT_DONE; } if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_WALK_TO_IDLE ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_Pain( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, GetPainAnim() ); painNextTime = DelayTime( GUARDIAN_PAIN_DELAY ); parms->stage = 1; return SRESULT_WAIT; } if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Pain", GUARDIAN_PAIN_TO_PAIN ); return SRESULT_DONE; } if ( AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_PAIN_TO_IDLE ) ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_PAIN_TO_IDLE ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_Sight( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "sight" ); parms->stage = 1; } if ( AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_SIGHT_TO_IDLE ) ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_SIGHT_TO_IDLE ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_Death( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "death" ); parms->stage = 1; } return AnimDone( ANIMCHANNEL_LEGS, 0 ) ? SRESULT_DONE : SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_RangeAttack( stateParms_t* parms ) { if ( parms->stage == 0 ) { StopMove(); FaceEnemy(); PlayAnim( ANIMCHANNEL_LEGS, "range_attack" ); parms->stage = 1; return SRESULT_WAIT; } if ( AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_RANGEATTACK_TO_IDLE ) ) { FinishAction( "range_attack" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_RANGEATTACK_TO_IDLE ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_MeleeAttack( stateParms_t* parms ) { if ( parms->stage == 0 ) { StopMove(); FaceEnemy(); in_melee = true; PlayAnim( ANIMCHANNEL_LEGS, "melee_attack" ); parms->stage = 1; return SRESULT_WAIT; } if ( AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_MELEE_TO_IDLE ) ) { in_melee = false; FinishAction( "melee_attack" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_MELEE_TO_IDLE ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_Walk_Melee_Left( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "walk_melee_left" ); parms->stage = 1; } return AnimDone( ANIMCHANNEL_LEGS, 4 ) ? SRESULT_DONE : SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_Walk_Melee_Right( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "walk_melee_right" ); parms->stage = 1; } return AnimDone( ANIMCHANNEL_LEGS, 4 ) ? SRESULT_DONE : SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_Run_Melee_Left( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "run_melee_left" ); parms->stage = 1; } return AnimDone( ANIMCHANNEL_LEGS, 4 ) ? SRESULT_DONE : SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_Run_Melee_Right( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "run_melee_right" ); parms->stage = 1; } return AnimDone( ANIMCHANNEL_LEGS, 4 ) ? SRESULT_DONE : SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_Charge( stateParms_t* parms ) { if ( parms->stage == 0 ) { charge = true; ChargeAttack( "damage_guardian_charge" ); PlayCycle( ANIMCHANNEL_LEGS, "charge" ); parms->stage = 1; return SRESULT_WAIT; } if ( Pain() ) { charge = false; AttackEnd(); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Pain", GUARDIAN_CHARGE_TO_PAIN ); return SRESULT_DONE; } if ( MoveDone() || EnemyRange() < 128.0f ) { charge = false; AttackEnd(); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_CHARGE_TO_IDLE ); FinishAction( "charge_attack" ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_Lost( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "lost" ); parms->stage = 1; } return AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_LOST_TO_IDLE ) ? SRESULT_DONE : SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::Legs_SpawnSeeker( stateParms_t* parms ) { if ( parms->stage == 0 ) { StopMove(); PlayAnim( ANIMCHANNEL_LEGS, "spawn_seeker" ); parms->stage = 1; return SRESULT_WAIT; } if ( AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_SPAWN_TO_IDLE ) ) { FinishAction( "spawn_seeker" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_SPAWN_TO_IDLE ); return SRESULT_DONE; } return SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::addSeeker( stateParms_t* parms ) { idEntity* ent = scratchEntity.GetEntity(); if ( !seeker1.GetEntity() ) seeker1 = ent; else if ( !seeker2.GetEntity() ) seeker2 = ent; else seeker3 = ent; return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardian::sightEnemy( stateParms_t* parms ) { FaceEnemy(); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Sight", GUARDIAN_IDLE_TO_SIGHT ); return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardian::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; StartAction( "sightEnemy" ); nextAttack = DelayTime( 2.0f ); SetState( "state_Combat" ); return SRESULT_DONE; } return SRESULT_ERROR; } stateResult_t rvmMonsterBossGuardian::state_Killed( stateParms_t* parms ) { return rvmMonsterBase::state_Killed( parms ); } stateResult_t rvmMonsterBossGuardian::state_Combat( stateParms_t* parms ) { enum { STAGE_SELECT, STAGE_ACTION }; switch ( parms->stage ) { case STAGE_SELECT: if ( EnemyDead() ) { SetState( "state_LostCombat" ); return SRESULT_DONE; } if ( Pain() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Pain", GUARDIAN_WALK_TO_PAIN ); } if ( CheckForMelee() ) { StartAction( "combat_melee" ); parms->stage = STAGE_ACTION; return SRESULT_WAIT; } if ( Time() >= nextAttack && EnemyRange() < GUARDIAN_RANGE_ATTACK_RANGE && CanHitEnemy() ) { StartAction( "combat_range" ); parms->stage = STAGE_ACTION; return SRESULT_WAIT; } if ( EnemyRange() < GUARDIAN_CHARGE_ATTACK_RANGE && TestChargeAttack() > 0.0f ) { StartAction( "combat_charge" ); parms->stage = STAGE_ACTION; return SRESULT_WAIT; } MoveToEnemy(); return SRESULT_WAIT; case STAGE_ACTION: if ( InAction() ) { return SRESULT_WAIT; } parms->stage = STAGE_SELECT; return SRESULT_WAIT; } return SRESULT_ERROR; } stateResult_t rvmMonsterBossGuardian::combat_melee( stateParms_t* parms ) { if ( parms->stage == 0 ) { waitActionName = "melee_attack"; SetAnimState( ANIMCHANNEL_LEGS, "Legs_MeleeAttack", GUARDIAN_IDLE_TO_MELEE ); parms->stage = 1; return SRESULT_WAIT; } return ActionDone( "melee_attack" ) ? SRESULT_DONE : SRESULT_WAIT; } stateResult_t rvmMonsterBossGuardian::combat_range( stateParms_t* parms ) { if ( parms->stage == 0 ) { waitActionName = "range_attack"; SetAnimState( ANIMCHANNEL_LEGS, "Legs_RangeAttack", GUARDIAN_IDLE_TO_RANGE ); parms->stage = 1; return SRESULT_WAIT; } if ( !ActionDone( "range_attack" ) ) { return SRESULT_WAIT; } nextAttack = DelayTime( GUARDIAN_RANGE_ATTACK_DELAY ); return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardian::combat_charge( stateParms_t* parms ) { if ( parms->stage == 0 ) { waitActionName = "charge_attack"; charge = true; SetAnimState( ANIMCHANNEL_LEGS, "Legs_Charge", GUARDIAN_IDLE_TO_CHARGE ); parms->stage = 1; return SRESULT_WAIT; } if ( !ActionDone( "charge_attack" ) ) { return SRESULT_WAIT; } charge = false; nextAttack = DelayTime( GUARDIAN_RANGE_ATTACK_DELAY ); return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardian::pound_impact( stateParms_t* parms ) { RadiusDamageFromJoint( rightfoot ? "r_foot" : "l_foot", "damage_guardian_footstep" ); return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardian::pound_attack( stateParms_t* parms ) { AttackMelee( "damage_guardian_melee" ); return SRESULT_DONE; } stateResult_t rvmMonsterBossGuardian::pound_attack_left( stateParms_t* parms ) { rightfoot = false; return pound_attack( parms ); } stateResult_t rvmMonsterBossGuardian::pound_attack_right( stateParms_t* parms ) { rightfoot = true; return pound_attack( parms ); } stateResult_t rvmMonsterBossGuardian::smash_ground( stateParms_t* parms ) { RadiusDamageFromJoint( "origin", "damage_guardian_smash" ); return SRESULT_DONE; }