Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1110 lines
30 KiB

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