mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
Numerous AI fixes.
This commit is contained in:
@@ -179,10 +179,8 @@ float rvmAIObject::RandomTime( float seconds ) const {
|
||||
}
|
||||
|
||||
bool rvmAIObject::InfluenceActive() const {
|
||||
// Some branches keep the old DoomScript influence query only in script/event glue,
|
||||
// and idPlayer has no public InfluenceActive() accessor. Keep this wrapper compile-safe;
|
||||
// wire it to your branch's real influence flag here if one exists.
|
||||
return false;
|
||||
idPlayer* player = gameLocal.GetLocalPlayer();
|
||||
return player && player->GetInfluenceLevel();
|
||||
}
|
||||
|
||||
idPlayer* rvmAIObject::Player() const {
|
||||
|
||||
@@ -22,6 +22,14 @@ rvmMonsterBase::rvmMonsterBase() {
|
||||
ignore_lostcombat = false;
|
||||
blocked = false;
|
||||
ignore_sight = false;
|
||||
monsterBeginStartActive = false;
|
||||
monsterBeginTeleportType = 0;
|
||||
monsterBeginMoveType = MOVETYPE_ANIM;
|
||||
spawnerTriggerCount = 0;
|
||||
monsterBeginActionName.Clear();
|
||||
ClearParms( monsterBeginActionParms );
|
||||
pathActionName.Clear();
|
||||
ClearParms( pathActionParms );
|
||||
}
|
||||
|
||||
void rvmMonsterBase::Init( idAI* aiOwner ) {
|
||||
@@ -36,6 +44,14 @@ void rvmMonsterBase::Init( idAI* aiOwner ) {
|
||||
run = false;
|
||||
blocked = false;
|
||||
ignore_sight = false;
|
||||
monsterBeginStartActive = false;
|
||||
monsterBeginTeleportType = 0;
|
||||
monsterBeginMoveType = MOVETYPE_ANIM;
|
||||
spawnerTriggerCount = 0;
|
||||
monsterBeginActionName.Clear();
|
||||
ClearParms( monsterBeginActionParms );
|
||||
pathActionName.Clear();
|
||||
ClearParms( pathActionParms );
|
||||
}
|
||||
|
||||
stateResult_t rvmMonsterBase::RunStateByName( const char* stateName, stateParms_t* parms ) {
|
||||
@@ -113,12 +129,14 @@ stateResult_t rvmMonsterBase::Torso_Sight( stateParms_t* parms ) {
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
FinishAction( "sight" );
|
||||
SetNeverDormant( GetFloatKey( "neverdormant" ) != 0.0f );
|
||||
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" );
|
||||
SetNeverDormant( GetFloatKey( "neverdormant" ) != 0.0f );
|
||||
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", GetIntKey( "on_activate_blend", "4" ) );
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
@@ -158,12 +176,16 @@ stateResult_t rvmMonsterBase::state_Spawner( stateParms_t* parms ) {
|
||||
case STAGE_START:
|
||||
HideOwner();
|
||||
ClearActivated();
|
||||
spawnerTriggerCount = 0;
|
||||
parms->stage = STAGE_LOOP;
|
||||
return SRESULT_WAIT;
|
||||
case STAGE_LOOP:
|
||||
if ( Activated() && CanBecomeSolid() ) {
|
||||
if ( Activated() ) {
|
||||
spawnerTriggerCount++;
|
||||
ClearActivated();
|
||||
// Spawn-copy behavior is project-specific; keep this state non-blocking.
|
||||
}
|
||||
if ( spawnerTriggerCount > 0 && CanBecomeSolid() && SpawnFromSpawner() ) {
|
||||
spawnerTriggerCount--;
|
||||
}
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
@@ -171,7 +193,18 @@ stateResult_t rvmMonsterBase::state_Spawner( stateParms_t* parms ) {
|
||||
}
|
||||
|
||||
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 };
|
||||
enum {
|
||||
STAGE_START,
|
||||
STAGE_WAIT_TRIGGER,
|
||||
STAGE_WAIT_SOLID,
|
||||
STAGE_TELEPORT_FX,
|
||||
STAGE_TRIGGER_ANIM,
|
||||
STAGE_DELAY,
|
||||
STAGE_WAKE_SELECT,
|
||||
STAGE_WAKE_ACTION,
|
||||
STAGE_ATTACK_PATH,
|
||||
STAGE_DONE
|
||||
};
|
||||
switch ( parms->stage ) {
|
||||
case STAGE_START:
|
||||
run = false;
|
||||
@@ -179,29 +212,104 @@ stateResult_t rvmMonsterBase::monster_begin( stateParms_t* parms ) {
|
||||
if ( !GetIntKey( "ignore_flashlight" ) ) {
|
||||
WakeOnFlashlight( true );
|
||||
}
|
||||
if ( GetIntKey( "hide" ) ) {
|
||||
|
||||
monsterBeginStartActive = false;
|
||||
monsterBeginMoveType = owner ? owner->GetMoveType() : MOVETYPE_ANIM;
|
||||
monsterBeginTeleportType = resurrect ? 4 : GetIntKey( "teleport" );
|
||||
scratchString = resurrect ? "" : GetKey( "trigger_anim" );
|
||||
if ( resurrect && owner ) {
|
||||
owner->AI_ACTIVATED = true;
|
||||
}
|
||||
|
||||
if ( GetIntKey( "spawner" ) ) {
|
||||
SetState( "state_Spawner" );
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
|
||||
if ( scratchString.Length() || monsterBeginTeleportType > 0 || GetIntKey( "hide" ) ) {
|
||||
HideOwner();
|
||||
ClearActivated();
|
||||
parms->stage = STAGE_HIDDEN_WAIT_TRIGGER;
|
||||
if ( !resurrect ) {
|
||||
ClearActivated();
|
||||
}
|
||||
parms->stage = STAGE_WAIT_TRIGGER;
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
|
||||
scratchEndTime = DelayTime( GetFloatKey( "wait" ) );
|
||||
parms->stage = STAGE_DELAY;
|
||||
return SRESULT_WAIT;
|
||||
|
||||
case STAGE_WAIT_TRIGGER:
|
||||
if ( !Activated() ) {
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
ClearActivated();
|
||||
parms->stage = STAGE_WAIT_SOLID;
|
||||
return SRESULT_WAIT;
|
||||
|
||||
case STAGE_WAIT_SOLID:
|
||||
if ( !CanBecomeSolid() ) {
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
if ( monsterBeginTeleportType > 0 ) {
|
||||
BecomeSolid();
|
||||
monsterBeginMoveType = owner ? owner->GetMoveType() : MOVETYPE_ANIM;
|
||||
SetMoveType( MOVETYPE_STATIC );
|
||||
SetNeverDormant( true );
|
||||
trigger_wakeup_targets();
|
||||
if ( monsterBeginTeleportType == 1 ) {
|
||||
StartFx( GetKey( "fx_teleport1" ) );
|
||||
scratchEndTime = DelayTime( 1.6f );
|
||||
} else if ( monsterBeginTeleportType == 2 ) {
|
||||
StartFx( GetKey( "fx_teleport2" ) );
|
||||
scratchEndTime = DelayTime( 2.6f );
|
||||
} else if ( monsterBeginTeleportType == 3 ) {
|
||||
StartFx( GetKey( "fx_teleport3" ) );
|
||||
scratchEndTime = DelayTime( 3.6f );
|
||||
} else {
|
||||
StartFx( GetKey( "fx_teleport" ) );
|
||||
scratchEndTime = DelayTime( 0.6f );
|
||||
}
|
||||
parms->stage = STAGE_TELEPORT_FX;
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
|
||||
ShowOwner();
|
||||
if ( scratchString.Length() ) {
|
||||
SetNeverDormant( true );
|
||||
trigger_wakeup_targets();
|
||||
waitActionName = "customAnim";
|
||||
playCustomAnim( scratchString.c_str(), 0.0f, 4.0f );
|
||||
parms->stage = STAGE_TRIGGER_ANIM;
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
if ( ( GetIntKey( "hide" ) == 1 ) || ambush ) {
|
||||
ClearEnemy();
|
||||
}
|
||||
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() ) {
|
||||
case STAGE_TELEPORT_FX:
|
||||
if ( Time() < scratchEndTime ) {
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
ShowOwner();
|
||||
waitActionName = "customAnim";
|
||||
playCustomAnim( "teleport", 0.0f, 4.0f );
|
||||
parms->stage = STAGE_TRIGGER_ANIM;
|
||||
return SRESULT_WAIT;
|
||||
|
||||
case STAGE_TRIGGER_ANIM:
|
||||
if ( !ActionDone( "customAnim" ) ) {
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
SetNeverDormant( GetFloatKey( "neverdormant" ) != 0.0f );
|
||||
LocateEnemy();
|
||||
if ( monsterBeginTeleportType > 0 ) {
|
||||
SetMoveType( monsterBeginMoveType );
|
||||
}
|
||||
monsterBeginStartActive = true;
|
||||
scratchEndTime = DelayTime( GetFloatKey( "wait" ) );
|
||||
parms->stage = STAGE_DELAY;
|
||||
return SRESULT_WAIT;
|
||||
@@ -213,24 +321,59 @@ stateResult_t rvmMonsterBase::monster_begin( stateParms_t* parms ) {
|
||||
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" ) ) {
|
||||
if ( monsterBeginStartActive ) {
|
||||
if ( GetIntKey( "attack_path" ) ) {
|
||||
current_path = owner ? owner->RandomPath() : NULL;
|
||||
if ( current_path.GetEntity() ) {
|
||||
run = true;
|
||||
ignoreEnemies = true;
|
||||
monsterBeginActionName = "idle_followPathEntities";
|
||||
ClearParms( monsterBeginActionParms );
|
||||
parms->stage = STAGE_ATTACK_PATH;
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
}
|
||||
parms->stage = STAGE_DONE;
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
|
||||
parms->stage = STAGE_WAKE_SELECT;
|
||||
return SRESULT_WAIT;
|
||||
|
||||
case STAGE_WAKE_SELECT:
|
||||
if ( GetIntKey( "wake_on_attackcone" ) ) {
|
||||
monsterBeginActionName = "wake_on_attackcone";
|
||||
} else if ( GetIntKey( "walk_on_trigger" ) ) {
|
||||
monsterBeginActionName = "walk_on_trigger";
|
||||
} else if ( GetIntKey( "trigger" ) ) {
|
||||
monsterBeginActionName = "wake_on_trigger";
|
||||
} else {
|
||||
monsterBeginActionName = "wake_on_enemy";
|
||||
}
|
||||
ClearParms( monsterBeginActionParms );
|
||||
parms->stage = STAGE_WAKE_ACTION;
|
||||
return SRESULT_WAIT;
|
||||
|
||||
case STAGE_WAKE_ACTION:
|
||||
if ( RunStateByName( monsterBeginActionName.c_str(), &monsterBeginActionParms ) == SRESULT_WAIT ) {
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
monsterBeginActionName.Clear();
|
||||
parms->stage = STAGE_DONE;
|
||||
return SRESULT_WAIT;
|
||||
|
||||
case STAGE_ATTACK_PATH:
|
||||
if ( RunStateByName( monsterBeginActionName.c_str(), &monsterBeginActionParms ) == SRESULT_WAIT ) {
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
monsterBeginActionName.Clear();
|
||||
ignoreEnemies = false;
|
||||
parms->stage = STAGE_DONE;
|
||||
return SRESULT_WAIT;
|
||||
|
||||
case STAGE_DONE:
|
||||
ignore_sight = false;
|
||||
WakeOnFlashlight( false );
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
return SRESULT_ERROR;
|
||||
@@ -409,11 +552,19 @@ stateResult_t rvmMonsterBase::walk_on_trigger( stateParms_t* parms ) {
|
||||
AllowMovement( true );
|
||||
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
||||
trigger_wakeup_targets();
|
||||
StartAction( "sight_enemy" );
|
||||
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_DONE;
|
||||
return SRESULT_WAIT;
|
||||
case STAGE_DONE:
|
||||
return InAction() ? SRESULT_WAIT : SRESULT_DONE;
|
||||
return !ActionDone( "sight" ) ? SRESULT_WAIT : SRESULT_DONE;
|
||||
}
|
||||
return SRESULT_ERROR;
|
||||
}
|
||||
@@ -433,11 +584,19 @@ stateResult_t rvmMonsterBase::wake_on_trigger( stateParms_t* parms ) {
|
||||
AllowMovement( true );
|
||||
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
||||
trigger_wakeup_targets();
|
||||
StartAction( "sight_enemy" );
|
||||
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_DONE;
|
||||
return SRESULT_WAIT;
|
||||
case STAGE_DONE:
|
||||
return InAction() ? SRESULT_WAIT : SRESULT_DONE;
|
||||
return !ActionDone( "sight" ) ? SRESULT_WAIT : SRESULT_DONE;
|
||||
}
|
||||
return SRESULT_ERROR;
|
||||
}
|
||||
@@ -457,11 +616,19 @@ stateResult_t rvmMonsterBase::wake_on_enemy( stateParms_t* parms ) {
|
||||
AllowMovement( true );
|
||||
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
||||
trigger_wakeup_targets();
|
||||
StartAction( "sight_enemy" );
|
||||
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_DONE;
|
||||
return SRESULT_WAIT;
|
||||
case STAGE_DONE:
|
||||
return InAction() ? SRESULT_WAIT : SRESULT_DONE;
|
||||
return !ActionDone( "sight" ) ? SRESULT_WAIT : SRESULT_DONE;
|
||||
}
|
||||
return SRESULT_ERROR;
|
||||
}
|
||||
@@ -485,12 +652,20 @@ stateResult_t rvmMonsterBase::wake_on_attackcone( stateParms_t* parms ) {
|
||||
AllowMovement( true );
|
||||
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
||||
trigger_wakeup_targets();
|
||||
StartAction( "sight_enemy" );
|
||||
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_DONE;
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
case STAGE_DONE:
|
||||
return InAction() ? SRESULT_WAIT : SRESULT_DONE;
|
||||
return !ActionDone( "sight" ) ? SRESULT_WAIT : SRESULT_DONE;
|
||||
}
|
||||
return SRESULT_ERROR;
|
||||
}
|
||||
@@ -514,33 +689,35 @@ stateResult_t rvmMonsterBase::idle_followPathEntities( stateParms_t* parms ) {
|
||||
// Resolve common node class names into action states.
|
||||
scratchString = current_path.GetEntity()->GetEntityDefName();
|
||||
if ( !scratchString.Icmp( "path_corner" ) ) {
|
||||
StartAction( "path_corner" );
|
||||
pathActionName = "path_corner";
|
||||
} else if ( !scratchString.Icmp( "path_anim" ) ) {
|
||||
StartAction( "path_anim" );
|
||||
pathActionName = "path_anim";
|
||||
} else if ( !scratchString.Icmp( "path_cycleanim" ) ) {
|
||||
StartAction( "path_cycleanim" );
|
||||
pathActionName = "path_cycleanim";
|
||||
} else if ( !scratchString.Icmp( "path_turn" ) ) {
|
||||
StartAction( "path_turn" );
|
||||
pathActionName = "path_turn";
|
||||
} else if ( !scratchString.Icmp( "path_wait" ) ) {
|
||||
StartAction( "path_wait" );
|
||||
pathActionName = "path_wait";
|
||||
} else if ( !scratchString.Icmp( "path_waitfortrigger" ) ) {
|
||||
StartAction( "path_waitfortrigger" );
|
||||
pathActionName = "path_waitfortrigger";
|
||||
} else if ( !scratchString.Icmp( "path_hide" ) ) {
|
||||
StartAction( "path_hide" );
|
||||
pathActionName = "path_hide";
|
||||
} else if ( !scratchString.Icmp( "path_show" ) ) {
|
||||
StartAction( "path_show" );
|
||||
pathActionName = "path_show";
|
||||
} else if ( !scratchString.Icmp( "path_attack" ) ) {
|
||||
StartAction( "path_attack" );
|
||||
pathActionName = "path_attack";
|
||||
} else {
|
||||
current_path = next_path;
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
ClearParms( pathActionParms );
|
||||
parms->stage = STAGE_NODE_ACTION;
|
||||
return SRESULT_WAIT;
|
||||
case STAGE_NODE_ACTION:
|
||||
if ( InAction() ) {
|
||||
if ( RunStateByName( pathActionName.c_str(), &pathActionParms ) == SRESULT_WAIT ) {
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
pathActionName.Clear();
|
||||
if ( checkForEnemy( true ) ) {
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
@@ -1087,6 +1264,52 @@ void rvmMonsterBase::trigger_wakeup_targets() {
|
||||
}
|
||||
}
|
||||
|
||||
void rvmMonsterBase::TriggerEntity( idEntity* ent ) {
|
||||
if ( !ent ) {
|
||||
return;
|
||||
}
|
||||
const idEventDef* activateEvent = ent->FindEventDef( "activate", "e" );
|
||||
ent->Signal( SIG_TRIGGER );
|
||||
if ( activateEvent ) {
|
||||
ent->ProcessEvent( activateEvent, gameLocal.GetLocalPlayer() );
|
||||
}
|
||||
ent->TriggerGuis();
|
||||
}
|
||||
|
||||
idEntity* rvmMonsterBase::SpawnFromSpawner() {
|
||||
if ( !owner ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int maxSpawn = GetIntKey( "spawner" );
|
||||
const char* baseName = owner->GetName();
|
||||
int slot = 0;
|
||||
for ( ; slot < maxSpawn; slot++ ) {
|
||||
if ( !gameLocal.FindEntity( va( "%s%d", baseName, slot ) ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( slot >= maxSpawn ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idDict args = owner->spawnArgs;
|
||||
args.SetInt( "spawner", 0 );
|
||||
args.Set( "name", va( "%s%d", baseName, slot ) );
|
||||
const char* spawnTarget = GetKey( "spawn_target" );
|
||||
if ( spawnTarget[0] ) {
|
||||
args.Set( "target", spawnTarget );
|
||||
}
|
||||
|
||||
idEntity* ent = NULL;
|
||||
if ( gameLocal.SpawnEntityDef( args, &ent ) && ent ) {
|
||||
TriggerEntity( ent );
|
||||
return ent;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool rvmMonsterBase::checkForEnemy( bool use_fov ) {
|
||||
if ( InfluenceActive() ) {
|
||||
return false;
|
||||
|
||||
@@ -27,6 +27,14 @@ protected:
|
||||
bool ignore_lostcombat;
|
||||
bool blocked;
|
||||
bool ignore_sight;
|
||||
bool monsterBeginStartActive;
|
||||
int monsterBeginTeleportType;
|
||||
int monsterBeginMoveType;
|
||||
int spawnerTriggerCount;
|
||||
idStr monsterBeginActionName;
|
||||
stateParms_t monsterBeginActionParms;
|
||||
idStr pathActionName;
|
||||
stateParms_t pathActionParms;
|
||||
|
||||
// Animation state machines.
|
||||
virtual stateResult_t Torso_CustomCycle( stateParms_t* parms );
|
||||
@@ -89,4 +97,6 @@ protected:
|
||||
virtual void follow_alternate_path1();
|
||||
virtual void follow_alternate_path2();
|
||||
virtual void follow_alternate_path3();
|
||||
virtual void TriggerEntity( idEntity* ent );
|
||||
virtual idEntity* SpawnFromSpawner();
|
||||
};
|
||||
|
||||
@@ -200,7 +200,31 @@ stateResult_t rvmMonsterBossGuardian::combat_charge( stateParms_t* parms ) {
|
||||
nextAttack = DelayTime( GUARDIAN_RANGE_ATTACK_DELAY );
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
stateResult_t rvmMonsterBossGuardian::pound_impact( stateParms_t* parms ) { RadiusDamageFromJoint( rightfoot ? "r_foot" : "l_foot", "damage_guardian_footstep" ); return SRESULT_DONE; }
|
||||
stateResult_t rvmMonsterBossGuardian::pound_impact( stateParms_t* parms ) {
|
||||
const char* jointName = rightfoot ? "rhand" : "lhand";
|
||||
idVec3 org = GetOrigin();
|
||||
idMat3 axis = mat3_identity;
|
||||
if ( owner ) {
|
||||
jointHandle_t joint = owner->GetAnimator()->GetJointHandle( jointName );
|
||||
if ( joint != INVALID_JOINT ) {
|
||||
owner->GetJointWorldTransform( joint, gameLocal.time, org, axis );
|
||||
}
|
||||
gameLocal.RadiusDamage( org, owner, owner, spawner.GetEntity(), spawner.GetEntity(), GetKey( "def_poundgrounddamage", "damage_guardianPoundGround" ), 1.0f );
|
||||
|
||||
const char* entname = GetKey( "def_poundground" );
|
||||
if ( entname[0] ) {
|
||||
idEntity* explosion = NULL;
|
||||
idDict args;
|
||||
args.Set( "classname", entname );
|
||||
if ( gameLocal.SpawnEntityDef( args, &explosion ) && explosion ) {
|
||||
explosion->SetOrigin( org - idVec3( 0.0f, 0.0f, 32.0f ) );
|
||||
explosion->SetShaderParm( SHADERPARM_TIMEOFFSET, -Time() );
|
||||
explosion->PostEventSec( &EV_Remove, 0.75f );
|
||||
}
|
||||
}
|
||||
}
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
stateResult_t rvmMonsterBossGuardian::pound_attack( stateParms_t* parms ) { AttackMelee( "damage_guardian_melee" ); return SRESULT_DONE; }
|
||||
stateResult_t rvmMonsterBossGuardian::pound_attack_left( stateParms_t* parms ) { rightfoot = false; return pound_attack( parms ); }
|
||||
stateResult_t rvmMonsterBossGuardian::pound_attack_right( stateParms_t* parms ) { rightfoot = true; return pound_attack( parms ); }
|
||||
|
||||
@@ -209,12 +209,12 @@ int rvmMonsterBossVagary::check_attacks() {
|
||||
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
|
||||
if ( !CanReachEnemy() || currentTime >= nextAttack ) {
|
||||
if ( CanHitEnemy() ) {
|
||||
throwEntity = FindActorsInBounds( idVec3( -512, -512, -512 ), idVec3( 512, 512, 512 ) );
|
||||
throwEntity = ChooseObjectToThrow();
|
||||
if ( throwEntity.GetEntity() ) { flags |= ATTACK_MISSILE; }
|
||||
}
|
||||
}
|
||||
}
|
||||
return static_cast<float>( flags );
|
||||
return flags;
|
||||
}
|
||||
|
||||
bool rvmMonsterBossVagary::do_attack( int attack_flags ) {
|
||||
@@ -231,16 +231,93 @@ bool rvmMonsterBossVagary::do_attack( int attack_flags ) {
|
||||
stateResult_t rvmMonsterBossVagary::throw_object( stateParms_t* parms ) {
|
||||
idEntity* ent = throwEntity.GetEntity();
|
||||
if ( !ent ) { return SRESULT_DONE; }
|
||||
// Hook point for the project-specific vagary_ThrowObjectAtEnemy helper.
|
||||
StartSoundKey( "snd_pickup", SND_CHANNEL_WEAPON, false );
|
||||
if ( GetEnemy() ) {
|
||||
idVec3 dir = GetEnemyPos() - ent->GetPhysics()->GetOrigin();
|
||||
dir.Normalize();
|
||||
ent->GetPhysics()->SetLinearVelocity( dir * VAGARY_THROW_SPEED2 );
|
||||
}
|
||||
ThrowObjectAtEnemy( ent, true );
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
|
||||
idEntity* rvmMonsterBossVagary::ChooseObjectToThrow() {
|
||||
if ( !owner ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idEntity* entityList[ MAX_GENTITIES ];
|
||||
idBounds checkBounds( VAGARY_THROW_MIN, VAGARY_THROW_MAX );
|
||||
checkBounds.TranslateSelf( GetOrigin() );
|
||||
const int numListedEntities = gameLocal.clip.EntitiesTouchingBounds( checkBounds, -1, entityList, MAX_GENTITIES );
|
||||
if ( numListedEntities <= 0 ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idActor* enemyEnt = GetEnemy();
|
||||
const idVec3 enemyEyePos = enemyEnt ? owner->lastVisibleEnemyPos + owner->lastVisibleEnemyEyeOffset : GetEnemyPos();
|
||||
const idBounds& myBounds = owner->GetPhysics()->GetAbsBounds();
|
||||
const int startIndex = gameLocal.random.RandomInt( numListedEntities );
|
||||
|
||||
for ( int i = 0; i < numListedEntities; i++ ) {
|
||||
int index = startIndex + i;
|
||||
if ( index >= numListedEntities ) {
|
||||
index -= numListedEntities;
|
||||
}
|
||||
|
||||
idEntity* ent = entityList[index];
|
||||
if ( !ent || !ent->IsType( idMoveable::Type ) || ent->fl.hidden ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
idPhysics* entPhys = ent->GetPhysics();
|
||||
const idVec3& entOrg = entPhys->GetOrigin();
|
||||
if ( ( entOrg - enemyEyePos ).LengthFast() < VAGARY_THROW_DIST ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
idBounds expandedBounds = myBounds.Expand( entPhys->GetBounds().GetRadius() );
|
||||
if ( expandedBounds.LineIntersection( entOrg, enemyEyePos ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
idVec3 vel;
|
||||
if ( idAI::PredictTrajectory( entOrg + idVec3( 0.0f, 0.0f, VAGARY_THROW_OFFSET ), enemyEyePos, VAGARY_THROW_SPEED, entPhys->GetGravity(),
|
||||
entPhys->GetClipModel(), entPhys->GetClipMask(), MAX_WORLD_SIZE, NULL, enemyEnt, ai_debugTrajectory.GetBool() ? 4000 : 0, vel ) ) {
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rvmMonsterBossVagary::ThrowObjectAtEnemy( idEntity* ent, bool delayedThrow ) {
|
||||
if ( !ent ) {
|
||||
return;
|
||||
}
|
||||
|
||||
StartSoundKey( "snd_pickup", SND_CHANNEL_WEAPON, false );
|
||||
idPhysics* entPhys = ent->GetPhysics();
|
||||
entPhys->SetAngularVelocity( idVec3( 30.0f, 30.0f, 0.0f ) );
|
||||
|
||||
idVec3 throwVelocity = owner ? owner->GetPhysics()->GetAxis()[0] * VAGARY_THROW_SPEED2 : idVec3( VAGARY_THROW_SPEED2, 0.0f, 0.0f );
|
||||
idActor* enemyEnt = GetEnemy();
|
||||
if ( enemyEnt && owner ) {
|
||||
idVec3 aimDir;
|
||||
if ( idAI::PredictTrajectory( entPhys->GetOrigin(), owner->lastVisibleEnemyPos + owner->lastVisibleEnemyEyeOffset, VAGARY_THROW_SPEED2,
|
||||
entPhys->GetGravity(), entPhys->GetClipModel(), entPhys->GetClipMask(), MAX_WORLD_SIZE, NULL, enemyEnt,
|
||||
ai_debugTrajectory.GetBool() ? 4000 : 0, aimDir ) ) {
|
||||
throwVelocity = aimDir * VAGARY_THROW_SPEED2;
|
||||
}
|
||||
}
|
||||
|
||||
if ( delayedThrow ) {
|
||||
const idVec3 hoverPos = entPhys->GetOrigin() + idVec3( 0.0f, 0.0f, VAGARY_THROW_OFFSET );
|
||||
entPhys->SetLinearVelocity( ( hoverPos - entPhys->GetOrigin() ) * 5.0f );
|
||||
ent->PostEventSec( &EV_SetLinearVelocity, 0.5f, throwVelocity );
|
||||
} else {
|
||||
entPhys->SetLinearVelocity( throwVelocity );
|
||||
}
|
||||
|
||||
if ( ent->IsType( idMoveable::Type ) ) {
|
||||
static_cast<idMoveable*>( ent )->EnableDamage( true, 2.5f );
|
||||
}
|
||||
}
|
||||
|
||||
stateResult_t rvmMonsterBossVagary::combat_range( stateParms_t* parms ) {
|
||||
enum { STAGE_FACE, STAGE_THROW, STAGE_COOLDOWN };
|
||||
switch ( parms->stage ) {
|
||||
@@ -252,12 +329,20 @@ stateResult_t rvmMonsterBossVagary::combat_range( stateParms_t* parms ) {
|
||||
return SRESULT_WAIT;
|
||||
case STAGE_THROW:
|
||||
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
|
||||
StartAction( "throw_object" );
|
||||
ThrowObjectAtEnemy( throwEntity.GetEntity(), true );
|
||||
scratchCount = RandomFloat( 4.0f );
|
||||
for ( int i = 1; i < idMath::Ftoi( scratchCount ); i++ ) {
|
||||
idEntity* ent = ChooseObjectToThrow();
|
||||
if ( !ent ) {
|
||||
break;
|
||||
}
|
||||
ThrowObjectAtEnemy( ent, true );
|
||||
}
|
||||
scratchEndTime = DelayTime( 1.0f );
|
||||
parms->stage = STAGE_COOLDOWN;
|
||||
return SRESULT_WAIT;
|
||||
case STAGE_COOLDOWN:
|
||||
if ( InAction() || Time() < scratchEndTime ) { return SRESULT_WAIT; }
|
||||
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
|
||||
LookAtEnemy( 1.0f );
|
||||
nextAttack = DelayTime( VAGARY_ATTACK_RATE );
|
||||
nextNoFOVAttack = Time() + VAGARY_NOFOVTIME;
|
||||
|
||||
@@ -22,6 +22,12 @@
|
||||
#ifndef VAGARY_THROW_SPEED2
|
||||
#define VAGARY_THROW_SPEED2 1500
|
||||
#endif
|
||||
#ifndef VAGARY_THROW_MIN
|
||||
#define VAGARY_THROW_MIN idVec3( -512.0f, -512.0f, -512.0f )
|
||||
#endif
|
||||
#ifndef VAGARY_THROW_MAX
|
||||
#define VAGARY_THROW_MAX idVec3( 512.0f, 512.0f, 512.0f )
|
||||
#endif
|
||||
#ifndef VAGARY_THROW_DIST
|
||||
#define VAGARY_THROW_DIST 200
|
||||
#endif
|
||||
@@ -105,5 +111,7 @@
|
||||
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
|
||||
virtual int check_attacks() override;
|
||||
virtual bool do_attack( int attack_flags ) override;
|
||||
virtual idEntity* ChooseObjectToThrow();
|
||||
virtual void ThrowObjectAtEnemy( idEntity* ent, bool delayedThrow );
|
||||
|
||||
};
|
||||
|
||||
@@ -6,6 +6,44 @@
|
||||
#include "../Game_local.h"
|
||||
|
||||
|
||||
static void PostEntityEventByName( idEntity* ent, const char* eventName, float delaySeconds ) {
|
||||
if ( !ent ) {
|
||||
return;
|
||||
}
|
||||
const idEventDef* eventDef = ent->FindEventDef( eventName, NULL );
|
||||
if ( !eventDef ) {
|
||||
return;
|
||||
}
|
||||
if ( delaySeconds <= 0.0f ) {
|
||||
ent->ProcessEvent( eventDef );
|
||||
} else {
|
||||
ent->PostEventSec( eventDef, delaySeconds );
|
||||
}
|
||||
}
|
||||
|
||||
static idEntity* SpawnArchvileTimedTrigger( const char* classname, const idVec3& origin, const idAngles& angles, idAI* owner, float enableDelay, float activeTime, float removeDelay ) {
|
||||
idEntity* ent = NULL;
|
||||
idDict args;
|
||||
args.Set( "classname", classname );
|
||||
args.Set( "cinematic_remove", "1" );
|
||||
if ( !gameLocal.SpawnEntityDef( args, &ent ) || !ent ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ent->SetOrigin( origin );
|
||||
ent->SetAngles( angles );
|
||||
ent->SetShaderParm( SHADERPARM_TIMEOFFSET, -MS2SEC( gameLocal.time ) );
|
||||
ent->SetShaderParm( SHADERPARM_DIVERSITY, gameLocal.random.RandomFloat() );
|
||||
if ( owner ) {
|
||||
ent->ProcessEvent( &EV_SetOwner, owner );
|
||||
}
|
||||
PostEntityEventByName( ent, "disable", 0.0f );
|
||||
PostEntityEventByName( ent, "enable", enableDelay );
|
||||
PostEntityEventByName( ent, "disable", enableDelay + activeTime );
|
||||
ent->PostEventSec( &EV_Remove, removeDelay );
|
||||
return ent;
|
||||
}
|
||||
|
||||
|
||||
rvmMonsterDemonArchvile::rvmMonsterDemonArchvile() {
|
||||
nextDodge = 0.0f;
|
||||
@@ -412,47 +450,69 @@ stateResult_t rvmMonsterDemonArchvile::combat_dodge_right( stateParms_t* parms )
|
||||
}
|
||||
|
||||
stateResult_t rvmMonsterDemonArchvile::spawn_flame_wall( stateParms_t* parms ) {
|
||||
// Hook point for the original script's repeated sys.spawn of flame-wall projectiles.
|
||||
// The looping behavior is represented by flame_wall_loop() below, not a blocking wait.
|
||||
idAngles angles = owner ? owner->GetPhysics()->GetAxis().ToAngles() : ang_zero;
|
||||
angles.yaw = RandomFloat( 360.0f );
|
||||
SpawnArchvileTimedTrigger( "archvile_flamewall", flameWallOrigin, angles, owner, 0.2f, 0.5f, 2.7f );
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
|
||||
stateResult_t rvmMonsterDemonArchvile::spawn_incerate( stateParms_t* parms ) {
|
||||
// Hook point for the original script's incinerate projectile spawn.
|
||||
idAngles angles = owner ? owner->GetPhysics()->GetAxis().ToAngles() : ang_zero;
|
||||
angles.yaw = RandomFloat( 360.0f );
|
||||
SpawnArchvileTimedTrigger( "archvile_incinerate", incinerateOrigin, angles, owner, 0.4f, 0.4f, 1.4f );
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
|
||||
stateResult_t rvmMonsterDemonArchvile::flame_wall_loop( stateParms_t* parms ) {
|
||||
enum { STAGE_INIT, STAGE_SPAWN, STAGE_DELAY };
|
||||
enum { STAGE_INIT, STAGE_TRACE, STAGE_DELAY };
|
||||
switch ( parms->stage ) {
|
||||
case STAGE_INIT:
|
||||
scratchCount = 0;
|
||||
parms->stage = STAGE_SPAWN;
|
||||
flameWallOrigin = GetOrigin();
|
||||
scratchEntity = NULL;
|
||||
parms->stage = STAGE_TRACE;
|
||||
return SRESULT_WAIT;
|
||||
case STAGE_SPAWN:
|
||||
if ( scratchCount >= ARCH_FLAME_WALL_NUM ) { return SRESULT_DONE; }
|
||||
flameWallOrigin = GetEnemyPos();
|
||||
StartAction( "spawn_flame_wall" );
|
||||
case STAGE_TRACE: {
|
||||
if ( scratchCount >= ARCH_FLAME_WALL_NUM || !owner ) { return SRESULT_DONE; }
|
||||
idAngles angles = owner->GetPhysics()->GetAxis().ToAngles();
|
||||
idVec3 dir = angles.ToForward() * ARCH_FLAME_WALL_SEPERATION;
|
||||
idVec3 pos = flameWallOrigin;
|
||||
const idBounds bounds( idVec3( -8.0f, -8.0f, 0.0f ), idVec3( 8.0f, 8.0f, 16.0f ) );
|
||||
trace_t trace;
|
||||
|
||||
pos.z += 48.0f;
|
||||
gameLocal.clip.TraceBounds( trace, pos, pos + dir, bounds, MASK_SOLID, owner );
|
||||
if ( trace.fraction < 1.0f ) {
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
pos += dir;
|
||||
gameLocal.clip.TraceBounds( trace, pos, pos - idVec3( 0.0f, 0.0f, 96.0f ), bounds, MASK_SOLID, owner );
|
||||
if ( trace.fraction >= 1.0f ) {
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
pos.z -= 96.0f * trace.fraction;
|
||||
flameWallOrigin = pos;
|
||||
spawn_flame_wall( parms );
|
||||
scratchCount++;
|
||||
scratchEndTime = DelayTime( 0.05f );
|
||||
scratchEndTime = DelayTime( 0.075f );
|
||||
parms->stage = STAGE_DELAY;
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
case STAGE_DELAY:
|
||||
if ( InAction() || Time() < scratchEndTime ) { return SRESULT_WAIT; }
|
||||
parms->stage = STAGE_SPAWN;
|
||||
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
|
||||
parms->stage = STAGE_TRACE;
|
||||
return SRESULT_WAIT;
|
||||
}
|
||||
return SRESULT_ERROR;
|
||||
}
|
||||
|
||||
stateResult_t rvmMonsterDemonArchvile::flame_wall( stateParms_t* parms ) {
|
||||
if ( parms->stage == 0 ) { StartAction( "flame_wall_loop" ); parms->stage = 1; return SRESULT_WAIT; }
|
||||
return InAction() ? SRESULT_WAIT : SRESULT_DONE;
|
||||
return flame_wall_loop( parms );
|
||||
}
|
||||
|
||||
stateResult_t rvmMonsterDemonArchvile::incinerate( stateParms_t* parms ) {
|
||||
incinerateOrigin = GetEnemyPos();
|
||||
StartAction( "spawn_incerate" );
|
||||
incinerateOrigin = PredictEnemyPos( 0.4f );
|
||||
spawn_incerate( parms );
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -414,7 +414,20 @@ stateResult_t rvmMonsterDemonTrite::state_Explode( stateParms_t* parms ) {
|
||||
enum { STAGE_START, STAGE_REMOVE };
|
||||
switch ( parms->stage ) {
|
||||
case STAGE_START:
|
||||
RadiusDamageFromJoint( "", GetKey( "def_explode_damage", "damage_trite_explode" ) );
|
||||
if ( owner ) {
|
||||
idEntity* explosion = NULL;
|
||||
idDict args;
|
||||
args.Set( "classname", "func_emitter" );
|
||||
args.Set( "model", GetKey( "model_explode" ) );
|
||||
if ( gameLocal.SpawnEntityDef( args, &explosion ) && explosion ) {
|
||||
explosion->Show();
|
||||
explosion->SetOrigin( GetOrigin() );
|
||||
explosion->SetShaderParm( SHADERPARM_TIMEOFFSET, -Time() );
|
||||
explosion->SetShaderParm( SHADERPARM_DIVERSITY, RandomFloat( 1.0f ) );
|
||||
explosion->PostEventSec( &EV_Remove, 5.0f );
|
||||
}
|
||||
gameLocal.RadiusDamage( GetOrigin(), owner, owner, owner, owner, GetKey( "def_explode_damage", "damage_trite_explode" ), 1.0f );
|
||||
}
|
||||
StartSoundKey( "snd_explode", SND_CHANNEL_BODY, false );
|
||||
if ( owner ) { owner->ActivateTargets( owner ); }
|
||||
HideOwner();
|
||||
|
||||
@@ -23,6 +23,14 @@ void rvmMonsterDemonWraith::Init( idAI* aiOwner ) {
|
||||
SetState( "state_Begin" );
|
||||
}
|
||||
|
||||
void rvmMonsterDemonWraith::Destroy() {
|
||||
if ( spawn_effect.GetEntity() ) {
|
||||
spawn_effect.GetEntity()->PostEventMS( &EV_Remove, 0 );
|
||||
spawn_effect = NULL;
|
||||
}
|
||||
rvmMonsterBase::Destroy();
|
||||
}
|
||||
|
||||
stateResult_t rvmMonsterDemonWraith::RunStateByName( const char* stateName, stateParms_t* parms ) {
|
||||
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
|
||||
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
|
||||
@@ -249,8 +257,22 @@ stateResult_t rvmMonsterDemonWraith::combat_dodge_right( stateParms_t* parms ) {
|
||||
}
|
||||
|
||||
stateResult_t rvmMonsterDemonWraith::spawn_fx( stateParms_t* parms ) {
|
||||
// Project-specific effect spawning is intentionally isolated here.
|
||||
// Bind your wraith_spawneffect entity to spawn_effect if desired.
|
||||
if ( !owner ) {
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
if ( !spawn_effect.GetEntity() ) {
|
||||
idDict args;
|
||||
args.Set( "classname", "wraith_spawneffect" );
|
||||
idEntity* ent = NULL;
|
||||
if ( gameLocal.SpawnEntityDef( args, &ent ) ) {
|
||||
spawn_effect = ent;
|
||||
}
|
||||
}
|
||||
if ( spawn_effect.GetEntity() ) {
|
||||
spawn_effect.GetEntity()->SetOrigin( GetOrigin() );
|
||||
spawn_effect.GetEntity()->Show();
|
||||
spawn_effect.GetEntity()->SetShaderParm( SHADERPARM_TIMEOFFSET, -Time() );
|
||||
}
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
|
||||
@@ -270,13 +292,20 @@ stateResult_t rvmMonsterDemonWraith::combat_become_visible( stateParms_t* parms
|
||||
AllowMovement( true );
|
||||
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
|
||||
StartSoundKey( "snd_fade_in", SND_CHANNEL_VOICE2, false );
|
||||
ShowOwner();
|
||||
scratchEndTime = DelayTime( 0.75f );
|
||||
spawn_fx( parms );
|
||||
scratchEndTime = DelayTime( 0.65f );
|
||||
parms->stage = STAGE_FADE;
|
||||
return SRESULT_WAIT;
|
||||
case STAGE_FADE:
|
||||
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
|
||||
ShowOwner();
|
||||
if ( owner ) {
|
||||
owner->SetShaderParm( SHADERPARM_TIME_OF_DEATH, Time() );
|
||||
}
|
||||
ClearBurn();
|
||||
if ( spawn_effect.GetEntity() ) {
|
||||
spawn_effect.GetEntity()->Hide();
|
||||
}
|
||||
invisible = false;
|
||||
nextInvisible = RandomDelay( WRAITH_INVISIBLE_DELAY_MIN, WRAITH_INVISIBLE_DELAY_MAX );
|
||||
return SRESULT_DONE;
|
||||
@@ -292,6 +321,7 @@ stateResult_t rvmMonsterDemonWraith::combat_become_invisible( stateParms_t* parm
|
||||
if ( invisible ) { return SRESULT_DONE; }
|
||||
invisible = true;
|
||||
StartSoundKey( "snd_fade_out", SND_CHANNEL_VOICE2, false );
|
||||
spawn_fx( parms );
|
||||
scratchEndTime = DelayTime( 0.817f );
|
||||
parms->stage = STAGE_FADEOUT;
|
||||
return SRESULT_WAIT;
|
||||
@@ -299,7 +329,13 @@ stateResult_t rvmMonsterDemonWraith::combat_become_invisible( stateParms_t* parm
|
||||
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
|
||||
PreBurn();
|
||||
HideOwner();
|
||||
if ( spawn_effect.GetEntity() ) {
|
||||
spawn_effect.GetEntity()->Hide();
|
||||
}
|
||||
if ( owner ) { owner->AllowHiddenMovement( 1 ); }
|
||||
if ( owner ) {
|
||||
owner->SetShaderParm( SHADERPARM_TIME_OF_DEATH, Time() - 100.0f );
|
||||
}
|
||||
nextInvisible = RandomDelay( WRAITH_VISIBLE_DELAY_MIN, WRAITH_VISIBLE_DELAY_MAX );
|
||||
return SRESULT_DONE;
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
|
||||
rvmMonsterDemonWraith();
|
||||
virtual void Init( idAI* aiOwner ) override;
|
||||
virtual void Destroy() override;
|
||||
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
|
||||
|
||||
protected:
|
||||
|
||||
Reference in New Issue
Block a user