mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-16 08:10:32 +02:00
1175 lines
39 KiB
C++
1175 lines
39 KiB
C++
// AI_monster_base.cpp
|
|
// Native state-machine replacement for ai_monster_base.script.
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
#include "../Game_local.h"
|
|
|
|
|
|
|
|
rvmMonsterBase::rvmMonsterBase() {
|
|
run_distance = 0.0f;
|
|
walk_turn = 360.0f;
|
|
run = false;
|
|
ambush = false;
|
|
ignoreEnemies = false;
|
|
stay_on_attackpath = false;
|
|
idle_sight_fov = true;
|
|
customBlendOut = 0.0f;
|
|
current_path = NULL;
|
|
next_path = NULL;
|
|
resurrect = false;
|
|
ignore_lostcombat = false;
|
|
blocked = false;
|
|
ignore_sight = false;
|
|
}
|
|
|
|
void rvmMonsterBase::Init( idAI* aiOwner ) {
|
|
rvmAIObject::Init( aiOwner );
|
|
ignoreEnemies = false;
|
|
run_distance = 0.0f;
|
|
walk_turn = 360.0f;
|
|
ambush = GetIntKey( "ambush" ) != 0;
|
|
ignore_lostcombat = GetIntKey( "ignore_lostcombat" ) != 0;
|
|
stay_on_attackpath = GetIntKey( "stay_on_attackpath" ) != 0;
|
|
idle_sight_fov = true;
|
|
run = false;
|
|
blocked = false;
|
|
ignore_sight = false;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::RunStateByName( const char* stateName, stateParms_t* parms ) {
|
|
AI_DISPATCH_STATE( "Torso_CustomCycle", Torso_CustomCycle );
|
|
AI_DISPATCH_STATE( "Torso_CustomAnim", Torso_CustomAnim );
|
|
AI_DISPATCH_STATE( "Torso_Sight", Torso_Sight );
|
|
AI_DISPATCH_STATE( "Torso_Death", Torso_Death );
|
|
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
|
|
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
|
|
AI_DISPATCH_STATE( "Legs_Death", Legs_Death );
|
|
AI_DISPATCH_STATE( "state_Begin", state_Begin );
|
|
AI_DISPATCH_STATE( "state_Spawner", state_Spawner );
|
|
AI_DISPATCH_STATE( "state_Resurrect", state_Resurrect );
|
|
AI_DISPATCH_STATE( "state_Killed", state_Killed );
|
|
AI_DISPATCH_STATE( "state_Dead", state_Dead );
|
|
AI_DISPATCH_STATE( "state_Combat", state_Combat );
|
|
AI_DISPATCH_STATE( "state_LostCombat", state_LostCombat );
|
|
AI_DISPATCH_STATE( "state_FollowAlternatePath", state_FollowAlternatePath );
|
|
AI_DISPATCH_STATE( "monster_begin", monster_begin );
|
|
AI_DISPATCH_STATE( "wait_for_enemy", wait_for_enemy );
|
|
AI_DISPATCH_STATE( "sight_enemy", sight_enemy );
|
|
AI_DISPATCH_STATE( "walk_on_trigger", walk_on_trigger );
|
|
AI_DISPATCH_STATE( "wake_on_trigger", wake_on_trigger );
|
|
AI_DISPATCH_STATE( "wake_on_enemy", wake_on_enemy );
|
|
AI_DISPATCH_STATE( "wake_on_attackcone", wake_on_attackcone );
|
|
AI_DISPATCH_STATE( "idle_followPathEntities", idle_followPathEntities );
|
|
AI_DISPATCH_STATE( "path_corner", path_corner );
|
|
AI_DISPATCH_STATE( "path_anim", path_anim );
|
|
AI_DISPATCH_STATE( "path_cycleanim", path_cycleanim );
|
|
AI_DISPATCH_STATE( "path_turn", path_turn );
|
|
AI_DISPATCH_STATE( "path_wait", path_wait );
|
|
AI_DISPATCH_STATE( "path_waitfortrigger", path_waitfortrigger );
|
|
AI_DISPATCH_STATE( "path_hide", path_hide );
|
|
AI_DISPATCH_STATE( "path_show", path_show );
|
|
AI_DISPATCH_STATE( "path_attack", path_attack );
|
|
AI_DISPATCH_STATE( "combat_wander", combat_wander );
|
|
AI_DISPATCH_STATE( "combat_turret_node", combat_turret_node );
|
|
AI_DISPATCH_STATE( "combat_attack_cone", combat_attack_cone );
|
|
AI_DISPATCH_STATE( "combat_ainode", combat_ainode );
|
|
return rvmAIObject::RunStateByName( stateName, parms );
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::Torso_CustomCycle( stateParms_t* parms ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::Torso_CustomAnim( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_WAIT };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
parms->stage = STAGE_WAIT;
|
|
return SRESULT_WAIT;
|
|
case STAGE_WAIT:
|
|
if ( AnimDone( ANIMCHANNEL_TORSO, idMath::Ftoi( customBlendOut ) ) ) {
|
|
FinishAction( "customAnim" );
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", idMath::Ftoi( customBlendOut ) );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::Torso_Sight( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_WAIT };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START: {
|
|
if ( !GetIntKey( "walk_on_sight" ) ) {
|
|
OverrideAnim( ANIMCHANNEL_LEGS );
|
|
}
|
|
const char* animname = GetKey( "on_activate" );
|
|
if ( animname && animname[0] && HasAnim( ANIMCHANNEL_TORSO, animname ) ) {
|
|
PlayAnim( ANIMCHANNEL_TORSO, animname );
|
|
parms->stage = STAGE_WAIT;
|
|
return SRESULT_WAIT;
|
|
}
|
|
FinishAction( "sight" );
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", GetIntKey( "on_activate_blend", "4" ) );
|
|
return SRESULT_DONE;
|
|
}
|
|
case STAGE_WAIT:
|
|
if ( AnimDone( ANIMCHANNEL_TORSO, GetIntKey( "on_activate_blend", "4" ) ) ) {
|
|
FinishAction( "sight" );
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", GetIntKey( "on_activate_blend", "4" ) );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::Torso_Death( stateParms_t* parms ) {
|
|
FinishAction( "dead" );
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::Torso_Idle( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
IdleAnim( ANIMCHANNEL_TORSO, "idle" );
|
|
parms->stage = 1;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::Legs_Idle( stateParms_t* parms ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::Legs_Death( stateParms_t* parms ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::state_Begin( stateParms_t* parms ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::state_Spawner( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_LOOP };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
HideOwner();
|
|
ClearActivated();
|
|
parms->stage = STAGE_LOOP;
|
|
return SRESULT_WAIT;
|
|
case STAGE_LOOP:
|
|
if ( Activated() && CanBecomeSolid() ) {
|
|
ClearActivated();
|
|
// Spawn-copy behavior is project-specific; keep this state non-blocking.
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::monster_begin( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_HIDDEN_WAIT_TRIGGER, STAGE_HIDDEN_WAIT_SOLID, STAGE_DELAY, STAGE_WAKE, STAGE_SIGHT };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
run = false;
|
|
ignore_sight = GetIntKey( "no_sight" ) != 0;
|
|
if ( !GetIntKey( "ignore_flashlight" ) ) {
|
|
WakeOnFlashlight( true );
|
|
}
|
|
if ( GetIntKey( "hide" ) ) {
|
|
HideOwner();
|
|
ClearActivated();
|
|
parms->stage = STAGE_HIDDEN_WAIT_TRIGGER;
|
|
return SRESULT_WAIT;
|
|
}
|
|
scratchEndTime = DelayTime( GetFloatKey( "wait" ) );
|
|
parms->stage = STAGE_DELAY;
|
|
return SRESULT_WAIT;
|
|
|
|
case STAGE_HIDDEN_WAIT_TRIGGER:
|
|
if ( !Activated() && !Pain() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
ClearActivated();
|
|
parms->stage = STAGE_HIDDEN_WAIT_SOLID;
|
|
return SRESULT_WAIT;
|
|
|
|
case STAGE_HIDDEN_WAIT_SOLID:
|
|
if ( !CanBecomeSolid() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
ShowOwner();
|
|
scratchEndTime = DelayTime( GetFloatKey( "wait" ) );
|
|
parms->stage = STAGE_DELAY;
|
|
return SRESULT_WAIT;
|
|
|
|
case STAGE_DELAY:
|
|
if ( Time() < scratchEndTime ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( idEntity* enemyEnt = GetEntityKey( "enemy" ) ) {
|
|
SetEnemy( enemyEnt );
|
|
}
|
|
parms->stage = STAGE_WAKE;
|
|
return SRESULT_WAIT;
|
|
|
|
case STAGE_WAKE:
|
|
if ( GetEnemy() || Pain() || Activated() || checkForEnemy( idle_sight_fov ) ) {
|
|
trigger_wakeup_targets();
|
|
ignore_sight = false;
|
|
WakeOnFlashlight( false );
|
|
waitActionName = "sight";
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Sight", 4 );
|
|
parms->stage = STAGE_SIGHT;
|
|
}
|
|
return SRESULT_WAIT;
|
|
|
|
case STAGE_SIGHT:
|
|
if ( !ActionDone( "sight" ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::state_Resurrect( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_SOLID };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
if ( owner ) {
|
|
owner->AI_DEAD = false;
|
|
}
|
|
StopMove();
|
|
HideOwner();
|
|
if ( owner ) {
|
|
owner->StopRagdoll();
|
|
// restorePosition() was a DoomScript helper; this idAI branch has no
|
|
// public RestorePosition() native. StopRagdoll() is the available
|
|
// native restore hook here.
|
|
}
|
|
parms->stage = STAGE_SOLID;
|
|
return SRESULT_WAIT;
|
|
case STAGE_SOLID:
|
|
if ( !CanBecomeSolid() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( owner ) {
|
|
owner->SetHealth( GetFloatKey( "health", "1" ) );
|
|
owner->TurnTo( GetFloatKey( "angle" ) );
|
|
owner->ClearBurn();
|
|
owner->AllowDamage();
|
|
}
|
|
SetState( "state_Begin" );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::wait_for_enemy( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_LOOP };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
if ( owner ) {
|
|
owner->AI_PAIN = false;
|
|
}
|
|
StopMove();
|
|
parms->stage = STAGE_LOOP;
|
|
return SRESULT_WAIT;
|
|
case STAGE_LOOP:
|
|
if ( Pain() || GetEnemy() || checkForEnemy( idle_sight_fov ) ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::state_Killed( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_WAIT_DEAD_ACTION };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
StopMove();
|
|
waitActionName = "dead";
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 0 );
|
|
parms->stage = STAGE_WAIT_DEAD_ACTION;
|
|
return SRESULT_WAIT;
|
|
case STAGE_WAIT_DEAD_ACTION:
|
|
if ( !ActionDone( "dead" ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
SetState( "state_Dead" );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::state_Dead( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_BURN_DELAY, STAGE_REMOVE_DELAY, STAGE_RESURRECT_HOLD };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
scratchEndTime = DelayTime( GetFloatKey( "burnaway" ) );
|
|
if ( GetFloatKey( "burnaway" ) > 0.0f && owner ) {
|
|
owner->PreBurn();
|
|
parms->stage = STAGE_BURN_DELAY;
|
|
} else {
|
|
scratchEndTime = DelayTime( 3.0f );
|
|
parms->stage = STAGE_REMOVE_DELAY;
|
|
}
|
|
return SRESULT_WAIT;
|
|
case STAGE_BURN_DELAY:
|
|
if ( Time() < scratchEndTime ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( owner ) {
|
|
owner->Burn();
|
|
StartSoundKey( "snd_burn", SND_CHANNEL_BODY );
|
|
}
|
|
scratchEndTime = DelayTime( 3.0f );
|
|
parms->stage = STAGE_REMOVE_DELAY;
|
|
return SRESULT_WAIT;
|
|
case STAGE_REMOVE_DELAY:
|
|
if ( Time() < scratchEndTime ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( resurrect ) {
|
|
HideOwner();
|
|
if ( owner ) {
|
|
owner->StopRagdoll();
|
|
// restorePosition() was a DoomScript helper; this idAI branch has no
|
|
// public RestorePosition() native. StopRagdoll() is the available
|
|
// native restore hook here.
|
|
}
|
|
parms->stage = STAGE_RESURRECT_HOLD;
|
|
return SRESULT_WAIT;
|
|
}
|
|
RemoveOwner();
|
|
return SRESULT_DONE;
|
|
case STAGE_RESURRECT_HOLD:
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::sight_enemy( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_WAIT };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
FaceEnemy();
|
|
if ( GetKey( "on_activate" )[0] ) {
|
|
SetNeverDormant( true );
|
|
if ( GetIntKey( "walk_on_sight" ) ) {
|
|
MoveToEnemy();
|
|
}
|
|
waitActionName = "sight";
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Sight", 4 );
|
|
parms->stage = STAGE_WAIT;
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_DONE;
|
|
case STAGE_WAIT:
|
|
if ( ActionDone( "sight" ) ) {
|
|
SetNeverDormant( GetFloatKey( "neverdormant" ) != 0.0f );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
void rvmMonsterBase::enemy_dead() {
|
|
if ( owner ) {
|
|
owner->AI_ENEMY_DEAD = false;
|
|
}
|
|
checkForEnemy( false );
|
|
if ( !GetEnemy() ) {
|
|
SetState( "state_Idle" );
|
|
} else {
|
|
SetState( "state_Combat" );
|
|
}
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::walk_on_trigger( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_WAIT_ACTIVATED, STAGE_DONE };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
AllowMovement( false );
|
|
playCustomCycle( GetKey( "anim" ), 4 );
|
|
parms->stage = STAGE_WAIT_ACTIVATED;
|
|
return SRESULT_WAIT;
|
|
case STAGE_WAIT_ACTIVATED:
|
|
if ( !Activated() && !Pain() && !checkForEnemy( true ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
ClearActivated();
|
|
AllowMovement( true );
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
trigger_wakeup_targets();
|
|
StartAction( "sight_enemy" );
|
|
parms->stage = STAGE_DONE;
|
|
return SRESULT_WAIT;
|
|
case STAGE_DONE:
|
|
return InAction() ? SRESULT_WAIT : SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::wake_on_trigger( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_WAIT_ACTIVATED, STAGE_DONE };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
AllowMovement( false );
|
|
playCustomCycle( GetKey( "anim" ), 4 );
|
|
parms->stage = STAGE_WAIT_ACTIVATED;
|
|
return SRESULT_WAIT;
|
|
case STAGE_WAIT_ACTIVATED:
|
|
if ( !Activated() && !Pain() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
AllowMovement( true );
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
trigger_wakeup_targets();
|
|
StartAction( "sight_enemy" );
|
|
parms->stage = STAGE_DONE;
|
|
return SRESULT_WAIT;
|
|
case STAGE_DONE:
|
|
return InAction() ? SRESULT_WAIT : SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::wake_on_enemy( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_WAIT_ENEMY, STAGE_DONE };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
AllowMovement( false );
|
|
playCustomCycle( GetKey( "anim" ), 4 );
|
|
parms->stage = STAGE_WAIT_ENEMY;
|
|
return SRESULT_WAIT;
|
|
case STAGE_WAIT_ENEMY:
|
|
if ( !Pain() && !Activated() && !checkForEnemy( true ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
AllowMovement( true );
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
trigger_wakeup_targets();
|
|
StartAction( "sight_enemy" );
|
|
parms->stage = STAGE_DONE;
|
|
return SRESULT_WAIT;
|
|
case STAGE_DONE:
|
|
return InAction() ? SRESULT_WAIT : SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::wake_on_attackcone( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_WAIT_CONE, STAGE_DONE };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
AllowMovement( false );
|
|
playCustomCycle( GetKey( "anim" ), 4 );
|
|
parms->stage = STAGE_WAIT_CONE;
|
|
return SRESULT_WAIT;
|
|
case STAGE_WAIT_CONE: {
|
|
idEntity* enemyEnt = owner ? owner->FindEnemyInCombatNodes() : NULL;
|
|
if ( enemyEnt ) {
|
|
SetEnemy( enemyEnt );
|
|
}
|
|
if ( !Activated() && !Pain() && !GetEnemy() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
AllowMovement( true );
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
trigger_wakeup_targets();
|
|
StartAction( "sight_enemy" );
|
|
parms->stage = STAGE_DONE;
|
|
return SRESULT_WAIT;
|
|
}
|
|
case STAGE_DONE:
|
|
return InAction() ? SRESULT_WAIT : SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::idle_followPathEntities( stateParms_t* parms ) {
|
|
// Path-entity execution is kept as a non-blocking state. The current node is advanced
|
|
// one frame at a time; unsupported nodes are skipped instead of suspending the VM.
|
|
enum { STAGE_START, STAGE_NODE, STAGE_NODE_ACTION };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
if ( !current_path.GetEntity() ) {
|
|
current_path = owner ? owner->RandomPath() : NULL;
|
|
}
|
|
parms->stage = STAGE_NODE;
|
|
return SRESULT_WAIT;
|
|
case STAGE_NODE:
|
|
if ( !current_path.GetEntity() ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
next_path = idPathCorner::RandomPath( current_path.GetEntity(), NULL );
|
|
// Resolve common node class names into action states.
|
|
scratchString = current_path.GetEntity()->GetEntityDefName();
|
|
if ( !scratchString.Icmp( "path_corner" ) ) {
|
|
StartAction( "path_corner" );
|
|
} else if ( !scratchString.Icmp( "path_anim" ) ) {
|
|
StartAction( "path_anim" );
|
|
} else if ( !scratchString.Icmp( "path_cycleanim" ) ) {
|
|
StartAction( "path_cycleanim" );
|
|
} else if ( !scratchString.Icmp( "path_turn" ) ) {
|
|
StartAction( "path_turn" );
|
|
} else if ( !scratchString.Icmp( "path_wait" ) ) {
|
|
StartAction( "path_wait" );
|
|
} else if ( !scratchString.Icmp( "path_waitfortrigger" ) ) {
|
|
StartAction( "path_waitfortrigger" );
|
|
} else if ( !scratchString.Icmp( "path_hide" ) ) {
|
|
StartAction( "path_hide" );
|
|
} else if ( !scratchString.Icmp( "path_show" ) ) {
|
|
StartAction( "path_show" );
|
|
} else if ( !scratchString.Icmp( "path_attack" ) ) {
|
|
StartAction( "path_attack" );
|
|
} else {
|
|
current_path = next_path;
|
|
return SRESULT_WAIT;
|
|
}
|
|
parms->stage = STAGE_NODE_ACTION;
|
|
return SRESULT_WAIT;
|
|
case STAGE_NODE_ACTION:
|
|
if ( InAction() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( checkForEnemy( true ) ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
current_path = next_path;
|
|
parms->stage = STAGE_NODE;
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::path_corner( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_MOVE };
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
run = node->spawnArgs.GetBool( "run", run ? "1" : "0" );
|
|
scratchString = node->spawnArgs.GetString( "anim", "" );
|
|
if ( scratchString.Length() ) {
|
|
playCustomCycle( scratchString.c_str(), 4 );
|
|
}
|
|
MoveToEntity( node );
|
|
parms->stage = STAGE_MOVE;
|
|
return SRESULT_WAIT;
|
|
case STAGE_MOVE:
|
|
if ( InfluenceActive() ) {
|
|
StopMove();
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( checkForEnemy( true ) ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( MoveDone() || DestinationUnreachable() ) {
|
|
if ( scratchString.Length() ) {
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
}
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::path_anim( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_TURN, STAGE_ANIM };
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
scratchString = node->spawnArgs.GetString( "anim", "" );
|
|
customBlendOut = node->spawnArgs.GetInt( "blend_out", "0" );
|
|
if ( node->spawnArgs.GetString( "angle", "" )[0] ) {
|
|
TurnTo( node->spawnArgs.GetFloat( "angle" ) );
|
|
parms->stage = STAGE_TURN;
|
|
return SRESULT_WAIT;
|
|
}
|
|
playCustomAnim( scratchString.c_str(), node->spawnArgs.GetInt( "blend_in", "0" ), customBlendOut );
|
|
parms->stage = STAGE_ANIM;
|
|
return SRESULT_WAIT;
|
|
case STAGE_TURN:
|
|
if ( checkForEnemy( true ) ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( !FacingIdeal() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
playCustomAnim( scratchString.c_str(), node->spawnArgs.GetInt( "blend_in", "0" ), customBlendOut );
|
|
parms->stage = STAGE_ANIM;
|
|
return SRESULT_WAIT;
|
|
case STAGE_ANIM:
|
|
if ( checkForEnemy( true ) || AnimDone( ANIMCHANNEL_TORSO, idMath::Ftoi( customBlendOut ) ) ) {
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", idMath::Ftoi( customBlendOut ) );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::path_cycleanim( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_TURN, STAGE_HOLD };
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
scratchString = node->spawnArgs.GetString( "anim", "" );
|
|
customBlendOut = node->spawnArgs.GetInt( "blend_out", "0" );
|
|
TurnTo( node->GetPhysics()->GetAxis()[0].ToYaw() );
|
|
parms->stage = STAGE_TURN;
|
|
return SRESULT_WAIT;
|
|
case STAGE_TURN:
|
|
if ( checkForEnemy( true ) ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( !FacingIdeal() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
playCustomCycle( scratchString.c_str(), node->spawnArgs.GetInt( "blend_in", "0" ) );
|
|
scratchEndTime = node->spawnArgs.GetFloat( "wait", "0" ) > 0.0f ? DelayTime( node->spawnArgs.GetFloat( "wait" ) ) : 0.0f;
|
|
ClearActivated();
|
|
parms->stage = STAGE_HOLD;
|
|
return SRESULT_WAIT;
|
|
case STAGE_HOLD:
|
|
if ( checkForEnemy( true ) ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( scratchEndTime > 0.0f ) {
|
|
if ( Time() < scratchEndTime ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
} else if ( !Activated() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", idMath::Ftoi( customBlendOut ) );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::path_turn( stateParms_t* parms ) {
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( parms->stage == 0 ) {
|
|
TurnTo( node->GetPhysics()->GetAxis()[0].ToYaw() );
|
|
parms->stage = 1;
|
|
}
|
|
if ( checkForEnemy( true ) || FacingIdeal() ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::path_wait( stateParms_t* parms ) {
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( parms->stage == 0 ) {
|
|
scratchEndTime = DelayTime( node->spawnArgs.GetFloat( "wait", "0" ) );
|
|
parms->stage = 1;
|
|
}
|
|
if ( checkForEnemy( true ) || Time() >= scratchEndTime ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::path_waitfortrigger( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
ClearActivated();
|
|
parms->stage = 1;
|
|
}
|
|
if ( checkForEnemy( true ) ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( Activated() ) {
|
|
ClearActivated();
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::path_hide( stateParms_t* parms ) {
|
|
HideOwner();
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::path_show( stateParms_t* parms ) {
|
|
if ( !CanBecomeSolid() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
ShowOwner();
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::path_attack( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_COMBAT };
|
|
idEntity* node = current_path.GetEntity();
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
scratchEntity = node->spawnArgs.GetString( "enemy", "" )[0] ? gameLocal.FindEntity( node->spawnArgs.GetString( "enemy" ) ) : NULL;
|
|
if ( !scratchEntity.GetEntity() ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
SetEnemy( scratchEntity.GetEntity() );
|
|
LocateEnemy();
|
|
ClearActivated();
|
|
parms->stage = STAGE_COMBAT;
|
|
return SRESULT_WAIT;
|
|
case STAGE_COMBAT:
|
|
if ( Activated() || !scratchEntity.GetEntity() || EnemyDead() ) {
|
|
StopMove();
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( combat_chase() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::state_Combat( stateParms_t* parms ) {
|
|
FaceEnemy();
|
|
if ( EnemyInFOV() ) {
|
|
LookAtEnemy( 1.0f );
|
|
}
|
|
|
|
if ( InfluenceActive() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
if ( EnemyDead() ) {
|
|
enemy_dead();
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
const int attack_flags = check_attacks();
|
|
if ( attack_flags != 0 && do_attack( attack_flags ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
if ( !combat_chase() ) {
|
|
LocateEnemy();
|
|
if ( !combat_chase() ) {
|
|
combat_lost();
|
|
}
|
|
}
|
|
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
bool rvmMonsterBase::check_blocked() {
|
|
if ( Blocked() ) {
|
|
Wander();
|
|
return false;
|
|
}
|
|
|
|
if ( MoveStatus() == MOVE_STATUS_BLOCKED_BY_OBJECT && owner ) {
|
|
owner->KickObstacles( owner->GetObstacle(), GetFloatKey( "kick_force", "60" ) );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool rvmMonsterBase::combat_chase() {
|
|
bool do_run = ( !EnemyVisible() || EnemyRange() > run_distance );
|
|
MoveToEnemy();
|
|
if ( MoveDone() || DestinationUnreachable() ) {
|
|
return false;
|
|
}
|
|
|
|
if ( EnemyDead() ) {
|
|
enemy_dead();
|
|
return true;
|
|
}
|
|
|
|
if ( EnemyInFOV() ) {
|
|
LookAtEnemy( 1.0f );
|
|
}
|
|
|
|
const int attack_flags = check_attacks();
|
|
if ( attack_flags != 0 && do_attack( attack_flags ) ) {
|
|
return true;
|
|
}
|
|
|
|
if ( check_blocked() ) {
|
|
return true;
|
|
}
|
|
|
|
if ( !EnemyVisible() || EnemyRange() > run_distance ) {
|
|
do_run = true;
|
|
}
|
|
|
|
const float delta = GetTurnDelta();
|
|
run = !(( delta > walk_turn ) || ( delta < -walk_turn )) && do_run;
|
|
return true;
|
|
}
|
|
|
|
bool rvmMonsterBase::checkTurretAttack() {
|
|
return CanHitEnemy();
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::combat_turret_node( stateParms_t* parms ) {
|
|
// Long turret-node script is represented as a resumable attack-node state.
|
|
// It reaches the node, runs the subclass turret animation, and releases movement.
|
|
enum { STAGE_START, STAGE_MOVE, STAGE_ATTACK };
|
|
idEntity* node = scratchEntity.GetEntity();
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
AllowMovement( true );
|
|
run = true;
|
|
MoveToEntity( node );
|
|
parms->stage = STAGE_MOVE;
|
|
return SRESULT_WAIT;
|
|
case STAGE_MOVE:
|
|
if ( !node || DestinationUnreachable() || CheckForMelee() || EnemyDead() ) {
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
AllowMovement( true );
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( !MoveDone() ) {
|
|
if ( EnemyInFOV() ) {
|
|
LookAtEnemy( 1.0f );
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
FaceEnemy();
|
|
AllowMovement( false );
|
|
waitActionName = "turret_attack";
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_TurretAttack", 4 );
|
|
parms->stage = STAGE_ATTACK;
|
|
return SRESULT_WAIT;
|
|
case STAGE_ATTACK:
|
|
if ( !ActionDone( "turret_attack" ) ) {
|
|
if ( EnemyInFOV() ) {
|
|
LookAtEnemy( 1.0f );
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
AllowMovement( true );
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
void rvmMonsterBase::checkNodeAnim( idEntity* node, const char* prefix, const char* anim ) {
|
|
idStr fullName = prefix;
|
|
fullName += "_";
|
|
fullName += anim;
|
|
if ( !HasAnim( ANIMCHANNEL_TORSO, fullName.c_str() ) && owner && node ) {
|
|
gameLocal.Warning( "AI node '%s' specifies missing anim '%s' on monster '%s'", node->GetName(), fullName.c_str(), owner->GetName() );
|
|
}
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::combat_attack_cone( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_MOVE, STAGE_WAIT, STAGE_OUT, STAGE_FIRE, STAGE_IN };
|
|
idEntity* node = scratchEntity.GetEntity();
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
run = true;
|
|
MoveToEntity( node );
|
|
parms->stage = STAGE_MOVE;
|
|
return SRESULT_WAIT;
|
|
case STAGE_MOVE:
|
|
if ( !node || DestinationUnreachable() || CheckForMelee() || EnemyDead() || ( owner && !owner->EnemyInCombatCone( node, false ) ) ) {
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( !MoveDone() ) {
|
|
if ( EnemyInFOV() ) {
|
|
LookAtEnemy( 1.0f );
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
scratchString = node->spawnArgs.GetString( "anim", "" );
|
|
if ( !scratchString.Length() ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
SetAnimPrefix( scratchString.c_str() );
|
|
checkNodeAnim( node, scratchString.c_str(), "out" );
|
|
checkNodeAnim( node, scratchString.c_str(), "fire" );
|
|
checkNodeAnim( node, scratchString.c_str(), "in" );
|
|
checkNodeAnim( node, scratchString.c_str(), "wait" );
|
|
TurnTo( node->GetPhysics()->GetAxis()[0].ToYaw() );
|
|
AllowMovement( false );
|
|
playCustomCycle( "wait", 4 );
|
|
scratchEndTime = DelayTime( node->spawnArgs.GetFloat( "min_wait", "0" ) + RandomFloat( node->spawnArgs.GetFloat( "max_wait", "0" ) - node->spawnArgs.GetFloat( "min_wait", "0" ) ) );
|
|
parms->stage = STAGE_WAIT;
|
|
return SRESULT_WAIT;
|
|
case STAGE_WAIT:
|
|
if ( !node || CheckForMelee() || EnemyDead() || ( owner && !owner->EnemyInCombatCone( node, false ) ) ) {
|
|
parms->stage = STAGE_IN;
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( Time() < scratchEndTime || !FacingIdeal() ) {
|
|
if ( EnemyInFOV() ) {
|
|
LookAtEnemy( 1.0f );
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
AllowMovement( true );
|
|
playCustomAnim( "out", 4, 0 );
|
|
parms->stage = STAGE_OUT;
|
|
return SRESULT_WAIT;
|
|
case STAGE_OUT:
|
|
if ( !AnimDone( ANIMCHANNEL_TORSO, 0 ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
AllowMovement( false );
|
|
playCustomAnim( "fire", 0, 0 );
|
|
scratchCount = idMath::Ftoi( RandomFloat( node ? node->spawnArgs.GetFloat( "num_shots", "1" ) : 1.0f ) ) + 1;
|
|
parms->stage = STAGE_FIRE;
|
|
return SRESULT_WAIT;
|
|
case STAGE_FIRE:
|
|
if ( !AnimDone( ANIMCHANNEL_TORSO, 0 ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
scratchCount -= 1.0f;
|
|
if ( scratchCount > 0.0f && node && !EnemyDead() ) {
|
|
playCustomAnim( "fire", 0, 0 );
|
|
return SRESULT_WAIT;
|
|
}
|
|
AllowMovement( true );
|
|
playCustomAnim( "in", 0, 4 );
|
|
parms->stage = STAGE_IN;
|
|
return SRESULT_WAIT;
|
|
case STAGE_IN:
|
|
if ( !AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
AllowMovement( true );
|
|
SetAnimPrefix( "" );
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::combat_ainode( stateParms_t* parms ) {
|
|
idEntity* node = scratchEntity.GetEntity();
|
|
if ( node && !idStr::Icmp( node->spawnArgs.GetString( "classname" ), "ai_attackcone_turret" ) ) {
|
|
return combat_turret_node( parms );
|
|
}
|
|
return combat_attack_cone( parms );
|
|
}
|
|
|
|
void rvmMonsterBase::combat_lost() {
|
|
if ( !ignore_lostcombat ) {
|
|
SetState( "state_LostCombat" );
|
|
}
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::state_LostCombat( stateParms_t* parms ) {
|
|
monster_base_lost_combat();
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
void rvmMonsterBase::monster_base_lost_combat() {
|
|
if ( EnemyVisible() || InfluenceActive() ) {
|
|
SetState( "state_Combat" );
|
|
} else {
|
|
ClearEnemy();
|
|
SetState( "state_Idle" );
|
|
}
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::combat_wander( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_WANDER };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
scratchMinTime = DelayTime( 0.2f );
|
|
scratchEndTime = DelayTime( 3.0f );
|
|
Wander();
|
|
parms->stage = STAGE_WANDER;
|
|
return SRESULT_WAIT;
|
|
case STAGE_WANDER:
|
|
if ( InfluenceActive() || check_attacks() || Time() >= scratchEndTime || ( Time() > scratchMinTime && CanReachEnemy() && ( EnemyInFOV() || Pain() ) ) ) {
|
|
SetState( "state_Combat" );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmMonsterBase::state_FollowAlternatePath( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_PATH };
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
if ( InAnimState( ANIMCHANNEL_TORSO, "Torso_CustomCycle" ) || InAnimState( ANIMCHANNEL_TORSO, "Torso_CustomAnim" ) ) {
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
|
|
}
|
|
ignoreEnemies = true;
|
|
StartAction( "idle_followPathEntities" );
|
|
parms->stage = STAGE_PATH;
|
|
return SRESULT_WAIT;
|
|
case STAGE_PATH:
|
|
if ( InAction() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
ignoreEnemies = false;
|
|
SetState( "state_Idle" );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
int rvmMonsterBase::check_attacks() {
|
|
return 0;
|
|
}
|
|
|
|
bool rvmMonsterBase::do_attack( int attack_flags ) {
|
|
return false;
|
|
}
|
|
|
|
void rvmMonsterBase::playCustomCycle( const char* animname, float blendTime ) {
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_CustomCycle", idMath::Ftoi( blendTime ) );
|
|
OverrideAnim( ANIMCHANNEL_LEGS );
|
|
PlayCycle( ANIMCHANNEL_TORSO, animname );
|
|
}
|
|
|
|
void rvmMonsterBase::playCustomAnim( const char* animname, float blendIn, float blendOut ) {
|
|
customBlendOut = blendOut;
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_CustomAnim", idMath::Ftoi( blendIn ) );
|
|
OverrideAnim( ANIMCHANNEL_LEGS );
|
|
PlayAnim( ANIMCHANNEL_TORSO, animname );
|
|
}
|
|
|
|
void rvmMonsterBase::trigger_wakeup_targets() {
|
|
if ( !owner ) {
|
|
return;
|
|
}
|
|
const idKeyValue* kv = owner->spawnArgs.MatchPrefix( "wakeup_target", NULL );
|
|
while ( kv ) {
|
|
idEntity* ent = gameLocal.FindEntity( kv->GetValue() );
|
|
if ( ent ) {
|
|
const idEventDef* activateEvent = ent->FindEventDef( "activate", "e" );
|
|
if ( activateEvent ) {
|
|
ent->ProcessEvent( activateEvent, owner );
|
|
}
|
|
} else {
|
|
gameLocal.Warning( "Unknown wakeup_target '%s' on entity '%s'", kv->GetValue().c_str(), owner->GetName() );
|
|
}
|
|
kv = owner->spawnArgs.MatchPrefix( "wakeup_target", kv );
|
|
}
|
|
}
|
|
|
|
bool rvmMonsterBase::checkForEnemy( bool use_fov ) {
|
|
if ( InfluenceActive() ) {
|
|
return false;
|
|
}
|
|
|
|
if ( Pain() ) {
|
|
ambush = false;
|
|
}
|
|
|
|
idEntity* enemyEnt = NULL;
|
|
if ( ignoreEnemies ) {
|
|
if ( stay_on_attackpath ) {
|
|
return false;
|
|
}
|
|
enemyEnt = GetEnemy();
|
|
if ( !enemyEnt && owner ) {
|
|
enemyEnt = owner->FindEnemy( false );
|
|
}
|
|
if ( !enemyEnt ) {
|
|
return false;
|
|
}
|
|
const idBounds& b = owner->GetPhysics()->GetBounds();
|
|
const float dist = ( ( b[1] - b[0] ).x * 1.414f ) + 16.0f;
|
|
if ( EnemyRange() > dist ) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if ( GetEnemy() ) {
|
|
return true;
|
|
}
|
|
if ( !ignore_sight && owner ) {
|
|
enemyEnt = owner->FindEnemy( use_fov ? 1 : 0 );
|
|
}
|
|
if ( !enemyEnt ) {
|
|
if ( ambush ) {
|
|
return false;
|
|
}
|
|
enemyEnt = owner ? owner->HeardSound( true ) : NULL;
|
|
if ( !enemyEnt ) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
ignoreEnemies = false;
|
|
ambush = false;
|
|
idle_sight_fov = false;
|
|
SetEnemy( enemyEnt );
|
|
return true;
|
|
}
|
|
|
|
void rvmMonsterBase::archvile_minion() {
|
|
HideOwner();
|
|
resurrect = true;
|
|
if ( owner ) {
|
|
owner->AI_DEAD = true;
|
|
}
|
|
}
|
|
|
|
bool rvmMonsterBase::can_resurrect() {
|
|
return owner && owner->AI_DEAD && owner->IsHidden() && CanBecomeSolid();
|
|
}
|
|
|
|
void rvmMonsterBase::monster_resurrect( idEntity* enemyEnt ) {
|
|
if ( owner ) {
|
|
owner->AI_DEAD = false;
|
|
}
|
|
SetEnemy( enemyEnt ? enemyEnt : Player() );
|
|
SetState( "state_Resurrect" );
|
|
}
|
|
|
|
void rvmMonsterBase::follow_alternate_path1() {
|
|
current_path = GetEntityKey( "alt_path1" );
|
|
SetState( "state_FollowAlternatePath" );
|
|
}
|
|
|
|
void rvmMonsterBase::follow_alternate_path2() {
|
|
current_path = GetEntityKey( "alt_path2" );
|
|
SetState( "state_FollowAlternatePath" );
|
|
}
|
|
|
|
void rvmMonsterBase::follow_alternate_path3() {
|
|
current_path = GetEntityKey( "alt_path3" );
|
|
SetState( "state_FollowAlternatePath" );
|
|
}
|