Files
DoomRTX/neo/doom3/game/ai/AI_character.cpp
T
2026-05-23 20:50:12 -07:00

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;
}