You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
730 lines
17 KiB
730 lines
17 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
#include "cbase.h" |
|
#include "hl1_npc_talker.h" |
|
#include "scripted.h" |
|
#include "soundent.h" |
|
#include "animation.h" |
|
#include "entitylist.h" |
|
#include "ai_navigator.h" |
|
#include "ai_motor.h" |
|
#include "player.h" |
|
#include "vstdlib/random.h" |
|
#include "engine/IEngineSound.h" |
|
#include "npcevent.h" |
|
#include "ai_interactions.h" |
|
#include "doors.h" |
|
|
|
#include "effect_dispatch_data.h" |
|
#include "te_effect_dispatch.h" |
|
#include "hl1_ai_basenpc.h" |
|
#include "SoundEmitterSystem/isoundemittersystembase.h" |
|
|
|
ConVar hl1_debug_sentence_volume( "hl1_debug_sentence_volume", "0" ); |
|
ConVar hl1_fixup_sentence_sndlevel( "hl1_fixup_sentence_sndlevel", "1" ); |
|
|
|
//#define TALKER_LOOK 0 |
|
|
|
BEGIN_DATADESC( CHL1NPCTalker ) |
|
|
|
DEFINE_ENTITYFUNC( Touch ), |
|
DEFINE_FIELD( m_bInBarnacleMouth, FIELD_BOOLEAN ), |
|
DEFINE_USEFUNC( FollowerUse ), |
|
|
|
END_DATADESC() |
|
|
|
void CHL1NPCTalker::RunTask( const Task_t *pTask ) |
|
{ |
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS: |
|
{ |
|
float distance; |
|
|
|
distance = (m_vecLastPosition - GetLocalOrigin()).Length2D(); |
|
|
|
// Walk path until far enough away |
|
if ( distance > pTask->flTaskData || |
|
GetNavigator()->GetGoalType() == GOALTYPE_NONE ) |
|
{ |
|
TaskComplete(); |
|
GetNavigator()->ClearGoal(); // Stop moving |
|
} |
|
break; |
|
} |
|
|
|
|
|
|
|
case TASK_TALKER_CLIENT_STARE: |
|
case TASK_TALKER_LOOK_AT_CLIENT: |
|
{ |
|
CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); |
|
|
|
// track head to the client for a while. |
|
if ( m_NPCState == NPC_STATE_IDLE && |
|
!IsMoving() && |
|
!GetExpresser()->IsSpeaking() ) |
|
{ |
|
|
|
if ( pPlayer ) |
|
{ |
|
IdleHeadTurn( pPlayer ); |
|
} |
|
} |
|
else |
|
{ |
|
// started moving or talking |
|
TaskFail( "moved away" ); |
|
return; |
|
} |
|
|
|
if ( pTask->iTask == TASK_TALKER_CLIENT_STARE ) |
|
{ |
|
// fail out if the player looks away or moves away. |
|
if ( ( pPlayer->GetAbsOrigin() - GetAbsOrigin() ).Length2D() > TALKER_STARE_DIST ) |
|
{ |
|
// player moved away. |
|
TaskFail( NO_TASK_FAILURE ); |
|
} |
|
|
|
Vector vForward; |
|
AngleVectors( GetAbsAngles(), &vForward ); |
|
if ( UTIL_DotPoints( pPlayer->GetAbsOrigin(), GetAbsOrigin(), vForward ) < m_flFieldOfView ) |
|
{ |
|
// player looked away |
|
TaskFail( "looked away" ); |
|
} |
|
} |
|
|
|
if ( gpGlobals->curtime > m_flWaitFinished ) |
|
{ |
|
TaskComplete( NO_TASK_FAILURE ); |
|
} |
|
|
|
break; |
|
} |
|
|
|
case TASK_WAIT_FOR_MOVEMENT: |
|
{ |
|
if ( GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL) |
|
{ |
|
// ALERT(at_console, "walking, talking\n"); |
|
IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime ); |
|
} |
|
else if ( GetEnemy() ) |
|
{ |
|
IdleHeadTurn( GetEnemy() ); |
|
} |
|
|
|
BaseClass::RunTask( pTask ); |
|
|
|
break; |
|
} |
|
|
|
case TASK_FACE_PLAYER: |
|
{ |
|
CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); |
|
|
|
if ( pPlayer ) |
|
{ |
|
//GetMotor()->SetIdealYaw( pPlayer->GetAbsOrigin() ); |
|
IdleHeadTurn( pPlayer ); |
|
if ( gpGlobals->curtime > m_flWaitFinished && GetMotor()->DeltaIdealYaw() < 10 ) |
|
{ |
|
TaskComplete(); |
|
} |
|
} |
|
else |
|
{ |
|
TaskFail( FAIL_NO_PLAYER ); |
|
} |
|
|
|
break; |
|
} |
|
|
|
case TASK_TALKER_EYECONTACT: |
|
{ |
|
if (!IsMoving() && GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL) |
|
{ |
|
// ALERT( at_console, "waiting %f\n", m_flStopTalkTime - gpGlobals->time ); |
|
IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime ); |
|
} |
|
|
|
BaseClass::RunTask( pTask ); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
default: |
|
{ |
|
if ( GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL) |
|
{ |
|
IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime ); |
|
} |
|
else if ( GetEnemy() && m_NPCState == NPC_STATE_COMBAT ) |
|
{ |
|
IdleHeadTurn( GetEnemy() ); |
|
} |
|
else if ( GetFollowTarget() ) |
|
{ |
|
IdleHeadTurn( GetFollowTarget() ); |
|
} |
|
|
|
BaseClass::RunTask( pTask ); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
bool CHL1NPCTalker::ShouldGib( const CTakeDamageInfo &info ) |
|
{ |
|
if ( info.GetDamageType() & DMG_NEVERGIB ) |
|
return false; |
|
|
|
if ( ( g_pGameRules->Damage_ShouldGibCorpse( info.GetDamageType() ) && m_iHealth < GIB_HEALTH_VALUE ) || ( info.GetDamageType() & DMG_ALWAYSGIB ) ) |
|
return true; |
|
|
|
return false; |
|
|
|
} |
|
|
|
void CHL1NPCTalker::StartTask( const Task_t *pTask ) |
|
{ |
|
switch( pTask->iTask ) |
|
{ |
|
case TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS: |
|
{ |
|
GetNavigator()->SetMovementActivity( ACT_WALK ); |
|
break; |
|
} |
|
case TASK_TALKER_SPEAK: |
|
// ask question or make statement |
|
FIdleSpeak(); |
|
TaskComplete(); |
|
break; |
|
default: |
|
BaseClass::StartTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
//========================================================= |
|
// FIdleSpeak |
|
// ask question of nearby friend, or make statement |
|
//========================================================= |
|
int CHL1NPCTalker::FIdleSpeak ( void ) |
|
{ |
|
if (!IsOkToSpeak()) |
|
return FALSE; |
|
|
|
// if there is a friend nearby to speak to, play sentence, set friend's response time, return |
|
// try to talk to any standing or sitting scientists nearby |
|
CBaseEntity *pentFriend = FindNearestFriend( false ); |
|
CHL1NPCTalker *pentTalker = dynamic_cast<CHL1NPCTalker *>( pentFriend ); |
|
if (pentTalker && random->RandomInt(0,1) ) |
|
{ |
|
Speak( TLK_QUESTION ); |
|
SetSpeechTarget( pentFriend ); |
|
|
|
pentTalker->SetSpeechTarget( this ); |
|
pentTalker->SetCondition( COND_TALKER_RESPOND_TO_QUESTION ); |
|
pentTalker->SetSchedule( SCHED_TALKER_IDLE_RESPONSE ); |
|
pentTalker->GetExpresser()->BlockSpeechUntil( GetExpresser()->GetTimeSpeechComplete() ); |
|
|
|
GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + random->RandomFloat(4.8, 5.2) ); |
|
|
|
//DevMsg( "Asking some question!\n" ); |
|
return TRUE; |
|
} |
|
else if ( random->RandomInt(0,1)) // otherwise, play an idle statement |
|
{ |
|
//DevMsg( "Making idle statement!\n" ); |
|
|
|
Speak( TLK_IDLE ); |
|
// set global min delay for next conversation |
|
GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + random->RandomFloat(4.8, 5.2) ); |
|
return TRUE; |
|
} |
|
|
|
// never spoke |
|
GetExpresser()->BlockSpeechUntil( 0 ); |
|
m_flNextIdleSpeechTime = gpGlobals->curtime + 3; |
|
return FALSE; |
|
} |
|
|
|
|
|
|
|
bool CHL1NPCTalker::IsValidSpeechTarget( int flags, CBaseEntity *pEntity ) |
|
{ |
|
if ( pEntity == this ) |
|
return false; |
|
|
|
CHL1NPCTalker *pentTarget = dynamic_cast<CHL1NPCTalker *>( pEntity ); |
|
if ( pentTarget ) |
|
{ |
|
if ( !(flags & AIST_IGNORE_RELATIONSHIP) ) |
|
{ |
|
if ( pEntity->IsPlayer() ) |
|
{ |
|
if ( !IsPlayerAlly( (CBasePlayer *)pEntity ) ) |
|
return false; |
|
} |
|
else |
|
{ |
|
if ( IRelationType( pEntity ) != D_LI ) |
|
return false; |
|
} |
|
} |
|
|
|
if ( !pEntity->IsAlive() ) |
|
// don't dead people |
|
return false; |
|
|
|
// Ignore no-target entities |
|
if ( pEntity->GetFlags() & FL_NOTARGET ) |
|
return false; |
|
|
|
CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); |
|
if ( pNPC ) |
|
{ |
|
// If not a NPC for some reason, or in a script. |
|
//if ( (pNPC->m_NPCState == NPC_STATE_SCRIPT || pNPC->m_NPCState == NPC_STATE_PRONE)) |
|
// return false; |
|
|
|
if ( pNPC->IsInAScript() ) |
|
return false; |
|
|
|
// Don't bother people who don't want to be bothered |
|
if ( !pNPC->CanBeUsedAsAFriend() ) |
|
return false; |
|
} |
|
|
|
if ( flags & AIST_FACING_TARGET ) |
|
{ |
|
if ( pEntity->IsPlayer() ) |
|
return HasCondition( COND_SEE_PLAYER ); |
|
else if ( !FInViewCone( pEntity ) ) |
|
return false; |
|
} |
|
|
|
return FVisible( pEntity ); |
|
} |
|
else |
|
return BaseClass::IsValidSpeechTarget( flags, pEntity ); |
|
} |
|
|
|
|
|
int CHL1NPCTalker::SelectSchedule ( void ) |
|
{ |
|
switch( m_NPCState ) |
|
{ |
|
case NPC_STATE_PRONE: |
|
{ |
|
if (m_bInBarnacleMouth) |
|
{ |
|
return SCHED_HL1TALKER_BARNACLE_CHOMP; |
|
} |
|
else |
|
{ |
|
return SCHED_HL1TALKER_BARNACLE_HIT; |
|
} |
|
} |
|
} |
|
|
|
return BaseClass::SelectSchedule(); |
|
} |
|
|
|
void CHL1NPCTalker::Precache() |
|
{ |
|
BaseClass::Precache(); |
|
|
|
PrecacheScriptSound( "Barney.Close" ); |
|
} |
|
|
|
bool CHL1NPCTalker::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt) |
|
{ |
|
if (interactionType == g_interactionBarnacleVictimDangle) |
|
{ |
|
// Force choosing of a new schedule |
|
ClearSchedule( "NPC talker being eaten by a barnacle" ); |
|
m_bInBarnacleMouth = true; |
|
return true; |
|
} |
|
else if ( interactionType == g_interactionBarnacleVictimReleased ) |
|
{ |
|
SetState ( NPC_STATE_IDLE ); |
|
|
|
CPASAttenuationFilter filter( this ); |
|
|
|
CSoundParameters params; |
|
|
|
if ( GetParametersForSound( "Barney.Close", params, NULL ) ) |
|
{ |
|
EmitSound_t ep( params ); |
|
ep.m_nPitch = GetExpresser()->GetVoicePitch(); |
|
|
|
EmitSound( filter, entindex(), ep ); |
|
} |
|
|
|
m_bInBarnacleMouth = false; |
|
SetAbsVelocity( vec3_origin ); |
|
SetMoveType( MOVETYPE_STEP ); |
|
return true; |
|
} |
|
else if ( interactionType == g_interactionBarnacleVictimGrab ) |
|
{ |
|
if ( GetFlags() & FL_ONGROUND ) |
|
{ |
|
SetGroundEntity( NULL ); |
|
} |
|
|
|
if ( GetState() == NPC_STATE_SCRIPT ) |
|
{ |
|
if ( m_hCine ) |
|
{ |
|
m_hCine->CancelScript(); |
|
} |
|
} |
|
|
|
SetState( NPC_STATE_PRONE ); |
|
ClearSchedule( "NPC talker grabbed by a barnacle" ); |
|
|
|
CTakeDamageInfo info; |
|
PainSound( info ); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
void CHL1NPCTalker::StartFollowing( CBaseEntity *pLeader ) |
|
{ |
|
if ( !HasSpawnFlags( SF_NPC_GAG ) ) |
|
{ |
|
if ( m_iszUse != NULL_STRING ) |
|
{ |
|
PlaySentence( STRING( m_iszUse ), 0.0f ); |
|
} |
|
else |
|
{ |
|
Speak( TLK_STARTFOLLOW ); |
|
} |
|
|
|
SetSpeechTarget( pLeader ); |
|
} |
|
|
|
BaseClass::StartFollowing( pLeader ); |
|
} |
|
|
|
int CHL1NPCTalker::PlayScriptedSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener ) |
|
{ |
|
if( hl1_debug_sentence_volume.GetBool() ) |
|
{ |
|
Msg( "SENTENCE: %s Vol:%f SndLevel:%d\n", GetDebugName(), volume, soundlevel ); |
|
} |
|
|
|
if( hl1_fixup_sentence_sndlevel.GetBool() ) |
|
{ |
|
if( soundlevel < SNDLVL_TALKING ) |
|
{ |
|
soundlevel = SNDLVL_TALKING; |
|
} |
|
} |
|
|
|
return BaseClass::PlayScriptedSentence( pszSentence, delay, volume, soundlevel, bConcurrent, pListener ); |
|
} |
|
|
|
Disposition_t CHL1NPCTalker::IRelationType( CBaseEntity *pTarget ) |
|
{ |
|
if ( pTarget->IsPlayer() ) |
|
{ |
|
if ( HasMemory( bits_MEMORY_PROVOKED ) ) |
|
{ |
|
return D_HT; |
|
} |
|
} |
|
|
|
return BaseClass::IRelationType( pTarget ); |
|
} |
|
|
|
void CHL1NPCTalker::Touch( CBaseEntity *pOther ) |
|
{ |
|
if ( m_NPCState == NPC_STATE_SCRIPT ) |
|
return; |
|
|
|
BaseClass::Touch(pOther); |
|
} |
|
|
|
void CHL1NPCTalker::StopFollowing( void ) |
|
{ |
|
if ( !(m_afMemory & bits_MEMORY_PROVOKED) ) |
|
{ |
|
if ( !HasSpawnFlags( SF_NPC_GAG ) ) |
|
{ |
|
if ( m_iszUnUse != NULL_STRING ) |
|
{ |
|
PlaySentence( STRING( m_iszUnUse ), 0.0f ); |
|
} |
|
else |
|
{ |
|
Speak( TLK_STOPFOLLOW ); |
|
} |
|
|
|
SetSpeechTarget( GetFollowTarget() ); |
|
} |
|
} |
|
|
|
BaseClass::StopFollowing(); |
|
} |
|
|
|
void CHL1NPCTalker::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) |
|
{ |
|
if ( info.GetDamage() >= 1.0 && !(info.GetDamageType() & DMG_SHOCK ) ) |
|
{ |
|
UTIL_BloodImpact( ptr->endpos, vecDir, BloodColor(), 4 ); |
|
} |
|
|
|
BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator ); |
|
} |
|
|
|
void CHL1NPCTalker::FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) |
|
{ |
|
// Don't allow use during a scripted_sentence |
|
if ( GetUseTime() > gpGlobals->curtime ) |
|
return; |
|
|
|
if ( m_hCine && !m_hCine->CanInterrupt() ) |
|
return; |
|
|
|
if ( pCaller != NULL && pCaller->IsPlayer() ) |
|
{ |
|
// Pre-disaster followers can't be used |
|
if ( m_spawnflags & SF_NPC_PREDISASTER ) |
|
{ |
|
SetSpeechTarget( pCaller ); |
|
DeclineFollowing(); |
|
return; |
|
} |
|
} |
|
|
|
BaseClass::FollowerUse( pActivator, pCaller, useType, value ); |
|
} |
|
|
|
int CHL1NPCTalker::TranslateSchedule( int scheduleType ) |
|
{ |
|
return BaseClass::TranslateSchedule( scheduleType ); |
|
} |
|
|
|
float CHL1NPCTalker::PickLookTarget( bool bExcludePlayers, float minTime, float maxTime ) |
|
{ |
|
return random->RandomFloat( 5.0f, 10.0f ); |
|
} |
|
|
|
void CHL1NPCTalker::IdleHeadTurn( CBaseEntity *pTarget, float flDuration, float flImportance ) |
|
{ |
|
// Must be able to turn our head |
|
if (!(CapabilitiesGet() & bits_CAP_TURN_HEAD)) |
|
return; |
|
|
|
// If the target is invalid, or we're in a script, do nothing |
|
if ( ( !pTarget ) || ( m_NPCState == NPC_STATE_SCRIPT ) ) |
|
return; |
|
|
|
// Fill in a duration if we haven't specified one |
|
if ( flDuration == 0.0f ) |
|
{ |
|
flDuration = random->RandomFloat( 2.0, 4.0 ); |
|
} |
|
|
|
// Add a look target |
|
AddLookTarget( pTarget, 1.0, flDuration ); |
|
} |
|
|
|
void CHL1NPCTalker::SetHeadDirection( const Vector &vTargetPos, float flInterval) |
|
{ |
|
#ifdef TALKER_LOOK |
|
// Draw line in body, head, and eye directions |
|
Vector vEyePos = EyePosition(); |
|
Vector vHeadDir = HeadDirection3D(); |
|
Vector vBodyDir = BodyDirection2D(); |
|
|
|
//UNDONE <<TODO>> |
|
// currently eye dir just returns head dir, so use vTargetPos for now |
|
//Vector vEyeDir; w |
|
//EyeDirection3D(&vEyeDir); |
|
NDebugOverlay::Line( vEyePos, vEyePos+(50*vHeadDir), 255, 0, 0, false, 0.1 ); |
|
NDebugOverlay::Line( vEyePos, vEyePos+(50*vBodyDir), 0, 255, 0, false, 0.1 ); |
|
NDebugOverlay::Line( vEyePos, vTargetPos, 0, 0, 255, false, 0.1 ); |
|
#endif |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CHL1NPCTalker::CorpseGib( const CTakeDamageInfo &info ) |
|
{ |
|
CEffectData data; |
|
|
|
data.m_vOrigin = WorldSpaceCenter(); |
|
data.m_vNormal = data.m_vOrigin - info.GetDamagePosition(); |
|
VectorNormalize( data.m_vNormal ); |
|
|
|
data.m_flScale = RemapVal( m_iHealth, 0, -500, 1, 3 ); |
|
data.m_flScale = clamp( data.m_flScale, 1, 3 ); |
|
|
|
data.m_nMaterial = 1; |
|
data.m_nHitBox = -m_iHealth; |
|
|
|
data.m_nColor = BloodColor(); |
|
|
|
DispatchEffect( "HL1Gib", data ); |
|
|
|
BaseClass::CorpseGib( info ); |
|
|
|
CSoundEnt::InsertSound( SOUND_MEAT, GetAbsOrigin(), 256, 0.5f, this ); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CHL1NPCTalker::OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult ) |
|
{ |
|
// If we can't get through the door, try and open it |
|
if ( BaseClass::OnObstructingDoor( pMoveGoal, pDoor, distClear, pResult ) ) |
|
{ |
|
if ( IsMoveBlocked( *pResult ) && pMoveGoal->directTrace.vHitNormal != vec3_origin ) |
|
{ |
|
// Can't do anything if the door's locked |
|
if ( !pDoor->m_bLocked && !pDoor->HasSpawnFlags(SF_DOOR_NONPCS) ) |
|
{ |
|
// Tell the door to open |
|
variant_t emptyVariant; |
|
pDoor->AcceptInput( "Open", this, this, emptyVariant, USE_TOGGLE ); |
|
*pResult = AIMR_OK; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
// HL1 version - never return Ragdoll as the automatic schedule at the end of a |
|
// scripted sequence |
|
int CHL1NPCTalker::SelectDeadSchedule() |
|
{ |
|
// Alread dead (by animation event maybe?) |
|
// Is it safe to set it to SCHED_NONE? |
|
if ( m_lifeState == LIFE_DEAD ) |
|
return SCHED_NONE; |
|
|
|
CleanupOnDeath(); |
|
return SCHED_DIE; |
|
} |
|
|
|
|
|
AI_BEGIN_CUSTOM_NPC( monster_hl1talker, CHL1NPCTalker ) |
|
|
|
DECLARE_TASK( TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS ) |
|
|
|
//========================================================= |
|
// > SCHED_HL1TALKER_MOVE_AWAY_FOLLOW |
|
//========================================================= |
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_HL1TALKER_FOLLOW_MOVE_AWAY, |
|
|
|
" Tasks" |
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_TARGET_FACE" |
|
" TASK_STORE_LASTPOSITION 0" |
|
" TASK_MOVE_AWAY_PATH 100" |
|
" TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS 100" |
|
" TASK_STOP_MOVING 0" |
|
" TASK_FACE_PLAYER 0" |
|
" TASK_SET_ACTIVITY ACT_IDLE" |
|
"" |
|
" Interrupts" |
|
) |
|
|
|
//========================================================= |
|
// > SCHED_HL1TALKER_IDLE_SPEAK_WAIT |
|
//========================================================= |
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_HL1TALKER_IDLE_SPEAK_WAIT, |
|
|
|
" Tasks" |
|
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" // Stop and talk |
|
" TASK_FACE_PLAYER 0" |
|
"" |
|
" Interrupts" |
|
" COND_NEW_ENEMY" |
|
" COND_LIGHT_DAMAGE" |
|
" COND_HEAVY_DAMAGE" |
|
" COND_HEAR_DANGER" |
|
) |
|
|
|
//========================================================= |
|
// > SCHED_HL1TALKER_BARNACLE_HIT |
|
//========================================================= |
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_HL1TALKER_BARNACLE_HIT, |
|
|
|
" Tasks" |
|
" TASK_STOP_MOVING 0" |
|
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_HIT" |
|
" TASK_SET_SCHEDULE SCHEDULE:SCHED_HL1TALKER_BARNACLE_PULL" |
|
"" |
|
" Interrupts" |
|
) |
|
|
|
//========================================================= |
|
// > SCHED_HL1TALKER_BARNACLE_PULL |
|
//========================================================= |
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_HL1TALKER_BARNACLE_PULL, |
|
|
|
" Tasks" |
|
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_PULL" |
|
"" |
|
" Interrupts" |
|
) |
|
|
|
//========================================================= |
|
// > SCHED_HL1TALKER_BARNACLE_CHOMP |
|
//========================================================= |
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_HL1TALKER_BARNACLE_CHOMP, |
|
|
|
" Tasks" |
|
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_CHOMP" |
|
" TASK_SET_SCHEDULE SCHEDULE:SCHED_HL1TALKER_BARNACLE_CHEW" |
|
"" |
|
" Interrupts" |
|
) |
|
|
|
//========================================================= |
|
// > SCHED_HL1TALKER_BARNACLE_CHEW |
|
//========================================================= |
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_HL1TALKER_BARNACLE_CHEW, |
|
|
|
" Tasks" |
|
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_CHEW" |
|
) |
|
|
|
AI_END_CUSTOM_NPC()
|
|
|