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.

1083 lines
29 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: An entity that creates NPCs in the game. There are two types of NPC
  4. // makers -- one which creates NPCs using a template NPC, and one which
  5. // creates an NPC via a classname.
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "datacache/imdlcache.h"
  10. #include "entityapi.h"
  11. #include "entityoutput.h"
  12. #include "ai_basenpc.h"
  13. #include "monstermaker.h"
  14. #include "TemplateEntities.h"
  15. #include "ndebugoverlay.h"
  16. #include "mapentities.h"
  17. #include "IEffects.h"
  18. #include "props.h"
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. static void DispatchActivate( CBaseEntity *pEntity )
  22. {
  23. bool bAsyncAnims = mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, false );
  24. pEntity->Activate();
  25. mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, bAsyncAnims );
  26. }
  27. ConVar ai_inhibit_spawners( "ai_inhibit_spawners", "0", FCVAR_CHEAT );
  28. LINK_ENTITY_TO_CLASS( info_npc_spawn_destination, CNPCSpawnDestination );
  29. BEGIN_DATADESC( CNPCSpawnDestination )
  30. DEFINE_KEYFIELD( m_ReuseDelay, FIELD_FLOAT, "ReuseDelay" ),
  31. DEFINE_KEYFIELD( m_RenameNPC,FIELD_STRING, "RenameNPC" ),
  32. DEFINE_FIELD( m_TimeNextAvailable, FIELD_TIME ),
  33. DEFINE_OUTPUT( m_OnSpawnNPC, "OnSpawnNPC" ),
  34. END_DATADESC()
  35. //---------------------------------------------------------
  36. //---------------------------------------------------------
  37. CNPCSpawnDestination::CNPCSpawnDestination()
  38. {
  39. // Available right away, the first time.
  40. m_TimeNextAvailable = gpGlobals->curtime;
  41. }
  42. //---------------------------------------------------------
  43. //---------------------------------------------------------
  44. bool CNPCSpawnDestination::IsAvailable()
  45. {
  46. if( m_TimeNextAvailable > gpGlobals->curtime )
  47. {
  48. return false;
  49. }
  50. return true;
  51. }
  52. //---------------------------------------------------------
  53. //---------------------------------------------------------
  54. void CNPCSpawnDestination::OnSpawnedNPC( CAI_BaseNPC *pNPC )
  55. {
  56. // Rename the NPC
  57. if( m_RenameNPC != NULL_STRING )
  58. {
  59. pNPC->SetName( m_RenameNPC );
  60. }
  61. m_OnSpawnNPC.FireOutput( pNPC, this );
  62. m_TimeNextAvailable = gpGlobals->curtime + m_ReuseDelay;
  63. }
  64. //-------------------------------------
  65. BEGIN_DATADESC( CBaseNPCMaker )
  66. DEFINE_KEYFIELD( m_nMaxNumNPCs, FIELD_INTEGER, "MaxNPCCount" ),
  67. DEFINE_KEYFIELD( m_nMaxLiveChildren, FIELD_INTEGER, "MaxLiveChildren" ),
  68. DEFINE_KEYFIELD( m_flSpawnFrequency, FIELD_FLOAT, "SpawnFrequency" ),
  69. DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  70. DEFINE_FIELD( m_nLiveChildren, FIELD_INTEGER ),
  71. // Inputs
  72. DEFINE_INPUTFUNC( FIELD_VOID, "Spawn", InputSpawnNPC ),
  73. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  74. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  75. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  76. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMaxChildren", InputSetMaxChildren ),
  77. DEFINE_INPUTFUNC( FIELD_INTEGER, "AddMaxChildren", InputAddMaxChildren ),
  78. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMaxLiveChildren", InputSetMaxLiveChildren ),
  79. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpawnFrequency", InputSetSpawnFrequency ),
  80. // Outputs
  81. DEFINE_OUTPUT( m_OnAllSpawned, "OnAllSpawned" ),
  82. DEFINE_OUTPUT( m_OnAllSpawnedDead, "OnAllSpawnedDead" ),
  83. DEFINE_OUTPUT( m_OnAllLiveChildrenDead, "OnAllLiveChildrenDead" ),
  84. DEFINE_OUTPUT( m_OnSpawnNPC, "OnSpawnNPC" ),
  85. // Function Pointers
  86. DEFINE_THINKFUNC( MakerThink ),
  87. DEFINE_FIELD( m_hIgnoreEntity, FIELD_EHANDLE ),
  88. DEFINE_KEYFIELD( m_iszIngoreEnt, FIELD_STRING, "IgnoreEntity" ),
  89. END_DATADESC()
  90. //-----------------------------------------------------------------------------
  91. // Purpose: Spawn
  92. //-----------------------------------------------------------------------------
  93. void CBaseNPCMaker::Spawn( void )
  94. {
  95. SetSolid( SOLID_NONE );
  96. m_nLiveChildren = 0;
  97. Precache();
  98. // If I can make an infinite number of NPC, force them to fade
  99. if ( m_spawnflags & SF_NPCMAKER_INF_CHILD )
  100. {
  101. m_spawnflags |= SF_NPCMAKER_FADE;
  102. }
  103. //Start on?
  104. if ( m_bDisabled == false )
  105. {
  106. SetThink ( &CBaseNPCMaker::MakerThink );
  107. SetNextThink( gpGlobals->curtime + 0.1f );
  108. }
  109. else
  110. {
  111. //wait to be activated.
  112. SetThink ( &CBaseNPCMaker::SUB_DoNothing );
  113. }
  114. }
  115. //-----------------------------------------------------------------------------
  116. // A not-very-robust check to see if a human hull could fit at this location.
  117. // used to validate spawn destinations.
  118. //-----------------------------------------------------------------------------
  119. bool CBaseNPCMaker::HumanHullFits( const Vector &vecLocation )
  120. {
  121. trace_t tr;
  122. UTIL_TraceHull( vecLocation,
  123. vecLocation + Vector( 0, 0, 1 ),
  124. NAI_Hull::Mins(HULL_HUMAN),
  125. NAI_Hull::Maxs(HULL_HUMAN),
  126. MASK_NPCSOLID,
  127. m_hIgnoreEntity,
  128. COLLISION_GROUP_NONE,
  129. &tr );
  130. if( tr.fraction == 1.0 )
  131. return true;
  132. return false;
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Purpose: Returns whether or not it is OK to make an NPC at this instant.
  136. //-----------------------------------------------------------------------------
  137. bool CBaseNPCMaker::CanMakeNPC( bool bIgnoreSolidEntities )
  138. {
  139. if( ai_inhibit_spawners.GetBool() )
  140. return false;
  141. if ( m_nMaxLiveChildren > 0 && m_nLiveChildren >= m_nMaxLiveChildren )
  142. {// not allowed to make a new one yet. Too many live ones out right now.
  143. return false;
  144. }
  145. if ( m_iszIngoreEnt != NULL_STRING )
  146. {
  147. m_hIgnoreEntity = gEntList.FindEntityByName( NULL, m_iszIngoreEnt );
  148. }
  149. Vector mins = GetAbsOrigin() - Vector( 34, 34, 0 );
  150. Vector maxs = GetAbsOrigin() + Vector( 34, 34, 0 );
  151. maxs.z = GetAbsOrigin().z;
  152. // If we care about not hitting solid entities, look for 'em
  153. if ( !bIgnoreSolidEntities )
  154. {
  155. CBaseEntity *pList[128];
  156. int count = UTIL_EntitiesInBox( pList, 128, mins, maxs, FL_CLIENT|FL_NPC );
  157. if ( count )
  158. {
  159. //Iterate through the list and check the results
  160. for ( int i = 0; i < count; i++ )
  161. {
  162. //Don't build on top of another entity
  163. if ( pList[i] == NULL )
  164. continue;
  165. //If one of the entities is solid, then we may not be able to spawn now
  166. if ( ( pList[i]->GetSolidFlags() & FSOLID_NOT_SOLID ) == false )
  167. {
  168. // Since the outer method doesn't work well around striders on account of their huge bounding box.
  169. // Find the ground under me and see if a human hull would fit there.
  170. trace_t tr;
  171. UTIL_TraceHull( GetAbsOrigin() + Vector( 0, 0, 2 ),
  172. GetAbsOrigin() - Vector( 0, 0, 8192 ),
  173. NAI_Hull::Mins(HULL_HUMAN),
  174. NAI_Hull::Maxs(HULL_HUMAN),
  175. MASK_NPCSOLID,
  176. m_hIgnoreEntity,
  177. COLLISION_GROUP_NONE,
  178. &tr );
  179. if( !HumanHullFits( tr.endpos + Vector( 0, 0, 1 ) ) )
  180. {
  181. return false;
  182. }
  183. }
  184. }
  185. }
  186. }
  187. // Do we need to check to see if the player's looking?
  188. if ( HasSpawnFlags( SF_NPCMAKER_HIDEFROMPLAYER ) )
  189. {
  190. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  191. {
  192. CBasePlayer *pPlayer = UTIL_PlayerByIndex(i);
  193. if ( pPlayer )
  194. {
  195. // Only spawn if the player's looking away from me
  196. if( pPlayer->FInViewCone( GetAbsOrigin() ) && pPlayer->FVisible( GetAbsOrigin() ) )
  197. {
  198. if ( !(pPlayer->GetFlags() & FL_NOTARGET) )
  199. return false;
  200. DevMsg( 2, "Spawner %s spawning even though seen due to notarget\n", STRING( GetEntityName() ) );
  201. }
  202. }
  203. }
  204. }
  205. return true;
  206. }
  207. //-----------------------------------------------------------------------------
  208. // Purpose: If this had a finite number of children, return true if they've all
  209. // been created.
  210. //-----------------------------------------------------------------------------
  211. bool CBaseNPCMaker::IsDepleted()
  212. {
  213. if ( (m_spawnflags & SF_NPCMAKER_INF_CHILD) || m_nMaxNumNPCs > 0 )
  214. return false;
  215. return true;
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Purpose: Toggle the spawner's state
  219. //-----------------------------------------------------------------------------
  220. void CBaseNPCMaker::Toggle( void )
  221. {
  222. if ( m_bDisabled )
  223. {
  224. Enable();
  225. }
  226. else
  227. {
  228. Disable();
  229. }
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Purpose: Start the spawner
  233. //-----------------------------------------------------------------------------
  234. void CBaseNPCMaker::Enable( void )
  235. {
  236. // can't be enabled once depleted
  237. if ( IsDepleted() )
  238. return;
  239. m_bDisabled = false;
  240. SetThink ( &CBaseNPCMaker::MakerThink );
  241. SetNextThink( gpGlobals->curtime );
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Purpose: Stop the spawner
  245. //-----------------------------------------------------------------------------
  246. void CBaseNPCMaker::Disable( void )
  247. {
  248. m_bDisabled = true;
  249. SetThink ( NULL );
  250. }
  251. //-----------------------------------------------------------------------------
  252. // Purpose: Input handler that spawns an NPC.
  253. //-----------------------------------------------------------------------------
  254. void CBaseNPCMaker::InputSpawnNPC( inputdata_t &inputdata )
  255. {
  256. if( !IsDepleted() )
  257. {
  258. MakeNPC();
  259. }
  260. }
  261. //-----------------------------------------------------------------------------
  262. // Purpose: Input hander that starts the spawner
  263. //-----------------------------------------------------------------------------
  264. void CBaseNPCMaker::InputEnable( inputdata_t &inputdata )
  265. {
  266. Enable();
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Purpose: Input hander that stops the spawner
  270. //-----------------------------------------------------------------------------
  271. void CBaseNPCMaker::InputDisable( inputdata_t &inputdata )
  272. {
  273. Disable();
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Purpose: Input hander that toggles the spawner
  277. //-----------------------------------------------------------------------------
  278. void CBaseNPCMaker::InputToggle( inputdata_t &inputdata )
  279. {
  280. Toggle();
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose:
  284. //-----------------------------------------------------------------------------
  285. void CBaseNPCMaker::InputSetMaxChildren( inputdata_t &inputdata )
  286. {
  287. m_nMaxNumNPCs = inputdata.value.Int();
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Purpose:
  291. //-----------------------------------------------------------------------------
  292. void CBaseNPCMaker::InputAddMaxChildren( inputdata_t &inputdata )
  293. {
  294. m_nMaxNumNPCs += inputdata.value.Int();
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Purpose:
  298. //-----------------------------------------------------------------------------
  299. void CBaseNPCMaker::InputSetMaxLiveChildren( inputdata_t &inputdata )
  300. {
  301. m_nMaxLiveChildren = inputdata.value.Int();
  302. }
  303. void CBaseNPCMaker::InputSetSpawnFrequency( inputdata_t &inputdata )
  304. {
  305. m_flSpawnFrequency = inputdata.value.Float();
  306. }
  307. LINK_ENTITY_TO_CLASS( npc_maker, CNPCMaker );
  308. BEGIN_DATADESC( CNPCMaker )
  309. DEFINE_KEYFIELD( m_iszNPCClassname, FIELD_STRING, "NPCType" ),
  310. DEFINE_KEYFIELD( m_ChildTargetName, FIELD_STRING, "NPCTargetname" ),
  311. DEFINE_KEYFIELD( m_SquadName, FIELD_STRING, "NPCSquadName" ),
  312. DEFINE_KEYFIELD( m_spawnEquipment, FIELD_STRING, "additionalequipment" ),
  313. DEFINE_KEYFIELD( m_strHintGroup, FIELD_STRING, "NPCHintGroup" ),
  314. DEFINE_KEYFIELD( m_RelationshipString, FIELD_STRING, "Relationship" ),
  315. END_DATADESC()
  316. //-----------------------------------------------------------------------------
  317. // Constructor
  318. //-----------------------------------------------------------------------------
  319. CNPCMaker::CNPCMaker( void )
  320. {
  321. m_spawnEquipment = NULL_STRING;
  322. }
  323. //-----------------------------------------------------------------------------
  324. // Purpose: Precache the target NPC
  325. //-----------------------------------------------------------------------------
  326. void CNPCMaker::Precache( void )
  327. {
  328. BaseClass::Precache();
  329. const char *pszNPCName = STRING( m_iszNPCClassname );
  330. if ( !pszNPCName || !pszNPCName[0] )
  331. {
  332. Warning("npc_maker %s has no specified NPC-to-spawn classname.\n", STRING(GetEntityName()) );
  333. }
  334. else
  335. {
  336. UTIL_PrecacheOther( pszNPCName );
  337. }
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Purpose: Creates the NPC.
  341. //-----------------------------------------------------------------------------
  342. void CNPCMaker::MakeNPC( void )
  343. {
  344. if (!CanMakeNPC())
  345. return;
  346. CAI_BaseNPC *pent = (CAI_BaseNPC*)CreateEntityByName( STRING(m_iszNPCClassname) );
  347. if ( !pent )
  348. {
  349. Warning("NULL Ent in NPCMaker!\n" );
  350. return;
  351. }
  352. // ------------------------------------------------
  353. // Intialize spawned NPC's relationships
  354. // ------------------------------------------------
  355. pent->SetRelationshipString( m_RelationshipString );
  356. m_OnSpawnNPC.Set( pent, pent, this );
  357. pent->SetAbsOrigin( GetAbsOrigin() );
  358. // Strip pitch and roll from the spawner's angles. Pass only yaw to the spawned NPC.
  359. QAngle angles = GetAbsAngles();
  360. angles.x = 0.0;
  361. angles.z = 0.0;
  362. pent->SetAbsAngles( angles );
  363. pent->AddSpawnFlags( SF_NPC_FALL_TO_GROUND );
  364. if ( m_spawnflags & SF_NPCMAKER_FADE )
  365. {
  366. pent->AddSpawnFlags( SF_NPC_FADE_CORPSE );
  367. }
  368. pent->m_spawnEquipment = m_spawnEquipment;
  369. pent->SetSquadName( m_SquadName );
  370. pent->SetHintGroup( m_strHintGroup );
  371. ChildPreSpawn( pent );
  372. DispatchSpawn( pent );
  373. pent->SetOwnerEntity( this );
  374. DispatchActivate( pent );
  375. if ( m_ChildTargetName != NULL_STRING )
  376. {
  377. // if I have a netname (overloaded), give the child NPC that name as a targetname
  378. pent->SetName( m_ChildTargetName );
  379. }
  380. ChildPostSpawn( pent );
  381. m_nLiveChildren++;// count this NPC
  382. if (!(m_spawnflags & SF_NPCMAKER_INF_CHILD))
  383. {
  384. m_nMaxNumNPCs--;
  385. if ( IsDepleted() )
  386. {
  387. m_OnAllSpawned.FireOutput( this, this );
  388. // Disable this forever. Don't kill it because it still gets death notices
  389. SetThink( NULL );
  390. SetUse( NULL );
  391. }
  392. }
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Purpose:
  396. // Input : *pChild -
  397. //-----------------------------------------------------------------------------
  398. void CBaseNPCMaker::ChildPostSpawn( CAI_BaseNPC *pChild )
  399. {
  400. // If I'm stuck inside any props, remove them
  401. bool bFound = true;
  402. while ( bFound )
  403. {
  404. trace_t tr;
  405. UTIL_TraceHull( pChild->GetAbsOrigin(), pChild->GetAbsOrigin(), pChild->WorldAlignMins(), pChild->WorldAlignMaxs(), MASK_NPCSOLID, pChild, COLLISION_GROUP_NONE, &tr );
  406. //NDebugOverlay::Box( pChild->GetAbsOrigin(), pChild->WorldAlignMins(), pChild->WorldAlignMaxs(), 0, 255, 0, 32, 5.0 );
  407. if ( tr.fraction != 1.0 && tr.m_pEnt )
  408. {
  409. if ( FClassnameIs( tr.m_pEnt, "prop_physics" ) )
  410. {
  411. // Set to non-solid so this loop doesn't keep finding it
  412. tr.m_pEnt->AddSolidFlags( FSOLID_NOT_SOLID );
  413. UTIL_RemoveImmediate( tr.m_pEnt );
  414. continue;
  415. }
  416. }
  417. bFound = false;
  418. }
  419. if ( m_hIgnoreEntity != NULL )
  420. {
  421. pChild->SetOwnerEntity( m_hIgnoreEntity );
  422. }
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose: Creates a new NPC every so often.
  426. //-----------------------------------------------------------------------------
  427. void CBaseNPCMaker::MakerThink ( void )
  428. {
  429. SetNextThink( gpGlobals->curtime + m_flSpawnFrequency );
  430. MakeNPC();
  431. }
  432. //-----------------------------------------------------------------------------
  433. // Purpose:
  434. // Input : *pVictim -
  435. //-----------------------------------------------------------------------------
  436. void CBaseNPCMaker::DeathNotice( CBaseEntity *pVictim )
  437. {
  438. // ok, we've gotten the deathnotice from our child, now clear out its owner if we don't want it to fade.
  439. m_nLiveChildren--;
  440. // If we're here, we're getting erroneous death messages from children we haven't created
  441. AssertMsg( m_nLiveChildren >= 0, "npc_maker receiving child death notice but thinks has no children\n" );
  442. if ( m_nLiveChildren <= 0 )
  443. {
  444. m_OnAllLiveChildrenDead.FireOutput( this, this );
  445. // See if we've exhausted our supply of NPCs
  446. if ( ( (m_spawnflags & SF_NPCMAKER_INF_CHILD) == false ) && IsDepleted() )
  447. {
  448. // Signal that all our children have been spawned and are now dead
  449. m_OnAllSpawnedDead.FireOutput( this, this );
  450. }
  451. }
  452. }
  453. //-----------------------------------------------------------------------------
  454. // Purpose: Creates new NPCs from a template NPC. The template NPC must be marked
  455. // as a template (spawnflag) and does not spawn.
  456. //-----------------------------------------------------------------------------
  457. LINK_ENTITY_TO_CLASS( npc_template_maker, CTemplateNPCMaker );
  458. BEGIN_DATADESC( CTemplateNPCMaker )
  459. DEFINE_KEYFIELD( m_iszTemplateName, FIELD_STRING, "TemplateName" ),
  460. DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
  461. DEFINE_FIELD( m_iszTemplateData, FIELD_STRING ),
  462. DEFINE_KEYFIELD( m_iszDestinationGroup, FIELD_STRING, "DestinationGroup" ),
  463. DEFINE_KEYFIELD( m_CriterionVisibility, FIELD_INTEGER, "CriterionVisibility" ),
  464. DEFINE_KEYFIELD( m_CriterionDistance, FIELD_INTEGER, "CriterionDistance" ),
  465. DEFINE_KEYFIELD( m_iMinSpawnDistance, FIELD_INTEGER, "MinSpawnDistance" ),
  466. DEFINE_INPUTFUNC( FIELD_VOID, "SpawnNPCInRadius", InputSpawnInRadius ),
  467. DEFINE_INPUTFUNC( FIELD_VOID, "SpawnNPCInLine", InputSpawnInLine ),
  468. DEFINE_INPUTFUNC( FIELD_INTEGER, "SpawnMultiple", InputSpawnMultiple ),
  469. DEFINE_INPUTFUNC( FIELD_STRING, "ChangeDestinationGroup", InputChangeDestinationGroup ),
  470. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMinimumSpawnDistance", InputSetMinimumSpawnDistance ),
  471. END_DATADESC()
  472. //-----------------------------------------------------------------------------
  473. // A hook that lets derived NPC makers do special stuff when precaching.
  474. //-----------------------------------------------------------------------------
  475. void CTemplateNPCMaker::PrecacheTemplateEntity( CBaseEntity *pEntity )
  476. {
  477. pEntity->Precache();
  478. }
  479. void CTemplateNPCMaker::Precache()
  480. {
  481. BaseClass::Precache();
  482. if ( !m_iszTemplateData )
  483. {
  484. //
  485. // This must be the first time we're activated, not a load from save game.
  486. // Look up the template in the template database.
  487. //
  488. if (!m_iszTemplateName)
  489. {
  490. Warning( "npc_template_maker %s has no template NPC!\n", STRING(GetEntityName()) );
  491. UTIL_Remove( this );
  492. return;
  493. }
  494. else
  495. {
  496. m_iszTemplateData = Templates_FindByTargetName(STRING(m_iszTemplateName));
  497. if ( m_iszTemplateData == NULL_STRING )
  498. {
  499. DevWarning( "npc_template_maker %s: template NPC %s not found!\n", STRING(GetEntityName()), STRING(m_iszTemplateName) );
  500. UTIL_Remove( this );
  501. return;
  502. }
  503. }
  504. }
  505. Assert( m_iszTemplateData != NULL_STRING );
  506. // If the mapper marked this as "preload", then instance the entity preache stuff and delete the entity
  507. //if ( !HasSpawnFlags(SF_NPCMAKER_NOPRELOADMODELS) )
  508. if ( m_iszTemplateData != NULL_STRING )
  509. {
  510. CBaseEntity *pEntity = NULL;
  511. MapEntity_ParseEntity( pEntity, STRING(m_iszTemplateData), NULL );
  512. if ( pEntity != NULL )
  513. {
  514. PrecacheTemplateEntity( pEntity );
  515. UTIL_RemoveImmediate( pEntity );
  516. }
  517. }
  518. }
  519. #define MAX_DESTINATION_ENTS 100
  520. CNPCSpawnDestination *CTemplateNPCMaker::FindSpawnDestination()
  521. {
  522. CNPCSpawnDestination *pDestinations[ MAX_DESTINATION_ENTS ];
  523. CBaseEntity *pEnt = NULL;
  524. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  525. int count = 0;
  526. if( !pPlayer )
  527. {
  528. return NULL;
  529. }
  530. // Collect all the qualifiying destination ents
  531. pEnt = gEntList.FindEntityByName( NULL, m_iszDestinationGroup );
  532. if( !pEnt )
  533. {
  534. DevWarning("Template NPC Spawner (%s) doesn't have any spawn destinations!\n", GetDebugName() );
  535. return NULL;
  536. }
  537. while( pEnt )
  538. {
  539. CNPCSpawnDestination *pDestination;
  540. pDestination = dynamic_cast <CNPCSpawnDestination*>(pEnt);
  541. if( pDestination && pDestination->IsAvailable() )
  542. {
  543. bool fValid = true;
  544. Vector vecTest = pDestination->GetAbsOrigin();
  545. if( m_CriterionVisibility != TS_YN_DONT_CARE )
  546. {
  547. // Right now View Cone check is omitted intentionally.
  548. Vector vecTopOfHull = NAI_Hull::Maxs( HULL_HUMAN );
  549. vecTopOfHull.x = 0;
  550. vecTopOfHull.y = 0;
  551. bool fVisible = (pPlayer->FVisible( vecTest ) || pPlayer->FVisible( vecTest + vecTopOfHull ) );
  552. if( m_CriterionVisibility == TS_YN_YES )
  553. {
  554. if( !fVisible )
  555. fValid = false;
  556. }
  557. else
  558. {
  559. if( fVisible )
  560. {
  561. if ( !(pPlayer->GetFlags() & FL_NOTARGET) )
  562. fValid = false;
  563. else
  564. DevMsg( 2, "Spawner %s spawning even though seen due to notarget\n", STRING( GetEntityName() ) );
  565. }
  566. }
  567. }
  568. if( fValid )
  569. {
  570. pDestinations[ count ] = pDestination;
  571. count++;
  572. }
  573. }
  574. pEnt = gEntList.FindEntityByName( pEnt, m_iszDestinationGroup );
  575. }
  576. if( count < 1 )
  577. return NULL;
  578. // Now find the nearest/farthest based on distance criterion
  579. if( m_CriterionDistance == TS_DIST_DONT_CARE )
  580. {
  581. // Pretty lame way to pick randomly. Try a few times to find a random
  582. // location where a hull can fit. Don't try too many times due to performance
  583. // concerns.
  584. for( int i = 0 ; i < 5 ; i++ )
  585. {
  586. CNPCSpawnDestination *pRandomDest = pDestinations[ rand() % count ];
  587. if( HumanHullFits( pRandomDest->GetAbsOrigin() ) )
  588. {
  589. return pRandomDest;
  590. }
  591. }
  592. return NULL;
  593. }
  594. else
  595. {
  596. if( m_CriterionDistance == TS_DIST_NEAREST )
  597. {
  598. float flNearest = FLT_MAX;
  599. CNPCSpawnDestination *pNearest = NULL;
  600. for( int i = 0 ; i < count ; i++ )
  601. {
  602. Vector vecTest = pDestinations[ i ]->GetAbsOrigin();
  603. float flDist = ( vecTest - pPlayer->GetAbsOrigin() ).Length();
  604. if ( m_iMinSpawnDistance != 0 && m_iMinSpawnDistance > flDist )
  605. continue;
  606. if( flDist < flNearest && HumanHullFits( vecTest ) )
  607. {
  608. flNearest = flDist;
  609. pNearest = pDestinations[ i ];
  610. }
  611. }
  612. return pNearest;
  613. }
  614. else
  615. {
  616. float flFarthest = 0;
  617. CNPCSpawnDestination *pFarthest = NULL;
  618. for( int i = 0 ; i < count ; i++ )
  619. {
  620. Vector vecTest = pDestinations[ i ]->GetAbsOrigin();
  621. float flDist = ( vecTest - pPlayer->GetAbsOrigin() ).Length();
  622. if ( m_iMinSpawnDistance != 0 && m_iMinSpawnDistance > flDist )
  623. continue;
  624. if( flDist > flFarthest && HumanHullFits( vecTest ) )
  625. {
  626. flFarthest = flDist;
  627. pFarthest = pDestinations[ i ];
  628. }
  629. }
  630. return pFarthest;
  631. }
  632. }
  633. return NULL;
  634. }
  635. //-----------------------------------------------------------------------------
  636. // Purpose:
  637. //-----------------------------------------------------------------------------
  638. void CTemplateNPCMaker::MakeNPC( void )
  639. {
  640. // If we should be using the radius spawn method instead, do so
  641. if ( m_flRadius && HasSpawnFlags(SF_NPCMAKER_ALWAYSUSERADIUS) )
  642. {
  643. MakeNPCInRadius();
  644. return;
  645. }
  646. if (!CanMakeNPC( ( m_iszDestinationGroup != NULL_STRING ) ))
  647. return;
  648. CNPCSpawnDestination *pDestination = NULL;
  649. if ( m_iszDestinationGroup != NULL_STRING )
  650. {
  651. pDestination = FindSpawnDestination();
  652. if ( !pDestination )
  653. {
  654. DevMsg( 2, "%s '%s' failed to find a valid spawnpoint in destination group: '%s'\n", GetClassname(), STRING(GetEntityName()), STRING(m_iszDestinationGroup) );
  655. return;
  656. }
  657. }
  658. CAI_BaseNPC *pent = NULL;
  659. CBaseEntity *pEntity = NULL;
  660. MapEntity_ParseEntity( pEntity, STRING(m_iszTemplateData), NULL );
  661. if ( pEntity != NULL )
  662. {
  663. pent = (CAI_BaseNPC *)pEntity;
  664. }
  665. if ( !pent )
  666. {
  667. Warning("NULL Ent in NPCMaker!\n" );
  668. return;
  669. }
  670. if ( pDestination )
  671. {
  672. pent->SetAbsOrigin( pDestination->GetAbsOrigin() );
  673. // Strip pitch and roll from the spawner's angles. Pass only yaw to the spawned NPC.
  674. QAngle angles = pDestination->GetAbsAngles();
  675. angles.x = 0.0;
  676. angles.z = 0.0;
  677. pent->SetAbsAngles( angles );
  678. pDestination->OnSpawnedNPC( pent );
  679. }
  680. else
  681. {
  682. pent->SetAbsOrigin( GetAbsOrigin() );
  683. // Strip pitch and roll from the spawner's angles. Pass only yaw to the spawned NPC.
  684. QAngle angles = GetAbsAngles();
  685. angles.x = 0.0;
  686. angles.z = 0.0;
  687. pent->SetAbsAngles( angles );
  688. }
  689. m_OnSpawnNPC.Set( pEntity, pEntity, this );
  690. if ( m_spawnflags & SF_NPCMAKER_FADE )
  691. {
  692. pent->AddSpawnFlags( SF_NPC_FADE_CORPSE );
  693. }
  694. pent->RemoveSpawnFlags( SF_NPC_TEMPLATE );
  695. if ( ( m_spawnflags & SF_NPCMAKER_NO_DROP ) == false )
  696. {
  697. pent->RemoveSpawnFlags( SF_NPC_FALL_TO_GROUND ); // don't fall, slam
  698. }
  699. ChildPreSpawn( pent );
  700. DispatchSpawn( pent );
  701. pent->SetOwnerEntity( this );
  702. DispatchActivate( pent );
  703. ChildPostSpawn( pent );
  704. m_nLiveChildren++;// count this NPC
  705. if (!(m_spawnflags & SF_NPCMAKER_INF_CHILD))
  706. {
  707. m_nMaxNumNPCs--;
  708. if ( IsDepleted() )
  709. {
  710. m_OnAllSpawned.FireOutput( this, this );
  711. // Disable this forever. Don't kill it because it still gets death notices
  712. SetThink( NULL );
  713. SetUse( NULL );
  714. }
  715. }
  716. }
  717. //-----------------------------------------------------------------------------
  718. //-----------------------------------------------------------------------------
  719. void CTemplateNPCMaker::MakeNPCInLine( void )
  720. {
  721. if (!CanMakeNPC(true))
  722. return;
  723. CAI_BaseNPC *pent = NULL;
  724. CBaseEntity *pEntity = NULL;
  725. MapEntity_ParseEntity( pEntity, STRING(m_iszTemplateData), NULL );
  726. if ( pEntity != NULL )
  727. {
  728. pent = (CAI_BaseNPC *)pEntity;
  729. }
  730. if ( !pent )
  731. {
  732. Warning("NULL Ent in NPCMaker!\n" );
  733. return;
  734. }
  735. m_OnSpawnNPC.Set( pEntity, pEntity, this );
  736. PlaceNPCInLine( pent );
  737. pent->AddSpawnFlags( SF_NPC_FALL_TO_GROUND );
  738. pent->RemoveSpawnFlags( SF_NPC_TEMPLATE );
  739. ChildPreSpawn( pent );
  740. DispatchSpawn( pent );
  741. pent->SetOwnerEntity( this );
  742. DispatchActivate( pent );
  743. ChildPostSpawn( pent );
  744. m_nLiveChildren++;// count this NPC
  745. if (!(m_spawnflags & SF_NPCMAKER_INF_CHILD))
  746. {
  747. m_nMaxNumNPCs--;
  748. if ( IsDepleted() )
  749. {
  750. m_OnAllSpawned.FireOutput( this, this );
  751. // Disable this forever. Don't kill it because it still gets death notices
  752. SetThink( NULL );
  753. SetUse( NULL );
  754. }
  755. }
  756. }
  757. //-----------------------------------------------------------------------------
  758. bool CTemplateNPCMaker::PlaceNPCInLine( CAI_BaseNPC *pNPC )
  759. {
  760. Vector vecPlace;
  761. Vector vecLine;
  762. GetVectors( &vecLine, NULL, NULL );
  763. // invert this, line up NPC's BEHIND the maker.
  764. vecLine *= -1;
  765. trace_t tr;
  766. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 8192 ), MASK_SHOT, pNPC, COLLISION_GROUP_NONE, &tr );
  767. vecPlace = tr.endpos;
  768. float flStepSize = pNPC->GetHullWidth();
  769. // Try 10 times to place this npc.
  770. for( int i = 0 ; i < 10 ; i++ )
  771. {
  772. UTIL_TraceHull( vecPlace,
  773. vecPlace + Vector( 0, 0, 10 ),
  774. pNPC->GetHullMins(),
  775. pNPC->GetHullMaxs(),
  776. MASK_SHOT,
  777. pNPC,
  778. COLLISION_GROUP_NONE,
  779. &tr );
  780. if( tr.fraction == 1.0 )
  781. {
  782. pNPC->SetAbsOrigin( tr.endpos );
  783. return true;
  784. }
  785. vecPlace += vecLine * flStepSize;
  786. }
  787. DevMsg("**Failed to place NPC in line!\n");
  788. return false;
  789. }
  790. //-----------------------------------------------------------------------------
  791. // Purpose: Place NPC somewhere on the perimeter of my radius.
  792. //-----------------------------------------------------------------------------
  793. void CTemplateNPCMaker::MakeNPCInRadius( void )
  794. {
  795. if ( !CanMakeNPC(true))
  796. return;
  797. CAI_BaseNPC *pent = NULL;
  798. CBaseEntity *pEntity = NULL;
  799. MapEntity_ParseEntity( pEntity, STRING(m_iszTemplateData), NULL );
  800. if ( pEntity != NULL )
  801. {
  802. pent = (CAI_BaseNPC *)pEntity;
  803. }
  804. if ( !pent )
  805. {
  806. Warning("NULL Ent in NPCMaker!\n" );
  807. return;
  808. }
  809. if ( !PlaceNPCInRadius( pent ) )
  810. {
  811. // Failed to place the NPC. Abort
  812. UTIL_RemoveImmediate( pent );
  813. return;
  814. }
  815. m_OnSpawnNPC.Set( pEntity, pEntity, this );
  816. pent->AddSpawnFlags( SF_NPC_FALL_TO_GROUND );
  817. pent->RemoveSpawnFlags( SF_NPC_TEMPLATE );
  818. ChildPreSpawn( pent );
  819. DispatchSpawn( pent );
  820. pent->SetOwnerEntity( this );
  821. DispatchActivate( pent );
  822. ChildPostSpawn( pent );
  823. m_nLiveChildren++;// count this NPC
  824. if (!(m_spawnflags & SF_NPCMAKER_INF_CHILD))
  825. {
  826. m_nMaxNumNPCs--;
  827. if ( IsDepleted() )
  828. {
  829. m_OnAllSpawned.FireOutput( this, this );
  830. // Disable this forever. Don't kill it because it still gets death notices
  831. SetThink( NULL );
  832. SetUse( NULL );
  833. }
  834. }
  835. }
  836. //-----------------------------------------------------------------------------
  837. // Purpose: Find a place to spawn an npc within my radius.
  838. // Right now this function tries to place them on the perimeter of radius.
  839. // Output : false if we couldn't find a spot!
  840. //-----------------------------------------------------------------------------
  841. bool CTemplateNPCMaker::PlaceNPCInRadius( CAI_BaseNPC *pNPC )
  842. {
  843. Vector vPos;
  844. if ( CAI_BaseNPC::FindSpotForNPCInRadius( &vPos, GetAbsOrigin(), pNPC, m_flRadius ) )
  845. {
  846. pNPC->SetAbsOrigin( vPos );
  847. return true;
  848. }
  849. DevMsg("**Failed to place NPC in radius!\n");
  850. return false;
  851. }
  852. //-----------------------------------------------------------------------------
  853. //-----------------------------------------------------------------------------
  854. void CTemplateNPCMaker::MakeMultipleNPCS( int nNPCs )
  855. {
  856. bool bInRadius = ( m_iszDestinationGroup == NULL_STRING && m_flRadius > 0.1 );
  857. while ( nNPCs-- )
  858. {
  859. if ( !bInRadius )
  860. {
  861. MakeNPC();
  862. }
  863. else
  864. {
  865. MakeNPCInRadius();
  866. }
  867. }
  868. }
  869. //-----------------------------------------------------------------------------
  870. //-----------------------------------------------------------------------------
  871. void CTemplateNPCMaker::InputSpawnMultiple( inputdata_t &inputdata )
  872. {
  873. MakeMultipleNPCS( inputdata.value.Int() );
  874. }
  875. //-----------------------------------------------------------------------------
  876. //-----------------------------------------------------------------------------
  877. void CTemplateNPCMaker::InputChangeDestinationGroup( inputdata_t &inputdata )
  878. {
  879. m_iszDestinationGroup = inputdata.value.StringID();
  880. }
  881. //-----------------------------------------------------------------------------
  882. //-----------------------------------------------------------------------------
  883. void CTemplateNPCMaker::InputSetMinimumSpawnDistance( inputdata_t &inputdata )
  884. {
  885. m_iMinSpawnDistance = inputdata.value.Int();
  886. }