quake4-sdk/source/mpgame/ai/Monster_HeavyHoverTank.cpp
2007-06-15 00:00:00 +00:00

534 lines
14 KiB
C++

#include "../../idlib/precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
#include "../vehicle/Vehicle.h"
class rvMonsterHeavyHoverTank : public idAI {
public:
CLASS_PROTOTYPE( rvMonsterHeavyHoverTank );
rvMonsterHeavyHoverTank ( void );
void InitSpawnArgsVariables( void );
void Spawn ( void );
void Save ( idSaveGame *savefile ) const;
void Restore ( idRestoreGame *savefile );
virtual void Think ( void );
virtual void Damage ( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location );
virtual void OnDeath ( void );
protected:
enum weaponState_t {
WEAPONSTATE_BLASTER,
WEAPONSTATE_ROCKET,
WEAPONSTATE_MAX
};
int blasterAnimIndex;
int shots;
weaponState_t weaponStateCurrent;
weaponState_t weaponStateIdeal;
rvAIAction actionRocketAttack;
rvAIAction actionBlasterAttack;
rvAIAction actionStrafe;
jointHandle_t jointHoverEffect;
rvClientEffectPtr effectDust;
rvClientEffectPtr effectHover;
virtual bool CheckActions ( void );
virtual void OnEnemyChange ( idEntity* oldEnemy );
// virtual const char* GetIdleAnimName ( void );
private:
float strafeSpeed;
bool CheckAction_Strafe ( rvAIAction* action, int animNum );
stateResult_t State_Torso_BlasterAttack ( const stateParms_t& parms );
stateResult_t State_Torso_RocketAttack ( const stateParms_t& parms );
stateResult_t State_Torso_ChangeWeaponState ( const stateParms_t& parms );
stateResult_t State_Torso_EvadeLeft ( const stateParms_t& parms );
stateResult_t State_Torso_EvadeRight ( const stateParms_t& parms );
stateResult_t State_Torso_Strafe ( const stateParms_t& parms );
CLASS_STATES_PROTOTYPE ( rvMonsterHeavyHoverTank );
};
CLASS_DECLARATION( idAI, rvMonsterHeavyHoverTank )
END_CLASS
/*
================
rvMonsterHeavyHoverTank::rvMonsterHeavyHoverTank
================
*/
rvMonsterHeavyHoverTank::rvMonsterHeavyHoverTank ( ) {
weaponStateIdeal = WEAPONSTATE_BLASTER;
weaponStateCurrent = weaponStateIdeal;
effectDust = NULL;
effectHover = NULL;
blasterAnimIndex = 0;
shots = 0;
strafeSpeed = 0;
}
void rvMonsterHeavyHoverTank::InitSpawnArgsVariables( void )
{
jointHoverEffect = animator.GetJointHandle ( spawnArgs.GetString("joint_hover") );
strafeSpeed = spawnArgs.GetFloat( "strafeSpeed", "300" );
}
/*
================
rvMonsterHeavyHoverTank::Spawn
================
*/
void rvMonsterHeavyHoverTank::Spawn ( void ) {
actionRocketAttack.Init ( spawnArgs, "action_rocketAttack", "Torso_RocketAttack", AIACTIONF_ATTACK );
actionBlasterAttack.Init ( spawnArgs, "action_blasterAttack", "Torso_BlasterAttack", AIACTIONF_ATTACK );
actionStrafe.Init ( spawnArgs, "action_strafe", "Torso_Strafe", 0 );
InitSpawnArgsVariables();
if ( jointHoverEffect != INVALID_JOINT ) {
effectHover = PlayEffect ( "fx_hover", jointHoverEffect, true );
}
}
/*
================
rvMonsterHeavyHoverTank::Think
================
*/
void rvMonsterHeavyHoverTank::Think ( void ) {
idAI::Think ( );
// If thinking we should play an effect on the ground under us
if ( jointHoverEffect != INVALID_JOINT ) {
if ( !fl.hidden && !fl.isDormant && (thinkFlags & TH_THINK ) && !aifl.dead ) {
trace_t tr;
idVec3 origin;
idMat3 axis;
// Project the effect 128 units down from the hover effect joint
GetJointWorldTransform ( jointHoverEffect, gameLocal.time, origin, axis );
// RAVEN BEGIN
// ddynerman: multiple clip worlds
gameLocal.TracePoint ( this, tr, origin, origin + axis[0] * 128.0f, CONTENTS_SOLID, this );
// RAVEN END
// Start the dust effect if not already started
if ( !effectDust ) {
effectDust = gameLocal.PlayEffect ( gameLocal.GetEffect ( spawnArgs, "fx_dust" ), tr.endpos, tr.c.normal.ToMat3(), true );
}
// If the effect is playing we should update its attenuation as well as its origin and axis
if ( effectDust ) {
effectDust->Attenuate ( 1.0f - idMath::ClampFloat ( 0.0f, 1.0f, (tr.endpos - origin).LengthFast ( ) / 127.0f ) );
effectDust->SetOrigin ( tr.endpos );
effectDust->SetAxis ( tr.c.normal.ToMat3() );
}
// If the hover effect is playing we can set its end origin to the ground
if ( effectHover ) {
effectHover->SetEndOrigin ( tr.endpos );
}
} else if ( effectDust ) {
effectDust->Stop ( );
effectDust = NULL;
}
}
}
/*
================
rvMonsterHeavyHoverTank::Save
================
*/
void rvMonsterHeavyHoverTank::Save ( idSaveGame *savefile ) const {
savefile->WriteInt ( blasterAnimIndex );
savefile->WriteInt ( shots );
savefile->WriteInt ( (int)weaponStateCurrent );
savefile->WriteInt ( (int)weaponStateIdeal );
actionRocketAttack.Save ( savefile );
actionBlasterAttack.Save ( savefile );
actionStrafe.Save ( savefile );
effectDust.Save ( savefile );
effectHover.Save ( savefile );
}
/*
================
rvMonsterHeavyHoverTank::Restore
================
*/
void rvMonsterHeavyHoverTank::Restore ( idRestoreGame *savefile ) {
savefile->ReadInt ( blasterAnimIndex );
savefile->ReadInt ( shots );
savefile->ReadInt ( (int&)weaponStateCurrent );
savefile->ReadInt ( (int&)weaponStateIdeal );
actionRocketAttack.Restore ( savefile );
actionBlasterAttack.Restore ( savefile );
actionStrafe.Restore ( savefile );
effectDust.Restore ( savefile );
effectHover.Restore ( savefile );
InitSpawnArgsVariables();
}
void rvMonsterHeavyHoverTank::Damage ( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location )
{
if ( damageScale > 0.0f ) {
const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName, false );
if ( damageDef && damageDef->GetBool( "vehicle_collision" ) ) {
//push me hard!
float push = idMath::ClampFloat( 250.0f, 500.0f, damageScale*1000.0f );
idVec3 vel = GetPhysics()->GetLinearVelocity();
vel += dir * push;
physicsObj.UseVelocityMove( true );
GetPhysics()->SetLinearVelocity( vel );
}
}
idAI::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
}
/*
================
rvMonsterHeavyHoverTank::OnDeath
================
*/
void rvMonsterHeavyHoverTank::OnDeath ( void ) {
// Stop the dust effect
if ( effectDust ) {
effectDust->Stop ( );
effectDust = NULL;
}
// Stop the hover effect
if ( effectHover ) {
effectHover->Stop ( );
effectHover = NULL;
}
idAI::OnDeath ( );
}
/*
================
rvMonsterHeavyHoverTank::CheckAction_Strafe
================
*/
bool rvMonsterHeavyHoverTank::CheckAction_Strafe ( rvAIAction* action, int animNum ) {
if ( !enemy.fl.visible ) {
return false;
}
if ( !enemy.fl.inFov ) {
return false;
}
if ( !move.fl.done ) {
return false;
}
if ( animNum != -1 && !TestAnimMove ( animNum ) ) {
//well, at least try a new attack position
if ( combat.tacticalCurrent == AITACTICAL_RANGED ) {
combat.tacticalUpdateTime = 0;
}
return false;
}
return true;
}
/*
================
rvMonsterHeavyHoverTank::Spawn
================
*/
bool rvMonsterHeavyHoverTank::CheckActions ( void ) {
if ( weaponStateIdeal != weaponStateCurrent ) {
PerformAction ( "Torso_ChangeWeaponState", 4 );
return true;
}
if ( PerformAction ( &actionRocketAttack, (checkAction_t)&idAI::CheckAction_RangedAttack, &actionTimerSpecialAttack ) ) {
return true;
}
if ( PerformAction ( &actionBlasterAttack, (checkAction_t)&idAI::CheckAction_RangedAttack, &actionTimerRangedAttack ) ) {
return true;
}
if ( idAI::CheckActions( ) ) {
return true;
}
if ( PerformAction ( &actionStrafe, (checkAction_t)&rvMonsterHeavyHoverTank::CheckAction_Strafe ) )
{
return true;
}
return false;
}
/*
================
rvMonsterHeavyHoverTank::OnEnemyChange
================
*/
void rvMonsterHeavyHoverTank::OnEnemyChange ( idEntity* oldEnemy ) {
idAI::OnEnemyChange ( oldEnemy );
if ( !enemy.ent ) {
return;
}
if ( enemy.ent->IsType ( rvVehicle::GetClassType() ) ) {
weaponStateIdeal = WEAPONSTATE_ROCKET;
}
}
/*
================
rvMonsterHeavyHoverTank::GetIdleAnimName
================
*/
/*
const char* rvMonsterHeavyHoverTank::GetIdleAnimName ( void ) {
if ( weaponStateCurrent == WEAPONSTATE_ROCKET ) {
return "rocket_idle";
}
return idAI::GetIdleAnimName ( );
}
*/
/*
===============================================================================
States
===============================================================================
*/
CLASS_STATES_DECLARATION ( rvMonsterHeavyHoverTank )
STATE ( "Torso_BlasterAttack", rvMonsterHeavyHoverTank::State_Torso_BlasterAttack )
STATE ( "Torso_RocketAttack", rvMonsterHeavyHoverTank::State_Torso_RocketAttack )
STATE ( "Torso_ChangeWeaponState", rvMonsterHeavyHoverTank::State_Torso_ChangeWeaponState )
STATE ( "Torso_EvadeLeft", rvMonsterHeavyHoverTank::State_Torso_EvadeLeft )
STATE ( "Torso_EvadeRight", rvMonsterHeavyHoverTank::State_Torso_EvadeRight )
STATE ( "Torso_Strafe", rvMonsterHeavyHoverTank::State_Torso_Strafe )
END_CLASS_STATES
/*
================
rvMonsterHeavyHoverTank::State_Torso_BlasterAttack
================
*/
stateResult_t rvMonsterHeavyHoverTank::State_Torso_BlasterAttack ( const stateParms_t& parms ) {
enum {
STAGE_INIT,
STAGE_WAITSTART,
STAGE_LOOP,
STAGE_WAITLOOP,
STAGE_WAITEND
};
switch ( parms.stage ) {
case STAGE_INIT:
weaponStateIdeal = WEAPONSTATE_BLASTER;
if ( weaponStateIdeal != weaponStateCurrent ) {
PostAnimState ( ANIMCHANNEL_TORSO, "Torso_ChangeWeaponState", parms.blendFrames );
PostAnimState ( ANIMCHANNEL_TORSO, "Torso_BlasterAttack", parms.blendFrames );
return SRESULT_DONE;
}
shots = gameLocal.random.RandomInt ( 8 ) + 4;
blasterAnimIndex = (blasterAnimIndex + 1) % 2;
PlayAnim ( ANIMCHANNEL_TORSO, va("blaster_%d_preshoot", blasterAnimIndex + 1 ), parms.blendFrames );
return SRESULT_STAGE ( STAGE_WAITSTART );
case STAGE_WAITSTART:
if ( AnimDone ( ANIMCHANNEL_TORSO, 0 ) ) {
return SRESULT_STAGE ( STAGE_LOOP );
}
return SRESULT_WAIT;
case STAGE_LOOP:
PlayAnim ( ANIMCHANNEL_TORSO, va("blaster_%d_fire", blasterAnimIndex + 1 ), 0 );
return SRESULT_STAGE ( STAGE_WAITLOOP );
case STAGE_WAITLOOP:
if ( AnimDone ( ANIMCHANNEL_TORSO, 0 ) ) {
if ( --shots <= 0 ) {
PlayAnim ( ANIMCHANNEL_TORSO, va("blaster_%d_postshoot", blasterAnimIndex + 1 ), 0 );
return SRESULT_STAGE ( STAGE_WAITEND );
}
return SRESULT_STAGE ( STAGE_LOOP );
}
return SRESULT_WAIT;
case STAGE_WAITEND:
if ( AnimDone ( ANIMCHANNEL_TORSO, 4 ) ) {
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
/*
================
rvMonsterHeavyHoverTank::State_Torso_RocketAttack
================
*/
stateResult_t rvMonsterHeavyHoverTank::State_Torso_RocketAttack ( const stateParms_t& parms ) {
enum {
STAGE_INIT,
STAGE_WAITSTART,
STAGE_LOOP,
STAGE_WAITLOOP,
STAGE_WAITEND
};
switch ( parms.stage ) {
case STAGE_INIT:
weaponStateIdeal = WEAPONSTATE_ROCKET;
if ( weaponStateIdeal != weaponStateCurrent ) {
PostAnimState ( ANIMCHANNEL_TORSO, "Torso_ChangeWeaponState", parms.blendFrames );
PostAnimState ( ANIMCHANNEL_TORSO, "Torso_RocketAttack", parms.blendFrames );
return SRESULT_DONE;
}
DisableAnimState ( ANIMCHANNEL_LEGS );
PlayAnim ( ANIMCHANNEL_TORSO, "range_attack", parms.blendFrames );
return SRESULT_STAGE ( STAGE_WAITSTART );
case STAGE_WAITSTART:
if ( AnimDone ( ANIMCHANNEL_TORSO, 0 ) ) {
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
/*
================
rvMonsterHeavyHoverTank::State_Torso_ChangeWeaponState
================
*/
stateResult_t rvMonsterHeavyHoverTank::State_Torso_ChangeWeaponState ( const stateParms_t& parms ) {
static const char* stateAnims [ WEAPONSTATE_MAX ] [ WEAPONSTATE_MAX ] = {
{ NULL, "blaster_to_rocket" }, // WEAPONSTATE_BLASTER
{ "rocket_to_blaster", NULL }, // WEAPONSTATE_ROCKET
};
enum {
STAGE_INIT,
STAGE_WAIT
};
switch ( parms.stage ) {
case STAGE_INIT:
// No anim for that transition?
if ( stateAnims [ weaponStateCurrent ] [ weaponStateIdeal ] == NULL ) {
return SRESULT_DONE;
}
DisableAnimState ( ANIMCHANNEL_LEGS );
PlayAnim ( ANIMCHANNEL_TORSO, stateAnims [ weaponStateCurrent ] [ weaponStateIdeal ], parms.blendFrames );
return SRESULT_STAGE ( STAGE_WAIT );
case STAGE_WAIT:
if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
weaponStateCurrent = weaponStateIdeal;
switch ( weaponStateCurrent ) {
case WEAPONSTATE_ROCKET:
animPrefix = "rocket";
break;
default:
animPrefix = "";
break;
}
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvMonsterHeavyHoverTank::State_Torso_EvadeLeft ( const stateParms_t& parms ) {
enum {
STAGE_INIT,
STAGE_WAIT
};
switch ( parms.stage ) {
case STAGE_INIT:
{
idVec3 vel = GetPhysics()->GetLinearVelocity();
vel += viewAxis[1] * strafeSpeed;
physicsObj.UseVelocityMove( true );
GetPhysics()->SetLinearVelocity( vel );
PlayAnim ( ANIMCHANNEL_TORSO, "evade_left", parms.blendFrames );
}
return SRESULT_STAGE ( STAGE_WAIT );
case STAGE_WAIT:
if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvMonsterHeavyHoverTank::State_Torso_EvadeRight ( const stateParms_t& parms ) {
enum {
STAGE_INIT,
STAGE_WAIT
};
switch ( parms.stage ) {
case STAGE_INIT:
{
idVec3 vel = GetPhysics()->GetLinearVelocity();
vel += viewAxis[1] * -strafeSpeed;
physicsObj.UseVelocityMove( true );
GetPhysics()->SetLinearVelocity( vel );
PlayAnim ( ANIMCHANNEL_TORSO, "evade_right", parms.blendFrames );
}
return SRESULT_STAGE ( STAGE_WAIT );
case STAGE_WAIT:
if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvMonsterHeavyHoverTank::State_Torso_Strafe ( const stateParms_t& parms ) {
//fixme: trace first for visibility & obstruction?
if ( gameLocal.random.RandomFloat() > 0.5f ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_EvadeRight", parms.blendFrames );
} else {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_EvadeLeft", parms.blendFrames );
}
return SRESULT_DONE;
}