/* ================ Monster_Fatguy.cpp AI for the fat guy on the putra level ================ */ #include "../../idlib/precompiled.h" #pragma hdrstop #include "../Game_local.h" #include "../Projectile.h" typedef struct monsterFattyChain_s { jointHandle_t orientationJoint; jointHandle_t attackJoint; bool out; idEntityPtr projectile; } monsterFattyChain_t; class rvMonsterFatty : public idAI { public: CLASS_PROTOTYPE( rvMonsterFatty ); rvMonsterFatty ( void ) {} ~rvMonsterFatty ( void ); void InitSpawnArgsVariables ( void ); void Spawn ( void ); void Save ( idSaveGame *savefile ) const; void Restore ( idRestoreGame *savefile ); virtual bool UpdateAnimationControllers ( void ); virtual bool CanPlayImpactEffect ( idEntity* attacker, idEntity* target ) { return false; }; virtual void AddDamageEffect ( const trace_t &collision, const idVec3 &velocity, const char *damageDefName, idEntity* inflictor ); protected: rvAIAction actionWhipAttack; enum { CHAIN_LEFT, CHAIN_RIGHT, CHAIN_MAX }; monsterFattyChain_t chains[CHAIN_MAX]; float missHeight; virtual bool CheckActions ( void ); void PlayAttackAnim ( const idVec3& target, int blendFrames ); void ResetAllChains ( void ); void ChainIn ( int chain ); void ChainOut ( int chain ); private: // Custom actions bool CheckAction_WhipAttack ( rvAIAction* action, int animNum ); // Frame Commands stateResult_t Frame_LeftChainOut ( const stateParms_t& parms ); stateResult_t Frame_LeftChainIn ( const stateParms_t& parms ); stateResult_t Frame_RightChainOut ( const stateParms_t& parms ); stateResult_t Frame_RightChainIn ( const stateParms_t& parms ); // Torso States stateResult_t State_Torso_WhipAttack ( const stateParms_t& parms ); CLASS_STATES_PROTOTYPE ( rvMonsterFatty ); }; CLASS_DECLARATION( idAI, rvMonsterFatty ) END_CLASS /* ================ rvMonsterFatty::~rvMonsterFatty ================ */ rvMonsterFatty::~rvMonsterFatty ( void ) { ResetAllChains ( ); } void rvMonsterFatty::InitSpawnArgsVariables ( void ) { chains[CHAIN_LEFT].orientationJoint = animator.GetJointHandle ( spawnArgs.GetString ( "joint_leftChain", "chainb1" ) ); chains[CHAIN_LEFT].attackJoint = animator.GetJointHandle ( spawnArgs.GetString ( "joint_leftChainAttack", "hookb" ) ); chains[CHAIN_RIGHT].orientationJoint = animator.GetJointHandle ( spawnArgs.GetString ( "joint_rightChain", "chaina1" ) ); chains[CHAIN_RIGHT].attackJoint = animator.GetJointHandle ( spawnArgs.GetString ( "joint_rightChainAttack", "hooka" ) ); missHeight = spawnArgs.GetFloat ( "missHeight", "72" ); } /* ================ rvMonsterFatty::Spawn ================ */ void rvMonsterFatty::Spawn ( void ) { // Custom actions actionWhipAttack.Init ( spawnArgs, "action_whipAttack", "Torso_WhipAttack", AIACTIONF_ATTACK ); // Cache joints chains[CHAIN_LEFT].out = false; chains[CHAIN_LEFT].projectile = NULL; chains[CHAIN_RIGHT].out = false; chains[CHAIN_RIGHT].projectile = NULL; InitSpawnArgsVariables(); } /* ================ rvMonsterFatty::Save ================ */ void rvMonsterFatty::Save ( idSaveGame *savefile ) const { int i; actionWhipAttack.Save( savefile ); for ( i = 0; i < CHAIN_MAX; i ++ ) { savefile->WriteBool( chains[i].out ); chains[i].projectile.Save ( savefile ); } } /* ================ rvMonsterFatty::Restore ================ */ void rvMonsterFatty::Restore ( idRestoreGame *savefile ) { int i; actionWhipAttack.Restore( savefile ); for ( i = 0; i < CHAIN_MAX; i ++ ) { savefile->ReadBool( chains[i].out ); chains[i].projectile.Restore ( savefile ); } InitSpawnArgsVariables(); } /* ================ rvMonsterFatty::UpdateAnimationControllers ================ */ bool rvMonsterFatty::UpdateAnimationControllers ( void ) { if ( !idAI::UpdateAnimationControllers ( ) ) { return false; } if ( enemy.ent && CheckFOV ( enemy.lastKnownPosition ) ) { int i; idVec3 origin; idMat3 axis; idVec3 dir; idVec3 localDir; idVec3 target; if ( enemy.ent->IsType ( idActor::GetClassType ( ) ) ) { target = static_cast(enemy.ent.GetEntity())->GetEyePosition ( ); } else { target = enemy.ent->GetPhysics()->GetOrigin ( ); } if ( !IsEnemyVisible ( ) ) { target -= enemy.ent->GetPhysics()->GetGravityNormal() * missHeight; } for ( i = 0; i < CHAIN_MAX; i ++ ) { if ( !chains[i].out ) { continue; } animator.ClearJoint ( chains[i].orientationJoint ); GetJointWorldTransform ( chains[i].orientationJoint, gameLocal.time, origin, axis ); dir = target - origin; dir.Normalize ( ); axis.ProjectVector ( dir, localDir ); animator.SetJointAxis ( chains[i].orientationJoint, JOINTMOD_LOCAL, localDir.ToMat3() ); } } return true; } /* ================ rvMonsterFatty::AddDamageEffect ================ */ void rvMonsterFatty::AddDamageEffect ( const trace_t &collision, const idVec3 &velocity, const char *damageDefName, idEntity* inflictor ) { // If there are still shields remaining then play a shield effect at the impact point /* idVec3 dir; dir = collision.c.point - GetPhysics()->GetCenterMass (); PlayEffect ( "fx_shield", collision.c.point, dir.ToMat3(), false, vec3_origin, true ); */ } /* ================ rvMonsterFatty::CheckAction_WhipAttack ================ */ bool rvMonsterFatty::CheckAction_WhipAttack ( rvAIAction* action, int animNum ) { if ( !enemy.ent ) { return false; } return true; } /* ================ rvMonsterFatty::CheckActions ================ */ bool rvMonsterFatty::CheckActions ( void ) { if ( PerformAction ( &actionWhipAttack, (checkAction_t)&rvMonsterFatty::CheckAction_WhipAttack, NULL ) ) { return true; } return idAI::CheckActions ( ); } /* ================ rvMonsterFatty::PlayAttackAnim ================ */ void rvMonsterFatty::PlayAttackAnim ( const idVec3& target, int blendFrames ) { idVec3 dir; idVec3 localDir; float yaw; const char* animName; // Get the local direction vector dir = target - GetPhysics()->GetOrigin(); dir.Normalize ( ); viewAxis.ProjectVector( dir, localDir ); // Get the yaw relative to forward yaw = idMath::AngleNormalize180 ( localDir.ToAngles ( )[YAW] ); if ( yaw < -45.0f ) { animName = "attack4b"; } else if ( yaw < -20.0f ) { animName = "attack3b"; } else if ( yaw < -5.0f ) { animName = "attack5r"; } else if ( yaw < 5.0f ) { animName = "attack5"; } else if ( yaw < 20.0f ) { animName = "attack5l"; } else if ( yaw < 45.0f ) { animName = "attack2b"; } else{ animName = "attack1b"; } PlayAnim ( ANIMCHANNEL_TORSO, animName, blendFrames ); } /* ================ rvMonsterFatty::ChainOut ================ */ void rvMonsterFatty::ChainOut ( int chain ) { idEntity* ent; idProjectile* proj; gameLocal.SpawnEntityDef( *gameLocal.FindEntityDefDict ( spawnArgs.GetString ( "def_attack_hook" ) ), &ent, false ); proj = dynamic_cast(ent); if ( !proj ) { delete ent; return; } chains[chain].out = true; proj->Create ( this, vec3_origin, idVec3(0,0,1) ); proj->Launch ( vec3_origin, idVec3(0,0,1), vec3_origin ); chains[chain].projectile = proj; ent->BindToJoint ( this, chains[chain].attackJoint, false ); ent->SetOrigin ( vec3_origin ); ent->SetAxis ( mat3_identity ); } /* ================ rvMonsterFatty::ChainIn ================ */ void rvMonsterFatty::ChainIn ( int chain ) { chains[chain].out = false; if ( chains[chain].projectile ) { delete chains[chain].projectile; chains[chain].projectile = NULL; } } /* ================ rvMonsterFatty::ResetChains ================ */ void rvMonsterFatty::ResetAllChains ( void ) { int i; animator.ClearAllJoints ( ); for ( i = 0; i < CHAIN_MAX; i ++ ) { ChainIn ( i ); } } /* =============================================================================== States =============================================================================== */ CLASS_STATES_DECLARATION ( rvMonsterFatty ) STATE ( "Torso_WhipAttack", rvMonsterFatty::State_Torso_WhipAttack ) STATE ( "Frame_LeftChainOut", rvMonsterFatty::Frame_LeftChainOut ) STATE ( "Frame_LeftChainIn", rvMonsterFatty::Frame_LeftChainIn ) STATE ( "Frame_RightChainOut", rvMonsterFatty::Frame_RightChainOut ) STATE ( "Frame_RightChainIn", rvMonsterFatty::Frame_RightChainIn ) END_CLASS_STATES /* ================ rvMonsterFatty::State_Torso_WhipAttack ================ */ stateResult_t rvMonsterFatty::State_Torso_WhipAttack ( const stateParms_t& parms ) { enum { STAGE_ATTACK, STAGE_ATTACK_WAIT, }; switch ( parms.stage ) { case STAGE_ATTACK: { if ( !enemy.ent ) { return SRESULT_DONE; } // Predict a bit PlayAttackAnim ( enemy.ent->GetEyePosition(), parms.blendFrames ); return SRESULT_STAGE ( STAGE_ATTACK_WAIT ); } case STAGE_ATTACK_WAIT: if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) { ResetAllChains ( ); return SRESULT_DONE; } return SRESULT_WAIT; } return SRESULT_ERROR; } /* ================ rvMonsterFatty::Frame_LeftChainOut ================ */ stateResult_t rvMonsterFatty::Frame_LeftChainOut ( const stateParms_t& parms ) { ChainOut ( CHAIN_LEFT ); return SRESULT_OK; } /* ================ rvMonsterFatty::Frame_LeftChainIn ================ */ stateResult_t rvMonsterFatty::Frame_LeftChainIn ( const stateParms_t& parms ) { ChainIn ( CHAIN_LEFT ); return SRESULT_OK; } /* ================ rvMonsterFatty::Frame_RightChainOut ================ */ stateResult_t rvMonsterFatty::Frame_RightChainOut ( const stateParms_t& parms ) { ChainOut ( CHAIN_RIGHT ); return SRESULT_OK; } /* ================ rvMonsterFatty::Frame_RightChainIn ================ */ stateResult_t rvMonsterFatty::Frame_RightChainIn ( const stateParms_t& parms ) { ChainIn ( CHAIN_RIGHT ); return SRESULT_OK; }