Missing file from previous checkin.

This commit is contained in:
Justin Marshall
2026-05-23 14:30:21 -07:00
parent 027067a122
commit df82170a67
68 changed files with 15720 additions and 0 deletions
@@ -0,0 +1,506 @@
// AI_alphalabs2_scientist1.cpp
// Native state-machine replacement for ai_alphalabs2_scientist1.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmAIObject, rvmAIAlphaLabs2Scientist1 )
END_CLASS
void rvmAIAlphaLabs2Scientist1::Init( idAI* aiOwner ) {
rvmAIObject::Init( aiOwner );
SetState( "state_Begin" );
}
void rvmAIAlphaLabs2Scientist1::Destroy() {
rvmAIObject::Destroy();
}
stateResult_t rvmAIAlphaLabs2Scientist1::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Head_Idle", Head_Idle );
AI_DISPATCH_STATE( "Head_Death", Head_Death );
AI_DISPATCH_STATE( "Head_DeathScene", Head_DeathScene );
AI_DISPATCH_STATE( "Head_Greeting", Head_Greeting );
AI_DISPATCH_STATE( "Head_Speech", Head_Speech );
AI_DISPATCH_STATE( "Head_Hurry", Head_Hurry );
AI_DISPATCH_STATE( "Head_StayClose", Head_StayClose );
AI_DISPATCH_STATE( "Head_Electro_Pulses", Head_Electro_Pulses );
AI_DISPATCH_STATE( "Head_Pulse1", Head_Pulse1 );
AI_DISPATCH_STATE( "Head_Pulse2", Head_Pulse2 );
AI_DISPATCH_STATE( "Head_WatchIt", Head_WatchIt );
AI_DISPATCH_STATE( "Head_Spooked", Head_Spooked );
AI_DISPATCH_STATE( "Head_Exit", Head_Exit );
AI_DISPATCH_STATE( "Head_SayStuff", Head_SayStuff );
AI_DISPATCH_STATE( "Head_Attacked", Head_Attacked );
AI_DISPATCH_STATE( "Head_AttackDone", Head_AttackDone );
AI_DISPATCH_STATE( "Torso_Work", Torso_Work );
AI_DISPATCH_STATE( "Torso_DeathScene", Torso_DeathScene );
AI_DISPATCH_STATE( "Torso_Death", Torso_Death );
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Legs_Death", Legs_Death );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_TurnLeft", Legs_TurnLeft );
AI_DISPATCH_STATE( "Legs_TurnRight", Legs_TurnRight );
AI_DISPATCH_STATE( "Legs_Cower", Legs_Cower );
AI_DISPATCH_STATE( "light_fade1", light_fade1 );
AI_DISPATCH_STATE( "light_fade2", light_fade2 );
AI_DISPATCH_STATE( "doEvent", doEvent );
AI_DISPATCH_STATE( "sayStuff", sayStuff );
AI_DISPATCH_STATE( "hurry_up", hurry_up );
AI_DISPATCH_STATE( "updatePlayerPositionThread", updatePlayerPositionThread );
AI_DISPATCH_STATE( "checkBlocked", checkBlocked );
AI_DISPATCH_STATE( "state_Killed", state_Killed );
AI_DISPATCH_STATE( "state_DeathScene", state_DeathScene );
AI_DISPATCH_STATE( "state_WorkingOnStuff", state_WorkingOnStuff );
AI_DISPATCH_STATE( "state_NoticePlayer", state_NoticePlayer );
AI_DISPATCH_STATE( "state_Wait", state_Wait );
AI_DISPATCH_STATE( "state_GetCloser", state_GetCloser );
AI_DISPATCH_STATE( "state_CantReachPlayer", state_CantReachPlayer );
AI_DISPATCH_STATE( "state_Spooked", state_Spooked );
AI_DISPATCH_STATE( "state_Lead", state_Lead );
AI_DISPATCH_STATE( "state_FindPlayer", state_FindPlayer );
AI_DISPATCH_STATE( "state_Cower", state_Cower );
return rvmAIObject::RunStateByName( stateName, parms );
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_HEAD, "stand" ); parms->stage = 1; }
return SRESULT_WAIT;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_Death( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_DeathScene( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_Greeting( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_Speech( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_Hurry( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_StayClose( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_Electro_Pulses( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_Pulse1( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_Pulse2( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_WatchIt( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_Spooked( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_Exit( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_SayStuff( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_Attacked( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Head_AttackDone( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Torso_Work( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Torso_DeathScene( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Torso_Death( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "stand" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { FinishAction( "pain" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Legs_Death( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_LEGS, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "stand" ); parms->stage = 1; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Legs_TurnLeft( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_LEGS, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Legs_TurnRight( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_LEGS, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::Legs_Cower( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_LEGS, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAIAlphaLabs2Scientist1::light_fade1( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::light_fade2( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::doEvent( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::sayStuff( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::hurry_up( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::updatePlayerPositionThread( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::checkBlocked( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::state_Killed( stateParms_t* parms ) {
StopMove();
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 0 );
StopThinking();
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::state_DeathScene( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::state_WorkingOnStuff( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::state_NoticePlayer( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::state_Wait( stateParms_t* parms ) {
if ( GetEnemy() ) { SetState( "state_Combat" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmAIAlphaLabs2Scientist1::state_GetCloser( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::state_CantReachPlayer( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::state_Spooked( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::state_Lead( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::state_FindPlayer( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAIAlphaLabs2Scientist1::state_Cower( stateParms_t* parms ) {
return SRESULT_DONE;
}
void rvmAIAlphaLabs2Scientist1::faceTowardsEntity( idEntity* target, float duration ) {
}
void rvmAIAlphaLabs2Scientist1::light_fade_out( float fadeTime ) {
}
void rvmAIAlphaLabs2Scientist1::light_fade_in( float fadeTime ) {
}
void rvmAIAlphaLabs2Scientist1::light_fade( float stay_off ) {
}
float rvmAIAlphaLabs2Scientist1::checkDestinationDistance( ) {
return 0.0f;
}
@@ -0,0 +1,67 @@
// AI_alphalabs2_scientist1.h
// Native state-machine replacement for ai_alphalabs2_scientist1.script.
#pragma once
class rvmAIAlphaLabs2Scientist1 : public rvmAIObject {
public:
CLASS_PROTOTYPE( rvmAIAlphaLabs2Scientist1 );
virtual void Init( idAI* aiOwner ) override;
virtual void Destroy() override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
virtual stateResult_t Head_Idle( stateParms_t* parms );
virtual stateResult_t Head_Death( stateParms_t* parms );
virtual stateResult_t Head_DeathScene( stateParms_t* parms );
virtual stateResult_t Head_Greeting( stateParms_t* parms );
virtual stateResult_t Head_Speech( stateParms_t* parms );
virtual stateResult_t Head_Hurry( stateParms_t* parms );
virtual stateResult_t Head_StayClose( stateParms_t* parms );
virtual stateResult_t Head_Electro_Pulses( stateParms_t* parms );
virtual stateResult_t Head_Pulse1( stateParms_t* parms );
virtual stateResult_t Head_Pulse2( stateParms_t* parms );
virtual stateResult_t Head_WatchIt( stateParms_t* parms );
virtual stateResult_t Head_Spooked( stateParms_t* parms );
virtual stateResult_t Head_Exit( stateParms_t* parms );
virtual stateResult_t Head_SayStuff( stateParms_t* parms );
virtual stateResult_t Head_Attacked( stateParms_t* parms );
virtual stateResult_t Head_AttackDone( stateParms_t* parms );
virtual stateResult_t Torso_Work( stateParms_t* parms );
virtual stateResult_t Torso_DeathScene( stateParms_t* parms );
virtual stateResult_t Torso_Death( stateParms_t* parms );
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Legs_Death( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_TurnLeft( stateParms_t* parms );
virtual stateResult_t Legs_TurnRight( stateParms_t* parms );
virtual stateResult_t Legs_Cower( stateParms_t* parms );
virtual stateResult_t light_fade1( stateParms_t* parms );
virtual stateResult_t light_fade2( stateParms_t* parms );
virtual stateResult_t doEvent( stateParms_t* parms );
virtual stateResult_t sayStuff( stateParms_t* parms );
virtual stateResult_t hurry_up( stateParms_t* parms );
virtual stateResult_t updatePlayerPositionThread( stateParms_t* parms );
virtual stateResult_t checkBlocked( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
virtual stateResult_t state_DeathScene( stateParms_t* parms );
virtual stateResult_t state_WorkingOnStuff( stateParms_t* parms );
virtual stateResult_t state_NoticePlayer( stateParms_t* parms );
virtual stateResult_t state_Wait( stateParms_t* parms );
virtual stateResult_t state_GetCloser( stateParms_t* parms );
virtual stateResult_t state_CantReachPlayer( stateParms_t* parms );
virtual stateResult_t state_Spooked( stateParms_t* parms );
virtual stateResult_t state_Lead( stateParms_t* parms );
virtual stateResult_t state_FindPlayer( stateParms_t* parms );
virtual stateResult_t state_Cower( stateParms_t* parms );
virtual void faceTowardsEntity( idEntity* target, float duration );
virtual void light_fade_out( float fadeTime );
virtual void light_fade_in( float fadeTime );
virtual void light_fade( float stay_off );
virtual float checkDestinationDistance( );
};
+874
View File
@@ -0,0 +1,874 @@
// AI_base.cpp
// Native state-machine replacement for ai_base.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( idClass, rvmAIObject )
END_CLASS
rvmAIObject::rvmAIObject() {
owner = NULL;
scratchEndTime = 0.0f;
scratchMinTime = 0.0f;
scratchNextTime = 0.0f;
scratchCount = 0.0f;
scratchFlags = 0.0f;
scratchBool = false;
scratchEntity = NULL;
ClearParms( stateParms );
ClearParms( actionParms );
for ( int i = 0; i < AI_NATIVE_MAX_CHANNELS; ++i ) {
ClearParms( animParms[i] );
}
}
rvmAIObject::~rvmAIObject() {
Destroy();
}
void rvmAIObject::Init( idAI* aiOwner ) {
owner = aiOwner;
currentState.Clear();
currentAction.Clear();
waitActionName.Clear();
for ( int i = 0; i < AI_NATIVE_MAX_CHANNELS; ++i ) {
currentAnimState[i].Clear();
ClearParms( animParms[i] );
}
ClearParms( stateParms );
ClearParms( actionParms );
}
void rvmAIObject::Destroy() {
owner = NULL;
currentState.Clear();
currentAction.Clear();
waitActionName.Clear();
}
void rvmAIObject::Update() {
for ( int i = 0; i < AI_NATIVE_MAX_CHANNELS; ++i ) {
if ( currentAnimState[i].Length() ) {
RunStateByName( currentAnimState[i].c_str(), &animParms[i] );
}
}
if ( currentAction.Length() ) {
stateResult_t result = RunStateByName( currentAction.c_str(), &actionParms );
if ( result != SRESULT_WAIT ) {
currentAction.Clear();
ClearParms( actionParms );
}
return;
}
if ( currentState.Length() ) {
RunStateByName( currentState.c_str(), &stateParms );
}
}
void rvmAIObject::SetState( const char* stateName ) {
currentState = stateName ? stateName : "";
ClearParms( stateParms );
}
void rvmAIObject::SetAnimState( int channel, const char* stateName, int blendFrames ) {
if ( channel == ANIMCHANNEL_ALL ) {
SetAnimState( ANIMCHANNEL_HEAD, stateName, blendFrames );
SetAnimState( ANIMCHANNEL_TORSO, stateName, blendFrames );
SetAnimState( ANIMCHANNEL_LEGS, stateName, blendFrames );
return;
}
const int index = ChannelIndex( channel );
if ( index < 0 ) {
return;
}
currentAnimState[index] = stateName ? stateName : "";
ClearParms( animParms[index] );
SetBlendFrames( channel, blendFrames );
if ( owner ) {
owner->Event_EnableAnim( channel, blendFrames );
if ( channel == ANIMCHANNEL_TORSO ) {
owner->Event_EnableAnim( ANIMCHANNEL_LEGS, blendFrames );
} else if ( channel == ANIMCHANNEL_LEGS ) {
owner->Event_EnableAnim( ANIMCHANNEL_TORSO, blendFrames );
}
}
}
bool rvmAIObject::InAnimState( int channel, const char* stateName ) const {
const int index = ChannelIndex( channel );
if ( index < 0 || !stateName ) {
return false;
}
return !idStr::Icmp( currentAnimState[index].c_str(), stateName );
}
void rvmAIObject::StartAction( const char* actionName ) {
currentAction = actionName ? actionName : "";
ClearParms( actionParms );
}
bool rvmAIObject::InAction() const {
return currentAction.Length() != 0;
}
void rvmAIObject::FinishAction( const char* actionName ) {
if ( !actionName || !waitActionName.Length() || !idStr::Icmp( waitActionName.c_str(), actionName ) ) {
waitActionName.Clear();
}
}
bool rvmAIObject::ActionDone( const char* actionName ) const {
if ( !waitActionName.Length() ) {
return true;
}
return actionName && idStr::Icmp( waitActionName.c_str(), actionName ) != 0;
}
stateResult_t rvmAIObject::RunStateByName( const char* stateName, stateParms_t* parms ) {
return SRESULT_DONE;
}
int rvmAIObject::ChannelIndex( int channel ) const {
if ( channel == ANIMCHANNEL_HEAD ) {
return 0;
}
if ( channel == ANIMCHANNEL_TORSO ) {
return 1;
}
if ( channel == ANIMCHANNEL_LEGS ) {
return 2;
}
if ( channel == ANIMCHANNEL_ALL ) {
return 3;
}
return -1;
}
void rvmAIObject::ClearParms( stateParms_t& parms ) {
memset( &parms, 0, sizeof( parms ) );
}
float rvmAIObject::Time() const {
return MS2SEC( gameLocal.time );
}
float rvmAIObject::FrameTime() const {
return MS2SEC( gameLocal.msec );
}
float rvmAIObject::RandomFloat( float max ) const {
return gameLocal.random.RandomFloat() * max;
}
float rvmAIObject::RandomDelay( float minSeconds, float maxSeconds ) const {
return Time() + minSeconds + RandomFloat( maxSeconds - minSeconds );
}
float rvmAIObject::DelayTime( float seconds ) const {
return Time() + seconds;
}
float rvmAIObject::RandomTime( float seconds ) const {
return Time() + RandomFloat( seconds );
}
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* rvmAIObject::Player() const {
return gameLocal.GetLocalPlayer();
}
void rvmAIObject::IdleAnim( int channel, const char* animName ) {
if ( owner ) {
owner->Event_IdleAnim( channel, animName );
}
}
void rvmAIObject::PlayCycle( int channel, const char* animName ) {
if ( owner ) {
owner->Event_PlayCycle( channel, animName, 1.0f);
}
}
void rvmAIObject::PlayAnim( int channel, const char* animName, bool blend ) {
if ( owner ) {
// idActor::Event_PlayAnim takes channel + anim name; the weapon-native bool
// parameter does not exist on actors.
owner->Event_PlayAnim( channel, animName, blend );
}
}
void rvmAIObject::StopAnim( int channel, int frames ) {
if ( owner ) {
owner->Event_StopAnim( channel, frames );
}
}
bool rvmAIObject::AnimDone( int channel, int blendFrames ) const {
return owner ? owner->AnimDone( channel, blendFrames ) : true;
}
bool rvmAIObject::HasAnim( int channel, const char* animName ) const {
return owner ? owner->GetAnim( channel, animName ) != 0 : false;
}
const char* rvmAIObject::GetPainAnim() const {
if ( !owner ) {
return "pain";
}
const char* animName = owner->spawnArgs.GetString( "anim_pain", "" );
if ( animName && animName[0] ) {
return animName;
}
animName = owner->spawnArgs.GetString( "pain_anim", "" );
if ( animName && animName[0] ) {
return animName;
}
return "pain";
}
void rvmAIObject::SetBlendFrames( int channel, int blendFrames ) {
if ( owner ) {
owner->Event_SetBlendFrames( channel, blendFrames );
}
}
void rvmAIObject::OverrideAnim( int channel ) {
if ( owner ) {
owner->Event_OverrideAnim( channel );
}
}
void rvmAIObject::SetAnimPrefix( const char* prefix ) {
if ( owner ) {
owner->Event_SetAnimPrefix( prefix );
}
}
void rvmAIObject::PreventPain( float seconds ) {
if ( owner ) {
owner->Event_PreventPain( seconds );
}
}
void rvmAIObject::LookAtEnemy( float seconds ) {
if ( owner ) {
owner->LookAtEnemy( seconds );
}
}
void rvmAIObject::LookAtEntity( idEntity* ent, float seconds ) {
if ( owner ) {
owner->LookAtEntity( ent, seconds );
}
}
void rvmAIObject::FaceEnemy() {
if ( owner ) {
owner->FaceEnemy();
}
}
void rvmAIObject::FaceEntity( idEntity* ent ) {
if ( owner ) {
owner->FaceEntity( ent );
}
}
bool rvmAIObject::FacingIdeal() const {
return owner ? owner->FacingIdeal() : true;
}
void rvmAIObject::StopMove() {
if ( owner ) {
owner->StopMove();
}
}
void rvmAIObject::MoveToEnemy() {
if ( owner ) {
owner->MoveToEnemy();
}
}
void rvmAIObject::MoveToEnemyHeight() {
if ( owner ) {
owner->MoveToEnemyHeight();
}
}
void rvmAIObject::MoveToEntity( idEntity* ent ) {
if ( owner ) {
owner->MoveToEntity( ent );
}
}
void rvmAIObject::MoveToPosition( const idVec3& pos ) {
if ( owner ) {
owner->MoveToPosition( pos );
}
}
void rvmAIObject::MoveToAttackPosition( idEntity* ent, const char* animName ) {
if ( owner && ent && animName && animName[0] ) {
const int anim = owner->GetAnim( ANIMCHANNEL_LEGS, animName );
if ( anim ) {
owner->MoveToAttackPosition( ent, anim );
}
}
}
void rvmAIObject::MoveOutOfRange( idEntity* ent, float range ) {
if ( owner ) {
owner->MoveOutOfRange( ent, range );
}
}
void rvmAIObject::MoveToCover() {
if ( owner ) {
owner->MoveToCover();
}
}
void rvmAIObject::Wander() {
if ( owner ) {
owner->Wander();
}
}
bool rvmAIObject::MoveDone() const {
return owner ? owner->MoveDone() : true;
}
int rvmAIObject::MoveStatus() const {
return owner ? owner->MoveStatus() : MOVE_STATUS_DONE;
}
bool rvmAIObject::DestinationUnreachable() const {
return owner ? owner->AI_DEST_UNREACHABLE != 0 : true;
}
idActor* rvmAIObject::GetEnemy() const {
return owner ? owner->GetEnemy() : NULL;
}
void rvmAIObject::SetEnemy( idEntity* ent ) {
if ( owner ) {
owner->SetEnemy( ent );
}
}
void rvmAIObject::ClearEnemy() {
if ( owner ) {
owner->ClearEnemy();
}
}
bool rvmAIObject::EnemyVisible() const {
return owner ? owner->AI_ENEMY_VISIBLE != 0 : false;
}
bool rvmAIObject::EnemyInFOV() const {
return owner ? owner->AI_ENEMY_IN_FOV != 0 : false;
}
bool rvmAIObject::EnemyDead() const {
return owner ? owner->AI_ENEMY_DEAD != 0 : true;
}
float rvmAIObject::EnemyRange() const {
return owner ? owner->EnemyRange() : idMath::INFINITY;
}
bool rvmAIObject::CheckForMelee() const {
return owner ? owner->TestMeleeAttack() != 0 : false;
}
bool rvmAIObject::CanHitEnemy() const {
return owner ? owner->CanHitEnemy() != 0 : false;
}
bool rvmAIObject::CanHitEnemyFromAnim( const char* animName ) const {
return owner ? owner->CanHitEnemyFromAnim( animName ) != 0 : false;
}
bool rvmAIObject::EntityInAttackCone( idEntity* ent ) const {
return owner ? owner->EntityInAttackCone( ent ) != 0 : false;
}
bool rvmAIObject::CanReachEnemy() const {
return owner ? owner->CanReachEnemy() != 0 : false;
}
bool rvmAIObject::CanReachEntity( idEntity* ent ) const {
return owner ? owner->CanReachEntity( ent ) != 0 : false;
}
bool rvmAIObject::CanSeeEntity( idEntity* ent ) const {
return owner ? owner->CanSeeEntity( ent ) != 0 : false;
}
void rvmAIObject::LocateEnemy() {
if ( owner ) {
owner->LocateEnemy();
}
}
bool rvmAIObject::AttackMelee( const char* meleeDefName ) {
return owner ? owner->AttackMelee( meleeDefName ) != 0 : false;
}
idEntity* rvmAIObject::AttackMissile( const char* jointName ) {
return owner ? owner->AttackMissile( jointName ) : NULL;
}
void rvmAIObject::MuzzleFlash( const char* jointName ) {
if ( owner ) {
owner->MuzzleFlash( jointName );
}
}
idEntity* rvmAIObject::GetCombatNode() {
return owner ? owner->GetCombatNode() : NULL;
}
bool rvmAIObject::Pain() const {
return owner ? owner->AI_PAIN != 0 : false;
}
bool rvmAIObject::Activated() const {
return owner ? owner->AI_ACTIVATED != 0 : false;
}
void rvmAIObject::ClearActivated() {
if ( owner ) {
owner->AI_ACTIVATED = false;
}
}
bool rvmAIObject::MovingForward() const {
return owner ? owner->AI_FORWARD != 0 : false;
}
bool rvmAIObject::Blocked() const {
return owner ? owner->AI_BLOCKED != 0 : false;
}
bool rvmAIObject::ObstacleInPath() const {
return owner ? owner->AI_OBSTACLE_IN_PATH != 0 : false;
}
void rvmAIObject::AllowMovement( bool allow ) {
if ( owner ) {
owner->AllowMovement( allow ? 1.0f : 0.0f );
}
}
void rvmAIObject::SetMoveType( int moveType ) {
if ( owner ) {
owner->SetMoveType( moveType );
}
}
float rvmAIObject::GetTurnDelta() const {
return owner ? owner->GetTurnDelta() : 0.0f;
}
void rvmAIObject::TurnTo( float yaw ) {
if ( owner ) {
owner->TurnTo( yaw );
}
}
float rvmAIObject::CurrentYaw() const {
return owner ? owner->GetCurrentYaw() : 0.0f;
}
void rvmAIObject::TurnToPos( const idVec3& pos ) {
if ( owner ) {
owner->TurnToPos( pos );
}
}
idVec3 rvmAIObject::GetOrigin() const {
return owner ? owner->GetPhysics()->GetOrigin() : vec3_zero;
}
idVec3 rvmAIObject::GetEnemyPos() const {
return owner ? owner->GetEnemyPos() : GetOrigin();
}
idVec3 rvmAIObject::PredictEnemyPos( float seconds ) const {
return owner ? owner->PredictEnemyPos( seconds ) : GetOrigin();
}
idVec3 rvmAIObject::GetLinearVelocity() const {
return owner ? owner->GetPhysics()->GetLinearVelocity() : vec3_zero;
}
void rvmAIObject::SetLinearVelocity( const idVec3& velocity ) {
if ( owner ) {
owner->GetPhysics()->SetLinearVelocity( velocity );
}
}
float rvmAIObject::AnimLength( int channel, const char* animName ) const {
if ( !owner || !animName || !animName[0] ) {
return 0.0f;
}
const int anim = owner->GetAnim( channel, animName );
if ( !anim || !owner->GetAnimator() ) {
return 0.0f;
}
return MS2SEC( owner->GetAnimator()->AnimLength( anim ) );
}
const char* rvmAIObject::ChooseAnim( int channel, const char* animName ) const {
if ( !owner || !animName || !animName[0] ) {
return "";
}
const int anim = owner->GetAnim( channel, animName );
if ( !anim ) {
return "";
}
if ( channel == ANIMCHANNEL_HEAD ) {
return animName;
}
return owner->GetAnimator() ? owner->GetAnimator()->AnimFullName( anim ) : "";
}
bool rvmAIObject::TestAnimMove( const char* animName ) const {
return owner ? owner->TestAnimMove( animName ) != 0 : false;
}
bool rvmAIObject::TestAnimMoveTowardEnemy( const char* animName ) const {
return owner ? owner->TestAnimMoveTowardEnemy( animName ) != 0 : false;
}
bool rvmAIObject::TestAnimAttack( const char* animName ) const {
return owner ? owner->TestAnimAttack( animName ) != 0 : false;
}
bool rvmAIObject::TestMoveToPosition( const idVec3& pos ) const {
return owner ? owner->TestMoveToPosition( pos ) != 0 : false;
}
float rvmAIObject::TravelDistanceToPoint( const idVec3& pos ) const {
return owner ? owner->TravelDistanceToPoint( pos ) : idMath::INFINITY;
}
float rvmAIObject::TravelDistanceToEntity( idEntity* ent ) const {
return ( owner && ent ) ? owner->TravelDistanceToEntity( ent ) : idMath::INFINITY;
}
float rvmAIObject::TravelDistanceBetweenPoints( const idVec3& source, const idVec3& dest ) const {
return owner ? owner->TravelDistanceBetweenPoints( source, dest ) : idMath::INFINITY;
}
idVec3 rvmAIObject::GetReachableEntityPosition( idEntity* ent ) const {
return ( owner && ent ) ? owner->GetReachableEntityPosition( ent ) : vec3_zero;
}
idVec3 rvmAIObject::GetJumpVelocity( const idVec3& pos, float speed, float maxHeight ) const {
return owner ? owner->GetJumpVelocity( pos, speed, maxHeight ) : vec3_zero;
}
void rvmAIObject::AttackBegin( const char* damageDefName ) {
if ( owner ) {
owner->BeginAttack( damageDefName );
}
}
void rvmAIObject::AttackEnd() {
if ( owner ) {
owner->EndAttack();
}
}
void rvmAIObject::ChargeAttack( const char* damageDefName ) {
if ( owner ) {
owner->ChargeAttack( damageDefName );
}
}
float rvmAIObject::TestChargeAttack() const {
return owner ? owner->TestChargeAttack() : 0.0f;
}
idEntity* rvmAIObject::CreateMissile( const char* jointName ) {
return owner ? owner->CreateMissile( jointName ) : NULL;
}
idEntity* rvmAIObject::FireMissileAtTarget( const char* jointName, const char* targetName ) {
return owner ? owner->FireMissileAtTarget( jointName, targetName ) : NULL;
}
idEntity* rvmAIObject::LaunchMissile( const idVec3& origin, const idAngles& angles ) {
return owner ? owner->LaunchMissile( origin, angles ) : NULL;
}
void rvmAIObject::DirectDamage( idEntity* target, const char* damageDefName ) {
if ( owner && target ) {
owner->DirectDamage( target, damageDefName );
}
}
void rvmAIObject::RadiusDamageFromJoint( const char* jointName, const char* damageDefName ) {
if ( owner ) {
owner->RadiusDamageFromJoint( jointName, damageDefName );
}
}
void rvmAIObject::SetFlySpeed( float speed ) {
if ( owner ) {
owner->SetFlySpeed( speed );
}
}
void rvmAIObject::SetFlyOffset( int offset ) {
if ( owner ) {
owner->SetFlyOffset( offset );
}
}
void rvmAIObject::ClearFlyOffset() {
if ( owner ) {
owner->ClearFlyOffset();
}
}
void rvmAIObject::SetHealth( float value ) {
if ( owner ) {
owner->SetHealth( value );
}
}
float rvmAIObject::GetHealth() const {
return owner ? owner->GetHealth() : 0.0f;
}
void rvmAIObject::AllowDamage() {
if ( owner ) {
owner->AllowDamage();
}
}
void rvmAIObject::IgnoreDamage() {
if ( owner ) {
owner->IgnoreDamage();
}
}
void rvmAIObject::BecomeSolid() {
if ( owner ) {
owner->BecomeSolid();
}
}
void rvmAIObject::BecomeNonSolid() {
if ( owner ) {
owner->BecomeNonSolid();
}
}
void rvmAIObject::PreBurn() {
if ( owner ) {
owner->PreBurn();
}
}
void rvmAIObject::Burn() {
if ( owner ) {
owner->Burn();
}
}
void rvmAIObject::ClearBurn() {
if ( owner ) {
owner->ClearBurn();
}
}
void rvmAIObject::KillEntity( idEntity* ent ) {
if ( !ent ) {
return;
}
if ( ent->IsType( idAI::Type ) ) {
static_cast<idAI*>( ent )->Kill();
} else {
ent->PostEventMS( &EV_Remove, 0 );
}
}
idEntity* rvmAIObject::RandomPathFrom( idEntity* from ) const {
return from ? idPathCorner::RandomPath( from, NULL ) : NULL;
}
idEntity* rvmAIObject::FindEnemy( bool useFOV ) {
return owner ? owner->FindEnemy( useFOV ? 1 : 0 ) : NULL;
}
idEntity* rvmAIObject::FindEnemyAI( bool useFOV ) {
return owner ? owner->FindEnemyAI( useFOV ? 1 : 0 ) : NULL;
}
idEntity* rvmAIObject::HeardSound( bool ignoreTeam ) {
return owner ? owner->HeardSound( ignoreTeam ? 1 : 0 ) : NULL;
}
idEntity* rvmAIObject::FindActorsInBounds( const idVec3& mins, const idVec3& maxs ) {
return owner ? owner->FindActorsInBounds( mins, maxs ) : NULL;
}
idEntity* rvmAIObject::ClosestReachableEnemyOfEntity( idEntity* teamMate ) {
return ( owner && teamMate ) ? owner->ClosestReachableEnemyOfEntity( teamMate ) : NULL;
}
idEntity* rvmAIObject::GetRandomTarget( const char* type ) {
return owner ? owner->GetRandomTarget( type ) : NULL;
}
idVec3 rvmAIObject::GetVectorKey( const char* key, const char* defaultValue ) const {
return owner ? owner->spawnArgs.GetVector( key, defaultValue ) : vec3_zero;
}
void rvmAIObject::HideEntity( idEntity* ent ) {
if ( ent ) {
ent->Hide();
}
}
void rvmAIObject::ShowEntity( idEntity* ent ) {
if ( ent ) {
ent->Show();
}
}
void rvmAIObject::RemoveEntity( idEntity* ent ) {
if ( ent ) {
ent->PostEventMS( &EV_Remove, 0 );
}
}
int rvmAIObject::GetIntKey( const char* key, const char* defaultValue ) const {
return owner ? owner->spawnArgs.GetInt( key, defaultValue ) : atoi( defaultValue );
}
int rvmAIObject::GetIntKey( const char* key, int defaultValue ) const {
if ( !owner ) {
return defaultValue;
}
return owner->spawnArgs.GetInt( key, va( "%d", defaultValue ) );
}
float rvmAIObject::GetFloatKey( const char* key, const char* defaultValue ) const {
return owner ? owner->spawnArgs.GetFloat( key, defaultValue ) : atof( defaultValue );
}
float rvmAIObject::GetFloatKey( const char* key, float defaultValue ) const {
if ( !owner ) {
return defaultValue;
}
return owner->spawnArgs.GetFloat( key, va( "%g", defaultValue ) );
}
const char* rvmAIObject::GetKey( const char* key, const char* defaultValue ) const {
return owner ? owner->spawnArgs.GetString( key, defaultValue ) : defaultValue;
}
idEntity* rvmAIObject::GetEntityKey( const char* key ) const {
if ( !owner ) {
return NULL;
}
const char* name = owner->spawnArgs.GetString( key, "" );
return name && name[0] ? gameLocal.FindEntity( name ) : NULL;
}
void rvmAIObject::HideOwner() {
if ( owner ) {
owner->Hide();
}
}
void rvmAIObject::ShowOwner() {
if ( owner ) {
owner->Show();
}
}
bool rvmAIObject::CanBecomeSolid() const {
return owner ? owner->CanBecomeSolid() != 0.0f : false;
}
void rvmAIObject::SetNeverDormant( bool value ) {
if ( owner ) {
owner->fl.neverDormant = value;
}
}
void rvmAIObject::WakeOnFlashlight( bool value ) {
if ( owner ) {
owner->WakeOnFlashlight( value ? 1 : 0 );
}
}
void rvmAIObject::StartSoundKey( const char* key, int channel, bool netSync ) {
if ( owner ) {
owner->StartSound( key, channel, netSync ? SSF_GLOBAL : 0, false, NULL );
}
}
void rvmAIObject::StartFx( const char* fx ) {
if ( owner && fx && fx[0] ) {
idEntityFx::StartFx( fx, NULL, NULL, owner, true );
}
}
void rvmAIObject::SetSmokeVisibility( int index, bool visible ) {
if ( owner ) {
owner->SetSmokeVisibility( index, visible ? 1 : 0 );
}
}
void rvmAIObject::TriggerParticles( const char* jointName ) {
if ( owner ) {
owner->TriggerParticles( jointName );
}
}
void rvmAIObject::BecomeRagdoll() {
if ( owner ) {
owner->BecomeRagdoll();
}
}
void rvmAIObject::StopThinking() {
if ( owner ) {
owner->StopThinking();
}
}
void rvmAIObject::RemoveOwner() {
if ( owner ) {
owner->PostEventMS( &EV_Remove, 0 );
}
}
+226
View File
@@ -0,0 +1,226 @@
// AI_base.h
// Native state-machine replacement for ai_base.script.
// Root object mirrors DoomScript object "ai" and owns an idAI native actor.
#pragma once
#ifndef ATTACK_DODGE_LEFT
#define ATTACK_DODGE_LEFT 1
#define ATTACK_DODGE_RIGHT 2
#define ATTACK_COMBAT_NODE 4
#define ATTACK_MELEE 8
#define ATTACK_LEAP 16
#define ATTACK_MISSILE 32
#define ATTACK_SPECIAL1 64
#define ATTACK_SPECIAL2 128
#define ATTACK_SPECIAL3 256
#define ATTACK_SPECIAL4 512
#endif
#ifndef AI_NOT_ACTIVATED
#define AI_NOT_ACTIVATED 0
#define AI_CHASING_ENEMY 1
#define AI_LOST 2
#define AI_PATH_FOLLOWING 3
#define AI_ATTACK_NODE 4
#endif
// The weapon native scripts use stateResult_t/stateParms_t. If this port is dropped
// into a branch where those are only declared in the weapon layer, include that header
// before this one or move the enum/struct into a shared script-state header.
class rvmAIObject : public idClass {
public:
CLASS_PROTOTYPE( rvmAIObject );
rvmAIObject();
virtual ~rvmAIObject();
virtual void Init( idAI* aiOwner );
virtual void Destroy();
virtual void Update();
virtual void SetState( const char* stateName );
virtual void SetAnimState( int channel, const char* stateName, int blendFrames );
virtual bool InAnimState( int channel, const char* stateName ) const;
virtual void StartAction( const char* actionName );
virtual bool InAction() const;
virtual void FinishAction( const char* actionName );
virtual bool ActionDone( const char* actionName ) const;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms );
protected:
static const int AI_NATIVE_MAX_CHANNELS = 4;
idAI* owner;
idStr currentState;
idStr currentAction;
idStr currentAnimState[AI_NATIVE_MAX_CHANNELS];
idStr waitActionName;
stateParms_t stateParms;
stateParms_t actionParms;
stateParms_t animParms[AI_NATIVE_MAX_CHANNELS];
float scratchEndTime;
float scratchMinTime;
float scratchNextTime;
float scratchCount;
float scratchFlags;
bool scratchBool;
idEntityPtr<idEntity> scratchEntity;
idStr scratchString;
virtual int ChannelIndex( int channel ) const;
virtual void ClearParms( stateParms_t& parms );
virtual float Time() const;
virtual float FrameTime() const;
virtual float RandomFloat( float max = 1.0f ) const;
virtual float RandomDelay( float minSeconds, float maxSeconds ) const;
virtual float DelayTime( float seconds ) const;
virtual float RandomTime( float seconds ) const;
virtual bool InfluenceActive() const;
virtual idPlayer* Player() const;
virtual void IdleAnim( int channel, const char* animName );
virtual void PlayCycle( int channel, const char* animName );
virtual void PlayAnim( int channel, const char* animName, bool blend = false );
virtual void StopAnim( int channel, int frames );
virtual bool AnimDone( int channel, int blendFrames ) const;
virtual bool HasAnim( int channel, const char* animName ) const;
virtual const char* GetPainAnim() const;
virtual void SetBlendFrames( int channel, int blendFrames );
virtual void OverrideAnim( int channel );
virtual void SetAnimPrefix( const char* prefix );
virtual void PreventPain( float seconds );
virtual void LookAtEnemy( float seconds );
virtual void LookAtEntity( idEntity* ent, float seconds );
virtual void FaceEnemy();
virtual void FaceEntity( idEntity* ent );
virtual bool FacingIdeal() const;
virtual void StopMove();
virtual void MoveToEnemy();
virtual void MoveToEnemyHeight();
virtual void MoveToEntity( idEntity* ent );
virtual void MoveToPosition( const idVec3& pos );
virtual void MoveToAttackPosition( idEntity* ent, const char* animName );
virtual void MoveOutOfRange( idEntity* ent, float range );
virtual void MoveToCover();
virtual void Wander();
virtual bool MoveDone() const;
virtual int MoveStatus() const;
virtual bool DestinationUnreachable() const;
virtual idActor* GetEnemy() const;
virtual void SetEnemy( idEntity* ent );
virtual void ClearEnemy();
virtual bool EnemyVisible() const;
virtual bool EnemyInFOV() const;
virtual bool EnemyDead() const;
virtual float EnemyRange() const;
virtual bool CheckForMelee() const;
virtual bool CanHitEnemy() const;
virtual bool CanHitEnemyFromAnim( const char* animName ) const;
virtual bool EntityInAttackCone( idEntity* ent ) const;
virtual bool CanReachEnemy() const;
virtual bool CanReachEntity( idEntity* ent ) const;
virtual bool CanSeeEntity( idEntity* ent ) const;
virtual void LocateEnemy();
virtual bool AttackMelee( const char* meleeDefName );
virtual idEntity* AttackMissile( const char* jointName );
virtual void MuzzleFlash( const char* jointName );
virtual idEntity* GetCombatNode();
virtual bool Pain() const;
virtual bool Activated() const;
virtual void ClearActivated();
virtual bool MovingForward() const;
virtual bool Blocked() const;
virtual bool ObstacleInPath() const;
virtual void AllowMovement( bool allow );
virtual void SetMoveType( int moveType );
virtual float GetTurnDelta() const;
virtual void TurnTo( float yaw );
virtual void TurnToPos( const idVec3& pos );
virtual float CurrentYaw() const;
virtual idVec3 GetOrigin() const;
virtual idVec3 GetEnemyPos() const;
virtual idVec3 PredictEnemyPos( float seconds ) const;
virtual idVec3 GetLinearVelocity() const;
virtual void SetLinearVelocity( const idVec3& velocity );
virtual float AnimLength( int channel, const char* animName ) const;
virtual const char* ChooseAnim( int channel, const char* animName ) const;
virtual bool TestAnimMove( const char* animName ) const;
virtual bool TestAnimMoveTowardEnemy( const char* animName ) const;
virtual bool TestAnimAttack( const char* animName ) const;
virtual bool TestMoveToPosition( const idVec3& pos ) const;
virtual float TravelDistanceToPoint( const idVec3& pos ) const;
virtual float TravelDistanceToEntity( idEntity* ent ) const;
virtual float TravelDistanceBetweenPoints( const idVec3& source, const idVec3& dest ) const;
virtual idVec3 GetReachableEntityPosition( idEntity* ent ) const;
virtual idVec3 GetJumpVelocity( const idVec3& pos, float speed, float maxHeight ) const;
virtual void AttackBegin( const char* damageDefName );
virtual void AttackEnd();
virtual void ChargeAttack( const char* damageDefName );
virtual float TestChargeAttack() const;
virtual idEntity* CreateMissile( const char* jointName );
virtual idEntity* FireMissileAtTarget( const char* jointName, const char* targetName );
virtual idEntity* LaunchMissile( const idVec3& origin, const idAngles& angles );
virtual void DirectDamage( idEntity* target, const char* damageDefName );
virtual void RadiusDamageFromJoint( const char* jointName, const char* damageDefName );
virtual void SetFlySpeed( float speed );
virtual void SetFlyOffset( int offset );
virtual void ClearFlyOffset();
virtual void SetHealth( float value );
virtual float GetHealth() const;
virtual void AllowDamage();
virtual void IgnoreDamage();
virtual void BecomeSolid();
virtual void BecomeNonSolid();
virtual void PreBurn();
virtual void Burn();
virtual void ClearBurn();
virtual void KillEntity( idEntity* ent );
virtual idEntity* RandomPathFrom( idEntity* from ) const;
virtual idEntity* FindEnemy( bool useFOV );
virtual idEntity* FindEnemyAI( bool useFOV );
virtual idEntity* HeardSound( bool ignoreTeam );
virtual idEntity* FindActorsInBounds( const idVec3& mins, const idVec3& maxs );
virtual idEntity* ClosestReachableEnemyOfEntity( idEntity* teamMate );
virtual idEntity* GetRandomTarget( const char* type );
virtual idVec3 GetVectorKey( const char* key, const char* defaultValue = "0 0 0" ) const;
virtual void HideEntity( idEntity* ent );
virtual void ShowEntity( idEntity* ent );
virtual void RemoveEntity( idEntity* ent );
virtual int GetIntKey( const char* key, const char* defaultValue = "0" ) const;
virtual int GetIntKey( const char* key, int defaultValue ) const;
virtual float GetFloatKey( const char* key, const char* defaultValue = "0" ) const;
virtual float GetFloatKey( const char* key, float defaultValue ) const;
virtual const char* GetKey( const char* key, const char* defaultValue = "" ) const;
virtual idEntity* GetEntityKey( const char* key ) const;
virtual void HideOwner();
virtual void ShowOwner();
virtual bool CanBecomeSolid() const;
virtual void SetNeverDormant( bool value );
virtual void WakeOnFlashlight( bool value );
virtual void StartSoundKey( const char* key, int channel, bool netSync = false );
virtual void StartFx( const char* fx );
virtual void SetSmokeVisibility( int index, bool visible );
virtual void TriggerParticles( const char* jointName );
virtual void BecomeRagdoll();
virtual void StopThinking();
virtual void RemoveOwner();
};
#define AI_DISPATCH_STATE( stateNameLiteral, methodName ) \
if ( !idStr::Icmp( stateName, stateNameLiteral ) ) { return methodName( parms ); }
File diff suppressed because it is too large Load Diff
+116
View File
@@ -0,0 +1,116 @@
// AI_character.h
// Native state-machine replacement for ai_character.script.
#pragma once
class rvmCharacter : public rvmAIObject {
public:
CLASS_PROTOTYPE( rvmCharacter );
rvmCharacter();
virtual void Init( idAI* aiOwner ) override;
virtual void Destroy() override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
enum {
CHAR_TALK_TRIGGERED = 0,
CHAR_TALK_PRIMARY = 1,
CHAR_TALK_SECONDARY = 2
};
float talkMode;
int talkSecondaryIndex;
idStr customAnim;
float customBlendOut;
idStr walkAnim;
bool allowTurn;
bool run;
bool canTalk;
bool noCower;
bool noCowerSaved;
bool ignorePush;
bool skipOutOfPath;
bool followNodes;
bool dontPushOthers;
float lastPushTime;
float pushTime;
float talkEndTime;
float talkChannel;
float talkOriginalYaw;
idEntityPtr<idEntity> current_path;
idEntityPtr<idEntity> next_path;
idEntityPtr<idEntity> listening_to_character;
idEntityPtr<idEntity> next_listener;
virtual stateResult_t Head_Idle( stateParms_t* parms );
virtual stateResult_t Anim_Disable( stateParms_t* parms );
virtual stateResult_t Head_Dead( stateParms_t* parms );
virtual stateResult_t Head_TalkHeadOnly( stateParms_t* parms );
virtual stateResult_t Legs_Death( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Walk_Special( stateParms_t* parms );
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t Legs_TurnLeft( stateParms_t* parms );
virtual stateResult_t Legs_TurnRight( stateParms_t* parms );
virtual stateResult_t Legs_Cower( stateParms_t* parms );
virtual stateResult_t Legs_CustomCycle( stateParms_t* parms );
virtual stateResult_t Legs_CustomAnim( stateParms_t* parms );
virtual stateResult_t state_Spawner( stateParms_t* parms );
virtual stateResult_t state_Cower( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_TalkTrigger( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
virtual stateResult_t idle_stand( stateParms_t* parms );
virtual stateResult_t idle_followPathEntities( stateParms_t* parms );
virtual stateResult_t idle_talk( stateParms_t* parms );
virtual stateResult_t checkBlocked( stateParms_t* parms );
virtual stateResult_t path_corner( stateParms_t* parms );
virtual stateResult_t path_anim( stateParms_t* parms );
virtual stateResult_t path_cycleanim( stateParms_t* parms );
virtual stateResult_t path_turn( stateParms_t* parms );
virtual stateResult_t path_wait( stateParms_t* parms );
virtual stateResult_t path_waitfortrigger( stateParms_t* parms );
virtual stateResult_t path_hide( stateParms_t* parms );
virtual stateResult_t path_show( stateParms_t* parms );
virtual stateResult_t path_conversation_listen( stateParms_t* parms );
virtual stateResult_t path_conversation( stateParms_t* parms );
virtual stateResult_t path_headanim( stateParms_t* parms );
virtual stateResult_t path_waitforheadanim( stateParms_t* parms );
virtual stateResult_t path_talk( stateParms_t* parms );
virtual stateResult_t path_talk_triggered( stateParms_t* parms );
virtual stateResult_t path_talk_primary( stateParms_t* parms );
virtual stateResult_t path_talk_secondary( stateParms_t* parms );
virtual stateResult_t path_lookat( stateParms_t* parms );
virtual stateResult_t state_FollowAlternatePath( stateParms_t* parms );
virtual void Anim_Disable( );
virtual void check_cower( );
virtual void target_talk( );
virtual void say_triggered( );
virtual void say_primary( );
virtual void say_secondary( );
virtual void faceTowardsEntity( idEntity* target, float duration );
virtual void playCustomCycle( const char* animname, float blendTime );
virtual void playCustomAnim( const char* animname, float blendIn, float blendOut );
virtual bool inCustomAnim( );
virtual void endCustomAnim( );
virtual void waitForCustomAnim( const char* animname, float blendIn, float blendOut );
virtual void endHeadAnim( float blend_out );
virtual float playHeadAnim( const char* animname, float blend_frames );
virtual void idle_followPathEntities( idEntity* pathnode );
virtual void executePathCommand( idEntity* pathnode );
virtual void finishPathCommand( );
virtual void cancelPathCommand( );
virtual bool getOutOfWay( );
virtual void skip_conversation( );
virtual void break_out_of_conversation( );
virtual void follow_alternate_path1( );
virtual void follow_alternate_path2( );
virtual void follow_alternate_path3( );
virtual void disable_turning( );
virtual void enable_turning( );
};
+160
View File
@@ -0,0 +1,160 @@
// AI_character_prone.cpp
// Native state-machine replacement for ai_character_prone.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmAIObject, rvmAICharacterProne )
END_CLASS
void rvmAICharacterProne::Init( idAI* aiOwner ) {
rvmAIObject::Init( aiOwner );
SetState( "state_Begin" );
}
void rvmAICharacterProne::Destroy() {
rvmAIObject::Destroy();
}
stateResult_t rvmAICharacterProne::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Dead", Torso_Dead );
AI_DISPATCH_STATE( "Torso_PlayDead", Torso_PlayDead );
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_TalkPrimary", Torso_TalkPrimary );
AI_DISPATCH_STATE( "Torso_TalkTrigger", Torso_TalkTrigger );
AI_DISPATCH_STATE( "Torso_TalkSecondary", Torso_TalkSecondary );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "state_Pain", state_Pain );
AI_DISPATCH_STATE( "state_Dead", state_Dead );
AI_DISPATCH_STATE( "state_Killed", state_Killed );
AI_DISPATCH_STATE( "idle_talk", idle_talk );
return rvmAIObject::RunStateByName( stateName, parms );
}
stateResult_t rvmAICharacterProne::Torso_Dead( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAICharacterProne::Torso_PlayDead( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAICharacterProne::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "stand" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmAICharacterProne::Torso_TalkPrimary( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAICharacterProne::Torso_TalkTrigger( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAICharacterProne::Torso_TalkSecondary( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAICharacterProne::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { FinishAction( "pain" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAICharacterProne::state_Idle( stateParms_t* parms ) {
if ( GetEnemy() ) { SetState( "state_Combat" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmAICharacterProne::state_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_HEAD, GetPainAnim() );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_HEAD, 4 ) ) { FinishAction( "pain" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmAICharacterProne::state_Dead( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmAICharacterProne::state_Killed( stateParms_t* parms ) {
StopMove();
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 0 );
StopThinking();
return SRESULT_DONE;
}
stateResult_t rvmAICharacterProne::idle_talk( stateParms_t* parms ) {
return SRESULT_DONE;
}
+28
View File
@@ -0,0 +1,28 @@
// AI_character_prone.h
// Native state-machine replacement for ai_character_prone.script.
#pragma once
class rvmAICharacterProne : public rvmAIObject {
public:
CLASS_PROTOTYPE( rvmAICharacterProne );
virtual void Init( idAI* aiOwner ) override;
virtual void Destroy() override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
virtual stateResult_t Torso_Dead( stateParms_t* parms );
virtual stateResult_t Torso_PlayDead( stateParms_t* parms );
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_TalkPrimary( stateParms_t* parms );
virtual stateResult_t Torso_TalkTrigger( stateParms_t* parms );
virtual stateResult_t Torso_TalkSecondary( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t state_Pain( stateParms_t* parms );
virtual stateResult_t state_Dead( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
virtual stateResult_t idle_talk( stateParms_t* parms );
};
File diff suppressed because it is too large Load Diff
+88
View File
@@ -0,0 +1,88 @@
// AI_character_sentry.h
// Native state-machine replacement for ai_character_sentry.script.
#pragma once
class rvmCharSentry : public rvmCharacter {
public:
CLASS_PROTOTYPE( rvmCharSentry );
rvmCharSentry();
virtual void Init( idAI* aiOwner ) override;
virtual void Destroy() override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
enum {
RETREATING = -1,
NOT_MOVING = 0,
APPROACHING = 1,
PLAYER_LOST = 0,
PLAYER_BEHIND_ME = 1,
PLAYER_OK = 2,
PLAYER_AHEAD_OF_ME = 3
};
bool lead_player;
bool hanging;
bool awake;
bool fire;
float playerMoveState;
idEntityPtr<idEntity> light;
bool light_on;
float nextPositionSearch;
idEntityPtr<idEntity> ignore_enemy;
float ignore_enemy_time;
idVec3 lastValidPlayerPosition;
bool playerPositionThread;
float nextLookTime;
float leadTimeout;
int shotsFired;
float nextFireTime;
virtual stateResult_t Torso_Death( stateParms_t* parms );
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Legs_Death( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Fold( stateParms_t* parms );
virtual stateResult_t Legs_Folded( stateParms_t* parms );
virtual stateResult_t Legs_Unfold( stateParms_t* parms );
virtual stateResult_t Legs_Hanging( stateParms_t* parms );
virtual stateResult_t Legs_TurnLeft( stateParms_t* parms );
virtual stateResult_t Legs_TurnRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t updatePlayerPositionThread( stateParms_t* parms );
virtual stateResult_t path_sentry_light_on( stateParms_t* parms );
virtual stateResult_t path_sentry_light_off( stateParms_t* parms );
virtual stateResult_t path_sentry_shutdown( stateParms_t* parms );
virtual stateResult_t path_sentry_unlock_door( stateParms_t* parms );
virtual stateResult_t path_corner( stateParms_t* parms );
virtual stateResult_t path_sentry_lead_player( stateParms_t* parms );
virtual stateResult_t path_sentry_ignore_player( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
virtual stateResult_t state_WaitForTrigger( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t state_FollowPath( stateParms_t* parms );
virtual stateResult_t state_Wait( stateParms_t* parms );
virtual stateResult_t state_GetCloser( stateParms_t* parms );
virtual stateResult_t state_CantReachPlayer( stateParms_t* parms );
virtual stateResult_t state_Lead( stateParms_t* parms );
virtual stateResult_t state_FindPlayer( stateParms_t* parms );
virtual stateResult_t state_Combat( stateParms_t* parms );
virtual stateResult_t combat_chase( stateParms_t* parms );
virtual stateResult_t combat_range( stateParms_t* parms );
virtual void spawn_flashlight( );
virtual void flashlight_off( );
virtual void flashlight_on( );
virtual void ignore( idEntity* ignore_ent );
virtual bool checkForEnemy( float use_fov );
virtual float checkDestinationDistance( );
virtual bool find_attack_position( );
virtual void do_attack( int attack_flags );
virtual int check_attacks( );
};
+199
View File
@@ -0,0 +1,199 @@
// AI_follower.cpp
// Native state-machine replacement for ai_follower(1).script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmAIObject, rvmFollower )
END_CLASS
void rvmFollower::Init( idAI* aiOwner ) {
rvmAIObject::Init( aiOwner );
SetState( "state_Begin" );
}
void rvmFollower::Destroy() {
rvmAIObject::Destroy();
}
stateResult_t rvmFollower::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_Death", Torso_Death );
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Legs_Death", Legs_Death );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "Legs_TurnLeft", Legs_TurnLeft );
AI_DISPATCH_STATE( "Legs_TurnRight", Legs_TurnRight );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "state_Killed", state_Killed );
AI_DISPATCH_STATE( "state_Follow", state_Follow );
AI_DISPATCH_STATE( "getCloser", getCloser );
return rvmAIObject::RunStateByName( stateName, parms );
}
stateResult_t rvmFollower::Torso_CustomCycle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmFollower::Torso_CustomAnim( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmFollower::Torso_Death( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmFollower::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "stand" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmFollower::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { FinishAction( "pain" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmFollower::Legs_Death( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_LEGS, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmFollower::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "stand" ); parms->stage = 1; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmFollower::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmFollower::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "run" ); parms->stage = 1; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmFollower::Legs_TurnLeft( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_LEGS, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmFollower::Legs_TurnRight( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ANIM };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_LEGS, "idle" );
parms->stage = STAGE_ANIM;
return SRESULT_WAIT;
case STAGE_ANIM:
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmFollower::state_Idle( stateParms_t* parms ) {
if ( GetEnemy() ) { SetState( "state_Combat" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmFollower::state_Killed( stateParms_t* parms ) {
StopMove();
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 0 );
StopThinking();
return SRESULT_DONE;
}
stateResult_t rvmFollower::state_Follow( stateParms_t* parms ) {
return SRESULT_DONE;
}
stateResult_t rvmFollower::getCloser( stateParms_t* parms ) {
return SRESULT_DONE;
}
float rvmFollower::should_turn_left( ) {
return 0.0f;
}
float rvmFollower::should_turn_right( ) {
return 0.0f;
}
void rvmFollower::playCustomCycle( const char* animname, float blendTime ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_CustomCycle", idMath::Ftoi( blendTime ) );
PlayCycle( ANIMCHANNEL_TORSO, animname );
}
void rvmFollower::playCustomAnim( const char* animname, float blendIn, float blendOut ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_CustomAnim", idMath::Ftoi( blendIn ) );
PlayAnim( ANIMCHANNEL_TORSO, animname );
}
+36
View File
@@ -0,0 +1,36 @@
// AI_follower.h
// Native state-machine replacement for ai_follower(1).script.
#pragma once
class rvmFollower : public rvmAIObject {
public:
CLASS_PROTOTYPE( rvmFollower );
virtual void Init( idAI* aiOwner ) override;
virtual void Destroy() override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
virtual stateResult_t Torso_CustomCycle( stateParms_t* parms );
virtual stateResult_t Torso_CustomAnim( stateParms_t* parms );
virtual stateResult_t Torso_Death( stateParms_t* parms );
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Legs_Death( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t Legs_TurnLeft( stateParms_t* parms );
virtual stateResult_t Legs_TurnRight( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
virtual stateResult_t state_Follow( stateParms_t* parms );
virtual stateResult_t getCloser( stateParms_t* parms );
virtual float should_turn_left( );
virtual float should_turn_right( );
virtual void playCustomCycle( const char* animname, float blendTime );
virtual void playCustomAnim( const char* animname, float blendIn, float blendOut );
};
File diff suppressed because it is too large Load Diff
+92
View File
@@ -0,0 +1,92 @@
// AI_monster_base.h
// Native state-machine replacement for ai_monster_base.script.
#pragma once
class rvmMonsterBase : public rvmAIObject {
public:
CLASS_PROTOTYPE( rvmMonsterBase );
rvmMonsterBase();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float run_distance;
float walk_turn;
bool run;
bool ambush;
bool ignoreEnemies;
bool stay_on_attackpath;
bool idle_sight_fov;
float customBlendOut;
idEntityPtr<idEntity> current_path;
idEntityPtr<idEntity> next_path;
bool resurrect;
bool ignore_lostcombat;
bool blocked;
bool ignore_sight;
// Animation state machines.
virtual stateResult_t Torso_CustomCycle( stateParms_t* parms );
virtual stateResult_t Torso_CustomAnim( stateParms_t* parms );
virtual stateResult_t Torso_Sight( stateParms_t* parms );
virtual stateResult_t Torso_Death( stateParms_t* parms );
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Death( stateParms_t* parms );
// Main/action state machines.
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Spawner( stateParms_t* parms );
virtual stateResult_t state_Resurrect( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
virtual stateResult_t state_Dead( stateParms_t* parms );
virtual stateResult_t state_Combat( stateParms_t* parms );
virtual stateResult_t state_LostCombat( stateParms_t* parms );
virtual stateResult_t state_FollowAlternatePath( stateParms_t* parms );
virtual stateResult_t monster_begin( stateParms_t* parms );
virtual stateResult_t wait_for_enemy( stateParms_t* parms );
virtual stateResult_t sight_enemy( stateParms_t* parms );
virtual stateResult_t walk_on_trigger( stateParms_t* parms );
virtual stateResult_t wake_on_trigger( stateParms_t* parms );
virtual stateResult_t wake_on_enemy( stateParms_t* parms );
virtual stateResult_t wake_on_attackcone( stateParms_t* parms );
virtual stateResult_t idle_followPathEntities( stateParms_t* parms );
virtual stateResult_t path_corner( stateParms_t* parms );
virtual stateResult_t path_anim( stateParms_t* parms );
virtual stateResult_t path_cycleanim( stateParms_t* parms );
virtual stateResult_t path_turn( stateParms_t* parms );
virtual stateResult_t path_wait( stateParms_t* parms );
virtual stateResult_t path_waitfortrigger( stateParms_t* parms );
virtual stateResult_t path_hide( stateParms_t* parms );
virtual stateResult_t path_show( stateParms_t* parms );
virtual stateResult_t path_attack( stateParms_t* parms );
virtual stateResult_t combat_wander( stateParms_t* parms );
virtual stateResult_t combat_turret_node( stateParms_t* parms );
virtual stateResult_t combat_attack_cone( stateParms_t* parms );
virtual stateResult_t combat_ainode( stateParms_t* parms );
// Non-blocking helpers.
virtual int check_attacks();
virtual bool do_attack( int attack_flags );
virtual bool checkTurretAttack();
virtual void playCustomCycle( const char* animname, float blendTime );
virtual void playCustomAnim( const char* animname, float blendIn, float blendOut );
virtual void trigger_wakeup_targets();
virtual bool checkForEnemy( bool use_fov );
virtual void archvile_minion();
virtual bool can_resurrect();
virtual void monster_resurrect( idEntity* enemy );
virtual void enemy_dead();
virtual bool check_blocked();
virtual bool combat_chase();
virtual void combat_lost();
virtual void monster_base_lost_combat();
virtual void checkNodeAnim( idEntity* node, const char* prefix, const char* anim );
virtual void follow_alternate_path1();
virtual void follow_alternate_path2();
virtual void follow_alternate_path3();
};
@@ -0,0 +1,208 @@
// AI_monster_boss_cyberdemon.cpp
// Native state-machine replacement for ai_monster_boss_cyberdemon.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterBossCyberdemon )
END_CLASS
rvmMonsterBossCyberdemon::rvmMonsterBossCyberdemon() {
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
painNextTime = 0.0f;
kick_entity = NULL;
}
void rvmMonsterBossCyberdemon::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
nextNoFOVAttack = 0.0f;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterBossCyberdemon::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_RangeAttack", Torso_RangeAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_range", combat_range );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterBossCyberdemon::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; }
if ( ( owner && owner->AI_SPECIAL_DAMAGE ) ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", CYBER_IDLE_TO_PAIN ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossCyberdemon::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( CYBER_PAIN_DELAY );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( ( owner && owner->AI_SPECIAL_DAMAGE ) && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", CYBER_PAIN_TO_PAIN ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, CYBER_PAIN_TO_IDLE ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", CYBER_PAIN_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossCyberdemon::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
LookAtEnemy( 0.1f );
if ( AnimDone( ANIMCHANNEL_TORSO, CYBER_MELEE_TO_IDLE ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", CYBER_MELEE_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossCyberdemon::Torso_RangeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
PlayAnim( ANIMCHANNEL_TORSO, range_attack_anim.c_str() );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
LookAtEnemy( 1.0f );
if ( AnimDone( ANIMCHANNEL_TORSO, CYBER_RANGE_TO_IDLE ) ) {
AllowMovement( true );
FinishAction( "range_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", CYBER_RANGE_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossCyberdemon::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "idle" ); parms->stage = 1; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", CYBER_IDLE_TO_WALK ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossCyberdemon::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", CYBER_WALK_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossCyberdemon::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossCyberdemon::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterBossCyberdemon::check_attacks() {
int flags = 0;
if ( EnemyVisible() ) {
const idVec3 org = GetOrigin();
kick_entity = FindActorsInBounds( org + idVec3( -65, -65, 0 ), org + idVec3( 65, 65, 300 ) );
if ( kick_entity.GetEntity() ) { flags |= ATTACK_MELEE; }
}
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
if ( !CanReachEnemy() || Time() >= nextAttack ) {
range_attack_anim = ChooseAnim( ANIMCHANNEL_LEGS, "range_attack" );
if ( CanHitEnemyFromAnim( range_attack_anim.c_str() ) ) { flags |= ATTACK_MISSILE; }
}
}
return static_cast<float>( flags );
}
bool rvmMonsterBossCyberdemon::do_attack( int attack_flags ) {
nextNoFOVAttack = Time() + CYBER_NOFOVTIME;
const int flags = attack_flags;
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "combat_range" ); return true; }
return false;
}
stateResult_t rvmMonsterBossCyberdemon::combat_range( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
StopMove();
waitActionName = "range_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", CYBER_WALK_TO_RANGEATTACK );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "range_attack" ) ) { return SRESULT_WAIT; }
nextAttack = DelayTime( CYBER_ATTACK_RATE );
nextNoFOVAttack = Time() + CYBER_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossCyberdemon::combat_melee( stateParms_t* parms ) {
enum { STAGE_KICK, STAGE_DAMAGE };
switch ( parms->stage ) {
case STAGE_KICK:
if ( owner ) { owner->KickObstacles( kick_entity.GetEntity(), 200.0f ); }
scratchEndTime = DelayTime( 0.5f );
parms->stage = STAGE_DAMAGE;
return SRESULT_WAIT;
case STAGE_DAMAGE:
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
DirectDamage( kick_entity.GetEntity(), "melee_cyberdemon_kick" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,72 @@
// AI_monster_boss_cyberdemon.h
// Native state-machine replacement for ai_monster_boss_cyberdemon.script.
#pragma once
#ifndef CYBER_ATTACK_RATE
#define CYBER_ATTACK_RATE 5
#endif
#ifndef CYBER_PAIN_DELAY
#define CYBER_PAIN_DELAY 0.25f
#endif
#ifndef CYBER_NOFOVTIME
#define CYBER_NOFOVTIME 4
#endif
#ifndef CYBER_PAIN_TO_IDLE
#define CYBER_PAIN_TO_IDLE 2
#endif
#ifndef CYBER_PAIN_TO_PAIN
#define CYBER_PAIN_TO_PAIN 0
#endif
#ifndef CYBER_MELEE_TO_IDLE
#define CYBER_MELEE_TO_IDLE 4
#endif
#ifndef CYBER_RANGE_TO_IDLE
#define CYBER_RANGE_TO_IDLE 4
#endif
#ifndef CYBER_IDLE_TO_PAIN
#define CYBER_IDLE_TO_PAIN 0
#endif
#ifndef CYBER_IDLE_TO_WALK
#define CYBER_IDLE_TO_WALK 4
#endif
#ifndef CYBER_WALK_TO_IDLE
#define CYBER_WALK_TO_IDLE 4
#endif
#ifndef CYBER_WALK_TO_RANGEATTACK
#define CYBER_WALK_TO_RANGEATTACK 4
#endif
#ifndef CYBER_WALK_TO_MELEE
#define CYBER_WALK_TO_MELEE 4
#endif
class rvmMonsterBossCyberdemon : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterBossCyberdemon );
rvmMonsterBossCyberdemon();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextAttack;
float nextNoFOVAttack;
float painNextTime;
idStr range_attack_anim;
idEntityPtr<idEntity> kick_entity;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_range( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,213 @@
// AI_monster_boss_guardian.cpp
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmSeekerBase )
END_CLASS
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterBossGuardianSpawner )
END_CLASS
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterBossGuardian )
END_CLASS
rvmSeekerBase::rvmSeekerBase() {}
void rvmSeekerBase::Init( idAI* aiOwner ) { rvmMonsterBase::Init( aiOwner ); }
stateResult_t rvmSeekerBase::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "respawnSeeker", respawnSeeker );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
bool rvmSeekerBase::isAlive() const { return owner && owner->health > 0 && !owner->AI_DEAD; }
stateResult_t rvmSeekerBase::respawnSeeker( stateParms_t* parms ) { return SRESULT_DONE; }
rvmMonsterBossGuardianSpawner::rvmMonsterBossGuardianSpawner() { guardian = NULL; inactive_time = active_time = pain_time = 0.0f; }
void rvmMonsterBossGuardianSpawner::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
inactive_time = GetFloatKey( "inactive_time", SPAWNER_FADEOUT );
active_time = GetFloatKey( "active_time", SPAWNER_FADEIN_TIME );
pain_time = GetFloatKey( "pain_time", SPAWNER_PAIN_FADEOUT );
SetState( "state_Inactive" );
}
stateResult_t rvmMonsterBossGuardianSpawner::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "update_effects", update_effects );
AI_DISPATCH_STATE( "setGuardian", setGuardian );
AI_DISPATCH_STATE( "state_Inactive", state_Inactive );
AI_DISPATCH_STATE( "state_Active", state_Active );
AI_DISPATCH_STATE( "state_Killed", state_Killed );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterBossGuardianSpawner::update_effects( stateParms_t* parms ) { return SRESULT_DONE; }
stateResult_t rvmMonsterBossGuardianSpawner::setGuardian( stateParms_t* parms ) { guardian = scratchEntity.GetEntity(); return SRESULT_DONE; }
stateResult_t rvmMonsterBossGuardianSpawner::state_Inactive( stateParms_t* parms ) {
if ( parms->stage == 0 ) { HideOwner(); IgnoreDamage(); parms->stage = 1; }
if ( Pain() || Activated() ) { ShowOwner(); AllowDamage(); SetState( "state_Active" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardianSpawner::state_Active( stateParms_t* parms ) {
if ( parms->stage == 0 ) { ShowOwner(); AllowDamage(); scratchEndTime = DelayTime( active_time ); parms->stage = 1; return SRESULT_WAIT; }
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
SetState( "state_Inactive" );
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossGuardianSpawner::state_Killed( stateParms_t* parms ) { return rvmMonsterBase::state_Killed( parms ); }
rvmMonsterBossGuardian::rvmMonsterBossGuardian() {
nextAttack = 0.0f; painNextTime = 0.0f; in_melee = false; rightfoot = false; charge = false;
seeker1 = seeker2 = seeker3 = spawner = NULL;
}
void rvmMonsterBossGuardian::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
SetMoveType( MOVETYPE_ANIM );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
SetState( "state_Idle" );
}
stateResult_t rvmMonsterBossGuardian::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Legs_Sight", Legs_Sight );
AI_DISPATCH_STATE( "Legs_Death", Legs_Death );
AI_DISPATCH_STATE( "Legs_Pain", Legs_Pain );
AI_DISPATCH_STATE( "Legs_RangeAttack", Legs_RangeAttack );
AI_DISPATCH_STATE( "Legs_MeleeAttack", Legs_MeleeAttack );
AI_DISPATCH_STATE( "Legs_Walk_Melee_Left", Legs_Walk_Melee_Left );
AI_DISPATCH_STATE( "Legs_Walk_Melee_Right", Legs_Walk_Melee_Right );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_Run_Melee_Left", Legs_Run_Melee_Left );
AI_DISPATCH_STATE( "Legs_Run_Melee_Right", Legs_Run_Melee_Right );
AI_DISPATCH_STATE( "Legs_Charge", Legs_Charge );
AI_DISPATCH_STATE( "Legs_Lost", Legs_Lost );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_SpawnSeeker", Legs_SpawnSeeker );
AI_DISPATCH_STATE( "addSeeker", addSeeker );
AI_DISPATCH_STATE( "sightEnemy", sightEnemy );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "state_Killed", state_Killed );
AI_DISPATCH_STATE( "state_Combat", state_Combat );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_range", combat_range );
AI_DISPATCH_STATE( "combat_charge", combat_charge );
AI_DISPATCH_STATE( "pound_impact", pound_impact );
AI_DISPATCH_STATE( "pound_attack", pound_attack );
AI_DISPATCH_STATE( "pound_attack_left", pound_attack_left );
AI_DISPATCH_STATE( "pound_attack_right", pound_attack_right );
AI_DISPATCH_STATE( "smash_ground", smash_ground );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterBossGuardian::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "idle" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Pain", GUARDIAN_IDLE_TO_PAIN ); return SRESULT_DONE; }
if ( charge ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Charge", GUARDIAN_IDLE_TO_CHARGE ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", GUARDIAN_IDLE_TO_WALK ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardian::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Pain", GUARDIAN_WALK_TO_PAIN ); return SRESULT_DONE; }
if ( charge ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Charge", GUARDIAN_WALK_TO_CHARGE ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_WALK_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardian::Legs_Pain( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, GetPainAnim() ); painNextTime = DelayTime( GUARDIAN_PAIN_DELAY ); parms->stage = 1; return SRESULT_WAIT; }
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Pain", GUARDIAN_PAIN_TO_PAIN ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_PAIN_TO_IDLE ) ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_PAIN_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardian::Legs_Sight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "sight" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_SIGHT_TO_IDLE ) ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_SIGHT_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardian::Legs_Death( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "death" ); parms->stage = 1; }
return AnimDone( ANIMCHANNEL_LEGS, 0 ) ? SRESULT_DONE : SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardian::Legs_RangeAttack( stateParms_t* parms ) {
if ( parms->stage == 0 ) { StopMove(); FaceEnemy(); PlayAnim( ANIMCHANNEL_LEGS, "range_attack" ); parms->stage = 1; return SRESULT_WAIT; }
if ( AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_RANGEATTACK_TO_IDLE ) ) { FinishAction( "range_attack" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_RANGEATTACK_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardian::Legs_MeleeAttack( stateParms_t* parms ) {
if ( parms->stage == 0 ) { StopMove(); FaceEnemy(); in_melee = true; PlayAnim( ANIMCHANNEL_LEGS, "melee_attack" ); parms->stage = 1; return SRESULT_WAIT; }
if ( AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_MELEE_TO_IDLE ) ) { in_melee = false; FinishAction( "melee_attack" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_MELEE_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardian::Legs_Walk_Melee_Left( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "walk_melee_left" ); parms->stage = 1; } return AnimDone( ANIMCHANNEL_LEGS, 4 ) ? SRESULT_DONE : SRESULT_WAIT; }
stateResult_t rvmMonsterBossGuardian::Legs_Walk_Melee_Right( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "walk_melee_right" ); parms->stage = 1; } return AnimDone( ANIMCHANNEL_LEGS, 4 ) ? SRESULT_DONE : SRESULT_WAIT; }
stateResult_t rvmMonsterBossGuardian::Legs_Run_Melee_Left( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "run_melee_left" ); parms->stage = 1; } return AnimDone( ANIMCHANNEL_LEGS, 4 ) ? SRESULT_DONE : SRESULT_WAIT; }
stateResult_t rvmMonsterBossGuardian::Legs_Run_Melee_Right( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "run_melee_right" ); parms->stage = 1; } return AnimDone( ANIMCHANNEL_LEGS, 4 ) ? SRESULT_DONE : SRESULT_WAIT; }
stateResult_t rvmMonsterBossGuardian::Legs_Charge( stateParms_t* parms ) {
if ( parms->stage == 0 ) { charge = true; ChargeAttack( "damage_guardian_charge" ); PlayCycle( ANIMCHANNEL_LEGS, "charge" ); parms->stage = 1; return SRESULT_WAIT; }
if ( Pain() ) { charge = false; AttackEnd(); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Pain", GUARDIAN_CHARGE_TO_PAIN ); return SRESULT_DONE; }
if ( MoveDone() || EnemyRange() < 128.0f ) { charge = false; AttackEnd(); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_CHARGE_TO_IDLE ); FinishAction( "charge_attack" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardian::Legs_Lost( stateParms_t* parms ) { if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "lost" ); parms->stage = 1; } return AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_LOST_TO_IDLE ) ? SRESULT_DONE : SRESULT_WAIT; }
stateResult_t rvmMonsterBossGuardian::Legs_SpawnSeeker( stateParms_t* parms ) {
if ( parms->stage == 0 ) { StopMove(); PlayAnim( ANIMCHANNEL_LEGS, "spawn_seeker" ); parms->stage = 1; return SRESULT_WAIT; }
if ( AnimDone( ANIMCHANNEL_LEGS, GUARDIAN_SPAWN_TO_IDLE ) ) { FinishAction( "spawn_seeker" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", GUARDIAN_SPAWN_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardian::addSeeker( stateParms_t* parms ) {
idEntity* ent = scratchEntity.GetEntity();
if ( !seeker1.GetEntity() ) seeker1 = ent; else if ( !seeker2.GetEntity() ) seeker2 = ent; else seeker3 = ent;
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossGuardian::sightEnemy( stateParms_t* parms ) {
FaceEnemy();
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Sight", GUARDIAN_IDLE_TO_SIGHT );
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossGuardian::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START: StartAction( "wait_for_enemy" ); parms->stage = STAGE_WAIT; return SRESULT_WAIT;
case STAGE_WAIT:
if ( InAction() ) return SRESULT_WAIT;
StartAction( "sightEnemy" );
nextAttack = DelayTime( 2.0f );
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossGuardian::state_Killed( stateParms_t* parms ) { return rvmMonsterBase::state_Killed( parms ); }
stateResult_t rvmMonsterBossGuardian::state_Combat( stateParms_t* parms ) {
enum { STAGE_SELECT, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_SELECT:
if ( EnemyDead() ) { SetState( "state_LostCombat" ); return SRESULT_DONE; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Pain", GUARDIAN_WALK_TO_PAIN ); }
if ( CheckForMelee() ) { StartAction( "combat_melee" ); parms->stage = STAGE_ACTION; return SRESULT_WAIT; }
if ( Time() >= nextAttack && EnemyRange() < GUARDIAN_RANGE_ATTACK_RANGE && CanHitEnemy() ) { StartAction( "combat_range" ); parms->stage = STAGE_ACTION; return SRESULT_WAIT; }
if ( EnemyRange() < GUARDIAN_CHARGE_ATTACK_RANGE && TestChargeAttack() > 0.0f ) { StartAction( "combat_charge" ); parms->stage = STAGE_ACTION; return SRESULT_WAIT; }
MoveToEnemy();
return SRESULT_WAIT;
case STAGE_ACTION:
if ( InAction() ) { return SRESULT_WAIT; }
parms->stage = STAGE_SELECT;
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossGuardian::combat_melee( stateParms_t* parms ) {
if ( parms->stage == 0 ) { waitActionName = "melee_attack"; SetAnimState( ANIMCHANNEL_LEGS, "Legs_MeleeAttack", GUARDIAN_IDLE_TO_MELEE ); parms->stage = 1; return SRESULT_WAIT; }
return ActionDone( "melee_attack" ) ? SRESULT_DONE : SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardian::combat_range( stateParms_t* parms ) {
if ( parms->stage == 0 ) { waitActionName = "range_attack"; SetAnimState( ANIMCHANNEL_LEGS, "Legs_RangeAttack", GUARDIAN_IDLE_TO_RANGE ); parms->stage = 1; return SRESULT_WAIT; }
if ( !ActionDone( "range_attack" ) ) { return SRESULT_WAIT; }
nextAttack = DelayTime( GUARDIAN_RANGE_ATTACK_DELAY );
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossGuardian::combat_charge( stateParms_t* parms ) {
if ( parms->stage == 0 ) { waitActionName = "charge_attack"; charge = true; SetAnimState( ANIMCHANNEL_LEGS, "Legs_Charge", GUARDIAN_IDLE_TO_CHARGE ); parms->stage = 1; return SRESULT_WAIT; }
if ( !ActionDone( "charge_attack" ) ) { return SRESULT_WAIT; }
charge = false;
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_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 ); }
stateResult_t rvmMonsterBossGuardian::smash_ground( stateParms_t* parms ) { RadiusDamageFromJoint( "origin", "damage_guardian_smash" ); return SRESULT_DONE; }
@@ -0,0 +1,172 @@
// AI_monster_boss_guardian.h
#pragma once
#ifndef SPAWNER_PAIN_FADEOUT
#define SPAWNER_PAIN_FADEOUT 2.5f
#endif
#ifndef SPAWNER_FADEOUT
#define SPAWNER_FADEOUT 1.0f
#endif
#ifndef SPAWNER_FADEIN_TIME
#define SPAWNER_FADEIN_TIME 0.25f
#endif
#ifndef GUARDIAN_PAIN_DELAY
#define GUARDIAN_PAIN_DELAY 0.25f
#endif
#ifndef GUARDIAN_RANGE_ATTACK_RANGE
#define GUARDIAN_RANGE_ATTACK_RANGE 1024
#endif
#ifndef GUARDIAN_RANGE_ATTACK_DELAY
#define GUARDIAN_RANGE_ATTACK_DELAY 7
#endif
#ifndef GUARDIAN_CHARGE_ATTACK_RANGE
#define GUARDIAN_CHARGE_ATTACK_RANGE 680
#endif
#ifndef GUARDIAN_NUM_PROJECTILES
#define GUARDIAN_NUM_PROJECTILES 12
#endif
#ifndef GUARDIAN_PAIN_TO_IDLE
#define GUARDIAN_PAIN_TO_IDLE 3
#endif
#ifndef GUARDIAN_PAIN_TO_PAIN
#define GUARDIAN_PAIN_TO_PAIN 3
#endif
#ifndef GUARDIAN_SIGHT_TO_IDLE
#define GUARDIAN_SIGHT_TO_IDLE 8
#endif
#ifndef GUARDIAN_MELEE_TO_IDLE
#define GUARDIAN_MELEE_TO_IDLE 12
#endif
#ifndef GUARDIAN_IDLE_TO_PAIN
#define GUARDIAN_IDLE_TO_PAIN 3
#endif
#ifndef GUARDIAN_IDLE_TO_WALK
#define GUARDIAN_IDLE_TO_WALK 12
#endif
#ifndef GUARDIAN_IDLE_TO_CHARGE
#define GUARDIAN_IDLE_TO_CHARGE 12
#endif
#ifndef GUARDIAN_IDLE_TO_MELEE
#define GUARDIAN_IDLE_TO_MELEE 12
#endif
#ifndef GUARDIAN_IDLE_TO_RANGE
#define GUARDIAN_IDLE_TO_RANGE 12
#endif
#ifndef GUARDIAN_IDLE_TO_SIGHT
#define GUARDIAN_IDLE_TO_SIGHT 12
#endif
#ifndef GUARDIAN_IDLE_TO_LOST
#define GUARDIAN_IDLE_TO_LOST 12
#endif
#ifndef GUARDIAN_WALK_TO_IDLE
#define GUARDIAN_WALK_TO_IDLE 12
#endif
#ifndef GUARDIAN_WALK_TO_PAIN
#define GUARDIAN_WALK_TO_PAIN 3
#endif
#ifndef GUARDIAN_WALK_TO_CHARGE
#define GUARDIAN_WALK_TO_CHARGE 8
#endif
#ifndef GUARDIAN_CHARGE_TO_WALK
#define GUARDIAN_CHARGE_TO_WALK 0
#endif
#ifndef GUARDIAN_CHARGE_TO_IDLE
#define GUARDIAN_CHARGE_TO_IDLE 12
#endif
#ifndef GUARDIAN_CHARGE_TO_PAIN
#define GUARDIAN_CHARGE_TO_PAIN 3
#endif
#ifndef GUARDIAN_LOST_TO_IDLE
#define GUARDIAN_LOST_TO_IDLE 10
#endif
#ifndef GUARDIAN_LOST_TO_PAIN
#define GUARDIAN_LOST_TO_PAIN 3
#endif
#ifndef GUARDIAN_LOST_TO_WALK
#define GUARDIAN_LOST_TO_WALK 24
#endif
#ifndef GUARDIAN_LOST_TO_CHARGE
#define GUARDIAN_LOST_TO_CHARGE 24
#endif
#ifndef GUARDIAN_RANGEATTACK_TO_IDLE
#define GUARDIAN_RANGEATTACK_TO_IDLE 4
#endif
#ifndef GUARDIAN_SPAWN_TO_IDLE
#define GUARDIAN_SPAWN_TO_IDLE 4
#endif
#ifndef GUARDIAN_IDLE_TO_SPAWN
#define GUARDIAN_IDLE_TO_SPAWN 4
#endif
class rvmSeekerBase : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmSeekerBase );
rvmSeekerBase();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
virtual bool isAlive() const;
protected:
virtual stateResult_t respawnSeeker( stateParms_t* parms );
};
class rvmMonsterBossGuardianSpawner : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterBossGuardianSpawner );
rvmMonsterBossGuardianSpawner();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
idEntityPtr<idEntity> guardian;
float inactive_time;
float active_time;
float pain_time;
virtual stateResult_t update_effects( stateParms_t* parms );
virtual stateResult_t setGuardian( stateParms_t* parms );
virtual stateResult_t state_Inactive( stateParms_t* parms );
virtual stateResult_t state_Active( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
};
class rvmMonsterBossGuardian : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterBossGuardian );
rvmMonsterBossGuardian();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextAttack;
float painNextTime;
bool in_melee;
bool rightfoot;
bool charge;
idEntityPtr<idEntity> seeker1;
idEntityPtr<idEntity> seeker2;
idEntityPtr<idEntity> seeker3;
idEntityPtr<idEntity> spawner;
virtual stateResult_t Legs_Sight( stateParms_t* parms );
virtual stateResult_t Legs_Death( stateParms_t* parms );
virtual stateResult_t Legs_Pain( stateParms_t* parms );
virtual stateResult_t Legs_RangeAttack( stateParms_t* parms );
virtual stateResult_t Legs_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Legs_Walk_Melee_Left( stateParms_t* parms );
virtual stateResult_t Legs_Walk_Melee_Right( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Run_Melee_Left( stateParms_t* parms );
virtual stateResult_t Legs_Run_Melee_Right( stateParms_t* parms );
virtual stateResult_t Legs_Charge( stateParms_t* parms );
virtual stateResult_t Legs_Lost( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_SpawnSeeker( stateParms_t* parms );
virtual stateResult_t addSeeker( stateParms_t* parms );
virtual stateResult_t sightEnemy( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
virtual stateResult_t state_Combat( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_range( stateParms_t* parms );
virtual stateResult_t combat_charge( stateParms_t* parms );
virtual stateResult_t pound_impact( stateParms_t* parms );
virtual stateResult_t pound_attack( stateParms_t* parms );
virtual stateResult_t pound_attack_left( stateParms_t* parms );
virtual stateResult_t pound_attack_right( stateParms_t* parms );
virtual stateResult_t smash_ground( stateParms_t* parms );
};
@@ -0,0 +1,252 @@
// AI_monster_boss_guardian_seeker.cpp
// Native state-machine replacement for ai_monster_boss_guardian_seeker.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmSeekerBase, rvmMonsterBossGuardianSeeker )
END_CLASS
rvmMonsterBossGuardianSeeker::rvmMonsterBossGuardianSeeker() {
painNextTime = 0.0f;
nextEnemyCheck = 0.0f;
getCloserEndTime = 0.0f;
retreatEndTime = 0.0f;
guardian = NULL;
}
void rvmMonsterBossGuardianSeeker::Init( idAI* aiOwner ) {
rvmSeekerBase::Init( aiOwner );
SetMoveType( MOVETYPE_FLY );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetState( "state_Inactive" );
}
stateResult_t rvmMonsterBossGuardianSeeker::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Fly", Torso_Fly );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_Death", Torso_Death );
AI_DISPATCH_STATE( "see_enemy", see_enemy );
AI_DISPATCH_STATE( "lost_enemy", lost_enemy );
AI_DISPATCH_STATE( "respawnSeeker", respawnSeeker );
AI_DISPATCH_STATE( "state_Respawn", state_Respawn );
AI_DISPATCH_STATE( "state_Inactive", state_Inactive );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "state_Killed", state_Killed );
AI_DISPATCH_STATE( "state_WatchPlayer", state_WatchPlayer );
AI_DISPATCH_STATE( "state_GetCloser", state_GetCloser );
AI_DISPATCH_STATE( "state_FindPlayer", state_FindPlayer );
AI_DISPATCH_STATE( "state_Retreat", state_Retreat );
AI_DISPATCH_STATE( "state_RunAway", state_RunAway );
return rvmSeekerBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterBossGuardianSeeker::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Fly", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardianSeeker::Torso_Fly( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "fly" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardianSeeker::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossGuardianSeeker::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossGuardianSeeker::Torso_Death( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "death" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 0 ) ) {
FinishAction( "death" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossGuardianSeeker::see_enemy( stateParms_t* parms ) {
LookAtEnemy( SEEKER_SIGHTTIME );
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossGuardianSeeker::lost_enemy( stateParms_t* parms ) {
LookAtEnemy( 0.1f );
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossGuardianSeeker::respawnSeeker( stateParms_t* parms ) {
SetState( "state_Respawn" );
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossGuardianSeeker::state_Respawn( stateParms_t* parms ) {
enum { STAGE_START, STAGE_SOLID };
switch ( parms->stage ) {
case STAGE_START:
ShowOwner();
AllowDamage();
SetHealth( GetFloatKey( "health", "100" ) );
StartSoundKey( "snd_respawn", SND_CHANNEL_VOICE, false );
scratchEndTime = DelayTime( 0.2f );
parms->stage = STAGE_SOLID;
return SRESULT_WAIT;
case STAGE_SOLID:
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
BecomeSolid();
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossGuardianSeeker::state_Inactive( stateParms_t* parms ) {
if ( parms->stage == 0 ) { HideOwner(); IgnoreDamage(); StopMove(); parms->stage = 1; }
if ( Activated() || Pain() ) { SetState( "state_Respawn" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardianSeeker::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START: StartAction( "wait_for_enemy" ); parms->stage = STAGE_WAIT; return SRESULT_WAIT;
case STAGE_WAIT:
if ( InAction() ) { return SRESULT_WAIT; }
SetState( "state_WatchPlayer" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossGuardianSeeker::state_Killed( stateParms_t* parms ) {
if ( parms->stage == 0 ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 0 ); BecomeNonSolid(); parms->stage = 1; return SRESULT_WAIT; }
if ( InAnimState( ANIMCHANNEL_TORSO, "Torso_Death" ) ) { return SRESULT_WAIT; }
HideOwner();
SetState( "state_Inactive" );
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossGuardianSeeker::state_WatchPlayer( stateParms_t* parms ) {
const float range = EnemyRange();
if ( !EnemyVisible() ) { SetState( "state_FindPlayer" ); return SRESULT_DONE; }
if ( CheckForMelee() ) { waitActionName = "melee_attack"; SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 2 ); return SRESULT_WAIT; }
if ( range < SEEKER_TOOCLOSE_RANGE ) { SetState( "state_Retreat" ); return SRESULT_DONE; }
if ( range > SEEKER_TOOFAR_RANGE ) { SetState( "state_GetCloser" ); return SRESULT_DONE; }
FaceEnemy();
MoveToEnemyHeight();
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossGuardianSeeker::state_GetCloser( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MOVE };
switch ( parms->stage ) {
case STAGE_START:
MoveToEnemy();
getCloserEndTime = DelayTime( SEEKER_GETCLOSER_TIME );
parms->stage = STAGE_MOVE;
return SRESULT_WAIT;
case STAGE_MOVE:
if ( Time() >= getCloserEndTime || EnemyRange() <= SEEKER_IDEAL_RANGE || MoveDone() ) { SetState( "state_WatchPlayer" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossGuardianSeeker::state_FindPlayer( stateParms_t* parms ) {
enum { STAGE_START, STAGE_SEEK };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "lost_enemy" );
MoveToEnemy();
scratchEndTime = DelayTime( SEEKER_SIGHTTIME );
parms->stage = STAGE_SEEK;
return SRESULT_WAIT;
case STAGE_SEEK:
if ( EnemyVisible() ) { StartAction( "see_enemy" ); SetState( "state_WatchPlayer" ); return SRESULT_DONE; }
if ( Time() >= scratchEndTime || MoveDone() ) { SetState( "state_WatchPlayer" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossGuardianSeeker::state_Retreat( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MOVE };
switch ( parms->stage ) {
case STAGE_START:
MoveOutOfRange( GetEnemy(), SEEKER_IDEAL_RANGE );
retreatEndTime = DelayTime( SEEKER_RUNTIME );
parms->stage = STAGE_MOVE;
return SRESULT_WAIT;
case STAGE_MOVE:
if ( Time() >= retreatEndTime || MoveDone() || EnemyRange() >= SEEKER_IDEAL_RANGE ) { SetState( "state_WatchPlayer" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossGuardianSeeker::state_RunAway( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUN };
switch ( parms->stage ) {
case STAGE_START:
SetFlySpeed( SEEKER_ESCAPE_SPEED );
MoveOutOfRange( GetEnemy(), SEEKER_MAXIMUM_RETREAT_RANGE );
scratchEndTime = DelayTime( SEEKER_RUNTIME );
parms->stage = STAGE_RUN;
return SRESULT_WAIT;
case STAGE_RUN:
if ( Time() >= scratchEndTime || MoveDone() ) { ClearFlyOffset(); SetState( "state_WatchPlayer" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,68 @@
// AI_monster_boss_guardian_seeker.h
// Native state-machine replacement for ai_monster_boss_guardian_seeker.script.
#pragma once
#ifndef SEEKER_TOOCLOSE_RANGE
#define SEEKER_TOOCLOSE_RANGE 200
#endif
#ifndef SEEKER_TOOFAR_RANGE
#define SEEKER_TOOFAR_RANGE 650
#endif
#ifndef SEEKER_IDEAL_RANGE
#define SEEKER_IDEAL_RANGE 430
#endif
#ifndef SEEKER_MAXIMUM_RETREAT_RANGE
#define SEEKER_MAXIMUM_RETREAT_RANGE ( SEEKER_TOOFAR_RANGE + 200 )
#endif
#ifndef SEEKER_RUNTIME
#define SEEKER_RUNTIME 2
#endif
#ifndef SEEKER_GETCLOSER_DELAY
#define SEEKER_GETCLOSER_DELAY 3
#endif
#ifndef SEEKER_GETCLOSER_TIME
#define SEEKER_GETCLOSER_TIME 1
#endif
#ifndef SEEKER_SIGHTTIME
#define SEEKER_SIGHTTIME 2
#endif
#ifndef SEEKER_ESCAPE_SPEED
#define SEEKER_ESCAPE_SPEED 800
#endif
class rvmMonsterBossGuardianSeeker : public rvmSeekerBase {
public:
CLASS_PROTOTYPE( rvmMonsterBossGuardianSeeker );
rvmMonsterBossGuardianSeeker();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float painNextTime;
float nextEnemyCheck;
float getCloserEndTime;
float retreatEndTime;
idEntityPtr<idEntity> guardian;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Fly( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_Death( stateParms_t* parms );
virtual stateResult_t see_enemy( stateParms_t* parms );
virtual stateResult_t lost_enemy( stateParms_t* parms );
virtual stateResult_t respawnSeeker( stateParms_t* parms );
virtual stateResult_t state_Respawn( stateParms_t* parms );
virtual stateResult_t state_Inactive( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
virtual stateResult_t state_WatchPlayer( stateParms_t* parms );
virtual stateResult_t state_GetCloser( stateParms_t* parms );
virtual stateResult_t state_FindPlayer( stateParms_t* parms );
virtual stateResult_t state_Retreat( stateParms_t* parms );
virtual stateResult_t state_RunAway( stateParms_t* parms );
};
@@ -0,0 +1,308 @@
// AI_monster_boss_sabaoth.cpp
// Native state-machine replacement for ai_monster_boss_sabaoth.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterBossSabaoth )
END_CLASS
rvmMonsterBossSabaoth::rvmMonsterBossSabaoth() {
nextAttack = 0.0f;
nextMeleeAttack = 0.0f;
pathSpeed = 0.0f;
noSlowToStop = false;
currentPath = NULL;
nextPath = NULL;
}
void rvmMonsterBossSabaoth::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
SetMoveType( MOVETYPE_SLIDE );
SetState( "state_Begin" );
}
stateResult_t rvmMonsterBossSabaoth::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_RangeAttack", Torso_RangeAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "state_Killed", state_Killed );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "move_to_corner", move_to_corner );
AI_DISPATCH_STATE( "turn_to_corner", turn_to_corner );
AI_DISPATCH_STATE( "closeSection", closeSection );
AI_DISPATCH_STATE( "openSection", openSection );
AI_DISPATCH_STATE( "state_Combat", state_Combat );
AI_DISPATCH_STATE( "combat_range", combat_range );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "fire_bfg", fire_bfg );
AI_DISPATCH_STATE( "open_section_1", open_section_1 );
AI_DISPATCH_STATE( "close_section_1", close_section_1 );
AI_DISPATCH_STATE( "open_section_2", open_section_2 );
AI_DISPATCH_STATE( "close_section_2", close_section_2 );
AI_DISPATCH_STATE( "open_section_3", open_section_3 );
AI_DISPATCH_STATE( "close_section_3", close_section_3 );
AI_DISPATCH_STATE( "open_section_4", open_section_4 );
AI_DISPATCH_STATE( "close_section_4", close_section_4 );
AI_DISPATCH_STATE( "open_section_5", open_section_5 );
AI_DISPATCH_STATE( "close_section_5", close_section_5 );
AI_DISPATCH_STATE( "open_section_6", open_section_6 );
AI_DISPATCH_STATE( "close_section_6", close_section_6 );
AI_DISPATCH_STATE( "open_section_7", open_section_7 );
AI_DISPATCH_STATE( "close_section_7", close_section_7 );
AI_DISPATCH_STATE( "open_section_8", open_section_8 );
AI_DISPATCH_STATE( "close_section_8", close_section_8 );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterBossSabaoth::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "stand" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossSabaoth::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
AllowMovement( false );
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
AllowMovement( true );
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossSabaoth::Torso_RangeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
AllowMovement( false );
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
AllowMovement( true );
FinishAction( "range_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossSabaoth::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "idle" ); parms->stage = 1; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossSabaoth::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossSabaoth::state_Killed( stateParms_t* parms ) {
return rvmMonsterBase::state_Killed( parms );
}
stateResult_t rvmMonsterBossSabaoth::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_SLIDE );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossSabaoth::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = DelayTime( 1.0f );
nextMeleeAttack = 0.0f;
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossSabaoth::move_to_corner( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
if ( scratchEntity.GetEntity() ) { MoveToEntity( scratchEntity.GetEntity() ); } else { MoveToEnemy(); }
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( check_attacks( false ) != 0 ) { return SRESULT_DONE; }
if ( !MoveDone() ) { return SRESULT_WAIT; }
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossSabaoth::turn_to_corner( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
if ( scratchEntity.GetEntity() ) { FaceEntity( scratchEntity.GetEntity() ); } else { FaceEnemy(); }
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( check_attacks( false ) != 0 ) { return SRESULT_DONE; }
if ( !FacingIdeal() ) { return SRESULT_WAIT; }
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossSabaoth::closeSection( stateParms_t* parms ) {
// Native hook for the map section-close target dispatch used by the script.
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossSabaoth::openSection( stateParms_t* parms ) {
// Native hook for the map section-open target dispatch used by the script.
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossSabaoth::state_Combat( stateParms_t* parms ) {
enum { STAGE_SELECT, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_SELECT:
if ( EnemyDead() ) { SetState( "state_LostCombat" ); return SRESULT_DONE; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); }
if ( do_attack( check_attacks( false ) ) ) { parms->stage = STAGE_ACTION; return SRESULT_WAIT; }
if ( !MoveDone() ) { return SRESULT_WAIT; }
MoveToEnemy();
return SRESULT_WAIT;
case STAGE_ACTION:
if ( InAction() ) { return SRESULT_WAIT; }
parms->stage = STAGE_SELECT;
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
int rvmMonsterBossSabaoth::check_attacks( bool meleeOnly ) {
int flags = 0;
if ( CheckForMelee() && Time() >= nextMeleeAttack ) { flags |= ATTACK_MELEE; }
if ( !meleeOnly && Time() >= nextAttack && CanHitEnemyFromAnim( "range_attack" ) ) { flags |= ATTACK_MISSILE; }
return static_cast<float>( flags );
}
int rvmMonsterBossSabaoth::check_attacks() {
return check_attacks( false );
}
bool rvmMonsterBossSabaoth::do_attack( int attack_flags ) {
const int flags = attack_flags;
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "combat_range" ); return true; }
return false;
}
stateResult_t rvmMonsterBossSabaoth::combat_range( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "range_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "range_attack" ) ) { return SRESULT_WAIT; }
nextAttack = DelayTime( SABAOTH_ATTACK_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossSabaoth::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
nextMeleeAttack = DelayTime( SABAOTH_ATTACK_RATE_MELEE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossSabaoth::fire_bfg( stateParms_t* parms ) {
AttackMissile( "bfg" );
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossSabaoth::set_section_open( stateParms_t* parms, int section, bool open ) {
// Hook point for firing section-specific map targets. section is 1-8, open selects open/close.
scratchCount = section;
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossSabaoth::open_section_1( stateParms_t* parms ) { return set_section_open( parms, 1, true ); }
stateResult_t rvmMonsterBossSabaoth::close_section_1( stateParms_t* parms ) { return set_section_open( parms, 1, false ); }
stateResult_t rvmMonsterBossSabaoth::open_section_2( stateParms_t* parms ) { return set_section_open( parms, 2, true ); }
stateResult_t rvmMonsterBossSabaoth::close_section_2( stateParms_t* parms ) { return set_section_open( parms, 2, false ); }
stateResult_t rvmMonsterBossSabaoth::open_section_3( stateParms_t* parms ) { return set_section_open( parms, 3, true ); }
stateResult_t rvmMonsterBossSabaoth::close_section_3( stateParms_t* parms ) { return set_section_open( parms, 3, false ); }
stateResult_t rvmMonsterBossSabaoth::open_section_4( stateParms_t* parms ) { return set_section_open( parms, 4, true ); }
stateResult_t rvmMonsterBossSabaoth::close_section_4( stateParms_t* parms ) { return set_section_open( parms, 4, false ); }
stateResult_t rvmMonsterBossSabaoth::open_section_5( stateParms_t* parms ) { return set_section_open( parms, 5, true ); }
stateResult_t rvmMonsterBossSabaoth::close_section_5( stateParms_t* parms ) { return set_section_open( parms, 5, false ); }
stateResult_t rvmMonsterBossSabaoth::open_section_6( stateParms_t* parms ) { return set_section_open( parms, 6, true ); }
stateResult_t rvmMonsterBossSabaoth::close_section_6( stateParms_t* parms ) { return set_section_open( parms, 6, false ); }
stateResult_t rvmMonsterBossSabaoth::open_section_7( stateParms_t* parms ) { return set_section_open( parms, 7, true ); }
stateResult_t rvmMonsterBossSabaoth::close_section_7( stateParms_t* parms ) { return set_section_open( parms, 7, false ); }
stateResult_t rvmMonsterBossSabaoth::open_section_8( stateParms_t* parms ) { return set_section_open( parms, 8, true ); }
stateResult_t rvmMonsterBossSabaoth::close_section_8( stateParms_t* parms ) { return set_section_open( parms, 8, false ); }
@@ -0,0 +1,79 @@
// AI_monster_boss_sabaoth.h
// Native state-machine replacement for ai_monster_boss_sabaoth.script.
#pragma once
#ifndef SABAOTH_ATTACK_RATE
#define SABAOTH_ATTACK_RATE 4
#endif
#ifndef SABAOTH_ATTACK_RATE_MELEE
#define SABAOTH_ATTACK_RATE_MELEE 1
#endif
#ifndef SABAOTH_SLOW_DIST
#define SABAOTH_SLOW_DIST 64
#endif
#ifndef SECTION_MOVE_DIST
#define SECTION_MOVE_DIST 190
#endif
#ifndef CENTER_DIST
#define CENTER_DIST 304
#endif
#ifndef MELEE_INIT
#define MELEE_INIT 290
#endif
class rvmMonsterBossSabaoth : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterBossSabaoth );
rvmMonsterBossSabaoth();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextAttack;
float nextMeleeAttack;
float pathSpeed;
bool noSlowToStop;
idEntityPtr<idEntity> currentPath;
idEntityPtr<idEntity> nextPath;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t move_to_corner( stateParms_t* parms );
virtual stateResult_t turn_to_corner( stateParms_t* parms );
virtual stateResult_t closeSection( stateParms_t* parms );
virtual stateResult_t openSection( stateParms_t* parms );
virtual stateResult_t state_Combat( stateParms_t* parms );
virtual stateResult_t combat_range( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t fire_bfg( stateParms_t* parms );
virtual stateResult_t open_section_1( stateParms_t* parms );
virtual stateResult_t close_section_1( stateParms_t* parms );
virtual stateResult_t open_section_2( stateParms_t* parms );
virtual stateResult_t close_section_2( stateParms_t* parms );
virtual stateResult_t open_section_3( stateParms_t* parms );
virtual stateResult_t close_section_3( stateParms_t* parms );
virtual stateResult_t open_section_4( stateParms_t* parms );
virtual stateResult_t close_section_4( stateParms_t* parms );
virtual stateResult_t open_section_5( stateParms_t* parms );
virtual stateResult_t close_section_5( stateParms_t* parms );
virtual stateResult_t open_section_6( stateParms_t* parms );
virtual stateResult_t close_section_6( stateParms_t* parms );
virtual stateResult_t open_section_7( stateParms_t* parms );
virtual stateResult_t close_section_7( stateParms_t* parms );
virtual stateResult_t open_section_8( stateParms_t* parms );
virtual stateResult_t close_section_8( stateParms_t* parms );
virtual int check_attacks( bool meleeOnly );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
stateResult_t set_section_open( stateParms_t* parms, int section, bool open );
};
@@ -0,0 +1,323 @@
// AI_monster_boss_vagary.cpp
// Native state-machine replacement for ai_monster_boss_vagary.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterBossVagary )
END_CLASS
rvmMonsterBossVagary::rvmMonsterBossVagary() {
nextDodge = 0.0f;
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
painNextTime = 0.0f;
combat_node = NULL;
throwEntity = NULL;
}
void rvmMonsterBossVagary::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
SetState( "state_Begin" );
}
stateResult_t rvmMonsterBossVagary::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_RangeAttack", Torso_RangeAttack );
AI_DISPATCH_STATE( "Torso_TurretAttack", Torso_TurretAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_DodgeLeft", Legs_DodgeLeft );
AI_DISPATCH_STATE( "Legs_DodgeRight", Legs_DodgeRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "throw_object", throw_object );
AI_DISPATCH_STATE( "combat_range", combat_range );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterBossVagary::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "stand" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", VAGARY_IDLE_TO_PAIN ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossVagary::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( VAGARY_PAIN_DELAY );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", VAGARY_PAIN_TO_PAIN ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, VAGARY_PAIN_TO_IDLE ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", VAGARY_PAIN_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossVagary::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, VAGARY_MELEE_TO_IDLE ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", VAGARY_MELEE_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossVagary::Torso_RangeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
LookAtEnemy( 1.0f );
if ( AnimDone( ANIMCHANNEL_TORSO, VAGARY_RANGE_TO_IDLE ) ) {
FinishAction( "range_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", VAGARY_RANGE_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossVagary::Torso_TurretAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
AllowMovement( false );
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, VAGARY_TURRET_TO_IDLE ) ) {
AllowMovement( true );
FinishAction( "turret_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossVagary::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "stand" ); parms->stage = 1; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", VAGARY_IDLE_TO_WALK ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossVagary::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", VAGARY_WALK_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossVagary::Legs_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, VAGARY_DODGE_LEFT_TO_IDLE ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", VAGARY_DODGE_LEFT_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossVagary::Legs_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, VAGARY_DODGE_RIGHT_TO_IDLE ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", VAGARY_DODGE_RIGHT_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterBossVagary::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossVagary::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextDodge = RandomTime( VAGARY_DODGE_RATE );
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterBossVagary::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
const float currentTime = Time();
if ( !canMelee && Pain() && currentTime >= nextDodge ) {
if ( TestAnimMove( "evade_left" ) ) { flags |= ATTACK_DODGE_LEFT; }
if ( TestAnimMove( "evade_right" ) ) {
flags |= ATTACK_DODGE_RIGHT;
if ( flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { flags &= ~ATTACK_DODGE_RIGHT; } else { flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
if ( !canMelee ) {
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; }
}
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
if ( !CanReachEnemy() || currentTime >= nextAttack ) {
if ( CanHitEnemy() ) {
throwEntity = FindActorsInBounds( idVec3( -512, -512, -512 ), idVec3( 512, 512, 512 ) );
if ( throwEntity.GetEntity() ) { flags |= ATTACK_MISSILE; }
}
}
}
return static_cast<float>( flags );
}
bool rvmMonsterBossVagary::do_attack( int attack_flags ) {
nextNoFOVAttack = Time() + VAGARY_NOFOVTIME;
const int flags = attack_flags;
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "combat_range" ); return true; }
return false;
}
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 );
}
return SRESULT_DONE;
}
stateResult_t rvmMonsterBossVagary::combat_range( stateParms_t* parms ) {
enum { STAGE_FACE, STAGE_THROW, STAGE_COOLDOWN };
switch ( parms->stage ) {
case STAGE_FACE:
FaceEnemy();
LookAtEntity( throwEntity.GetEntity(), 3.0f );
scratchEndTime = DelayTime( 0.3f );
parms->stage = STAGE_THROW;
return SRESULT_WAIT;
case STAGE_THROW:
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
StartAction( "throw_object" );
scratchEndTime = DelayTime( 1.0f );
parms->stage = STAGE_COOLDOWN;
return SRESULT_WAIT;
case STAGE_COOLDOWN:
if ( InAction() || Time() < scratchEndTime ) { return SRESULT_WAIT; }
LookAtEnemy( 1.0f );
nextAttack = DelayTime( VAGARY_ATTACK_RATE );
nextNoFOVAttack = Time() + VAGARY_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossVagary::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", VAGARY_WALK_TO_MELEE );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossVagary::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeLeft", VAGARY_WALK_TO_DODGE_LEFT );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( VAGARY_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterBossVagary::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeRight", VAGARY_WALK_TO_DODGE_RIGHT );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( VAGARY_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
+109
View File
@@ -0,0 +1,109 @@
// AI_monster_boss_vagary.h
// Native state-machine replacement for ai_monster_boss_vagary.script.
#pragma once
#ifndef VAGARY_ATTACK_RATE
#define VAGARY_ATTACK_RATE 1.5f
#endif
#ifndef VAGARY_DODGE_RATE
#define VAGARY_DODGE_RATE 4
#endif
#ifndef VAGARY_PAIN_DELAY
#define VAGARY_PAIN_DELAY 0.25f
#endif
#ifndef VAGARY_TURRET_TO_IDLE
#define VAGARY_TURRET_TO_IDLE 4
#endif
#ifndef VAGARY_THROW_SPEED
#define VAGARY_THROW_SPEED 1200
#endif
#ifndef VAGARY_THROW_SPEED2
#define VAGARY_THROW_SPEED2 1500
#endif
#ifndef VAGARY_THROW_DIST
#define VAGARY_THROW_DIST 200
#endif
#ifndef VAGARY_THROW_OFFSET
#define VAGARY_THROW_OFFSET 64
#endif
#ifndef VAGARY_NOFOVTIME
#define VAGARY_NOFOVTIME 4
#endif
#ifndef VAGARY_PAIN_TO_IDLE
#define VAGARY_PAIN_TO_IDLE 2
#endif
#ifndef VAGARY_PAIN_TO_PAIN
#define VAGARY_PAIN_TO_PAIN 0
#endif
#ifndef VAGARY_MELEE_TO_IDLE
#define VAGARY_MELEE_TO_IDLE 4
#endif
#ifndef VAGARY_RANGE_TO_IDLE
#define VAGARY_RANGE_TO_IDLE 4
#endif
#ifndef VAGARY_DODGE_LEFT_TO_IDLE
#define VAGARY_DODGE_LEFT_TO_IDLE 4
#endif
#ifndef VAGARY_DODGE_RIGHT_TO_IDLE
#define VAGARY_DODGE_RIGHT_TO_IDLE 4
#endif
#ifndef VAGARY_IDLE_TO_PAIN
#define VAGARY_IDLE_TO_PAIN 0
#endif
#ifndef VAGARY_IDLE_TO_WALK
#define VAGARY_IDLE_TO_WALK 4
#endif
#ifndef VAGARY_WALK_TO_IDLE
#define VAGARY_WALK_TO_IDLE 4
#endif
#ifndef VAGARY_WALK_TO_RANGEATTACK
#define VAGARY_WALK_TO_RANGEATTACK 4
#endif
#ifndef VAGARY_WALK_TO_MELEE
#define VAGARY_WALK_TO_MELEE 4
#endif
#ifndef VAGARY_WALK_TO_DODGE_LEFT
#define VAGARY_WALK_TO_DODGE_LEFT 4
#endif
#ifndef VAGARY_WALK_TO_DODGE_RIGHT
#define VAGARY_WALK_TO_DODGE_RIGHT 4
#endif
class rvmMonsterBossVagary : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterBossVagary );
rvmMonsterBossVagary();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextDodge;
float nextAttack;
float nextNoFOVAttack;
float painNextTime;
idEntityPtr<idEntity> combat_node;
idEntityPtr<idEntity> throwEntity;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Torso_TurretAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Legs_DodgeRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t throw_object( stateParms_t* parms );
virtual stateResult_t combat_range( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,465 @@
// AI_monster_demon_archvile.cpp
// Native state-machine replacement for ai_monster_demon_archvile.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterDemonArchvile )
END_CLASS
rvmMonsterDemonArchvile::rvmMonsterDemonArchvile() {
nextDodge = 0.0f;
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
painNextTime = 0.0f;
keep_alive = 0.0f;
num_start = 0.0f;
summon_delay = 0.0f;
summon_time = 0.0f;
combat_node = NULL;
resurrectionTarget = NULL;
flameWallOrigin = vec3_zero;
incinerateOrigin = vec3_zero;
}
void rvmMonsterDemonArchvile::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
keep_alive = GetFloatKey( "keep_alive", "0" );
num_start = GetFloatKey( "num_start", "0" );
summon_delay = GetFloatKey( "summon_delay", "0" );
summon_time = Time() + summon_delay;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterDemonArchvile::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_Resurrect", Torso_Resurrect );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_FlameWallAttack", Torso_FlameWallAttack );
AI_DISPATCH_STATE( "Torso_IncinerateAttack", Torso_IncinerateAttack );
AI_DISPATCH_STATE( "Torso_TurretAttack", Torso_TurretAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_DodgeLeft", Legs_DodgeLeft );
AI_DISPATCH_STATE( "Legs_DodgeRight", Legs_DodgeRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_resurrect", combat_resurrect );
AI_DISPATCH_STATE( "combat_flamewall", combat_flamewall );
AI_DISPATCH_STATE( "combat_incinerate", combat_incinerate );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
AI_DISPATCH_STATE( "spawn_flame_wall", spawn_flame_wall );
AI_DISPATCH_STATE( "spawn_incerate", spawn_incerate );
AI_DISPATCH_STATE( "flame_wall_loop", flame_wall_loop );
AI_DISPATCH_STATE( "flame_wall", flame_wall );
AI_DISPATCH_STATE( "incinerate", incinerate );
AI_DISPATCH_STATE( "resurrection", resurrection );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterDemonArchvile::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "stand" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", ARCH_IDLE_TO_PAIN ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonArchvile::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( ARCH_PAIN_DELAY );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", ARCH_PAIN_TO_PAIN ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, ARCH_PAIN_TO_IDLE ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", ARCH_PAIN_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonArchvile::Torso_Resurrect( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
SetSmokeVisibility( -1, true );
PlayAnim( ANIMCHANNEL_TORSO, "resurrection" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, ARCH_RESURRECT_TO_IDLE ) ) {
SetSmokeVisibility( -1, false );
FinishAction( "resurrect" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", ARCH_RESURRECT_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonArchvile::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
LookAtEnemy( 0.2f );
if ( AnimDone( ANIMCHANNEL_TORSO, ARCH_MELEE_TO_IDLE ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", ARCH_MELEE_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonArchvile::Torso_FlameWallAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
FaceEnemy();
SetSmokeVisibility( -1, true );
PlayAnim( ANIMCHANNEL_TORSO, "flamewall_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
LookAtEnemy( 1.0f );
if ( AnimDone( ANIMCHANNEL_TORSO, ARCH_FLAMEWALL_TO_IDLE ) ) {
SetSmokeVisibility( -1, false );
nextAttack = DelayTime( ARCH_ATTACK_RATE );
FinishAction( "flamewall_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", ARCH_FLAMEWALL_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonArchvile::Torso_IncinerateAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, "incinerate_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
LookAtEnemy( 1.0f );
if ( AnimDone( ANIMCHANNEL_TORSO, ARCH_INCINERATE_TO_IDLE ) ) {
nextAttack = DelayTime( ARCH_ATTACK_RATE );
FinishAction( "incinerate_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", ARCH_INCINERATE_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonArchvile::Torso_TurretAttack( stateParms_t* parms ) {
// The DoomScript turret attack can resurrect, flame-wall, or incinerate from the same entry.
// Choose once at entry, then delegate to the concrete non-blocking torso state.
if ( parms->stage == 0 ) {
if ( num_minions() < keep_alive && Time() >= summon_time ) {
waitActionName = "resurrect";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Resurrect", ARCH_IDLE_TO_TURRETATTACK );
} else if ( EnemyRange() < ARCH_FLAME_WALL_RANGE && CanHitEnemy() ) {
waitActionName = "flamewall_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_FlameWallAttack", ARCH_IDLE_TO_TURRETATTACK );
} else {
waitActionName = "incinerate_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_IncinerateAttack", ARCH_IDLE_TO_TURRETATTACK );
}
parms->stage = 1;
return SRESULT_WAIT;
}
if ( InAnimState( ANIMCHANNEL_TORSO, "Torso_TurretAttack" ) ) { return SRESULT_WAIT; }
FinishAction( "turret_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", ARCH_TURRETATTACK_TO_IDLE );
return SRESULT_DONE;
}
stateResult_t rvmMonsterDemonArchvile::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "stand" ); parms->stage = 1; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", ARCH_IDLE_TO_WALK ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonArchvile::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", ARCH_WALK_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonArchvile::Legs_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, ARCH_DODGE_LEFT_TO_IDLE ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", ARCH_DODGE_LEFT_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonArchvile::Legs_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, ARCH_DODGE_RIGHT_TO_IDLE ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", ARCH_DODGE_RIGHT_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonArchvile::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonArchvile::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextDodge = RandomTime( ARCH_DODGE_RATE );
summon_time = Time() + summon_delay;
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
float rvmMonsterDemonArchvile::num_minions() {
// Integration hook: the script counted live minion targets from its target list. Return zero here
// so a native port can opt into resurrect behavior without blocking; replace with target iteration
// if your project exposes the original target array to native AI objects.
return 0;
}
int rvmMonsterDemonArchvile::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
const float currentTime = Time();
if ( !canMelee && Pain() && currentTime >= nextDodge ) {
if ( TestAnimMove( "evade_left" ) ) { flags |= ATTACK_DODGE_LEFT; }
if ( TestAnimMove( "evade_right" ) ) {
flags |= ATTACK_DODGE_RIGHT;
if ( flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { flags &= ~ATTACK_DODGE_RIGHT; } else { flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
if ( !canMelee ) {
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; }
}
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( num_minions() < keep_alive && currentTime >= summon_time ) { flags |= ATTACK_RESURRECT; }
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
if ( currentTime >= nextAttack ) {
if ( EnemyRange() < ARCH_FLAME_WALL_RANGE && CanHitEnemy() ) { flags |= ATTACK_FLAMEWALL; }
if ( CanHitEnemy() ) { flags |= ATTACK_MISSILE; }
}
}
return static_cast<float>( flags );
}
bool rvmMonsterDemonArchvile::do_attack( int attack_flags ) {
nextNoFOVAttack = Time() + ARCH_NOFOVTIME;
const int flags = attack_flags;
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_RESURRECT ) { StartAction( "combat_resurrect" ); return true; }
if ( flags & ATTACK_FLAMEWALL ) { StartAction( "combat_flamewall" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "combat_incinerate" ); return true; }
return false;
}
stateResult_t rvmMonsterDemonArchvile::combat_resurrect( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "resurrect";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Resurrect", ARCH_IDLE_TO_RESURRECT );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "resurrect" ) ) { return SRESULT_WAIT; }
summon_time = Time() + summon_delay;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonArchvile::combat_flamewall( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "flamewall_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_FlameWallAttack", ARCH_IDLE_TO_FLAMEWALLATTACK );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "flamewall_attack" ) ) { return SRESULT_WAIT; }
nextAttack = DelayTime( ARCH_ATTACK_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonArchvile::combat_incinerate( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "incinerate_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_IncinerateAttack", ARCH_IDLE_TO_INCINERATEATTACK );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "incinerate_attack" ) ) { return SRESULT_WAIT; }
nextAttack = DelayTime( ARCH_ATTACK_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonArchvile::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", ARCH_WALK_TO_MELEE );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonArchvile::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeLeft", ARCH_WALK_TO_DODGE_LEFT );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( ARCH_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonArchvile::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeRight", ARCH_WALK_TO_DODGE_RIGHT );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( ARCH_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
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.
return SRESULT_DONE;
}
stateResult_t rvmMonsterDemonArchvile::spawn_incerate( stateParms_t* parms ) {
// Hook point for the original script's incinerate projectile spawn.
return SRESULT_DONE;
}
stateResult_t rvmMonsterDemonArchvile::flame_wall_loop( stateParms_t* parms ) {
enum { STAGE_INIT, STAGE_SPAWN, STAGE_DELAY };
switch ( parms->stage ) {
case STAGE_INIT:
scratchCount = 0;
parms->stage = STAGE_SPAWN;
return SRESULT_WAIT;
case STAGE_SPAWN:
if ( scratchCount >= ARCH_FLAME_WALL_NUM ) { return SRESULT_DONE; }
flameWallOrigin = GetEnemyPos();
StartAction( "spawn_flame_wall" );
scratchCount++;
scratchEndTime = DelayTime( 0.05f );
parms->stage = STAGE_DELAY;
return SRESULT_WAIT;
case STAGE_DELAY:
if ( InAction() || Time() < scratchEndTime ) { return SRESULT_WAIT; }
parms->stage = STAGE_SPAWN;
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;
}
stateResult_t rvmMonsterDemonArchvile::incinerate( stateParms_t* parms ) {
incinerateOrigin = GetEnemyPos();
StartAction( "spawn_incerate" );
return SRESULT_DONE;
}
stateResult_t rvmMonsterDemonArchvile::resurrection( stateParms_t* parms ) {
// Hook point for resurrecting a selected dead minion. This remains non-blocking.
resurrectionTarget = NULL;
return SRESULT_DONE;
}
@@ -0,0 +1,173 @@
// AI_monster_demon_archvile.h
// Native state-machine replacement for ai_monster_demon_archvile.script.
#pragma once
#ifndef ARCH_ATTACK_RATE
#define ARCH_ATTACK_RATE 2
#endif
#ifndef ARCH_DODGE_RATE
#define ARCH_DODGE_RATE 4
#endif
#ifndef ARCH_PAIN_DELAY
#define ARCH_PAIN_DELAY 0.25f
#endif
#ifndef ARCH_TURRET_TO_IDLE
#define ARCH_TURRET_TO_IDLE 4
#endif
#ifndef ARCH_FLAME_WALL_NUM
#define ARCH_FLAME_WALL_NUM 20
#endif
#ifndef ARCH_FLAME_WALL_SEPERATION
#define ARCH_FLAME_WALL_SEPERATION 48
#endif
#ifndef ARCH_FLAME_WALL_RANGE
#define ARCH_FLAME_WALL_RANGE ( ARCH_FLAME_WALL_NUM * ARCH_FLAME_WALL_SEPERATION )
#endif
#ifndef ARCH_NOFOVTIME
#define ARCH_NOFOVTIME 4
#endif
#ifndef ARCH_PAIN_TO_IDLE
#define ARCH_PAIN_TO_IDLE 2
#endif
#ifndef ARCH_PAIN_TO_PAIN
#define ARCH_PAIN_TO_PAIN 0
#endif
#ifndef ARCH_SIGHT_TO_IDLE
#define ARCH_SIGHT_TO_IDLE 4
#endif
#ifndef ARCH_RESURRECT_TO_IDLE
#define ARCH_RESURRECT_TO_IDLE 4
#endif
#ifndef ARCH_IDLE_TO_RESURRECT
#define ARCH_IDLE_TO_RESURRECT 4
#endif
#ifndef ARCH_MELEE_TO_IDLE
#define ARCH_MELEE_TO_IDLE 4
#endif
#ifndef ARCH_INCINERATE_TO_IDLE
#define ARCH_INCINERATE_TO_IDLE 4
#endif
#ifndef ARCH_FLAMEWALL_TO_IDLE
#define ARCH_FLAMEWALL_TO_IDLE 4
#endif
#ifndef ARCH_DODGE_LEFT_TO_IDLE
#define ARCH_DODGE_LEFT_TO_IDLE 4
#endif
#ifndef ARCH_DODGE_RIGHT_TO_IDLE
#define ARCH_DODGE_RIGHT_TO_IDLE 4
#endif
#ifndef ARCH_WAIT_TO_OUT
#define ARCH_WAIT_TO_OUT 4
#endif
#ifndef ARCH_IN_TO_WAIT
#define ARCH_IN_TO_WAIT 4
#endif
#ifndef ARCH_WALK_TO_WAIT
#define ARCH_WALK_TO_WAIT 4
#endif
#ifndef ARCH_WALK_TO_ACTION
#define ARCH_WALK_TO_ACTION 4
#endif
#ifndef ARCH_ACTION_TO_IDLE
#define ARCH_ACTION_TO_IDLE 4
#endif
#ifndef ARCH_WALK_TO_DODGE_LEFT
#define ARCH_WALK_TO_DODGE_LEFT 4
#endif
#ifndef ARCH_WALK_TO_DODGE_RIGHT
#define ARCH_WALK_TO_DODGE_RIGHT 4
#endif
#ifndef ARCH_IDLE_TO_PAIN
#define ARCH_IDLE_TO_PAIN 0
#endif
#ifndef ARCH_IDLE_TO_WALK
#define ARCH_IDLE_TO_WALK 4
#endif
#ifndef ARCH_IDLE_TO_SIGHT
#define ARCH_IDLE_TO_SIGHT 4
#endif
#ifndef ARCH_WALK_TO_IDLE
#define ARCH_WALK_TO_IDLE 4
#endif
#ifndef ARCH_WALK_TO_FLAMEWALLATTACK
#define ARCH_WALK_TO_FLAMEWALLATTACK 4
#endif
#ifndef ARCH_IDLE_TO_FLAMEWALLATTACK
#define ARCH_IDLE_TO_FLAMEWALLATTACK 4
#endif
#ifndef ARCH_WALK_TO_INCINERATEATTACK
#define ARCH_WALK_TO_INCINERATEATTACK 4
#endif
#ifndef ARCH_IDLE_TO_INCINERATEATTACK
#define ARCH_IDLE_TO_INCINERATEATTACK 4
#endif
#ifndef ARCH_WALK_TO_MELEE
#define ARCH_WALK_TO_MELEE 4
#endif
#ifndef ARCH_IDLE_TO_TURRETATTACK
#define ARCH_IDLE_TO_TURRETATTACK 4
#endif
#ifndef ARCH_TURRETATTACK_TO_IDLE
#define ARCH_TURRETATTACK_TO_IDLE 4
#endif
#ifndef ATTACK_FLAMEWALL
#define ATTACK_FLAMEWALL ATTACK_SPECIAL1
#endif
#ifndef ATTACK_RESURRECT
#define ATTACK_RESURRECT ATTACK_SPECIAL2
#endif
class rvmMonsterDemonArchvile : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterDemonArchvile );
rvmMonsterDemonArchvile();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextDodge;
float nextAttack;
float nextNoFOVAttack;
float painNextTime;
float keep_alive;
float num_start;
float summon_delay;
float summon_time;
idEntityPtr<idEntity> combat_node;
idEntityPtr<idEntity> resurrectionTarget;
idVec3 flameWallOrigin;
idVec3 incinerateOrigin;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_Resurrect( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_FlameWallAttack( stateParms_t* parms );
virtual stateResult_t Torso_IncinerateAttack( stateParms_t* parms );
virtual stateResult_t Torso_TurretAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Legs_DodgeRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_resurrect( stateParms_t* parms );
virtual stateResult_t combat_flamewall( stateParms_t* parms );
virtual stateResult_t combat_incinerate( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual stateResult_t spawn_flame_wall( stateParms_t* parms );
virtual stateResult_t spawn_incerate( stateParms_t* parms );
virtual stateResult_t flame_wall_loop( stateParms_t* parms );
virtual stateResult_t flame_wall( stateParms_t* parms );
virtual stateResult_t incinerate( stateParms_t* parms );
virtual stateResult_t resurrection( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
virtual float num_minions();
};
@@ -0,0 +1,343 @@
// AI_monster_demon_cherub.cpp
// Native state-machine replacement for ai_monster_demon_cherub.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterDemonCherub )
END_CLASS
rvmMonsterDemonCherub::rvmMonsterDemonCherub() {
explode = false;
nextDodge = 0.0f;
nextLeap = 0.0f;
painNextTime = 0.0f;
jumpVelocity = vec3_zero;
combat_node = NULL;
}
void rvmMonsterDemonCherub::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
run_distance = CHERUB_RUNDISTANCE;
walk_turn = CHERUB_WALKTURN;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterDemonCherub::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_LeapAttack", Torso_LeapAttack );
AI_DISPATCH_STATE( "Torso_Walk", Torso_Walk );
AI_DISPATCH_STATE( "Torso_Run", Torso_Run );
AI_DISPATCH_STATE( "Torso_DodgeLeft", Torso_DodgeLeft );
AI_DISPATCH_STATE( "Torso_DodgeRight", Torso_DodgeRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_leap", combat_leap );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
AI_DISPATCH_STATE( "path_jump", path_jump );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterDemonCherub::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Run", 4 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Walk", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonCherub::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonCherub::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonCherub::Torso_LeapAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_START_WAIT, STAGE_LOOP, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
OverrideAnim( ANIMCHANNEL_LEGS );
PreventPain( 1.0f );
PlayAnim( ANIMCHANNEL_TORSO, "jump_start" );
parms->stage = STAGE_START_WAIT;
return SRESULT_WAIT;
case STAGE_START_WAIT:
if ( !AnimDone( ANIMCHANNEL_TORSO, 2 ) ) { return SRESULT_WAIT; }
AttackBegin( "melee_cherubLeapAttack" );
SetLinearVelocity( jumpVelocity );
PlayCycle( ANIMCHANNEL_TORSO, "jump_loop" );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
if ( owner && !owner->AI_ONGROUND ) { return SRESULT_WAIT; }
AttackEnd();
PlayAnim( ANIMCHANNEL_TORSO, "jump_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "leap_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonCherub::Torso_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "walk" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Run", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonCherub::Torso_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "run" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( !run && MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Walk", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonCherub::Torso_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_TORSO, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonCherub::Torso_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_TORSO, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonCherub::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonCherub::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextLeap = RandomTime( CHERUB_LEAP_RATE );
nextDodge = RandomTime( CHERUB_DODGE_RATE );
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterDemonCherub::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
const float currentTime = Time();
if ( !canMelee && Pain() && currentTime >= nextDodge ) {
if ( TestAnimMove( "evade_left" ) ) { flags |= ATTACK_DODGE_LEFT; }
if ( TestAnimMove( "evade_right" ) ) {
flags |= ATTACK_DODGE_RIGHT;
if ( flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { flags &= ~ATTACK_DODGE_RIGHT; } else { flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
if ( !canMelee ) {
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; }
}
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( EnemyInFOV() ) {
const float range = EnemyRange();
if ( range >= 0 && range < CHERUB_LEAP_RANGE && currentTime >= nextLeap && CanHitEnemy() ) {
const float t = AnimLength( ANIMCHANNEL_TORSO, "jump_start" );
const idVec3 jumpTarget = PredictEnemyPos( t );
jumpVelocity = GetJumpVelocity( jumpTarget, CHERUB_LEAP_SPEED, CHERUB_LEAP_MAXHEIGHT );
if ( jumpVelocity.LengthSqr() > 0.001f ) { flags |= ATTACK_LEAP; } else { nextLeap = DelayTime( 2.0f ); }
}
}
return static_cast<float>( flags );
}
bool rvmMonsterDemonCherub::do_attack( int attack_flags ) {
const int flags = attack_flags;
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_LEAP ) { StartAction( "combat_leap" ); return true; }
return false;
}
stateResult_t rvmMonsterDemonCherub::combat_leap( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
TurnToPos( GetOrigin() + jumpVelocity );
waitActionName = "leap_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "leap_attack" ) ) { return SRESULT_WAIT; }
nextLeap = DelayTime( CHERUB_LEAP_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonCherub::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonCherub::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_DodgeLeft", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( CHERUB_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonCherub::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_DodgeRight", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( CHERUB_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonCherub::path_jump( stateParms_t* parms ) {
enum { STAGE_PATH_CORNER, STAGE_SETUP, STAGE_TURN, STAGE_LEAP, STAGE_DONE };
idEntity* path = current_path.GetEntity();
switch ( parms->stage ) {
case STAGE_PATH_CORNER:
StartAction( "path_corner" );
parms->stage = STAGE_SETUP;
return SRESULT_WAIT;
case STAGE_SETUP: {
if ( InAction() ) { return SRESULT_WAIT; }
if ( checkForEnemy( true ) ) { return SRESULT_DONE; }
idEntity* target = RandomPathFrom( path );
if ( !target ) { return SRESULT_DONE; }
scratchEntity = target;
const idVec3 jumpTarget = target->GetPhysics()->GetOrigin();
if ( path && path->spawnArgs.GetString( "up", "" )[0] ) {
float forward = path->spawnArgs.GetFloat( "forward", "0" );
idVec3 dir = jumpTarget - GetOrigin();
dir.z = 0.0f;
dir.Normalize();
jumpVelocity = dir * forward;
jumpVelocity.z = path->spawnArgs.GetFloat( "up", "0" );
} else {
jumpVelocity = GetJumpVelocity( jumpTarget, CHERUB_LEAP_SPEED, 1024.0f );
}
StopMove();
TurnToPos( jumpTarget );
parms->stage = STAGE_TURN;
return SRESULT_WAIT;
}
case STAGE_TURN:
if ( checkForEnemy( true ) ) { return SRESULT_DONE; }
if ( !FacingIdeal() ) { return SRESULT_WAIT; }
waitActionName = "leap_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
parms->stage = STAGE_LEAP;
return SRESULT_WAIT;
case STAGE_LEAP:
if ( !ActionDone( "leap_attack" ) ) { return SRESULT_WAIT; }
StopMove();
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,63 @@
// AI_monster_demon_cherub.h
// Native state-machine replacement for ai_monster_demon_cherub.script.
#pragma once
#ifndef CHERUB_RUNDISTANCE
#define CHERUB_RUNDISTANCE 192
#endif
#ifndef CHERUB_WALKTURN
#define CHERUB_WALKTURN 65
#endif
#ifndef CHERUB_LEAP_RATE
#define CHERUB_LEAP_RATE 2
#endif
#ifndef CHERUB_DODGE_RATE
#define CHERUB_DODGE_RATE 2
#endif
#ifndef CHERUB_LEAP_RANGE
#define CHERUB_LEAP_RANGE 1024
#endif
#ifndef CHERUB_LEAP_SPEED
#define CHERUB_LEAP_SPEED 650
#endif
#ifndef CHERUB_LEAP_MAXHEIGHT
#define CHERUB_LEAP_MAXHEIGHT 48
#endif
class rvmMonsterDemonCherub : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterDemonCherub );
rvmMonsterDemonCherub();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
bool explode;
float nextDodge;
float nextLeap;
float painNextTime;
idVec3 jumpVelocity;
idEntityPtr<idEntity> combat_node;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_LeapAttack( stateParms_t* parms );
virtual stateResult_t Torso_Walk( stateParms_t* parms );
virtual stateResult_t Torso_Run( stateParms_t* parms );
virtual stateResult_t Torso_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Torso_DodgeRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_leap( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual stateResult_t path_jump( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,246 @@
// AI_monster_demon_hellknight.cpp
// Native state-machine replacement for ai_monster_demon_hellknight.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterDemonHellknight )
END_CLASS
rvmMonsterDemonHellknight::rvmMonsterDemonHellknight() {
nextDodge = 0.0f;
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextLeap = 0.0f;
painNextTime = 0.0f;
jumpVelocity = vec3_zero;
combat_node = NULL;
}
void rvmMonsterDemonHellknight::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
SetState( "state_Begin" );
}
stateResult_t rvmMonsterDemonHellknight::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_RangeAttack", Torso_RangeAttack );
AI_DISPATCH_STATE( "Torso_TurretAttack", Torso_TurretAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_range", combat_range );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterDemonHellknight::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "stand" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonHellknight::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", HELLKNIGHT_PAIN_TO_PAIN ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, HELLKNIGHT_PAIN_TO_IDLE ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", HELLKNIGHT_PAIN_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonHellknight::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, HELLKNIGHT_MELEE_TO_IDLE ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", HELLKNIGHT_MELEE_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonHellknight::Torso_RangeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, range_attack_anim.c_str() );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
LookAtEnemy( 1.0f );
if ( AnimDone( ANIMCHANNEL_TORSO, HELLKNIGHT_RANGE_TO_IDLE ) ) {
AllowMovement( true );
FinishAction( "range_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", HELLKNIGHT_RANGE_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonHellknight::Torso_TurretAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
AllowMovement( false );
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, "turret_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
FaceEnemy();
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
AllowMovement( true );
FinishAction( "turret_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonHellknight::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "stand" ); parms->stage = 1; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonHellknight::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonHellknight::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonHellknight::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextDodge = 0.0f;
nextLeap = 0.0f;
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterDemonHellknight::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
const float currentTime = Time();
if ( !canMelee ) {
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; }
}
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
if ( !CanReachEnemy() || currentTime >= nextAttack ) {
range_attack_anim = ChooseAnim( ANIMCHANNEL_LEGS, "turret_attack" );
if ( CanHitEnemyFromAnim( range_attack_anim.c_str() ) ) { flags |= ATTACK_MISSILE; }
const char* movingAnim = ChooseAnim( ANIMCHANNEL_LEGS, "range_attack" );
if ( TestAnimMoveTowardEnemy( movingAnim ) && CanHitEnemyFromAnim( movingAnim ) ) {
range_attack_anim = movingAnim;
flags |= ATTACK_MISSILE;
}
}
}
return static_cast<float>( flags );
}
bool rvmMonsterDemonHellknight::do_attack( int attack_flags ) {
nextNoFOVAttack = Time() + HELLKNIGHT_NOFOVTIME;
const int flags = attack_flags;
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "combat_range" ); return true; }
return false;
}
stateResult_t rvmMonsterDemonHellknight::combat_range( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
waitActionName = "range_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "range_attack" ) ) { return SRESULT_WAIT; }
nextAttack = DelayTime( HELLKNIGHT_ATTACK_RATE );
nextNoFOVAttack = Time() + HELLKNIGHT_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonHellknight::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,61 @@
// AI_monster_demon_hellknight.h
// Native state-machine replacement for ai_monster_demon_hellknight.script.
#pragma once
#ifndef HELLKNIGHT_ATTACK_RATE
#define HELLKNIGHT_ATTACK_RATE 3
#endif
#ifndef HELLKNIGHT_PAIN_DELAY
#define HELLKNIGHT_PAIN_DELAY 0.25f
#endif
#ifndef HELLKNIGHT_NOFOVTIME
#define HELLKNIGHT_NOFOVTIME 6
#endif
#ifndef HELLKNIGHT_PAIN_TO_IDLE
#define HELLKNIGHT_PAIN_TO_IDLE 2
#endif
#ifndef HELLKNIGHT_PAIN_TO_PAIN
#define HELLKNIGHT_PAIN_TO_PAIN 0
#endif
#ifndef HELLKNIGHT_MELEE_TO_IDLE
#define HELLKNIGHT_MELEE_TO_IDLE 4
#endif
#ifndef HELLKNIGHT_RANGE_TO_IDLE
#define HELLKNIGHT_RANGE_TO_IDLE 4
#endif
class rvmMonsterDemonHellknight : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterDemonHellknight );
rvmMonsterDemonHellknight();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextDodge;
float nextAttack;
float nextNoFOVAttack;
float nextLeap;
float painNextTime;
idVec3 jumpVelocity;
idStr range_attack_anim;
idEntityPtr<idEntity> combat_node;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Torso_TurretAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_range( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
+435
View File
@@ -0,0 +1,435 @@
// AI_monster_demon_imp.cpp
// Native state-machine replacement for ai_monster_demon_imp.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterDemonImp )
END_CLASS
rvmMonsterDemonImp::rvmMonsterDemonImp() {
nextDodge = 0.0f;
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextLeap = 0.0f;
painNextTime = 0.0f;
jumpVelocity = vec3_zero;
combat_node = NULL;
}
void rvmMonsterDemonImp::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
run_distance = IMP_RUNDISTANCE;
walk_turn = IMP_WALKTURN;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterDemonImp::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_RangeAttack", Torso_RangeAttack );
AI_DISPATCH_STATE( "Torso_TurretAttack", Torso_TurretAttack );
AI_DISPATCH_STATE( "Torso_TurretAttackIdle", Torso_TurretAttackIdle );
AI_DISPATCH_STATE( "Torso_LeapAttack", Torso_LeapAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "Legs_DodgeLeft", Legs_DodgeLeft );
AI_DISPATCH_STATE( "Legs_DodgeRight", Legs_DodgeRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_range", combat_range );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_leap", combat_leap );
AI_DISPATCH_STATE( "path_jump", path_jump );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterDemonImp::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "stand" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonImp::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 6 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 6 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonImp::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonImp::Torso_RangeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, range_attack_anim.c_str() );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
LookAtEnemy( 1.0f );
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
AllowMovement( true );
FinishAction( "range_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonImp::Torso_TurretAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
AllowMovement( false );
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, "turret_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
FaceEnemy();
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
AllowMovement( true );
FinishAction( "turret_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonImp::Torso_TurretAttackIdle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { FaceEnemy(); IdleAnim( ANIMCHANNEL_TORSO, "turret_idle" ); parms->stage = 1; }
FaceEnemy();
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonImp::Torso_LeapAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_START_WAIT, STAGE_LOOP, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
OverrideAnim( ANIMCHANNEL_LEGS );
PreventPain( 1.0f );
PlayAnim( ANIMCHANNEL_TORSO, "jump_start" );
parms->stage = STAGE_START_WAIT;
return SRESULT_WAIT;
case STAGE_START_WAIT:
if ( !AnimDone( ANIMCHANNEL_TORSO, 2 ) ) { return SRESULT_WAIT; }
AttackBegin( "melee_impLeapAttack" );
SetLinearVelocity( jumpVelocity );
PlayCycle( ANIMCHANNEL_TORSO, "jump_loop" );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
if ( owner && !owner->AI_ONGROUND ) { return SRESULT_WAIT; }
AttackEnd();
PlayAnim( ANIMCHANNEL_TORSO, "jump_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "leap_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonImp::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "stand" ); parms->stage = 1; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonImp::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonImp::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "run" ); parms->stage = 1; }
if ( !run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonImp::Legs_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonImp::Legs_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonImp::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonImp::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextDodge = RandomTime( IMP_DODGE_RATE );
nextLeap = RandomTime( IMP_LEAP_RATE );
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterDemonImp::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
const float currentTime = Time();
if ( !canMelee && Pain() && currentTime >= nextDodge ) {
if ( TestAnimMove( "evade_left" ) ) { flags |= ATTACK_DODGE_LEFT; }
if ( TestAnimMove( "evade_right" ) ) {
flags |= ATTACK_DODGE_RIGHT;
if ( flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { flags &= ~ATTACK_DODGE_RIGHT; } else { flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
if ( !canMelee ) {
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; }
}
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
const float range = EnemyRange();
if ( range >= IMP_LEAP_RANGE_MIN && range < IMP_LEAP_RANGE_MAX && currentTime >= nextLeap && CanHitEnemy() ) {
const float t = AnimLength( ANIMCHANNEL_TORSO, "jump_start" );
const idVec3 jumpTarget = PredictEnemyPos( t );
jumpVelocity = GetJumpVelocity( jumpTarget, IMP_LEAP_SPEED, IMP_LEAP_MAXHEIGHT );
if ( jumpVelocity.LengthSqr() > 0.001f ) { flags |= ATTACK_LEAP; } else { nextLeap = DelayTime( 2.0f ); }
}
if ( !CanReachEnemy() || currentTime >= nextAttack ) {
range_attack_anim = ChooseAnim( ANIMCHANNEL_LEGS, "turret_attack" );
if ( CanHitEnemyFromAnim( range_attack_anim.c_str() ) ) { flags |= ATTACK_MISSILE; }
const char* movingAnim = ChooseAnim( ANIMCHANNEL_LEGS, "range_attack" );
if ( TestAnimMoveTowardEnemy( movingAnim ) && CanHitEnemyFromAnim( movingAnim ) ) {
range_attack_anim = movingAnim;
flags |= ATTACK_MISSILE;
}
}
}
return static_cast<float>( flags );
}
bool rvmMonsterDemonImp::do_attack( int attack_flags ) {
nextNoFOVAttack = Time() + IMP_NOFOVTIME;
const int flags = attack_flags;
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_LEAP ) { StartAction( "combat_leap" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "combat_range" ); return true; }
return false;
}
stateResult_t rvmMonsterDemonImp::combat_range( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
waitActionName = "range_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "range_attack" ) ) { return SRESULT_WAIT; }
nextAttack = DelayTime( IMP_ATTACK_RATE );
nextNoFOVAttack = Time() + IMP_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonImp::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonImp::combat_leap( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
TurnToPos( GetOrigin() + jumpVelocity );
waitActionName = "leap_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "leap_attack" ) ) { return SRESULT_WAIT; }
nextLeap = DelayTime( IMP_LEAP_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonImp::path_jump( stateParms_t* parms ) {
enum { STAGE_PATH_CORNER, STAGE_SETUP, STAGE_TURN, STAGE_LEAP, STAGE_DONE };
idEntity* path = current_path.GetEntity();
switch ( parms->stage ) {
case STAGE_PATH_CORNER:
StartAction( "path_corner" );
parms->stage = STAGE_SETUP;
return SRESULT_WAIT;
case STAGE_SETUP: {
if ( InAction() ) { return SRESULT_WAIT; }
if ( checkForEnemy( true ) ) { return SRESULT_DONE; }
idEntity* target = RandomPathFrom( path );
if ( !target ) { return SRESULT_DONE; }
scratchEntity = target;
const idVec3 jumpTarget = target->GetPhysics()->GetOrigin();
if ( path && path->spawnArgs.GetString( "up", "" )[0] ) {
float forward = path->spawnArgs.GetFloat( "forward", "0" );
idVec3 dir = jumpTarget - GetOrigin();
dir.z = 0.0f;
dir.Normalize();
jumpVelocity = dir * forward;
jumpVelocity.z = path->spawnArgs.GetFloat( "up", "0" );
} else {
jumpVelocity = GetJumpVelocity( jumpTarget, IMP_LEAP_SPEED, 1024.0f );
}
StopMove();
TurnToPos( jumpTarget );
parms->stage = STAGE_TURN;
return SRESULT_WAIT;
}
case STAGE_TURN:
if ( checkForEnemy( true ) ) { return SRESULT_DONE; }
if ( !FacingIdeal() ) { return SRESULT_WAIT; }
waitActionName = "leap_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
parms->stage = STAGE_LEAP;
return SRESULT_WAIT;
case STAGE_LEAP:
if ( !ActionDone( "leap_attack" ) ) { return SRESULT_WAIT; }
StopMove();
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonImp::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
StopMove();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeLeft", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( IMP_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonImp::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
StopMove();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeRight", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( IMP_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
+79
View File
@@ -0,0 +1,79 @@
// AI_monster_demon_imp.h
// Native state-machine replacement for ai_monster_demon_imp.script.
#pragma once
#ifndef IMP_RUNDISTANCE
#define IMP_RUNDISTANCE 192
#endif
#ifndef IMP_WALKTURN
#define IMP_WALKTURN 65
#endif
#ifndef IMP_ATTACK_RATE
#define IMP_ATTACK_RATE 1
#endif
#ifndef IMP_LEAP_RATE
#define IMP_LEAP_RATE 7
#endif
#ifndef IMP_LEAP_SPEED
#define IMP_LEAP_SPEED 650
#endif
#ifndef IMP_LEAP_MAXHEIGHT
#define IMP_LEAP_MAXHEIGHT 48
#endif
#ifndef IMP_DODGE_RATE
#define IMP_DODGE_RATE 8
#endif
#ifndef IMP_LEAP_RANGE_MIN
#define IMP_LEAP_RANGE_MIN 200
#endif
#ifndef IMP_LEAP_RANGE_MAX
#define IMP_LEAP_RANGE_MAX 480
#endif
#ifndef IMP_NOFOVTIME
#define IMP_NOFOVTIME 4
#endif
class rvmMonsterDemonImp : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterDemonImp );
rvmMonsterDemonImp();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextDodge;
float nextAttack;
float nextNoFOVAttack;
float nextLeap;
float painNextTime;
idVec3 jumpVelocity;
idStr range_attack_anim;
idEntityPtr<idEntity> combat_node;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Torso_TurretAttack( stateParms_t* parms );
virtual stateResult_t Torso_TurretAttackIdle( stateParms_t* parms );
virtual stateResult_t Torso_LeapAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t Legs_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Legs_DodgeRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_range( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_leap( stateParms_t* parms );
virtual stateResult_t path_jump( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,349 @@
// AI_monster_demon_maggot.cpp
// Native state-machine replacement for ai_monster_demon_maggot.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterDemonMaggot )
END_CLASS
rvmMonsterDemonMaggot::rvmMonsterDemonMaggot() {
explode = false;
nextDodge = 0.0f;
nextLeap = 0.0f;
painNextTime = 0.0f;
jumpVelocity = vec3_zero;
combat_node = NULL;
}
void rvmMonsterDemonMaggot::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
run_distance = MAGGOT_RUNDISTANCE;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterDemonMaggot::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_LeapAttack", Torso_LeapAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "Legs_DodgeLeft", Legs_DodgeLeft );
AI_DISPATCH_STATE( "Legs_DodgeRight", Legs_DodgeRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_leap", combat_leap );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
AI_DISPATCH_STATE( "path_jump", path_jump );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterDemonMaggot::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonMaggot::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMaggot::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMaggot::Torso_LeapAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_START_WAIT, STAGE_LOOP, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
OverrideAnim( ANIMCHANNEL_LEGS );
PreventPain( 1.0f );
PlayAnim( ANIMCHANNEL_TORSO, "jump_start" );
parms->stage = STAGE_START_WAIT;
return SRESULT_WAIT;
case STAGE_START_WAIT:
if ( !AnimDone( ANIMCHANNEL_TORSO, 2 ) ) { return SRESULT_WAIT; }
AttackBegin( "melee_maggotLeapAttack" );
SetLinearVelocity( jumpVelocity );
PlayCycle( ANIMCHANNEL_TORSO, "jump_loop" );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
if ( owner && !owner->AI_ONGROUND ) { return SRESULT_WAIT; }
AttackEnd();
PlayAnim( ANIMCHANNEL_TORSO, "jump_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "leap_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMaggot::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "idle" ); parms->stage = 1; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonMaggot::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonMaggot::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "run" ); parms->stage = 1; }
if ( !run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonMaggot::Legs_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonMaggot::Legs_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonMaggot::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMaggot::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextLeap = RandomTime( MAGGOT_LEAP_RATE );
nextDodge = RandomTime( MAGGOT_DODGE_RATE );
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterDemonMaggot::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
const float currentTime = Time();
if ( !canMelee && Pain() && currentTime >= nextDodge ) {
if ( TestAnimMove( "evade_left" ) ) { flags |= ATTACK_DODGE_LEFT; }
if ( TestAnimMove( "evade_right" ) ) {
flags |= ATTACK_DODGE_RIGHT;
if ( flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { flags &= ~ATTACK_DODGE_RIGHT; } else { flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
if ( !canMelee ) {
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; }
}
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( EnemyInFOV() ) {
const float range = EnemyRange();
if ( range >= MAGGOT_LEAP_RANGE_MIN && range < MAGGOT_LEAP_RANGE_MAX && currentTime >= nextLeap && CanHitEnemy() ) {
const float t = AnimLength( ANIMCHANNEL_TORSO, "jump_start" );
const idVec3 jumpTarget = PredictEnemyPos( t );
jumpVelocity = GetJumpVelocity( jumpTarget, MAGGOT_LEAP_SPEED, MAGGOT_LEAP_MAXHEIGHT );
if ( jumpVelocity.LengthSqr() > 0.001f ) { flags |= ATTACK_LEAP; } else { nextLeap = DelayTime( 2.0f ); }
}
}
return static_cast<float>( flags );
}
bool rvmMonsterDemonMaggot::do_attack( int attack_flags ) {
const int flags = attack_flags;
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_LEAP ) { StartAction( "combat_leap" ); return true; }
return false;
}
stateResult_t rvmMonsterDemonMaggot::combat_leap( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
TurnToPos( GetOrigin() + jumpVelocity );
waitActionName = "leap_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "leap_attack" ) ) { return SRESULT_WAIT; }
nextLeap = DelayTime( MAGGOT_LEAP_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMaggot::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMaggot::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeLeft", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
MoveToEnemy();
nextDodge = DelayTime( MAGGOT_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMaggot::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeRight", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
MoveToEnemy();
nextDodge = DelayTime( MAGGOT_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMaggot::path_jump( stateParms_t* parms ) {
enum { STAGE_PATH_CORNER, STAGE_SETUP, STAGE_TURN, STAGE_LEAP, STAGE_DONE };
idEntity* path = current_path.GetEntity();
switch ( parms->stage ) {
case STAGE_PATH_CORNER:
StartAction( "path_corner" );
parms->stage = STAGE_SETUP;
return SRESULT_WAIT;
case STAGE_SETUP: {
if ( InAction() ) { return SRESULT_WAIT; }
if ( checkForEnemy( true ) ) { return SRESULT_DONE; }
idEntity* target = RandomPathFrom( path );
if ( !target ) { return SRESULT_DONE; }
scratchEntity = target;
const idVec3 jumpTarget = target->GetPhysics()->GetOrigin();
if ( path && path->spawnArgs.GetString( "up", "" )[0] ) {
float forward = path->spawnArgs.GetFloat( "forward", "0" );
idVec3 dir = jumpTarget - GetOrigin();
dir.z = 0.0f;
dir.Normalize();
jumpVelocity = dir * forward;
jumpVelocity.z = path->spawnArgs.GetFloat( "up", "0" );
} else {
jumpVelocity = GetJumpVelocity( jumpTarget, MAGGOT_LEAP_SPEED, 1024.0f );
}
StopMove();
TurnToPos( jumpTarget );
parms->stage = STAGE_TURN;
return SRESULT_WAIT;
}
case STAGE_TURN:
if ( checkForEnemy( true ) ) { return SRESULT_DONE; }
if ( !FacingIdeal() ) { return SRESULT_WAIT; }
waitActionName = "leap_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
parms->stage = STAGE_LEAP;
return SRESULT_WAIT;
case STAGE_LEAP:
if ( !ActionDone( "leap_attack" ) ) { return SRESULT_WAIT; }
StopMove();
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,67 @@
// AI_monster_demon_maggot.h
// Native state-machine replacement for ai_monster_demon_maggot.script.
#pragma once
#ifndef MAGGOT_RUNDISTANCE
#define MAGGOT_RUNDISTANCE 192
#endif
#ifndef MAGGOT_ATTACK_RATE
#define MAGGOT_ATTACK_RATE 1
#endif
#ifndef MAGGOT_LEAP_RATE
#define MAGGOT_LEAP_RATE 7
#endif
#ifndef MAGGOT_LEAP_SPEED
#define MAGGOT_LEAP_SPEED 650
#endif
#ifndef MAGGOT_LEAP_MAXHEIGHT
#define MAGGOT_LEAP_MAXHEIGHT 48
#endif
#ifndef MAGGOT_DODGE_RATE
#define MAGGOT_DODGE_RATE 8
#endif
#ifndef MAGGOT_LEAP_RANGE_MIN
#define MAGGOT_LEAP_RANGE_MIN 200
#endif
#ifndef MAGGOT_LEAP_RANGE_MAX
#define MAGGOT_LEAP_RANGE_MAX 480
#endif
class rvmMonsterDemonMaggot : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterDemonMaggot );
rvmMonsterDemonMaggot();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
bool explode;
float nextDodge;
float nextLeap;
float painNextTime;
idVec3 jumpVelocity;
idEntityPtr<idEntity> combat_node;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_LeapAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t Legs_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Legs_DodgeRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_leap( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual stateResult_t path_jump( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,247 @@
// AI_monster_demon_mancubus.cpp
// Native state-machine replacement for ai_monster_demon_mancubus.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterDemonMancubus )
END_CLASS
rvmMonsterDemonMancubus::rvmMonsterDemonMancubus() {
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
fire = 0.0f;
painNextTime = 0.0f;
combat_node = NULL;
}
void rvmMonsterDemonMancubus::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
walk_turn = MANC_WALKTURN;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterDemonMancubus::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_RangeAttack", Torso_RangeAttack );
AI_DISPATCH_STATE( "Torso_TurretAttack", Torso_TurretAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_range", combat_range );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterDemonMancubus::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "stand" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 ); return SRESULT_DONE; }
if ( fire > 0.0f ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonMancubus::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 8 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMancubus::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMancubus::Torso_RangeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_START_WAIT, STAGE_LOOP_START, STAGE_LOOP_WAIT, STAGE_END, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_start" );
parms->stage = STAGE_START_WAIT;
return SRESULT_WAIT;
case STAGE_START_WAIT:
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 ); return SRESULT_DONE; }
if ( !AnimDone( ANIMCHANNEL_TORSO, 0 ) ) { return SRESULT_WAIT; }
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_LOOP_START:
if ( fire <= 0.0f ) { parms->stage = STAGE_END; return SRESULT_WAIT; }
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_loop" );
parms->stage = STAGE_LOOP_WAIT;
return SRESULT_WAIT;
case STAGE_LOOP_WAIT:
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 ); return SRESULT_DONE; }
if ( !AnimDone( ANIMCHANNEL_TORSO, 0 ) ) { return SRESULT_WAIT; }
fire -= 1.0f;
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_END:
fire = 0.0f;
if ( !AnimDone( ANIMCHANNEL_TORSO, 0 ) ) { SetBlendFrames( ANIMCHANNEL_TORSO, 4 ); }
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 0 ) ) { FinishAction( "range_attack" ); SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMancubus::Torso_TurretAttack( stateParms_t* parms ) {
// Uses the same staged burst sequence as Torso_RangeAttack, but completes the turret action token.
stateResult_t r = Torso_RangeAttack( parms );
if ( r == SRESULT_DONE ) { FinishAction( "turret_attack" ); SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); }
return r;
}
stateResult_t rvmMonsterDemonMancubus::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "stand" ); parms->stage = 1; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 2 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonMancubus::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 2 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonMancubus::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMancubus::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterDemonMancubus::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
if ( !canMelee ) { combat_node = GetCombatNode(); if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; } }
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
if ( !CanReachEnemy() || Time() >= nextAttack ) {
if ( CanHitEnemyFromAnim( "range_attack_loop" ) ) { flags |= ATTACK_MISSILE; }
}
}
return static_cast<float>( flags );
}
bool rvmMonsterDemonMancubus::do_attack( int attack_flags ) {
nextNoFOVAttack = Time() + MANC_NOFOVTIME;
const int flags = attack_flags;
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "combat_range" ); return true; }
return false;
}
stateResult_t rvmMonsterDemonMancubus::combat_range( stateParms_t* parms ) {
enum { STAGE_START, STAGE_DELAY, STAGE_FIRE, STAGE_FINISH };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
scratchEndTime = EnemyInFOV() ? Time() : DelayTime( 0.4f );
parms->stage = STAGE_DELAY;
return SRESULT_WAIT;
case STAGE_DELAY:
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
fire = MANC_MIN_SHOTS + idMath::Ftoi( RandomFloat( MANC_MAX_SHOTS - MANC_MIN_SHOTS ) );
waitActionName = "range_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
parms->stage = STAGE_FIRE;
return SRESULT_WAIT;
case STAGE_FIRE:
if ( fire > 0.0f ) {
if ( !CanHitEnemyFromAnim( "range_attack_loop" ) ) { fire = 0.0f; }
if ( Pain() && ( check_attacks() & ATTACK_MELEE ) ) { fire = 0.0f; }
return SRESULT_WAIT;
}
parms->stage = STAGE_FINISH;
return SRESULT_WAIT;
case STAGE_FINISH:
if ( InAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack" ) ) { return SRESULT_WAIT; }
nextAttack = DelayTime( MANC_ATTACK_RATE );
nextNoFOVAttack = Time() + MANC_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonMancubus::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,52 @@
// AI_monster_demon_mancubus.h
// Native state-machine replacement for ai_monster_demon_mancubus.script.
#pragma once
#ifndef MANC_WALKTURN
#define MANC_WALKTURN 65
#endif
#ifndef MANC_ATTACK_RATE
#define MANC_ATTACK_RATE 1
#endif
#ifndef MANC_MAX_SHOTS
#define MANC_MAX_SHOTS 5
#endif
#ifndef MANC_MIN_SHOTS
#define MANC_MIN_SHOTS 1
#endif
#ifndef MANC_NOFOVTIME
#define MANC_NOFOVTIME 8
#endif
class rvmMonsterDemonMancubus : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterDemonMancubus );
rvmMonsterDemonMancubus();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextAttack;
float nextNoFOVAttack;
float fire;
float painNextTime;
idEntityPtr<idEntity> combat_node;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Torso_TurretAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_range( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,274 @@
// AI_monster_demon_pinky.cpp
// Native state-machine replacement for ai_monster_demon_pinky.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterDemonPinky )
END_CLASS
rvmMonsterDemonPinky::rvmMonsterDemonPinky() {
nextDodge = 0.0f;
nextRoar = 0.0f;
painNextTime = 0.0f;
combat_node = NULL;
}
void rvmMonsterDemonPinky::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
SetState( "state_Begin" );
}
stateResult_t rvmMonsterDemonPinky::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_Roar", Torso_Roar );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "Legs_DodgeLeft", Legs_DodgeLeft );
AI_DISPATCH_STATE( "Legs_DodgeRight", Legs_DodgeRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_roar", combat_roar );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterDemonPinky::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "stand" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonPinky::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonPinky::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonPinky::Torso_Roar( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
PlayAnim( ANIMCHANNEL_TORSO, "roar" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "roar" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonPinky::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "idle" ); parms->stage = 1; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonPinky::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonPinky::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "run" ); parms->stage = 1; }
if ( !run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonPinky::Legs_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonPinky::Legs_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonPinky::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonPinky::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextRoar = RandomTime( PINKY_ROAR_RATE );
nextDodge = RandomTime( PINKY_DODGE_RATE );
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterDemonPinky::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
const float currentTime = Time();
if ( !canMelee && Pain() && currentTime >= nextDodge ) {
if ( TestAnimMove( "evade_left" ) ) { flags |= ATTACK_DODGE_LEFT; }
if ( TestAnimMove( "evade_right" ) ) {
flags |= ATTACK_DODGE_RIGHT;
if ( flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { flags &= ~ATTACK_DODGE_RIGHT; } else { flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
if ( !canMelee ) {
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; }
}
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( EnemyInFOV() && currentTime >= nextRoar ) { flags |= ATTACK_MISSILE; }
return static_cast<float>( flags );
}
bool rvmMonsterDemonPinky::do_attack( int attack_flags ) {
const int flags = attack_flags;
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "combat_roar" ); return true; }
return false;
}
stateResult_t rvmMonsterDemonPinky::combat_roar( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
StopMove();
waitActionName = "roar";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Roar", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "roar" ) ) { return SRESULT_WAIT; }
nextRoar = DelayTime( PINKY_ROAR_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonPinky::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonPinky::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeLeft", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( PINKY_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonPinky::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeRight", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( PINKY_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,46 @@
// AI_monster_demon_pinky.h
// Native state-machine replacement for ai_monster_demon_pinky.script.
#pragma once
#ifndef PINKY_ROAR_RATE
#define PINKY_ROAR_RATE 5
#endif
#ifndef PINKY_DODGE_RATE
#define PINKY_DODGE_RATE 5
#endif
class rvmMonsterDemonPinky : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterDemonPinky );
rvmMonsterDemonPinky();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextDodge;
float nextRoar;
float painNextTime;
idEntityPtr<idEntity> combat_node;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_Roar( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t Legs_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Legs_DodgeRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_roar( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,310 @@
// AI_monster_demon_revenant.cpp
// Native state-machine replacement for ai_monster_demon_revenant.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterDemonRevenant )
END_CLASS
rvmMonsterDemonRevenant::rvmMonsterDemonRevenant() {
nextDodge = 0.0f;
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextLeap = 0.0f;
painNextTime = 0.0f;
jumpVelocity = vec3_zero;
combat_node = NULL;
}
void rvmMonsterDemonRevenant::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
walk_turn = REV_WALKTURN;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterDemonRevenant::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_RangeAttack", Torso_RangeAttack );
AI_DISPATCH_STATE( "Torso_TurretAttack", Torso_TurretAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_DodgeLeft", Legs_DodgeLeft );
AI_DISPATCH_STATE( "Legs_DodgeRight", Legs_DodgeRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_range", combat_range );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterDemonRevenant::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonRevenant::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 7 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 7 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 7 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonRevenant::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonRevenant::Torso_RangeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, range_attack_anim.c_str() );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
LookAtEnemy( 1.0f );
if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) {
AllowMovement( true );
FinishAction( "range_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonRevenant::Torso_TurretAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
AllowMovement( false );
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, "turret_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
FaceEnemy();
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
AllowMovement( true );
FinishAction( "turret_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonRevenant::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "idle" ); parms->stage = 1; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonRevenant::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonRevenant::Legs_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonRevenant::Legs_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonRevenant::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonRevenant::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextDodge = RandomTime( REV_DODGE_RATE );
nextLeap = 0.0f;
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterDemonRevenant::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
const float currentTime = Time();
if ( !canMelee && Pain() && currentTime >= nextDodge ) {
if ( TestAnimMove( "evade_left" ) ) { flags |= ATTACK_DODGE_LEFT; }
if ( TestAnimMove( "evade_right" ) ) {
flags |= ATTACK_DODGE_RIGHT;
if ( flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { flags &= ~ATTACK_DODGE_RIGHT; } else { flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
if ( !canMelee ) {
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; }
}
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
if ( !CanReachEnemy() || currentTime >= nextAttack ) {
range_attack_anim = ChooseAnim( ANIMCHANNEL_LEGS, "turret_attack" );
if ( CanHitEnemyFromAnim( range_attack_anim.c_str() ) ) { flags |= ATTACK_MISSILE; }
const char* movingAnim = ChooseAnim( ANIMCHANNEL_LEGS, "range_attack" );
if ( TestAnimMoveTowardEnemy( movingAnim ) && CanHitEnemyFromAnim( movingAnim ) ) {
range_attack_anim = movingAnim;
flags |= ATTACK_MISSILE;
}
}
}
return static_cast<float>( flags );
}
bool rvmMonsterDemonRevenant::do_attack( int attack_flags ) {
nextNoFOVAttack = Time() + REV_NOFOVTIME;
const int flags = attack_flags;
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "combat_range" ); return true; }
return false;
}
stateResult_t rvmMonsterDemonRevenant::combat_range( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
waitActionName = "range_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "range_attack" ) ) { return SRESULT_WAIT; }
nextAttack = DelayTime( REV_ATTACK_RATE );
nextNoFOVAttack = Time() + REV_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonRevenant::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonRevenant::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
StopMove();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeLeft", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( REV_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonRevenant::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
StopMove();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeRight", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( REV_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,56 @@
// AI_monster_demon_revenant.h
// Native state-machine replacement for ai_monster_demon_revenant.script.
#pragma once
#ifndef REV_WALKTURN
#define REV_WALKTURN 65
#endif
#ifndef REV_ATTACK_RATE
#define REV_ATTACK_RATE 1.2f
#endif
#ifndef REV_DODGE_RATE
#define REV_DODGE_RATE 8
#endif
#ifndef REV_NOFOVTIME
#define REV_NOFOVTIME 4
#endif
class rvmMonsterDemonRevenant : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterDemonRevenant );
rvmMonsterDemonRevenant();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextDodge;
float nextAttack;
float nextNoFOVAttack;
float nextLeap;
float painNextTime;
idVec3 jumpVelocity;
idStr range_attack_anim;
idEntityPtr<idEntity> combat_node;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Torso_TurretAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Legs_DodgeRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_range( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,441 @@
// AI_monster_demon_trite.cpp
// Native state-machine replacement for ai_monster_demon_trite.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterDemonTrite )
END_CLASS
rvmMonsterDemonTrite::rvmMonsterDemonTrite() {
explode = false;
nextDodge = 0.0f;
nextLeap = 0.0f;
painNextTime = 0.0f;
jumpVelocity = vec3_zero;
combat_node = NULL;
}
void rvmMonsterDemonTrite::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
run_distance = TRITE_RUNDISTANCE;
walk_turn = TRITE_WALKTURN;
explode = GetIntKey( "no_explode" ) == 0;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterDemonTrite::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_LeapAttack", Torso_LeapAttack );
AI_DISPATCH_STATE( "Torso_Walk", Torso_Walk );
AI_DISPATCH_STATE( "Torso_Run", Torso_Run );
AI_DISPATCH_STATE( "Torso_DodgeLeft", Torso_DodgeLeft );
AI_DISPATCH_STATE( "Torso_DodgeRight", Torso_DodgeRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_leap", combat_leap );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
AI_DISPATCH_STATE( "path_jump", path_jump );
AI_DISPATCH_STATE( "state_LostCombat", state_LostCombat );
AI_DISPATCH_STATE( "state_Explode", state_Explode );
AI_DISPATCH_STATE( "combat_explode", combat_explode );
AI_DISPATCH_STATE( "state_Killed", state_Killed );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterDemonTrite::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Run", 4 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Walk", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonTrite::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonTrite::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonTrite::Torso_LeapAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_START_WAIT, STAGE_LOOP, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
OverrideAnim( ANIMCHANNEL_LEGS );
PreventPain( 1.0f );
PlayAnim( ANIMCHANNEL_TORSO, "jump_start" );
parms->stage = STAGE_START_WAIT;
return SRESULT_WAIT;
case STAGE_START_WAIT:
if ( !AnimDone( ANIMCHANNEL_TORSO, 0 ) ) { return SRESULT_WAIT; }
FinishAction( "leap_attack_start" );
AttackBegin( "melee_triteLeapAttack" );
SetLinearVelocity( jumpVelocity );
PlayCycle( ANIMCHANNEL_TORSO, "jump_loop" );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
if ( owner && !owner->AI_ONGROUND ) { return SRESULT_WAIT; }
AttackEnd();
PlayAnim( ANIMCHANNEL_TORSO, "jump_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "leap_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonTrite::Torso_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "walk" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Run", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonTrite::Torso_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "run" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( !run && MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Walk", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonTrite::Torso_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_TORSO, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonTrite::Torso_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_TORSO, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonTrite::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonTrite::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextLeap = RandomTime( TRITE_LEAP_RATE );
nextDodge = RandomTime( TRITE_DODGE_RATE );
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterDemonTrite::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
const float currentTime = Time();
if ( !canMelee && Pain() && currentTime >= nextDodge ) {
if ( TestAnimMove( "evade_left" ) ) { flags |= ATTACK_DODGE_LEFT; }
if ( TestAnimMove( "evade_right" ) ) {
flags |= ATTACK_DODGE_RIGHT;
if ( flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { flags &= ~ATTACK_DODGE_RIGHT; } else { flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
if ( !canMelee ) {
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; }
}
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( EnemyInFOV() ) {
const float range = EnemyRange();
if ( range >= TRITE_LEAP_RANGE_MIN && range < TRITE_LEAP_RANGE_MAX && currentTime >= nextLeap && CanHitEnemy() ) {
const float t = AnimLength( ANIMCHANNEL_TORSO, "jump_start" );
const idVec3 jumpTarget = PredictEnemyPos( t );
jumpVelocity = GetJumpVelocity( jumpTarget, TRITE_LEAP_SPEED, TRITE_LEAP_MAXHEIGHT );
if ( jumpVelocity.LengthSqr() > 0.001f ) { flags |= ATTACK_LEAP; } else { nextLeap = DelayTime( 2.0f ); }
}
}
return static_cast<float>( flags );
}
bool rvmMonsterDemonTrite::do_attack( int attack_flags ) {
const int flags = attack_flags;
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_LEAP ) { StartAction( "combat_leap" ); return true; }
return false;
}
stateResult_t rvmMonsterDemonTrite::combat_leap( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
TurnToPos( GetOrigin() + jumpVelocity );
waitActionName = "leap_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "leap_attack" ) ) { return SRESULT_WAIT; }
nextLeap = DelayTime( TRITE_LEAP_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonTrite::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonTrite::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_DodgeLeft", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( TRITE_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonTrite::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_DodgeRight", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( TRITE_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonTrite::path_jump( stateParms_t* parms ) {
enum { STAGE_PATH_CORNER, STAGE_SETUP, STAGE_TURN, STAGE_LEAP, STAGE_DONE };
idEntity* path = current_path.GetEntity();
switch ( parms->stage ) {
case STAGE_PATH_CORNER:
StartAction( "path_corner" );
parms->stage = STAGE_SETUP;
return SRESULT_WAIT;
case STAGE_SETUP: {
if ( InAction() ) { return SRESULT_WAIT; }
if ( checkForEnemy( true ) ) { return SRESULT_DONE; }
idEntity* target = RandomPathFrom( path );
if ( !target ) { return SRESULT_DONE; }
scratchEntity = target;
const idVec3 jumpTarget = target->GetPhysics()->GetOrigin();
if ( path && path->spawnArgs.GetString( "up", "" )[0] ) {
float forward = path->spawnArgs.GetFloat( "forward", "0" );
idVec3 dir = jumpTarget - GetOrigin();
dir.z = 0.0f;
dir.Normalize();
jumpVelocity = dir * forward;
jumpVelocity.z = path->spawnArgs.GetFloat( "up", "0" );
} else {
jumpVelocity = GetJumpVelocity( jumpTarget, TRITE_LEAP_SPEED, 1024.0f );
}
StopMove();
TurnToPos( jumpTarget );
parms->stage = STAGE_TURN;
return SRESULT_WAIT;
}
case STAGE_TURN:
if ( checkForEnemy( true ) ) { return SRESULT_DONE; }
if ( !FacingIdeal() ) { return SRESULT_WAIT; }
waitActionName = "leap_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
parms->stage = STAGE_LEAP;
return SRESULT_WAIT;
case STAGE_LEAP:
if ( !ActionDone( "leap_attack" ) ) { return SRESULT_WAIT; }
StopMove();
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonTrite::combat_explode( stateParms_t* parms ) {
enum { STAGE_START, STAGE_LAUNCH, STAGE_MONITOR };
switch ( parms->stage ) {
case STAGE_START: {
const float t = AnimLength( ANIMCHANNEL_TORSO, "jump_start" );
const idVec3 enemyOrg = PredictEnemyPos( t );
const idVec3 org = GetOrigin();
float maxHeight = ( enemyOrg.z > org.z + 32.0f ) ? enemyOrg.z : ( org.z + 32.0f );
idVec3 vel = enemyOrg - org;
vel.Normalize();
vel *= 320.0f;
vel.z = idMath::Sqrt( ( 2.0f * ( maxHeight - org.z ) * 1066.0f ) > 0.0f ? ( 2.0f * ( maxHeight - org.z ) * 1066.0f ) : 0.0f );
jumpVelocity = vel;
scratchEndTime = maxHeight;
StopMove();
FaceEnemy();
waitActionName = "leap_attack_start";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
parms->stage = STAGE_LAUNCH;
return SRESULT_WAIT;
}
case STAGE_LAUNCH:
if ( !ActionDone( "leap_attack_start" ) ) { return SRESULT_WAIT; }
parms->stage = STAGE_MONITOR;
return SRESULT_WAIT;
case STAGE_MONITOR: {
const idVec3 org = GetOrigin();
const idVec3 enemyOrg = GetEnemyPos();
const idVec3 vel = GetLinearVelocity();
if ( org.z > enemyOrg.z || org.z > scratchEndTime || vel.z <= -10.0f || !InAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack" ) ) {
SetState( "state_Explode" );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonTrite::state_LostCombat( stateParms_t* parms ) {
enum { STAGE_START, STAGE_CHASE, STAGE_WANDER };
switch ( parms->stage ) {
case STAGE_START:
if ( !explode ) { monster_base_lost_combat(); return SRESULT_DONE; }
MoveToEnemy();
scratchEndTime = DelayTime( 1.5f );
parms->stage = STAGE_CHASE;
return SRESULT_WAIT;
case STAGE_CHASE: {
if ( CanReachEnemy() ) { SetState( "state_Combat" ); return SRESULT_DONE; }
if ( EnemyRange() < TRITE_EXPLOSION_RADIUS ) { StartAction( "combat_explode" ); return SRESULT_WAIT; }
const int flags = check_attacks();
if ( flags & ATTACK_LEAP ) { StartAction( "combat_explode" ); return SRESULT_WAIT; }
if ( flags ) { SetState( "state_Combat" ); return SRESULT_DONE; }
if ( MoveDone() ) { Wander(); parms->stage = STAGE_WANDER; }
return SRESULT_WAIT;
}
case STAGE_WANDER:
if ( CanReachEnemy() || Time() >= scratchEndTime ) { SetState( "state_Combat" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
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" ) );
StartSoundKey( "snd_explode", SND_CHANNEL_BODY, false );
if ( owner ) { owner->ActivateTargets( owner ); }
HideOwner();
scratchEndTime = DelayTime( 5.0f );
parms->stage = STAGE_REMOVE;
return SRESULT_WAIT;
case STAGE_REMOVE:
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
RemoveOwner();
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonTrite::state_Killed( stateParms_t* parms ) {
StopMove();
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 0 );
waitActionName = "dead";
SetState( "state_Dead" );
return SRESULT_DONE;
}
@@ -0,0 +1,73 @@
// AI_monster_demon_trite.h
// Native state-machine replacement for ai_monster_demon_trite.script.
#pragma once
#ifndef TRITE_RUNDISTANCE
#define TRITE_RUNDISTANCE 192
#endif
#ifndef TRITE_WALKTURN
#define TRITE_WALKTURN 65
#endif
#ifndef TRITE_LEAP_RATE
#define TRITE_LEAP_RATE 2
#endif
#ifndef TRITE_DODGE_RATE
#define TRITE_DODGE_RATE 2
#endif
#ifndef TRITE_LEAP_RANGE_MIN
#define TRITE_LEAP_RANGE_MIN 64
#endif
#ifndef TRITE_LEAP_RANGE_MAX
#define TRITE_LEAP_RANGE_MAX 1024
#endif
#ifndef TRITE_LEAP_SPEED
#define TRITE_LEAP_SPEED 650
#endif
#ifndef TRITE_LEAP_MAXHEIGHT
#define TRITE_LEAP_MAXHEIGHT 48
#endif
#ifndef TRITE_EXPLOSION_RADIUS
#define TRITE_EXPLOSION_RADIUS 200
#endif
class rvmMonsterDemonTrite : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterDemonTrite );
rvmMonsterDemonTrite();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
bool explode;
float nextDodge;
float nextLeap;
float painNextTime;
idVec3 jumpVelocity;
idEntityPtr<idEntity> combat_node;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_LeapAttack( stateParms_t* parms );
virtual stateResult_t Torso_Walk( stateParms_t* parms );
virtual stateResult_t Torso_Run( stateParms_t* parms );
virtual stateResult_t Torso_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Torso_DodgeRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_leap( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual stateResult_t path_jump( stateParms_t* parms );
virtual stateResult_t state_LostCombat( stateParms_t* parms );
virtual stateResult_t state_Explode( stateParms_t* parms );
virtual stateResult_t combat_explode( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,428 @@
// AI_monster_demon_vulgar.cpp
// Native state-machine replacement for ai_monster_demon_vulgar.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterDemonVulgar )
END_CLASS
rvmMonsterDemonVulgar::rvmMonsterDemonVulgar() {
nextDodge = 0.0f;
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextLeap = 0.0f;
painNextTime = 0.0f;
jumpVelocity = vec3_zero;
combat_node = NULL;
}
void rvmMonsterDemonVulgar::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
run_distance = VULGAR_RUNDISTANCE;
walk_turn = VULGAR_WALKTURN;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterDemonVulgar::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_RangeAttack", Torso_RangeAttack );
AI_DISPATCH_STATE( "Torso_TurretAttack", Torso_TurretAttack );
AI_DISPATCH_STATE( "Torso_LeapAttack", Torso_LeapAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "Legs_DodgeLeft", Legs_DodgeLeft );
AI_DISPATCH_STATE( "Legs_DodgeRight", Legs_DodgeRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_range", combat_range );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_leap", combat_leap );
AI_DISPATCH_STATE( "path_jump", path_jump );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterDemonVulgar::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonVulgar::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", VULGAR_PAIN_TO_PAIN ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, VULGAR_PAIN_TO_IDLE ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", VULGAR_PAIN_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonVulgar::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, VULGAR_MELEE_TO_IDLE ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", VULGAR_MELEE_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonVulgar::Torso_RangeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, range_attack_anim.c_str() );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
LookAtEnemy( 1.0f );
if ( AnimDone( ANIMCHANNEL_TORSO, VULGAR_RANGE_TO_IDLE ) ) {
AllowMovement( true );
FinishAction( "range_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", VULGAR_RANGE_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonVulgar::Torso_TurretAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
AllowMovement( false );
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, "turret_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
FaceEnemy();
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
AllowMovement( true );
FinishAction( "turret_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonVulgar::Torso_LeapAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_START_WAIT, STAGE_LOOP, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
OverrideAnim( ANIMCHANNEL_LEGS );
PreventPain( 1.0f );
PlayAnim( ANIMCHANNEL_TORSO, "jump_start" );
parms->stage = STAGE_START_WAIT;
return SRESULT_WAIT;
case STAGE_START_WAIT:
if ( !AnimDone( ANIMCHANNEL_TORSO, 2 ) ) { return SRESULT_WAIT; }
AttackBegin( "melee_vulgarLeapAttack" );
SetLinearVelocity( jumpVelocity );
PlayCycle( ANIMCHANNEL_TORSO, "jump_loop" );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
if ( owner && !owner->AI_ONGROUND ) { return SRESULT_WAIT; }
AttackEnd();
PlayAnim( ANIMCHANNEL_TORSO, "jump_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "leap_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonVulgar::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "idle" ); parms->stage = 1; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonVulgar::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonVulgar::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "run" ); parms->stage = 1; }
if ( !run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonVulgar::Legs_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonVulgar::Legs_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonVulgar::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonVulgar::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextDodge = RandomTime( VULGAR_DODGE_RATE );
nextLeap = RandomTime( VULGAR_LEAP_RATE );
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterDemonVulgar::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
const float currentTime = Time();
if ( !canMelee && Pain() && currentTime >= nextDodge ) {
if ( TestAnimMove( "evade_left" ) ) { flags |= ATTACK_DODGE_LEFT; }
if ( TestAnimMove( "evade_right" ) ) {
flags |= ATTACK_DODGE_RIGHT;
if ( flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { flags &= ~ATTACK_DODGE_RIGHT; } else { flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
if ( !canMelee ) {
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; }
}
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
const float range = EnemyRange();
if ( range >= VULGAR_LEAP_RANGE_MIN && range < VULGAR_LEAP_RANGE_MAX && currentTime >= nextLeap && CanHitEnemy() ) {
const float t = AnimLength( ANIMCHANNEL_TORSO, "jump_start" );
const idVec3 jumpTarget = PredictEnemyPos( t );
jumpVelocity = GetJumpVelocity( jumpTarget, VULGAR_LEAP_SPEED, VULGAR_LEAP_MAXHEIGHT );
if ( jumpVelocity.LengthSqr() > 0.001f ) { flags |= ATTACK_LEAP; } else { nextLeap = DelayTime( 2.0f ); }
}
if ( !CanReachEnemy() || currentTime >= nextAttack ) {
range_attack_anim = ChooseAnim( ANIMCHANNEL_LEGS, "turret_attack" );
if ( CanHitEnemyFromAnim( range_attack_anim.c_str() ) ) { flags |= ATTACK_MISSILE; }
const char* movingAnim = ChooseAnim( ANIMCHANNEL_LEGS, "range_attack" );
if ( TestAnimMoveTowardEnemy( movingAnim ) && CanHitEnemyFromAnim( movingAnim ) ) {
range_attack_anim = movingAnim;
flags |= ATTACK_MISSILE;
}
}
}
return static_cast<float>( flags );
}
bool rvmMonsterDemonVulgar::do_attack( int attack_flags ) {
nextNoFOVAttack = Time() + VULGAR_NOFOVTIME;
const int flags = attack_flags;
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_LEAP ) { StartAction( "combat_leap" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "combat_range" ); return true; }
return false;
}
stateResult_t rvmMonsterDemonVulgar::combat_range( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
waitActionName = "range_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "range_attack" ) ) { return SRESULT_WAIT; }
nextAttack = DelayTime( VULGAR_ATTACK_RATE );
nextNoFOVAttack = Time() + VULGAR_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonVulgar::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonVulgar::combat_leap( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
TurnToPos( GetOrigin() + jumpVelocity );
waitActionName = "leap_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "leap_attack" ) ) { return SRESULT_WAIT; }
nextLeap = DelayTime( VULGAR_LEAP_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonVulgar::path_jump( stateParms_t* parms ) {
enum { STAGE_PATH_CORNER, STAGE_SETUP, STAGE_TURN, STAGE_LEAP, STAGE_DONE };
idEntity* path = current_path.GetEntity();
switch ( parms->stage ) {
case STAGE_PATH_CORNER:
StartAction( "path_corner" );
parms->stage = STAGE_SETUP;
return SRESULT_WAIT;
case STAGE_SETUP: {
if ( InAction() ) { return SRESULT_WAIT; }
if ( checkForEnemy( true ) ) { return SRESULT_DONE; }
idEntity* target = RandomPathFrom( path );
if ( !target ) { return SRESULT_DONE; }
scratchEntity = target;
const idVec3 jumpTarget = target->GetPhysics()->GetOrigin();
if ( path && path->spawnArgs.GetString( "up", "" )[0] ) {
float forward = path->spawnArgs.GetFloat( "forward", "0" );
idVec3 dir = jumpTarget - GetOrigin();
dir.z = 0.0f;
dir.Normalize();
jumpVelocity = dir * forward;
jumpVelocity.z = path->spawnArgs.GetFloat( "up", "0" );
} else {
jumpVelocity = GetJumpVelocity( jumpTarget, VULGAR_LEAP_SPEED, 1024.0f );
}
StopMove();
TurnToPos( jumpTarget );
parms->stage = STAGE_TURN;
return SRESULT_WAIT;
}
case STAGE_TURN:
if ( checkForEnemy( true ) ) { return SRESULT_DONE; }
if ( !FacingIdeal() ) { return SRESULT_WAIT; }
waitActionName = "leap_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
parms->stage = STAGE_LEAP;
return SRESULT_WAIT;
case STAGE_LEAP:
if ( !ActionDone( "leap_attack" ) ) { return SRESULT_WAIT; }
StopMove();
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonVulgar::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
StopMove();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeLeft", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( VULGAR_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonVulgar::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
StopMove();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeRight", 2 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( VULGAR_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,96 @@
// AI_monster_demon_vulgar.h
// Native state-machine replacement for ai_monster_demon_vulgar.script.
#pragma once
#ifndef VULGAR_RUNDISTANCE
#define VULGAR_RUNDISTANCE 192
#endif
#ifndef VULGAR_WALKTURN
#define VULGAR_WALKTURN 65
#endif
#ifndef VULGAR_ATTACK_RATE
#define VULGAR_ATTACK_RATE 1
#endif
#ifndef VULGAR_DODGE_RATE
#define VULGAR_DODGE_RATE 1
#endif
#ifndef VULGAR_LEAP_RATE
#define VULGAR_LEAP_RATE 8
#endif
#ifndef VULGAR_LEAP_SPEED
#define VULGAR_LEAP_SPEED 650
#endif
#ifndef VULGAR_LEAP_MAXHEIGHT
#define VULGAR_LEAP_MAXHEIGHT 48
#endif
#ifndef VULGAR_LEAP_RANGE_MIN
#define VULGAR_LEAP_RANGE_MIN 200
#endif
#ifndef VULGAR_LEAP_RANGE_MAX
#define VULGAR_LEAP_RANGE_MAX 480
#endif
#ifndef VULGAR_PAIN_DELAY
#define VULGAR_PAIN_DELAY 0.25f
#endif
#ifndef VULGAR_PAIN_TO_IDLE
#define VULGAR_PAIN_TO_IDLE 8
#endif
#ifndef VULGAR_PAIN_TO_PAIN
#define VULGAR_PAIN_TO_PAIN 4
#endif
#ifndef VULGAR_MELEE_TO_IDLE
#define VULGAR_MELEE_TO_IDLE 4
#endif
#ifndef VULGAR_RANGE_TO_IDLE
#define VULGAR_RANGE_TO_IDLE 4
#endif
#ifndef VULGAR_LEAP_TO_IDLE
#define VULGAR_LEAP_TO_IDLE 4
#endif
#ifndef VULGAR_NOFOVTIME
#define VULGAR_NOFOVTIME 4
#endif
class rvmMonsterDemonVulgar : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterDemonVulgar );
rvmMonsterDemonVulgar();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextDodge;
float nextAttack;
float nextNoFOVAttack;
float nextLeap;
float painNextTime;
idVec3 jumpVelocity;
idStr range_attack_anim;
idEntityPtr<idEntity> combat_node;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Torso_TurretAttack( stateParms_t* parms );
virtual stateResult_t Torso_LeapAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t Legs_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Legs_DodgeRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_range( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_leap( stateParms_t* parms );
virtual stateResult_t path_jump( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,332 @@
// AI_monster_demon_wraith.cpp
// Native state-machine replacement for ai_monster_demon_wraith.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterDemonWraith )
END_CLASS
rvmMonsterDemonWraith::rvmMonsterDemonWraith() {
invisible = false;
nextDodge = 0.0f;
nextInvisible = 0.0f;
painNextTime = 0.0f;
combat_node = NULL;
spawn_effect = NULL;
}
void rvmMonsterDemonWraith::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
run_distance = WRAITH_RUNDISTANCE;
walk_turn = WRAITH_WALKTURN;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterDemonWraith::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "Legs_DodgeLeft", Legs_DodgeLeft );
AI_DISPATCH_STATE( "Legs_DodgeRight", Legs_DodgeRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "state_LostCombat", state_LostCombat );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
AI_DISPATCH_STATE( "combat_become_visible", combat_become_visible );
AI_DISPATCH_STATE( "combat_become_invisible", combat_become_invisible );
AI_DISPATCH_STATE( "spawn_fx", spawn_fx );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterDemonWraith::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", WRAITH_IDLE_TO_PAIN ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonWraith::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( WRAITH_PAIN_DELAY );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", WRAITH_PAIN_TO_PAIN ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, WRAITH_PAIN_TO_IDLE ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", WRAITH_PAIN_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonWraith::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, WRAITH_MELEE_TO_IDLE ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", WRAITH_MELEE_TO_IDLE );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonWraith::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "idle" ); parms->stage = 1; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", WRAITH_IDLE_TO_RUN ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", WRAITH_IDLE_TO_WALK ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonWraith::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", WRAITH_IDLE_TO_RUN ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", WRAITH_RUN_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonWraith::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "run" ); parms->stage = 1; }
if ( !run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", WRAITH_IDLE_TO_WALK ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", WRAITH_RUN_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonWraith::Legs_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, WRAITH_DODGE_LEFT_TO_IDLE ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", WRAITH_DODGE_LEFT_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonWraith::Legs_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, WRAITH_DODGE_RIGHT_TO_IDLE ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", WRAITH_DODGE_RIGHT_TO_IDLE ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterDemonWraith::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonWraith::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextDodge = RandomTime( WRAITH_DODGE_RATE );
nextInvisible = 0.0f;
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterDemonWraith::check_attacks() {
int flags = 0;
const float range = EnemyRange();
const float currentTime = Time();
if ( invisible ) {
if ( ( range < WRAITH_INVISIBLE_DIST_MIN || currentTime >= nextInvisible ) && CanBecomeSolid() ) { flags |= ATTACK_BECOME_VISIBLE; }
return static_cast<float>( flags );
}
const bool canMelee = CheckForMelee();
if ( !canMelee && Pain() && currentTime >= nextDodge ) {
if ( TestAnimMove( "evade_left" ) ) { flags |= ATTACK_DODGE_LEFT; }
if ( TestAnimMove( "evade_right" ) ) {
flags |= ATTACK_DODGE_RIGHT;
if ( flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { flags &= ~ATTACK_DODGE_RIGHT; } else { flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
if ( !canMelee ) {
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; }
}
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( owner && owner->AI_ONGROUND && range > WRAITH_INVISIBLE_DIST_MAX && currentTime >= nextInvisible ) { flags |= ATTACK_BECOME_INVISIBLE; }
return static_cast<float>( flags );
}
bool rvmMonsterDemonWraith::do_attack( int attack_flags ) {
const int flags = attack_flags;
if ( flags & ATTACK_BECOME_VISIBLE ) { StartAction( "combat_become_visible" ); return true; }
if ( flags & ATTACK_BECOME_INVISIBLE ) { StartAction( "combat_become_invisible" ); return true; }
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
return false;
}
stateResult_t rvmMonsterDemonWraith::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", WRAITH_WALK_TO_MELEE );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonWraith::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeLeft", WRAITH_WALK_TO_DODGE_LEFT );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( WRAITH_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonWraith::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeRight", WRAITH_WALK_TO_DODGE_RIGHT );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( WRAITH_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
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.
return SRESULT_DONE;
}
stateResult_t rvmMonsterDemonWraith::combat_become_visible( stateParms_t* parms ) {
enum { STAGE_START, STAGE_SOLID, STAGE_FADE, STAGE_DONE };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
if ( !invisible ) { nextInvisible = RandomDelay( WRAITH_INVISIBLE_DELAY_MIN, WRAITH_INVISIBLE_DELAY_MAX ); return SRESULT_DONE; }
AllowMovement( false );
parms->stage = STAGE_SOLID;
return SRESULT_WAIT;
case STAGE_SOLID:
if ( !CanBecomeSolid() ) { return SRESULT_WAIT; }
BecomeSolid();
if ( owner ) { owner->AllowHiddenMovement( 0 ); }
AllowMovement( true );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartSoundKey( "snd_fade_in", SND_CHANNEL_VOICE2, false );
ShowOwner();
scratchEndTime = DelayTime( 0.75f );
parms->stage = STAGE_FADE;
return SRESULT_WAIT;
case STAGE_FADE:
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
ClearBurn();
invisible = false;
nextInvisible = RandomDelay( WRAITH_INVISIBLE_DELAY_MIN, WRAITH_INVISIBLE_DELAY_MAX );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonWraith::combat_become_invisible( stateParms_t* parms ) {
enum { STAGE_START, STAGE_FADEOUT, STAGE_HIDE };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
if ( invisible ) { return SRESULT_DONE; }
invisible = true;
StartSoundKey( "snd_fade_out", SND_CHANNEL_VOICE2, false );
scratchEndTime = DelayTime( 0.817f );
parms->stage = STAGE_FADEOUT;
return SRESULT_WAIT;
case STAGE_FADEOUT:
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
PreBurn();
HideOwner();
if ( owner ) { owner->AllowHiddenMovement( 1 ); }
nextInvisible = RandomDelay( WRAITH_VISIBLE_DELAY_MIN, WRAITH_VISIBLE_DELAY_MAX );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterDemonWraith::state_LostCombat( stateParms_t* parms ) {
enum { STAGE_INVISIBLE, STAGE_ROUTE, STAGE_VISIBLE };
switch ( parms->stage ) {
case STAGE_INVISIBLE:
StartAction( "combat_become_invisible" );
parms->stage = STAGE_ROUTE;
return SRESULT_WAIT;
case STAGE_ROUTE:
if ( InAction() || Time() < nextInvisible ) { return SRESULT_WAIT; }
StopMove();
if ( !CanReachEnemy() ) { return SRESULT_WAIT; }
scratchEndTime = DelayTime( 2.0f );
parms->stage = STAGE_VISIBLE;
return SRESULT_WAIT;
case STAGE_VISIBLE:
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
StartAction( "combat_become_visible" );
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
+128
View File
@@ -0,0 +1,128 @@
// AI_monster_demon_wraith.h
// Native state-machine replacement for ai_monster_demon_wraith.script.
#pragma once
#ifndef WRAITH_ATTACK_RATE
#define WRAITH_ATTACK_RATE 1.5f
#endif
#ifndef WRAITH_DODGE_RATE
#define WRAITH_DODGE_RATE 5
#endif
#ifndef WRAITH_PAIN_DELAY
#define WRAITH_PAIN_DELAY 0.25f
#endif
#ifndef WRAITH_RUNDISTANCE
#define WRAITH_RUNDISTANCE 192
#endif
#ifndef WRAITH_WALKTURN
#define WRAITH_WALKTURN 65
#endif
#ifndef WRAITH_INVISIBLE_DIST_MIN
#define WRAITH_INVISIBLE_DIST_MIN 140
#endif
#ifndef WRAITH_INVISIBLE_DIST_MAX
#define WRAITH_INVISIBLE_DIST_MAX 256
#endif
#ifndef WRAITH_INVISIBLE_DELAY_MIN
#define WRAITH_INVISIBLE_DELAY_MIN 0.6f
#endif
#ifndef WRAITH_INVISIBLE_DELAY_MAX
#define WRAITH_INVISIBLE_DELAY_MAX 1.5f
#endif
#ifndef WRAITH_VISIBLE_DELAY_MIN
#define WRAITH_VISIBLE_DELAY_MIN 1.3f
#endif
#ifndef WRAITH_VISIBLE_DELAY_MAX
#define WRAITH_VISIBLE_DELAY_MAX 2.2f
#endif
#ifndef WRAITH_PAIN_TO_IDLE
#define WRAITH_PAIN_TO_IDLE 2
#endif
#ifndef WRAITH_PAIN_TO_PAIN
#define WRAITH_PAIN_TO_PAIN 0
#endif
#ifndef WRAITH_MELEE_TO_IDLE
#define WRAITH_MELEE_TO_IDLE 4
#endif
#ifndef WRAITH_DODGE_LEFT_TO_IDLE
#define WRAITH_DODGE_LEFT_TO_IDLE 4
#endif
#ifndef WRAITH_DODGE_RIGHT_TO_IDLE
#define WRAITH_DODGE_RIGHT_TO_IDLE 4
#endif
#ifndef WRAITH_IDLE_TO_PAIN
#define WRAITH_IDLE_TO_PAIN 0
#endif
#ifndef WRAITH_IDLE_TO_WALK
#define WRAITH_IDLE_TO_WALK 4
#endif
#ifndef WRAITH_WALK_TO_IDLE
#define WRAITH_WALK_TO_IDLE 4
#endif
#ifndef WRAITH_IDLE_TO_RUN
#define WRAITH_IDLE_TO_RUN 4
#endif
#ifndef WRAITH_WALK_TO_RUN
#define WRAITH_WALK_TO_RUN 4
#endif
#ifndef WRAITH_RUN_TO_WALK
#define WRAITH_RUN_TO_WALK 4
#endif
#ifndef WRAITH_RUN_TO_IDLE
#define WRAITH_RUN_TO_IDLE 4
#endif
#ifndef WRAITH_WALK_TO_MELEE
#define WRAITH_WALK_TO_MELEE 4
#endif
#ifndef WRAITH_WALK_TO_DODGE_LEFT
#define WRAITH_WALK_TO_DODGE_LEFT 4
#endif
#ifndef WRAITH_WALK_TO_DODGE_RIGHT
#define WRAITH_WALK_TO_DODGE_RIGHT 4
#endif
#ifndef ATTACK_BECOME_INVISIBLE
#define ATTACK_BECOME_INVISIBLE ATTACK_SPECIAL1
#endif
#ifndef ATTACK_BECOME_VISIBLE
#define ATTACK_BECOME_VISIBLE ATTACK_SPECIAL2
#endif
class rvmMonsterDemonWraith : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterDemonWraith );
rvmMonsterDemonWraith();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
bool invisible;
float nextDodge;
float nextInvisible;
float painNextTime;
idEntityPtr<idEntity> combat_node;
idEntityPtr<idEntity> spawn_effect;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms );
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t Legs_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Legs_DodgeRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t state_LostCombat( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual stateResult_t combat_become_visible( stateParms_t* parms );
virtual stateResult_t combat_become_invisible( stateParms_t* parms );
virtual stateResult_t spawn_fx( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,221 @@
// AI_monster_flying_cacodemon.cpp
// Native state-machine replacement for ai_monster_flying_cacodemon.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterFlyingCacodemon )
END_CLASS
rvmMonsterFlyingCacodemon::rvmMonsterFlyingCacodemon() {
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
painNextTime = 0.0f;
combat_node = NULL;
}
void rvmMonsterFlyingCacodemon::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
SetState( "state_Begin" );
}
stateResult_t rvmMonsterFlyingCacodemon::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Fly", Torso_Fly );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_RangeAttack", Torso_RangeAttack );
AI_DISPATCH_STATE( "Torso_TurretAttack", Torso_TurretAttack );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_range", combat_range );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterFlyingCacodemon::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Fly", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterFlyingCacodemon::Torso_Fly( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "fly" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterFlyingCacodemon::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingCacodemon::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingCacodemon::Torso_RangeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
FaceEnemy();
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "range_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingCacodemon::Torso_TurretAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PreventPain( 1.0f );
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
FaceEnemy();
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "turret_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingCacodemon::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_FLY );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingCacodemon::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterFlyingCacodemon::check_attacks() {
int flags = 0;
const bool canMelee = CheckForMelee();
const float currentTime = Time();
if ( !canMelee ) { combat_node = GetCombatNode(); if ( combat_node.GetEntity() ) { flags |= ATTACK_COMBAT_NODE; } }
if ( canMelee ) { flags |= ATTACK_MELEE; }
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
if ( !CanReachEnemy() || currentTime >= nextAttack ) {
if ( CanHitEnemyFromAnim( "range_attack" ) ) { flags |= ATTACK_MISSILE; }
}
}
return static_cast<float>( flags );
}
bool rvmMonsterFlyingCacodemon::do_attack( int attack_flags ) {
nextNoFOVAttack = Time() + CACO_NOFOVTIME;
const int flags = attack_flags;
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "combat_range" ); return true; }
return false;
}
stateResult_t rvmMonsterFlyingCacodemon::combat_range( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
waitActionName = "range_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "range_attack" ) ) { return SRESULT_WAIT; }
nextAttack = DelayTime( CACO_ATTACK_RATE );
nextNoFOVAttack = Time() + CACO_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingCacodemon::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,41 @@
// AI_monster_flying_cacodemon.h
// Native state-machine replacement for ai_monster_flying_cacodemon.script.
#pragma once
#ifndef CACO_ATTACK_RATE
#define CACO_ATTACK_RATE 3
#endif
#ifndef CACO_NOFOVTIME
#define CACO_NOFOVTIME 4
#endif
class rvmMonsterFlyingCacodemon : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterFlyingCacodemon );
rvmMonsterFlyingCacodemon();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextAttack;
float nextNoFOVAttack;
float painNextTime;
idEntityPtr<idEntity> combat_node;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Fly( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Torso_TurretAttack( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_range( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,242 @@
// AI_monster_flying_lostsoul.cpp
// Native state-machine replacement for ai_monster_flying_lostsoul.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterFlyingLostsoul )
END_CLASS
rvmMonsterFlyingLostsoul::rvmMonsterFlyingLostsoul() {
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
noMeleeTime = 0.0f;
fly_offset = 0.0f;
painNextTime = 0.0f;
chargeVelocity = vec3_zero;
}
void rvmMonsterFlyingLostsoul::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
fly_offset = GetFloatKey( "fly_offset", "0" );
SetState( "state_Begin" );
}
stateResult_t rvmMonsterFlyingLostsoul::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Fly", Torso_Fly );
AI_DISPATCH_STATE( "Torso_Charge", Torso_Charge );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_charge", combat_charge );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
AI_DISPATCH_STATE( "combat_retreat", combat_retreat );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterFlyingLostsoul::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Fly", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterFlyingLostsoul::Torso_Fly( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "fly" ); parms->stage = 1; }
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterFlyingLostsoul::Torso_Charge( stateParms_t* parms ) {
enum { STAGE_START, STAGE_CHARGE };
switch ( parms->stage ) {
case STAGE_START:
PlayCycle( ANIMCHANNEL_TORSO, "charge" );
AttackBegin( "melee_lostsoul_charge" );
scratchEndTime = DelayTime( ( EnemyRange() + 256.0f ) / LOSTSOUL_CHARGE_SPEED );
parms->stage = STAGE_CHARGE;
return SRESULT_WAIT;
case STAGE_CHARGE:
if ( InfluenceActive() || Pain() || Time() >= scratchEndTime ) {
AttackEnd();
FinishAction( "charge_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
if ( CheckForMelee() ) { AttackMelee( "melee_lostsoul_charge" ); }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingLostsoul::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( Pain() && Time() > painNextTime ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingLostsoul::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_RUNNING };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_RUNNING;
return SRESULT_WAIT;
case STAGE_RUNNING:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingLostsoul::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_FLY );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingLostsoul::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterFlyingLostsoul::check_attacks() {
int flags = 0;
const float currentTime = Time();
if ( currentTime >= noMeleeTime && CheckForMelee() ) { flags |= ATTACK_MELEE; }
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
const float range = EnemyRange();
if ( range < 40.0f ) { flags |= ATTACK_LOSTSOUL_RETREAT; }
if ( range >= LOSTSOUL_CHARGE_RANGE_MIN && range < LOSTSOUL_CHARGE_RANGE_MAX && ( !CanReachEnemy() || currentTime >= nextAttack ) && owner && owner->TestChargeAttack() > 0.0f ) {
flags |= ATTACK_LOSTSOUL_CHARGE;
}
}
return static_cast<float>( flags );
}
bool rvmMonsterFlyingLostsoul::do_attack( int attack_flags ) {
nextNoFOVAttack = Time() + LOSTSOUL_NOFOVTIME;
const int flags = attack_flags;
if ( flags & ATTACK_LOSTSOUL_RETREAT ) { StartAction( "combat_retreat" ); return true; }
if ( flags & ATTACK_MELEE ) { StartAction( "combat_melee" ); return true; }
if ( flags & ATTACK_LOSTSOUL_CHARGE ) { StartAction( "combat_charge" ); return true; }
return false;
}
stateResult_t rvmMonsterFlyingLostsoul::combat_charge( stateParms_t* parms ) {
enum { STAGE_RISE, STAGE_FACE, STAGE_CHARGE, STAGE_FINISH };
switch ( parms->stage ) {
case STAGE_RISE:
SetFlyOffset( idMath::Ftoi( fly_offset + 200.0f ) );
scratchEndTime = DelayTime( 0.5f );
parms->stage = STAGE_FACE;
return SRESULT_WAIT;
case STAGE_FACE: {
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
FaceEnemy();
idVec3 forward = GetEnemyPos() - GetOrigin();
forward.z = 0.0f;
forward.Normalize();
chargeVelocity = forward * LOSTSOUL_CHARGE_SPEED;
SetFlyOffset( idMath::Ftoi( fly_offset ) );
SetFlySpeed( LOSTSOUL_CHARGE_SPEED );
waitActionName = "charge_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Charge", 4 );
parms->stage = STAGE_CHARGE;
return SRESULT_WAIT;
}
case STAGE_CHARGE: {
if ( !ActionDone( "charge_attack" ) ) {
idVec3 vel = chargeVelocity;
const idVec3 org = GetOrigin();
const idVec3 pos = GetEnemyPos();
vel.z = -( org.z - pos.z + 16.0f ) * 3.0f;
SetLinearVelocity( vel );
return SRESULT_WAIT;
}
SetFlySpeed( LOSTSOUL_NORMAL_SPEED );
nextAttack = DelayTime( LOSTSOUL_ATTACK_RATE );
nextNoFOVAttack = Time() + LOSTSOUL_NOFOVTIME;
noMeleeTime = Time() + 1.0f;
return SRESULT_DONE;
}
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingLostsoul::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_ACTION };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 4 );
parms->stage = STAGE_ACTION;
return SRESULT_WAIT;
case STAGE_ACTION:
if ( !ActionDone( "melee_attack" ) ) { return SRESULT_WAIT; }
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterFlyingLostsoul::combat_retreat( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MOVE };
switch ( parms->stage ) {
case STAGE_START:
MoveOutOfRange( GetEnemy(), EnemyRange() + 256.0f );
scratchEndTime = DelayTime( 1.0f );
parms->stage = STAGE_MOVE;
return SRESULT_WAIT;
case STAGE_MOVE:
if ( MoveDone() || Time() >= scratchEndTime ) { return SRESULT_DONE; }
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,61 @@
// AI_monster_flying_lostsoul.h
// Native state-machine replacement for ai_monster_flying_lostsoul.script.
#pragma once
#ifndef LOSTSOUL_CHARGE_SPEED
#define LOSTSOUL_CHARGE_SPEED 440
#endif
#ifndef LOSTSOUL_NORMAL_SPEED
#define LOSTSOUL_NORMAL_SPEED 140
#endif
#ifndef LOSTSOUL_ATTACK_RATE
#define LOSTSOUL_ATTACK_RATE 2
#endif
#ifndef LOSTSOUL_CHARGE_RANGE_MAX
#define LOSTSOUL_CHARGE_RANGE_MAX 768
#endif
#ifndef LOSTSOUL_CHARGE_RANGE_MIN
#define LOSTSOUL_CHARGE_RANGE_MIN 64
#endif
#ifndef LOSTSOUL_NOFOVTIME
#define LOSTSOUL_NOFOVTIME 4
#endif
#ifndef ATTACK_LOSTSOUL_CHARGE
#define ATTACK_LOSTSOUL_CHARGE ATTACK_SPECIAL1
#endif
#ifndef ATTACK_LOSTSOUL_RETREAT
#define ATTACK_LOSTSOUL_RETREAT ATTACK_SPECIAL2
#endif
class rvmMonsterFlyingLostsoul : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterFlyingLostsoul );
rvmMonsterFlyingLostsoul();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextAttack;
float nextNoFOVAttack;
float noMeleeTime;
float fly_offset;
float painNextTime;
idVec3 chargeVelocity;
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Fly( stateParms_t* parms );
virtual stateResult_t Torso_Charge( stateParms_t* parms );
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_charge( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual stateResult_t combat_retreat( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
+172
View File
@@ -0,0 +1,172 @@
// AI_monster_turret.cpp
// Native state-machine replacement for ai_monster_turret.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterTurret )
END_CLASS
rvmMonsterTurret::rvmMonsterTurret() {
fire = false;
attack_monsters = false;
light_is_on = false;
attackTime = 0.0f;
nextFireTime = 0.0f;
shutdownTime = 0.0f;
light = NULL;
}
void rvmMonsterTurret::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
IgnoreDamage();
ambush = true;
attack_monsters = GetFloatKey( "team", "1" ) != 1.0f;
if ( GetIntKey( "light" ) ) { StartAction( "spawn_light" ); }
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetState( "state_Begin" );
}
stateResult_t rvmMonsterTurret::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Death", Torso_Death );
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Attack", Torso_Attack );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "state_Disabled", state_Disabled );
AI_DISPATCH_STATE( "state_Killed", state_Killed );
AI_DISPATCH_STATE( "state_Combat", state_Combat );
AI_DISPATCH_STATE( "combat_attack", combat_attack );
AI_DISPATCH_STATE( "spawn_light", spawn_light );
AI_DISPATCH_STATE( "light_off", light_off );
AI_DISPATCH_STATE( "light_on", light_on );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterTurret::Torso_Death( stateParms_t* parms ) {
FinishAction( "dead" );
return SRESULT_WAIT;
}
stateResult_t rvmMonsterTurret::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_TORSO, "idle" ); parms->stage = 1; }
if ( fire ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Attack", 0 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterTurret::Torso_Attack( stateParms_t* parms ) {
enum { STAGE_WINDUP, STAGE_FIRE, STAGE_WINDDOWN };
switch ( parms->stage ) {
case STAGE_WINDUP:
PlayCycle( ANIMCHANNEL_TORSO, "idle" );
StartSoundKey( "snd_windup", SND_CHANNEL_BODY, false );
attackTime = DelayTime( TURRET_MIN_ATTACK_TIME );
nextFireTime = Time();
parms->stage = STAGE_FIRE;
return SRESULT_WAIT;
case STAGE_FIRE:
if ( ( !fire && Time() >= attackTime ) || InfluenceActive() ) {
StartSoundKey( "snd_winddown", SND_CHANNEL_BODY2, false );
parms->stage = STAGE_WINDDOWN;
scratchEndTime = DelayTime( 0.35f );
return SRESULT_WAIT;
}
if ( Time() >= nextFireTime ) {
StartSoundKey( "snd_fire", SND_CHANNEL_WEAPON, false );
AttackMissile( "barrel" );
nextFireTime = DelayTime( TURRET_FIRE_RATE );
}
return SRESULT_WAIT;
case STAGE_WINDDOWN:
if ( Time() < scratchEndTime ) { return SRESULT_WAIT; }
FinishAction( "attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterTurret::spawn_light( stateParms_t* parms ) { return SRESULT_DONE; }
stateResult_t rvmMonsterTurret::light_off( stateParms_t* parms ) { light_is_on = false; return SRESULT_DONE; }
stateResult_t rvmMonsterTurret::light_on( stateParms_t* parms ) { light_is_on = true; return SRESULT_DONE; }
stateResult_t rvmMonsterTurret::state_Begin( stateParms_t* parms ) {
SetMoveType( MOVETYPE_STATIC );
if ( GetIntKey( "trigger" ) ) { SetState( "state_Disabled" ); return SRESULT_DONE; }
StartAction( "wait_for_enemy" );
parms->stage = 1;
return SRESULT_WAIT;
}
stateResult_t rvmMonsterTurret::state_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { StartSoundKey( "snd_shutdown", SND_CHANNEL_VOICE, false ); StartAction( "light_off" ); StartAction( "wait_for_enemy" ); parms->stage = 1; return SRESULT_WAIT; }
if ( InAction() ) { return SRESULT_WAIT; }
SetState( "state_Combat" );
return SRESULT_DONE;
}
stateResult_t rvmMonsterTurret::state_Disabled( stateParms_t* parms ) {
enum { STAGE_START, STAGE_TRIGGER };
switch ( parms->stage ) {
case STAGE_START:
StartSoundKey( "snd_shutdown", SND_CHANNEL_VOICE, false );
LookAtEnemy( 0.0f );
attackTime = 0.0f;
fire = false;
ClearEnemy();
ClearActivated();
parms->stage = STAGE_TRIGGER;
return SRESULT_WAIT;
case STAGE_TRIGGER:
if ( !Activated() ) { return SRESULT_WAIT; }
ClearActivated();
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterTurret::state_Killed( stateParms_t* parms ) {
StopMove();
StartAction( "light_off" );
waitActionName = "dead";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
StopThinking();
return SRESULT_WAIT;
}
bool rvmMonsterTurret::checkForEnemy( bool use_fov ) {
idEntity* enemy = FindEnemy( use_fov );
if ( !enemy && attack_monsters ) { enemy = FindEnemyAI( use_fov ); }
if ( !enemy ) { return false; }
SetEnemy( enemy );
return true;
}
stateResult_t rvmMonsterTurret::state_Combat( stateParms_t* parms ) {
if ( parms->stage == 0 ) { StartSoundKey( "snd_wakeup", SND_CHANNEL_VOICE, false ); StartAction( "light_on" ); shutdownTime = DelayTime( TURRET_SHUTDOWN_TIME ); parms->stage = 1; }
LookAtEnemy( 1.0f );
if ( EnemyDead() ) { enemy_dead(); return SRESULT_DONE; }
if ( Activated() ) { SetState( "state_Disabled" ); return SRESULT_DONE; }
if ( EnemyVisible() ) {
if ( owner && owner->CanHitEnemyFromJoint( "barrel" ) ) { StartAction( "combat_attack" ); } else { checkForEnemy( false ); }
shutdownTime = DelayTime( TURRET_SHUTDOWN_TIME );
return SRESULT_WAIT;
}
if ( !checkForEnemy( false ) && Time() >= shutdownTime ) { LookAtEnemy( 0.0f ); ClearEnemy(); SetState( "state_Idle" ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterTurret::combat_attack( stateParms_t* parms ) {
FaceEnemy();
fire = true;
if ( !EnemyVisible() || !( owner && owner->CanHitEnemyFromJoint( "barrel" ) ) || InfluenceActive() || Activated() ) {
fire = false;
if ( Activated() ) { SetState( "state_Disabled" ); }
return SRESULT_DONE;
}
LookAtEnemy( 1.0f );
return SRESULT_WAIT;
}
+48
View File
@@ -0,0 +1,48 @@
// AI_monster_turret.h
// Native state-machine replacement for ai_monster_turret.script.
#pragma once
#ifndef TURRET_FIRE_RATE
#define TURRET_FIRE_RATE 0.1f
#endif
#ifndef TURRET_MIN_ATTACK_TIME
#define TURRET_MIN_ATTACK_TIME 1.5f
#endif
#ifndef TURRET_SHUTDOWN_TIME
#define TURRET_SHUTDOWN_TIME 4
#endif
class rvmMonsterTurret : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterTurret );
rvmMonsterTurret();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
bool fire;
bool attack_monsters;
bool light_is_on;
float attackTime;
float nextFireTime;
float shutdownTime;
idEntityPtr<idEntity> light;
virtual stateResult_t Torso_Death( stateParms_t* parms );
virtual stateResult_t Torso_Idle( stateParms_t* parms );
virtual stateResult_t Torso_Attack( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms );
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t state_Disabled( stateParms_t* parms );
virtual stateResult_t state_Killed( stateParms_t* parms );
virtual stateResult_t state_Combat( stateParms_t* parms );
virtual stateResult_t combat_attack( stateParms_t* parms );
virtual stateResult_t spawn_light( stateParms_t* parms );
virtual stateResult_t light_off( stateParms_t* parms );
virtual stateResult_t light_on( stateParms_t* parms );
virtual bool checkForEnemy( bool use_fov );
};
+193
View File
@@ -0,0 +1,193 @@
// AI_monster_zombie.cpp
// Native state-machine replacement for ai_monster_zombie.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterZombieBase, rvmMonsterZombie )
END_CLASS
rvmMonsterZombie::rvmMonsterZombie() {
painNextTime = 0.0f;
can_run = false;
}
void rvmMonsterZombie::Init( idAI* aiOwner ) {
rvmMonsterZombieBase::Init( aiOwner );
run_distance = 192.0f;
walk_turn = 65.0f;
can_run = HasAnim( ANIMCHANNEL_LEGS, "run" );
SetState( "state_Begin" );
}
stateResult_t rvmMonsterZombie::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
return rvmMonsterZombieBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterZombie::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = 1;
}
if ( Pain() ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombie::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( Pain() && Time() > painNextTime ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 8 );
return SRESULT_DONE;
}
if ( AnimDone( ANIMCHANNEL_TORSO, 3 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombie::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 8 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombie::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_LEGS, "idle" );
parms->stage = 1;
}
if ( can_run && run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 8 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombie::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
PlayCycle( ANIMCHANNEL_LEGS, "walk" );
parms->stage = 1;
}
if ( can_run && run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 8 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombie::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
PlayCycle( ANIMCHANNEL_LEGS, "run" );
parms->stage = 1;
}
if ( !run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombie::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) {
return SRESULT_WAIT;
}
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombie::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) {
return SRESULT_WAIT;
}
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterZombie::check_attacks() {
int attack_flags = 0;
if ( CheckForMelee() ) {
attack_flags |= ATTACK_MELEE;
}
return attack_flags;
}
bool rvmMonsterZombie::do_attack( int attack_flags ) {
const int flags = attack_flags;
if ( flags & ATTACK_MELEE ) {
StartAction( "combat_melee" );
return true;
}
return false;
}
stateResult_t rvmMonsterZombie::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 8 );
parms->stage = STAGE_WAIT_ACTION;
return SRESULT_WAIT;
case STAGE_WAIT_ACTION:
if ( !ActionDone( "melee_attack" ) ) {
return SRESULT_WAIT;
}
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
+31
View File
@@ -0,0 +1,31 @@
// AI_monster_zombie.h
// Native state-machine replacement for ai_monster_zombie.script.
#pragma once
class rvmMonsterZombie : public rvmMonsterZombieBase {
public:
CLASS_PROTOTYPE( rvmMonsterZombie );
rvmMonsterZombie();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float painNextTime;
bool can_run;
virtual stateResult_t Torso_Idle( stateParms_t* parms ) override;
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms ) override;
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms ) override;
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,45 @@
// AI_monster_zombie_base.cpp
// Native state-machine replacement for ai_monster_zombie_base.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterZombieBase )
END_CLASS
stateResult_t rvmMonsterZombieBase::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "state_Killed", state_Killed );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
void rvmMonsterZombieBase::archvile_minion() {
if ( owner ) {
gameLocal.Error( "'%s': Zombies cannot be minions for archviles", owner->GetName() );
}
}
bool rvmMonsterZombieBase::can_resurrect() {
return false;
}
stateResult_t rvmMonsterZombieBase::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;
}
StopThinking();
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,17 @@
// AI_monster_zombie_base.h
// Native state-machine replacement for ai_monster_zombie_base.script.
#pragma once
class rvmMonsterZombieBase : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterZombieBase );
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
virtual void archvile_minion() override;
virtual bool can_resurrect() override;
virtual stateResult_t state_Killed( stateParms_t* parms ) override;
};
@@ -0,0 +1,176 @@
// AI_monster_zombie_bernie.cpp
// Native state-machine replacement for ai_monster_zombie_bernie.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterZombieBase, rvmMonsterZombieBernie )
END_CLASS
rvmMonsterZombieBernie::rvmMonsterZombieBernie() {
painNextTime = 0.0f;
}
void rvmMonsterZombieBernie::Init( idAI* aiOwner ) {
rvmMonsterZombieBase::Init( aiOwner );
SetState( "state_Begin" );
}
stateResult_t rvmMonsterZombieBernie::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
return rvmMonsterZombieBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterZombieBernie::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = 1;
}
if ( Pain() ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieBernie::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( Pain() && Time() > painNextTime ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 );
return SRESULT_DONE;
}
if ( AnimDone( ANIMCHANNEL_TORSO, 3 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 6 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieBernie::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 8 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieBernie::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_LEGS, "idle" );
parms->stage = 1;
}
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieBernie::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
PlayCycle( ANIMCHANNEL_LEGS, "walk" );
parms->stage = 1;
}
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieBernie::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) {
return SRESULT_WAIT;
}
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieBernie::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) {
return SRESULT_WAIT;
}
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterZombieBernie::check_attacks() {
int attack_flags = 0;
if ( CheckForMelee() ) {
attack_flags |= ATTACK_MELEE;
}
return attack_flags;
}
bool rvmMonsterZombieBernie::do_attack( int attack_flags ) {
const int flags = attack_flags;
if ( flags & ATTACK_MELEE ) {
StartAction( "combat_melee" );
return true;
}
return false;
}
stateResult_t rvmMonsterZombieBernie::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 8 );
parms->stage = STAGE_WAIT_ACTION;
return SRESULT_WAIT;
case STAGE_WAIT_ACTION:
if ( !ActionDone( "melee_attack" ) ) {
return SRESULT_WAIT;
}
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,29 @@
// AI_monster_zombie_bernie.h
// Native state-machine replacement for ai_monster_zombie_bernie.script.
#pragma once
class rvmMonsterZombieBernie : public rvmMonsterZombieBase {
public:
CLASS_PROTOTYPE( rvmMonsterZombieBernie );
rvmMonsterZombieBernie();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float painNextTime;
virtual stateResult_t Torso_Idle( stateParms_t* parms ) override;
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms ) override;
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms ) override;
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,479 @@
// AI_monster_zombie_commando_cgun.cpp
// Native state-machine replacement for ai_monster_zombie_commando_cgun.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterZombieCommandoCGun )
END_CLASS
rvmMonsterZombieCommandoCGun::rvmMonsterZombieCommandoCGun() {
fire = false;
crouch_fire = false;
step_left = false;
step_right = false;
run_attack = false;
nextDodge = 0.0f;
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
combat_node = NULL;
painNextTime = 0.0f;
attackEndTime = 0.0f;
loopEndTime = 0.0f;
minShootTime = 0.0f;
firstShot = false;
}
void rvmMonsterZombieCommandoCGun::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
run_distance = ZOMBIE_CGUN_RUNDISTANCE;
walk_turn = ZOMBIE_CGUN_WALKTURN;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterZombieCommandoCGun::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_RangeAttack", Torso_RangeAttack );
AI_DISPATCH_STATE( "Torso_TurretAttack", Torso_TurretAttack );
AI_DISPATCH_STATE( "Torso_CrouchAttack", Torso_CrouchAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "Legs_DodgeLeft", Legs_DodgeLeft );
AI_DISPATCH_STATE( "Legs_DodgeRight", Legs_DodgeRight );
AI_DISPATCH_STATE( "Legs_StepLeft", Legs_StepLeft );
AI_DISPATCH_STATE( "Legs_StepRight", Legs_StepRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "stand_attack", stand_attack );
AI_DISPATCH_STATE( "crouch_attack", crouch_attack );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterZombieCommandoCGun::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_TORSO, "range_attack_aim" );
parms->stage = 1;
}
if ( Pain() ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 );
return SRESULT_DONE;
}
if ( fire ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 8 );
return SRESULT_DONE;
}
if ( crouch_fire ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_CrouchAttack", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoCGun::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
SetBlendFrames( ANIMCHANNEL_TORSO, 3 );
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 12 ) ) {
PreventPain( 0.6f );
FinishAction( "pain" );
SetBlendFrames( ANIMCHANNEL_TORSO, 12 );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 12 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoCGun::Torso_RangeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_INTRO, STAGE_LOOP_START, STAGE_LOOP_WAIT, STAGE_END, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
SetAnimPrefix( "" );
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_INTRO;
return SRESULT_WAIT;
case STAGE_INTRO:
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 ); return SRESULT_DONE; }
if ( !AnimDone( ANIMCHANNEL_TORSO, 12 ) ) { return SRESULT_WAIT; }
minShootTime = Time() + ZOMBIE_CGUN_MIN_SHOT_TIME;
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_LOOP_START:
if ( !fire && Time() >= minShootTime ) { parms->stage = STAGE_END; return SRESULT_WAIT; }
if ( InfluenceActive() ) { fire = false; parms->stage = STAGE_END; return SRESULT_WAIT; }
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_loop" );
parms->stage = STAGE_LOOP_WAIT;
return SRESULT_WAIT;
case STAGE_LOOP_WAIT:
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 ); return SRESULT_DONE; }
if ( InfluenceActive() ) { fire = false; parms->stage = STAGE_END; return SRESULT_WAIT; }
if ( !AnimDone( ANIMCHANNEL_TORSO, 12 ) ) { return SRESULT_WAIT; }
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_END:
if ( !AnimDone( ANIMCHANNEL_TORSO, 8 ) ) { SetBlendFrames( ANIMCHANNEL_TORSO, 8 ); }
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 12 ) ) {
FinishAction( "range_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 12 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoCGun::Torso_TurretAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_INTRO, STAGE_LOOP_START, STAGE_LOOP_WAIT, STAGE_END, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
SetAnimPrefix( "" );
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_INTRO;
return SRESULT_WAIT;
case STAGE_INTRO:
if ( !AnimDone( ANIMCHANNEL_TORSO, 8 ) ) { return SRESULT_WAIT; }
attackEndTime = RandomDelay( ZOMBIE_CGUN_STAND_ATTACK_MIN_LENGTH, ZOMBIE_CGUN_STAND_ATTACK_MAX_LENGTH );
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_LOOP_START:
if ( Time() >= attackEndTime || !CanHitEnemyFromAnim( "range_attack_loop" ) || InfluenceActive() ) { parms->stage = STAGE_END; return SRESULT_WAIT; }
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_loop" );
parms->stage = STAGE_LOOP_WAIT;
return SRESULT_WAIT;
case STAGE_LOOP_WAIT:
if ( !AnimDone( ANIMCHANNEL_TORSO, 8 ) ) { return SRESULT_WAIT; }
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_END:
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 8 ) ) {
FinishAction( "turret_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoCGun::Torso_CrouchAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_INTRO, STAGE_LOOP_START, STAGE_LOOP_WAIT, STAGE_AIM, STAGE_END, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
OverrideAnim( ANIMCHANNEL_LEGS );
SetAnimPrefix( "crouch" );
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_INTRO;
return SRESULT_WAIT;
case STAGE_INTRO:
if ( !AnimDone( ANIMCHANNEL_TORSO, 8 ) ) { return SRESULT_WAIT; }
minShootTime = Time() + ZOMBIE_CGUN_MIN_SHOT_TIME;
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_LOOP_START:
if ( !crouch_fire && Time() >= minShootTime ) { parms->stage = STAGE_END; return SRESULT_WAIT; }
loopEndTime = RandomDelay( ZOMBIE_CGUN_STAND_ATTACK_MIN_LENGTH, ZOMBIE_CGUN_STAND_ATTACK_MAX_LENGTH );
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_loop" );
parms->stage = STAGE_LOOP_WAIT;
return SRESULT_WAIT;
case STAGE_LOOP_WAIT:
if ( InfluenceActive() ) { crouch_fire = false; parms->stage = STAGE_END; return SRESULT_WAIT; }
if ( !AnimDone( ANIMCHANNEL_TORSO, 8 ) ) { return SRESULT_WAIT; }
if ( !crouch_fire || Time() >= loopEndTime ) {
PlayCycle( ANIMCHANNEL_TORSO, "range_attack_aim" );
loopEndTime = RandomDelay( ZOMBIE_CGUN_WAIT_MIN_LENGTH, ZOMBIE_CGUN_WAIT_MAX_LENGTH );
parms->stage = STAGE_AIM;
return SRESULT_WAIT;
}
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_AIM:
if ( !crouch_fire || InfluenceActive() ) { crouch_fire = false; parms->stage = STAGE_END; return SRESULT_WAIT; }
if ( Time() < loopEndTime ) { return SRESULT_WAIT; }
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_END:
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 8 ) ) {
FinishAction( "crouch_attack" );
SetAnimPrefix( "" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoCGun::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "range_attack_aim" ); parms->stage = 1; }
if ( GetEnemy() ) { run = true; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 8 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 ); return SRESULT_DONE; }
if ( step_left ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_StepLeft", 8 ); return SRESULT_DONE; }
if ( step_right ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_StepRight", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoCGun::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( GetEnemy() ) { run = true; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 8 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoCGun::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "run" ); parms->stage = 1; }
if ( GetEnemy() ) { run = true; }
if ( !run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoCGun::Legs_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 8 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoCGun::Legs_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 8 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoCGun::Legs_StepLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "step_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 8 ) ) { step_left = false; FinishAction( "sidestep" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoCGun::Legs_StepRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "step_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 8 ) ) { step_right = false; FinishAction( "sidestep" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoCGun::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
fire = false; crouch_fire = false; step_left = false; step_right = false;
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoCGun::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextDodge = RandomTime( ZOMBIE_CGUN_DODGE_RATE );
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterZombieCommandoCGun::check_attacks() {
int attack_flags = 0;
const float currentTime = Time();
if ( Pain() && currentTime >= nextDodge && owner ) {
if ( owner->TestAnimMove( "evade_left" ) ) { attack_flags |= ATTACK_DODGE_LEFT; }
if ( owner->TestAnimMove( "evade_right" ) ) {
attack_flags |= ATTACK_DODGE_RIGHT;
if ( attack_flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { attack_flags &= ~ATTACK_DODGE_RIGHT; } else { attack_flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { attack_flags |= ATTACK_COMBAT_NODE; }
const bool reachable = CanReachEnemy();
bool try_attack = false;
if ( EnemyInFOV() && ( !reachable || currentTime >= nextAttack ) ) {
try_attack = true;
} else if ( Time() > nextNoFOVAttack && EnemyVisible() && ( !reachable || currentTime >= nextAttack ) ) {
try_attack = true;
} else if ( EnemyRange() < 64.0f ) {
try_attack = true;
}
if ( try_attack ) {
if ( RandomFloat() < 0.5f ) {
if ( CanHitEnemyFromAnim( "crouch_range_attack_loop" ) ) { attack_flags |= ATTACK_ZCC_CROUCHFIRE; }
else if ( CanHitEnemyFromAnim( "range_attack_loop" ) ) { attack_flags |= ATTACK_MISSILE; }
} else if ( CanHitEnemyFromAnim( "range_attack_loop" ) ) {
attack_flags |= ATTACK_MISSILE;
}
}
return attack_flags;
}
bool rvmMonsterZombieCommandoCGun::do_attack( int attack_flags ) {
const int flags = attack_flags;
nextNoFOVAttack = Time() + ZOMBIE_CGUN_NOFOVTIME;
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_ZCC_CROUCHFIRE ) { StartAction( "crouch_attack" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "stand_attack" ); return true; }
return false;
}
stateResult_t rvmMonsterZombieCommandoCGun::stand_attack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_LOOP, STAGE_FINISH };
switch ( parms->stage ) {
case STAGE_START:
fire = true;
attackEndTime = RandomDelay( ZOMBIE_CGUN_STAND_ATTACK_MIN_LENGTH, ZOMBIE_CGUN_STAND_ATTACK_MAX_LENGTH );
loopEndTime = RandomDelay( 0.5f, 1.0f );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
if ( InfluenceActive() || Time() > attackEndTime || !EnemyVisible() || !CanHitEnemyFromAnim( "range_attack_loop" ) ) {
fire = false;
parms->stage = STAGE_FINISH;
return SRESULT_WAIT;
}
LookAtEnemy( 1.0f );
if ( !run_attack || EnemyRange() < 90.0f ) {
FaceEnemy();
} else if ( !EntityInAttackCone( GetEnemy() ) ) {
fire = false;
parms->stage = STAGE_FINISH;
return SRESULT_WAIT;
} else {
MoveToEnemy();
}
if ( Time() >= loopEndTime ) {
loopEndTime = RandomDelay( 0.5f, 1.0f );
if ( owner ) {
const bool left = owner->TestAnimMove( "step_left" ) != 0;
const bool right = owner->TestAnimMove( "step_right" ) != 0;
if ( left && right ) {
step_left = RandomFloat( 100.0f ) >= 50.0f;
step_right = !step_left;
} else {
step_left = left;
step_right = right;
}
}
}
return SRESULT_WAIT;
case STAGE_FINISH:
fire = false;
if ( InAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack" ) ) { return SRESULT_WAIT; }
nextAttack = RandomDelay( ZOMBIE_CGUN_ATTACK_DELAY_MIN, ZOMBIE_CGUN_ATTACK_DELAY_MAX );
nextNoFOVAttack = Time() + ZOMBIE_CGUN_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoCGun::crouch_attack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_LOOP, STAGE_FINISH };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
crouch_fire = true;
attackEndTime = RandomDelay( ZOMBIE_CGUN_CROUCH_ATTACK_MIN_LENGTH, ZOMBIE_CGUN_CROUCH_ATTACK_MAX_LENGTH );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
if ( InfluenceActive() || Time() > attackEndTime || !EnemyVisible() || !CanHitEnemyFromAnim( "range_attack_loop" ) ) {
crouch_fire = false;
SetAnimPrefix( "" );
parms->stage = STAGE_FINISH;
return SRESULT_WAIT;
}
LookAtEnemy( 1.0f );
return SRESULT_WAIT;
case STAGE_FINISH:
crouch_fire = false;
if ( InAnimState( ANIMCHANNEL_TORSO, "Torso_CrouchAttack" ) ) { return SRESULT_WAIT; }
nextAttack = RandomDelay( ZOMBIE_CGUN_ATTACK_DELAY_MIN, ZOMBIE_CGUN_ATTACK_DELAY_MAX );
nextNoFOVAttack = Time() + ZOMBIE_CGUN_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoCGun::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeLeft", 8 );
parms->stage = STAGE_WAIT_ACTION;
return SRESULT_WAIT;
case STAGE_WAIT_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( ZOMBIE_CGUN_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoCGun::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeRight", 8 );
parms->stage = STAGE_WAIT_ACTION;
return SRESULT_WAIT;
case STAGE_WAIT_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( ZOMBIE_CGUN_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,69 @@
// AI_monster_zombie_commando_cgun.h
// Native state-machine replacement for ai_monster_zombie_commando_cgun.script.
#pragma once
#ifndef ZOMBIE_CGUN_RUNDISTANCE
#define ZOMBIE_CGUN_RUNDISTANCE 192
#define ZOMBIE_CGUN_WALKTURN 65
#define ZOMBIE_CGUN_DODGE_RATE 3
#define ZOMBIE_CGUN_NOFOVTIME 4
#define ZOMBIE_CGUN_MIN_SHOT_TIME 0.5
#define ZOMBIE_CGUN_ATTACK_DELAY_MIN 0.5
#define ZOMBIE_CGUN_ATTACK_DELAY_MAX 2
#define ZOMBIE_CGUN_STAND_ATTACK_MIN_LENGTH 3
#define ZOMBIE_CGUN_STAND_ATTACK_MAX_LENGTH 15
#define ZOMBIE_CGUN_CROUCH_ATTACK_MIN_LENGTH 3
#define ZOMBIE_CGUN_CROUCH_ATTACK_MAX_LENGTH 15
#define ZOMBIE_CGUN_WAIT_MIN_LENGTH 0.3
#define ZOMBIE_CGUN_WAIT_MAX_LENGTH 1.5
#define ATTACK_ZCC_CROUCHFIRE ATTACK_SPECIAL1
#endif
class rvmMonsterZombieCommandoCGun : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterZombieCommandoCGun );
rvmMonsterZombieCommandoCGun();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
bool fire;
bool crouch_fire;
bool step_left;
bool step_right;
bool run_attack;
float nextDodge;
float nextAttack;
float nextNoFOVAttack;
idEntityPtr<idEntity> combat_node;
float painNextTime;
float attackEndTime;
float loopEndTime;
float minShootTime;
bool firstShot;
virtual stateResult_t Torso_Idle( stateParms_t* parms ) override;
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Torso_TurretAttack( stateParms_t* parms );
virtual stateResult_t Torso_CrouchAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms ) override;
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t Legs_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Legs_DodgeRight( stateParms_t* parms );
virtual stateResult_t Legs_StepLeft( stateParms_t* parms );
virtual stateResult_t Legs_StepRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms ) override;
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t stand_attack( stateParms_t* parms );
virtual stateResult_t crouch_attack( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,256 @@
// AI_monster_zombie_commando_tentacle.cpp
// Native state-machine replacement for ai_monster_zombie_commando_tentacle.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterBase, rvmMonsterZombieCommandoTentacle )
END_CLASS
rvmMonsterZombieCommandoTentacle::rvmMonsterZombieCommandoTentacle() {
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
tentacleDamage = false;
painNextTime = 0.0f;
}
void rvmMonsterZombieCommandoTentacle::Init( idAI* aiOwner ) {
rvmMonsterBase::Init( aiOwner );
run_distance = ZCT_RUNDISTANCE;
walk_turn = ZCT_WALKTURN;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterZombieCommandoTentacle::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Torso_TentacleAttack", Torso_TentacleAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_tentacle", combat_tentacle );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
return rvmMonsterBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterZombieCommandoTentacle::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = 1;
}
if ( Pain() ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoTentacle::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( Pain() && Time() > painNextTime ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 );
return SRESULT_DONE;
}
if ( AnimDone( ANIMCHANNEL_TORSO, 2 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoTentacle::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoTentacle::Torso_TentacleAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
FaceEnemy();
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "tentacle_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoTentacle::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_LEGS, "idle" );
parms->stage = 1;
}
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoTentacle::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoTentacle::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "run" ); parms->stage = 1; }
if ( !run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieCommandoTentacle::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoTentacle::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
tentacleDamage = false;
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterZombieCommandoTentacle::check_attacks() {
int attack_flags = 0;
if ( CheckForMelee() ) {
attack_flags |= ATTACK_MELEE;
}
if ( ( Time() > nextNoFOVAttack && EnemyVisible() ) || EnemyInFOV() ) {
const float range = EnemyRange();
if ( range < ZCT_TENTACLE_RANGE_MAX && ( !CanReachEnemy() || Time() >= nextAttack ) && CanHitEnemy() ) {
attack_flags |= ATTACK_TENTACLE;
}
}
return attack_flags;
}
bool rvmMonsterZombieCommandoTentacle::do_attack( int attack_flags ) {
const int flags = attack_flags;
nextNoFOVAttack = Time() + ZCT_NOFOVTIME;
if ( flags & ATTACK_MELEE ) {
StartAction( "combat_melee" );
return true;
}
if ( flags & ATTACK_TENTACLE ) {
StartAction( "combat_tentacle" );
return true;
}
return false;
}
stateResult_t rvmMonsterZombieCommandoTentacle::combat_tentacle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_LOOP };
switch ( parms->stage ) {
case STAGE_START:
tentacleDamage = false;
StopMove();
waitActionName = "tentacle_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_TentacleAttack", 4 );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
if ( tentacleDamage && owner && owner->MeleeAttackToJoint( "joint13", "melee_commandoTentacle" ) ) {
tentacleDamage = false;
}
if ( !InAnimState( ANIMCHANNEL_TORSO, "Torso_TentacleAttack" ) || ActionDone( "tentacle_attack" ) ) {
tentacleDamage = false;
nextAttack = DelayTime( ZCT_ATTACK_RATE );
nextNoFOVAttack = Time() + ZCT_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieCommandoTentacle::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_LOOP };
switch ( parms->stage ) {
case STAGE_START:
tentacleDamage = false;
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
LookAtEnemy( 1.0f );
if ( tentacleDamage && owner && owner->MeleeAttackToJoint( "joint13", "melee_commandoTentacle" ) ) {
tentacleDamage = false;
}
if ( !InAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack" ) || ActionDone( "melee_attack" ) ) {
tentacleDamage = false;
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
void rvmMonsterZombieCommandoTentacle::tentacle_attack_start() {
tentacleDamage = true;
}
void rvmMonsterZombieCommandoTentacle::tentacle_attack_end() {
tentacleDamage = false;
}
@@ -0,0 +1,46 @@
// AI_monster_zombie_commando_tentacle.h
// Native state-machine replacement for ai_monster_zombie_commando_tentacle.script.
#pragma once
#ifndef ZCT_RUNDISTANCE
#define ZCT_RUNDISTANCE 128
#define ZCT_WALKTURN 65
#define ZCT_ATTACK_RATE 5
#define ZCT_TENTACLE_RANGE_MAX 200
#define ZCT_NOFOVTIME 4
#define ATTACK_TENTACLE ATTACK_SPECIAL1
#endif
class rvmMonsterZombieCommandoTentacle : public rvmMonsterBase {
public:
CLASS_PROTOTYPE( rvmMonsterZombieCommandoTentacle );
rvmMonsterZombieCommandoTentacle();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float nextAttack;
float nextNoFOVAttack;
bool tentacleDamage;
float painNextTime;
virtual stateResult_t Torso_Idle( stateParms_t* parms ) override;
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Torso_TentacleAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms ) override;
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms ) override;
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_tentacle( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
virtual void tentacle_attack_start();
virtual void tentacle_attack_end();
};
@@ -0,0 +1,176 @@
// AI_monster_zombie_morgue.cpp
// Native state-machine replacement for ai_monster_zombie_morgue.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterZombieBase, rvmMonsterZombieMorgue )
END_CLASS
rvmMonsterZombieMorgue::rvmMonsterZombieMorgue() {
painNextTime = 0.0f;
}
void rvmMonsterZombieMorgue::Init( idAI* aiOwner ) {
rvmMonsterZombieBase::Init( aiOwner );
SetState( "state_Begin" );
}
stateResult_t rvmMonsterZombieMorgue::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
return rvmMonsterZombieBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterZombieMorgue::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_TORSO, "idle" );
parms->stage = 1;
}
if ( Pain() ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieMorgue::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( Pain() && Time() > painNextTime ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 8 );
return SRESULT_DONE;
}
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieMorgue::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieMorgue::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_LEGS, "idle" );
parms->stage = 1;
}
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieMorgue::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
PlayCycle( ANIMCHANNEL_LEGS, "run" );
parms->stage = 1;
}
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieMorgue::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) {
return SRESULT_WAIT;
}
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieMorgue::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) {
return SRESULT_WAIT;
}
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterZombieMorgue::check_attacks() {
int attack_flags = 0;
if ( CheckForMelee() ) {
attack_flags |= ATTACK_MELEE;
}
return attack_flags;
}
bool rvmMonsterZombieMorgue::do_attack( int attack_flags ) {
const int flags = attack_flags;
if ( flags & ATTACK_MELEE ) {
StartAction( "combat_melee" );
return true;
}
return false;
}
stateResult_t rvmMonsterZombieMorgue::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_ACTION };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 8 );
parms->stage = STAGE_WAIT_ACTION;
return SRESULT_WAIT;
case STAGE_WAIT_ACTION:
if ( !ActionDone( "melee_attack" ) ) {
return SRESULT_WAIT;
}
LookAtEnemy( 8.0f );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,29 @@
// AI_monster_zombie_morgue.h
// Native state-machine replacement for ai_monster_zombie_morgue.script.
#pragma once
class rvmMonsterZombieMorgue : public rvmMonsterZombieBase {
public:
CLASS_PROTOTYPE( rvmMonsterZombieMorgue );
rvmMonsterZombieMorgue();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
float painNextTime;
virtual stateResult_t Torso_Idle( stateParms_t* parms ) override;
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms ) override;
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms ) override;
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};
@@ -0,0 +1,216 @@
// AI_monster_zombie_sawyer.cpp
// Native state-machine replacement for ai_monster_zombie_sawyer.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterZombieBase, rvmMonsterZombieSawyer )
END_CLASS
rvmMonsterZombieSawyer::rvmMonsterZombieSawyer() {
attack = false;
painNextTime = 0.0f;
nextHitTime = 0.0f;
bloodSmokeFrames = 0;
}
void rvmMonsterZombieSawyer::Init( idAI* aiOwner ) {
rvmMonsterZombieBase::Init( aiOwner );
SetSmokeVisibility( -1, false );
SetState( "state_Begin" );
}
stateResult_t rvmMonsterZombieSawyer::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_MeleeAttack", Torso_MeleeAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "combat_melee", combat_melee );
return rvmMonsterZombieBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterZombieSawyer::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_TORSO, "stand" );
parms->stage = 1;
}
if ( Pain() ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieSawyer::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( Pain() && Time() > painNextTime ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 4 );
return SRESULT_DONE;
}
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "pain" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieSawyer::Torso_MeleeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
PlayAnim( ANIMCHANNEL_TORSO, "melee_attack" );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 8 ) ) {
FinishAction( "melee_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieSawyer::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_LEGS, "stand" );
parms->stage = 1;
}
if ( MovingForward() ) {
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieSawyer::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
PlayCycle( ANIMCHANNEL_LEGS, "run" );
parms->stage = 1;
}
if ( !MovingForward() ) {
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieSawyer::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) {
return SRESULT_WAIT;
}
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieSawyer::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) {
return SRESULT_WAIT;
}
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterZombieSawyer::check_attacks() {
int attack_flags = 0;
if ( CheckForMelee() ) {
attack_flags |= ATTACK_MELEE;
}
return attack_flags;
}
bool rvmMonsterZombieSawyer::do_attack( int attack_flags ) {
const int flags = attack_flags;
if ( flags & ATTACK_MELEE ) {
StartAction( "combat_melee" );
return true;
}
return false;
}
stateResult_t rvmMonsterZombieSawyer::combat_melee( stateParms_t* parms ) {
enum { STAGE_START, STAGE_LOOP };
switch ( parms->stage ) {
case STAGE_START:
LookAtEnemy( 100.0f );
FaceEnemy();
nextHitTime = 0.0f;
bloodSmokeFrames = 0;
attack = false;
waitActionName = "melee_attack";
SetAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 8 );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
if ( attack && Time() > nextHitTime ) {
if ( AttackMelee( "melee_zombie_chainsaw" ) ) {
nextHitTime = Time() + 0.025f;
SetSmokeVisibility( SAWYER_BLOOD_PARTICLES, true );
bloodSmokeFrames = 4;
}
}
if ( bloodSmokeFrames > 0 ) {
--bloodSmokeFrames;
if ( bloodSmokeFrames == 0 ) {
SetSmokeVisibility( SAWYER_BLOOD_PARTICLES, false );
}
}
if ( !InAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack" ) || ActionDone( "melee_attack" ) ) {
SetSmokeVisibility( SAWYER_BLOOD_PARTICLES, false );
attack = false;
LookAtEnemy( 1.0f );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
void rvmMonsterZombieSawyer::start_smoke() {
SetSmokeVisibility( SAWYER_SMOKE_PARTICLES, true );
}
void rvmMonsterZombieSawyer::start_attack() {
attack = true;
}
void rvmMonsterZombieSawyer::end_attack() {
attack = false;
}
@@ -0,0 +1,40 @@
// AI_monster_zombie_sawyer.h
// Native state-machine replacement for ai_monster_zombie_sawyer.script.
#pragma once
#ifndef SAWYER_SMOKE_PARTICLES
#define SAWYER_SMOKE_PARTICLES 0
#define SAWYER_BLOOD_PARTICLES 1
#endif
class rvmMonsterZombieSawyer : public rvmMonsterZombieBase {
public:
CLASS_PROTOTYPE( rvmMonsterZombieSawyer );
rvmMonsterZombieSawyer();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
bool attack;
float painNextTime;
float nextHitTime;
int bloodSmokeFrames;
virtual stateResult_t Torso_Idle( stateParms_t* parms ) override;
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_MeleeAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms ) override;
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms ) override;
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t combat_melee( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
virtual void start_smoke();
virtual void start_attack();
virtual void end_attack();
};
@@ -0,0 +1,485 @@
// AI_monster_zombie_security_pistol.cpp
// Native state-machine replacement for ai_monster_zombie_security_pistol.script.
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
CLASS_DECLARATION( rvmMonsterZombieBase, rvmMonsterZombieSecurityPistol )
END_CLASS
static float zsecp_num_stand_attacks = 1.0f;
static float zsecp_num_crouch_attacks = 1.0f;
rvmMonsterZombieSecurityPistol::rvmMonsterZombieSecurityPistol() {
fire = false;
crouch_fire = false;
step_left = false;
step_right = false;
run_attack = false;
nextDodge = 0.0f;
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
combat_node = NULL;
painNextTime = 0.0f;
attackEndTime = 0.0f;
loopEndTime = 0.0f;
minShootTime = 0.0f;
firstShot = false;
}
void rvmMonsterZombieSecurityPistol::Init( idAI* aiOwner ) {
rvmMonsterZombieBase::Init( aiOwner );
run_distance = ZSECP_RUNDISTANCE;
walk_turn = ZSECP_WALKTURN;
run_attack = GetIntKey( "run_attack" ) != 0;
SetState( "state_Begin" );
}
stateResult_t rvmMonsterZombieSecurityPistol::RunStateByName( const char* stateName, stateParms_t* parms ) {
AI_DISPATCH_STATE( "Torso_Idle", Torso_Idle );
AI_DISPATCH_STATE( "Torso_Pain", Torso_Pain );
AI_DISPATCH_STATE( "Torso_RangeAttack", Torso_RangeAttack );
AI_DISPATCH_STATE( "Torso_TurretAttack", Torso_TurretAttack );
AI_DISPATCH_STATE( "Torso_CrouchAttack", Torso_CrouchAttack );
AI_DISPATCH_STATE( "Legs_Idle", Legs_Idle );
AI_DISPATCH_STATE( "Legs_Walk", Legs_Walk );
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
AI_DISPATCH_STATE( "Legs_DodgeLeft", Legs_DodgeLeft );
AI_DISPATCH_STATE( "Legs_DodgeRight", Legs_DodgeRight );
AI_DISPATCH_STATE( "Legs_StepLeft", Legs_StepLeft );
AI_DISPATCH_STATE( "Legs_StepRight", Legs_StepRight );
AI_DISPATCH_STATE( "state_Begin", state_Begin );
AI_DISPATCH_STATE( "state_Idle", state_Idle );
AI_DISPATCH_STATE( "stand_attack", stand_attack );
AI_DISPATCH_STATE( "crouch_attack", crouch_attack );
AI_DISPATCH_STATE( "combat_dodge_left", combat_dodge_left );
AI_DISPATCH_STATE( "combat_dodge_right", combat_dodge_right );
return rvmMonsterZombieBase::RunStateByName( stateName, parms );
}
stateResult_t rvmMonsterZombieSecurityPistol::Torso_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) {
IdleAnim( ANIMCHANNEL_TORSO, "stand" );
parms->stage = 1;
}
if ( Pain() ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 );
return SRESULT_DONE;
}
if ( fire ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 0 );
return SRESULT_DONE;
}
if ( crouch_fire ) {
SetAnimState( ANIMCHANNEL_TORSO, "Torso_CrouchAttack", 0 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieSecurityPistol::Torso_Pain( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT };
switch ( parms->stage ) {
case STAGE_START:
SetBlendFrames( ANIMCHANNEL_TORSO, 2 );
PlayAnim( ANIMCHANNEL_TORSO, GetPainAnim() );
painNextTime = DelayTime( 0.25f );
parms->stage = STAGE_WAIT;
return SRESULT_WAIT;
case STAGE_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
PreventPain( 0.6f );
FinishAction( "pain" );
SetBlendFrames( ANIMCHANNEL_TORSO, 4 );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieSecurityPistol::Torso_RangeAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_INTRO, STAGE_LOOP_START, STAGE_LOOP_WAIT, STAGE_END, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
SetAnimPrefix( "" );
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_INTRO;
return SRESULT_WAIT;
case STAGE_INTRO:
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; }
if ( !AnimDone( ANIMCHANNEL_TORSO, 0 ) ) { return SRESULT_WAIT; }
minShootTime = Time() + ZSECP_MIN_SHOT_TIME;
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_LOOP_START:
if ( !fire && Time() >= minShootTime ) { parms->stage = STAGE_END; return SRESULT_WAIT; }
if ( InfluenceActive() ) { fire = false; parms->stage = STAGE_END; return SRESULT_WAIT; }
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_loop" );
parms->stage = STAGE_LOOP_WAIT;
return SRESULT_WAIT;
case STAGE_LOOP_WAIT:
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; }
if ( InfluenceActive() ) { fire = false; parms->stage = STAGE_END; return SRESULT_WAIT; }
if ( !AnimDone( ANIMCHANNEL_TORSO, 0 ) ) { return SRESULT_WAIT; }
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_END:
if ( !AnimDone( ANIMCHANNEL_TORSO, 8 ) ) { SetBlendFrames( ANIMCHANNEL_TORSO, 8 ); }
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( Pain() ) { SetAnimState( ANIMCHANNEL_TORSO, "Torso_Pain", 2 ); return SRESULT_DONE; }
if ( AnimDone( ANIMCHANNEL_TORSO, 4 ) ) {
FinishAction( "range_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieSecurityPistol::Torso_TurretAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_INTRO, STAGE_LOOP_START, STAGE_LOOP_WAIT, STAGE_END, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
SetAnimPrefix( "" );
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_INTRO;
return SRESULT_WAIT;
case STAGE_INTRO:
if ( !AnimDone( ANIMCHANNEL_TORSO, 8 ) ) { return SRESULT_WAIT; }
attackEndTime = RandomDelay( ZSECP_STAND_ATTACK_MIN_LENGTH, ZSECP_STAND_ATTACK_MAX_LENGTH );
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_LOOP_START:
if ( Time() >= attackEndTime || !CanHitEnemyFromAnim( "range_attack_loop" ) || InfluenceActive() ) { parms->stage = STAGE_END; return SRESULT_WAIT; }
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_loop" );
parms->stage = STAGE_LOOP_WAIT;
return SRESULT_WAIT;
case STAGE_LOOP_WAIT:
if ( !AnimDone( ANIMCHANNEL_TORSO, 8 ) ) { return SRESULT_WAIT; }
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_END:
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 8 ) ) {
FinishAction( "turret_attack" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieSecurityPistol::Torso_CrouchAttack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_INTRO, STAGE_LOOP_START, STAGE_LOOP_WAIT, STAGE_AIM, STAGE_END, STAGE_END_WAIT };
switch ( parms->stage ) {
case STAGE_START:
OverrideAnim( ANIMCHANNEL_LEGS );
SetAnimPrefix( "crouch" );
PlayAnim( ANIMCHANNEL_TORSO, "range_attack" );
parms->stage = STAGE_INTRO;
return SRESULT_WAIT;
case STAGE_INTRO:
if ( !AnimDone( ANIMCHANNEL_TORSO, 0 ) ) { return SRESULT_WAIT; }
minShootTime = Time() + ZSECP_MIN_SHOT_TIME;
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_LOOP_START:
if ( !crouch_fire && Time() >= minShootTime ) { parms->stage = STAGE_END; return SRESULT_WAIT; }
loopEndTime = RandomDelay( ZSECP_STAND_ATTACK_MIN_LENGTH, ZSECP_STAND_ATTACK_MAX_LENGTH );
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_loop" );
parms->stage = STAGE_LOOP_WAIT;
return SRESULT_WAIT;
case STAGE_LOOP_WAIT:
if ( InfluenceActive() ) { crouch_fire = false; parms->stage = STAGE_END; return SRESULT_WAIT; }
if ( !AnimDone( ANIMCHANNEL_TORSO, 0 ) ) { return SRESULT_WAIT; }
if ( !crouch_fire || Time() >= loopEndTime ) {
PlayCycle( ANIMCHANNEL_TORSO, "range_attack_aim" );
loopEndTime = RandomDelay( ZSECP_WAIT_MIN_LENGTH, ZSECP_WAIT_MAX_LENGTH );
parms->stage = STAGE_AIM;
return SRESULT_WAIT;
}
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_AIM:
if ( !crouch_fire || InfluenceActive() ) { crouch_fire = false; parms->stage = STAGE_END; return SRESULT_WAIT; }
if ( Time() < loopEndTime ) { return SRESULT_WAIT; }
parms->stage = STAGE_LOOP_START;
return SRESULT_WAIT;
case STAGE_END:
PlayAnim( ANIMCHANNEL_TORSO, "range_attack_end" );
parms->stage = STAGE_END_WAIT;
return SRESULT_WAIT;
case STAGE_END_WAIT:
if ( AnimDone( ANIMCHANNEL_TORSO, 6 ) ) {
FinishAction( "crouch_attack" );
SetAnimPrefix( "" );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 6 );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieSecurityPistol::Legs_Idle( stateParms_t* parms ) {
if ( parms->stage == 0 ) { IdleAnim( ANIMCHANNEL_LEGS, "stand" ); parms->stage = 1; }
if ( GetEnemy() ) { run = true; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 8 ); return SRESULT_DONE; }
if ( MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 ); return SRESULT_DONE; }
if ( step_left ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_StepLeft", 8 ); return SRESULT_DONE; }
if ( step_right ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_StepRight", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieSecurityPistol::Legs_Walk( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "walk" ); parms->stage = 1; }
if ( GetEnemy() ) { run = true; }
if ( run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 8 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieSecurityPistol::Legs_Run( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayCycle( ANIMCHANNEL_LEGS, "run" ); parms->stage = 1; }
if ( GetEnemy() ) { run = true; }
if ( !run && MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 ); return SRESULT_DONE; }
if ( !MovingForward() ) { SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieSecurityPistol::Legs_DodgeLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 2 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 2 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieSecurityPistol::Legs_DodgeRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "evade_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 2 ) ) { FinishAction( "strafe" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 2 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieSecurityPistol::Legs_StepLeft( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "step_left" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { step_left = false; FinishAction( "sidestep" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieSecurityPistol::Legs_StepRight( stateParms_t* parms ) {
if ( parms->stage == 0 ) { PlayAnim( ANIMCHANNEL_LEGS, "step_right" ); parms->stage = 1; }
if ( AnimDone( ANIMCHANNEL_LEGS, 4 ) ) { step_right = false; FinishAction( "sidestep" ); SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); return SRESULT_DONE; }
return SRESULT_WAIT;
}
stateResult_t rvmMonsterZombieSecurityPistol::state_Begin( stateParms_t* parms ) {
enum { STAGE_START, STAGE_MONSTER_BEGIN };
switch ( parms->stage ) {
case STAGE_START:
fire = false; crouch_fire = false; step_left = false; step_right = false;
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
StartAction( "monster_begin" );
parms->stage = STAGE_MONSTER_BEGIN;
return SRESULT_WAIT;
case STAGE_MONSTER_BEGIN:
if ( InAction() ) { return SRESULT_WAIT; }
SetMoveType( MOVETYPE_ANIM );
SetState( "state_Idle" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieSecurityPistol::state_Idle( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_FOR_ENEMY };
switch ( parms->stage ) {
case STAGE_START:
StartAction( "wait_for_enemy" );
parms->stage = STAGE_WAIT_FOR_ENEMY;
return SRESULT_WAIT;
case STAGE_WAIT_FOR_ENEMY:
if ( InAction() ) { return SRESULT_WAIT; }
nextAttack = 0.0f;
nextNoFOVAttack = 0.0f;
nextDodge = RandomTime( ZSECP_DODGE_RATE );
SetState( "state_Combat" );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
int rvmMonsterZombieSecurityPistol::check_attacks() {
int attack_flags = 0;
const float currentTime = Time();
if ( Pain() && currentTime >= nextDodge && owner ) {
if ( owner->TestAnimMove( "evade_left" ) ) { attack_flags |= ATTACK_DODGE_LEFT; }
if ( owner->TestAnimMove( "evade_right" ) ) {
attack_flags |= ATTACK_DODGE_RIGHT;
if ( attack_flags & ATTACK_DODGE_LEFT ) {
if ( RandomFloat( 100.0f ) < 50.0f ) { attack_flags &= ~ATTACK_DODGE_RIGHT; } else { attack_flags &= ~ATTACK_DODGE_LEFT; }
}
}
}
combat_node = GetCombatNode();
if ( combat_node.GetEntity() ) { attack_flags |= ATTACK_COMBAT_NODE; }
const bool reachable = CanReachEnemy();
bool try_attack = false;
if ( EnemyInFOV() && ( !reachable || currentTime >= nextAttack ) ) {
try_attack = true;
} else if ( Time() > nextNoFOVAttack && EnemyVisible() && ( !reachable || currentTime >= nextAttack ) ) {
try_attack = true;
} else if ( EnemyRange() < 64.0f ) {
try_attack = true;
}
if ( try_attack ) {
if ( RandomFloat( zsecp_num_stand_attacks + zsecp_num_crouch_attacks ) < zsecp_num_stand_attacks ) {
if ( CanHitEnemyFromAnim( "crouch_range_attack_loop" ) ) { attack_flags |= ATTACK_ZSECP_CROUCHFIRE; }
else if ( CanHitEnemyFromAnim( "range_attack_loop" ) ) { attack_flags |= ATTACK_MISSILE; }
} else if ( CanHitEnemyFromAnim( "range_attack_loop" ) ) {
attack_flags |= ATTACK_MISSILE;
}
}
return attack_flags;
}
bool rvmMonsterZombieSecurityPistol::do_attack( int attack_flags ) {
const int flags = attack_flags;
nextNoFOVAttack = Time() + ZSECP_NOFOVTIME;
if ( flags & ATTACK_DODGE_LEFT ) { StartAction( "combat_dodge_left" ); return true; }
if ( flags & ATTACK_DODGE_RIGHT ) { StartAction( "combat_dodge_right" ); return true; }
if ( flags & ATTACK_COMBAT_NODE ) { scratchEntity = combat_node.GetEntity(); StartAction( "combat_ainode" ); return true; }
if ( flags & ATTACK_ZSECP_CROUCHFIRE ) { StartAction( "crouch_attack" ); return true; }
if ( flags & ATTACK_MISSILE ) { StartAction( "stand_attack" ); return true; }
return false;
}
stateResult_t rvmMonsterZombieSecurityPistol::stand_attack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_LOOP, STAGE_FINISH };
switch ( parms->stage ) {
case STAGE_START:
zsecp_num_stand_attacks += 1.0f;
fire = true;
attackEndTime = RandomDelay( ZSECP_STAND_ATTACK_MIN_LENGTH, ZSECP_STAND_ATTACK_MAX_LENGTH );
loopEndTime = RandomDelay( 0.5f, 1.0f );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
if ( InfluenceActive() || Time() > attackEndTime || !EnemyVisible() || !CanHitEnemyFromAnim( "range_attack_loop" ) ) {
fire = false;
parms->stage = STAGE_FINISH;
return SRESULT_WAIT;
}
LookAtEnemy( 1.0f );
if ( !run_attack || EnemyRange() < 90.0f ) {
FaceEnemy();
} else if ( !EntityInAttackCone( GetEnemy() ) ) {
fire = false;
parms->stage = STAGE_FINISH;
return SRESULT_WAIT;
} else {
MoveToEnemy();
}
if ( Time() >= loopEndTime ) {
loopEndTime = RandomDelay( 0.5f, 1.0f );
if ( owner ) {
const bool left = owner->TestAnimMove( "step_left" ) != 0;
const bool right = owner->TestAnimMove( "step_right" ) != 0;
if ( left && right ) {
step_left = RandomFloat( 100.0f ) >= 50.0f;
step_right = !step_left;
} else {
step_left = left;
step_right = right;
}
}
}
return SRESULT_WAIT;
case STAGE_FINISH:
fire = false;
if ( InAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack" ) ) { return SRESULT_WAIT; }
nextAttack = RandomDelay( ZSECP_ATTACK_DELAY_MIN, ZSECP_ATTACK_DELAY_MAX );
nextNoFOVAttack = Time() + ZSECP_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieSecurityPistol::crouch_attack( stateParms_t* parms ) {
enum { STAGE_START, STAGE_LOOP, STAGE_FINISH };
switch ( parms->stage ) {
case STAGE_START:
zsecp_num_crouch_attacks += 1.0f;
FaceEnemy();
crouch_fire = true;
attackEndTime = RandomDelay( ZSECP_CROUCH_ATTACK_MIN_LENGTH, ZSECP_CROUCH_ATTACK_MAX_LENGTH );
parms->stage = STAGE_LOOP;
return SRESULT_WAIT;
case STAGE_LOOP:
if ( InfluenceActive() || Time() > attackEndTime || !EnemyVisible() || !CanHitEnemyFromAnim( "range_attack_loop" ) ) {
crouch_fire = false;
SetAnimPrefix( "" );
parms->stage = STAGE_FINISH;
return SRESULT_WAIT;
}
LookAtEnemy( 1.0f );
return SRESULT_WAIT;
case STAGE_FINISH:
crouch_fire = false;
if ( InAnimState( ANIMCHANNEL_TORSO, "Torso_CrouchAttack" ) ) { return SRESULT_WAIT; }
nextAttack = RandomDelay( ZSECP_ATTACK_DELAY_MIN, ZSECP_ATTACK_DELAY_MAX );
nextNoFOVAttack = Time() + ZSECP_NOFOVTIME;
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieSecurityPistol::combat_dodge_left( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeLeft", 2 );
parms->stage = STAGE_WAIT_ACTION;
return SRESULT_WAIT;
case STAGE_WAIT_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( ZSECP_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
stateResult_t rvmMonsterZombieSecurityPistol::combat_dodge_right( stateParms_t* parms ) {
enum { STAGE_START, STAGE_WAIT_ACTION };
switch ( parms->stage ) {
case STAGE_START:
StopMove();
FaceEnemy();
waitActionName = "strafe";
SetAnimState( ANIMCHANNEL_LEGS, "Legs_DodgeRight", 2 );
parms->stage = STAGE_WAIT_ACTION;
return SRESULT_WAIT;
case STAGE_WAIT_ACTION:
if ( !ActionDone( "strafe" ) ) { return SRESULT_WAIT; }
nextDodge = DelayTime( ZSECP_DODGE_RATE );
return SRESULT_DONE;
}
return SRESULT_ERROR;
}
@@ -0,0 +1,69 @@
// AI_monster_zombie_security_pistol.h
// Native state-machine replacement for ai_monster_zombie_security_pistol.script.
#pragma once
#ifndef ZSECP_RUNDISTANCE
#define ZSECP_RUNDISTANCE 192
#define ZSECP_WALKTURN 65
#define ZSECP_DODGE_RATE 3
#define ZSECP_NOFOVTIME 4
#define ZSECP_MIN_SHOT_TIME 0.5
#define ZSECP_ATTACK_DELAY_MIN 0.5
#define ZSECP_ATTACK_DELAY_MAX 2
#define ZSECP_STAND_ATTACK_MIN_LENGTH 3
#define ZSECP_STAND_ATTACK_MAX_LENGTH 15
#define ZSECP_CROUCH_ATTACK_MIN_LENGTH 3
#define ZSECP_CROUCH_ATTACK_MAX_LENGTH 15
#define ZSECP_WAIT_MIN_LENGTH 0.3
#define ZSECP_WAIT_MAX_LENGTH 1.5
#define ATTACK_ZSECP_CROUCHFIRE ATTACK_SPECIAL1
#endif
class rvmMonsterZombieSecurityPistol : public rvmMonsterZombieBase {
public:
CLASS_PROTOTYPE( rvmMonsterZombieSecurityPistol );
rvmMonsterZombieSecurityPistol();
virtual void Init( idAI* aiOwner ) override;
virtual stateResult_t RunStateByName( const char* stateName, stateParms_t* parms ) override;
protected:
bool fire;
bool crouch_fire;
bool step_left;
bool step_right;
bool run_attack;
float nextDodge;
float nextAttack;
float nextNoFOVAttack;
idEntityPtr<idEntity> combat_node;
float painNextTime;
float attackEndTime;
float loopEndTime;
float minShootTime;
bool firstShot;
virtual stateResult_t Torso_Idle( stateParms_t* parms ) override;
virtual stateResult_t Torso_Pain( stateParms_t* parms );
virtual stateResult_t Torso_RangeAttack( stateParms_t* parms );
virtual stateResult_t Torso_TurretAttack( stateParms_t* parms );
virtual stateResult_t Torso_CrouchAttack( stateParms_t* parms );
virtual stateResult_t Legs_Idle( stateParms_t* parms ) override;
virtual stateResult_t Legs_Walk( stateParms_t* parms );
virtual stateResult_t Legs_Run( stateParms_t* parms );
virtual stateResult_t Legs_DodgeLeft( stateParms_t* parms );
virtual stateResult_t Legs_DodgeRight( stateParms_t* parms );
virtual stateResult_t Legs_StepLeft( stateParms_t* parms );
virtual stateResult_t Legs_StepRight( stateParms_t* parms );
virtual stateResult_t state_Begin( stateParms_t* parms ) override;
virtual stateResult_t state_Idle( stateParms_t* parms );
virtual stateResult_t stand_attack( stateParms_t* parms );
virtual stateResult_t crouch_attack( stateParms_t* parms );
virtual stateResult_t combat_dodge_left( stateParms_t* parms );
virtual stateResult_t combat_dodge_right( stateParms_t* parms );
virtual int check_attacks() override;
virtual bool do_attack( int attack_flags ) override;
};