mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-16 16:20:42 +02:00
1402 lines
47 KiB
C++
1402 lines
47 KiB
C++
// AI_character.cpp
|
|
// Native state-machine replacement for ai_character.script.
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
#include "../Game_local.h"
|
|
|
|
|
|
namespace {
|
|
const float CHAR_WALKTURN = 65.0f;
|
|
const float CHAR_MIN_TURN = 10.0f;
|
|
const float CHAR_TURN_THRESHOLD = 45.0f;
|
|
const float CHAR_BLEND_COWER_TO_IDLE = 6.0f;
|
|
const float CHAR_BLEND_TURN_TO_IDLE = 0.0f;
|
|
}
|
|
|
|
|
|
rvmCharacter::rvmCharacter() {
|
|
talkMode = CHAR_TALK_PRIMARY;
|
|
talkSecondaryIndex = 1;
|
|
customBlendOut = 0.0f;
|
|
allowTurn = false;
|
|
run = false;
|
|
canTalk = false;
|
|
noCower = false;
|
|
noCowerSaved = false;
|
|
ignorePush = false;
|
|
skipOutOfPath = false;
|
|
followNodes = true;
|
|
dontPushOthers = false;
|
|
lastPushTime = 0.0f;
|
|
pushTime = 0.0f;
|
|
talkEndTime = 0.0f;
|
|
talkChannel = ANIMCHANNEL_LEGS;
|
|
talkOriginalYaw = 0.0f;
|
|
}
|
|
|
|
void rvmCharacter::Init( idAI* aiOwner ) {
|
|
rvmAIObject::Init( aiOwner );
|
|
|
|
if ( owner ) {
|
|
owner->SetJointMod( GetIntKey( "head_look" ) );
|
|
owner->spawnArgs.Set( "conversationNext", "" );
|
|
}
|
|
|
|
lastPushTime = 0.0f;
|
|
pushTime = 0.0f;
|
|
talkMode = CHAR_TALK_PRIMARY;
|
|
talkSecondaryIndex = 1;
|
|
followNodes = true;
|
|
walkAnim.Clear();
|
|
run = false;
|
|
canTalk = GetIntKey( "talks" ) != 0;
|
|
noCower = GetIntKey( "no_cower" ) != 0;
|
|
ignorePush = GetIntKey( "ignore_push" ) != 0;
|
|
noCowerSaved = noCower;
|
|
dontPushOthers = GetIntKey( "dont_push_others" ) != 0;
|
|
allowTurn = GetFloatKey( "turn_rate" ) != 0.0f;
|
|
if ( !allowTurn ) {
|
|
ignorePush = true;
|
|
}
|
|
|
|
SetState( GetIntKey( "spawner" ) ? "state_Spawner" : "state_Begin" );
|
|
}
|
|
|
|
void rvmCharacter::Destroy() {
|
|
if ( listening_to_character.GetEntity() && listening_to_character.GetEntity()->IsType( idAI::Type ) ) {
|
|
idAI* listeningAI = static_cast<idAI*>( listening_to_character.GetEntity() );
|
|
rvmCharacter* conversation = listeningAI->nativeAIObject ? listeningAI->nativeAIObject->Cast<rvmCharacter>() : NULL;
|
|
while ( conversation ) {
|
|
if ( conversation->next_listener.GetEntity() == owner ) {
|
|
conversation->next_listener = next_listener;
|
|
break;
|
|
}
|
|
idAI* nextAI = conversation->next_listener.GetEntity() && conversation->next_listener.GetEntity()->IsType( idAI::Type )
|
|
? static_cast<idAI*>( conversation->next_listener.GetEntity() )
|
|
: NULL;
|
|
conversation = nextAI && nextAI->nativeAIObject ? nextAI->nativeAIObject->Cast<rvmCharacter>() : NULL;
|
|
}
|
|
}
|
|
|
|
rvmAIObject::Destroy();
|
|
}
|
|
|
|
stateResult_t rvmCharacter::RunStateByName( const char* stateName, stateParms_t* parms ) {
|
|
AI_DISPATCH_STATE( "Anim_Disable", Anim_Disable );
|
|
AI_DISPATCH_STATE( "Head_Idle", Head_Idle );
|
|
AI_DISPATCH_STATE( "Head_Dead", Head_Dead );
|
|
AI_DISPATCH_STATE( "Head_TalkHeadOnly", Head_TalkHeadOnly );
|
|
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_Walk_Special", Legs_Walk_Special );
|
|
AI_DISPATCH_STATE( "Legs_Run", Legs_Run );
|
|
AI_DISPATCH_STATE( "Legs_TurnLeft", Legs_TurnLeft );
|
|
AI_DISPATCH_STATE( "Legs_TurnRight", Legs_TurnRight );
|
|
AI_DISPATCH_STATE( "Legs_Cower", Legs_Cower );
|
|
AI_DISPATCH_STATE( "Legs_CustomCycle", Legs_CustomCycle );
|
|
AI_DISPATCH_STATE( "Legs_CustomAnim", Legs_CustomAnim );
|
|
AI_DISPATCH_STATE( "state_Spawner", state_Spawner );
|
|
AI_DISPATCH_STATE( "state_Cower", state_Cower );
|
|
AI_DISPATCH_STATE( "state_Begin", state_Begin );
|
|
AI_DISPATCH_STATE( "state_TalkTrigger", state_TalkTrigger );
|
|
AI_DISPATCH_STATE( "state_Idle", state_Idle );
|
|
AI_DISPATCH_STATE( "state_Killed", state_Killed );
|
|
AI_DISPATCH_STATE( "idle_stand", idle_stand );
|
|
AI_DISPATCH_STATE( "idle_followPathEntities", idle_followPathEntities );
|
|
AI_DISPATCH_STATE( "idle_talk", idle_talk );
|
|
AI_DISPATCH_STATE( "checkBlocked", checkBlocked );
|
|
AI_DISPATCH_STATE( "path_corner", path_corner );
|
|
AI_DISPATCH_STATE( "path_anim", path_anim );
|
|
AI_DISPATCH_STATE( "path_cycleanim", path_cycleanim );
|
|
AI_DISPATCH_STATE( "path_turn", path_turn );
|
|
AI_DISPATCH_STATE( "path_wait", path_wait );
|
|
AI_DISPATCH_STATE( "path_waitfortrigger", path_waitfortrigger );
|
|
AI_DISPATCH_STATE( "path_hide", path_hide );
|
|
AI_DISPATCH_STATE( "path_show", path_show );
|
|
AI_DISPATCH_STATE( "path_conversation_listen", path_conversation_listen );
|
|
AI_DISPATCH_STATE( "path_conversation", path_conversation );
|
|
AI_DISPATCH_STATE( "path_headanim", path_headanim );
|
|
AI_DISPATCH_STATE( "path_waitforheadanim", path_waitforheadanim );
|
|
AI_DISPATCH_STATE( "path_talk", path_talk );
|
|
AI_DISPATCH_STATE( "path_talk_triggered", path_talk_triggered );
|
|
AI_DISPATCH_STATE( "path_talk_primary", path_talk_primary );
|
|
AI_DISPATCH_STATE( "path_talk_secondary", path_talk_secondary );
|
|
AI_DISPATCH_STATE( "path_lookat", path_lookat );
|
|
AI_DISPATCH_STATE( "state_FollowAlternatePath", state_FollowAlternatePath );
|
|
return rvmAIObject::RunStateByName( stateName, parms );
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Anim_Disable( stateParms_t* parms ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Head_Idle( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
IdleAnim( ANIMCHANNEL_HEAD, "stand" );
|
|
parms->stage = 1;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Head_Dead( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
PlayAnim( ANIMCHANNEL_HEAD, "dead" );
|
|
parms->stage = 1;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Head_TalkHeadOnly( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
PlayAnim( ANIMCHANNEL_HEAD, customAnim.c_str() );
|
|
parms->stage = 1;
|
|
}
|
|
if ( AnimDone( ANIMCHANNEL_HEAD, idMath::Ftoi( customBlendOut ) ) ) {
|
|
FinishAction( "talkAnim" );
|
|
SetAnimState( ANIMCHANNEL_HEAD, "Head_Idle", idMath::Ftoi( customBlendOut ) );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Legs_Death( stateParms_t* parms ) {
|
|
FinishAction( "dead" );
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Legs_Idle( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
AllowMovement( false );
|
|
IdleAnim( ANIMCHANNEL_LEGS, "stand" );
|
|
parms->stage = 1;
|
|
}
|
|
|
|
if ( MovingForward() ) {
|
|
if ( walkAnim.Length() ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk_Special", 8 );
|
|
} else if ( run ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 8 );
|
|
} else {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 );
|
|
}
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
if ( allowTurn ) {
|
|
const float delta = GetTurnDelta();
|
|
if ( delta > CHAR_MIN_TURN ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_TurnLeft", 4 );
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( delta < -CHAR_MIN_TURN ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_TurnRight", 4 );
|
|
return SRESULT_DONE;
|
|
}
|
|
}
|
|
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Legs_Walk( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
AllowMovement( true );
|
|
PlayCycle( ANIMCHANNEL_LEGS, "walk" );
|
|
parms->stage = 1;
|
|
}
|
|
if ( !MovingForward() || walkAnim.Length() ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( run ) {
|
|
const float delta = GetTurnDelta();
|
|
if ( delta < CHAR_WALKTURN && delta > -CHAR_WALKTURN ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Run", 8 );
|
|
return SRESULT_DONE;
|
|
}
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Legs_Walk_Special( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
AllowMovement( true );
|
|
PlayCycle( ANIMCHANNEL_LEGS, walkAnim.c_str() );
|
|
parms->stage = 1;
|
|
}
|
|
if ( !MovingForward() || !walkAnim.Length() ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Legs_Run( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
AllowMovement( true );
|
|
PlayCycle( ANIMCHANNEL_LEGS, "run" );
|
|
parms->stage = 1;
|
|
}
|
|
const float delta = GetTurnDelta();
|
|
if ( !MovingForward() || !run || walkAnim.Length() ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( delta > CHAR_WALKTURN || delta < -CHAR_WALKTURN ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Legs_TurnLeft( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
AllowMovement( true );
|
|
if ( owner ) {
|
|
owner->AnimTurn( 180.0f );
|
|
}
|
|
PlayAnim( ANIMCHANNEL_LEGS, "turn_left" );
|
|
parms->stage = 1;
|
|
}
|
|
if ( !AnimDone( ANIMCHANNEL_LEGS, idMath::Ftoi( CHAR_BLEND_TURN_TO_IDLE ) ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( owner ) {
|
|
owner->AnimTurn( 0.0f );
|
|
}
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( CHAR_BLEND_TURN_TO_IDLE ) );
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Legs_TurnRight( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
AllowMovement( true );
|
|
if ( owner ) {
|
|
owner->AnimTurn( 180.0f );
|
|
}
|
|
PlayAnim( ANIMCHANNEL_LEGS, "turn_right" );
|
|
parms->stage = 1;
|
|
}
|
|
if ( !AnimDone( ANIMCHANNEL_LEGS, idMath::Ftoi( CHAR_BLEND_TURN_TO_IDLE ) ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( owner ) {
|
|
owner->AnimTurn( 0.0f );
|
|
}
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( CHAR_BLEND_TURN_TO_IDLE ) );
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Legs_Cower( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( NULL );
|
|
}
|
|
StartSoundKey( "snd_cower", SND_CHANNEL_VOICE, false );
|
|
PlayAnim( ANIMCHANNEL_LEGS, "cower" );
|
|
parms->stage = 1;
|
|
}
|
|
LookAtEntity( Player(), 0.1f );
|
|
if ( AnimDone( ANIMCHANNEL_LEGS, idMath::Ftoi( CHAR_BLEND_COWER_TO_IDLE ) ) ) {
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( NULL );
|
|
}
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( CHAR_BLEND_COWER_TO_IDLE ) );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Legs_CustomCycle( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
AllowMovement( true );
|
|
PlayCycle( ANIMCHANNEL_LEGS, customAnim.c_str() );
|
|
parms->stage = 1;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::Legs_CustomAnim( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
AllowMovement( true );
|
|
PlayAnim( ANIMCHANNEL_LEGS, customAnim.c_str() );
|
|
parms->stage = 1;
|
|
}
|
|
if ( AnimDone( ANIMCHANNEL_LEGS, idMath::Ftoi( customBlendOut ) ) ) {
|
|
FinishAction( "customAnim" );
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( customBlendOut ) );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::state_Spawner( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
HideOwner();
|
|
ClearActivated();
|
|
parms->stage = 1;
|
|
}
|
|
if ( Activated() && CanBecomeSolid() ) {
|
|
ClearActivated();
|
|
if ( owner ) {
|
|
idDict args;
|
|
args = owner->spawnArgs;
|
|
args.SetInt( "spawner", 0 );
|
|
gameLocal.SpawnEntityDef( args );
|
|
}
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::state_Cower( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
if ( owner ) {
|
|
owner->StopSound( SND_CHANNEL_VOICE, false );
|
|
owner->SetTalkTarget( NULL );
|
|
}
|
|
endHeadAnim( 6.0f );
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Cower", 6 );
|
|
parms->stage = 1;
|
|
}
|
|
if ( InAnimState( ANIMCHANNEL_LEGS, "Legs_Cower" ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( NULL );
|
|
}
|
|
SetState( "state_Idle" );
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::state_Begin( stateParms_t* parms ) {
|
|
enum {
|
|
STAGE_INIT,
|
|
STAGE_WAIT_TRIGGER,
|
|
STAGE_WAIT_SOLID,
|
|
STAGE_TRIGGER_ANIM,
|
|
STAGE_TELEPORT_FX,
|
|
STAGE_TELEPORT_ANIM,
|
|
STAGE_POST_WAIT,
|
|
STAGE_WAIT_EXPLICIT_TRIGGER
|
|
};
|
|
|
|
switch ( parms->stage ) {
|
|
case STAGE_INIT: {
|
|
talkMode = CHAR_TALK_PRIMARY;
|
|
followNodes = true;
|
|
walkAnim.Clear();
|
|
run = false;
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
|
|
SetAnimState( ANIMCHANNEL_HEAD, "Head_Idle", 0 );
|
|
|
|
const char* triggerAnim = GetKey( "trigger_anim" );
|
|
if ( triggerAnim[0] ) {
|
|
customAnim = triggerAnim;
|
|
customBlendOut = 4.0f;
|
|
HideOwner();
|
|
ClearActivated();
|
|
parms->stage = STAGE_WAIT_TRIGGER;
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
const int teleportType = GetIntKey( "teleport" );
|
|
if ( teleportType > 0 ) {
|
|
HideOwner();
|
|
ClearActivated();
|
|
parms->stage = STAGE_WAIT_TRIGGER;
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
if ( GetIntKey( "hide" ) ) {
|
|
HideOwner();
|
|
ClearActivated();
|
|
parms->stage = STAGE_WAIT_TRIGGER;
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
if ( GetIntKey( "trigger" ) ) {
|
|
ClearActivated();
|
|
parms->stage = STAGE_WAIT_EXPLICIT_TRIGGER;
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
scratchEndTime = GetFloatKey( "wait" ) > 0.0f ? DelayTime( GetFloatKey( "wait" ) ) : 0.0f;
|
|
parms->stage = STAGE_POST_WAIT;
|
|
return SRESULT_WAIT;
|
|
}
|
|
case STAGE_WAIT_TRIGGER:
|
|
if ( !Activated() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
ClearActivated();
|
|
ClearEnemy();
|
|
parms->stage = STAGE_WAIT_SOLID;
|
|
return SRESULT_WAIT;
|
|
case STAGE_WAIT_SOLID:
|
|
if ( !CanBecomeSolid() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( GetIntKey( "teleport" ) > 0 ) {
|
|
BecomeSolid();
|
|
SetMoveType( MOVETYPE_STATIC );
|
|
const int teleportType = GetIntKey( "teleport" );
|
|
StartFx( teleportType == 1 ? "fx/teleporter1.fx" :
|
|
teleportType == 2 ? "fx/teleporter2.fx" :
|
|
teleportType == 3 ? "fx/teleporter3.fx" : "fx/teleporter.fx" );
|
|
scratchEndTime = DelayTime( teleportType == 1 ? 1.6f : teleportType == 2 ? 2.6f : teleportType == 3 ? 3.6f : 0.6f );
|
|
parms->stage = STAGE_TELEPORT_FX;
|
|
return SRESULT_WAIT;
|
|
}
|
|
ShowOwner();
|
|
if ( customAnim.Length() ) {
|
|
waitForCustomAnim( customAnim.c_str(), 0.0f, 4.0f );
|
|
parms->stage = STAGE_TRIGGER_ANIM;
|
|
return SRESULT_WAIT;
|
|
}
|
|
scratchEndTime = GetFloatKey( "wait" ) > 0.0f ? DelayTime( GetFloatKey( "wait" ) ) : 0.0f;
|
|
parms->stage = STAGE_POST_WAIT;
|
|
return SRESULT_WAIT;
|
|
case STAGE_TRIGGER_ANIM:
|
|
if ( InAnimState( ANIMCHANNEL_LEGS, "Legs_CustomAnim" ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
scratchEndTime = GetFloatKey( "wait" ) > 0.0f ? DelayTime( GetFloatKey( "wait" ) ) : 0.0f;
|
|
parms->stage = STAGE_POST_WAIT;
|
|
return SRESULT_WAIT;
|
|
case STAGE_TELEPORT_FX:
|
|
if ( Time() < scratchEndTime ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
ShowOwner();
|
|
playCustomAnim( "teleport", 0.0f, 4.0f );
|
|
parms->stage = STAGE_TELEPORT_ANIM;
|
|
return SRESULT_WAIT;
|
|
case STAGE_TELEPORT_ANIM:
|
|
if ( InAnimState( ANIMCHANNEL_LEGS, "Legs_CustomAnim" ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
SetMoveType( MOVETYPE_ANIM );
|
|
scratchEndTime = GetFloatKey( "wait" ) > 0.0f ? DelayTime( GetFloatKey( "wait" ) ) : 0.0f;
|
|
parms->stage = STAGE_POST_WAIT;
|
|
return SRESULT_WAIT;
|
|
case STAGE_WAIT_EXPLICIT_TRIGGER:
|
|
if ( !Activated() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
ClearActivated();
|
|
scratchEndTime = GetFloatKey( "wait" ) > 0.0f ? DelayTime( GetFloatKey( "wait" ) ) : 0.0f;
|
|
parms->stage = STAGE_POST_WAIT;
|
|
return SRESULT_WAIT;
|
|
case STAGE_POST_WAIT:
|
|
if ( scratchEndTime > 0.0f && Time() < scratchEndTime ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
SetState( GetIntKey( "talktrigger" ) ? "state_TalkTrigger" : "state_Idle" );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::state_TalkTrigger( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
const char* animname = GetKey( "anim" );
|
|
if ( animname[0] ) {
|
|
playCustomCycle( animname, 0.0f );
|
|
}
|
|
ClearActivated();
|
|
parms->stage = 1;
|
|
}
|
|
check_cower();
|
|
if ( !Activated() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 12 );
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( Player() );
|
|
}
|
|
talkMode = CHAR_TALK_TRIGGERED;
|
|
SetState( "state_Idle" );
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::state_Idle( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
if ( canTalk && owner ) {
|
|
owner->SetTalkState( TALK_OK );
|
|
}
|
|
noCower = noCowerSaved;
|
|
parms->stage = 1;
|
|
}
|
|
|
|
if ( owner && owner->AI_TALK ) {
|
|
StartAction( "idle_talk" );
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
if ( followNodes ) {
|
|
if ( !current_path.GetEntity() ) {
|
|
current_path = owner ? owner->RandomPath() : NULL;
|
|
}
|
|
if ( current_path.GetEntity() ) {
|
|
executePathCommand( current_path.GetEntity() );
|
|
if ( GetIntKey( "follow_once" ) ) {
|
|
followNodes = false;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
}
|
|
|
|
check_cower();
|
|
if ( owner && owner->AI_PUSHED ) {
|
|
getOutOfWay();
|
|
owner->AI_PUSHED = false;
|
|
}
|
|
StopMove();
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::state_Killed( stateParms_t* parms ) {
|
|
StopMove();
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 0 );
|
|
SetAnimState( ANIMCHANNEL_HEAD, "Head_Dead", 0 );
|
|
StopThinking();
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::idle_stand( stateParms_t* parms ) {
|
|
StopMove();
|
|
check_cower();
|
|
if ( owner && owner->AI_PUSHED ) {
|
|
getOutOfWay();
|
|
owner->AI_PUSHED = false;
|
|
}
|
|
return ( owner && owner->AI_TALK ) ? SRESULT_DONE : SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::idle_followPathEntities( stateParms_t* parms ) {
|
|
if ( !current_path.GetEntity() ) {
|
|
current_path = owner ? owner->RandomPath() : NULL;
|
|
}
|
|
if ( current_path.GetEntity() ) {
|
|
executePathCommand( current_path.GetEntity() );
|
|
}
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::idle_talk( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_TURN_WAIT, STAGE_PLAY, STAGE_TALKING, STAGE_AFTER_TALK };
|
|
idEntity* talkTo = owner ? owner->GetTalkTarget() : NULL;
|
|
if ( !talkTo ) {
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( NULL );
|
|
}
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
const bool noTurn = GetIntKey( "talk_no_turn" ) != 0;
|
|
const float talkRadius = GetFloatKey( "talkradius", 300.0f );
|
|
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
StopMove();
|
|
talkOriginalYaw = owner ? owner->GetAngles().y : 0.0f;
|
|
if ( !noTurn && allowTurn ) {
|
|
faceTowardsEntity( talkTo, 0.1f );
|
|
scratchEndTime = DelayTime( 0.5f );
|
|
parms->stage = STAGE_TURN_WAIT;
|
|
return SRESULT_WAIT;
|
|
}
|
|
parms->stage = STAGE_PLAY;
|
|
return SRESULT_WAIT;
|
|
case STAGE_TURN_WAIT:
|
|
LookAtEntity( talkTo, 0.1f );
|
|
if ( Time() < scratchEndTime || !InAnimState( ANIMCHANNEL_LEGS, "Legs_Idle" ) ) {
|
|
if ( talkMode != CHAR_TALK_TRIGGERED ) {
|
|
check_cower();
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
parms->stage = STAGE_PLAY;
|
|
return SRESULT_WAIT;
|
|
case STAGE_PLAY: {
|
|
AllowMovement( true );
|
|
int blendIn = 8;
|
|
if ( talkMode == CHAR_TALK_TRIGGERED ) {
|
|
blendIn = GetIntKey( "talk0_blendin", 8 );
|
|
customBlendOut = GetIntKey( "talk0_blendout", 8 );
|
|
talkChannel = playHeadAnim( "talk_trigger", blendIn );
|
|
talkMode = CHAR_TALK_PRIMARY;
|
|
} else if ( talkMode == CHAR_TALK_PRIMARY ) {
|
|
blendIn = GetIntKey( "talk1_blendin", 8 );
|
|
customBlendOut = GetIntKey( "talk1_blendout", 8 );
|
|
talkChannel = playHeadAnim( "talk_primary", blendIn );
|
|
talkMode = CHAR_TALK_SECONDARY;
|
|
} else {
|
|
idStr anim = va( "talk_secondary%d", talkSecondaryIndex );
|
|
blendIn = GetIntKey( "talk2_blendin", 8 );
|
|
customBlendOut = GetIntKey( "talk2_blendout", 8 );
|
|
talkChannel = playHeadAnim( anim.c_str(), blendIn );
|
|
talkSecondaryIndex++;
|
|
anim = va( "talk_secondary%d", talkSecondaryIndex );
|
|
if ( !HasAnim( ANIMCHANNEL_HEAD, anim.c_str() ) && !HasAnim( ANIMCHANNEL_LEGS, anim.c_str() ) ) {
|
|
talkSecondaryIndex = 1;
|
|
}
|
|
}
|
|
parms->stage = STAGE_TALKING;
|
|
return SRESULT_WAIT;
|
|
}
|
|
case STAGE_TALKING:
|
|
if ( owner && ( owner->GetPhysics()->GetOrigin() - talkTo->GetPhysics()->GetOrigin() ).Length() > talkRadius ) {
|
|
owner->StopSound( SND_CHANNEL_VOICE, false );
|
|
parms->stage = STAGE_AFTER_TALK;
|
|
return SRESULT_WAIT;
|
|
}
|
|
if ( !AnimDone( idMath::Ftoi( talkChannel ), idMath::Ftoi( customBlendOut ) ) ) {
|
|
check_cower();
|
|
if ( noTurn || !allowTurn ) {
|
|
LookAtEntity( talkTo, 0.1f );
|
|
} else {
|
|
faceTowardsEntity( talkTo, 0.1f );
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
parms->stage = STAGE_AFTER_TALK;
|
|
talkEndTime = DelayTime( GetFloatKey( "talktime", 10.0f ) );
|
|
endHeadAnim( customBlendOut );
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( NULL );
|
|
}
|
|
return SRESULT_WAIT;
|
|
case STAGE_AFTER_TALK:
|
|
if ( Time() < talkEndTime && owner && ( owner->GetPhysics()->GetOrigin() - talkTo->GetPhysics()->GetOrigin() ).Length() <= talkRadius ) {
|
|
check_cower();
|
|
if ( noTurn || !allowTurn ) {
|
|
LookAtEntity( talkTo, 0.1f );
|
|
} else {
|
|
faceTowardsEntity( talkTo, 0.1f );
|
|
}
|
|
if ( owner->AI_TALK ) {
|
|
AllowMovement( false );
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( owner->AI_PUSHED ) {
|
|
getOutOfWay();
|
|
owner->AI_PUSHED = false;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
LookAtEntity( NULL, 0.0f );
|
|
TurnTo( talkOriginalYaw );
|
|
AllowMovement( false );
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::checkBlocked( stateParms_t* parms ) {
|
|
if ( owner && MoveStatus() >= MOVE_STATUS_BLOCKED_BY_OBJECT ) {
|
|
owner->KickObstacles( owner->GetObstacle(), GetFloatKey( "kick_force", 60.0f ) );
|
|
}
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_corner( stateParms_t* parms ) {
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( parms->stage == 0 ) {
|
|
run = node->spawnArgs.GetBool( "run", "0" );
|
|
walkAnim = node->spawnArgs.GetString( "anim", "" );
|
|
MoveToEntity( node );
|
|
parms->stage = 1;
|
|
}
|
|
check_cower();
|
|
checkBlocked( parms );
|
|
if ( owner && owner->AI_TALK ) {
|
|
cancelPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( MoveDone() || DestinationUnreachable() ) {
|
|
walkAnim.Clear();
|
|
run = false;
|
|
if ( DestinationUnreachable() && owner ) {
|
|
gameLocal.Warning( "entity '%s' couldn't reach path_corner '%s'", owner->GetName(), node->GetName() );
|
|
}
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
MoveToEntity( node );
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_anim( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_TURN, STAGE_ANIM };
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
customAnim = node->spawnArgs.GetString( "anim", "" );
|
|
customBlendOut = node->spawnArgs.GetInt( "blend_out", "0" );
|
|
if ( node->spawnArgs.GetString( "angle", "" )[0] ) {
|
|
TurnTo( node->spawnArgs.GetFloat( "angle" ) );
|
|
parms->stage = STAGE_TURN;
|
|
return SRESULT_WAIT;
|
|
}
|
|
parms->stage = STAGE_ANIM;
|
|
return SRESULT_WAIT;
|
|
case STAGE_TURN:
|
|
check_cower();
|
|
if ( !FacingIdeal() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
parms->stage = STAGE_ANIM;
|
|
return SRESULT_WAIT;
|
|
case STAGE_ANIM:
|
|
if ( node->spawnArgs.GetBool( "head_anim", "0" ) ) {
|
|
talkChannel = playHeadAnim( customAnim.c_str(), node->spawnArgs.GetInt( "blend_in", "0" ) );
|
|
parms->stage = 3;
|
|
return SRESULT_WAIT;
|
|
}
|
|
playCustomAnim( customAnim.c_str(), node->spawnArgs.GetInt( "blend_in", "0" ), customBlendOut );
|
|
parms->stage = 4;
|
|
return SRESULT_WAIT;
|
|
case 3:
|
|
check_cower();
|
|
if ( owner && owner->AI_TALK ) {
|
|
endHeadAnim( customBlendOut );
|
|
cancelPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( AnimDone( idMath::Ftoi( talkChannel ), idMath::Ftoi( customBlendOut ) ) ) {
|
|
endHeadAnim( customBlendOut );
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
case 4:
|
|
check_cower();
|
|
if ( owner && owner->AI_TALK ) {
|
|
endCustomAnim();
|
|
cancelPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( inCustomAnim() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_cycleanim( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_TURN, STAGE_HOLD };
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
customAnim = node->spawnArgs.GetString( "anim", "" );
|
|
customBlendOut = node->spawnArgs.GetInt( "blend_out", "0" );
|
|
if ( node->spawnArgs.GetString( "angle", "" )[0] ) {
|
|
TurnTo( node->spawnArgs.GetFloat( "angle" ) );
|
|
parms->stage = STAGE_TURN;
|
|
return SRESULT_WAIT;
|
|
}
|
|
parms->stage = STAGE_TURN;
|
|
return SRESULT_WAIT;
|
|
case STAGE_TURN:
|
|
if ( !FacingIdeal() ) {
|
|
check_cower();
|
|
return SRESULT_WAIT;
|
|
}
|
|
playCustomCycle( customAnim.c_str(), node->spawnArgs.GetInt( "blend_in", "0" ) );
|
|
scratchEndTime = node->spawnArgs.GetFloat( "wait", "0" ) > 0.0f ? DelayTime( node->spawnArgs.GetFloat( "wait" ) ) : 0.0f;
|
|
ClearActivated();
|
|
parms->stage = STAGE_HOLD;
|
|
return SRESULT_WAIT;
|
|
case STAGE_HOLD:
|
|
check_cower();
|
|
if ( owner && owner->AI_TALK ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( customBlendOut ) );
|
|
cancelPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( scratchEndTime > 0.0f ) {
|
|
if ( Time() < scratchEndTime ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
} else if ( !Activated() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( customBlendOut ) );
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_turn( stateParms_t* parms ) {
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( parms->stage == 0 ) {
|
|
TurnTo( node->GetPhysics()->GetAxis()[0].ToYaw() );
|
|
parms->stage = 1;
|
|
}
|
|
check_cower();
|
|
if ( FacingIdeal() ) {
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_wait( stateParms_t* parms ) {
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( parms->stage == 0 ) {
|
|
scratchEndTime = DelayTime( node->spawnArgs.GetFloat( "wait", "0" ) );
|
|
parms->stage = 1;
|
|
}
|
|
check_cower();
|
|
if ( Time() >= scratchEndTime ) {
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_waitfortrigger( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
ClearActivated();
|
|
parms->stage = 1;
|
|
}
|
|
check_cower();
|
|
if ( owner && owner->AI_TALK ) {
|
|
cancelPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( Activated() ) {
|
|
ClearActivated();
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_hide( stateParms_t* parms ) {
|
|
HideOwner();
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_show( stateParms_t* parms ) {
|
|
if ( !CanBecomeSolid() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
ShowOwner();
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_conversation_listen( stateParms_t* parms ) {
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( parms->stage == 0 ) {
|
|
StopMove();
|
|
listening_to_character = gameLocal.FindEntity( node->spawnArgs.GetString( "focus", "" ) );
|
|
customAnim = node->spawnArgs.GetString( "anim", "" );
|
|
customBlendOut = node->spawnArgs.GetInt( "blend_out", "0" );
|
|
if ( customAnim.Length() ) {
|
|
playCustomCycle( customAnim.c_str(), node->spawnArgs.GetInt( "blend_in", "0" ) );
|
|
}
|
|
parms->stage = 1;
|
|
}
|
|
if ( owner && owner->AI_TALK ) {
|
|
cancelPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( !listening_to_character.GetEntity() ) {
|
|
if ( customAnim.Length() ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( customBlendOut ) );
|
|
}
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
if ( !node->spawnArgs.GetBool( "no_look", "0" ) ) {
|
|
LookAtEntity( listening_to_character.GetEntity(), 0.1f );
|
|
}
|
|
check_cower();
|
|
return SRESULT_WAIT;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_conversation( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_ANIM, STAGE_WAIT };
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
idEntity* listener = gameLocal.FindEntity( node->spawnArgs.GetString( "focus", "" ) );
|
|
switch ( parms->stage ) {
|
|
case STAGE_START: {
|
|
StopMove();
|
|
const char* dialog = node->spawnArgs.GetString( "snd_dialog", "" );
|
|
if ( dialog[0] && owner ) {
|
|
const idSoundShader* shader = declManager->FindSound( dialog );
|
|
owner->StartSoundShader( shader, SND_CHANNEL_VOICE, 0, false, NULL );
|
|
}
|
|
customBlendOut = node->spawnArgs.GetInt( "blend_out", "0" );
|
|
talkChannel = playHeadAnim( node->spawnArgs.GetString( "anim", "" ), node->spawnArgs.GetInt( "blend_in", "0" ) );
|
|
parms->stage = STAGE_ANIM;
|
|
return SRESULT_WAIT;
|
|
}
|
|
case STAGE_ANIM:
|
|
if ( owner && owner->AI_TALK ) {
|
|
break_out_of_conversation();
|
|
cancelPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
check_cower();
|
|
LookAtEntity( listener, 0.1f );
|
|
if ( !AnimDone( idMath::Ftoi( talkChannel ), idMath::Ftoi( customBlendOut ) ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
endHeadAnim( customBlendOut );
|
|
scratchEndTime = node->spawnArgs.GetFloat( "wait", "0" ) > 0.0f ? DelayTime( node->spawnArgs.GetFloat( "wait" ) ) : 0.0f;
|
|
parms->stage = STAGE_WAIT;
|
|
return SRESULT_WAIT;
|
|
case STAGE_WAIT:
|
|
if ( owner && owner->AI_TALK ) {
|
|
break_out_of_conversation();
|
|
cancelPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
check_cower();
|
|
LookAtEntity( listener, 0.1f );
|
|
if ( scratchEndTime > 0.0f && Time() < scratchEndTime ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
while ( next_listener.GetEntity() ) {
|
|
idAI* listenerAI = next_listener.GetEntity()->IsType( idAI::Type ) ? static_cast<idAI*>( next_listener.GetEntity() ) : NULL;
|
|
rvmCharacter* charListener = listenerAI && listenerAI->nativeAIObject ? listenerAI->nativeAIObject->Cast<rvmCharacter>() : NULL;
|
|
if ( charListener ) {
|
|
next_listener = charListener->next_listener;
|
|
charListener->next_listener = NULL;
|
|
charListener->listening_to_character = NULL;
|
|
} else {
|
|
next_listener = NULL;
|
|
}
|
|
}
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_headanim( stateParms_t* parms ) {
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
customBlendOut = node->spawnArgs.GetInt( "blend_out", "0" );
|
|
customAnim = node->spawnArgs.GetString( "anim", "" );
|
|
SetAnimState( ANIMCHANNEL_HEAD, "Head_TalkHeadOnly", node->spawnArgs.GetInt( "blend_in", "0" ) );
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_waitforheadanim( stateParms_t* parms ) {
|
|
idEntity* node = current_path.GetEntity();
|
|
idEntity* focus = node ? gameLocal.FindEntity( node->spawnArgs.GetString( "focus", "" ) ) : NULL;
|
|
StopMove();
|
|
if ( owner && owner->AI_TALK ) {
|
|
cancelPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
check_cower();
|
|
LookAtEntity( focus, 0.1f );
|
|
if ( InAnimState( ANIMCHANNEL_HEAD, "Head_TalkHeadOnly" ) ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_talk( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( Player() );
|
|
}
|
|
parms->stage = 1;
|
|
}
|
|
stateResult_t result = idle_talk( parms );
|
|
if ( result == SRESULT_DONE ) {
|
|
finishPathCommand();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_talk_triggered( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
talkMode = CHAR_TALK_TRIGGERED;
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( Player() );
|
|
}
|
|
parms->stage = 1;
|
|
}
|
|
stateResult_t result = idle_talk( parms );
|
|
if ( result == SRESULT_DONE ) {
|
|
finishPathCommand();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_talk_primary( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
talkMode = CHAR_TALK_PRIMARY;
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( Player() );
|
|
}
|
|
parms->stage = 1;
|
|
}
|
|
stateResult_t result = idle_talk( parms );
|
|
if ( result == SRESULT_DONE ) {
|
|
finishPathCommand();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_talk_secondary( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
talkMode = CHAR_TALK_SECONDARY;
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( Player() );
|
|
}
|
|
parms->stage = 1;
|
|
}
|
|
stateResult_t result = idle_talk( parms );
|
|
if ( result == SRESULT_DONE ) {
|
|
finishPathCommand();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::path_lookat( stateParms_t* parms ) {
|
|
enum { STAGE_START, STAGE_HOLD };
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( !node ) {
|
|
return SRESULT_DONE;
|
|
}
|
|
idEntity* focus = gameLocal.FindEntity( node->spawnArgs.GetString( "focus", "" ) );
|
|
switch ( parms->stage ) {
|
|
case STAGE_START:
|
|
customAnim = node->spawnArgs.GetString( "anim", "" );
|
|
customBlendOut = node->spawnArgs.GetInt( "blend_out", "0" );
|
|
if ( customAnim.Length() ) {
|
|
playCustomCycle( customAnim.c_str(), node->spawnArgs.GetInt( "blend_in", "0" ) );
|
|
}
|
|
scratchEndTime = node->spawnArgs.GetFloat( "wait", "0" ) > 0.0f ? DelayTime( node->spawnArgs.GetFloat( "wait" ) ) : 0.0f;
|
|
ClearActivated();
|
|
parms->stage = STAGE_HOLD;
|
|
return SRESULT_WAIT;
|
|
case STAGE_HOLD:
|
|
if ( owner && owner->AI_TALK ) {
|
|
if ( customAnim.Length() ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( customBlendOut ) );
|
|
}
|
|
cancelPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
check_cower();
|
|
LookAtEntity( focus, 0.1f );
|
|
if ( Activated() || ( scratchEndTime > 0.0f && Time() >= scratchEndTime ) ) {
|
|
if ( customAnim.Length() ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( customBlendOut ) );
|
|
}
|
|
finishPathCommand();
|
|
return SRESULT_DONE;
|
|
}
|
|
return SRESULT_WAIT;
|
|
}
|
|
return SRESULT_ERROR;
|
|
}
|
|
|
|
stateResult_t rvmCharacter::state_FollowAlternatePath( stateParms_t* parms ) {
|
|
if ( parms->stage == 0 ) {
|
|
endHeadAnim( 8.0f );
|
|
executePathCommand( current_path.GetEntity() );
|
|
parms->stage = 1;
|
|
}
|
|
if ( InAction() ) {
|
|
return SRESULT_WAIT;
|
|
}
|
|
SetState( "state_Idle" );
|
|
return SRESULT_DONE;
|
|
}
|
|
|
|
void rvmCharacter::Anim_Disable() {
|
|
}
|
|
|
|
void rvmCharacter::check_cower() {
|
|
if ( !noCower && HeardSound( false ) ) {
|
|
endHeadAnim( 6.0f );
|
|
cancelPathCommand();
|
|
SetState( "state_Cower" );
|
|
}
|
|
}
|
|
|
|
void rvmCharacter::target_talk() {
|
|
cancelPathCommand();
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( Player() );
|
|
}
|
|
StartAction( "idle_talk" );
|
|
SetState( "state_Idle" );
|
|
}
|
|
|
|
void rvmCharacter::say_triggered() {
|
|
cancelPathCommand();
|
|
talkMode = CHAR_TALK_TRIGGERED;
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( Player() );
|
|
}
|
|
SetState( "state_Idle" );
|
|
}
|
|
|
|
void rvmCharacter::say_primary() {
|
|
cancelPathCommand();
|
|
talkMode = CHAR_TALK_PRIMARY;
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( Player() );
|
|
}
|
|
SetState( "state_Idle" );
|
|
}
|
|
|
|
void rvmCharacter::say_secondary() {
|
|
cancelPathCommand();
|
|
talkMode = CHAR_TALK_SECONDARY;
|
|
if ( owner ) {
|
|
owner->SetTalkTarget( Player() );
|
|
}
|
|
SetState( "state_Idle" );
|
|
}
|
|
|
|
void rvmCharacter::faceTowardsEntity( idEntity* target, float duration ) {
|
|
if ( !target || !owner ) {
|
|
return;
|
|
}
|
|
idVec3 delta = target->GetPhysics()->GetOrigin() - GetOrigin();
|
|
idAngles targetAngles = delta.ToAngles();
|
|
const idVec3 currentAngles = owner->GetAngles();
|
|
const float yawDelta = idMath::AngleNormalize180( targetAngles.yaw - currentAngles.y );
|
|
if ( yawDelta < -CHAR_TURN_THRESHOLD || yawDelta > CHAR_TURN_THRESHOLD ) {
|
|
TurnTo( targetAngles.yaw );
|
|
}
|
|
LookAtEntity( target, duration );
|
|
}
|
|
|
|
void rvmCharacter::playCustomCycle( const char* animname, float blendTime ) {
|
|
customAnim = animname ? animname : "";
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_CustomCycle", idMath::Ftoi( blendTime ) );
|
|
}
|
|
|
|
void rvmCharacter::playCustomAnim( const char* animname, float blendIn, float blendOut ) {
|
|
customBlendOut = blendOut;
|
|
customAnim = animname ? animname : "";
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_CustomAnim", idMath::Ftoi( blendIn ) );
|
|
}
|
|
|
|
bool rvmCharacter::inCustomAnim() {
|
|
return InAnimState( ANIMCHANNEL_LEGS, "Legs_CustomAnim" );
|
|
}
|
|
|
|
void rvmCharacter::endCustomAnim() {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( customBlendOut ) );
|
|
}
|
|
|
|
void rvmCharacter::waitForCustomAnim( const char* animname, float blendIn, float blendOut ) {
|
|
check_cower();
|
|
customBlendOut = blendOut;
|
|
customAnim = animname ? animname : "";
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_CustomAnim", idMath::Ftoi( blendIn ) );
|
|
}
|
|
|
|
void rvmCharacter::endHeadAnim( float blend_out ) {
|
|
StopAnim( ANIMCHANNEL_HEAD, idMath::Ftoi( blend_out ) );
|
|
SetBlendFrames( ANIMCHANNEL_LEGS, idMath::Ftoi( blend_out ) );
|
|
IdleAnim( ANIMCHANNEL_LEGS, "stand" );
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( blend_out ) );
|
|
SetAnimState( ANIMCHANNEL_HEAD, "Head_Idle", idMath::Ftoi( blend_out ) );
|
|
}
|
|
|
|
float rvmCharacter::playHeadAnim( const char* animname, float blend_frames ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Anim_Disable", idMath::Ftoi( blend_frames ) );
|
|
SetBlendFrames( ANIMCHANNEL_LEGS, idMath::Ftoi( blend_frames ) );
|
|
IdleAnim( ANIMCHANNEL_LEGS, "stand" );
|
|
|
|
const char* headAnim = ChooseAnim( ANIMCHANNEL_HEAD, animname );
|
|
if ( headAnim[0] ) {
|
|
SetBlendFrames( ANIMCHANNEL_HEAD, idMath::Ftoi( blend_frames ) );
|
|
PlayAnim( ANIMCHANNEL_HEAD, headAnim );
|
|
const float headLength = AnimLength( ANIMCHANNEL_HEAD, headAnim );
|
|
|
|
const char* bodyAnim = ChooseAnim( ANIMCHANNEL_LEGS, animname );
|
|
if ( !bodyAnim[0] ) {
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", idMath::Ftoi( blend_frames ) );
|
|
} else if ( AnimLength( ANIMCHANNEL_LEGS, bodyAnim ) < headLength ) {
|
|
PlayCycle( ANIMCHANNEL_LEGS, bodyAnim );
|
|
} else {
|
|
PlayAnim( ANIMCHANNEL_LEGS, bodyAnim );
|
|
}
|
|
return ANIMCHANNEL_HEAD;
|
|
}
|
|
|
|
if ( blend_frames >= 0.0f ) {
|
|
SetBlendFrames( ANIMCHANNEL_LEGS, idMath::Ftoi( blend_frames ) );
|
|
}
|
|
const char* bodyAnim = ChooseAnim( ANIMCHANNEL_LEGS, animname );
|
|
PlayAnim( ANIMCHANNEL_LEGS, bodyAnim[0] ? bodyAnim : "stand" );
|
|
return ANIMCHANNEL_LEGS;
|
|
}
|
|
|
|
void rvmCharacter::idle_followPathEntities( idEntity* pathnode ) {
|
|
current_path = pathnode;
|
|
StartAction( "idle_followPathEntities" );
|
|
}
|
|
|
|
void rvmCharacter::executePathCommand( idEntity* pathnode ) {
|
|
current_path = pathnode;
|
|
next_path = idPathCorner::RandomPath( current_path.GetEntity(), NULL );
|
|
if ( current_path.GetEntity() ) {
|
|
const char* classname = current_path.GetEntity()->spawnArgs.GetString( "classname", current_path.GetEntity()->GetEntityDefName() );
|
|
StartAction( classname );
|
|
}
|
|
}
|
|
|
|
void rvmCharacter::finishPathCommand() {
|
|
idEntity* node = current_path.GetEntity();
|
|
if ( node ) {
|
|
const char* triggername = node->spawnArgs.GetString( "trigger", "" );
|
|
if ( triggername[0] ) {
|
|
idEntity* trigger = gameLocal.FindEntity( triggername );
|
|
if ( trigger ) {
|
|
trigger->ActivateTargets( owner );
|
|
}
|
|
}
|
|
}
|
|
current_path = next_path;
|
|
skipOutOfPath = false;
|
|
if ( canTalk && owner ) {
|
|
owner->SetTalkState( TALK_OK );
|
|
}
|
|
noCower = noCowerSaved;
|
|
}
|
|
|
|
void rvmCharacter::cancelPathCommand() {
|
|
if ( skipOutOfPath ) {
|
|
finishPathCommand();
|
|
}
|
|
skipOutOfPath = false;
|
|
if ( canTalk && owner ) {
|
|
owner->SetTalkState( TALK_OK );
|
|
}
|
|
noCower = noCowerSaved;
|
|
}
|
|
|
|
bool rvmCharacter::getOutOfWay() {
|
|
if ( ignorePush || !owner ) {
|
|
return false;
|
|
}
|
|
|
|
if ( ( Time() - lastPushTime ) > FrameTime() ) {
|
|
lastPushTime = Time();
|
|
pushTime = lastPushTime + 0.35f;
|
|
return false;
|
|
}
|
|
|
|
lastPushTime = Time() + FrameTime();
|
|
if ( Time() < pushTime ) {
|
|
return false;
|
|
}
|
|
|
|
idVec3 org = owner->PushPointIntoNAV( GetOrigin() );
|
|
for ( float yaw = 0.0f; yaw < 360.0f; yaw += 45.0f ) {
|
|
idAngles angles( 0.0f, yaw, 0.0f );
|
|
idVec3 pos = owner->PushPointIntoNAV( org + angles.ToForward() * 124.0f );
|
|
if ( TestMoveToPosition( pos ) ) {
|
|
MoveToPosition( pos );
|
|
TurnTo( CurrentYaw() + 180.0f );
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void rvmCharacter::skip_conversation() {
|
|
while ( current_path.GetEntity() ) {
|
|
const char* classname = current_path.GetEntity()->spawnArgs.GetString( "classname", current_path.GetEntity()->GetEntityDefName() );
|
|
if ( idStr::Cmpn( classname, "path_conversation", 17 ) ) {
|
|
break;
|
|
}
|
|
current_path = next_path;
|
|
if ( next_path.GetEntity() ) {
|
|
next_path = idPathCorner::RandomPath( next_path.GetEntity(), NULL );
|
|
} else {
|
|
next_path = NULL;
|
|
}
|
|
}
|
|
if ( owner ) {
|
|
owner->StopSound( SND_CHANNEL_VOICE, false );
|
|
}
|
|
}
|
|
|
|
void rvmCharacter::break_out_of_conversation() {
|
|
while ( next_listener.GetEntity() ) {
|
|
idAI* listenerAI = next_listener.GetEntity()->IsType( idAI::Type ) ? static_cast<idAI*>( next_listener.GetEntity() ) : NULL;
|
|
rvmCharacter* listener = listenerAI && listenerAI->nativeAIObject ? listenerAI->nativeAIObject->Cast<rvmCharacter>() : NULL;
|
|
if ( listener ) {
|
|
next_listener = listener->next_listener;
|
|
listener->skip_conversation();
|
|
listener->next_listener = NULL;
|
|
listener->listening_to_character = NULL;
|
|
} else {
|
|
next_listener = NULL;
|
|
}
|
|
}
|
|
skip_conversation();
|
|
}
|
|
|
|
void rvmCharacter::follow_alternate_path1() {
|
|
current_path = GetEntityKey( "alt_path1" );
|
|
SetState( "state_FollowAlternatePath" );
|
|
}
|
|
|
|
void rvmCharacter::follow_alternate_path2() {
|
|
current_path = GetEntityKey( "alt_path2" );
|
|
SetState( "state_FollowAlternatePath" );
|
|
}
|
|
|
|
void rvmCharacter::follow_alternate_path3() {
|
|
current_path = GetEntityKey( "alt_path3" );
|
|
SetState( "state_FollowAlternatePath" );
|
|
}
|
|
|
|
void rvmCharacter::disable_turning() {
|
|
allowTurn = false;
|
|
}
|
|
|
|
void rvmCharacter::enable_turning() {
|
|
allowTurn = true;
|
|
}
|