quake4-sdk/source/game/ai/Monster_Grunt.cpp
2007-06-15 00:00:00 +00:00

318 lines
7.9 KiB
C++

#include "../../idlib/precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
class rvMonsterGrunt : public idAI {
public:
CLASS_PROTOTYPE( rvMonsterGrunt );
rvMonsterGrunt ( void );
void Spawn ( void );
void Save ( idSaveGame *savefile ) const;
void Restore ( idRestoreGame *savefile );
virtual void AdjustHealthByDamage ( int damage );
protected:
rvAIAction actionMeleeMoveAttack;
rvAIAction actionChaingunAttack;
virtual bool CheckActions ( void );
virtual void OnTacticalChange ( aiTactical_t oldTactical );
virtual void OnDeath ( void );
private:
int standingMeleeNoAttackTime;
int rageThreshold;
void RageStart ( void );
void RageStop ( void );
// Torso States
stateResult_t State_Torso_Enrage ( const stateParms_t& parms );
stateResult_t State_Torso_Pain ( const stateParms_t& parms );
stateResult_t State_Torso_LeapAttack ( const stateParms_t& parms );
CLASS_STATES_PROTOTYPE ( rvMonsterGrunt );
};
CLASS_DECLARATION( idAI, rvMonsterGrunt )
END_CLASS
/*
================
rvMonsterGrunt::rvMonsterGrunt
================
*/
rvMonsterGrunt::rvMonsterGrunt ( void ) {
standingMeleeNoAttackTime = 0;
}
/*
================
rvMonsterGrunt::Spawn
================
*/
void rvMonsterGrunt::Spawn ( void ) {
rageThreshold = spawnArgs.GetInt ( "health_rageThreshold" );
// Custom actions
actionMeleeMoveAttack.Init ( spawnArgs, "action_meleeMoveAttack", NULL, AIACTIONF_ATTACK );
actionChaingunAttack.Init ( spawnArgs, "action_chaingunAttack", NULL, AIACTIONF_ATTACK );
actionLeapAttack.Init ( spawnArgs, "action_leapAttack", "Torso_LeapAttack", AIACTIONF_ATTACK );
// Enraged to start?
if ( spawnArgs.GetBool ( "preinject" ) ) {
RageStart ( );
}
}
/*
================
rvMonsterGrunt::Save
================
*/
void rvMonsterGrunt::Save ( idSaveGame *savefile ) const {
actionMeleeMoveAttack.Save( savefile );
actionChaingunAttack.Save( savefile );
savefile->WriteInt( rageThreshold );
savefile->WriteInt( standingMeleeNoAttackTime );
}
/*
================
rvMonsterGrunt::Restore
================
*/
void rvMonsterGrunt::Restore ( idRestoreGame *savefile ) {
actionMeleeMoveAttack.Restore( savefile );
actionChaingunAttack.Restore( savefile );
savefile->ReadInt( rageThreshold );
savefile->ReadInt( standingMeleeNoAttackTime );
}
/*
================
rvMonsterGrunt::RageStart
================
*/
void rvMonsterGrunt::RageStart ( void ) {
SetShaderParm ( 6, 1 );
// Disable non-rage actions
actionEvadeLeft.fl.disabled = true;
actionEvadeRight.fl.disabled = true;
// Speed up animations
animator.SetPlaybackRate ( 1.25f );
// Disable pain
pain.threshold = 0;
// Start over with health when enraged
health = spawnArgs.GetInt ( "health" );
// No more going to rage
rageThreshold = 0;
}
/*
================
rvMonsterGrunt::RageStop
================
*/
void rvMonsterGrunt::RageStop ( void ) {
SetShaderParm ( 6, 0 );
}
/*
================
rvMonsterGrunt::CheckActions
================
*/
bool rvMonsterGrunt::CheckActions ( void ) {
// If our health is below the rage threshold then enrage
if ( health < rageThreshold ) {
PerformAction ( "Torso_Enrage", 4, true );
return true;
}
// Moving melee attack?
if ( PerformAction ( &actionMeleeMoveAttack, (checkAction_t)&idAI::CheckAction_MeleeAttack, NULL ) ) {
return true;
}
// Default actions
if ( CheckPainActions ( ) ) {
return true;
}
if ( PerformAction ( &actionEvadeLeft, (checkAction_t)&idAI::CheckAction_EvadeLeft, &actionTimerEvade ) ||
PerformAction ( &actionEvadeRight, (checkAction_t)&idAI::CheckAction_EvadeRight, &actionTimerEvade ) ||
PerformAction ( &actionJumpBack, (checkAction_t)&idAI::CheckAction_JumpBack, &actionTimerEvade ) ||
PerformAction ( &actionLeapAttack, (checkAction_t)&idAI::CheckAction_LeapAttack ) ) {
return true;
} else if ( PerformAction ( &actionMeleeAttack, (checkAction_t)&idAI::CheckAction_MeleeAttack ) ) {
standingMeleeNoAttackTime = 0;
return true;
} else {
if ( actionMeleeAttack.status != rvAIAction::STATUS_FAIL_TIMER
&& actionMeleeAttack.status != rvAIAction::STATUS_FAIL_EXTERNALTIMER
&& actionMeleeAttack.status != rvAIAction::STATUS_FAIL_CHANCE )
{//melee attack fail for any reason other than timer?
if ( combat.tacticalCurrent == AITACTICAL_MELEE && !move.fl.moving )
{//special case: we're in tactical melee and we're close enough to think we've reached the enemy, but he's just out of melee range!
if ( !standingMeleeNoAttackTime )
{
standingMeleeNoAttackTime = gameLocal.GetTime();
}
else if ( standingMeleeNoAttackTime + 2500 < gameLocal.GetTime() )
{//we've been standing still and not attacking for at least 2.5 seconds, fall back to ranged attack
//allow ranged attack
actionRangedAttack.fl.disabled = false;
}
}
}
if ( PerformAction ( &actionRangedAttack,(checkAction_t)&idAI::CheckAction_RangedAttack, &actionTimerRangedAttack ) ) {
return true;
}
}
return false;
}
/*
================
rvMonsterGrunt::OnDeath
================
*/
void rvMonsterGrunt::OnDeath ( void ) {
RageStop ( );
return idAI::OnDeath ( );
}
/*
================
rvMonsterGrunt::OnTacticalChange
Enable/Disable the ranged attack based on whether the grunt needs it
================
*/
void rvMonsterGrunt::OnTacticalChange ( aiTactical_t oldTactical ) {
switch ( combat.tacticalCurrent ) {
case AITACTICAL_MELEE:
actionRangedAttack.fl.disabled = true;
break;
default:
actionRangedAttack.fl.disabled = false;
break;
}
}
/*
=====================
rvMonsterGrunt::AdjustHealthByDamage
=====================
*/
void rvMonsterGrunt::AdjustHealthByDamage ( int damage ) {
// Take less damage during enrage process
if ( rageThreshold && health < rageThreshold ) {
health -= (damage * 0.25f);
return;
}
return idAI::AdjustHealthByDamage ( damage );
}
/*
===============================================================================
States
===============================================================================
*/
CLASS_STATES_DECLARATION ( rvMonsterGrunt )
STATE ( "Torso_Enrage", rvMonsterGrunt::State_Torso_Enrage )
STATE ( "Torso_Pain", rvMonsterGrunt::State_Torso_Pain )
STATE ( "Torso_LeapAttack", rvMonsterGrunt::State_Torso_LeapAttack )
END_CLASS_STATES
/*
================
rvMonsterGrunt::State_Torso_Pain
================
*/
stateResult_t rvMonsterGrunt::State_Torso_Pain ( const stateParms_t& parms ) {
// Stop streaming pain if its time to get angry
if ( pain.loopEndTime && health < rageThreshold ) {
pain.loopEndTime = 0;
}
return idAI::State_Torso_Pain ( parms );
}
/*
================
rvMonsterGrunt::State_Torso_Enrage
================
*/
stateResult_t rvMonsterGrunt::State_Torso_Enrage ( const stateParms_t& parms ) {
enum {
STAGE_ANIM,
STAGE_ANIM_WAIT,
};
switch ( parms.stage ) {
case STAGE_ANIM:
DisableAnimState ( ANIMCHANNEL_LEGS );
PlayAnim ( ANIMCHANNEL_TORSO, "anger", parms.blendFrames );
return SRESULT_STAGE ( STAGE_ANIM_WAIT );
case STAGE_ANIM_WAIT:
if ( AnimDone ( ANIMCHANNEL_TORSO, 4 ) ) {
RageStart ( );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
/*
================
rvMonsterGrunt::State_Torso_LeapAttack
================
*/
stateResult_t rvMonsterGrunt::State_Torso_LeapAttack ( const stateParms_t& parms ) {
enum {
STAGE_ANIM,
STAGE_ANIM_WAIT,
};
switch ( parms.stage ) {
case STAGE_ANIM:
DisableAnimState ( ANIMCHANNEL_LEGS );
lastAttackTime = 0;
// Play the action animation
PlayAnim ( ANIMCHANNEL_TORSO, animator.GetAnim ( actionAnimNum )->FullName ( ), parms.blendFrames );
return SRESULT_STAGE ( STAGE_ANIM_WAIT );
case STAGE_ANIM_WAIT:
if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
// If we missed our leap attack get angry
if ( !lastAttackTime && rageThreshold ) {
PostAnimState ( ANIMCHANNEL_TORSO, "Torso_Enrage", parms.blendFrames );
}
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}