mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-20 04:30:12 +02:00
Quake 4 integrated with DoomRTX
This commit is contained in:
@@ -0,0 +1,610 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "../Game_local.h"
|
||||
|
||||
class rvMonsterLightTank : public idAI {
|
||||
public:
|
||||
|
||||
CLASS_PROTOTYPE( rvMonsterLightTank );
|
||||
|
||||
rvMonsterLightTank ( void );
|
||||
|
||||
void Spawn ( void );
|
||||
void Save ( idSaveGame *savefile ) const;
|
||||
void Restore ( idRestoreGame *savefile );
|
||||
|
||||
virtual bool Pain ( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location );
|
||||
virtual int GetDamageForLocation ( int damage, int location );
|
||||
virtual void DamageFeedback ( idEntity *victim, idEntity *inflictor, int &damage );
|
||||
|
||||
protected:
|
||||
|
||||
virtual void OnStopMoving ( aiMoveCommand_t oldMoveCommand );
|
||||
|
||||
virtual bool CheckActions ( void );
|
||||
virtual void OnTacticalChange ( aiTactical_t oldTactical );
|
||||
|
||||
virtual bool UpdateRunStatus ( void );
|
||||
|
||||
virtual int FilterTactical ( int availableTactical );
|
||||
|
||||
int flamethrowerHealth;
|
||||
int chargeDebounce;
|
||||
void DestroyFlamethrower ( void );
|
||||
|
||||
private:
|
||||
|
||||
int standingMeleeNoAttackTime;
|
||||
bool damaged;
|
||||
// bool damagedMove;
|
||||
int powerUpStartTime;
|
||||
|
||||
rvAIAction actionFlameThrower;
|
||||
rvAIAction actionPowerUp;
|
||||
rvAIAction actionChargeAttack;
|
||||
|
||||
bool CheckAction_PowerUp ( rvAIAction* action, int animNum );
|
||||
virtual bool CheckAction_EvadeLeft ( rvAIAction* action, int animNum );
|
||||
virtual bool CheckAction_EvadeRight ( rvAIAction* action, int animNum );
|
||||
bool CheckAction_ChargeAttack ( rvAIAction* action, int animNum );
|
||||
|
||||
// Global States
|
||||
stateResult_t State_Killed ( const stateParms_t& parms );
|
||||
|
||||
// Torso States
|
||||
stateResult_t State_Torso_FlameThrower ( const stateParms_t& parms );
|
||||
stateResult_t State_Torso_FlameThrowerThink ( const stateParms_t& parms );
|
||||
stateResult_t State_Torso_Pain ( const stateParms_t& parms );
|
||||
stateResult_t State_Torso_RangedAttack ( const stateParms_t& parms );
|
||||
stateResult_t State_Torso_PowerUp ( const stateParms_t& parms );
|
||||
|
||||
rvScriptFuncUtility mPostWeaponDestroyed; // script to run after flamethrower is destroyed
|
||||
|
||||
CLASS_STATES_PROTOTYPE ( rvMonsterLightTank );
|
||||
};
|
||||
|
||||
CLASS_DECLARATION( idAI, rvMonsterLightTank )
|
||||
END_CLASS
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::rvMonsterLightTank
|
||||
================
|
||||
*/
|
||||
rvMonsterLightTank::rvMonsterLightTank ( void ) {
|
||||
damaged = false;
|
||||
standingMeleeNoAttackTime = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::Spawn
|
||||
================
|
||||
*/
|
||||
void rvMonsterLightTank::Spawn ( void ) {
|
||||
// damagedThreshold = spawnArgs.GetInt ( "health_damagedThreshold" );
|
||||
flamethrowerHealth = spawnArgs.GetInt ( "flamethrowerHealth", "160" );
|
||||
chargeDebounce = 0;
|
||||
|
||||
actionFlameThrower.Init ( spawnArgs, "action_flameThrower", "Torso_FlameThrower", AIACTIONF_ATTACK );
|
||||
actionPowerUp.Init ( spawnArgs, "action_powerup", "Torso_PowerUp", 0 );
|
||||
actionChargeAttack.Init ( spawnArgs, "action_chargeAttack", NULL, AIACTIONF_ATTACK );
|
||||
|
||||
const char *func;
|
||||
if ( spawnArgs.GetString( "script_postWeaponDestroyed", "", &func ) )
|
||||
{
|
||||
mPostWeaponDestroyed.Init( func );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::Save
|
||||
================
|
||||
*/
|
||||
void rvMonsterLightTank::Save ( idSaveGame *savefile ) const {
|
||||
savefile->WriteInt( flamethrowerHealth );
|
||||
savefile->WriteInt( chargeDebounce );
|
||||
|
||||
savefile->WriteBool( damaged );
|
||||
// savefile->WriteBool( damagedMove );
|
||||
savefile->WriteInt( powerUpStartTime );
|
||||
|
||||
actionFlameThrower.Save( savefile );
|
||||
actionPowerUp.Save( savefile );
|
||||
actionChargeAttack.Save( savefile );
|
||||
mPostWeaponDestroyed.Save( savefile );
|
||||
savefile->WriteInt( standingMeleeNoAttackTime );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::Restore
|
||||
================
|
||||
*/
|
||||
void rvMonsterLightTank::Restore ( idRestoreGame *savefile ) {
|
||||
savefile->ReadInt( flamethrowerHealth );
|
||||
savefile->ReadInt( chargeDebounce );
|
||||
|
||||
savefile->ReadBool( damaged );
|
||||
// savefile->ReadBool( damagedMove );
|
||||
savefile->ReadInt( powerUpStartTime );
|
||||
|
||||
actionFlameThrower.Restore( savefile );
|
||||
actionPowerUp.Restore( savefile );
|
||||
actionChargeAttack.Restore( savefile );
|
||||
mPostWeaponDestroyed.Restore( savefile );
|
||||
savefile->ReadInt( standingMeleeNoAttackTime );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::FilterTactical
|
||||
================
|
||||
*/
|
||||
int rvMonsterLightTank::FilterTactical ( int availableTactical ) {
|
||||
if ( flamethrowerHealth > 0 ) {
|
||||
// Only let the light tank use ranged tactical when he is really far from his enemy
|
||||
if ( !enemy.range || enemy.range < combat.attackRange[1] ) {
|
||||
availableTactical &= ~(AITACTICAL_RANGED_BITS);
|
||||
}
|
||||
}
|
||||
if ( chargeDebounce > gameLocal.GetTime() )
|
||||
{//don't charge again any time soon
|
||||
availableTactical &= ~(AITACTICAL_MELEE_BIT);
|
||||
}
|
||||
|
||||
return idAI::FilterTactical( availableTactical );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::OnTacticalChange
|
||||
|
||||
Enable/Disable the ranged attack based on whether the grunt needs it
|
||||
================
|
||||
*/
|
||||
void rvMonsterLightTank::OnTacticalChange ( aiTactical_t oldTactical ) {
|
||||
switch ( combat.tacticalCurrent ) {
|
||||
case AITACTICAL_MELEE:
|
||||
actionFlameThrower.fl.disabled = true;
|
||||
actionRangedAttack.fl.disabled = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
actionFlameThrower.fl.disabled = false;
|
||||
actionRangedAttack.fl.disabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::UpdateRunStatus
|
||||
================
|
||||
*/
|
||||
bool rvMonsterLightTank::UpdateRunStatus ( void ) {
|
||||
// If rushing, run
|
||||
if ( combat.tacticalCurrent == AITACTICAL_MELEE )
|
||||
{
|
||||
move.fl.idealRunning = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
move.fl.idealRunning = false;
|
||||
}
|
||||
|
||||
return move.fl.running != move.fl.idealRunning;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::DestroyFlamethrower
|
||||
================
|
||||
*/
|
||||
void rvMonsterLightTank::DestroyFlamethrower ( void ) {
|
||||
|
||||
StopEffect ( "fx_flame_muzzle" );
|
||||
//HideSurface ( "models/monsters/light_tank/flamethrower" );
|
||||
//GetAFPhysics()->GetBody ( "b_right_forearm" )->SetClipMask ( 0 );
|
||||
animator.CollapseJoint( animator.GetJointHandle( "r_smallShield_nadeLauncher" ), animator.GetJointHandle( "r_elbo" ) );
|
||||
animator.CollapseJoint( animator.GetJointHandle( "r_bigShield_nadeLauncher" ), animator.GetJointHandle( "r_elbo" ) );
|
||||
animator.CollapseJoint( animator.GetJointHandle( "r_gun_effect" ), animator.GetJointHandle( "r_elbo" ) );
|
||||
flamethrowerHealth = -1;
|
||||
|
||||
|
||||
pain.takenThisFrame = pain.threshold;
|
||||
pain.lastTakenTime = gameLocal.time;
|
||||
// flamethrowerDestroyedTime = gameLocal.GetTime();
|
||||
|
||||
// Tweak-out the AI to be more aggressive and more likely to charge?
|
||||
|
||||
PlayEffect ( "fx_destroy_arm", animator.GetJointHandle("r_elbo") );
|
||||
PlayEffect ( "fx_destroy_arm_trail", animator.GetJointHandle("r_elbo"), true );
|
||||
|
||||
DisableAnimState( ANIMCHANNEL_LEGS );
|
||||
painAnim = "damaged";
|
||||
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain" );
|
||||
PostAnimState( ANIMCHANNEL_TORSO, "Torso_Idle" );
|
||||
|
||||
chargeDebounce = 0;
|
||||
damaged = true;
|
||||
animPrefix = "damage";
|
||||
|
||||
actionFlameThrower.fl.disabled = true;
|
||||
actionRangedAttack.fl.disabled = true;
|
||||
|
||||
combat.attackRange[1] = 200;
|
||||
combat.aggressiveRange = 400;
|
||||
|
||||
spawnArgs.SetFloat( "action_meleeAttack_rate", 0.3f );
|
||||
actionMeleeAttack.Init( spawnArgs, "action_meleeAttack", NULL, AIACTIONF_ATTACK );
|
||||
actionMeleeAttack.failRate = 200;
|
||||
actionMeleeAttack.chance = 1.0f;
|
||||
|
||||
combat.tacticalMaskAvailable &= ~(AITACTICAL_RANGED_BITS);
|
||||
|
||||
actionMeleeAttack.timer.Reset( actionTime );
|
||||
actionChargeAttack.timer.Reset( actionTime );
|
||||
|
||||
ExecScriptFunction( mPostWeaponDestroyed );
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
rvMonsterLightTank::Pain
|
||||
=====================
|
||||
*/
|
||||
bool rvMonsterLightTank::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
|
||||
bool didPain = idAI::Pain( inflictor, attacker, damage, dir, location );
|
||||
if ( move.fl.moving && move.fl.running )
|
||||
{
|
||||
painAnim = "pain_charge";
|
||||
}
|
||||
return didPain;
|
||||
}
|
||||
/*
|
||||
=====================
|
||||
rvMonsterLightTank::GetDamageForLocation
|
||||
=====================
|
||||
*/
|
||||
int rvMonsterLightTank::GetDamageForLocation( int damage, int location ) {
|
||||
// If the flamethrower was hit only do damage to it
|
||||
if( !damaged && !aifl.dead )
|
||||
{
|
||||
if ( idStr::Icmp ( GetDamageGroup ( location ), "flamethrower" ) == 0 ) {
|
||||
// pain.takenThisFrame = damage;
|
||||
if ( flamethrowerHealth > 0 ){
|
||||
flamethrowerHealth -= damage;
|
||||
if ( flamethrowerHealth <= 0 ) {
|
||||
DestroyFlamethrower();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return idAI::GetDamageForLocation ( damage, location );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::DamageFeedback
|
||||
|
||||
callback function for when another entity recieved damage from this entity
|
||||
================
|
||||
*/
|
||||
void rvMonsterLightTank::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
|
||||
if ( !damaged )
|
||||
{
|
||||
if ( victim == GetEnemy() && inflictor == this )
|
||||
{
|
||||
if ( combat.tacticalCurrent == AITACTICAL_MELEE )
|
||||
{//okay, get out of melee state for now
|
||||
chargeDebounce = gameLocal.GetTime() + gameLocal.random.RandomInt(3000) + 3000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idAI::DamageFeedback( victim, inflictor, damage );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
rvMonsterLightTank::OnStopMoving
|
||||
============
|
||||
*/
|
||||
void rvMonsterLightTank::OnStopMoving ( aiMoveCommand_t oldMoveCommand ) {
|
||||
//MCG - once you get to your position, attack immediately (no pause)
|
||||
//FIXME: Restrict this some? Not after animmoves? Not if move was short? Only in certain tactical states?
|
||||
if ( GetEnemy() )
|
||||
{
|
||||
if ( combat.tacticalCurrent == AITACTICAL_RANGED )
|
||||
{
|
||||
actionRangedAttack.timer.Clear( actionTime );
|
||||
actionTimerRangedAttack.Clear( actionTime );
|
||||
actionFlameThrower.timer.Clear( actionTime );
|
||||
}
|
||||
else if ( combat.tacticalCurrent == AITACTICAL_MELEE )
|
||||
{//so we don't stand there and look stupid
|
||||
actionMeleeAttack.timer.Clear( actionTime );
|
||||
actionChargeAttack.timer.Clear( actionTime );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::CheckAction_PowerUp
|
||||
================
|
||||
*/
|
||||
bool rvMonsterLightTank::CheckAction_PowerUp ( rvAIAction* action, int animNum )
|
||||
{
|
||||
if ( !damaged && combat.tacticalCurrent == AITACTICAL_MELEE )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ( health > 20 || gameLocal.time - pain.lastTakenTime < 500 ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::CheckAction_EvadeLeft
|
||||
================
|
||||
*/
|
||||
bool rvMonsterLightTank::CheckAction_EvadeLeft ( rvAIAction* action, int animNum ) {
|
||||
if ( damaged || combat.tacticalCurrent == AITACTICAL_MELEE )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return idAI::CheckAction_EvadeLeft( action, animNum );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::CheckAction_EvadeRight
|
||||
================
|
||||
*/
|
||||
bool rvMonsterLightTank::CheckAction_EvadeRight ( rvAIAction* action, int animNum ) {
|
||||
if ( damaged || combat.tacticalCurrent == AITACTICAL_MELEE )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return idAI::CheckAction_EvadeRight( action, animNum );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::CheckAction_ChargeAttack
|
||||
================
|
||||
*/
|
||||
bool rvMonsterLightTank::CheckAction_ChargeAttack ( rvAIAction* action, int animNum ) {
|
||||
if ( !enemy.ent || !enemy.fl.inFov ) {
|
||||
return false;
|
||||
}
|
||||
if ( !CheckFOV ( enemy.ent->GetPhysics()->GetOrigin(), 10 ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( damaged || idStr::Icmp( "run", animator.CurrentAnim(ANIMCHANNEL_TORSO)->AnimName() ) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::CheckActions
|
||||
================
|
||||
*/
|
||||
bool rvMonsterLightTank::CheckActions ( void ) {
|
||||
if ( PerformAction ( &actionFlameThrower, (checkAction_t)&idAI::CheckAction_RangedAttack ) ) {
|
||||
return true;
|
||||
}
|
||||
if ( PerformAction ( &actionChargeAttack, (checkAction_t)&rvMonsterLightTank::CheckAction_ChargeAttack ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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!
|
||||
//allow ranged attack
|
||||
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
|
||||
actionFlameThrower.fl.disabled = false;
|
||||
actionRangedAttack.fl.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( PerformAction ( &actionRangedAttack,(checkAction_t)&idAI::CheckAction_RangedAttack, &actionTimerRangedAttack ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ( PerformAction ( &actionPowerUp, (checkAction_t)&rvMonsterLightTank::CheckAction_PowerUp ) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
States
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
CLASS_STATES_DECLARATION ( rvMonsterLightTank )
|
||||
STATE ( "State_Killed", rvMonsterLightTank::State_Killed )
|
||||
|
||||
STATE ( "Torso_FlameThrower", rvMonsterLightTank::State_Torso_FlameThrower )
|
||||
STATE ( "Torso_FlameThrowerThink", rvMonsterLightTank::State_Torso_FlameThrowerThink )
|
||||
STATE ( "Torso_Pain", rvMonsterLightTank::State_Torso_Pain )
|
||||
STATE ( "Torso_RangedAttack", rvMonsterLightTank::State_Torso_RangedAttack )
|
||||
STATE ( "Torso_PowerUp", rvMonsterLightTank::State_Torso_PowerUp )
|
||||
|
||||
END_CLASS_STATES
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::State_Killed
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterLightTank::State_Killed ( const stateParms_t& parms ) {
|
||||
StopEffect ( "fx_destroy_arm_trail" );
|
||||
StopEffect ( "fx_flame_muzzle" );
|
||||
return idAI::State_Killed ( parms );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::State_Torso_FlameThrower
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterLightTank::State_Torso_FlameThrower ( const stateParms_t& parms ) {
|
||||
DisableAnimState ( ANIMCHANNEL_LEGS );
|
||||
|
||||
// Flame effect
|
||||
PlayEffect ( "fx_flame_muzzle", animator.GetJointHandle ( "gun_effect" ), true );
|
||||
|
||||
// Loop the flame animation
|
||||
PlayAnim( ANIMCHANNEL_TORSO, "flamethrower", parms.blendFrames );
|
||||
|
||||
// Delay start the flame thrower think to ensure he flames for a minimum time
|
||||
PostAnimState ( ANIMCHANNEL_TORSO, "Torso_FlameThrowerThink", 0, 500 );
|
||||
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::State_Torso_FlameThrowerThink
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterLightTank::State_Torso_FlameThrowerThink ( const stateParms_t& parms ) {
|
||||
if ( !enemy.fl.inFov || AnimDone ( ANIMCHANNEL_TORSO, 4 ) ) {
|
||||
StopEffect ( "fx_flame_muzzle" );
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::State_Torso_Pain
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterLightTank::State_Torso_Pain ( const stateParms_t& parms ) {
|
||||
|
||||
StopEffect ( "fx_flame_muzzle" );
|
||||
|
||||
// Default pain animation
|
||||
return idAI::State_Torso_Pain ( parms );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::State_Torso_RangedAttack
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterLightTank::State_Torso_RangedAttack ( const stateParms_t& parms ) {
|
||||
enum {
|
||||
STAGE_START,
|
||||
STAGE_FINISH,
|
||||
};
|
||||
switch ( parms.stage ) {
|
||||
case STAGE_START:
|
||||
// If moving switch to the moving ranged attack (torso only)
|
||||
if ( !move.fl.moving || !FacingIdeal() ) {
|
||||
// Full body animations
|
||||
DisableAnimState ( ANIMCHANNEL_LEGS );
|
||||
PlayAnim ( ANIMCHANNEL_TORSO, "range_megaattack", parms.blendFrames );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayAnim ( ANIMCHANNEL_TORSO, "range_attack_torso", parms.blendFrames );
|
||||
}
|
||||
|
||||
return SRESULT_STAGE ( STAGE_FINISH );
|
||||
|
||||
case STAGE_FINISH:
|
||||
if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
return SRESULT_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
rvMonsterLightTank::State_Torso_PowerUp
|
||||
================
|
||||
*/
|
||||
stateResult_t rvMonsterLightTank::State_Torso_PowerUp ( const stateParms_t& parms ) {
|
||||
enum {
|
||||
STAGE_START,
|
||||
STAGE_START_WAIT,
|
||||
STAGE_LOOP,
|
||||
STAGE_FINISH,
|
||||
};
|
||||
switch ( parms.stage ) {
|
||||
case STAGE_START:
|
||||
// If moving switch to the moving ranged attack (torso only)
|
||||
//fl.takedamage = false;
|
||||
powerUpStartTime = gameLocal.GetTime();
|
||||
DisableAnimState ( ANIMCHANNEL_LEGS );
|
||||
PlayAnim ( ANIMCHANNEL_TORSO, "powerup_start", parms.blendFrames );
|
||||
|
||||
return SRESULT_STAGE ( STAGE_START_WAIT );
|
||||
|
||||
case STAGE_START_WAIT:
|
||||
if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
|
||||
PlayCycle( ANIMCHANNEL_TORSO, "powerup_loop", parms.blendFrames );
|
||||
return SRESULT_STAGE ( STAGE_LOOP );
|
||||
}
|
||||
return SRESULT_WAIT;
|
||||
|
||||
case STAGE_LOOP:
|
||||
health++;
|
||||
if ( health >= spawnArgs.GetInt( "health" )
|
||||
|| gameLocal.GetTime() - powerUpStartTime > 3875 )
|
||||
{//full health or been charging up for 3 full anim loops
|
||||
PlayAnim ( ANIMCHANNEL_TORSO, "powerup_end", parms.blendFrames );
|
||||
return SRESULT_STAGE ( STAGE_FINISH );
|
||||
}
|
||||
return SRESULT_WAIT;
|
||||
|
||||
case STAGE_FINISH:
|
||||
if ( AnimDone ( ANIMCHANNEL_TORSO, parms.blendFrames ) ) {
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
return SRESULT_ERROR;
|
||||
}
|
||||
Reference in New Issue
Block a user