mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-21 05:02:28 +02:00
Quake 4 integrated with DoomRTX
This commit is contained in:
@@ -0,0 +1,531 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "../Game_local.h"
|
||||
|
||||
class rvMonsterIronMaiden : public idAI {
|
||||
public:
|
||||
|
||||
CLASS_PROTOTYPE( rvMonsterIronMaiden );
|
||||
|
||||
rvMonsterIronMaiden ( void );
|
||||
|
||||
void InitSpawnArgsVariables( void );
|
||||
void Spawn ( void );
|
||||
void Save ( idSaveGame *savefile ) const;
|
||||
void Restore ( idRestoreGame *savefile );
|
||||
|
||||
// Add some dynamic externals for debugging
|
||||
virtual void GetDebugInfo ( debugInfoProc_t proc, void* userData );
|
||||
|
||||
virtual int FilterTactical ( int availableTactical );
|
||||
|
||||
protected:
|
||||
|
||||
int phaseTime;
|
||||
jointHandle_t jointBansheeAttack;
|
||||
int enemyStunTime;
|
||||
|
||||
rvAIAction actionBansheeAttack;
|
||||
|
||||
virtual bool CheckActions ( void );
|
||||
|
||||
void PhaseOut ( void );
|
||||
void PhaseIn ( void );
|
||||
|
||||
virtual void OnDeath ( void );
|
||||
|
||||
private:
|
||||
|
||||
// Custom actions
|
||||
bool CheckAction_BansheeAttack ( rvAIAction* action, int animNum );
|
||||
bool PerformAction_PhaseOut ( void );
|
||||
bool PerformAction_PhaseIn ( void );
|
||||
|
||||
// Global States
|
||||
stateResult_t State_Phased ( const stateParms_t& parms );
|
||||
|
||||
// Torso States
|
||||
stateResult_t State_Torso_PhaseIn ( const stateParms_t& parms );
|
||||
stateResult_t State_Torso_PhaseOut ( const stateParms_t& parms );
|
||||
stateResult_t State_Torso_BansheeAttack ( const stateParms_t& parms );
|
||||
stateResult_t State_Torso_BansheeAttackDone ( const stateParms_t& parms );
|
||||
|
||||
// Frame commands
|
||||
stateResult_t Frame_PhaseIn ( const stateParms_t& parms );
|
||||
stateResult_t Frame_PhaseOut ( const stateParms_t& parms );
|
||||
stateResult_t Frame_BansheeAttack ( const stateParms_t& parms );
|
||||
stateResult_t Frame_EndBansheeAttack ( const stateParms_t& parms );
|
||||
|
||||
CLASS_STATES_PROTOTYPE ( rvMonsterIronMaiden );
|
||||
};
|
||||
|
||||
CLASS_DECLARATION( idAI, rvMonsterIronMaiden )
|
||||
END_CLASS
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::rvMonsterIronMaiden
|
||||
================
|
||||
*/
|
||||
rvMonsterIronMaiden::rvMonsterIronMaiden ( void ) {
|
||||
enemyStunTime = 0;
|
||||
phaseTime = 0;
|
||||
}
|
||||
|
||||
void rvMonsterIronMaiden::InitSpawnArgsVariables ( void ) {
|
||||
// Cache the mouth joint
|
||||
jointBansheeAttack = animator.GetJointHandle ( spawnArgs.GetString ( "joint_bansheeAttack", "mouth_effect" ) );
|
||||
}
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::Spawn
|
||||
================
|
||||
*/
|
||||
void rvMonsterIronMaiden::Spawn ( void ) {
|
||||
// Custom actions
|
||||
actionBansheeAttack.Init ( spawnArgs, "action_bansheeAttack", "Torso_BansheeAttack", AIACTIONF_ATTACK );
|
||||
|
||||
InitSpawnArgsVariables();
|
||||
|
||||
PlayEffect ( "fx_dress", animator.GetJointHandle ( spawnArgs.GetString ( "joint_laser", "cog_bone" ) ), true );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::Save
|
||||
================
|
||||
*/
|
||||
void rvMonsterIronMaiden::Save( idSaveGame *savefile ) const {
|
||||
savefile->WriteInt ( phaseTime );
|
||||
savefile->WriteInt ( enemyStunTime );
|
||||
|
||||
actionBansheeAttack.Save ( savefile );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::Restore
|
||||
================
|
||||
*/
|
||||
void rvMonsterIronMaiden::Restore( idRestoreGame *savefile ) {
|
||||
savefile->ReadInt ( phaseTime );
|
||||
savefile->ReadInt ( enemyStunTime );
|
||||
|
||||
actionBansheeAttack.Restore ( savefile );
|
||||
|
||||
InitSpawnArgsVariables();
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::FilterTactical
|
||||
================
|
||||
*/
|
||||
int rvMonsterIronMaiden::FilterTactical ( int availableTactical ) {
|
||||
// When hidden the iron maiden only uses ranged tactical
|
||||
if ( fl.hidden ) {
|
||||
availableTactical &= (AITACTICAL_RANGED_BIT | AITACTICAL_TURRET_BIT);
|
||||
}
|
||||
return idAI::FilterTactical( availableTactical );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::CheckAction_BansheeAttack
|
||||
================
|
||||
*/
|
||||
bool rvMonsterIronMaiden::CheckAction_BansheeAttack ( rvAIAction* action, int animNum ) {
|
||||
return CheckAction_RangedAttack ( action, animNum );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::PerformAction_PhaseIn
|
||||
================
|
||||
*/
|
||||
bool rvMonsterIronMaiden::PerformAction_PhaseIn ( void ) {
|
||||
if ( !phaseTime ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must be out for at least 3 seconds
|
||||
if ( gameLocal.time - phaseTime < 3000 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Phase in after 10 seconds or our movement is done
|
||||
if ( gameLocal.time - phaseTime > 10000 || move.fl.done ) {
|
||||
// Make sure we arent in something
|
||||
if ( CanBecomeSolid ( ) ) {
|
||||
PerformAction ( "Torso_PhaseIn", 4, true );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::PerformAction_PhaseOut
|
||||
================
|
||||
*/
|
||||
bool rvMonsterIronMaiden::PerformAction_PhaseOut ( void ) {
|
||||
// Little randomization
|
||||
if ( gameLocal.random.RandomFloat ( ) > 0.5f ) {
|
||||
return false;
|
||||
}
|
||||
if ( !enemyStunTime || gameLocal.time - enemyStunTime > 1500 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PerformAction ( "Torso_PhaseOut", 4, true );
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::CheckActions
|
||||
================
|
||||
*/
|
||||
bool rvMonsterIronMaiden::CheckActions ( void ) {
|
||||
// When phased the only available action is phase in
|
||||
if ( phaseTime ) {
|
||||
if ( PerformAction_PhaseIn ( ) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( PerformAction ( &actionBansheeAttack, (checkAction_t)&rvMonsterIronMaiden::CheckAction_BansheeAttack, &actionTimerRangedAttack ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( PerformAction_PhaseOut ( ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return idAI::CheckActions ( );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::PhaseOut
|
||||
================
|
||||
*/
|
||||
void rvMonsterIronMaiden::PhaseOut ( void ) {
|
||||
if ( phaseTime ) {
|
||||
return;
|
||||
}
|
||||
|
||||
rvClientEffect* effect;
|
||||
effect = PlayEffect ( "fx_phase", animator.GetJointHandle("cog_bone") );
|
||||
if ( effect ) {
|
||||
effect->Unbind ( );
|
||||
}
|
||||
|
||||
Hide ( );
|
||||
|
||||
move.fl.allowHiddenMove = true;
|
||||
|
||||
ProcessEvent ( &EV_BecomeNonSolid );
|
||||
|
||||
StopMove ( MOVE_STATUS_DONE );
|
||||
SetState ( "State_Phased" );
|
||||
|
||||
// Move away from here, to anywhere
|
||||
MoveOutOfRange ( this, 500.0f, 150.0f );
|
||||
|
||||
SetShaderParm ( 5, MS2SEC ( gameLocal.time ) );
|
||||
|
||||
phaseTime = gameLocal.time;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::PhaseIn
|
||||
================
|
||||
*/
|
||||
void rvMonsterIronMaiden::PhaseIn ( void ) {
|
||||
if ( !phaseTime ) {
|
||||
return;
|
||||
}
|
||||
|
||||
rvClientEffect* effect;
|
||||
effect = PlayEffect ( "fx_phase", animator.GetJointHandle("cog_bone") );
|
||||
if ( effect ) {
|
||||
effect->Unbind ( );
|
||||
}
|
||||
|
||||
if ( enemy.ent ) {
|
||||
TurnToward ( enemy.lastKnownPosition );
|
||||
}
|
||||
|
||||
ProcessEvent ( &AI_BecomeSolid );
|
||||
|
||||
Show ( );
|
||||
|
||||
// PlayEffect ( "fx_dress", animator.GetJointHandle ( "cog_bone" ), true );
|
||||
|
||||
phaseTime = 0;
|
||||
// Wait for the action that started the phase in to finish, then go back to combat
|
||||
SetState ( "Wait_Action" );
|
||||
PostState ( "State_Combat" );
|
||||
|
||||
SetShaderParm ( 5, MS2SEC ( gameLocal.time ) );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::OnDeath
|
||||
================
|
||||
*/
|
||||
void rvMonsterIronMaiden::OnDeath ( void ) {
|
||||
StopSound ( SND_CHANNEL_ITEM, false );
|
||||
|
||||
// Stop looping effects
|
||||
StopEffect ( "fx_banshee" );
|
||||
StopEffect ( "fx_dress" );
|
||||
|
||||
idAI::OnDeath( );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::GetDebugInfo
|
||||
================
|
||||
*/
|
||||
void rvMonsterIronMaiden::GetDebugInfo ( debugInfoProc_t proc, void* userData ) {
|
||||
// Base class first
|
||||
idAI::GetDebugInfo( proc, userData );
|
||||
|
||||
proc ( "idAI", "action_BansheeAttack", aiActionStatusString[actionBansheeAttack.status], userData );
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
States
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
CLASS_STATES_DECLARATION ( rvMonsterIronMaiden )
|
||||
STATE ( "State_Phased", rvMonsterIronMaiden::State_Phased )
|
||||
|
||||
STATE ( "Torso_PhaseOut", rvMonsterIronMaiden::State_Torso_PhaseOut )
|
||||
STATE ( "Torso_PhaseIn", rvMonsterIronMaiden::State_Torso_PhaseIn )
|
||||
STATE ( "Torso_BansheeAttack", rvMonsterIronMaiden::State_Torso_BansheeAttack )
|
||||
STATE ( "Torso_BansheeAttackDone", rvMonsterIronMaiden::State_Torso_BansheeAttackDone )
|
||||
|
||||
STATE ( "Frame_PhaseIn", rvMonsterIronMaiden::Frame_PhaseIn )
|
||||
STATE ( "Frame_PhaseOut", rvMonsterIronMaiden::Frame_PhaseOut )
|
||||
STATE ( "Frame_BansheeAttack", rvMonsterIronMaiden::Frame_BansheeAttack )
|
||||
STATE ( "Frame_EndBansheeAttack", rvMonsterIronMaiden::Frame_EndBansheeAttack )
|
||||
END_CLASS_STATES
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::State_Phased
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterIronMaiden::State_Phased ( const stateParms_t& parms ) {
|
||||
// If done moving and cant become solid here, move again
|
||||
if ( move.fl.done ) {
|
||||
if ( !CanBecomeSolid ( ) ) {
|
||||
MoveOutOfRange ( this, 300.0f, 150.0f );
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the enemy status up to date
|
||||
if ( !enemy.ent ) {
|
||||
CheckForEnemy ( true );
|
||||
}
|
||||
|
||||
// Make sure we keep facing our enemy
|
||||
if ( enemy.ent ) {
|
||||
TurnToward ( enemy.lastKnownPosition );
|
||||
}
|
||||
|
||||
// Make sure we are checking actions if we have no tactical move
|
||||
UpdateAction ( );
|
||||
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::State_Torso_PhaseIn
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterIronMaiden::State_Torso_PhaseIn ( const stateParms_t& parms ) {
|
||||
enum {
|
||||
STAGE_ANIM,
|
||||
STAGE_ANIM_WAIT,
|
||||
STAGE_PHASE,
|
||||
};
|
||||
switch ( parms.stage ) {
|
||||
case STAGE_ANIM:
|
||||
if ( enemy.ent ) {
|
||||
TurnToward ( enemy.lastKnownPosition );
|
||||
}
|
||||
DisableAnimState ( ANIMCHANNEL_LEGS );
|
||||
PlayAnim ( ANIMCHANNEL_TORSO, "phase_in", parms.blendFrames );
|
||||
return SRESULT_STAGE ( STAGE_ANIM_WAIT );
|
||||
|
||||
case STAGE_ANIM_WAIT:
|
||||
if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
return SRESULT_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::State_Torso_PhaseOut
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterIronMaiden::State_Torso_PhaseOut ( const stateParms_t& parms ) {
|
||||
enum {
|
||||
STAGE_ANIM,
|
||||
STAGE_ANIM_WAIT,
|
||||
};
|
||||
switch ( parms.stage ) {
|
||||
case STAGE_ANIM:
|
||||
DisableAnimState ( ANIMCHANNEL_LEGS );
|
||||
PlayAnim ( ANIMCHANNEL_TORSO, "phase_out", parms.blendFrames );
|
||||
return SRESULT_STAGE ( STAGE_ANIM_WAIT );
|
||||
|
||||
case STAGE_ANIM_WAIT:
|
||||
if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
return SRESULT_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::State_Torso_BansheeAttack
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterIronMaiden::State_Torso_BansheeAttack ( const stateParms_t& parms ) {
|
||||
enum {
|
||||
STAGE_ATTACK,
|
||||
STAGE_ATTACK_WAIT,
|
||||
};
|
||||
switch ( parms.stage ) {
|
||||
case STAGE_ATTACK:
|
||||
PlayAnim ( ANIMCHANNEL_TORSO, "banshee", parms.blendFrames );
|
||||
PostAnimState ( ANIMCHANNEL_TORSO, "Torso_BansheeAttackDone", 0, 0, SFLAG_ONCLEAR );
|
||||
return SRESULT_STAGE ( STAGE_ATTACK_WAIT );
|
||||
|
||||
case STAGE_ATTACK_WAIT:
|
||||
if ( enemy.ent ) {
|
||||
TurnToward ( enemy.lastKnownPosition );
|
||||
}
|
||||
if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
return SRESULT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::State_Torso_BansheeAttackDone
|
||||
|
||||
To ensure the movement is enabled after the banshee attack and that the effect is stopped this state
|
||||
will be posted to be run after the banshe attack finishes.
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterIronMaiden::State_Torso_BansheeAttackDone ( const stateParms_t& parms ) {
|
||||
Frame_EndBansheeAttack ( parms );
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::Frame_PhaseIn
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterIronMaiden::Frame_PhaseIn ( const stateParms_t& parms ) {
|
||||
PhaseIn ( );
|
||||
return SRESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::Frame_PhaseOut
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterIronMaiden::Frame_PhaseOut ( const stateParms_t& parms ) {
|
||||
PhaseOut ( );
|
||||
return SRESULT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::Frame_BansheeAttack
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterIronMaiden::Frame_BansheeAttack ( const stateParms_t& parms ) {
|
||||
idVec3 origin;
|
||||
idMat3 axis;
|
||||
idEntity* entities[ 1024 ];
|
||||
int count;
|
||||
int i;
|
||||
|
||||
// Get mouth origin
|
||||
GetJointWorldTransform ( jointBansheeAttack, gameLocal.time, origin, axis );
|
||||
|
||||
// Find all entities within the banshee attacks radius
|
||||
count = gameLocal.EntitiesWithinRadius ( origin, actionBansheeAttack.maxRange, entities, 1024 );
|
||||
for ( i = 0; i < count; i ++ ) {
|
||||
idEntity* ent = entities[i];
|
||||
if ( !ent || ent == this ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Must be an actor that takes damage to be affected
|
||||
if ( !ent->fl.takedamage || !ent->IsType ( idActor::GetClassType() ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Has to be within fov
|
||||
if ( !CheckFOV ( ent->GetEyePosition ( ) ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do some damage
|
||||
idVec3 dir;
|
||||
dir = origin = ent->GetEyePosition ( );
|
||||
dir.NormalizeFast ( );
|
||||
ent->Damage ( this, this, dir, spawnArgs.GetString ( "def_banshee_damage" ), 1.0f, 0 );
|
||||
|
||||
// Cache the last time we stunned our own enemy for the phase out
|
||||
if ( ent == enemy.ent ) {
|
||||
enemyStunTime = gameLocal.time;
|
||||
}
|
||||
}
|
||||
|
||||
return SRESULT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterIronMaiden::Frame_EndBansheeAttack
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterIronMaiden::Frame_EndBansheeAttack ( const stateParms_t& parms ) {
|
||||
StopEffect ( "fx_banshee" );
|
||||
return SRESULT_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user