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.

409 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Point entity used to create templates out of other entities or groups of entities
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "entityinput.h"
  8. #include "entityoutput.h"
  9. #include "TemplateEntities.h"
  10. #include "point_template.h"
  11. #include "saverestore_utlvector.h"
  12. #include "mapentities.h"
  13. #include "tier0/icommandline.h"
  14. #include "mapentities_shared.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. #define SF_POINTTEMPLATE_DONTREMOVETEMPLATEENTITIES 0x0001
  18. // Level designers can suppress the uniquification of the spawned entity
  19. // names with a spawnflag, provided they guarantee that only one instance
  20. // of the entities will ever be spawned at a time.
  21. #define SF_POINTTEMPLATE_PRESERVE_NAMES 0x0002
  22. LINK_ENTITY_TO_CLASS(point_template, CPointTemplate);
  23. BEGIN_SIMPLE_DATADESC( template_t )
  24. DEFINE_FIELD( iTemplateIndex, FIELD_INTEGER ),
  25. DEFINE_FIELD( matEntityToTemplate, FIELD_VMATRIX ),
  26. END_DATADESC()
  27. BEGIN_DATADESC( CPointTemplate )
  28. // Keys
  29. // Silence, Classcheck!
  30. // DEFINE_ARRAY( m_iszTemplateEntityNames, FIELD_STRING, MAX_NUM_TEMPLATES ),
  31. DEFINE_KEYFIELD( m_iszTemplateEntityNames[0], FIELD_STRING, "Template01"),
  32. DEFINE_KEYFIELD( m_iszTemplateEntityNames[1], FIELD_STRING, "Template02"),
  33. DEFINE_KEYFIELD( m_iszTemplateEntityNames[2], FIELD_STRING, "Template03"),
  34. DEFINE_KEYFIELD( m_iszTemplateEntityNames[3], FIELD_STRING, "Template04"),
  35. DEFINE_KEYFIELD( m_iszTemplateEntityNames[4], FIELD_STRING, "Template05"),
  36. DEFINE_KEYFIELD( m_iszTemplateEntityNames[5], FIELD_STRING, "Template06"),
  37. DEFINE_KEYFIELD( m_iszTemplateEntityNames[6], FIELD_STRING, "Template07"),
  38. DEFINE_KEYFIELD( m_iszTemplateEntityNames[7], FIELD_STRING, "Template08"),
  39. DEFINE_KEYFIELD( m_iszTemplateEntityNames[8], FIELD_STRING, "Template09"),
  40. DEFINE_KEYFIELD( m_iszTemplateEntityNames[9], FIELD_STRING, "Template10"),
  41. DEFINE_KEYFIELD( m_iszTemplateEntityNames[10], FIELD_STRING, "Template11"),
  42. DEFINE_KEYFIELD( m_iszTemplateEntityNames[11], FIELD_STRING, "Template12"),
  43. DEFINE_KEYFIELD( m_iszTemplateEntityNames[12], FIELD_STRING, "Template13"),
  44. DEFINE_KEYFIELD( m_iszTemplateEntityNames[13], FIELD_STRING, "Template14"),
  45. DEFINE_KEYFIELD( m_iszTemplateEntityNames[14], FIELD_STRING, "Template15"),
  46. DEFINE_KEYFIELD( m_iszTemplateEntityNames[15], FIELD_STRING, "Template16"),
  47. DEFINE_UTLVECTOR( m_hTemplateEntities, FIELD_CLASSPTR ),
  48. DEFINE_UTLVECTOR( m_hTemplates, FIELD_EMBEDDED ),
  49. // Inputs
  50. DEFINE_INPUTFUNC( FIELD_VOID, "ForceSpawn", InputForceSpawn ),
  51. // Outputs
  52. DEFINE_OUTPUT( m_pOutputOnSpawned, "OnEntitySpawned" ),
  53. END_DATADESC()
  54. //-----------------------------------------------------------------------------
  55. // Purpose: A simple system to help precache point_template entities ... ywb
  56. //-----------------------------------------------------------------------------
  57. class CPointTemplatePrecacher : public CAutoGameSystem
  58. {
  59. public:
  60. CPointTemplatePrecacher( char const *name ) : CAutoGameSystem( name )
  61. {
  62. }
  63. void AddToPrecache( CBaseEntity *ent )
  64. {
  65. m_Ents.AddToTail( EHANDLE( ent ) );
  66. }
  67. virtual void LevelInitPreEntity()
  68. {
  69. m_Ents.RemoveAll();
  70. }
  71. virtual void Shutdown()
  72. {
  73. m_Ents.RemoveAll();
  74. }
  75. void Precache()
  76. {
  77. int c = m_Ents.Count();
  78. for ( int i = 0 ; i < c; ++i )
  79. {
  80. CPointTemplate *ent = m_Ents[ i ].Get();
  81. if ( ent )
  82. {
  83. ent->PerformPrecache();
  84. }
  85. }
  86. m_Ents.RemoveAll();
  87. }
  88. private:
  89. CUtlVector< CHandle< CPointTemplate > > m_Ents;
  90. };
  91. CPointTemplatePrecacher g_PointTemplatePrecacher( "CPointTemplatePrecacher" );
  92. //-----------------------------------------------------------------------------
  93. // Purpose:
  94. //-----------------------------------------------------------------------------
  95. void PrecachePointTemplates()
  96. {
  97. g_PointTemplatePrecacher.Precache();
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Purpose:
  101. //-----------------------------------------------------------------------------
  102. void CPointTemplate::Spawn( void )
  103. {
  104. Precache();
  105. }
  106. void CPointTemplate::Precache()
  107. {
  108. // We can't call precache right when we instance the template, we need to defer it until after all map entities have
  109. // been loaded, so add this template to a list which is cleared after map entity parsing is completed.
  110. g_PointTemplatePrecacher.AddToPrecache( this );
  111. }
  112. //-----------------------------------------------------------------------------
  113. // Purpose: Level designers can suppress the uniquification of the spawned entity
  114. // names with a spawnflag, provided they guarantee that only one instance
  115. // of the entities will ever be spawned at a time.
  116. //-----------------------------------------------------------------------------
  117. bool CPointTemplate::AllowNameFixup()
  118. {
  119. return !HasSpawnFlags( SF_POINTTEMPLATE_PRESERVE_NAMES );
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Called at the start of template initialization for this point_template.
  123. // Find all the entities referenced by this point_template, which will
  124. // then be turned into templates by the map-parsing code.
  125. //-----------------------------------------------------------------------------
  126. void CPointTemplate::StartBuildingTemplates( void )
  127. {
  128. // Build our list of template entities
  129. for ( int i = 0; i < MAX_NUM_TEMPLATES; i++ )
  130. {
  131. if ( m_iszTemplateEntityNames[i] != NULL_STRING )
  132. {
  133. CBaseEntity *pEntity = NULL;
  134. int iOldNum = m_hTemplateEntities.Count();
  135. // Add all the entities with the matching targetname
  136. while ( (pEntity = gEntList.FindEntityByName( pEntity, STRING(m_iszTemplateEntityNames[i]) )) != NULL )
  137. {
  138. m_hTemplateEntities.AddToTail( pEntity );
  139. }
  140. // Useful mapmaker warning
  141. if ( iOldNum == m_hTemplateEntities.Count() )
  142. {
  143. Warning( "Couldn't find any entities named %s, which point_template %s is specifying.\n", STRING(m_iszTemplateEntityNames[i]), STRING(GetEntityName()) );
  144. }
  145. }
  146. }
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Purpose: Called at the end of template initialization for this point_template.
  150. // All of our referenced entities have now been destroyed.
  151. //-----------------------------------------------------------------------------
  152. void CPointTemplate::FinishBuildingTemplates( void )
  153. {
  154. // Our template entities are now gone, deleted by the server post turning them into templates.
  155. m_hTemplateEntities.Purge();
  156. // Now tell the template system to hook up all the Entity I/O connections within our set of templates.
  157. Templates_ReconnectIOForGroup( this );
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Purpose:
  161. //-----------------------------------------------------------------------------
  162. void CPointTemplate::AddTemplate( CBaseEntity *pEntity, const char *pszMapData, int nLen )
  163. {
  164. // Add it to the template list
  165. int iIndex = Templates_Add( pEntity, pszMapData, nLen );
  166. if ( iIndex == -1 )
  167. {
  168. Warning( "point_template %s failed to add template.\n", STRING(GetEntityName()) );
  169. return;
  170. }
  171. template_t newTemplate;
  172. newTemplate.iTemplateIndex = iIndex;
  173. // Store the entity's origin & angles in a matrix in the template's local space
  174. VMatrix matTemplateToWorld, matWorldToTemplate, matEntityToWorld, matEntityToTemplate;
  175. matTemplateToWorld.SetupMatrixOrgAngles( GetAbsOrigin(), GetAbsAngles() );
  176. matTemplateToWorld.InverseTR( matWorldToTemplate );
  177. matEntityToWorld.SetupMatrixOrgAngles( pEntity->GetAbsOrigin(), pEntity->GetAbsAngles() );
  178. MatrixMultiply( matWorldToTemplate, matEntityToWorld, matEntityToTemplate );
  179. newTemplate.matEntityToTemplate = matEntityToTemplate;
  180. m_hTemplates.AddToTail( newTemplate );
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Purpose:
  184. //-----------------------------------------------------------------------------
  185. bool CPointTemplate::ShouldRemoveTemplateEntities( void )
  186. {
  187. return ( !(m_spawnflags & SF_POINTTEMPLATE_DONTREMOVETEMPLATEENTITIES) );
  188. }
  189. //-----------------------------------------------------------------------------
  190. // Purpose:
  191. //-----------------------------------------------------------------------------
  192. int CPointTemplate::GetNumTemplates( void )
  193. {
  194. return m_hTemplates.Count();
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Purpose:
  198. //-----------------------------------------------------------------------------
  199. int CPointTemplate::GetTemplateIndexForTemplate( int iTemplate )
  200. {
  201. Assert( iTemplate < m_hTemplates.Count() );
  202. return m_hTemplates[iTemplate].iTemplateIndex;
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Purpose:
  206. //-----------------------------------------------------------------------------
  207. int CPointTemplate::GetNumTemplateEntities( void )
  208. {
  209. return m_hTemplateEntities.Count();
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Purpose:
  213. //-----------------------------------------------------------------------------
  214. CBaseEntity *CPointTemplate::GetTemplateEntity( int iTemplateNumber )
  215. {
  216. Assert( iTemplateNumber < m_hTemplateEntities.Count() );
  217. return m_hTemplateEntities[iTemplateNumber];
  218. }
  219. //-----------------------------------------------------------------------------
  220. // Purpose:
  221. //-----------------------------------------------------------------------------
  222. void CPointTemplate::PerformPrecache()
  223. {
  224. // Go through all our templated map data and precache all the entities in it
  225. int iTemplates = m_hTemplates.Count();
  226. if ( !iTemplates )
  227. {
  228. Msg("Precache called on a point_template that has no templates: %s\n", STRING(GetEntityName()) );
  229. return;
  230. }
  231. // Tell the template system we're about to start a new template
  232. Templates_StartUniqueInstance();
  233. //HierarchicalSpawn_t *pSpawnList = (HierarchicalSpawn_t*)stackalloc( iTemplates * sizeof(HierarchicalSpawn_t) );
  234. int i;
  235. for ( i = 0; i < iTemplates; i++ )
  236. {
  237. //CBaseEntity *pEntity = NULL;
  238. char *pMapData;
  239. int iTemplateIndex = m_hTemplates[i].iTemplateIndex;
  240. // Some templates have Entity I/O connecting the entities within the template.
  241. // Unique versions of these templates need to be created whenever they're instanced.
  242. int nStringSize;
  243. if ( AllowNameFixup() && Templates_IndexRequiresEntityIOFixup( iTemplateIndex ) )
  244. {
  245. // This template requires instancing.
  246. // Create a new mapdata block and ask the template system to fill it in with
  247. // a unique version (with fixed Entity I/O connections).
  248. pMapData = Templates_GetEntityIOFixedMapData( iTemplateIndex );
  249. }
  250. else
  251. {
  252. // Use the unmodified mapdata
  253. pMapData = (char*)STRING( Templates_FindByIndex( iTemplateIndex ) );
  254. }
  255. nStringSize = Templates_GetStringSize( iTemplateIndex );
  256. // Create the entity from the mapdata
  257. MapEntity_PrecacheEntity( pMapData, nStringSize );
  258. }
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Purpose: Spawn the entities I contain
  262. // Input : &vecOrigin -
  263. // &vecAngles -
  264. // pEntities -
  265. // Output : Returns true on success, false on failure.
  266. //-----------------------------------------------------------------------------
  267. bool CPointTemplate::CreateInstance( const Vector &vecOrigin, const QAngle &vecAngles, CUtlVector<CBaseEntity*> *pEntities )
  268. {
  269. // Go through all our templated map data and spawn all the entities in it
  270. int iTemplates = m_hTemplates.Count();
  271. if ( !iTemplates )
  272. {
  273. Msg("CreateInstance called on a point_template that has no templates: %s\n", STRING(GetEntityName()) );
  274. return false;
  275. }
  276. // Tell the template system we're about to start a new template
  277. Templates_StartUniqueInstance();
  278. HierarchicalSpawn_t *pSpawnList = (HierarchicalSpawn_t*)stackalloc( iTemplates * sizeof(HierarchicalSpawn_t) );
  279. int i;
  280. for ( i = 0; i < iTemplates; i++ )
  281. {
  282. CBaseEntity *pEntity = NULL;
  283. char *pMapData;
  284. int iTemplateIndex = m_hTemplates[i].iTemplateIndex;
  285. // Some templates have Entity I/O connecting the entities within the template.
  286. // Unique versions of these templates need to be created whenever they're instanced.
  287. if ( AllowNameFixup() && Templates_IndexRequiresEntityIOFixup( iTemplateIndex ) )
  288. {
  289. // This template requires instancing.
  290. // Create a new mapdata block and ask the template system to fill it in with
  291. // a unique version (with fixed Entity I/O connections).
  292. pMapData = Templates_GetEntityIOFixedMapData( iTemplateIndex );
  293. }
  294. else
  295. {
  296. // Use the unmodified mapdata
  297. pMapData = (char*)STRING( Templates_FindByIndex( iTemplateIndex ) );
  298. }
  299. // Create the entity from the mapdata
  300. MapEntity_ParseEntity( pEntity, pMapData, NULL );
  301. if ( pEntity == NULL )
  302. {
  303. Msg("Failed to initialize templated entity with mapdata: %s\n", pMapData );
  304. return false;
  305. }
  306. // Get a matrix that'll convert from world to the new local space
  307. VMatrix matNewTemplateToWorld, matStoredLocalToWorld;
  308. matNewTemplateToWorld.SetupMatrixOrgAngles( vecOrigin, vecAngles );
  309. MatrixMultiply( matNewTemplateToWorld, m_hTemplates[i].matEntityToTemplate, matStoredLocalToWorld );
  310. // Get the world origin & angles from the stored local coordinates
  311. Vector vecNewOrigin;
  312. QAngle vecNewAngles;
  313. vecNewOrigin = matStoredLocalToWorld.GetTranslation();
  314. MatrixToAngles( matStoredLocalToWorld, vecNewAngles );
  315. // Set its origin & angles
  316. pEntity->SetAbsOrigin( vecNewOrigin );
  317. pEntity->SetAbsAngles( vecNewAngles );
  318. pSpawnList[i].m_hEntity = pEntity;
  319. pSpawnList[i].m_nDepth = 0;
  320. pSpawnList[i].m_pDeferredParent = NULL;
  321. }
  322. SpawnHierarchicalList( iTemplates, pSpawnList, true );
  323. for ( i = 0; i < iTemplates; ++i )
  324. {
  325. if ( pSpawnList[i].m_hEntity )
  326. {
  327. pEntities->AddToTail( pSpawnList[i].m_hEntity );
  328. }
  329. }
  330. return true;
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Purpose:
  334. // Input : &inputdata -
  335. //-----------------------------------------------------------------------------
  336. void CPointTemplate::InputForceSpawn( inputdata_t &inputdata )
  337. {
  338. // Spawn our template
  339. CUtlVector<CBaseEntity*> hNewEntities;
  340. if ( !CreateInstance( GetAbsOrigin(), GetAbsAngles(), &hNewEntities ) )
  341. return;
  342. // Fire our output
  343. m_pOutputOnSpawned.FireOutput( this, this );
  344. }