Counter Strike : Global Offensive Source Code
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.

2238 lines
69 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implementation of entities that cause NPCs to participate in
  4. // scripted events. These entities find and temporarily possess NPCs
  5. // within a given search radius.
  6. //
  7. // Multiple scripts with the same targetname will start frame-synchronized.
  8. //
  9. // Scripts will find available NPCs by name or class name and grab them
  10. // to play the script. If the NPC is already playing a script, the
  11. // new script may enqueue itself unless there is already a non critical
  12. // script in the queue.
  13. //
  14. //=============================================================================//
  15. #include "cbase.h"
  16. #include "ai_schedule.h"
  17. #include "ai_default.h"
  18. #include "ai_motor.h"
  19. #include "ai_hint.h"
  20. #include "ai_networkmanager.h"
  21. #include "ai_network.h"
  22. #include "engine/IEngineSound.h"
  23. #include "animation.h"
  24. #include "scripted.h"
  25. #include "entitylist.h"
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. ConVar ai_task_pre_script( "ai_task_pre_script", "0", FCVAR_NONE );
  29. //
  30. // targetname "me" - there can be more than one with the same name, and they act in concert
  31. // target "the_entity_I_want_to_start_playing" or "class entity_classname" will pick the closest inactive scientist
  32. // play "name_of_sequence"
  33. // idle "name of idle sequence to play before starting"
  34. // moveto - if set the NPC first moves to this nodes position
  35. // range # - only search this far to find the target
  36. // spawnflags - (stop if blocked, stop if player seen)
  37. //
  38. BEGIN_DATADESC( CAI_ScriptedSequence )
  39. DEFINE_KEYFIELD( m_iszEntry, FIELD_STRING, "m_iszEntry" ),
  40. DEFINE_KEYFIELD( m_iszPreIdle, FIELD_STRING, "m_iszIdle" ),
  41. DEFINE_KEYFIELD( m_iszPlay, FIELD_STRING, "m_iszPlay" ),
  42. DEFINE_KEYFIELD( m_iszPostIdle, FIELD_STRING, "m_iszPostIdle" ),
  43. DEFINE_KEYFIELD( m_iszCustomMove, FIELD_STRING, "m_iszCustomMove" ),
  44. DEFINE_KEYFIELD( m_iszNextScript, FIELD_STRING, "m_iszNextScript" ),
  45. DEFINE_KEYFIELD( m_iszEntity, FIELD_STRING, "m_iszEntity" ),
  46. DEFINE_KEYFIELD( m_fMoveTo, FIELD_INTEGER, "m_fMoveTo" ),
  47. DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "m_flRadius" ),
  48. DEFINE_KEYFIELD( m_flRepeat, FIELD_FLOAT, "m_flRepeat" ),
  49. DEFINE_FIELD( m_bIsPlayingEntry, FIELD_BOOLEAN ),
  50. DEFINE_KEYFIELD( m_bLoopActionSequence, FIELD_BOOLEAN, "m_bLoopActionSequence" ),
  51. DEFINE_KEYFIELD( m_bSynchPostIdles, FIELD_BOOLEAN, "m_bSynchPostIdles" ),
  52. DEFINE_KEYFIELD( m_bIgnoreGravity, FIELD_BOOLEAN, "m_bIgnoreGravity" ),
  53. DEFINE_KEYFIELD( m_bDisableNPCCollisions, FIELD_BOOLEAN, "m_bDisableNPCCollisions" ),
  54. DEFINE_FIELD( m_iDelay, FIELD_INTEGER ),
  55. DEFINE_FIELD( m_bDelayed, FIELD_BOOLEAN ),
  56. DEFINE_FIELD( m_startTime, FIELD_TIME ),
  57. DEFINE_FIELD( m_bWaitForBeginSequence, FIELD_BOOLEAN ),
  58. DEFINE_FIELD( m_saved_effects, FIELD_INTEGER ),
  59. DEFINE_FIELD( m_savedFlags, FIELD_INTEGER ),
  60. DEFINE_FIELD( m_savedCollisionGroup, FIELD_INTEGER ),
  61. DEFINE_FIELD( m_interruptable, FIELD_BOOLEAN ),
  62. DEFINE_FIELD( m_sequenceStarted, FIELD_BOOLEAN ),
  63. DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ),
  64. DEFINE_FIELD( m_hNextCine, FIELD_EHANDLE ),
  65. DEFINE_FIELD( m_hLastFoundEntity, FIELD_EHANDLE ),
  66. DEFINE_FIELD( m_hForcedTarget, FIELD_EHANDLE ),
  67. DEFINE_FIELD( m_bDontCancelOtherSequences, FIELD_BOOLEAN ),
  68. DEFINE_FIELD( m_bForceSynch, FIELD_BOOLEAN ),
  69. DEFINE_FIELD( m_bThinking, FIELD_BOOLEAN ),
  70. DEFINE_FIELD( m_bInitiatedSelfDelete, FIELD_BOOLEAN ),
  71. DEFINE_FIELD( m_bIsTeleportingDueToMoveTo, FIELD_BOOLEAN ),
  72. DEFINE_FIELD( m_matInteractionPosition, FIELD_VMATRIX ),
  73. DEFINE_FIELD( m_hInteractionRelativeEntity, FIELD_EHANDLE ),
  74. DEFINE_FIELD( m_bTargetWasAsleep, FIELD_BOOLEAN ),
  75. // Function Pointers
  76. DEFINE_THINKFUNC( ScriptThink ),
  77. // Inputs
  78. DEFINE_INPUTFUNC( FIELD_VOID, "MoveToPosition", InputMoveToPosition ),
  79. DEFINE_INPUTFUNC( FIELD_VOID, "BeginSequence", InputBeginSequence ),
  80. DEFINE_INPUTFUNC( FIELD_VOID, "CancelSequence", InputCancelSequence ),
  81. DEFINE_KEYFIELD( m_iPlayerDeathBehavior, FIELD_INTEGER, "onplayerdeath" ),
  82. DEFINE_INPUTFUNC( FIELD_VOID, "ScriptPlayerDeath", InputScriptPlayerDeath ),
  83. // Outputs
  84. DEFINE_OUTPUT(m_OnBeginSequence, "OnBeginSequence"),
  85. DEFINE_OUTPUT(m_OnEndSequence, "OnEndSequence"),
  86. DEFINE_OUTPUT(m_OnPostIdleEndSequence, "OnPostIdleEndSequence"),
  87. DEFINE_OUTPUT(m_OnCancelSequence, "OnCancelSequence"),
  88. DEFINE_OUTPUT(m_OnCancelFailedSequence, "OnCancelFailedSequence"),
  89. DEFINE_OUTPUT(m_OnScriptEvent[0], "OnScriptEvent01"),
  90. DEFINE_OUTPUT(m_OnScriptEvent[1], "OnScriptEvent02"),
  91. DEFINE_OUTPUT(m_OnScriptEvent[2], "OnScriptEvent03"),
  92. DEFINE_OUTPUT(m_OnScriptEvent[3], "OnScriptEvent04"),
  93. DEFINE_OUTPUT(m_OnScriptEvent[4], "OnScriptEvent05"),
  94. DEFINE_OUTPUT(m_OnScriptEvent[5], "OnScriptEvent06"),
  95. DEFINE_OUTPUT(m_OnScriptEvent[6], "OnScriptEvent07"),
  96. DEFINE_OUTPUT(m_OnScriptEvent[7], "OnScriptEvent08"),
  97. END_DATADESC()
  98. LINK_ENTITY_TO_CLASS( scripted_sequence, CAI_ScriptedSequence );
  99. #define CLASSNAME "scripted_sequence"
  100. //-----------------------------------------------------------------------------
  101. // Purpose: Cancels the given scripted sequence.
  102. // Input : pentCine -
  103. //-----------------------------------------------------------------------------
  104. void CAI_ScriptedSequence::ScriptEntityCancel( CBaseEntity *pentCine, bool bPretendSuccess )
  105. {
  106. // make sure they are a scripted_sequence
  107. if ( FClassnameIs( pentCine, CLASSNAME ) )
  108. {
  109. CAI_ScriptedSequence *pCineTarget = (CAI_ScriptedSequence *)pentCine;
  110. // make sure they have a NPC in mind for the script
  111. CBaseEntity *pEntity = pCineTarget->GetTarget();
  112. CAI_BaseNPC *pTarget = NULL;
  113. if ( pEntity )
  114. pTarget = pEntity->MyNPCPointer();
  115. if (pTarget)
  116. {
  117. // make sure their NPC is actually playing a script
  118. if ( pTarget->m_NPCState == NPC_STATE_SCRIPT )
  119. {
  120. // tell them do die
  121. pTarget->m_scriptState = CAI_BaseNPC::SCRIPT_CLEANUP;
  122. // We have to save off the flags here, because the NPC's m_hCine is cleared in CineCleanup()
  123. int iSavedFlags = (pTarget->m_hCine ? pTarget->m_hCine->m_savedFlags : 0);
  124. // do it now
  125. pTarget->CineCleanup( );
  126. pCineTarget->FixScriptNPCSchedule( pTarget, iSavedFlags );
  127. }
  128. else
  129. {
  130. // Robin HACK: If a script is started and then cancelled before an NPC gets to
  131. // think, we have to manually clear it out of scripted state, or it'll never recover.
  132. pCineTarget->SetTarget( NULL );
  133. pTarget->SetEffects( pCineTarget->m_saved_effects );
  134. pTarget->m_hCine = NULL;
  135. pTarget->SetTarget( NULL );
  136. pTarget->SetGoalEnt( NULL );
  137. pTarget->SetIdealState( NPC_STATE_IDLE );
  138. }
  139. }
  140. // FIXME: this needs to be done in a cine cleanup function
  141. pCineTarget->m_iDelay = 0;
  142. if ( bPretendSuccess )
  143. {
  144. // We need to pretend that this sequence actually finished fully
  145. pCineTarget->m_OnEndSequence.FireOutput(NULL, pCineTarget);
  146. pCineTarget->m_OnPostIdleEndSequence.FireOutput(NULL, pCineTarget);
  147. }
  148. else
  149. {
  150. // Fire the cancel
  151. pCineTarget->m_OnCancelSequence.FireOutput(NULL, pCineTarget);
  152. if ( pCineTarget->m_startTime == 0 )
  153. {
  154. // If start time is 0, this sequence never actually ran. Fire the failed output.
  155. pCineTarget->m_OnCancelFailedSequence.FireOutput(NULL, pCineTarget);
  156. }
  157. }
  158. }
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Purpose: Called before spawning, after keyvalues have been parsed.
  162. //-----------------------------------------------------------------------------
  163. void CAI_ScriptedSequence::Spawn( void )
  164. {
  165. SetSolid( SOLID_NONE );
  166. //
  167. // If we have no name or we are set to start immediately, find the NPC and
  168. // have them move to their script position now.
  169. //
  170. if ( !GetEntityName() || ( m_spawnflags & SF_SCRIPT_START_ON_SPAWN ) )
  171. {
  172. StartThink();
  173. SetNextThink( gpGlobals->curtime + 1.0f );
  174. //
  175. // If we have a name, wait for a BeginSequence input to play the
  176. // action animation. Otherwise, we'll play the action animation
  177. // as soon as the NPC reaches the script position.
  178. //
  179. if ( GetEntityName() != NULL_STRING )
  180. {
  181. m_bWaitForBeginSequence = true;
  182. }
  183. }
  184. if ( m_spawnflags & SF_SCRIPT_NOINTERRUPT )
  185. {
  186. m_interruptable = false;
  187. }
  188. else
  189. {
  190. m_interruptable = true;
  191. }
  192. m_sequenceStarted = false;
  193. m_startTime = 0;
  194. m_hNextCine = NULL;
  195. m_hLastFoundEntity = NULL;
  196. }
  197. //-----------------------------------------------------------------------------
  198. void CAI_ScriptedSequence::UpdateOnRemove(void)
  199. {
  200. ScriptEntityCancel( this );
  201. BaseClass::UpdateOnRemove();
  202. }
  203. //-----------------------------------------------------------------------------
  204. void CAI_ScriptedSequence::StartThink()
  205. {
  206. m_sequenceStarted = false;
  207. m_bThinking = true;
  208. SetThink( &CAI_ScriptedSequence::ScriptThink );
  209. }
  210. //-----------------------------------------------------------------------------
  211. void CAI_ScriptedSequence::StopThink()
  212. {
  213. if ( m_bThinking )
  214. {
  215. Assert( !m_bInitiatedSelfDelete );
  216. SetThink( NULL);
  217. m_bThinking = false;
  218. }
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: Returns true if this scripted sequence can possess entities
  222. // regardless of state.
  223. //-----------------------------------------------------------------------------
  224. bool CAI_ScriptedSequence::FCanOverrideState( void )
  225. {
  226. if ( m_spawnflags & SF_SCRIPT_OVERRIDESTATE )
  227. return true;
  228. return false;
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose: Fires a script event by number.
  232. // Input : nEvent - One based index of the script event from the , from 1 to 8.
  233. //-----------------------------------------------------------------------------
  234. void CAI_ScriptedSequence::FireScriptEvent( int nEvent )
  235. {
  236. if ( ( nEvent >= 1 ) && ( nEvent <= MAX_SCRIPT_EVENTS ) )
  237. {
  238. m_OnScriptEvent[nEvent - 1].FireOutput( this, this );
  239. }
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Purpose: Input handler that causes the NPC to move to the script position.
  243. //-----------------------------------------------------------------------------
  244. void CAI_ScriptedSequence::InputMoveToPosition( inputdata_t &inputdata )
  245. {
  246. if ( m_bInitiatedSelfDelete )
  247. return;
  248. // Have I already grabbed an NPC?
  249. CBaseEntity *pEntity = GetTarget();
  250. CAI_BaseNPC *pTarget = NULL;
  251. if ( pEntity )
  252. {
  253. pTarget = pEntity->MyNPCPointer();
  254. }
  255. if ( pTarget != NULL )
  256. {
  257. // Yes, are they already playing this script?
  258. if ( pTarget->m_scriptState == CAI_BaseNPC::SCRIPT_PLAYING || pTarget->m_scriptState == CAI_BaseNPC::SCRIPT_POST_IDLE )
  259. {
  260. // Yes, see if we can enqueue ourselves.
  261. if ( pTarget->CanPlaySequence( FCanOverrideState(), SS_INTERRUPT_BY_NAME ) )
  262. {
  263. StartScript();
  264. m_bWaitForBeginSequence = true;
  265. }
  266. }
  267. // No, presumably they are moving to position or waiting for the BeginSequence input.
  268. }
  269. else
  270. {
  271. // No, grab the NPC but make them wait until BeginSequence is fired. They'll play
  272. // their pre-action idle animation until BeginSequence is fired.
  273. StartThink();
  274. SetNextThink( gpGlobals->curtime );
  275. m_bWaitForBeginSequence = true;
  276. }
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Purpose: Input handler that activates the scripted sequence.
  280. //-----------------------------------------------------------------------------
  281. void CAI_ScriptedSequence::InputBeginSequence( inputdata_t &inputdata )
  282. {
  283. if ( m_bInitiatedSelfDelete )
  284. return;
  285. // Start the script as soon as possible.
  286. m_bWaitForBeginSequence = false;
  287. // do I already know who I should use?
  288. CBaseEntity *pEntity = GetTarget();
  289. CAI_BaseNPC *pTarget = NULL;
  290. if ( !pEntity && m_hForcedTarget )
  291. {
  292. if ( FindEntity() )
  293. {
  294. pEntity = GetTarget();
  295. }
  296. }
  297. if ( pEntity )
  298. {
  299. pTarget = pEntity->MyNPCPointer();
  300. }
  301. if ( pTarget )
  302. {
  303. // Are they already playing a script?
  304. if ( pTarget->m_scriptState == CAI_BaseNPC::SCRIPT_PLAYING || pTarget->m_scriptState == CAI_BaseNPC::SCRIPT_POST_IDLE )
  305. {
  306. // See if we can enqueue ourselves after the current script.
  307. if ( pTarget->CanPlaySequence( FCanOverrideState(), SS_INTERRUPT_BY_NAME ) )
  308. {
  309. StartScript();
  310. }
  311. }
  312. }
  313. else
  314. {
  315. // if not, try finding them
  316. StartThink();
  317. // Because we are directly calling the new "think" function ScriptThink, assume we're done
  318. // This fixes the following bug (along with the WokeThisTick() code herein:
  319. // A zombie is created in the asleep state and then, the mapper fires both Wake and BeginSequence
  320. // messages to have it jump up out of the slime, e.g. What happens before this change is that
  321. // the Wake code removed EF_NODRAW, but so the zombie is transmitted to the client, but the script
  322. // hasn't started and won't start until the next Think time (2 ticks on xbox) at which time the
  323. // actual sequence starts causing the zombie to quickly lie down.
  324. // The changes here are to track what tick we "awoke" on and get rid of the lag between Wake and
  325. // ScriptThink by actually calling ScriptThink directly on the same frame and checking for the
  326. // zombie having woken up and been instructed to play a sequence in the same frame.
  327. SetNextThink( TICK_NEVER_THINK );
  328. ScriptThink();
  329. }
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose: Input handler that activates the scripted sequence.
  333. //-----------------------------------------------------------------------------
  334. void CAI_ScriptedSequence::InputCancelSequence( inputdata_t &inputdata )
  335. {
  336. if ( m_bInitiatedSelfDelete )
  337. return;
  338. //
  339. // We don't call CancelScript because entity I/O will handle dispatching
  340. // this input to all other scripts with our same name.
  341. //
  342. DevMsg( 2, "InputCancelScript: Cancelling script '%s'\n", STRING( m_iszPlay ));
  343. StopThink();
  344. ScriptEntityCancel( this );
  345. }
  346. void CAI_ScriptedSequence::InputScriptPlayerDeath( inputdata_t &inputdata )
  347. {
  348. if ( m_iPlayerDeathBehavior == SCRIPT_CANCEL )
  349. {
  350. if ( m_bInitiatedSelfDelete )
  351. return;
  352. //
  353. // We don't call CancelScript because entity I/O will handle dispatching
  354. // this input to all other scripts with our same name.
  355. //
  356. DevMsg( 2, "InputCancelScript: Cancelling script '%s'\n", STRING( m_iszPlay ));
  357. StopThink();
  358. ScriptEntityCancel( this );
  359. }
  360. }
  361. //-----------------------------------------------------------------------------
  362. // Purpose: Returns true if it is time for this script to start, false if the
  363. // NPC should continue waiting.
  364. //
  365. // Scripts wait for two reasons:
  366. //
  367. // 1. To frame-syncronize with other scripts of the same name.
  368. // 2. To wait indefinitely for the BeginSequence input after the NPC
  369. // moves to the script position.
  370. //-----------------------------------------------------------------------------
  371. bool CAI_ScriptedSequence::IsTimeToStart( void )
  372. {
  373. Assert( !m_bWaitForBeginSequence );
  374. return ( m_iDelay == 0 );
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose: Returns true if the script is still waiting to call StartScript()
  378. //-----------------------------------------------------------------------------
  379. bool CAI_ScriptedSequence::IsWaitingForBegin( void )
  380. {
  381. return m_bWaitForBeginSequence;
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose: This doesn't really make sense since only MOVETYPE_PUSH get 'Blocked' events
  385. // Input : pOther - The entity blocking us.
  386. //-----------------------------------------------------------------------------
  387. void CAI_ScriptedSequence::Blocked( CBaseEntity *pOther )
  388. {
  389. }
  390. //-----------------------------------------------------------------------------
  391. // Purpose:
  392. // Input : pOther - The entity touching us.
  393. //-----------------------------------------------------------------------------
  394. void CAI_ScriptedSequence::Touch( CBaseEntity *pOther )
  395. {
  396. /*
  397. DevMsg( 2, "Cine Touch\n" );
  398. if (m_pentTarget && OFFSET(pOther->pev) == OFFSET(m_pentTarget))
  399. {
  400. CAI_BaseNPC *pTarget = GetClassPtr((CAI_BaseNPC *)VARS(m_pentTarget));
  401. pTarget->m_NPCState == NPC_STATE_SCRIPT;
  402. }
  403. */
  404. }
  405. //-----------------------------------------------------------------------------
  406. // Purpose:
  407. //-----------------------------------------------------------------------------
  408. void CAI_ScriptedSequence::Die( void )
  409. {
  410. SetThink( &CAI_ScriptedSequence::SUB_Remove );
  411. m_bThinking = false;
  412. m_bInitiatedSelfDelete = true;
  413. }
  414. //-----------------------------------------------------------------------------
  415. // Purpose:
  416. //-----------------------------------------------------------------------------
  417. void CAI_ScriptedSequence::Pain( void )
  418. {
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Purpose:
  422. // Input : eMode -
  423. // Output : Returns true on success, false on failure.
  424. //-----------------------------------------------------------------------------
  425. CAI_BaseNPC *CAI_ScriptedSequence::FindScriptEntity( )
  426. {
  427. CAI_BaseNPC *pEnqueueNPC = NULL;
  428. CBaseEntity *pEntity;
  429. int interrupt;
  430. if ( m_hForcedTarget )
  431. {
  432. interrupt = SS_INTERRUPT_BY_NAME;
  433. pEntity = m_hForcedTarget;
  434. }
  435. else
  436. {
  437. interrupt = SS_INTERRUPT_BY_NAME;
  438. pEntity = gEntList.FindEntityByNameWithin( m_hLastFoundEntity, STRING( m_iszEntity ), GetAbsOrigin(), m_flRadius );
  439. if (!pEntity)
  440. {
  441. pEntity = gEntList.FindEntityByClassnameWithin( m_hLastFoundEntity, STRING( m_iszEntity ), GetAbsOrigin(), m_flRadius );
  442. interrupt = SS_INTERRUPT_BY_CLASS;
  443. }
  444. }
  445. while ( pEntity != NULL )
  446. {
  447. CAI_BaseNPC *pNPC = pEntity->MyNPCPointer( );
  448. if ( pNPC )
  449. {
  450. //
  451. // If they can play the sequence...
  452. //
  453. CanPlaySequence_t eCanPlay = pNPC->CanPlaySequence( FCanOverrideState(), interrupt );
  454. if ( eCanPlay == CAN_PLAY_NOW )
  455. {
  456. // If they can play it now, we're done!
  457. return pNPC;
  458. }
  459. else if ( eCanPlay == CAN_PLAY_ENQUEUED )
  460. {
  461. // They can play it, but only enqueued. We'll use them as a last resort.
  462. pEnqueueNPC = pNPC;
  463. }
  464. else if (!(m_spawnflags & SF_SCRIPT_NO_COMPLAINTS))
  465. {
  466. // They cannot play the script.
  467. DevMsg( "Found %s, but can't play!\n", STRING( m_iszEntity ));
  468. }
  469. }
  470. if ( m_hForcedTarget )
  471. {
  472. Warning( "Code forced %s(%s), to be the target of scripted sequence %s, but it can't play it.\n",
  473. pEntity->GetClassname(), pEntity->GetDebugName(), GetDebugName() );
  474. pEntity = NULL;
  475. UTIL_Remove( this );
  476. return NULL;
  477. }
  478. else
  479. {
  480. if ( interrupt == SS_INTERRUPT_BY_NAME )
  481. pEntity = gEntList.FindEntityByNameWithin( pEntity, STRING( m_iszEntity ), GetAbsOrigin(), m_flRadius );
  482. else
  483. pEntity = gEntList.FindEntityByClassnameWithin( pEntity, STRING( m_iszEntity ), GetAbsOrigin(), m_flRadius );
  484. }
  485. }
  486. //
  487. // If we found an NPC that will enqueue the script, use them.
  488. //
  489. if ( pEnqueueNPC != NULL )
  490. {
  491. return pEnqueueNPC;
  492. }
  493. return NULL;
  494. }
  495. //-----------------------------------------------------------------------------
  496. // Purpose:
  497. // Output : Returns true on success, false on failure.
  498. //-----------------------------------------------------------------------------
  499. bool CAI_ScriptedSequence::FindEntity( void )
  500. {
  501. CAI_BaseNPC *pTarget = FindScriptEntity( );
  502. if ( (m_spawnflags & SF_SCRIPT_SEARCH_CYCLICALLY))
  503. {
  504. // next time this is called, start searching from the one found last time
  505. m_hLastFoundEntity = pTarget;
  506. }
  507. SetTarget( pTarget );
  508. return pTarget != NULL;
  509. }
  510. //-----------------------------------------------------------------------------
  511. // Purpose: Make the entity enter a scripted sequence.
  512. //-----------------------------------------------------------------------------
  513. void CAI_ScriptedSequence::StartScript( void )
  514. {
  515. CBaseEntity *pEntity = GetTarget();
  516. CAI_BaseNPC *pTarget = NULL;
  517. if ( pEntity )
  518. pTarget = pEntity->MyNPCPointer();
  519. if ( pTarget )
  520. {
  521. pTarget->RemoveSpawnFlags( SF_NPC_WAIT_FOR_SCRIPT );
  522. //
  523. // If the NPC is in another script, just enqueue ourselves and bail out.
  524. // We'll possess the NPC when the current script finishes with the NPC.
  525. // Note that we only enqueue one deep currently, so if there is someone
  526. // else in line we'll stomp them.
  527. //
  528. if ( pTarget->m_hCine != NULL )
  529. {
  530. if ( pTarget->m_hCine->m_hNextCine != NULL )
  531. {
  532. //
  533. // Kicking another script out of the queue.
  534. //
  535. CAI_ScriptedSequence *pCine = ( CAI_ScriptedSequence * )pTarget->m_hCine->m_hNextCine.Get();
  536. if (pTarget->m_hCine->m_hNextCine != pTarget->m_hCine)
  537. {
  538. // Don't clear the currently playing script's target!
  539. pCine->SetTarget( NULL );
  540. }
  541. DevMsg( 2, "script \"%s\" kicking script \"%s\" out of the queue\n", GetDebugName(), pCine->GetDebugName() );
  542. }
  543. pTarget->m_hCine->m_hNextCine = this;
  544. return;
  545. }
  546. //
  547. // If no next script is specified, clear it out so other scripts can enqueue themselves
  548. // after us.
  549. //
  550. if ( !m_iszNextScript )
  551. {
  552. m_hNextCine = NULL;
  553. }
  554. // UNDONE: Do this to sync up multi-entity scripts?
  555. //pTarget->SetNextThink( gpGlobals->curtime );
  556. pTarget->SetGoalEnt( this );
  557. pTarget->ForceDecisionThink();
  558. pTarget->m_hCine = this;
  559. pTarget->SetTarget( this );
  560. // Notify the NPC tat we're stomping them into a scene!
  561. pTarget->OnStartScene();
  562. {
  563. m_bTargetWasAsleep = ( pTarget->GetSleepState() != AISS_AWAKE ) ? true : false;
  564. bool justAwoke = pTarget->WokeThisTick();
  565. if ( m_bTargetWasAsleep || justAwoke )
  566. {
  567. // Note, Wake() will remove the EF_NODRAW flag, but if we are starting a seq on a hidden entity
  568. // we don't want it to draw on the client until the sequence actually starts to play
  569. // Make sure it stays hidden!!!
  570. if ( m_bTargetWasAsleep )
  571. {
  572. pTarget->Wake();
  573. }
  574. m_bTargetWasAsleep = true;
  575. // Even if awakened this frame, temporarily keep the entity hidden for now
  576. pTarget->AddEffects( EF_NODRAW );
  577. }
  578. }
  579. // If the entity was asleep at the start, make sure we don't make it invisible
  580. // AFTER the script finishes (can't think of a case where you'd want that to happen)
  581. m_saved_effects = pTarget->GetEffects() & ~EF_NODRAW;
  582. pTarget->AddEffects( GetEffects() );
  583. m_savedFlags = pTarget->GetFlags();
  584. m_savedCollisionGroup = pTarget->GetCollisionGroup();
  585. if ( m_bDisableNPCCollisions )
  586. {
  587. pTarget->SetCollisionGroup( COLLISION_GROUP_NPC_SCRIPTED );
  588. }
  589. switch (m_fMoveTo)
  590. {
  591. case CINE_MOVETO_WAIT:
  592. case CINE_MOVETO_WAIT_FACING:
  593. pTarget->m_scriptState = CAI_BaseNPC::SCRIPT_WAIT;
  594. if ( m_bIgnoreGravity )
  595. {
  596. pTarget->AddFlag( FL_FLY );
  597. pTarget->SetGroundEntity( NULL );
  598. }
  599. break;
  600. case CINE_MOVETO_WALK:
  601. pTarget->m_scriptState = CAI_BaseNPC::SCRIPT_WALK_TO_MARK;
  602. break;
  603. case CINE_MOVETO_RUN:
  604. pTarget->m_scriptState = CAI_BaseNPC::SCRIPT_RUN_TO_MARK;
  605. break;
  606. case CINE_MOVETO_CUSTOM:
  607. pTarget->m_scriptState = CAI_BaseNPC::SCRIPT_CUSTOM_MOVE_TO_MARK;
  608. break;
  609. case CINE_MOVETO_TELEPORT:
  610. m_bIsTeleportingDueToMoveTo = true;
  611. pTarget->Teleport( &GetAbsOrigin(), NULL, &vec3_origin );
  612. m_bIsTeleportingDueToMoveTo = false;
  613. pTarget->GetMotor()->SetIdealYaw( GetLocalAngles().y );
  614. pTarget->SetLocalAngularVelocity( vec3_angle );
  615. pTarget->AddEffects( EF_NOINTERP );
  616. QAngle angles = pTarget->GetLocalAngles();
  617. angles.y = GetLocalAngles().y;
  618. pTarget->SetLocalAngles( angles );
  619. pTarget->m_scriptState = CAI_BaseNPC::SCRIPT_WAIT;
  620. if ( m_bIgnoreGravity )
  621. {
  622. pTarget->AddFlag( FL_FLY );
  623. pTarget->SetGroundEntity( NULL );
  624. }
  625. // UNDONE: Add a flag to do this so people can fixup physics after teleporting NPCs
  626. //pTarget->SetGroundEntity( NULL );
  627. break;
  628. }
  629. //DevMsg( 2, "\"%s\" found and used (INT: %s)\n", STRING( pTarget->m_iName ), FBitSet(m_spawnflags, SF_SCRIPT_NOINTERRUPT)?"No":"Yes" );
  630. // Wait until all scripts of the same name are ready to play.
  631. m_bDelayed = false;
  632. DelayStart( true );
  633. pTarget->SetIdealState(NPC_STATE_SCRIPT);
  634. // FIXME: not sure why this is happening, or what to do about truely dormant NPCs
  635. if ( pTarget->IsEFlagSet( EFL_NO_THINK_FUNCTION ) && pTarget->GetNextThink() != TICK_NEVER_THINK )
  636. {
  637. DevWarning( "scripted_sequence %d:%s - restarting dormant entity %d:%s : %.1f:%.1f\n", entindex(), GetDebugName(), pTarget->entindex(), pTarget->GetDebugName(), gpGlobals->curtime, pTarget->GetNextThink() );
  638. pTarget->SetNextThink( gpGlobals->curtime );
  639. }
  640. }
  641. }
  642. //-----------------------------------------------------------------------------
  643. // Purpose: First think after activation. Grabs an NPC and makes it do things.
  644. //-----------------------------------------------------------------------------
  645. void CAI_ScriptedSequence::ScriptThink( void )
  646. {
  647. if ( g_pAINetworkManager && !g_pAINetworkManager->IsInitialized() )
  648. {
  649. SetNextThink( gpGlobals->curtime + 0.1f );
  650. }
  651. else if (FindEntity())
  652. {
  653. StartScript( );
  654. DevMsg( 2, "scripted_sequence %d:\"%s\" using NPC %d:\"%s\"(%s)\n", entindex(), GetDebugName(), GetTarget()->entindex(), GetTarget()->GetEntityName().ToCStr(), STRING( m_iszEntity ) );
  655. }
  656. else
  657. {
  658. CancelScript( );
  659. DevMsg( 2, "scripted_sequence %d:\"%s\" can't find NPC \"%s\"\n", entindex(), GetDebugName(), STRING( m_iszEntity ) );
  660. // FIXME: just trying again is bad. This should fire an output instead.
  661. // FIXME: Think about puting output triggers in both StartScript() and CancelScript().
  662. SetNextThink( gpGlobals->curtime + 1.0f );
  663. }
  664. }
  665. //-----------------------------------------------------------------------------
  666. // Purpose: Callback for firing the begin sequence output. Called by the NPC that
  667. // is running the script as it starts the action seqeunce.
  668. //-----------------------------------------------------------------------------
  669. void CAI_ScriptedSequence::OnBeginSequence( void )
  670. {
  671. m_OnBeginSequence.FireOutput( this, this );
  672. }
  673. //-----------------------------------------------------------------------------
  674. // Purpose: Look up a sequence name and setup the target NPC to play it.
  675. // Input : pTarget -
  676. // iszSeq -
  677. // completeOnEmpty -
  678. // Output : Returns true on success, false on failure.
  679. //-----------------------------------------------------------------------------
  680. bool CAI_ScriptedSequence::StartSequence( CAI_BaseNPC *pTarget, string_t iszSeq, bool completeOnEmpty )
  681. {
  682. Assert( pTarget );
  683. m_sequenceStarted = true;
  684. m_bIsPlayingEntry = (iszSeq == m_iszEntry);
  685. if ( !iszSeq && completeOnEmpty )
  686. {
  687. SequenceDone( pTarget );
  688. return false;
  689. }
  690. int nSequence = pTarget->LookupSequence( STRING( iszSeq ) );
  691. if (nSequence == -1)
  692. {
  693. Warning( "%s: unknown scripted sequence \"%s\"\n", pTarget->GetDebugName(), STRING( iszSeq ));
  694. nSequence = 0;
  695. }
  696. // look for the activity that this represents
  697. Activity act = pTarget->GetSequenceActivity( nSequence );
  698. if (act == ACT_INVALID)
  699. act = ACT_IDLE;
  700. pTarget->SetActivityAndSequence( act, nSequence, act, act );
  701. // If the target was hidden even though we woke it up, only make it drawable if we're not still on the preidle seq...
  702. if ( m_bTargetWasAsleep &&
  703. iszSeq != m_iszPreIdle )
  704. {
  705. m_bTargetWasAsleep = false;
  706. // Show it
  707. pTarget->RemoveEffects( EF_NODRAW );
  708. // Don't blend...
  709. pTarget->AddEffects( EF_NOINTERP );
  710. }
  711. //DevMsg( 2, "%s (%s): started \"%s\":INT:%s\n", STRING( pTarget->m_iName ), pTarget->GetClassname(), STRING( iszSeq), (m_spawnflags & SF_SCRIPT_NOINTERRUPT) ? "No" : "Yes" );
  712. return true;
  713. }
  714. //-----------------------------------------------------------------------------
  715. // Purpose: Called when a scripted sequence is ready to start playing the sequence
  716. // Input : pNPC - Pointer to the NPC that the sequence possesses.
  717. //-----------------------------------------------------------------------------
  718. void CAI_ScriptedSequence::SynchronizeSequence( CAI_BaseNPC *pNPC )
  719. {
  720. //Msg("%s (for %s) called SynchronizeSequence() at %0.2f\n", GetTarget()->GetDebugName(), GetDebugName(), gpGlobals->curtime);
  721. Assert( m_iDelay == 0 );
  722. Assert( m_bWaitForBeginSequence == false );
  723. m_bForceSynch = false;
  724. // Reset cycle position
  725. float flCycleRate = pNPC->GetSequenceCycleRate( pNPC->GetSequence() );
  726. float flInterval = gpGlobals->curtime - m_startTime;
  727. // Msg("%.2f \"%s\" %s : %f (%f): interval %f\n", gpGlobals->curtime, GetEntityName().ToCStr(), pNPC->GetClassname(), pNPC->m_flAnimTime.Get(), m_startTime, flInterval );
  728. //Assert( flInterval >= 0.0 && flInterval <= 0.15 );
  729. flInterval = clamp( flInterval, 0, 0.15 );
  730. if (flInterval == 0)
  731. return;
  732. // do the movement for the missed portion of the sequence
  733. pNPC->SetCycle( 0.0f );
  734. pNPC->AutoMovement( flInterval );
  735. // reset the cycle to a common basis
  736. pNPC->SetCycle( flInterval * flCycleRate );
  737. }
  738. //-----------------------------------------------------------------------------
  739. // Purpose: Moves to the next action sequence if the scripted_sequence wants to,
  740. // or returns true if it wants to leave the action sequence
  741. //-----------------------------------------------------------------------------
  742. bool CAI_ScriptedSequence::FinishedActionSequence( CAI_BaseNPC *pNPC )
  743. {
  744. // Restart the action sequence when the entry finishes, or when the action
  745. // finishes and we're set to loop it.
  746. if ( IsPlayingEntry() )
  747. {
  748. if ( GetEntityName() != NULL_STRING )
  749. {
  750. // Force all matching ss's to synchronize their action sequences
  751. SynchNewSequence( CAI_BaseNPC::SCRIPT_PLAYING, m_iszPlay, true );
  752. }
  753. else
  754. {
  755. StartSequence( pNPC, m_iszPlay, true );
  756. }
  757. return false;
  758. }
  759. // Let the core action sequence continue to loop
  760. if ( ShouldLoopActionSequence() )
  761. {
  762. // If the NPC has reached post idle state, we need to stop looping the action sequence
  763. if ( pNPC->m_scriptState == CAI_BaseNPC::SCRIPT_POST_IDLE )
  764. return true;
  765. return false;
  766. }
  767. return true;
  768. }
  769. //-----------------------------------------------------------------------------
  770. // Purpose: Called when a scripted sequence animation sequence is done playing
  771. // (or when an AI Scripted Sequence doesn't supply an animation sequence
  772. // to play).
  773. // Input : pNPC - Pointer to the NPC that the sequence possesses.
  774. //-----------------------------------------------------------------------------
  775. void CAI_ScriptedSequence::SequenceDone( CAI_BaseNPC *pNPC )
  776. {
  777. //DevMsg( 2, "Sequence %s finished\n", STRING( pNPC->m_hCine->m_iszPlay ) );
  778. //Msg("%s SequenceDone() at %0.2f\n", pNPC->GetDebugName(), gpGlobals->curtime );
  779. // If we're part of a synchronised post-idle sequence, we need to do things differently
  780. if ( m_bSynchPostIdles && GetEntityName() != NULL_STRING )
  781. {
  782. // If we're already in POST_IDLE state, then one of the other scripted
  783. // sequences we're synching with has already kicked us into running
  784. // the post idle sequence, so we do nothing.
  785. if ( pNPC->m_scriptState != CAI_BaseNPC::SCRIPT_POST_IDLE )
  786. {
  787. if ( ( m_iszPostIdle != NULL_STRING ) && ( m_hNextCine == NULL ) )
  788. {
  789. SynchNewSequence( CAI_BaseNPC::SCRIPT_POST_IDLE, m_iszPostIdle, true );
  790. }
  791. else
  792. {
  793. PostIdleDone( pNPC );
  794. }
  795. }
  796. }
  797. else
  798. {
  799. //
  800. // If we have a post idle set, and no other script is in the queue for this
  801. // NPC, start playing the idle now.
  802. //
  803. if ( ( m_iszPostIdle != NULL_STRING ) && ( m_hNextCine == NULL ) )
  804. {
  805. //
  806. // First time we've gotten here for this script. Start playing the post idle
  807. // if one is specified.
  808. //
  809. pNPC->m_scriptState = CAI_BaseNPC::SCRIPT_POST_IDLE;
  810. StartSequence( pNPC, m_iszPostIdle, false ); // false to prevent recursion here!
  811. }
  812. else
  813. {
  814. PostIdleDone( pNPC );
  815. }
  816. }
  817. m_OnEndSequence.FireOutput(NULL, this);
  818. }
  819. //-----------------------------------------------------------------------------
  820. // Purpose:
  821. //-----------------------------------------------------------------------------
  822. void CAI_ScriptedSequence::SynchNewSequence( CAI_BaseNPC::SCRIPTSTATE newState, string_t iszSequence, bool bSynchOtherScenes )
  823. {
  824. // Do we need to synchronize all our matching scripted scenes?
  825. if ( bSynchOtherScenes )
  826. {
  827. //Msg("%s (for %s) forcing synch of %s at %0.2f\n", GetTarget()->GetDebugName(), GetDebugName(), iszSequence, gpGlobals->curtime);
  828. CBaseEntity *pentCine = gEntList.FindEntityByName( NULL, GetEntityName(), NULL );
  829. while ( pentCine )
  830. {
  831. CAI_ScriptedSequence *pScene = dynamic_cast<CAI_ScriptedSequence *>(pentCine);
  832. if ( pScene && pScene != this )
  833. {
  834. pScene->SynchNewSequence( newState, iszSequence, false );
  835. }
  836. pentCine = gEntList.FindEntityByName( pentCine, GetEntityName(), NULL );
  837. }
  838. }
  839. // Now force this one to start the post idle?
  840. if ( !GetTarget() )
  841. return;
  842. CAI_BaseNPC *pNPC = GetTarget()->MyNPCPointer();
  843. if ( !pNPC )
  844. return;
  845. //Msg("%s (for %s) starting %s seq at %0.2f\n", pNPC->GetDebugName(), GetDebugName(), iszSequence, gpGlobals->curtime);
  846. m_startTime = gpGlobals->curtime;
  847. pNPC->m_scriptState = newState;
  848. StartSequence( pNPC, iszSequence, false );
  849. // Force the NPC to synchronize animations on their next think
  850. m_bForceSynch = true;
  851. }
  852. //-----------------------------------------------------------------------------
  853. // Purpose: Called when a scripted sequence animation sequence is done playing
  854. // (or when an AI Scripted Sequence doesn't supply an animation sequence
  855. // to play).
  856. // Input : pNPC - Pointer to the NPC that the sequence possesses.
  857. //-----------------------------------------------------------------------------
  858. void CAI_ScriptedSequence::PostIdleDone( CAI_BaseNPC *pNPC )
  859. {
  860. //
  861. // If we're set to keep the NPC in a scripted idle, hack them back into the script,
  862. // but allow another scripted sequence to take control of the NPC if it wants to,
  863. // unless another script has stolen them from us.
  864. //
  865. if ( ( m_iszPostIdle != NULL_STRING ) && ( m_spawnflags & SF_SCRIPT_LOOP_IN_POST_IDLE ) && ( m_hNextCine == NULL ) )
  866. {
  867. //
  868. // First time we've gotten here for this script. Start playing the post idle
  869. // if one is specified.
  870. // Only do so if we're selected, to prevent spam
  871. if ( pNPC->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT )
  872. {
  873. DevMsg( 2, "Post Idle %s finished for %s\n", STRING( pNPC->m_hCine->m_iszPostIdle ), pNPC->GetDebugName() );
  874. }
  875. pNPC->m_scriptState = CAI_BaseNPC::SCRIPT_POST_IDLE;
  876. StartSequence( pNPC, m_iszPostIdle, false );
  877. }
  878. else
  879. {
  880. if ( !( m_spawnflags & SF_SCRIPT_REPEATABLE ) )
  881. {
  882. SetThink( &CAI_ScriptedSequence::SUB_Remove );
  883. SetNextThink( gpGlobals->curtime + 0.1f );
  884. m_bThinking = false;
  885. m_bInitiatedSelfDelete = true;
  886. }
  887. //
  888. // This is done so that another sequence can take over the NPC when triggered
  889. // by the first.
  890. //
  891. pNPC->CineCleanup();
  892. // We have to pass in the flags here, because the NPC's m_hCine was cleared in CineCleanup()
  893. FixScriptNPCSchedule( pNPC, m_savedFlags );
  894. //
  895. // If another script is waiting to grab this NPC, start the next script
  896. // immediately.
  897. //
  898. if ( m_hNextCine != NULL )
  899. {
  900. CAI_ScriptedSequence *pNextCine = ( CAI_ScriptedSequence * )m_hNextCine.Get();
  901. //
  902. // Don't link ourselves in if we are going to be deleted.
  903. // TODO: use EHANDLEs instead of pointers to scripts!
  904. //
  905. if ( ( pNextCine != this ) || ( m_spawnflags & SF_SCRIPT_REPEATABLE ) )
  906. {
  907. pNextCine->SetTarget( pNPC );
  908. pNextCine->StartScript();
  909. }
  910. }
  911. }
  912. //Msg("%s finished post idle at %0.2f\n", pNPC->GetDebugName(), gpGlobals->curtime );
  913. m_OnPostIdleEndSequence.FireOutput(NULL, this);
  914. }
  915. //-----------------------------------------------------------------------------
  916. // Purpose: When a NPC finishes a scripted sequence, we have to fix up its state
  917. // and schedule for it to return to a normal AI NPC.
  918. // Scripted sequences just dirty the Schedule and drop the NPC in Idle State.
  919. // Input : *pNPC -
  920. //-----------------------------------------------------------------------------
  921. void CAI_ScriptedSequence::FixScriptNPCSchedule( CAI_BaseNPC *pNPC, int iSavedCineFlags )
  922. {
  923. if ( pNPC->GetIdealState() != NPC_STATE_DEAD )
  924. {
  925. pNPC->SetIdealState( NPC_STATE_IDLE );
  926. }
  927. if ( pNPC == NULL )
  928. return;
  929. FixFlyFlag( pNPC, iSavedCineFlags );
  930. pNPC->ClearSchedule( "Finished scripted sequence" );
  931. }
  932. void CAI_ScriptedSequence::FixFlyFlag( CAI_BaseNPC *pNPC, int iSavedCineFlags )
  933. {
  934. //Adrian: We NEED to clear this or the NPC's FL_FLY flag will never be removed cause of ClearSchedule!
  935. if ( pNPC->GetTask() && ( pNPC->GetTask()->iTask == TASK_PLAY_SCRIPT || pNPC->GetTask()->iTask == TASK_PLAY_SCRIPT_POST_IDLE ) )
  936. {
  937. if ( !(iSavedCineFlags & FL_FLY) )
  938. {
  939. if ( pNPC->GetFlags() & FL_FLY )
  940. {
  941. pNPC->RemoveFlag( FL_FLY );
  942. }
  943. }
  944. }
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Purpose:
  948. // Input : fAllow -
  949. //-----------------------------------------------------------------------------
  950. void CAI_ScriptedSequence::AllowInterrupt( bool fAllow )
  951. {
  952. if ( m_spawnflags & SF_SCRIPT_NOINTERRUPT )
  953. return;
  954. m_interruptable = fAllow;
  955. }
  956. //-----------------------------------------------------------------------------
  957. // Purpose:
  958. // Output : Returns true on success, false on failure.
  959. //-----------------------------------------------------------------------------
  960. bool CAI_ScriptedSequence::CanInterrupt( void )
  961. {
  962. if ( !m_interruptable )
  963. return false;
  964. CBaseEntity *pTarget = GetTarget();
  965. if ( pTarget != NULL && pTarget->IsAlive() )
  966. return true;
  967. return false;
  968. }
  969. //-----------------------------------------------------------------------------
  970. // Purpose:
  971. //-----------------------------------------------------------------------------
  972. void CAI_ScriptedSequence::RemoveIgnoredConditions( void )
  973. {
  974. if ( CanInterrupt() )
  975. {
  976. return;
  977. }
  978. CBaseEntity *pEntity = GetTarget();
  979. if ( pEntity == NULL )
  980. {
  981. return;
  982. }
  983. CAI_BaseNPC *pTarget = pEntity->MyNPCPointer();
  984. if ( pTarget == NULL )
  985. {
  986. return;
  987. }
  988. if ( pTarget )
  989. {
  990. pTarget->ClearCondition(COND_LIGHT_DAMAGE);
  991. pTarget->ClearCondition(COND_HEAVY_DAMAGE);
  992. }
  993. }
  994. //-----------------------------------------------------------------------------
  995. // Purpose: Returns true if another script is allowed to enqueue itself after
  996. // this script. The enqueued script will begin immediately after the
  997. // current script without dropping the NPC into AI.
  998. //-----------------------------------------------------------------------------
  999. bool CAI_ScriptedSequence::CanEnqueueAfter( void )
  1000. {
  1001. if ( m_hNextCine == NULL )
  1002. {
  1003. return true;
  1004. }
  1005. if ( m_iszNextScript != NULL_STRING )
  1006. {
  1007. DevMsg( 2, "%s is specified as the 'Next Script' and cannot be kicked out of the queue\n", m_hNextCine->GetDebugName() );
  1008. return false;
  1009. }
  1010. if ( !m_hNextCine->HasSpawnFlags( SF_SCRIPT_HIGH_PRIORITY ) )
  1011. {
  1012. return true;
  1013. }
  1014. DevMsg( 2, "%s is a priority script and cannot be kicked out of the queue\n", m_hNextCine->GetDebugName() );
  1015. return false;
  1016. }
  1017. //-----------------------------------------------------------------------------
  1018. // Purpose:
  1019. //-----------------------------------------------------------------------------
  1020. void CAI_ScriptedSequence::StopActionLoop( bool bStopSynchronizedScenes )
  1021. {
  1022. // Stop looping our action sequence. Next time the loop finishes,
  1023. // we'll move to the post idle sequence instead.
  1024. m_bLoopActionSequence = false;
  1025. // If we have synchronized scenes, and we're supposed to stop them, do so
  1026. if ( !bStopSynchronizedScenes || GetEntityName() == NULL_STRING )
  1027. return;
  1028. CBaseEntity *pentCine = gEntList.FindEntityByName( NULL, GetEntityName(), NULL );
  1029. while ( pentCine )
  1030. {
  1031. CAI_ScriptedSequence *pScene = dynamic_cast<CAI_ScriptedSequence *>(pentCine);
  1032. if ( pScene && pScene != this )
  1033. {
  1034. pScene->StopActionLoop( false );
  1035. }
  1036. pentCine = gEntList.FindEntityByName( pentCine, GetEntityName(), NULL );
  1037. }
  1038. }
  1039. //-----------------------------------------------------------------------------
  1040. // Purpose: Code method of forcing a scripted sequence entity to use a particular NPC.
  1041. // Useful when you don't know if the NPC has a unique targetname.
  1042. //-----------------------------------------------------------------------------
  1043. void CAI_ScriptedSequence::ForceSetTargetEntity( CAI_BaseNPC *pTarget, bool bDontCancelOtherSequences )
  1044. {
  1045. m_hForcedTarget = pTarget;
  1046. m_iszEntity = m_hForcedTarget->GetEntityName(); // Not guaranteed to be unique
  1047. m_bDontCancelOtherSequences = bDontCancelOtherSequences;
  1048. }
  1049. //-----------------------------------------------------------------------------
  1050. // Purpose: Setup this scripted sequence to maintain the desired position offset
  1051. // to the other NPC in the scripted interaction.
  1052. //-----------------------------------------------------------------------------
  1053. void CAI_ScriptedSequence::SetupInteractionPosition( CBaseEntity *pRelativeEntity, VMatrix &matDesiredLocalToWorld )
  1054. {
  1055. m_matInteractionPosition = matDesiredLocalToWorld;
  1056. m_hInteractionRelativeEntity = pRelativeEntity;
  1057. }
  1058. extern ConVar ai_debug_dyninteractions;
  1059. //-----------------------------------------------------------------------------
  1060. // Purpose: Modify the target AutoMovement() position before the NPC moves.
  1061. //-----------------------------------------------------------------------------
  1062. void CAI_ScriptedSequence::ModifyScriptedAutoMovement( Vector *vecNewPos )
  1063. {
  1064. if ( m_hInteractionRelativeEntity )
  1065. {
  1066. // If we have an entry animation, only do it on the entry
  1067. if ( m_iszEntry != NULL_STRING && !m_bIsPlayingEntry )
  1068. return;
  1069. Vector vecRelativeOrigin = m_hInteractionRelativeEntity->GetAbsOrigin();
  1070. QAngle angRelativeAngles = m_hInteractionRelativeEntity->GetAbsAngles();
  1071. CAI_BaseNPC *pNPC = m_hInteractionRelativeEntity->MyNPCPointer();
  1072. if ( pNPC )
  1073. {
  1074. angRelativeAngles[YAW] = pNPC->GetInteractionYaw();
  1075. }
  1076. bool bDebug = ai_debug_dyninteractions.GetInt() == 2;
  1077. if ( bDebug )
  1078. {
  1079. Msg("--\n%s current org: %f %f\n", m_hTargetEnt->GetDebugName(), m_hTargetEnt->GetAbsOrigin().x, m_hTargetEnt->GetAbsOrigin().y );
  1080. Msg("%s current org: %f %f", m_hInteractionRelativeEntity->GetDebugName(), vecRelativeOrigin.x, vecRelativeOrigin.y );
  1081. }
  1082. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating*>(m_hInteractionRelativeEntity.Get());
  1083. if ( pAnimating )
  1084. {
  1085. Vector vecDeltaPos;
  1086. QAngle vecDeltaAngles;
  1087. pAnimating->GetSequenceMovement( pAnimating->GetSequence(), 0.0f, pAnimating->GetCycle(), vecDeltaPos, vecDeltaAngles );
  1088. VectorYawRotate( vecDeltaPos, pAnimating->GetLocalAngles().y, vecDeltaPos );
  1089. if ( bDebug )
  1090. {
  1091. NDebugOverlay::Box( vecRelativeOrigin, -Vector(2,2,2), Vector(2,2,2), 0,255,0, 8, 0.1 );
  1092. }
  1093. vecRelativeOrigin -= vecDeltaPos;
  1094. if ( bDebug )
  1095. {
  1096. Msg(", relative to sequence start: %f %f\n", vecRelativeOrigin.x, vecRelativeOrigin.y );
  1097. NDebugOverlay::Box( vecRelativeOrigin, -Vector(3,3,3), Vector(3,3,3), 255,0,0, 8, 0.1 );
  1098. }
  1099. }
  1100. // We've been asked to maintain a specific position relative to the other NPC
  1101. // we're interacting with. Lerp towards the relative position.
  1102. VMatrix matMeToWorld, matLocalToWorld;
  1103. matMeToWorld.SetupMatrixOrgAngles( vecRelativeOrigin, angRelativeAngles );
  1104. MatrixMultiply( matMeToWorld, m_matInteractionPosition, matLocalToWorld );
  1105. // Get the desired NPC position in worldspace
  1106. Vector vecOrigin;
  1107. QAngle angAngles;
  1108. vecOrigin = matLocalToWorld.GetTranslation();
  1109. MatrixToAngles( matLocalToWorld, angAngles );
  1110. if ( bDebug )
  1111. {
  1112. Msg("Desired Origin for %s: %f %f\n", m_hTargetEnt->GetDebugName(), vecOrigin.x, vecOrigin.y );
  1113. NDebugOverlay::Axis( vecOrigin, angAngles, 5, true, 0.1 );
  1114. }
  1115. // Lerp to it over time
  1116. Vector vecToTarget = (vecOrigin - *vecNewPos);
  1117. if ( bDebug )
  1118. {
  1119. Msg("Automovement's output origin: %f %f\n", (*vecNewPos).x, (*vecNewPos).y );
  1120. Msg("Vector from automovement to desired: %f %f\n", vecToTarget.x, vecToTarget.y );
  1121. }
  1122. *vecNewPos += (vecToTarget * pAnimating->GetCycle());
  1123. }
  1124. }
  1125. //-----------------------------------------------------------------------------
  1126. // Purpose: Find all the cinematic entities with my targetname and stop them
  1127. // from playing.
  1128. //-----------------------------------------------------------------------------
  1129. void CAI_ScriptedSequence::CancelScript( void )
  1130. {
  1131. DevMsg( 2, "Cancelling script: %s\n", STRING( m_iszPlay ));
  1132. // Don't cancel matching sequences if we're asked not to, unless we didn't actually
  1133. // succeed in starting, in which case we should always cancel. This fixes
  1134. // dynamic interactions where an NPC was killed the same frame another NPC
  1135. // started a dynamic interaction with him.
  1136. bool bDontCancelOther = ((m_bDontCancelOtherSequences || HasSpawnFlags( SF_SCRIPT_ALLOW_DEATH ) )&& (m_startTime != 0));
  1137. if ( bDontCancelOther || !GetEntityName() )
  1138. {
  1139. ScriptEntityCancel( this );
  1140. return;
  1141. }
  1142. CBaseEntity *pentCineTarget = gEntList.FindEntityByName( NULL, GetEntityName() );
  1143. while ( pentCineTarget )
  1144. {
  1145. ScriptEntityCancel( pentCineTarget );
  1146. pentCineTarget = gEntList.FindEntityByName( pentCineTarget, GetEntityName() );
  1147. }
  1148. }
  1149. //-----------------------------------------------------------------------------
  1150. // Purpose: Find all the cinematic entities with my targetname and tell them to
  1151. // wait before starting. This ensures that all scripted sequences with
  1152. // the same name are frame-synchronized.
  1153. //
  1154. // When triggered, scripts call this first with a state of 1 to indicate that
  1155. // they are not ready to play (while NPCs move to their cue positions, etc).
  1156. // Once they are ready to play, they call it with a state of 0. When all
  1157. // the scripts are ready, they all are told to start.
  1158. //
  1159. // Input : bDelay - true means this script is not ready, false means it is ready.
  1160. //-----------------------------------------------------------------------------
  1161. void CAI_ScriptedSequence::DelayStart( bool bDelay )
  1162. {
  1163. //Msg("SSEQ: %.2f \"%s\" (%d) DelayStart( %d ). Current m_iDelay is: %d\n", gpGlobals->curtime, GetDebugName(), entindex(), bDelay, m_iDelay );
  1164. if ( ai_task_pre_script.GetBool() )
  1165. {
  1166. if ( bDelay == m_bDelayed )
  1167. return;
  1168. m_bDelayed = bDelay;
  1169. }
  1170. // Without a name, we cannot synchronize with anything else
  1171. if ( GetEntityName() == NULL_STRING )
  1172. {
  1173. m_iDelay = bDelay;
  1174. m_startTime = gpGlobals->curtime;
  1175. return;
  1176. }
  1177. CBaseEntity *pentCine = gEntList.FindEntityByName( NULL, GetEntityName() );
  1178. while ( pentCine )
  1179. {
  1180. if ( FClassnameIs( pentCine, "scripted_sequence" ) )
  1181. {
  1182. CAI_ScriptedSequence *pTarget = (CAI_ScriptedSequence *)pentCine;
  1183. if (bDelay)
  1184. {
  1185. // if delaying, add up the number of other scripts in the group
  1186. m_iDelay++;
  1187. //Msg("SSEQ: (%d) Found matching SS (%d). Incrementing MY m_iDelay to %d.\n", entindex(), pTarget->entindex(), m_iDelay );
  1188. }
  1189. else
  1190. {
  1191. // if ready, decrement each of other scripts in the group
  1192. // members not yet delayed will decrement below zero.
  1193. pTarget->m_iDelay--;
  1194. //Msg("SSEQ: (%d) Found matching SS (%d). Decrementing THEIR m_iDelay to %d.\n", entindex(), pTarget->entindex(), pTarget->m_iDelay );
  1195. // once everything is balanced, everyone will start.
  1196. if (pTarget->m_iDelay == 0)
  1197. {
  1198. pTarget->m_startTime = gpGlobals->curtime;
  1199. //Msg("SSEQ: STARTING SEQUENCE for \"%s\" (%d) (m_iDelay reached 0).\n", pTarget->GetDebugName(), pTarget->entindex() );
  1200. }
  1201. }
  1202. }
  1203. pentCine = gEntList.FindEntityByName( pentCine, GetEntityName() );
  1204. }
  1205. //Msg("SSEQ: Exited DelayStart() with m_iDelay of: %d.\n", m_iDelay );
  1206. }
  1207. //-----------------------------------------------------------------------------
  1208. // Purpose: Find an entity that I'm interested in and precache the sounds he'll
  1209. // need in the sequence.
  1210. //-----------------------------------------------------------------------------
  1211. void CAI_ScriptedSequence::Activate( void )
  1212. {
  1213. BaseClass::Activate();
  1214. //
  1215. // See if there is another script specified to run immediately after this one.
  1216. //
  1217. m_hNextCine = gEntList.FindEntityByName( NULL, m_iszNextScript );
  1218. if ( m_hNextCine == NULL )
  1219. {
  1220. m_iszNextScript = NULL_STRING;
  1221. }
  1222. }
  1223. //-----------------------------------------------------------------------------
  1224. // Purpose:
  1225. //-----------------------------------------------------------------------------
  1226. void CAI_ScriptedSequence::DrawDebugGeometryOverlays( void )
  1227. {
  1228. BaseClass::DrawDebugGeometryOverlays();
  1229. if ( m_debugOverlays & OVERLAY_TEXT_BIT )
  1230. {
  1231. if ( GetTarget() )
  1232. {
  1233. NDebugOverlay::HorzArrow( GetAbsOrigin(), GetTarget()->GetAbsOrigin(), 16, 0, 255, 0, 64, true, 0.0f );
  1234. }
  1235. }
  1236. }
  1237. //-----------------------------------------------------------------------------
  1238. // Purpose: Draw any debug text overlays
  1239. // Input :
  1240. // Output : Current text offset from the top
  1241. //-----------------------------------------------------------------------------
  1242. int CAI_ScriptedSequence::DrawDebugTextOverlays( void )
  1243. {
  1244. int text_offset = BaseClass::DrawDebugTextOverlays();
  1245. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  1246. {
  1247. char tempstr[512];
  1248. Q_snprintf(tempstr,sizeof(tempstr),"Target: %s", GetTarget() ? GetTarget()->GetDebugName() : "None" ) ;
  1249. EntityText(text_offset,tempstr,0);
  1250. text_offset++;
  1251. switch (m_fMoveTo)
  1252. {
  1253. case CINE_MOVETO_WAIT:
  1254. Q_snprintf(tempstr,sizeof(tempstr),"Moveto: Wait" );
  1255. break;
  1256. case CINE_MOVETO_WAIT_FACING:
  1257. Q_snprintf(tempstr,sizeof(tempstr),"Moveto: Wait Facing" );
  1258. break;
  1259. case CINE_MOVETO_WALK:
  1260. Q_snprintf(tempstr,sizeof(tempstr),"Moveto: Walk to Mark" );
  1261. break;
  1262. case CINE_MOVETO_RUN:
  1263. Q_snprintf(tempstr,sizeof(tempstr),"Moveto: Run to Mark" );
  1264. break;
  1265. case CINE_MOVETO_CUSTOM:
  1266. Q_snprintf(tempstr,sizeof(tempstr),"Moveto: Custom move to Mark" );
  1267. break;
  1268. case CINE_MOVETO_TELEPORT:
  1269. Q_snprintf(tempstr,sizeof(tempstr),"Moveto: Teleport to Mark" );
  1270. break;
  1271. }
  1272. EntityText(text_offset,tempstr,0);
  1273. text_offset++;
  1274. Q_snprintf(tempstr,sizeof(tempstr),"Thinking: %s", m_bThinking ? "Yes" : "No" ) ;
  1275. EntityText(text_offset,tempstr,0);
  1276. text_offset++;
  1277. if ( GetEntityName() != NULL_STRING )
  1278. {
  1279. Q_snprintf(tempstr,sizeof(tempstr),"Delay: %d", m_iDelay ) ;
  1280. EntityText(text_offset,tempstr,0);
  1281. text_offset++;
  1282. }
  1283. Q_snprintf(tempstr,sizeof(tempstr),"Start Time: %f", m_startTime ) ;
  1284. EntityText(text_offset,tempstr,0);
  1285. text_offset++;
  1286. Q_snprintf(tempstr,sizeof(tempstr),"Sequence has started: %s", m_sequenceStarted ? "Yes" : "No" ) ;
  1287. EntityText(text_offset,tempstr,0);
  1288. text_offset++;
  1289. Q_snprintf(tempstr,sizeof(tempstr),"Cancel Other Sequences: %s", m_bDontCancelOtherSequences ? "No" : "Yes" ) ;
  1290. EntityText(text_offset,tempstr,0);
  1291. text_offset++;
  1292. if ( m_bWaitForBeginSequence )
  1293. {
  1294. Q_snprintf(tempstr,sizeof(tempstr),"Is waiting for BeingSequence" );
  1295. EntityText(text_offset,tempstr,0);
  1296. text_offset++;
  1297. }
  1298. if ( m_bIsPlayingEntry )
  1299. {
  1300. Q_snprintf(tempstr,sizeof(tempstr),"Is playing entry" );
  1301. EntityText(text_offset,tempstr,0);
  1302. text_offset++;
  1303. }
  1304. if ( m_bLoopActionSequence )
  1305. {
  1306. Q_snprintf(tempstr,sizeof(tempstr),"Will loop action sequence" );
  1307. EntityText(text_offset,tempstr,0);
  1308. text_offset++;
  1309. }
  1310. if ( m_bSynchPostIdles )
  1311. {
  1312. Q_snprintf(tempstr,sizeof(tempstr),"Will synch post idles" );
  1313. EntityText(text_offset,tempstr,0);
  1314. text_offset++;
  1315. }
  1316. }
  1317. return text_offset;
  1318. }
  1319. //-----------------------------------------------------------------------------
  1320. // Purpose: Modifies an NPC's AI state without taking it out of its AI.
  1321. //-----------------------------------------------------------------------------
  1322. class CAI_ScriptedSchedule : public CBaseEntity
  1323. {
  1324. DECLARE_CLASS( CAI_ScriptedSchedule, CBaseEntity );
  1325. public:
  1326. CAI_ScriptedSchedule( void );
  1327. private:
  1328. void StartSchedule( CAI_BaseNPC *pTarget );
  1329. void StopSchedule( CAI_BaseNPC *pTarget );
  1330. void ScriptThink( void );
  1331. // Input handlers
  1332. void InputStartSchedule( inputdata_t &inputdata );
  1333. void InputStopSchedule( inputdata_t &inputdata );
  1334. CAI_BaseNPC *FindScriptEntity( bool bCyclic );
  1335. //---------------------------------
  1336. enum Schedule_t
  1337. {
  1338. SCHED_SCRIPT_NONE = 0,
  1339. SCHED_SCRIPT_WALK_TO_GOAL,
  1340. SCHED_SCRIPT_RUN_TO_GOAL,
  1341. SCHED_SCRIPT_ENEMY_IS_GOAL,
  1342. SCHED_SCRIPT_WALK_PATH_GOAL,
  1343. SCHED_SCRIPT_RUN_PATH_GOAL,
  1344. SCHED_SCRIPT_ENEMY_IS_GOAL_AND_RUN_TO_GOAL,
  1345. };
  1346. //---------------------------------
  1347. EHANDLE m_hLastFoundEntity;
  1348. EHANDLE m_hActivator; // Held from the input to allow procedural calls
  1349. string_t m_iszEntity; // Entity that is wanted for this script
  1350. float m_flRadius; // Range to search for an NPC to possess.
  1351. string_t m_sGoalEnt;
  1352. Schedule_t m_nSchedule;
  1353. int m_nForceState;
  1354. bool m_bGrabAll;
  1355. Interruptability_t m_Interruptability;
  1356. bool m_bDidFireOnce;
  1357. //---------------------------------
  1358. DECLARE_DATADESC();
  1359. };
  1360. BEGIN_DATADESC( CAI_ScriptedSchedule )
  1361. DEFINE_FIELD( m_hLastFoundEntity, FIELD_EHANDLE ),
  1362. DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "m_flRadius" ),
  1363. DEFINE_KEYFIELD( m_iszEntity, FIELD_STRING, "m_iszEntity" ),
  1364. DEFINE_KEYFIELD( m_nSchedule, FIELD_INTEGER, "schedule" ),
  1365. DEFINE_KEYFIELD( m_nForceState, FIELD_INTEGER, "forcestate" ),
  1366. DEFINE_KEYFIELD( m_sGoalEnt, FIELD_STRING, "goalent" ),
  1367. DEFINE_KEYFIELD( m_bGrabAll, FIELD_BOOLEAN, "graball" ),
  1368. DEFINE_KEYFIELD( m_Interruptability, FIELD_INTEGER, "interruptability"),
  1369. DEFINE_FIELD( m_bDidFireOnce, FIELD_BOOLEAN ),
  1370. DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ),
  1371. DEFINE_THINKFUNC( ScriptThink ),
  1372. DEFINE_INPUTFUNC( FIELD_VOID, "StartSchedule", InputStartSchedule ),
  1373. DEFINE_INPUTFUNC( FIELD_VOID, "StopSchedule", InputStopSchedule ),
  1374. END_DATADESC()
  1375. LINK_ENTITY_TO_CLASS( aiscripted_schedule, CAI_ScriptedSchedule );
  1376. //-----------------------------------------------------------------------------
  1377. // Purpose:
  1378. //-----------------------------------------------------------------------------
  1379. CAI_ScriptedSchedule::CAI_ScriptedSchedule( void ) : m_hActivator( INVALID_EHANDLE )
  1380. {
  1381. }
  1382. //-----------------------------------------------------------------------------
  1383. //-----------------------------------------------------------------------------
  1384. void CAI_ScriptedSchedule::ScriptThink( void )
  1385. {
  1386. bool success = false;
  1387. CAI_BaseNPC *pTarget;
  1388. if ( !m_bGrabAll )
  1389. {
  1390. pTarget = FindScriptEntity( (m_spawnflags & SF_SCRIPT_SEARCH_CYCLICALLY) != 0 );
  1391. if ( pTarget )
  1392. {
  1393. DevMsg( 2, "scripted_schedule \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), STRING( m_iszEntity ), pTarget->GetEntityName().ToCStr() );
  1394. StartSchedule( pTarget );
  1395. success = true;
  1396. }
  1397. }
  1398. else
  1399. {
  1400. m_hLastFoundEntity = NULL;
  1401. while ( ( pTarget = FindScriptEntity( true ) ) != NULL )
  1402. {
  1403. DevMsg( 2, "scripted_schedule \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), pTarget->GetEntityName().ToCStr(), STRING( m_iszEntity ) );
  1404. StartSchedule( pTarget );
  1405. success = true;
  1406. }
  1407. }
  1408. if ( !success )
  1409. {
  1410. DevMsg( 2, "scripted_schedule \"%s\" can't find NPC \"%s\"\n", GetDebugName(), STRING( m_iszEntity ) );
  1411. // FIXME: just trying again is bad. This should fire an output instead.
  1412. // FIXME: Think about puting output triggers on success true and sucess false
  1413. // FIXME: also needs to check the result of StartSchedule(), which can fail and not complain
  1414. SetNextThink( gpGlobals->curtime + 1.0f );
  1415. }
  1416. else
  1417. {
  1418. m_bDidFireOnce = true;
  1419. }
  1420. }
  1421. //-----------------------------------------------------------------------------
  1422. //-----------------------------------------------------------------------------
  1423. CAI_BaseNPC *CAI_ScriptedSchedule::FindScriptEntity( bool bCyclic )
  1424. {
  1425. CBaseEntity *pEntity = gEntList.FindEntityGenericWithin( m_hLastFoundEntity, STRING( m_iszEntity ), GetAbsOrigin(), m_flRadius, this, m_hActivator );
  1426. while ( pEntity != NULL )
  1427. {
  1428. CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
  1429. if ( pNPC && pNPC->IsAlive() && pNPC->IsInterruptable())
  1430. {
  1431. if ( bCyclic )
  1432. {
  1433. // next time this is called, start searching from the one found last time
  1434. m_hLastFoundEntity = pNPC;
  1435. }
  1436. return pNPC;
  1437. }
  1438. pEntity = gEntList.FindEntityGenericWithin( pEntity, STRING( m_iszEntity ), GetAbsOrigin(), m_flRadius, this, NULL );
  1439. }
  1440. m_hLastFoundEntity = NULL;
  1441. return NULL;
  1442. }
  1443. //-----------------------------------------------------------------------------
  1444. // Purpose: Make the entity carry out the scripted instructions, but without
  1445. // destroying the NPC's state.
  1446. //-----------------------------------------------------------------------------
  1447. void CAI_ScriptedSchedule::StartSchedule( CAI_BaseNPC *pTarget )
  1448. {
  1449. if ( pTarget == NULL )
  1450. return;
  1451. CBaseEntity *pGoalEnt = gEntList.FindEntityGeneric( NULL, STRING( m_sGoalEnt ), this, NULL );
  1452. // NOTE: !!! all possible choices require a goal ent currently
  1453. if ( !pGoalEnt )
  1454. {
  1455. CHintCriteria hintCriteria;
  1456. hintCriteria.SetGroup( m_sGoalEnt );
  1457. hintCriteria.SetHintType( HINT_ANY );
  1458. hintCriteria.AddIncludePosition( pTarget->GetAbsOrigin(), FLT_MAX );
  1459. CAI_Hint *pHint = CAI_HintManager::FindHint( pTarget->GetAbsOrigin(), hintCriteria );
  1460. if ( !pHint )
  1461. {
  1462. DevMsg( 1, "Can't find goal entity %s\nCan't execute script %s\n", STRING(m_sGoalEnt), GetDebugName() );
  1463. return;
  1464. }
  1465. pGoalEnt = pHint;
  1466. }
  1467. static NPC_STATE forcedStatesMap[] =
  1468. {
  1469. NPC_STATE_NONE,
  1470. NPC_STATE_IDLE,
  1471. NPC_STATE_ALERT,
  1472. NPC_STATE_COMBAT
  1473. };
  1474. if ( pTarget->GetSleepState() > AISS_AWAKE )
  1475. pTarget->Wake();
  1476. pTarget->ForceDecisionThink();
  1477. Assert( m_nForceState >= 0 && m_nForceState < ARRAYSIZE(forcedStatesMap) );
  1478. NPC_STATE forcedState = forcedStatesMap[m_nForceState];
  1479. // trap if this isn't a legal thing to do
  1480. Assert( pTarget->IsInterruptable() );
  1481. if ( forcedState != NPC_STATE_NONE )
  1482. pTarget->SetState( forcedState );
  1483. //
  1484. // Set enemy and make the NPC aware of the enemy's current position.
  1485. //
  1486. if ( m_nSchedule == SCHED_SCRIPT_ENEMY_IS_GOAL || m_nSchedule == SCHED_SCRIPT_ENEMY_IS_GOAL_AND_RUN_TO_GOAL )
  1487. {
  1488. if ( pGoalEnt && pGoalEnt->MyCombatCharacterPointer() )
  1489. {
  1490. pTarget->SetEnemy( pGoalEnt );
  1491. pTarget->UpdateEnemyMemory( pGoalEnt, pGoalEnt->GetAbsOrigin() );
  1492. pTarget->SetCondition( COND_SCHEDULE_DONE );
  1493. }
  1494. else
  1495. DevMsg( "Scripted schedule %s specified an invalid enemy %s\n", STRING( GetEntityName() ), STRING( m_sGoalEnt ) );
  1496. }
  1497. bool bDidSetSchedule = false;
  1498. switch ( m_nSchedule )
  1499. {
  1500. //
  1501. // Walk or run to position.
  1502. //
  1503. case SCHED_SCRIPT_WALK_TO_GOAL:
  1504. case SCHED_SCRIPT_RUN_TO_GOAL:
  1505. case SCHED_SCRIPT_ENEMY_IS_GOAL_AND_RUN_TO_GOAL:
  1506. {
  1507. Activity movementActivity = ( m_nSchedule == SCHED_SCRIPT_WALK_TO_GOAL ) ? ACT_WALK : ACT_RUN;
  1508. bool bIsFlying = (pTarget->GetMoveType() == MOVETYPE_FLY) || (pTarget->GetMoveType() == MOVETYPE_FLYGRAVITY);
  1509. if ( bIsFlying )
  1510. {
  1511. movementActivity = ACT_FLY;
  1512. }
  1513. if (!pTarget->ScheduledMoveToGoalEntity( SCHED_IDLE_WALK, pGoalEnt, movementActivity ))
  1514. {
  1515. if (!(m_spawnflags & SF_SCRIPT_NO_COMPLAINTS))
  1516. {
  1517. DevMsg( 1, "ScheduledMoveToGoalEntity to goal entity %s failed\nCan't execute script %s\n", STRING(m_sGoalEnt), GetDebugName() );
  1518. }
  1519. return;
  1520. }
  1521. bDidSetSchedule = true;
  1522. break;
  1523. }
  1524. case SCHED_SCRIPT_WALK_PATH_GOAL:
  1525. case SCHED_SCRIPT_RUN_PATH_GOAL:
  1526. {
  1527. Activity movementActivity = ( m_nSchedule == SCHED_SCRIPT_WALK_PATH_GOAL ) ? ACT_WALK : ACT_RUN;
  1528. bool bIsFlying = (pTarget->GetMoveType() == MOVETYPE_FLY) || (pTarget->GetMoveType() == MOVETYPE_FLYGRAVITY);
  1529. if ( bIsFlying )
  1530. {
  1531. movementActivity = ACT_FLY;
  1532. }
  1533. if (!pTarget->ScheduledFollowPath( SCHED_IDLE_WALK, pGoalEnt, movementActivity ))
  1534. {
  1535. if (!(m_spawnflags & SF_SCRIPT_NO_COMPLAINTS))
  1536. {
  1537. DevMsg( 1, "ScheduledFollowPath to goal entity %s failed\nCan't execute script %s\n", STRING(m_sGoalEnt), GetDebugName() );
  1538. }
  1539. return;
  1540. }
  1541. bDidSetSchedule = true;
  1542. break;
  1543. }
  1544. }
  1545. if ( bDidSetSchedule )
  1546. {
  1547. // Chain this to the target so that it can add the base and any custom interrupts to this
  1548. pTarget->SetScriptedScheduleIgnoreConditions( m_Interruptability );
  1549. }
  1550. }
  1551. //-----------------------------------------------------------------------------
  1552. // Purpose: Input handler to activate the scripted schedule. Finds the NPC to
  1553. // act on and sets a think for the near future to do the real work.
  1554. //-----------------------------------------------------------------------------
  1555. void CAI_ScriptedSchedule::InputStartSchedule( inputdata_t &inputdata )
  1556. {
  1557. if (( m_nForceState == 0 ) && ( m_nSchedule == 0 ))
  1558. {
  1559. DevMsg( 2, "aiscripted_schedule - no schedule or state has been set!\n" );
  1560. }
  1561. if ( !m_bDidFireOnce || ( m_spawnflags & SF_SCRIPT_REPEATABLE ) )
  1562. {
  1563. // DVS TODO: Is the NPC already playing the script?
  1564. m_hActivator = inputdata.pActivator;
  1565. SetThink( &CAI_ScriptedSchedule::ScriptThink );
  1566. SetNextThink( gpGlobals->curtime );
  1567. }
  1568. else
  1569. {
  1570. DevMsg( 2, "aiscripted_schedule - not playing schedule again: not flagged to repeat\n" );
  1571. }
  1572. }
  1573. //-----------------------------------------------------------------------------
  1574. // Purpose: Input handler to stop a previously activated scripted schedule.
  1575. //-----------------------------------------------------------------------------
  1576. void CAI_ScriptedSchedule::InputStopSchedule( inputdata_t &inputdata )
  1577. {
  1578. if ( !m_bDidFireOnce )
  1579. {
  1580. DevMsg( 2, "aiscripted_schedule - StopSchedule called, but schedule's never started.\n" );
  1581. return;
  1582. }
  1583. CAI_BaseNPC *pTarget;
  1584. if ( !m_bGrabAll )
  1585. {
  1586. pTarget = FindScriptEntity( (m_spawnflags & SF_SCRIPT_SEARCH_CYCLICALLY) != 0 );
  1587. if ( pTarget )
  1588. {
  1589. StopSchedule( pTarget );
  1590. }
  1591. }
  1592. else
  1593. {
  1594. m_hLastFoundEntity = NULL;
  1595. while ( ( pTarget = FindScriptEntity( true ) ) != NULL )
  1596. {
  1597. StopSchedule( pTarget );
  1598. }
  1599. }
  1600. }
  1601. //-----------------------------------------------------------------------------
  1602. // Purpose: If the target entity appears to be running this scripted schedule break it
  1603. //-----------------------------------------------------------------------------
  1604. void CAI_ScriptedSchedule::StopSchedule( CAI_BaseNPC *pTarget )
  1605. {
  1606. if ( pTarget->IsCurSchedule( SCHED_IDLE_WALK ) )
  1607. {
  1608. DevMsg( 2, "%s (%s): StopSchedule called on NPC %s.\n", GetClassname(), GetDebugName(), pTarget->GetDebugName() );
  1609. pTarget->ClearSchedule( "Stopping scripted schedule" );
  1610. }
  1611. }
  1612. class CAI_ScriptedSentence : public CPointEntity
  1613. {
  1614. public:
  1615. DECLARE_CLASS( CAI_ScriptedSentence, CPointEntity );
  1616. void Spawn( void );
  1617. bool KeyValue( const char *szKeyName, const char *szValue );
  1618. void FindThink( void );
  1619. void DelayThink( void );
  1620. int ObjectCaps( void ) { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
  1621. // Input handlers
  1622. void InputBeginSentence( inputdata_t &inputdata );
  1623. DECLARE_DATADESC();
  1624. CAI_BaseNPC *FindEntity( void );
  1625. bool AcceptableSpeaker( CAI_BaseNPC *pNPC );
  1626. int StartSentence( CAI_BaseNPC *pTarget );
  1627. private:
  1628. string_t m_iszSentence; // string index for sentence name
  1629. string_t m_iszEntity; // entity that is wanted for this sentence
  1630. float m_flRadius; // range to search
  1631. float m_flDelay; // How long the sentence lasts
  1632. float m_flRepeat; // repeat rate
  1633. soundlevel_t m_iSoundLevel;
  1634. int m_TempAttenuation;
  1635. float m_flVolume;
  1636. bool m_active;
  1637. string_t m_iszListener; // name of entity to look at while talking
  1638. CBaseEntity *m_pActivator;
  1639. COutputEvent m_OnBeginSentence;
  1640. COutputEvent m_OnEndSentence;
  1641. };
  1642. #define SF_SENTENCE_ONCE 0x0001
  1643. #define SF_SENTENCE_FOLLOWERS 0x0002 // only say if following player
  1644. #define SF_SENTENCE_INTERRUPT 0x0004 // force talking except when dead
  1645. #define SF_SENTENCE_CONCURRENT 0x0008 // allow other people to keep talking
  1646. #define SF_SENTENCE_SPEAKTOACTIVATOR 0x0010
  1647. BEGIN_DATADESC( CAI_ScriptedSentence )
  1648. DEFINE_KEYFIELD( m_iszSentence, FIELD_STRING, "sentence" ),
  1649. DEFINE_KEYFIELD( m_iszEntity, FIELD_STRING, "entity" ),
  1650. DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
  1651. DEFINE_KEYFIELD( m_flDelay, FIELD_FLOAT, "delay" ),
  1652. DEFINE_KEYFIELD( m_flRepeat, FIELD_FLOAT, "refire" ),
  1653. DEFINE_KEYFIELD( m_iszListener, FIELD_STRING, "listener" ),
  1654. DEFINE_KEYFIELD( m_TempAttenuation, FIELD_INTEGER, "attenuation" ),
  1655. DEFINE_FIELD( m_iSoundLevel, FIELD_INTEGER ),
  1656. DEFINE_FIELD( m_flVolume, FIELD_FLOAT ),
  1657. DEFINE_FIELD( m_active, FIELD_BOOLEAN ),
  1658. DEFINE_FIELD( m_pActivator, FIELD_EHANDLE ),
  1659. // Function Pointers
  1660. DEFINE_FUNCTION( FindThink ),
  1661. DEFINE_FUNCTION( DelayThink ),
  1662. // Inputs
  1663. DEFINE_INPUTFUNC(FIELD_VOID, "BeginSentence", InputBeginSentence),
  1664. // Outputs
  1665. DEFINE_OUTPUT(m_OnBeginSentence, "OnBeginSentence"),
  1666. DEFINE_OUTPUT(m_OnEndSentence, "OnEndSentence"),
  1667. END_DATADESC()
  1668. LINK_ENTITY_TO_CLASS( scripted_sentence, CAI_ScriptedSentence );
  1669. //-----------------------------------------------------------------------------
  1670. // Purpose:
  1671. // Input : szKeyName -
  1672. // szValue -
  1673. // Output : Returns true on success, false on failure.
  1674. //-----------------------------------------------------------------------------
  1675. bool CAI_ScriptedSentence::KeyValue( const char *szKeyName, const char *szValue )
  1676. {
  1677. if(FStrEq(szKeyName, "volume"))
  1678. {
  1679. m_flVolume = atof( szValue ) * 0.1;
  1680. }
  1681. else
  1682. {
  1683. return BaseClass::KeyValue( szKeyName, szValue );
  1684. }
  1685. return true;
  1686. }
  1687. //-----------------------------------------------------------------------------
  1688. // Purpose: Input handler for starting the scripted sentence.
  1689. //-----------------------------------------------------------------------------
  1690. void CAI_ScriptedSentence::InputBeginSentence( inputdata_t &inputdata )
  1691. {
  1692. if ( !m_active )
  1693. return;
  1694. m_pActivator = inputdata.pActivator;
  1695. //Msg( "Firing sentence: %s\n", STRING( m_iszSentence ));
  1696. SetThink( &CAI_ScriptedSentence::FindThink );
  1697. SetNextThink( gpGlobals->curtime );
  1698. }
  1699. //-----------------------------------------------------------------------------
  1700. // Purpose:
  1701. //-----------------------------------------------------------------------------
  1702. void CAI_ScriptedSentence::Spawn( void )
  1703. {
  1704. SetSolid( SOLID_NONE );
  1705. m_active = true;
  1706. // if no targetname, start now
  1707. if ( !GetEntityName() )
  1708. {
  1709. SetThink( &CAI_ScriptedSentence::FindThink );
  1710. SetNextThink( gpGlobals->curtime + 1.0f );
  1711. }
  1712. switch( m_TempAttenuation )
  1713. {
  1714. case 1: // Medium radius
  1715. m_iSoundLevel = SNDLVL_80dB;
  1716. break;
  1717. case 2: // Large radius
  1718. m_iSoundLevel = SNDLVL_85dB;
  1719. break;
  1720. case 3: //EVERYWHERE
  1721. m_iSoundLevel = SNDLVL_NONE;
  1722. break;
  1723. default:
  1724. case 0: // Small radius
  1725. m_iSoundLevel = SNDLVL_70dB;
  1726. break;
  1727. }
  1728. m_TempAttenuation = 0;
  1729. // No volume, use normal
  1730. if ( m_flVolume <= 0 )
  1731. m_flVolume = 1.0;
  1732. }
  1733. //-----------------------------------------------------------------------------
  1734. // Purpose:
  1735. //-----------------------------------------------------------------------------
  1736. void CAI_ScriptedSentence::FindThink( void )
  1737. {
  1738. CAI_BaseNPC *pNPC = FindEntity();
  1739. if ( pNPC )
  1740. {
  1741. int index = StartSentence( pNPC );
  1742. float length = engine->SentenceLength(index);
  1743. m_OnEndSentence.FireOutput(NULL, this, length + m_flRepeat);
  1744. if ( m_spawnflags & SF_SENTENCE_ONCE )
  1745. UTIL_Remove( this );
  1746. float delay = m_flDelay + length + 0.1;
  1747. if ( delay < 0 )
  1748. delay = 0;
  1749. SetThink( &CAI_ScriptedSentence::DelayThink );
  1750. // calculate delay dynamically because this could play a sentence group
  1751. // rather than a single sentence.
  1752. // add 0.1 because the sound engine mixes ahead -- the sentence will actually start ~0.1 secs from now
  1753. SetNextThink( gpGlobals->curtime + delay + m_flRepeat );
  1754. m_active = false;
  1755. //Msg( "%s: found NPC %s\n", STRING(m_iszSentence), STRING(m_iszEntity) );
  1756. }
  1757. else
  1758. {
  1759. //Msg( "%s: can't find NPC %s\n", STRING(m_iszSentence), STRING(m_iszEntity) );
  1760. SetNextThink( gpGlobals->curtime + m_flRepeat + 0.5 );
  1761. }
  1762. }
  1763. //-----------------------------------------------------------------------------
  1764. // Purpose:
  1765. //-----------------------------------------------------------------------------
  1766. void CAI_ScriptedSentence::DelayThink( void )
  1767. {
  1768. m_active = true;
  1769. if ( !GetEntityName() )
  1770. SetNextThink( gpGlobals->curtime + 0.1f );
  1771. SetThink( &CAI_ScriptedSentence::FindThink );
  1772. }
  1773. //-----------------------------------------------------------------------------
  1774. // Purpose:
  1775. // Input : *pNPC -
  1776. // Output : Returns true on success, false on failure.
  1777. //-----------------------------------------------------------------------------
  1778. bool CAI_ScriptedSentence::AcceptableSpeaker( CAI_BaseNPC *pNPC )
  1779. {
  1780. if ( pNPC )
  1781. {
  1782. if ( m_spawnflags & SF_SENTENCE_FOLLOWERS )
  1783. {
  1784. if ( pNPC->GetTarget() == NULL || !pNPC->GetTarget()->IsPlayer() )
  1785. return false;
  1786. }
  1787. bool override;
  1788. if ( m_spawnflags & SF_SENTENCE_INTERRUPT )
  1789. override = true;
  1790. else
  1791. override = false;
  1792. if ( pNPC->CanPlaySentence( override ) )
  1793. return true;
  1794. }
  1795. return false;
  1796. }
  1797. //-----------------------------------------------------------------------------
  1798. // Purpose:
  1799. // Output :
  1800. //-----------------------------------------------------------------------------
  1801. CAI_BaseNPC *CAI_ScriptedSentence::FindEntity( void )
  1802. {
  1803. CBaseEntity *pentTarget;
  1804. CAI_BaseNPC *pNPC;
  1805. pentTarget = gEntList.FindEntityByName( NULL, m_iszEntity );
  1806. pNPC = NULL;
  1807. while (pentTarget)
  1808. {
  1809. pNPC = pentTarget->MyNPCPointer();
  1810. if ( pNPC != NULL )
  1811. {
  1812. if ( AcceptableSpeaker( pNPC ) )
  1813. return pNPC;
  1814. //Msg( "%s (%s), not acceptable\n", pNPC->GetClassname(), pNPC->GetDebugName() );
  1815. }
  1816. pentTarget = gEntList.FindEntityByName( pentTarget, m_iszEntity );
  1817. }
  1818. CBaseEntity *pEntity = NULL;
  1819. for ( CEntitySphereQuery sphere( GetAbsOrigin(), m_flRadius, FL_NPC ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
  1820. {
  1821. if (FClassnameIs( pEntity, STRING(m_iszEntity)))
  1822. {
  1823. pNPC = pEntity->MyNPCPointer( );
  1824. if ( AcceptableSpeaker( pNPC ) )
  1825. return pNPC;
  1826. }
  1827. }
  1828. return NULL;
  1829. }
  1830. //-----------------------------------------------------------------------------
  1831. // Purpose:
  1832. // Input : pTarget -
  1833. // Output :
  1834. //-----------------------------------------------------------------------------
  1835. int CAI_ScriptedSentence::StartSentence( CAI_BaseNPC *pTarget )
  1836. {
  1837. if ( !pTarget )
  1838. {
  1839. DevMsg( 2, "Not Playing sentence %s\n", STRING(m_iszSentence) );
  1840. return -1;
  1841. }
  1842. bool bConcurrent = false;
  1843. if ( !(m_spawnflags & SF_SENTENCE_CONCURRENT) )
  1844. bConcurrent = true;
  1845. CBaseEntity *pListener = NULL;
  1846. if ( m_spawnflags & SF_SENTENCE_SPEAKTOACTIVATOR )
  1847. {
  1848. pListener = m_pActivator;
  1849. }
  1850. else if (m_iszListener != NULL_STRING)
  1851. {
  1852. float radius = m_flRadius;
  1853. if ( FStrEq( STRING(m_iszListener ), "!player" ) )
  1854. radius = MAX_TRACE_LENGTH; // Always find the player
  1855. pListener = gEntList.FindEntityGenericNearest( STRING( m_iszListener ), pTarget->GetAbsOrigin(), radius, this, NULL );
  1856. }
  1857. int sentenceIndex = pTarget->PlayScriptedSentence( STRING(m_iszSentence), m_flDelay, m_flVolume, m_iSoundLevel, bConcurrent, pListener );
  1858. DevMsg( 2, "Playing sentence %s\n", STRING(m_iszSentence) );
  1859. m_OnBeginSentence.FireOutput(NULL, this);
  1860. return sentenceIndex;
  1861. }
  1862. // HACKHACK: This is a little expensive with the dynamic_cast<> and all, but it lets us solve
  1863. // the problem of matching scripts back to entities without new state.
  1864. const char *CAI_ScriptedSequence::GetSpawnPreIdleSequenceForScript( CBaseEntity *pEntity )
  1865. {
  1866. CAI_ScriptedSequence *pScript = gEntList.NextEntByClass( (CAI_ScriptedSequence *)NULL );
  1867. while ( pScript )
  1868. {
  1869. if ( pScript->HasSpawnFlags( SF_SCRIPT_START_ON_SPAWN ) && pScript->m_iszEntity == pEntity->GetEntityName() )
  1870. {
  1871. if ( pScript->m_iszPreIdle != NULL_STRING )
  1872. {
  1873. return STRING(pScript->m_iszPreIdle);
  1874. }
  1875. return NULL;
  1876. }
  1877. pScript = gEntList.NextEntByClass( pScript );
  1878. }
  1879. return NULL;
  1880. }