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.

688 lines
21 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Controls the loading, parsing and creation of the entities from the BSP.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "entitylist.h"
  8. #include "mapentities_shared.h"
  9. #include "soundent.h"
  10. #include "TemplateEntities.h"
  11. #include "point_template.h"
  12. #include "ai_initutils.h"
  13. #include "lights.h"
  14. #include "mapentities.h"
  15. #include "wcedit.h"
  16. #include "stringregistry.h"
  17. #include "datacache/imdlcache.h"
  18. #include "world.h"
  19. #include "toolframework/iserverenginetools.h"
  20. #if defined( CSTRIKE15 )
  21. #include "cs_gamerules.h"
  22. #include "weapon_csbase.h"
  23. #include "econ_entity_creation.h"
  24. #endif
  25. // memdbgon must be the last include file in a .cpp file!!!
  26. #include "tier0/memdbgon.h"
  27. static CStringRegistry *g_pClassnameSpawnPriority = NULL;
  28. extern edict_t *g_pForceAttachEdict;
  29. #if defined( CSTRIKE15 )
  30. extern ConVar mp_weapons_allow_map_placed;
  31. #endif
  32. // creates an entity by string name, but does not spawn it
  33. CBaseEntity *CreateEntityByName( const char *className, int iForceEdictIndex, bool bNotify )
  34. {
  35. if ( iForceEdictIndex != -1 )
  36. {
  37. g_pForceAttachEdict = engine->CreateEdict( iForceEdictIndex );
  38. if ( !g_pForceAttachEdict )
  39. Error( "CreateEntityByName( %s, %d ) - CreateEdict failed.", className, iForceEdictIndex );
  40. }
  41. IServerNetworkable *pNetwork = EntityFactoryDictionary()->Create( className );
  42. g_pForceAttachEdict = NULL;
  43. if ( !pNetwork )
  44. return NULL;
  45. CBaseEntity *pEntity = pNetwork->GetBaseEntity();
  46. Assert( pEntity );
  47. if ( bNotify )
  48. {
  49. gEntList.NotifyCreateEntity( pEntity );
  50. }
  51. return pEntity;
  52. }
  53. CBaseNetworkable *CreateNetworkableByName( const char *className )
  54. {
  55. IServerNetworkable *pNetwork = EntityFactoryDictionary()->Create( className );
  56. if ( !pNetwork )
  57. return NULL;
  58. CBaseNetworkable *pNetworkable = pNetwork->GetBaseNetworkable();
  59. Assert( pNetworkable );
  60. return pNetworkable;
  61. }
  62. void FreeContainingEntity( edict_t *ed )
  63. {
  64. if ( ed )
  65. {
  66. CBaseEntity *ent = GetContainingEntity( ed );
  67. if ( ent )
  68. {
  69. ed->SetEdict( NULL, false );
  70. CBaseEntity::PhysicsRemoveTouchedList( ent );
  71. CBaseEntity::PhysicsRemoveGroundList( ent );
  72. UTIL_RemoveImmediate( ent );
  73. }
  74. }
  75. }
  76. // parent name may have a , in it to include an attachment point
  77. string_t ExtractParentName(string_t parentName)
  78. {
  79. if ( !strchr(STRING(parentName), ',') )
  80. return parentName;
  81. char szToken[256];
  82. nexttoken(szToken, STRING(parentName), ',');
  83. return AllocPooledString(szToken);
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose: Callback function for qsort, used to sort entities by their depth
  87. // in the movement hierarchy.
  88. // Input : pEnt1 -
  89. // pEnt2 -
  90. // Output : Returns -1, 0, or 1 per qsort spec.
  91. //-----------------------------------------------------------------------------
  92. static int __cdecl CompareSpawnOrder(HierarchicalSpawn_t *pEnt1, HierarchicalSpawn_t *pEnt2)
  93. {
  94. if (pEnt1->m_nDepth == pEnt2->m_nDepth)
  95. {
  96. if ( g_pClassnameSpawnPriority )
  97. {
  98. int o1 = pEnt1->m_pEntity ? g_pClassnameSpawnPriority->GetStringID( pEnt1->m_pEntity->GetClassname() ) : -1;
  99. int o2 = pEnt2->m_pEntity ? g_pClassnameSpawnPriority->GetStringID( pEnt2->m_pEntity->GetClassname() ) : -1;
  100. if ( o1 < o2 )
  101. return 1;
  102. if ( o2 < o1 )
  103. return -1;
  104. }
  105. return 0;
  106. }
  107. if (pEnt1->m_nDepth > pEnt2->m_nDepth)
  108. return 1;
  109. return -1;
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Computes the hierarchical depth of the entities to spawn..
  113. //-----------------------------------------------------------------------------
  114. static int ComputeSpawnHierarchyDepth_r( CBaseEntity *pEntity )
  115. {
  116. if ( !pEntity )
  117. return 1;
  118. if (pEntity->m_iParent == NULL_STRING)
  119. return 1;
  120. CBaseEntity *pParent = gEntList.FindEntityByName( NULL, ExtractParentName(pEntity->m_iParent) );
  121. if (!pParent)
  122. return 1;
  123. if (pParent == pEntity)
  124. {
  125. Warning( "LEVEL DESIGN ERROR: Entity %s is parented to itself!\n", pEntity->GetDebugName() );
  126. return 1;
  127. }
  128. return 1 + ComputeSpawnHierarchyDepth_r( pParent );
  129. }
  130. static void ComputeSpawnHierarchyDepth( int nEntities, HierarchicalSpawn_t *pSpawnList )
  131. {
  132. // NOTE: This isn't particularly efficient, but so what? It's at the beginning of time
  133. // I did it this way because it simplified the parent setting in hierarchy (basically
  134. // eliminated questions about whether you should transform origin from global to local or not)
  135. int nEntity;
  136. for (nEntity = 0; nEntity < nEntities; nEntity++)
  137. {
  138. CBaseEntity *pEntity = pSpawnList[nEntity].m_pEntity;
  139. if (pEntity && !pEntity->IsDormant())
  140. {
  141. pSpawnList[nEntity].m_nDepth = ComputeSpawnHierarchyDepth_r( pEntity );
  142. }
  143. else
  144. {
  145. pSpawnList[nEntity].m_nDepth = 1;
  146. }
  147. }
  148. }
  149. static void SortSpawnListByHierarchy( int nEntities, HierarchicalSpawn_t *pSpawnList )
  150. {
  151. MEM_ALLOC_CREDIT();
  152. g_pClassnameSpawnPriority = new CStringRegistry;
  153. // this will cause the entities to be spawned in the indicated order
  154. // Highest string ID spawns first. String ID is spawn priority.
  155. // by default, anything not in this list has priority -1
  156. g_pClassnameSpawnPriority->AddString( "func_wall", 10 );
  157. g_pClassnameSpawnPriority->AddString( "scripted_sequence", 9 );
  158. g_pClassnameSpawnPriority->AddString( "phys_hinge", 8 );
  159. g_pClassnameSpawnPriority->AddString( "phys_ballsocket", 8 );
  160. g_pClassnameSpawnPriority->AddString( "phys_slideconstraint", 8 );
  161. g_pClassnameSpawnPriority->AddString( "phys_constraint", 8 );
  162. g_pClassnameSpawnPriority->AddString( "phys_pulleyconstraint", 8 );
  163. g_pClassnameSpawnPriority->AddString( "phys_lengthconstraint", 8 );
  164. g_pClassnameSpawnPriority->AddString( "phys_ragdollconstraint", 8 );
  165. g_pClassnameSpawnPriority->AddString( "info_mass_center", 8 ); // spawn these before physbox/prop_physics
  166. g_pClassnameSpawnPriority->AddString( "trigger_vphysics_motion", 8 ); // spawn these before physbox/prop_physics
  167. g_pClassnameSpawnPriority->AddString( "prop_physics", 7 );
  168. g_pClassnameSpawnPriority->AddString( "prop_ragdoll", 7 );
  169. // Sort the entities (other than the world) by hierarchy depth, in order to spawn them in
  170. // that order. This insures that each entity's parent spawns before it does so that
  171. // it can properly set up anything that relies on hierarchy.
  172. #ifdef _WIN32
  173. qsort(&pSpawnList[0], nEntities, sizeof(pSpawnList[0]), (int (__cdecl *)(const void *, const void *))CompareSpawnOrder);
  174. #elif POSIX
  175. qsort(&pSpawnList[0], nEntities, sizeof(pSpawnList[0]), (int (*)(const void *, const void *))CompareSpawnOrder);
  176. #endif
  177. delete g_pClassnameSpawnPriority;
  178. g_pClassnameSpawnPriority = NULL;
  179. }
  180. void SetupParentsForSpawnList( int nEntities, HierarchicalSpawn_t *pSpawnList )
  181. {
  182. int nEntity;
  183. for (nEntity = nEntities - 1; nEntity >= 0; nEntity--)
  184. {
  185. CBaseEntity *pEntity = pSpawnList[nEntity].m_pEntity;
  186. if ( pEntity )
  187. {
  188. if ( strchr(STRING(pEntity->m_iParent), ',') )
  189. {
  190. char szToken[256];
  191. const char *pAttachmentName = nexttoken(szToken, STRING(pEntity->m_iParent), ',');
  192. pEntity->m_iParent = AllocPooledString(szToken);
  193. CBaseEntity *pParent = gEntList.FindEntityByName( NULL, pEntity->m_iParent );
  194. // setparent in the spawn pass instead - so the model will have been set & loaded
  195. pSpawnList[nEntity].m_pDeferredParent = pParent;
  196. pSpawnList[nEntity].m_pDeferredParentAttachment = pAttachmentName;
  197. }
  198. else
  199. {
  200. CBaseEntity *pParent = gEntList.FindEntityByName( NULL, pEntity->m_iParent );
  201. if ((pParent != NULL) && (pParent->edict() != NULL))
  202. {
  203. pEntity->SetParent( pParent );
  204. }
  205. }
  206. }
  207. }
  208. }
  209. // this is a hook for edit mode
  210. void RememberInitialEntityPositions( int nEntities, HierarchicalSpawn_t *pSpawnList )
  211. {
  212. for (int nEntity = 0; nEntity < nEntities; nEntity++)
  213. {
  214. CBaseEntity *pEntity = pSpawnList[nEntity].m_pEntity;
  215. if ( pEntity )
  216. {
  217. NWCEdit::RememberEntityPosition( pEntity );
  218. }
  219. }
  220. }
  221. void SpawnAllEntities( int nEntities, HierarchicalSpawn_t *pSpawnList, bool bActivateEntities )
  222. {
  223. int nEntity;
  224. for (nEntity = 0; nEntity < nEntities; nEntity++)
  225. {
  226. VPROF( "MapEntity_ParseAllEntities_Spawn");
  227. CBaseEntity *pEntity = pSpawnList[nEntity].m_pEntity;
  228. if ( pSpawnList[nEntity].m_pDeferredParent )
  229. {
  230. // UNDONE: Promote this up to the root of this function?
  231. MDLCACHE_CRITICAL_SECTION();
  232. CBaseEntity *pParent = pSpawnList[nEntity].m_pDeferredParent;
  233. int iAttachment = -1;
  234. CBaseAnimating *pAnim = pParent->GetBaseAnimating();
  235. if ( pAnim )
  236. {
  237. iAttachment = pAnim->LookupAttachment(pSpawnList[nEntity].m_pDeferredParentAttachment);
  238. }
  239. pEntity->SetParent( pParent, iAttachment );
  240. }
  241. if ( pEntity )
  242. {
  243. if (DispatchSpawn(pEntity) < 0)
  244. {
  245. // Walk through all entities in this list in case spawning an entity
  246. // resulted in another one being UTIL_Remove'd
  247. for ( int i = 0; i < nEntities; i++ )
  248. {
  249. // this is a child object that will be deleted now
  250. if ( pSpawnList[i].m_pEntity && pSpawnList[i].m_pEntity->IsMarkedForDeletion() )
  251. {
  252. pSpawnList[i].m_pEntity = NULL;
  253. }
  254. }
  255. // Spawn failed.
  256. gEntList.CleanupDeleteList();
  257. // Remove the entity from the spawn list
  258. pSpawnList[nEntity].m_pEntity = NULL;
  259. }
  260. }
  261. }
  262. if ( bActivateEntities )
  263. {
  264. VPROF( "MapEntity_ParseAllEntities_Activate");
  265. bool bAsyncAnims = mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, false );
  266. for (nEntity = 0; nEntity < nEntities; nEntity++)
  267. {
  268. CBaseEntity *pEntity = pSpawnList[nEntity].m_pEntity;
  269. if ( pEntity )
  270. {
  271. MDLCACHE_CRITICAL_SECTION();
  272. pEntity->Activate();
  273. }
  274. }
  275. mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, bAsyncAnims );
  276. }
  277. }
  278. // --------------------------------------------------------------------------------------------------- //
  279. // CMapEntitySpawner implementation.
  280. // --------------------------------------------------------------------------------------------------- //
  281. CMapEntitySpawner::CMapEntitySpawner()
  282. {
  283. m_nEntities = 0;
  284. m_pSpawnMapData = new HierarchicalSpawnMapData_t[NUM_ENT_ENTRIES];
  285. m_pSpawnList = new HierarchicalSpawn_t[NUM_ENT_ENTRIES];
  286. m_bFoundryMode = false;
  287. }
  288. CMapEntitySpawner::~CMapEntitySpawner()
  289. {
  290. delete [] m_pSpawnMapData;
  291. delete [] m_pSpawnList;
  292. }
  293. void CMapEntitySpawner::AddEntity( CBaseEntity *pEntity, const char *pCurMapData, int iMapDataLength )
  294. {
  295. if (pEntity->IsTemplate())
  296. {
  297. if ( m_bFoundryMode )
  298. Templates_RemoveByHammerID( pEntity->GetHammerID() );
  299. // It's a template entity. Squirrel away its keyvalue text so that we can
  300. // recreate the entity later via a spawner. pMapData points at the '}'
  301. // so we must add one to include it in the string.
  302. Templates_Add( pEntity, pCurMapData, iMapDataLength, pEntity->GetHammerID() );
  303. // Remove the template entity so that it does not show up in FindEntityXXX searches.
  304. UTIL_Remove(pEntity);
  305. PurgeRemovedEntities();
  306. return;
  307. }
  308. // To
  309. if ( dynamic_cast<CWorld*>( pEntity ) )
  310. {
  311. Assert( !m_bFoundryMode );
  312. VPROF( "MapEntity_ParseAllEntities_SpawnWorld");
  313. pEntity->m_iParent = NULL_STRING; // don't allow a parent on the first entity (worldspawn)
  314. DispatchSpawn(pEntity);
  315. return;
  316. }
  317. CNodeEnt *pNode = dynamic_cast<CNodeEnt*>(pEntity);
  318. if ( pNode )
  319. {
  320. VPROF( "MapEntity_ParseAllEntities_SpawnTransients");
  321. // We overflow the max edicts on large maps that have lots of entities.
  322. // Nodes & Lights remove themselves immediately on Spawn(), so dispatch their
  323. // spawn now, to free up the slot inside this loop.
  324. // NOTE: This solution prevents nodes & lights from being used inside point_templates.
  325. //
  326. // NOTE: Nodes spawn other entities (ai_hint) if they need to have a persistent presence.
  327. // To ensure keys are copied over into the new entity, we pass the mapdata into the
  328. // node spawn function.
  329. if ( pNode->Spawn( pCurMapData ) < 0 )
  330. {
  331. PurgeRemovedEntities();
  332. }
  333. return;
  334. }
  335. if ( dynamic_cast<CLight*>(pEntity) )
  336. {
  337. VPROF( "MapEntity_ParseAllEntities_SpawnTransients");
  338. // We overflow the max edicts on large maps that have lots of entities.
  339. // Nodes & Lights remove themselves immediately on Spawn(), so dispatch their
  340. // spawn now, to free up the slot inside this loop.
  341. // NOTE: This solution prevents nodes & lights from being used inside point_templates.
  342. if (DispatchSpawn(pEntity) < 0)
  343. {
  344. PurgeRemovedEntities();
  345. }
  346. return;
  347. }
  348. // Build a list of all point_template's so we can spawn them before everything else
  349. CPointTemplate *pTemplate = dynamic_cast< CPointTemplate* >(pEntity);
  350. if ( pTemplate )
  351. {
  352. m_PointTemplates.AddToTail( pTemplate );
  353. }
  354. else
  355. {
  356. // Queue up this entity for spawning
  357. m_pSpawnList[m_nEntities].m_pEntity = pEntity;
  358. m_pSpawnList[m_nEntities].m_nDepth = 0;
  359. m_pSpawnList[m_nEntities].m_pDeferredParentAttachment = NULL;
  360. m_pSpawnList[m_nEntities].m_pDeferredParent = NULL;
  361. m_pSpawnMapData[m_nEntities].m_pMapData = pCurMapData;
  362. m_pSpawnMapData[m_nEntities].m_iMapDataLength = iMapDataLength;
  363. m_nEntities++;
  364. }
  365. }
  366. void MapEntity_ParseAllEntites_SpawnTemplates( CPointTemplate **pTemplates, int iTemplateCount, CBaseEntity **pSpawnedEntities, HierarchicalSpawnMapData_t *pSpawnMapData, int iSpawnedEntityCount )
  367. {
  368. // Now loop through all our point_template entities and tell them to make templates of everything they're pointing to
  369. for ( int i = 0; i < iTemplateCount; i++ )
  370. {
  371. VPROF( "MapEntity_ParseAllEntities_SpawnTemplates");
  372. CPointTemplate *pPointTemplate = pTemplates[i];
  373. // First, tell the Point template to Spawn
  374. if ( DispatchSpawn(pPointTemplate) < 0 )
  375. {
  376. UTIL_Remove(pPointTemplate);
  377. gEntList.CleanupDeleteList();
  378. continue;
  379. }
  380. pPointTemplate->StartBuildingTemplates();
  381. // Now go through all it's templates and turn the entities into templates
  382. int iNumTemplates = pPointTemplate->GetNumTemplateEntities();
  383. for ( int iTemplateNum = 0; iTemplateNum < iNumTemplates; iTemplateNum++ )
  384. {
  385. // Find it in the spawn list
  386. CBaseEntity *pEntity = pPointTemplate->GetTemplateEntity( iTemplateNum );
  387. for ( int iEntNum = 0; iEntNum < iSpawnedEntityCount; iEntNum++ )
  388. {
  389. if ( pSpawnedEntities[iEntNum] == pEntity )
  390. {
  391. // Give the point_template the mapdata
  392. pPointTemplate->AddTemplate( pEntity, pSpawnMapData[iEntNum].m_pMapData, pSpawnMapData[iEntNum].m_iMapDataLength );
  393. if ( pPointTemplate->ShouldRemoveTemplateEntities() )
  394. {
  395. // Remove the template entity so that it does not show up in FindEntityXXX searches.
  396. UTIL_Remove(pEntity);
  397. gEntList.CleanupDeleteList();
  398. // Remove the entity from the spawn list
  399. pSpawnedEntities[iEntNum] = NULL;
  400. }
  401. break;
  402. }
  403. }
  404. }
  405. pPointTemplate->FinishBuildingTemplates();
  406. }
  407. }
  408. void CMapEntitySpawner::HandleTemplates()
  409. {
  410. if( m_PointTemplates.Count() == 0 )
  411. return;
  412. CBaseEntity **pSpawnedEntities = (CBaseEntity **)stackalloc( sizeof(CBaseEntity *) * m_nEntities );
  413. for( int i = 0; i != m_nEntities; ++i )
  414. {
  415. pSpawnedEntities[i] = m_pSpawnList[i].m_pEntity;
  416. }
  417. PurgeRemovedEntities();
  418. MapEntity_ParseAllEntites_SpawnTemplates( m_PointTemplates.Base(), m_PointTemplates.Count(), pSpawnedEntities, m_pSpawnMapData, m_nEntities );
  419. //copy the entity list back since some entities may have been removed and nulled out
  420. for( int i = 0; i != m_nEntities; ++i )
  421. {
  422. m_pSpawnList[i].m_pEntity = pSpawnedEntities[i];
  423. }
  424. }
  425. void CMapEntitySpawner::SpawnAndActivate( bool bActivateEntities )
  426. {
  427. SpawnHierarchicalList( m_nEntities, m_pSpawnList, bActivateEntities );
  428. }
  429. void CMapEntitySpawner::PurgeRemovedEntities()
  430. {
  431. // Walk through spawn list and NULL out any soon-to-be-stale pointers
  432. for ( int i = 0; i < m_nEntities; ++ i )
  433. {
  434. if ( m_pSpawnList[i].m_pEntity && m_pSpawnList[i].m_pEntity->IsMarkedForDeletion() )
  435. {
  436. #ifdef _DEBUG
  437. // Catch a specific error that bit us
  438. if ( dynamic_cast< CGameRulesProxy * >( m_pSpawnList[i].m_pEntity ) != NULL )
  439. {
  440. Warning( "Map-placed game rules entity is being deleted; does the map contain more than one?\n" );
  441. }
  442. #endif
  443. m_pSpawnList[i].m_pEntity = NULL;
  444. }
  445. }
  446. gEntList.CleanupDeleteList();
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Purpose: Only called on BSP load. Parses and spawns all the entities in the BSP.
  450. // Input : pMapData - Pointer to the entity data block to parse.
  451. //-----------------------------------------------------------------------------
  452. void MapEntity_ParseAllEntities(const char *pMapData, IMapEntityFilter *pFilter, bool bActivateEntities)
  453. {
  454. VPROF("MapEntity_ParseAllEntities");
  455. CMapEntitySpawner spawner;
  456. char szTokenBuffer[MAPKEY_MAXLENGTH];
  457. // Allow the tools to spawn different things
  458. if ( serverenginetools )
  459. {
  460. pMapData = serverenginetools->GetEntityData( pMapData );
  461. }
  462. // Loop through all entities in the map data, creating each.
  463. for ( ; true; pMapData = MapEntity_SkipToNextEntity(pMapData, szTokenBuffer) )
  464. {
  465. //
  466. // Parse the opening brace.
  467. //
  468. char token[MAPKEY_MAXLENGTH];
  469. pMapData = MapEntity_ParseToken( pMapData, token );
  470. //
  471. // Check to see if we've finished or not.
  472. //
  473. if (!pMapData)
  474. break;
  475. if (token[0] != '{')
  476. {
  477. Error( "MapEntity_ParseAllEntities: found %s when expecting {", token);
  478. continue;
  479. }
  480. //
  481. // Parse the entity and add it to the spawn list.
  482. //
  483. CBaseEntity *pEntity;
  484. const char *pCurMapData = pMapData;
  485. pMapData = MapEntity_ParseEntity(pEntity, pMapData, pFilter);
  486. if (pEntity == NULL)
  487. continue;
  488. spawner.AddEntity( pEntity, pCurMapData, pMapData - pCurMapData + 2 );
  489. }
  490. spawner.HandleTemplates();
  491. spawner.SpawnAndActivate( bActivateEntities );
  492. }
  493. void SpawnHierarchicalList( int nEntities, HierarchicalSpawn_t *pSpawnList, bool bActivateEntities )
  494. {
  495. // Compute the hierarchical depth of all entities hierarchically attached
  496. ComputeSpawnHierarchyDepth( nEntities, pSpawnList );
  497. // Sort the entities (other than the world) by hierarchy depth, in order to spawn them in
  498. // that order. This insures that each entity's parent spawns before it does so that
  499. // it can properly set up anything that relies on hierarchy.
  500. SortSpawnListByHierarchy( nEntities, pSpawnList );
  501. // save off entity positions if in edit mode
  502. if ( engine->IsInEditMode() )
  503. {
  504. RememberInitialEntityPositions( nEntities, pSpawnList );
  505. }
  506. // Set up entity movement hierarchy in reverse hierarchy depth order. This allows each entity
  507. // to use its parent's world spawn origin to calculate its local origin.
  508. SetupParentsForSpawnList( nEntities, pSpawnList );
  509. // Spawn all the entities in hierarchy depth order so that parents spawn before their children.
  510. SpawnAllEntities( nEntities, pSpawnList, bActivateEntities );
  511. }
  512. //-----------------------------------------------------------------------------
  513. // Purpose:
  514. // Input : *pEntData -
  515. //-----------------------------------------------------------------------------
  516. void MapEntity_PrecacheEntity( const char *pEntData, int &nStringSize )
  517. {
  518. CEntityMapData entData( (char*)pEntData, nStringSize );
  519. char className[MAPKEY_MAXLENGTH];
  520. if (!entData.ExtractValue("classname", className))
  521. {
  522. Error( "classname missing from entity!\n" );
  523. }
  524. // Construct via the LINK_ENTITY_TO_CLASS factory.
  525. CBaseEntity *pEntity = CreateEntityByName(className);
  526. //
  527. // Set up keyvalues, which can set the model name, which is why we don't just do UTIL_PrecacheOther here...
  528. //
  529. if ( pEntity != NULL )
  530. {
  531. pEntity->ParseMapData(&entData);
  532. pEntity->Precache();
  533. UTIL_RemoveImmediate( pEntity );
  534. }
  535. }
  536. //-----------------------------------------------------------------------------
  537. // Purpose: Takes a block of character data as the input
  538. // Input : pEntity - Receives the newly constructed entity, NULL on failure.
  539. // pEntData - Data block to parse to extract entity keys.
  540. // Output : Returns the current position in the entity data block.
  541. //-----------------------------------------------------------------------------
  542. const char *MapEntity_ParseEntity(CBaseEntity *&pEntity, const char *pEntData, IMapEntityFilter *pFilter)
  543. {
  544. CEntityMapData entData( (char*)pEntData );
  545. char className[MAPKEY_MAXLENGTH];
  546. if (!entData.ExtractValue("classname", className))
  547. {
  548. Error( "classname missing from entity!\n" );
  549. }
  550. bool skipForGameType = false;
  551. pEntity = NULL;
  552. if ( !pFilter || ( pFilter->ShouldCreateEntity( className ) && !skipForGameType ) )
  553. {
  554. //
  555. // Construct via the LINK_ENTITY_TO_CLASS factory.
  556. //
  557. if ( pFilter )
  558. {
  559. pEntity = pFilter->CreateNextEntity( className );
  560. }
  561. else
  562. {
  563. pEntity = CreateEntityByName(className);
  564. }
  565. //
  566. // Set up keyvalues.
  567. //
  568. if (pEntity != NULL)
  569. {
  570. pEntity->ParseMapData(&entData);
  571. }
  572. else
  573. {
  574. Warning("Can't init %s\n", className);
  575. }
  576. }
  577. else
  578. {
  579. // Just skip past all the keys.
  580. char keyName[MAPKEY_MAXLENGTH];
  581. char value[MAPKEY_MAXLENGTH];
  582. if ( entData.GetFirstKey(keyName, value) )
  583. {
  584. do
  585. {
  586. }
  587. while ( entData.GetNextKey(keyName, value) );
  588. }
  589. }
  590. //
  591. // Return the current parser position in the data block
  592. //
  593. return entData.CurrentBufferPosition();
  594. }