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.

1753 lines
49 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "npc_antlion.h"
  8. #include "antlion_maker.h"
  9. #include "saverestore_utlvector.h"
  10. #include "mapentities.h"
  11. #include "decals.h"
  12. #include "iservervehicle.h"
  13. #include "antlion_dust.h"
  14. #include "smoke_trail.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. CAntlionMakerManager g_AntlionMakerManager( "CAntlionMakerManager" );
  18. static const char *s_pPoolThinkContext = "PoolThinkContext";
  19. static const char *s_pBlockedEffectsThinkContext = "BlockedEffectsThinkContext";
  20. static const char *s_pBlockedCheckContext = "BlockedCheckContext";
  21. ConVar g_debug_antlionmaker( "g_debug_antlionmaker", "0", FCVAR_CHEAT );
  22. #define ANTLION_MAKER_PLAYER_DETECT_RADIUS 512
  23. #define ANTLION_MAKER_BLOCKED_MASS 250.0f // half the weight of a car
  24. #define ANTLION_MAKE_SPORE_SPAWNRATE 25.0f
  25. //-----------------------------------------------------------------------------
  26. // Purpose:
  27. // Input : &vFightGoal -
  28. //-----------------------------------------------------------------------------
  29. void CAntlionMakerManager::BroadcastFightGoal( const Vector &vFightGoal )
  30. {
  31. CAntlionTemplateMaker *pMaker;
  32. for ( int i=0; i < m_Makers.Count(); i++ )
  33. {
  34. pMaker = m_Makers[i];
  35. if ( pMaker && pMaker->ShouldHearBugbait() )
  36. {
  37. pMaker->SetFightTarget( vFightGoal );
  38. pMaker->SetChildMoveState( ANTLION_MOVE_FIGHT_TO_GOAL );
  39. pMaker->UpdateChildren();
  40. }
  41. }
  42. }
  43. //-----------------------------------------------------------------------------
  44. // Purpose:
  45. // Input : *pFightGoal -
  46. //-----------------------------------------------------------------------------
  47. void CAntlionMakerManager::BroadcastFightGoal( CBaseEntity *pFightGoal )
  48. {
  49. CAntlionTemplateMaker *pMaker;
  50. for ( int i=0; i < m_Makers.Count(); i++ )
  51. {
  52. pMaker = m_Makers[i];
  53. if ( pMaker && pMaker->ShouldHearBugbait() )
  54. {
  55. pMaker->SetFightTarget( pFightGoal );
  56. pMaker->SetChildMoveState( ANTLION_MOVE_FIGHT_TO_GOAL );
  57. pMaker->UpdateChildren();
  58. }
  59. }
  60. }
  61. //-----------------------------------------------------------------------------
  62. // Purpose:
  63. // Input : *pFightGoal -
  64. //-----------------------------------------------------------------------------
  65. void CAntlionMakerManager::BroadcastFollowGoal( CBaseEntity *pFollowGoal )
  66. {
  67. CAntlionTemplateMaker *pMaker;
  68. for ( int i=0; i < m_Makers.Count(); i++ )
  69. {
  70. pMaker = m_Makers[i];
  71. if ( pMaker && pMaker->ShouldHearBugbait() )
  72. {
  73. //pMaker->SetFightTarget( NULL );
  74. pMaker->SetFollowTarget( pFollowGoal );
  75. pMaker->SetChildMoveState( ANTLION_MOVE_FOLLOW );
  76. pMaker->UpdateChildren();
  77. }
  78. }
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Purpose:
  82. //-----------------------------------------------------------------------------
  83. void CAntlionMakerManager::GatherMakers( void )
  84. {
  85. CBaseEntity *pSearch = NULL;
  86. CAntlionTemplateMaker *pMaker;
  87. m_Makers.Purge();
  88. // Find these all once
  89. while ( ( pSearch = gEntList.FindEntityByClassname( pSearch, "npc_antlion_template_maker" ) ) != NULL )
  90. {
  91. pMaker = static_cast<CAntlionTemplateMaker *>(pSearch);
  92. m_Makers.AddToTail( pMaker );
  93. }
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Purpose:
  97. //-----------------------------------------------------------------------------
  98. void CAntlionMakerManager::LevelInitPostEntity( void )
  99. {
  100. //Find all antlion makers
  101. GatherMakers();
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Antlion template maker
  105. //-----------------------------------------------------------------------------
  106. LINK_ENTITY_TO_CLASS( npc_antlion_template_maker, CAntlionTemplateMaker );
  107. //DT Definition
  108. BEGIN_DATADESC( CAntlionTemplateMaker )
  109. DEFINE_KEYFIELD( m_strSpawnGroup, FIELD_STRING, "spawngroup" ),
  110. DEFINE_KEYFIELD( m_strSpawnTarget, FIELD_STRING, "spawntarget" ),
  111. DEFINE_KEYFIELD( m_flSpawnRadius, FIELD_FLOAT, "spawnradius" ),
  112. DEFINE_KEYFIELD( m_strFightTarget, FIELD_STRING, "fighttarget" ),
  113. DEFINE_KEYFIELD( m_strFollowTarget, FIELD_STRING, "followtarget" ),
  114. DEFINE_KEYFIELD( m_bIgnoreBugbait, FIELD_BOOLEAN, "ignorebugbait" ),
  115. DEFINE_KEYFIELD( m_flVehicleSpawnDistance, FIELD_FLOAT, "vehicledistance" ),
  116. DEFINE_KEYFIELD( m_flWorkerSpawnRate, FIELD_FLOAT, "workerspawnrate" ),
  117. DEFINE_FIELD( m_nChildMoveState, FIELD_INTEGER ),
  118. DEFINE_FIELD( m_hFightTarget, FIELD_EHANDLE ),
  119. DEFINE_FIELD( m_hProxyTarget, FIELD_EHANDLE ),
  120. DEFINE_FIELD( m_hFollowTarget, FIELD_EHANDLE ),
  121. DEFINE_FIELD( m_iSkinCount, FIELD_INTEGER ),
  122. DEFINE_FIELD( m_flBlockedBumpTime, FIELD_TIME ),
  123. DEFINE_FIELD( m_bBlocked, FIELD_BOOLEAN ),
  124. DEFINE_UTLVECTOR( m_Children, FIELD_EHANDLE ),
  125. DEFINE_KEYFIELD( m_iPool, FIELD_INTEGER, "pool_start" ),
  126. DEFINE_KEYFIELD( m_iMaxPool, FIELD_INTEGER, "pool_max" ),
  127. DEFINE_KEYFIELD( m_iPoolRegenAmount,FIELD_INTEGER, "pool_regen_amount" ),
  128. DEFINE_KEYFIELD( m_flPoolRegenTime, FIELD_FLOAT, "pool_regen_time" ),
  129. DEFINE_INPUTFUNC( FIELD_STRING, "SetFightTarget", InputSetFightTarget ),
  130. DEFINE_INPUTFUNC( FIELD_STRING, "SetFollowTarget", InputSetFollowTarget ),
  131. DEFINE_INPUTFUNC( FIELD_VOID, "ClearFollowTarget", InputClearFollowTarget ),
  132. DEFINE_INPUTFUNC( FIELD_VOID, "ClearFightTarget", InputClearFightTarget ),
  133. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpawnRadius", InputSetSpawnRadius ),
  134. DEFINE_INPUTFUNC( FIELD_INTEGER, "AddToPool", InputAddToPool ),
  135. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMaxPool", InputSetMaxPool ),
  136. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetPoolRegenAmount", InputSetPoolRegenAmount ),
  137. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPoolRegenTime", InputSetPoolRegenTime ),
  138. DEFINE_INPUTFUNC( FIELD_STRING, "ChangeDestinationGroup", InputChangeDestinationGroup ),
  139. DEFINE_OUTPUT( m_OnAllBlocked, "OnAllBlocked" ),
  140. DEFINE_KEYFIELD( m_bCreateSpores, FIELD_BOOLEAN, "createspores" ),
  141. DEFINE_THINKFUNC( PoolRegenThink ),
  142. DEFINE_THINKFUNC( FindNodesCloseToPlayer ),
  143. DEFINE_THINKFUNC( BlockedCheckFunc ),
  144. END_DATADESC()
  145. //-----------------------------------------------------------------------------
  146. // Purpose:
  147. //-----------------------------------------------------------------------------
  148. CAntlionTemplateMaker::CAntlionTemplateMaker( void )
  149. {
  150. m_hFightTarget = NULL;
  151. m_hProxyTarget = NULL;
  152. m_hFollowTarget = NULL;
  153. m_nChildMoveState = ANTLION_MOVE_FREE;
  154. m_iSkinCount = 0;
  155. m_flBlockedBumpTime = 0.0f;
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose:
  159. //-----------------------------------------------------------------------------
  160. CAntlionTemplateMaker::~CAntlionTemplateMaker( void )
  161. {
  162. DestroyProxyTarget();
  163. m_Children.Purge();
  164. }
  165. //-----------------------------------------------------------------------------
  166. // Purpose:
  167. // Input : *pAnt -
  168. //-----------------------------------------------------------------------------
  169. void CAntlionTemplateMaker::AddChild( CNPC_Antlion *pAnt )
  170. {
  171. m_Children.AddToTail( pAnt );
  172. m_nLiveChildren = m_Children.Count();
  173. pAnt->SetOwnerEntity( this );
  174. }
  175. //-----------------------------------------------------------------------------
  176. // Purpose:
  177. // Input : *pAnt -
  178. //-----------------------------------------------------------------------------
  179. void CAntlionTemplateMaker::RemoveChild( CNPC_Antlion *pAnt )
  180. {
  181. m_Children.FindAndRemove( pAnt );
  182. m_nLiveChildren = m_Children.Count();
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose:
  186. //-----------------------------------------------------------------------------
  187. void CAntlionTemplateMaker::FixupOrphans( void )
  188. {
  189. CBaseEntity *pSearch = NULL;
  190. CNPC_Antlion *pAntlion = NULL;
  191. // Iterate through all antlions and see if there are any orphans
  192. while ( ( pSearch = gEntList.FindEntityByClassname( pSearch, "npc_antlion" ) ) != NULL )
  193. {
  194. pAntlion = dynamic_cast<CNPC_Antlion *>(pSearch);
  195. // See if it's a live orphan
  196. if ( pAntlion && pAntlion->GetOwnerEntity() == NULL && pAntlion->IsAlive() )
  197. {
  198. // See if its parent was named the same as we are
  199. if ( stricmp( pAntlion->GetParentSpawnerName(), STRING( GetEntityName() ) ) == 0 )
  200. {
  201. // Relink us to this antlion, he's come through a transition and was orphaned
  202. AddChild( pAntlion );
  203. }
  204. }
  205. }
  206. }
  207. //-----------------------------------------------------------------------------
  208. //-----------------------------------------------------------------------------
  209. void CAntlionTemplateMaker::PrecacheTemplateEntity( CBaseEntity *pEntity )
  210. {
  211. BaseClass::PrecacheTemplateEntity( pEntity );
  212. // If we can spawn workers, precache the worker as well.
  213. if ( m_flWorkerSpawnRate != 0 )
  214. {
  215. pEntity->AddSpawnFlags( SF_ANTLION_WORKER );
  216. pEntity->Precache();
  217. }
  218. }
  219. //-----------------------------------------------------------------------------
  220. // Purpose:
  221. //-----------------------------------------------------------------------------
  222. void CAntlionTemplateMaker::Activate( void )
  223. {
  224. FixupOrphans();
  225. BaseClass::Activate();
  226. // Are we using the pool behavior for coast?
  227. if ( m_iMaxPool )
  228. {
  229. if ( !m_flPoolRegenTime )
  230. {
  231. Msg("%s using pool behavior without a specified pool regen time.\n", GetClassname() );
  232. m_flPoolRegenTime = 0.1;
  233. }
  234. // Start up our think cycle unless we're reloading this map (which would reset it)
  235. if ( m_bDisabled == false && gpGlobals->eLoadType != MapLoad_LoadGame )
  236. {
  237. // Start our pool regeneration cycle
  238. SetContextThink( &CAntlionTemplateMaker::PoolRegenThink, gpGlobals->curtime + m_flPoolRegenTime, s_pPoolThinkContext );
  239. // Start our blocked effects cycle
  240. if ( hl2_episodic.GetBool() == true && HasSpawnFlags( SF_ANTLIONMAKER_DO_BLOCKEDEFFECTS ) )
  241. {
  242. SetContextThink( &CAntlionTemplateMaker::FindNodesCloseToPlayer, gpGlobals->curtime + 1.0f, s_pBlockedEffectsThinkContext );
  243. }
  244. }
  245. }
  246. ActivateAllSpores();
  247. }
  248. void CAntlionTemplateMaker::ActivateSpore( const char* sporename, Vector vOrigin )
  249. {
  250. if ( m_bCreateSpores == false )
  251. return;
  252. char szName[64];
  253. Q_snprintf( szName, sizeof( szName ), "%s_spore", sporename );
  254. SporeExplosion *pSpore = (SporeExplosion*)gEntList.FindEntityByName( NULL, szName );
  255. //One already exists...
  256. if ( pSpore )
  257. {
  258. if ( pSpore->m_bDisabled == true )
  259. {
  260. inputdata_t inputdata;
  261. pSpore->InputEnable( inputdata );
  262. }
  263. return;
  264. }
  265. CBaseEntity *pEnt = CreateEntityByName( "env_sporeexplosion" );
  266. if ( pEnt )
  267. {
  268. pSpore = dynamic_cast<SporeExplosion*>(pEnt);
  269. if ( pSpore )
  270. {
  271. pSpore->SetAbsOrigin( vOrigin );
  272. pSpore->SetName( AllocPooledString( szName ) );
  273. pSpore->m_flSpawnRate = ANTLION_MAKE_SPORE_SPAWNRATE;
  274. }
  275. }
  276. }
  277. void CAntlionTemplateMaker::DisableSpore( const char* sporename )
  278. {
  279. if ( m_bCreateSpores == false )
  280. return;
  281. char szName[64];
  282. Q_snprintf( szName, sizeof( szName ), "%s_spore", sporename );
  283. SporeExplosion *pSpore = (SporeExplosion*)gEntList.FindEntityByName( NULL, szName );
  284. if ( pSpore && pSpore->m_bDisabled == false )
  285. {
  286. inputdata_t inputdata;
  287. pSpore->InputDisable( inputdata );
  288. return;
  289. }
  290. }
  291. void CAntlionTemplateMaker::ActivateAllSpores( void )
  292. {
  293. if ( m_bDisabled == true )
  294. return;
  295. if ( m_bCreateSpores == false )
  296. return;
  297. CHintCriteria hintCriteria;
  298. hintCriteria.SetGroup( m_strSpawnGroup );
  299. hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );
  300. CUtlVector<CAI_Hint *> hintList;
  301. CAI_HintManager::FindAllHints( vec3_origin, hintCriteria, &hintList );
  302. for ( int i = 0; i < hintList.Count(); i++ )
  303. {
  304. CAI_Hint *pTestHint = hintList[i];
  305. if ( pTestHint )
  306. {
  307. bool bBlank;
  308. if ( !AllHintsFromClusterBlocked( pTestHint, bBlank ) )
  309. {
  310. ActivateSpore( STRING( pTestHint->GetEntityName() ), pTestHint->GetAbsOrigin() );
  311. }
  312. }
  313. }
  314. }
  315. void CAntlionTemplateMaker::DisableAllSpores( void )
  316. {
  317. CHintCriteria hintCriteria;
  318. hintCriteria.SetGroup( m_strSpawnGroup );
  319. hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );
  320. CUtlVector<CAI_Hint *> hintList;
  321. CAI_HintManager::FindAllHints( vec3_origin, hintCriteria, &hintList );
  322. for ( int i = 0; i < hintList.Count(); i++ )
  323. {
  324. CAI_Hint *pTestHint = hintList[i];
  325. if ( pTestHint )
  326. {
  327. DisableSpore( STRING( pTestHint->GetEntityName() ) );
  328. }
  329. }
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose:
  333. // Output : CBaseEntity
  334. //-----------------------------------------------------------------------------
  335. CBaseEntity *CAntlionTemplateMaker::GetFightTarget( void )
  336. {
  337. if ( m_hFightTarget != NULL )
  338. return m_hFightTarget;
  339. return m_hProxyTarget;
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Purpose:
  343. // Output : CBaseEntity
  344. //-----------------------------------------------------------------------------
  345. CBaseEntity *CAntlionTemplateMaker::GetFollowTarget( void )
  346. {
  347. return m_hFollowTarget;
  348. }
  349. //-----------------------------------------------------------------------------
  350. // Purpose:
  351. //-----------------------------------------------------------------------------
  352. void CAntlionTemplateMaker::UpdateChildren( void )
  353. {
  354. //Update all children
  355. CNPC_Antlion *pAntlion = NULL;
  356. // Move through our child list
  357. int i=0;
  358. for ( ; i < m_Children.Count(); i++ )
  359. {
  360. pAntlion = m_Children[i];
  361. //HACKHACK
  362. //Let's just fix this up.
  363. //This guy might have been killed in another level and we just came back.
  364. if ( pAntlion == NULL )
  365. {
  366. m_Children.Remove( i );
  367. i--;
  368. continue;
  369. }
  370. if ( pAntlion->m_lifeState != LIFE_ALIVE )
  371. continue;
  372. pAntlion->SetFightTarget( GetFightTarget() );
  373. pAntlion->SetFollowTarget( GetFollowTarget() );
  374. pAntlion->SetMoveState( m_nChildMoveState );
  375. }
  376. m_nLiveChildren = i;
  377. }
  378. //-----------------------------------------------------------------------------
  379. // Purpose:
  380. // Input : strTarget -
  381. //-----------------------------------------------------------------------------
  382. void CAntlionTemplateMaker::SetFightTarget( string_t strTarget, CBaseEntity *pActivator, CBaseEntity *pCaller )
  383. {
  384. if ( HasSpawnFlags( SF_ANTLIONMAKER_RANDOM_FIGHT_TARGET ) )
  385. {
  386. CBaseEntity *pSearch = m_hFightTarget;
  387. for ( int i = random->RandomInt(1,5); i > 0; i-- )
  388. pSearch = gEntList.FindEntityByName( pSearch, strTarget, this, pActivator, pCaller );
  389. if ( pSearch != NULL )
  390. {
  391. SetFightTarget( pSearch );
  392. }
  393. else
  394. {
  395. SetFightTarget( gEntList.FindEntityByName( NULL, strTarget, this, pActivator, pCaller ) );
  396. }
  397. }
  398. else
  399. {
  400. SetFightTarget( gEntList.FindEntityByName( NULL, strTarget, this, pActivator, pCaller ) );
  401. }
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose:
  405. // Input : *pEntity -
  406. //-----------------------------------------------------------------------------
  407. void CAntlionTemplateMaker::SetFightTarget( CBaseEntity *pEntity )
  408. {
  409. m_hFightTarget = pEntity;
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Purpose:
  413. // Input : &position -
  414. //-----------------------------------------------------------------------------
  415. void CAntlionTemplateMaker::SetFightTarget( const Vector &position )
  416. {
  417. CreateProxyTarget( position );
  418. m_hFightTarget = NULL;
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Purpose:
  422. // Input : *pTarget -
  423. //-----------------------------------------------------------------------------
  424. void CAntlionTemplateMaker::SetFollowTarget( CBaseEntity *pTarget )
  425. {
  426. m_hFollowTarget = pTarget;
  427. }
  428. //-----------------------------------------------------------------------------
  429. // Purpose:
  430. // Input : *pTarget -
  431. //-----------------------------------------------------------------------------
  432. void CAntlionTemplateMaker::SetFollowTarget( string_t strTarget, CBaseEntity *pActivator, CBaseEntity *pCaller )
  433. {
  434. CBaseEntity *pSearch = gEntList.FindEntityByName( NULL, strTarget, NULL, pActivator, pCaller );
  435. if ( pSearch != NULL )
  436. {
  437. SetFollowTarget( pSearch );
  438. }
  439. }
  440. //-----------------------------------------------------------------------------
  441. // Purpose:
  442. // Input : state -
  443. //-----------------------------------------------------------------------------
  444. void CAntlionTemplateMaker::SetChildMoveState( AntlionMoveState_e state )
  445. {
  446. m_nChildMoveState = state;
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Purpose:
  450. // Input : &position -
  451. //-----------------------------------------------------------------------------
  452. void CAntlionTemplateMaker::CreateProxyTarget( const Vector &position )
  453. {
  454. // Create if we don't have one
  455. if ( m_hProxyTarget == NULL )
  456. {
  457. m_hProxyTarget = CreateEntityByName( "info_target" );
  458. }
  459. // Update if we do
  460. if ( m_hProxyTarget != NULL )
  461. {
  462. m_hProxyTarget->SetAbsOrigin( position );
  463. }
  464. }
  465. //-----------------------------------------------------------------------------
  466. // Purpose:
  467. //-----------------------------------------------------------------------------
  468. void CAntlionTemplateMaker::DestroyProxyTarget( void )
  469. {
  470. if ( m_hProxyTarget )
  471. {
  472. UTIL_Remove( m_hProxyTarget );
  473. }
  474. }
  475. //-----------------------------------------------------------------------------
  476. // Purpose:
  477. // Input : bIgnoreSolidEntities -
  478. // Output : Returns true on success, false on failure.
  479. //-----------------------------------------------------------------------------
  480. bool CAntlionTemplateMaker::CanMakeNPC( bool bIgnoreSolidEntities )
  481. {
  482. if ( m_nMaxLiveChildren == 0 )
  483. return false;
  484. if ( !HasSpawnFlags( SF_ANTLIONMAKER_SPAWN_CLOSE_TO_TARGET ) )
  485. {
  486. if ( m_strSpawnGroup == NULL_STRING )
  487. return BaseClass::CanMakeNPC( bIgnoreSolidEntities );
  488. }
  489. if ( m_nMaxLiveChildren > 0 && m_nLiveChildren >= m_nMaxLiveChildren )
  490. return false;
  491. // If we're spawning from a pool, ensure the pool has an antlion in it
  492. if ( m_iMaxPool && !m_iPool )
  493. return false;
  494. if ( (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI) == bits_debugDisableAI )
  495. return false;
  496. return true;
  497. }
  498. void CAntlionTemplateMaker::Enable( void )
  499. {
  500. BaseClass::Enable();
  501. if ( m_iMaxPool )
  502. {
  503. SetContextThink( &CAntlionTemplateMaker::PoolRegenThink, gpGlobals->curtime + m_flPoolRegenTime, s_pPoolThinkContext );
  504. }
  505. if ( hl2_episodic.GetBool() == true && HasSpawnFlags( SF_ANTLIONMAKER_DO_BLOCKEDEFFECTS ) )
  506. {
  507. SetContextThink( &CAntlionTemplateMaker::FindNodesCloseToPlayer, gpGlobals->curtime + 1.0f, s_pBlockedEffectsThinkContext );
  508. }
  509. ActivateAllSpores();
  510. }
  511. void CAntlionTemplateMaker::Disable( void )
  512. {
  513. BaseClass::Disable();
  514. SetContextThink( NULL, gpGlobals->curtime, s_pPoolThinkContext );
  515. SetContextThink( NULL, gpGlobals->curtime, s_pBlockedEffectsThinkContext );
  516. DisableAllSpores();
  517. }
  518. //-----------------------------------------------------------------------------
  519. // Randomly turn it into an antlion worker if that is enabled for this maker.
  520. //-----------------------------------------------------------------------------
  521. void CAntlionTemplateMaker::ChildPreSpawn( CAI_BaseNPC *pChild )
  522. {
  523. BaseClass::ChildPreSpawn( pChild );
  524. if ( ( m_flWorkerSpawnRate > 0 ) && ( random->RandomFloat( 0, 1 ) < m_flWorkerSpawnRate ) )
  525. {
  526. pChild->AddSpawnFlags( SF_ANTLION_WORKER );
  527. }
  528. }
  529. //-----------------------------------------------------------------------------
  530. // Purpose:
  531. //-----------------------------------------------------------------------------
  532. void CAntlionTemplateMaker::MakeNPC( void )
  533. {
  534. // If we're not restricting to hint groups, spawn as normal
  535. if ( !HasSpawnFlags( SF_ANTLIONMAKER_SPAWN_CLOSE_TO_TARGET ) )
  536. {
  537. if ( m_strSpawnGroup == NULL_STRING )
  538. {
  539. BaseClass::MakeNPC();
  540. return;
  541. }
  542. }
  543. if ( CanMakeNPC( true ) == false )
  544. return;
  545. // Set our defaults
  546. Vector targetOrigin = GetAbsOrigin();
  547. QAngle targetAngles = GetAbsAngles();
  548. // Look for our target entity
  549. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_strSpawnTarget, this );
  550. // Take its position if it exists
  551. if ( pTarget != NULL )
  552. {
  553. UTIL_PredictedPosition( pTarget, 1.5f, &targetOrigin );
  554. }
  555. Vector spawnOrigin = vec3_origin;
  556. CAI_Hint *pNode = NULL;
  557. bool bRandom = HasSpawnFlags( SF_ANTLIONMAKER_RANDOM_SPAWN_NODE );
  558. if ( HasSpawnFlags( SF_ANTLIONMAKER_SPAWN_CLOSE_TO_TARGET ) )
  559. {
  560. if ( FindNearTargetSpawnPosition( spawnOrigin, m_flSpawnRadius, pTarget ) == false )
  561. return;
  562. }
  563. else
  564. {
  565. // If we can't find a spawn position, then we can't spawn this time
  566. if ( FindHintSpawnPosition( targetOrigin, m_flSpawnRadius, m_strSpawnGroup, &pNode, bRandom ) == false )
  567. return;
  568. pNode->GetPosition( HULL_MEDIUM, &spawnOrigin );
  569. }
  570. // Point at the current position of the enemy
  571. if ( pTarget != NULL )
  572. {
  573. targetOrigin = pTarget->GetAbsOrigin();
  574. }
  575. // Create the entity via a template
  576. CAI_BaseNPC *pent = NULL;
  577. CBaseEntity *pEntity = NULL;
  578. MapEntity_ParseEntity( pEntity, STRING(m_iszTemplateData), NULL );
  579. if ( pEntity != NULL )
  580. {
  581. pent = (CAI_BaseNPC *) pEntity;
  582. }
  583. if ( pent == NULL )
  584. {
  585. Warning("NULL Ent in NPCMaker!\n" );
  586. return;
  587. }
  588. if ( !HasSpawnFlags( SF_ANTLIONMAKER_SPAWN_CLOSE_TO_TARGET ) )
  589. {
  590. // Lock this hint node
  591. pNode->Lock( pEntity );
  592. // Unlock it in two seconds, this forces subsequent antlions to
  593. // reject this point as a spawn point to spread them out a bit
  594. pNode->Unlock( 2.0f );
  595. }
  596. m_OnSpawnNPC.Set( pEntity, pEntity, this );
  597. pent->AddSpawnFlags( SF_NPC_FALL_TO_GROUND );
  598. ChildPreSpawn( pent );
  599. // Put us at the desired location
  600. pent->SetLocalOrigin( spawnOrigin );
  601. QAngle spawnAngles;
  602. if ( pTarget )
  603. {
  604. // Face our spawning direction
  605. Vector spawnDir = ( targetOrigin - spawnOrigin );
  606. VectorNormalize( spawnDir );
  607. VectorAngles( spawnDir, spawnAngles );
  608. spawnAngles[PITCH] = 0.0f;
  609. spawnAngles[ROLL] = 0.0f;
  610. }
  611. else if ( pNode )
  612. {
  613. spawnAngles = QAngle( 0, pNode->Yaw(), 0 );
  614. }
  615. pent->SetLocalAngles( spawnAngles );
  616. DispatchSpawn( pent );
  617. pent->Activate();
  618. m_iSkinCount = ( m_iSkinCount + 1 ) % ANTLION_SKIN_COUNT;
  619. pent->m_nSkin = m_iSkinCount;
  620. ChildPostSpawn( pent );
  621. // Hold onto the child
  622. CNPC_Antlion *pAntlion = dynamic_cast<CNPC_Antlion *>(pent);
  623. AddChild( pAntlion );
  624. m_bBlocked = false;
  625. SetContextThink( NULL, -1, s_pBlockedCheckContext );
  626. pAntlion->ClearBurrowPoint( spawnOrigin );
  627. if (!(m_spawnflags & SF_NPCMAKER_INF_CHILD))
  628. {
  629. if ( m_iMaxPool )
  630. {
  631. m_iPool--;
  632. if ( g_debug_antlionmaker.GetInt() == 2 )
  633. {
  634. Msg("SPAWNED: Pool: %d (max %d) (Regenerating %d every %f)\n", m_iPool, m_iMaxPool, m_iPoolRegenAmount, m_flPoolRegenTime );
  635. }
  636. }
  637. else
  638. {
  639. m_nMaxNumNPCs--;
  640. }
  641. if ( IsDepleted() )
  642. {
  643. m_OnAllSpawned.FireOutput( this, this );
  644. // Disable this forever. Don't kill it because it still gets death notices
  645. SetThink( NULL );
  646. SetUse( NULL );
  647. }
  648. }
  649. }
  650. bool CAntlionTemplateMaker::FindPositionOnFoot( Vector &origin, float radius, CBaseEntity *pTarget )
  651. {
  652. int iMaxTries = 10;
  653. Vector vSpawnOrigin = pTarget->GetAbsOrigin();
  654. while ( iMaxTries > 0 )
  655. {
  656. vSpawnOrigin.x += random->RandomFloat( -radius, radius );
  657. vSpawnOrigin.y += random->RandomFloat( -radius, radius );
  658. vSpawnOrigin.z += 96;
  659. if ( ValidateSpawnPosition( vSpawnOrigin, pTarget ) == false )
  660. {
  661. iMaxTries--;
  662. continue;
  663. }
  664. origin = vSpawnOrigin;
  665. return true;
  666. }
  667. return false;
  668. }
  669. bool CAntlionTemplateMaker::FindPositionOnVehicle( Vector &origin, float radius, CBaseEntity *pTarget )
  670. {
  671. int iMaxTries = 10;
  672. Vector vSpawnOrigin = pTarget->GetAbsOrigin();
  673. vSpawnOrigin.z += 96;
  674. if ( pTarget == NULL )
  675. return false;
  676. while ( iMaxTries > 0 )
  677. {
  678. Vector vForward, vRight;
  679. pTarget->GetVectors( &vForward, &vRight, NULL );
  680. float flSpeed = (pTarget->GetSmoothedVelocity().Length() * m_flVehicleSpawnDistance) * random->RandomFloat( 1.0f, 1.5f );
  681. vSpawnOrigin = vSpawnOrigin + (vForward * flSpeed) + vRight * random->RandomFloat( -radius, radius );
  682. if ( ValidateSpawnPosition( vSpawnOrigin, pTarget ) == false )
  683. {
  684. iMaxTries--;
  685. continue;
  686. }
  687. origin = vSpawnOrigin;
  688. return true;
  689. }
  690. return false;
  691. }
  692. bool CAntlionTemplateMaker::ValidateSpawnPosition( Vector &vOrigin, CBaseEntity *pTarget )
  693. {
  694. trace_t tr;
  695. UTIL_TraceLine( vOrigin, vOrigin - Vector( 0, 0, 1024 ), MASK_BLOCKLOS | CONTENTS_WATER, NULL, COLLISION_GROUP_NONE, &tr );
  696. if ( g_debug_antlionmaker.GetInt() == 1 )
  697. NDebugOverlay::Line( vOrigin, tr.endpos, 0, 255, 0, false, 5 );
  698. // Make sure this point is clear
  699. if ( tr.fraction != 1.0 )
  700. {
  701. if ( tr.contents & ( CONTENTS_WATER ) )
  702. return false;
  703. const surfacedata_t *psurf = physprops->GetSurfaceData( tr.surface.surfaceProps );
  704. if ( psurf )
  705. {
  706. if ( g_debug_antlionmaker.GetInt() == 1 )
  707. {
  708. char szText[16];
  709. Q_snprintf( szText, 16, "Material %c", psurf->game.material );
  710. NDebugOverlay::Text( vOrigin, szText, true, 5 );
  711. }
  712. if ( psurf->game.material != CHAR_TEX_SAND )
  713. return false;
  714. }
  715. if ( CAntlionRepellant::IsPositionRepellantFree( tr.endpos ) == false )
  716. return false;
  717. trace_t trCheck;
  718. UTIL_TraceHull( tr.endpos, tr.endpos + Vector(0,0,5), NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), MASK_NPCSOLID, NULL, COLLISION_GROUP_NONE, &trCheck );
  719. if ( trCheck.DidHit() == false )
  720. {
  721. if ( g_debug_antlionmaker.GetInt() == 1 )
  722. NDebugOverlay::Box( tr.endpos + Vector(0,0,5), NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 0, 255, 0, 128, 5 );
  723. if ( pTarget )
  724. {
  725. if ( pTarget->IsPlayer() )
  726. {
  727. CBaseEntity *pVehicle = NULL;
  728. CBasePlayer *pPlayer = dynamic_cast < CBasePlayer *> ( pTarget );
  729. if ( pPlayer && pPlayer->GetVehicle() )
  730. pVehicle = ((CBasePlayer *)pTarget)->GetVehicle()->GetVehicleEnt();
  731. CTraceFilterSkipTwoEntities traceFilter( pPlayer, pVehicle, COLLISION_GROUP_NONE );
  732. trace_t trVerify;
  733. Vector vVerifyOrigin = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset();
  734. float flZOffset = NAI_Hull::Maxs( HULL_MEDIUM ).z;
  735. UTIL_TraceLine( vVerifyOrigin, tr.endpos + Vector( 0, 0, flZOffset ), MASK_BLOCKLOS | CONTENTS_WATER, &traceFilter, &trVerify );
  736. if ( trVerify.fraction != 1.0f )
  737. {
  738. const surfacedata_t *psurf = physprops->GetSurfaceData( trVerify.surface.surfaceProps );
  739. if ( psurf )
  740. {
  741. if ( psurf->game.material == CHAR_TEX_DIRT )
  742. {
  743. if ( g_debug_antlionmaker.GetInt() == 1 )
  744. {
  745. NDebugOverlay::Line( vVerifyOrigin, trVerify.endpos, 255, 0, 0, false, 5 );
  746. }
  747. return false;
  748. }
  749. }
  750. }
  751. if ( g_debug_antlionmaker.GetInt() == 1 )
  752. {
  753. NDebugOverlay::Line( vVerifyOrigin, trVerify.endpos, 0, 255, 0, false, 5 );
  754. }
  755. }
  756. }
  757. vOrigin = trCheck.endpos + Vector(0,0,5);
  758. return true;
  759. }
  760. else
  761. {
  762. if ( g_debug_antlionmaker.GetInt() == 1 )
  763. NDebugOverlay::Box( tr.endpos + Vector(0,0,5), NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 255, 0, 0, 128, 5 );
  764. return false;
  765. }
  766. }
  767. return false;
  768. }
  769. //-----------------------------------------------------------------------------
  770. // Purpose: Find a position near the player to spawn the new antlion at
  771. // Input : &origin - search origin
  772. // radius - search radius
  773. // *retOrigin - found origin (if any)
  774. // Output : Returns true on success, false on failure.
  775. //-----------------------------------------------------------------------------
  776. bool CAntlionTemplateMaker::FindNearTargetSpawnPosition( Vector &origin, float radius, CBaseEntity *pTarget )
  777. {
  778. if ( pTarget )
  779. {
  780. CBaseEntity *pVehicle = NULL;
  781. if ( pTarget->IsPlayer() )
  782. {
  783. CBasePlayer *pPlayer = ((CBasePlayer *)pTarget);
  784. if ( pPlayer->GetVehicle() )
  785. pVehicle = ((CBasePlayer *)pTarget)->GetVehicle()->GetVehicleEnt();
  786. }
  787. if ( pVehicle )
  788. return FindPositionOnVehicle( origin, radius, pVehicle );
  789. else
  790. return FindPositionOnFoot( origin, radius, pTarget );
  791. }
  792. return false;
  793. }
  794. //-----------------------------------------------------------------------------
  795. // Purpose: Find a hint position to spawn the new antlion at
  796. // Input : &origin - search origin
  797. // radius - search radius
  798. // hintGroupName - search hint group name
  799. // *retOrigin - found origin (if any)
  800. // Output : Returns true on success, false on failure.
  801. //-----------------------------------------------------------------------------
  802. bool CAntlionTemplateMaker::FindHintSpawnPosition( const Vector &origin, float radius, string_t hintGroupName, CAI_Hint **pHint, bool bRandom )
  803. {
  804. CAI_Hint *pChosenHint = NULL;
  805. CHintCriteria hintCriteria;
  806. hintCriteria.SetGroup( hintGroupName );
  807. hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );
  808. if ( bRandom )
  809. {
  810. hintCriteria.SetFlag( bits_HINT_NODE_RANDOM );
  811. }
  812. else
  813. {
  814. hintCriteria.SetFlag( bits_HINT_NODE_NEAREST );
  815. }
  816. // If requested, deny nodes that can be seen by the player
  817. if ( m_spawnflags & SF_NPCMAKER_HIDEFROMPLAYER )
  818. {
  819. hintCriteria.SetFlag( bits_HINT_NODE_NOT_VISIBLE_TO_PLAYER );
  820. }
  821. hintCriteria.AddIncludePosition( origin, radius );
  822. if ( bRandom == true )
  823. {
  824. pChosenHint = CAI_HintManager::FindHintRandom( NULL, origin, hintCriteria );
  825. }
  826. else
  827. {
  828. pChosenHint = CAI_HintManager::FindHint( origin, hintCriteria );
  829. }
  830. if ( pChosenHint != NULL )
  831. {
  832. bool bChosenHintBlocked = false;
  833. if ( AllHintsFromClusterBlocked( pChosenHint, bChosenHintBlocked ) )
  834. {
  835. if ( ( GetIndexForThinkContext( s_pBlockedCheckContext ) == NO_THINK_CONTEXT ) ||
  836. ( GetNextThinkTick( s_pBlockedCheckContext ) == TICK_NEVER_THINK ) )
  837. {
  838. SetContextThink( &CAntlionTemplateMaker::BlockedCheckFunc, gpGlobals->curtime + 2.0f, s_pBlockedCheckContext );
  839. }
  840. return false;
  841. }
  842. if ( bChosenHintBlocked == true )
  843. {
  844. return false;
  845. }
  846. *pHint = pChosenHint;
  847. return true;
  848. }
  849. return false;
  850. }
  851. void CAntlionTemplateMaker::DoBlockedEffects( CBaseEntity *pBlocker, Vector vOrigin )
  852. {
  853. // If the object blocking the hole is a physics object, wobble it a bit.
  854. if( pBlocker )
  855. {
  856. IPhysicsObject *pPhysObj = pBlocker->VPhysicsGetObject();
  857. if( pPhysObj && pPhysObj->IsAsleep() )
  858. {
  859. // Don't bonk the object unless it is at rest.
  860. float x = RandomFloat( -5000, 5000 );
  861. float y = RandomFloat( -5000, 5000 );
  862. Vector vecForce = Vector( x, y, RandomFloat(10000, 15000) );
  863. pPhysObj->ApplyForceCenter( vecForce );
  864. UTIL_CreateAntlionDust( vOrigin, vec3_angle, true );
  865. pBlocker->EmitSound( "NPC_Antlion.MeleeAttackSingle_Muffled" );
  866. pBlocker->EmitSound( "NPC_Antlion.TrappedMetal" );
  867. m_flBlockedBumpTime = gpGlobals->curtime + random->RandomFloat( 1.75, 2.75 );
  868. }
  869. }
  870. }
  871. CBaseEntity *CAntlionTemplateMaker::AllHintsFromClusterBlocked( CAI_Hint *pNode, bool &bChosenHintBlocked )
  872. {
  873. // Only do this for episodic content!
  874. if ( hl2_episodic.GetBool() == false )
  875. return NULL;
  876. CBaseEntity *pBlocker = NULL;
  877. if ( pNode != NULL )
  878. {
  879. int iNumBlocked = 0;
  880. int iNumNodes = 0;
  881. CHintCriteria hintCriteria;
  882. hintCriteria.SetGroup( m_strSpawnGroup );
  883. hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );
  884. CUtlVector<CAI_Hint *> hintList;
  885. CAI_HintManager::FindAllHints( vec3_origin, hintCriteria, &hintList );
  886. for ( int i = 0; i < hintList.Count(); i++ )
  887. {
  888. CAI_Hint *pTestHint = hintList[i];
  889. if ( pTestHint )
  890. {
  891. if ( pTestHint->NameMatches( pNode->GetEntityName() ) )
  892. {
  893. bool bBlocked;
  894. iNumNodes++;
  895. Vector spawnOrigin;
  896. pTestHint->GetPosition( HULL_MEDIUM, &spawnOrigin );
  897. bBlocked = false;
  898. CBaseEntity* pList[20];
  899. int count = UTIL_EntitiesInBox( pList, 20, spawnOrigin + NAI_Hull::Mins( HULL_MEDIUM ), spawnOrigin + NAI_Hull::Maxs( HULL_MEDIUM ), 0 );
  900. //Iterate over all the possible targets
  901. for ( int i = 0; i < count; i++ )
  902. {
  903. if ( pList[i]->GetMoveType() != MOVETYPE_VPHYSICS )
  904. continue;
  905. if ( PhysGetEntityMass( pList[i] ) > ANTLION_MAKER_BLOCKED_MASS )
  906. {
  907. bBlocked = true;
  908. iNumBlocked++;
  909. pBlocker = pList[i];
  910. if ( pTestHint == pNode )
  911. {
  912. bChosenHintBlocked = true;
  913. }
  914. break;
  915. }
  916. }
  917. if ( g_debug_antlionmaker.GetInt() == 1 )
  918. {
  919. if ( bBlocked )
  920. {
  921. NDebugOverlay::Box( spawnOrigin + Vector(0,0,5), NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 255, 0, 0, 128, 0.25f );
  922. }
  923. else
  924. {
  925. NDebugOverlay::Box( spawnOrigin + Vector(0,0,5), NAI_Hull::Mins( HULL_MEDIUM ), NAI_Hull::Maxs( HULL_MEDIUM ), 0, 255, 0, 128, 0.25f );
  926. }
  927. }
  928. }
  929. }
  930. }
  931. //All the nodes from this cluster are blocked so start playing the effects.
  932. if ( iNumNodes > 0 && iNumBlocked == iNumNodes )
  933. {
  934. return pBlocker;
  935. }
  936. }
  937. return NULL;
  938. }
  939. void CAntlionTemplateMaker::FindNodesCloseToPlayer( void )
  940. {
  941. SetContextThink( &CAntlionTemplateMaker::FindNodesCloseToPlayer, gpGlobals->curtime + random->RandomFloat( 0.75, 1.75 ), s_pBlockedEffectsThinkContext );
  942. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  943. if ( pPlayer == NULL )
  944. return;
  945. CHintCriteria hintCriteria;
  946. float flRadius = ANTLION_MAKER_PLAYER_DETECT_RADIUS;
  947. hintCriteria.SetGroup( m_strSpawnGroup );
  948. hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );
  949. hintCriteria.AddIncludePosition( pPlayer->GetAbsOrigin(), ANTLION_MAKER_PLAYER_DETECT_RADIUS );
  950. CUtlVector<CAI_Hint *> hintList;
  951. if ( CAI_HintManager::FindAllHints( vec3_origin, hintCriteria, &hintList ) <= 0 )
  952. return;
  953. CUtlVector<string_t> m_BlockedNames;
  954. //----
  955. //What we do here is find all hints of the same name (cluster name) and figure out if all of them are blocked.
  956. //If they are then we only need to play the blocked effects once
  957. //---
  958. for ( int i = 0; i < hintList.Count(); i++ )
  959. {
  960. CAI_Hint *pNode = hintList[i];
  961. if ( pNode && pNode->HintMatchesCriteria( NULL, hintCriteria, pPlayer->GetAbsOrigin(), &flRadius ) )
  962. {
  963. bool bClusterAlreadyBlocked = false;
  964. //Have one of the nodes from this cluster been checked for blockage? If so then there's no need to do block checks again for this cluster.
  965. for ( int iStringCount = 0; iStringCount < m_BlockedNames.Count(); iStringCount++ )
  966. {
  967. if ( pNode->NameMatches( m_BlockedNames[iStringCount] ) )
  968. {
  969. bClusterAlreadyBlocked = true;
  970. break;
  971. }
  972. }
  973. if ( bClusterAlreadyBlocked == true )
  974. continue;
  975. Vector vHintPos;
  976. pNode->GetPosition( HULL_MEDIUM, &vHintPos );
  977. bool bBlank;
  978. if ( CBaseEntity *pBlocker = AllHintsFromClusterBlocked( pNode, bBlank ) )
  979. {
  980. DisableSpore( STRING( pNode->GetEntityName() ) );
  981. DoBlockedEffects( pBlocker, vHintPos );
  982. m_BlockedNames.AddToTail( pNode->GetEntityName() );
  983. }
  984. else
  985. {
  986. ActivateSpore( STRING( pNode->GetEntityName() ), pNode->GetAbsOrigin() );
  987. }
  988. }
  989. }
  990. }
  991. void CAntlionTemplateMaker::BlockedCheckFunc( void )
  992. {
  993. SetContextThink( &CAntlionTemplateMaker::BlockedCheckFunc, -1, s_pBlockedCheckContext );
  994. if ( m_bBlocked == true )
  995. return;
  996. CUtlVector<CAI_Hint *> hintList;
  997. int iBlocked = 0;
  998. CHintCriteria hintCriteria;
  999. hintCriteria.SetGroup( m_strSpawnGroup );
  1000. hintCriteria.SetHintType( HINT_ANTLION_BURROW_POINT );
  1001. if ( CAI_HintManager::FindAllHints( vec3_origin, hintCriteria, &hintList ) > 0 )
  1002. {
  1003. for ( int i = 0; i < hintList.Count(); i++ )
  1004. {
  1005. CAI_Hint *pNode = hintList[i];
  1006. if ( pNode )
  1007. {
  1008. Vector vHintPos;
  1009. pNode->GetPosition( AI_GetSinglePlayer(), &vHintPos );
  1010. CBaseEntity* pList[20];
  1011. int count = UTIL_EntitiesInBox( pList, 20, vHintPos + NAI_Hull::Mins( HULL_MEDIUM ), vHintPos + NAI_Hull::Maxs( HULL_MEDIUM ), 0 );
  1012. //Iterate over all the possible targets
  1013. for ( int i = 0; i < count; i++ )
  1014. {
  1015. if ( pList[i]->GetMoveType() != MOVETYPE_VPHYSICS )
  1016. continue;
  1017. if ( PhysGetEntityMass( pList[i] ) > ANTLION_MAKER_BLOCKED_MASS )
  1018. {
  1019. iBlocked++;
  1020. break;
  1021. }
  1022. }
  1023. }
  1024. }
  1025. }
  1026. if ( iBlocked > 0 && hintList.Count() == iBlocked )
  1027. {
  1028. m_bBlocked = true;
  1029. m_OnAllBlocked.FireOutput( this, this );
  1030. }
  1031. }
  1032. //-----------------------------------------------------------------------------
  1033. // Purpose: Makes the antlion immediatley unburrow if it started burrowed
  1034. //-----------------------------------------------------------------------------
  1035. void CAntlionTemplateMaker::ChildPostSpawn( CAI_BaseNPC *pChild )
  1036. {
  1037. CNPC_Antlion *pAntlion = static_cast<CNPC_Antlion*>(pChild);
  1038. if ( pAntlion == NULL )
  1039. return;
  1040. // Unburrow the spawned antlion immediately
  1041. if ( pAntlion->m_bStartBurrowed )
  1042. {
  1043. pAntlion->BurrowUse( this, this, USE_ON, 0.0f );
  1044. }
  1045. // Set us to a follow target, if we have one
  1046. if ( GetFollowTarget() )
  1047. {
  1048. pAntlion->SetFollowTarget( GetFollowTarget() );
  1049. }
  1050. else if ( ( m_strFollowTarget != NULL_STRING ) )
  1051. {
  1052. // If we don't already have a fight target, set it up
  1053. SetFollowTarget( m_strFollowTarget );
  1054. if ( GetFightTarget() == NULL )
  1055. {
  1056. SetChildMoveState( ANTLION_MOVE_FOLLOW );
  1057. // If it's valid, fight there
  1058. if ( GetFollowTarget() != NULL )
  1059. {
  1060. pAntlion->SetFollowTarget( GetFollowTarget() );
  1061. }
  1062. }
  1063. }
  1064. // See if we need to send them on their way to a fight goal
  1065. if ( GetFightTarget() && !HasSpawnFlags( SF_ANTLIONMAKER_RANDOM_FIGHT_TARGET ) )
  1066. {
  1067. pAntlion->SetFightTarget( GetFightTarget() );
  1068. }
  1069. else if ( m_strFightTarget != NULL_STRING )
  1070. {
  1071. // If we don't already have a fight target, set it up
  1072. SetFightTarget( m_strFightTarget );
  1073. SetChildMoveState( ANTLION_MOVE_FIGHT_TO_GOAL );
  1074. // If it's valid, fight there
  1075. if ( GetFightTarget() != NULL )
  1076. {
  1077. pAntlion->SetFightTarget( GetFightTarget() );
  1078. }
  1079. }
  1080. // Set us to the desired movement state
  1081. pAntlion->SetMoveState( m_nChildMoveState );
  1082. // Save our name for level transitions
  1083. pAntlion->SetParentSpawnerName( STRING( GetEntityName() ) );
  1084. if ( m_hIgnoreEntity != NULL )
  1085. {
  1086. pChild->SetOwnerEntity( m_hIgnoreEntity );
  1087. }
  1088. }
  1089. //-----------------------------------------------------------------------------
  1090. // Purpose:
  1091. // Input : &inputdata -
  1092. //-----------------------------------------------------------------------------
  1093. void CAntlionTemplateMaker::InputSetFightTarget( inputdata_t &inputdata )
  1094. {
  1095. // Set our new goal
  1096. m_strFightTarget = MAKE_STRING( inputdata.value.String() );
  1097. SetFightTarget( m_strFightTarget, inputdata.pActivator, inputdata.pCaller );
  1098. SetChildMoveState( ANTLION_MOVE_FIGHT_TO_GOAL );
  1099. UpdateChildren();
  1100. }
  1101. //-----------------------------------------------------------------------------
  1102. // Purpose:
  1103. // Input : &inputdata -
  1104. //-----------------------------------------------------------------------------
  1105. void CAntlionTemplateMaker::InputSetFollowTarget( inputdata_t &inputdata )
  1106. {
  1107. // Set our new goal
  1108. m_strFollowTarget = MAKE_STRING( inputdata.value.String() );
  1109. SetFollowTarget( m_strFollowTarget, inputdata.pActivator, inputdata.pCaller );
  1110. SetChildMoveState( ANTLION_MOVE_FOLLOW );
  1111. UpdateChildren();
  1112. }
  1113. //-----------------------------------------------------------------------------
  1114. // Purpose:
  1115. // Input : &inputdata -
  1116. //-----------------------------------------------------------------------------
  1117. void CAntlionTemplateMaker::InputClearFightTarget( inputdata_t &inputdata )
  1118. {
  1119. SetFightTarget( NULL );
  1120. SetChildMoveState( ANTLION_MOVE_FOLLOW );
  1121. UpdateChildren();
  1122. }
  1123. //-----------------------------------------------------------------------------
  1124. // Purpose:
  1125. // Input : &inputdata -
  1126. //-----------------------------------------------------------------------------
  1127. void CAntlionTemplateMaker::InputClearFollowTarget( inputdata_t &inputdata )
  1128. {
  1129. SetFollowTarget( NULL );
  1130. SetChildMoveState( ANTLION_MOVE_FIGHT_TO_GOAL );
  1131. UpdateChildren();
  1132. }
  1133. //-----------------------------------------------------------------------------
  1134. // Purpose:
  1135. // Input : &inputdata -
  1136. //-----------------------------------------------------------------------------
  1137. void CAntlionTemplateMaker::InputSetSpawnRadius( inputdata_t &inputdata )
  1138. {
  1139. m_flSpawnRadius = inputdata.value.Float();
  1140. }
  1141. //-----------------------------------------------------------------------------
  1142. // Purpose:
  1143. // Input : &inputdata -
  1144. //-----------------------------------------------------------------------------
  1145. void CAntlionTemplateMaker::InputAddToPool( inputdata_t &inputdata )
  1146. {
  1147. PoolAdd( inputdata.value.Int() );
  1148. }
  1149. //-----------------------------------------------------------------------------
  1150. // Purpose:
  1151. // Input : &inputdata -
  1152. //-----------------------------------------------------------------------------
  1153. void CAntlionTemplateMaker::InputSetMaxPool( inputdata_t &inputdata )
  1154. {
  1155. m_iMaxPool = inputdata.value.Int();
  1156. if ( m_iPool > m_iMaxPool )
  1157. {
  1158. m_iPool = m_iMaxPool;
  1159. }
  1160. // Stop regenerating if we're supposed to stop using the pool
  1161. if ( !m_iMaxPool )
  1162. {
  1163. SetContextThink( NULL, gpGlobals->curtime, s_pPoolThinkContext );
  1164. }
  1165. }
  1166. //-----------------------------------------------------------------------------
  1167. // Purpose:
  1168. // Input : &inputdata -
  1169. //-----------------------------------------------------------------------------
  1170. void CAntlionTemplateMaker::InputSetPoolRegenAmount( inputdata_t &inputdata )
  1171. {
  1172. m_iPoolRegenAmount = inputdata.value.Int();
  1173. }
  1174. //-----------------------------------------------------------------------------
  1175. // Purpose:
  1176. // Input : &inputdata -
  1177. //-----------------------------------------------------------------------------
  1178. void CAntlionTemplateMaker::InputSetPoolRegenTime( inputdata_t &inputdata )
  1179. {
  1180. m_flPoolRegenTime = inputdata.value.Float();
  1181. if ( m_flPoolRegenTime != 0.0f )
  1182. {
  1183. SetContextThink( &CAntlionTemplateMaker::PoolRegenThink, gpGlobals->curtime + m_flPoolRegenTime, s_pPoolThinkContext );
  1184. }
  1185. else
  1186. {
  1187. SetContextThink( NULL, gpGlobals->curtime, s_pPoolThinkContext );
  1188. }
  1189. }
  1190. //-----------------------------------------------------------------------------
  1191. // Purpose: Pool behavior for coast
  1192. // Input : iNumToAdd -
  1193. //-----------------------------------------------------------------------------
  1194. void CAntlionTemplateMaker::PoolAdd( int iNumToAdd )
  1195. {
  1196. m_iPool = clamp( m_iPool + iNumToAdd, 0, m_iMaxPool );
  1197. }
  1198. //-----------------------------------------------------------------------------
  1199. // Purpose: Regenerate the pool
  1200. //-----------------------------------------------------------------------------
  1201. void CAntlionTemplateMaker::PoolRegenThink( void )
  1202. {
  1203. if ( m_iPool < m_iMaxPool )
  1204. {
  1205. m_iPool = clamp( m_iPool + m_iPoolRegenAmount, 0, m_iMaxPool );
  1206. if ( g_debug_antlionmaker.GetInt() == 2 )
  1207. {
  1208. Msg("REGENERATED: Pool: %d (max %d) (Regenerating %d every %f)\n", m_iPool, m_iMaxPool, m_iPoolRegenAmount, m_flPoolRegenTime );
  1209. }
  1210. }
  1211. SetContextThink( &CAntlionTemplateMaker::PoolRegenThink, gpGlobals->curtime + m_flPoolRegenTime, s_pPoolThinkContext );
  1212. }
  1213. //-----------------------------------------------------------------------------
  1214. // Purpose:
  1215. // Input : *pVictim -
  1216. //-----------------------------------------------------------------------------
  1217. void CAntlionTemplateMaker::DeathNotice( CBaseEntity *pVictim )
  1218. {
  1219. CNPC_Antlion *pAnt = dynamic_cast<CNPC_Antlion *>(pVictim);
  1220. if ( pAnt == NULL )
  1221. return;
  1222. // Take it out of our list
  1223. RemoveChild( pAnt );
  1224. // Check if all live children are now dead
  1225. if ( m_nLiveChildren <= 0 )
  1226. {
  1227. // Fire the output for this case
  1228. m_OnAllLiveChildrenDead.FireOutput( this, this );
  1229. bool bPoolDepleted = ( m_iMaxPool != 0 && m_iPool == 0 );
  1230. if ( bPoolDepleted || IsDepleted() )
  1231. {
  1232. // Signal that all our children have been spawned and are now dead
  1233. m_OnAllSpawnedDead.FireOutput( this, this );
  1234. }
  1235. }
  1236. }
  1237. //-----------------------------------------------------------------------------
  1238. // Purpose: If this had a finite number of children, return true if they've all
  1239. // been created.
  1240. //-----------------------------------------------------------------------------
  1241. bool CAntlionTemplateMaker::IsDepleted( void )
  1242. {
  1243. // If we're running pool behavior, we're never depleted
  1244. if ( m_iMaxPool )
  1245. return false;
  1246. return BaseClass::IsDepleted();
  1247. }
  1248. //-----------------------------------------------------------------------------
  1249. // Purpose: Change the spawn group the maker is using
  1250. //-----------------------------------------------------------------------------
  1251. void CAntlionTemplateMaker::InputChangeDestinationGroup( inputdata_t &inputdata )
  1252. {
  1253. // FIXME: This function is redundant to the base class version, remove the m_strSpawnGroup
  1254. m_strSpawnGroup = inputdata.value.StringID();
  1255. }
  1256. //-----------------------------------------------------------------------------
  1257. // Purpose: Draw debugging text for the spawner
  1258. //-----------------------------------------------------------------------------
  1259. int CAntlionTemplateMaker::DrawDebugTextOverlays( void )
  1260. {
  1261. // We don't want the base class info, it's not useful to us
  1262. int text_offset = BaseClass::DrawDebugTextOverlays();
  1263. if ( m_debugOverlays & OVERLAY_TEXT_BIT )
  1264. {
  1265. char tempstr[255];
  1266. // Print the state of the spawner
  1267. if ( m_bDisabled )
  1268. {
  1269. Q_strncpy( tempstr, "State: Disabled\n", sizeof(tempstr) );
  1270. }
  1271. else
  1272. {
  1273. Q_strncpy( tempstr, "State: Enabled\n", sizeof(tempstr) );
  1274. }
  1275. EntityText( text_offset, tempstr, 0 );
  1276. text_offset++;
  1277. // Print follow information
  1278. if ( m_strFollowTarget != NULL_STRING )
  1279. {
  1280. Q_snprintf( tempstr, sizeof(tempstr), "Follow Target: %s\n", STRING( m_strFollowTarget ) );
  1281. }
  1282. else
  1283. {
  1284. Q_strncpy( tempstr, "Follow Target : NONE\n", sizeof(tempstr) );
  1285. }
  1286. EntityText( text_offset, tempstr, 0 );
  1287. text_offset++;
  1288. // Print fight information
  1289. if ( m_strFightTarget != NULL_STRING )
  1290. {
  1291. Q_snprintf( tempstr, sizeof(tempstr), "Fight Target: %s\n", STRING( m_strFightTarget ) );
  1292. }
  1293. else
  1294. {
  1295. Q_strncpy( tempstr, "Fight Target : NONE\n", sizeof(tempstr) );
  1296. }
  1297. EntityText( text_offset, tempstr, 0 );
  1298. text_offset++;
  1299. // Print spawning criteria information
  1300. if ( m_strSpawnTarget != NULL_STRING )
  1301. {
  1302. Q_snprintf( tempstr, sizeof(tempstr), "Spawn Target: %s\n", STRING( m_strSpawnTarget ) );
  1303. }
  1304. else
  1305. {
  1306. Q_strncpy( tempstr, "Spawn Target : NONE\n", sizeof(tempstr) );
  1307. }
  1308. EntityText( text_offset, tempstr, 0 );
  1309. text_offset++;
  1310. // Print the chilrens' state
  1311. Q_snprintf( tempstr, sizeof(tempstr), "Spawn Frequency: %f\n", m_flSpawnFrequency );
  1312. EntityText( text_offset, tempstr, 0 );
  1313. text_offset++;
  1314. // Print the spawn radius
  1315. Q_snprintf( tempstr, sizeof(tempstr), "Spawn Radius: %.02f units\n", m_flSpawnRadius );
  1316. EntityText( text_offset, tempstr, 0 );
  1317. text_offset++;
  1318. // Print the spawn group we're using
  1319. if ( m_strSpawnGroup != NULL_STRING )
  1320. {
  1321. Q_snprintf( tempstr, sizeof(tempstr), "Spawn Group: %s\n", STRING( m_strSpawnGroup ) );
  1322. EntityText( text_offset, tempstr, 0 );
  1323. text_offset++;
  1324. }
  1325. // Print the chilrens' state
  1326. Q_snprintf( tempstr, sizeof(tempstr), "Live Children: (%d/%d)\n", m_nLiveChildren, m_nMaxLiveChildren );
  1327. EntityText( text_offset, tempstr, 0 );
  1328. text_offset++;
  1329. // Print pool information
  1330. if ( m_iMaxPool )
  1331. {
  1332. // Print the pool's state
  1333. Q_snprintf( tempstr, sizeof(tempstr), "Pool: (%d/%d) (%d per regen)\n", m_iPool, m_iMaxPool, m_iPoolRegenAmount );
  1334. EntityText( text_offset, tempstr, 0 );
  1335. text_offset++;
  1336. float flTimeRemaining = GetNextThink( s_pPoolThinkContext ) - gpGlobals->curtime;
  1337. if ( flTimeRemaining < 0.0f )
  1338. {
  1339. flTimeRemaining = 0.0f;
  1340. }
  1341. // Print the pool's regeneration state
  1342. Q_snprintf( tempstr, sizeof(tempstr), "Pool Regen Time: %.02f sec. (%.02f remaining)\n", m_flPoolRegenTime, flTimeRemaining );
  1343. EntityText( text_offset, tempstr, 0 );
  1344. text_offset++;
  1345. }
  1346. }
  1347. return text_offset;
  1348. }
  1349. //-----------------------------------------------------------------------------
  1350. // Purpose: Draw debugging overlays for the spawner
  1351. //-----------------------------------------------------------------------------
  1352. void CAntlionTemplateMaker::DrawDebugGeometryOverlays( void )
  1353. {
  1354. BaseClass::DrawDebugGeometryOverlays();
  1355. if ( m_debugOverlays & OVERLAY_TEXT_BIT )
  1356. {
  1357. float r, g, b;
  1358. // Color by active state
  1359. if ( m_bDisabled )
  1360. {
  1361. r = 255.0f;
  1362. g = 0.0f;
  1363. b = 0.0f;
  1364. }
  1365. else
  1366. {
  1367. r = 0.0f;
  1368. g = 255.0f;
  1369. b = 0.0f;
  1370. }
  1371. // Draw ourself
  1372. NDebugOverlay::Box( GetAbsOrigin(), -Vector(8,8,8), Vector(8,8,8), r, g, b, true, 0.05f );
  1373. // Draw lines to our spawngroup hints
  1374. if ( m_strSpawnGroup != NULL_STRING )
  1375. {
  1376. // Draw lines to our active hint groups
  1377. AIHintIter_t iter;
  1378. CAI_Hint *pHint = CAI_HintManager::GetFirstHint( &iter );
  1379. while ( pHint != NULL )
  1380. {
  1381. // Must be of the hint group we care about
  1382. if ( pHint->GetGroup() != m_strSpawnGroup )
  1383. {
  1384. pHint = CAI_HintManager::GetNextHint( &iter );
  1385. continue;
  1386. }
  1387. // Draw an arrow to the spot
  1388. NDebugOverlay::VertArrow( GetAbsOrigin(), pHint->GetAbsOrigin() + Vector( 0, 0, 32 ), 8.0f, r, g, b, 0, true, 0.05f );
  1389. // Draw a box to represent where it's sitting
  1390. Vector vecForward;
  1391. AngleVectors( pHint->GetAbsAngles(), &vecForward );
  1392. NDebugOverlay::BoxDirection( pHint->GetAbsOrigin(), -Vector(32,32,0), Vector(32,32,16), vecForward, r, g, b, true, 0.05f );
  1393. // Move to the next
  1394. pHint = CAI_HintManager::GetNextHint( &iter );
  1395. }
  1396. }
  1397. // Draw a line to the spawn target (if it exists)
  1398. if ( m_strSpawnTarget != NULL_STRING )
  1399. {
  1400. // Find all the possible targets
  1401. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_strSpawnTarget );
  1402. if ( pTarget != NULL )
  1403. {
  1404. NDebugOverlay::VertArrow( GetAbsOrigin(), pTarget->WorldSpaceCenter(), 4.0f, 255, 255, 255, 0, true, 0.05f );
  1405. }
  1406. }
  1407. // Draw a line to the follow target (if it exists)
  1408. if ( m_strFollowTarget != NULL_STRING )
  1409. {
  1410. // Find all the possible targets
  1411. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_strFollowTarget );
  1412. if ( pTarget != NULL )
  1413. {
  1414. NDebugOverlay::VertArrow( GetAbsOrigin(), pTarget->WorldSpaceCenter(), 4.0f, 255, 255, 0, 0, true, 0.05f );
  1415. }
  1416. }
  1417. // Draw a line to the fight target (if it exists)
  1418. if ( m_strFightTarget != NULL_STRING )
  1419. {
  1420. // Find all the possible targets
  1421. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_strFightTarget );
  1422. if ( pTarget != NULL )
  1423. {
  1424. NDebugOverlay::VertArrow( GetAbsOrigin(), pTarget->WorldSpaceCenter(), 4.0f, 255, 0, 0, 0, true, 0.05f );
  1425. }
  1426. }
  1427. }
  1428. }