Team Fortress 2 Source Code as on 22/4/2020
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.

728 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "hl1_npc_talker.h"
  10. #include "scripted.h"
  11. #include "soundent.h"
  12. #include "animation.h"
  13. #include "entitylist.h"
  14. #include "ai_navigator.h"
  15. #include "ai_motor.h"
  16. #include "player.h"
  17. #include "vstdlib/random.h"
  18. #include "engine/IEngineSound.h"
  19. #include "npcevent.h"
  20. #include "ai_interactions.h"
  21. #include "doors.h"
  22. #include "effect_dispatch_data.h"
  23. #include "te_effect_dispatch.h"
  24. #include "hl1_ai_basenpc.h"
  25. #include "SoundEmitterSystem/isoundemittersystembase.h"
  26. ConVar hl1_debug_sentence_volume( "hl1_debug_sentence_volume", "0" );
  27. ConVar hl1_fixup_sentence_sndlevel( "hl1_fixup_sentence_sndlevel", "1" );
  28. //#define TALKER_LOOK 0
  29. BEGIN_DATADESC( CHL1NPCTalker )
  30. DEFINE_ENTITYFUNC( Touch ),
  31. DEFINE_FIELD( m_bInBarnacleMouth, FIELD_BOOLEAN ),
  32. DEFINE_USEFUNC( FollowerUse ),
  33. END_DATADESC()
  34. void CHL1NPCTalker::RunTask( const Task_t *pTask )
  35. {
  36. switch ( pTask->iTask )
  37. {
  38. case TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS:
  39. {
  40. float distance;
  41. distance = (m_vecLastPosition - GetLocalOrigin()).Length2D();
  42. // Walk path until far enough away
  43. if ( distance > pTask->flTaskData ||
  44. GetNavigator()->GetGoalType() == GOALTYPE_NONE )
  45. {
  46. TaskComplete();
  47. GetNavigator()->ClearGoal(); // Stop moving
  48. }
  49. break;
  50. }
  51. case TASK_TALKER_CLIENT_STARE:
  52. case TASK_TALKER_LOOK_AT_CLIENT:
  53. {
  54. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  55. // track head to the client for a while.
  56. if ( m_NPCState == NPC_STATE_IDLE &&
  57. !IsMoving() &&
  58. !GetExpresser()->IsSpeaking() )
  59. {
  60. if ( pPlayer )
  61. {
  62. IdleHeadTurn( pPlayer );
  63. }
  64. }
  65. else
  66. {
  67. // started moving or talking
  68. TaskFail( "moved away" );
  69. return;
  70. }
  71. if ( pTask->iTask == TASK_TALKER_CLIENT_STARE )
  72. {
  73. // fail out if the player looks away or moves away.
  74. if ( ( pPlayer->GetAbsOrigin() - GetAbsOrigin() ).Length2D() > TALKER_STARE_DIST )
  75. {
  76. // player moved away.
  77. TaskFail( NO_TASK_FAILURE );
  78. }
  79. Vector vForward;
  80. AngleVectors( GetAbsAngles(), &vForward );
  81. if ( UTIL_DotPoints( pPlayer->GetAbsOrigin(), GetAbsOrigin(), vForward ) < m_flFieldOfView )
  82. {
  83. // player looked away
  84. TaskFail( "looked away" );
  85. }
  86. }
  87. if ( gpGlobals->curtime > m_flWaitFinished )
  88. {
  89. TaskComplete( NO_TASK_FAILURE );
  90. }
  91. break;
  92. }
  93. case TASK_WAIT_FOR_MOVEMENT:
  94. {
  95. if ( GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
  96. {
  97. // ALERT(at_console, "walking, talking\n");
  98. IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
  99. }
  100. else if ( GetEnemy() )
  101. {
  102. IdleHeadTurn( GetEnemy() );
  103. }
  104. BaseClass::RunTask( pTask );
  105. break;
  106. }
  107. case TASK_FACE_PLAYER:
  108. {
  109. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  110. if ( pPlayer )
  111. {
  112. //GetMotor()->SetIdealYaw( pPlayer->GetAbsOrigin() );
  113. IdleHeadTurn( pPlayer );
  114. if ( gpGlobals->curtime > m_flWaitFinished && GetMotor()->DeltaIdealYaw() < 10 )
  115. {
  116. TaskComplete();
  117. }
  118. }
  119. else
  120. {
  121. TaskFail( FAIL_NO_PLAYER );
  122. }
  123. break;
  124. }
  125. case TASK_TALKER_EYECONTACT:
  126. {
  127. if (!IsMoving() && GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
  128. {
  129. // ALERT( at_console, "waiting %f\n", m_flStopTalkTime - gpGlobals->time );
  130. IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
  131. }
  132. BaseClass::RunTask( pTask );
  133. break;
  134. }
  135. default:
  136. {
  137. if ( GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
  138. {
  139. IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
  140. }
  141. else if ( GetEnemy() && m_NPCState == NPC_STATE_COMBAT )
  142. {
  143. IdleHeadTurn( GetEnemy() );
  144. }
  145. else if ( GetFollowTarget() )
  146. {
  147. IdleHeadTurn( GetFollowTarget() );
  148. }
  149. BaseClass::RunTask( pTask );
  150. break;
  151. }
  152. }
  153. }
  154. bool CHL1NPCTalker::ShouldGib( const CTakeDamageInfo &info )
  155. {
  156. if ( info.GetDamageType() & DMG_NEVERGIB )
  157. return false;
  158. if ( ( g_pGameRules->Damage_ShouldGibCorpse( info.GetDamageType() ) && m_iHealth < GIB_HEALTH_VALUE ) || ( info.GetDamageType() & DMG_ALWAYSGIB ) )
  159. return true;
  160. return false;
  161. }
  162. void CHL1NPCTalker::StartTask( const Task_t *pTask )
  163. {
  164. switch( pTask->iTask )
  165. {
  166. case TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS:
  167. {
  168. GetNavigator()->SetMovementActivity( ACT_WALK );
  169. break;
  170. }
  171. case TASK_TALKER_SPEAK:
  172. // ask question or make statement
  173. FIdleSpeak();
  174. TaskComplete();
  175. break;
  176. default:
  177. BaseClass::StartTask( pTask );
  178. break;
  179. }
  180. }
  181. //=========================================================
  182. // FIdleSpeak
  183. // ask question of nearby friend, or make statement
  184. //=========================================================
  185. int CHL1NPCTalker::FIdleSpeak ( void )
  186. {
  187. if (!IsOkToSpeak())
  188. return FALSE;
  189. // if there is a friend nearby to speak to, play sentence, set friend's response time, return
  190. // try to talk to any standing or sitting scientists nearby
  191. CBaseEntity *pentFriend = FindNearestFriend( false );
  192. CHL1NPCTalker *pentTalker = dynamic_cast<CHL1NPCTalker *>( pentFriend );
  193. if (pentTalker && random->RandomInt(0,1) )
  194. {
  195. Speak( TLK_QUESTION );
  196. SetSpeechTarget( pentFriend );
  197. pentTalker->SetSpeechTarget( this );
  198. pentTalker->SetCondition( COND_TALKER_RESPOND_TO_QUESTION );
  199. pentTalker->SetSchedule( SCHED_TALKER_IDLE_RESPONSE );
  200. pentTalker->GetExpresser()->BlockSpeechUntil( GetExpresser()->GetTimeSpeechComplete() );
  201. GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + random->RandomFloat(4.8, 5.2) );
  202. //DevMsg( "Asking some question!\n" );
  203. return TRUE;
  204. }
  205. else if ( random->RandomInt(0,1)) // otherwise, play an idle statement
  206. {
  207. //DevMsg( "Making idle statement!\n" );
  208. Speak( TLK_IDLE );
  209. // set global min delay for next conversation
  210. GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + random->RandomFloat(4.8, 5.2) );
  211. return TRUE;
  212. }
  213. // never spoke
  214. GetExpresser()->BlockSpeechUntil( 0 );
  215. m_flNextIdleSpeechTime = gpGlobals->curtime + 3;
  216. return FALSE;
  217. }
  218. bool CHL1NPCTalker::IsValidSpeechTarget( int flags, CBaseEntity *pEntity )
  219. {
  220. if ( pEntity == this )
  221. return false;
  222. CHL1NPCTalker *pentTarget = dynamic_cast<CHL1NPCTalker *>( pEntity );
  223. if ( pentTarget )
  224. {
  225. if ( !(flags & AIST_IGNORE_RELATIONSHIP) )
  226. {
  227. if ( pEntity->IsPlayer() )
  228. {
  229. if ( !IsPlayerAlly( (CBasePlayer *)pEntity ) )
  230. return false;
  231. }
  232. else
  233. {
  234. if ( IRelationType( pEntity ) != D_LI )
  235. return false;
  236. }
  237. }
  238. if ( !pEntity->IsAlive() )
  239. // don't dead people
  240. return false;
  241. // Ignore no-target entities
  242. if ( pEntity->GetFlags() & FL_NOTARGET )
  243. return false;
  244. CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
  245. if ( pNPC )
  246. {
  247. // If not a NPC for some reason, or in a script.
  248. //if ( (pNPC->m_NPCState == NPC_STATE_SCRIPT || pNPC->m_NPCState == NPC_STATE_PRONE))
  249. // return false;
  250. if ( pNPC->IsInAScript() )
  251. return false;
  252. // Don't bother people who don't want to be bothered
  253. if ( !pNPC->CanBeUsedAsAFriend() )
  254. return false;
  255. }
  256. if ( flags & AIST_FACING_TARGET )
  257. {
  258. if ( pEntity->IsPlayer() )
  259. return HasCondition( COND_SEE_PLAYER );
  260. else if ( !FInViewCone( pEntity ) )
  261. return false;
  262. }
  263. return FVisible( pEntity );
  264. }
  265. else
  266. return BaseClass::IsValidSpeechTarget( flags, pEntity );
  267. }
  268. int CHL1NPCTalker::SelectSchedule ( void )
  269. {
  270. switch( m_NPCState )
  271. {
  272. case NPC_STATE_PRONE:
  273. {
  274. if (m_bInBarnacleMouth)
  275. {
  276. return SCHED_HL1TALKER_BARNACLE_CHOMP;
  277. }
  278. else
  279. {
  280. return SCHED_HL1TALKER_BARNACLE_HIT;
  281. }
  282. }
  283. }
  284. return BaseClass::SelectSchedule();
  285. }
  286. void CHL1NPCTalker::Precache()
  287. {
  288. BaseClass::Precache();
  289. PrecacheScriptSound( "Barney.Close" );
  290. }
  291. bool CHL1NPCTalker::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
  292. {
  293. if (interactionType == g_interactionBarnacleVictimDangle)
  294. {
  295. // Force choosing of a new schedule
  296. ClearSchedule( "NPC talker being eaten by a barnacle" );
  297. m_bInBarnacleMouth = true;
  298. return true;
  299. }
  300. else if ( interactionType == g_interactionBarnacleVictimReleased )
  301. {
  302. SetState ( NPC_STATE_IDLE );
  303. CPASAttenuationFilter filter( this );
  304. CSoundParameters params;
  305. if ( GetParametersForSound( "Barney.Close", params, NULL ) )
  306. {
  307. EmitSound_t ep( params );
  308. ep.m_nPitch = GetExpresser()->GetVoicePitch();
  309. EmitSound( filter, entindex(), ep );
  310. }
  311. m_bInBarnacleMouth = false;
  312. SetAbsVelocity( vec3_origin );
  313. SetMoveType( MOVETYPE_STEP );
  314. return true;
  315. }
  316. else if ( interactionType == g_interactionBarnacleVictimGrab )
  317. {
  318. if ( GetFlags() & FL_ONGROUND )
  319. {
  320. SetGroundEntity( NULL );
  321. }
  322. if ( GetState() == NPC_STATE_SCRIPT )
  323. {
  324. if ( m_hCine )
  325. {
  326. m_hCine->CancelScript();
  327. }
  328. }
  329. SetState( NPC_STATE_PRONE );
  330. ClearSchedule( "NPC talker grabbed by a barnacle" );
  331. CTakeDamageInfo info;
  332. PainSound( info );
  333. return true;
  334. }
  335. return false;
  336. }
  337. void CHL1NPCTalker::StartFollowing( CBaseEntity *pLeader )
  338. {
  339. if ( !HasSpawnFlags( SF_NPC_GAG ) )
  340. {
  341. if ( m_iszUse != NULL_STRING )
  342. {
  343. PlaySentence( STRING( m_iszUse ), 0.0f );
  344. }
  345. else
  346. {
  347. Speak( TLK_STARTFOLLOW );
  348. }
  349. SetSpeechTarget( pLeader );
  350. }
  351. BaseClass::StartFollowing( pLeader );
  352. }
  353. int CHL1NPCTalker::PlayScriptedSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener )
  354. {
  355. if( hl1_debug_sentence_volume.GetBool() )
  356. {
  357. Msg( "SENTENCE: %s Vol:%f SndLevel:%d\n", GetDebugName(), volume, soundlevel );
  358. }
  359. if( hl1_fixup_sentence_sndlevel.GetBool() )
  360. {
  361. if( soundlevel < SNDLVL_TALKING )
  362. {
  363. soundlevel = SNDLVL_TALKING;
  364. }
  365. }
  366. return BaseClass::PlayScriptedSentence( pszSentence, delay, volume, soundlevel, bConcurrent, pListener );
  367. }
  368. Disposition_t CHL1NPCTalker::IRelationType( CBaseEntity *pTarget )
  369. {
  370. if ( pTarget->IsPlayer() )
  371. {
  372. if ( HasMemory( bits_MEMORY_PROVOKED ) )
  373. {
  374. return D_HT;
  375. }
  376. }
  377. return BaseClass::IRelationType( pTarget );
  378. }
  379. void CHL1NPCTalker::Touch( CBaseEntity *pOther )
  380. {
  381. if ( m_NPCState == NPC_STATE_SCRIPT )
  382. return;
  383. BaseClass::Touch(pOther);
  384. }
  385. void CHL1NPCTalker::StopFollowing( void )
  386. {
  387. if ( !(m_afMemory & bits_MEMORY_PROVOKED) )
  388. {
  389. if ( !HasSpawnFlags( SF_NPC_GAG ) )
  390. {
  391. if ( m_iszUnUse != NULL_STRING )
  392. {
  393. PlaySentence( STRING( m_iszUnUse ), 0.0f );
  394. }
  395. else
  396. {
  397. Speak( TLK_STOPFOLLOW );
  398. }
  399. SetSpeechTarget( GetFollowTarget() );
  400. }
  401. }
  402. BaseClass::StopFollowing();
  403. }
  404. void CHL1NPCTalker::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  405. {
  406. if ( info.GetDamage() >= 1.0 && !(info.GetDamageType() & DMG_SHOCK ) )
  407. {
  408. UTIL_BloodImpact( ptr->endpos, vecDir, BloodColor(), 4 );
  409. }
  410. BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
  411. }
  412. void CHL1NPCTalker::FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  413. {
  414. // Don't allow use during a scripted_sentence
  415. if ( GetUseTime() > gpGlobals->curtime )
  416. return;
  417. if ( m_hCine && !m_hCine->CanInterrupt() )
  418. return;
  419. if ( pCaller != NULL && pCaller->IsPlayer() )
  420. {
  421. // Pre-disaster followers can't be used
  422. if ( m_spawnflags & SF_NPC_PREDISASTER )
  423. {
  424. SetSpeechTarget( pCaller );
  425. DeclineFollowing();
  426. return;
  427. }
  428. }
  429. BaseClass::FollowerUse( pActivator, pCaller, useType, value );
  430. }
  431. int CHL1NPCTalker::TranslateSchedule( int scheduleType )
  432. {
  433. return BaseClass::TranslateSchedule( scheduleType );
  434. }
  435. float CHL1NPCTalker::PickLookTarget( bool bExcludePlayers, float minTime, float maxTime )
  436. {
  437. return random->RandomFloat( 5.0f, 10.0f );
  438. }
  439. void CHL1NPCTalker::IdleHeadTurn( CBaseEntity *pTarget, float flDuration, float flImportance )
  440. {
  441. // Must be able to turn our head
  442. if (!(CapabilitiesGet() & bits_CAP_TURN_HEAD))
  443. return;
  444. // If the target is invalid, or we're in a script, do nothing
  445. if ( ( !pTarget ) || ( m_NPCState == NPC_STATE_SCRIPT ) )
  446. return;
  447. // Fill in a duration if we haven't specified one
  448. if ( flDuration == 0.0f )
  449. {
  450. flDuration = random->RandomFloat( 2.0, 4.0 );
  451. }
  452. // Add a look target
  453. AddLookTarget( pTarget, 1.0, flDuration );
  454. }
  455. void CHL1NPCTalker::SetHeadDirection( const Vector &vTargetPos, float flInterval)
  456. {
  457. #ifdef TALKER_LOOK
  458. // Draw line in body, head, and eye directions
  459. Vector vEyePos = EyePosition();
  460. Vector vHeadDir = HeadDirection3D();
  461. Vector vBodyDir = BodyDirection2D();
  462. //UNDONE <<TODO>>
  463. // currently eye dir just returns head dir, so use vTargetPos for now
  464. //Vector vEyeDir; w
  465. //EyeDirection3D(&vEyeDir);
  466. NDebugOverlay::Line( vEyePos, vEyePos+(50*vHeadDir), 255, 0, 0, false, 0.1 );
  467. NDebugOverlay::Line( vEyePos, vEyePos+(50*vBodyDir), 0, 255, 0, false, 0.1 );
  468. NDebugOverlay::Line( vEyePos, vTargetPos, 0, 0, 255, false, 0.1 );
  469. #endif
  470. }
  471. //-----------------------------------------------------------------------------
  472. // Purpose:
  473. // Output : Returns true on success, false on failure.
  474. //-----------------------------------------------------------------------------
  475. bool CHL1NPCTalker::CorpseGib( const CTakeDamageInfo &info )
  476. {
  477. CEffectData data;
  478. data.m_vOrigin = WorldSpaceCenter();
  479. data.m_vNormal = data.m_vOrigin - info.GetDamagePosition();
  480. VectorNormalize( data.m_vNormal );
  481. data.m_flScale = RemapVal( m_iHealth, 0, -500, 1, 3 );
  482. data.m_flScale = clamp( data.m_flScale, 1, 3 );
  483. data.m_nMaterial = 1;
  484. data.m_nHitBox = -m_iHealth;
  485. data.m_nColor = BloodColor();
  486. DispatchEffect( "HL1Gib", data );
  487. CSoundEnt::InsertSound( SOUND_MEAT, GetAbsOrigin(), 256, 0.5f, this );
  488. return true;
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Purpose:
  492. //-----------------------------------------------------------------------------
  493. bool CHL1NPCTalker::OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult )
  494. {
  495. // If we can't get through the door, try and open it
  496. if ( BaseClass::OnObstructingDoor( pMoveGoal, pDoor, distClear, pResult ) )
  497. {
  498. if ( IsMoveBlocked( *pResult ) && pMoveGoal->directTrace.vHitNormal != vec3_origin )
  499. {
  500. // Can't do anything if the door's locked
  501. if ( !pDoor->m_bLocked && !pDoor->HasSpawnFlags(SF_DOOR_NONPCS) )
  502. {
  503. // Tell the door to open
  504. variant_t emptyVariant;
  505. pDoor->AcceptInput( "Open", this, this, emptyVariant, USE_TOGGLE );
  506. *pResult = AIMR_OK;
  507. }
  508. }
  509. return true;
  510. }
  511. return false;
  512. }
  513. // HL1 version - never return Ragdoll as the automatic schedule at the end of a
  514. // scripted sequence
  515. int CHL1NPCTalker::SelectDeadSchedule()
  516. {
  517. // Alread dead (by animation event maybe?)
  518. // Is it safe to set it to SCHED_NONE?
  519. if ( m_lifeState == LIFE_DEAD )
  520. return SCHED_NONE;
  521. CleanupOnDeath();
  522. return SCHED_DIE;
  523. }
  524. AI_BEGIN_CUSTOM_NPC( monster_hl1talker, CHL1NPCTalker )
  525. DECLARE_TASK( TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS )
  526. //=========================================================
  527. // > SCHED_HL1TALKER_MOVE_AWAY_FOLLOW
  528. //=========================================================
  529. DEFINE_SCHEDULE
  530. (
  531. SCHED_HL1TALKER_FOLLOW_MOVE_AWAY,
  532. " Tasks"
  533. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_TARGET_FACE"
  534. " TASK_STORE_LASTPOSITION 0"
  535. " TASK_MOVE_AWAY_PATH 100"
  536. " TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS 100"
  537. " TASK_STOP_MOVING 0"
  538. " TASK_FACE_PLAYER 0"
  539. " TASK_SET_ACTIVITY ACT_IDLE"
  540. ""
  541. " Interrupts"
  542. )
  543. //=========================================================
  544. // > SCHED_HL1TALKER_IDLE_SPEAK_WAIT
  545. //=========================================================
  546. DEFINE_SCHEDULE
  547. (
  548. SCHED_HL1TALKER_IDLE_SPEAK_WAIT,
  549. " Tasks"
  550. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" // Stop and talk
  551. " TASK_FACE_PLAYER 0"
  552. ""
  553. " Interrupts"
  554. " COND_NEW_ENEMY"
  555. " COND_LIGHT_DAMAGE"
  556. " COND_HEAVY_DAMAGE"
  557. " COND_HEAR_DANGER"
  558. )
  559. //=========================================================
  560. // > SCHED_HL1TALKER_BARNACLE_HIT
  561. //=========================================================
  562. DEFINE_SCHEDULE
  563. (
  564. SCHED_HL1TALKER_BARNACLE_HIT,
  565. " Tasks"
  566. " TASK_STOP_MOVING 0"
  567. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_HIT"
  568. " TASK_SET_SCHEDULE SCHEDULE:SCHED_HL1TALKER_BARNACLE_PULL"
  569. ""
  570. " Interrupts"
  571. )
  572. //=========================================================
  573. // > SCHED_HL1TALKER_BARNACLE_PULL
  574. //=========================================================
  575. DEFINE_SCHEDULE
  576. (
  577. SCHED_HL1TALKER_BARNACLE_PULL,
  578. " Tasks"
  579. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_PULL"
  580. ""
  581. " Interrupts"
  582. )
  583. //=========================================================
  584. // > SCHED_HL1TALKER_BARNACLE_CHOMP
  585. //=========================================================
  586. DEFINE_SCHEDULE
  587. (
  588. SCHED_HL1TALKER_BARNACLE_CHOMP,
  589. " Tasks"
  590. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_CHOMP"
  591. " TASK_SET_SCHEDULE SCHEDULE:SCHED_HL1TALKER_BARNACLE_CHEW"
  592. ""
  593. " Interrupts"
  594. )
  595. //=========================================================
  596. // > SCHED_HL1TALKER_BARNACLE_CHEW
  597. //=========================================================
  598. DEFINE_SCHEDULE
  599. (
  600. SCHED_HL1TALKER_BARNACLE_CHEW,
  601. " Tasks"
  602. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_CHEW"
  603. )
  604. AI_END_CUSTOM_NPC()