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.

366 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "entityoutput.h"
  10. #include "TemplateEntities.h"
  11. #include "point_template.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. #define SF_ENTMAKER_AUTOSPAWN 0x0001
  15. #define SF_ENTMAKER_WAITFORDESTRUCTION 0x0002
  16. #define SF_ENTMAKER_IGNOREFACING 0x0004
  17. #define SF_ENTMAKER_CHECK_FOR_SPACE 0x0008
  18. #define SF_ENTMAKER_CHECK_PLAYER_LOOKING 0x0010
  19. //-----------------------------------------------------------------------------
  20. // Purpose: An entity that mapmakers can use to ensure there's a required entity never runs out.
  21. // i.e. physics cannisters that need to be used.
  22. //-----------------------------------------------------------------------------
  23. class CEnvEntityMaker : public CPointEntity
  24. {
  25. DECLARE_CLASS( CEnvEntityMaker, CPointEntity );
  26. public:
  27. DECLARE_DATADESC();
  28. virtual void Spawn( void );
  29. virtual void Activate( void );
  30. void SpawnEntity( Vector vecAlternateOrigin = vec3_invalid, QAngle vecAlternateAngles = vec3_angle );
  31. void CheckSpawnThink( void );
  32. void InputForceSpawn( inputdata_t &inputdata );
  33. void InputForceSpawnAtEntityOrigin( inputdata_t &inputdata );
  34. private:
  35. CPointTemplate *FindTemplate();
  36. bool HasRoomToSpawn();
  37. bool IsPlayerLooking();
  38. Vector m_vecEntityMins;
  39. Vector m_vecEntityMaxs;
  40. EHANDLE m_hCurrentInstance;
  41. EHANDLE m_hCurrentBlocker; // Last entity that blocked us spawning something
  42. Vector m_vecBlockerOrigin;
  43. // Movement after spawn
  44. QAngle m_angPostSpawnDirection;
  45. float m_flPostSpawnDirectionVariance;
  46. float m_flPostSpawnSpeed;
  47. bool m_bPostSpawnUseAngles;
  48. string_t m_iszTemplate;
  49. COutputEvent m_pOutputOnSpawned;
  50. COutputEvent m_pOutputOnFailedSpawn;
  51. };
  52. BEGIN_DATADESC( CEnvEntityMaker )
  53. // DEFINE_FIELD( m_vecEntityMins, FIELD_VECTOR ),
  54. // DEFINE_FIELD( m_vecEntityMaxs, FIELD_VECTOR ),
  55. DEFINE_FIELD( m_hCurrentInstance, FIELD_EHANDLE ),
  56. DEFINE_FIELD( m_hCurrentBlocker, FIELD_EHANDLE ),
  57. DEFINE_FIELD( m_vecBlockerOrigin, FIELD_VECTOR ),
  58. DEFINE_KEYFIELD( m_iszTemplate, FIELD_STRING, "EntityTemplate" ),
  59. DEFINE_KEYFIELD( m_angPostSpawnDirection, FIELD_VECTOR, "PostSpawnDirection" ),
  60. DEFINE_KEYFIELD( m_flPostSpawnDirectionVariance, FIELD_FLOAT, "PostSpawnDirectionVariance" ),
  61. DEFINE_KEYFIELD( m_flPostSpawnSpeed, FIELD_FLOAT, "PostSpawnSpeed" ),
  62. DEFINE_KEYFIELD( m_bPostSpawnUseAngles, FIELD_BOOLEAN, "PostSpawnInheritAngles" ),
  63. // Outputs
  64. DEFINE_OUTPUT( m_pOutputOnSpawned, "OnEntitySpawned" ),
  65. DEFINE_OUTPUT( m_pOutputOnFailedSpawn, "OnEntityFailedSpawn" ),
  66. // Inputs
  67. DEFINE_INPUTFUNC( FIELD_VOID, "ForceSpawn", InputForceSpawn ),
  68. DEFINE_INPUTFUNC( FIELD_STRING, "ForceSpawnAtEntityOrigin", InputForceSpawnAtEntityOrigin ),
  69. // Functions
  70. DEFINE_THINKFUNC( CheckSpawnThink ),
  71. END_DATADESC()
  72. LINK_ENTITY_TO_CLASS( env_entity_maker, CEnvEntityMaker );
  73. //-----------------------------------------------------------------------------
  74. // Purpose:
  75. //-----------------------------------------------------------------------------
  76. void CEnvEntityMaker::Spawn( void )
  77. {
  78. m_vecEntityMins = vec3_origin;
  79. m_vecEntityMaxs = vec3_origin;
  80. m_hCurrentInstance = NULL;
  81. m_hCurrentBlocker = NULL;
  82. m_vecBlockerOrigin = vec3_origin;
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Purpose:
  86. //-----------------------------------------------------------------------------
  87. void CEnvEntityMaker::Activate( void )
  88. {
  89. BaseClass::Activate();
  90. // check for valid template
  91. if ( m_iszTemplate == NULL_STRING )
  92. {
  93. Warning( "env_entity_maker %s has no template entity!\n", GetEntityName().ToCStr() );
  94. UTIL_Remove( this );
  95. return;
  96. }
  97. // Spawn an instance
  98. if ( m_spawnflags & SF_ENTMAKER_AUTOSPAWN )
  99. {
  100. SpawnEntity();
  101. }
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Purpose:
  105. //-----------------------------------------------------------------------------
  106. CPointTemplate *CEnvEntityMaker::FindTemplate()
  107. {
  108. // Find our point_template
  109. CPointTemplate *pTemplate = dynamic_cast<CPointTemplate *>(gEntList.FindEntityByName( NULL, STRING(m_iszTemplate) ));
  110. if ( !pTemplate )
  111. {
  112. Warning( "env_entity_maker %s failed to find template %s.\n", GetEntityName().ToCStr(), STRING(m_iszTemplate) );
  113. }
  114. return pTemplate;
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose: Spawn an instance of the entity
  118. //-----------------------------------------------------------------------------
  119. void CEnvEntityMaker::SpawnEntity( Vector vecAlternateOrigin, QAngle vecAlternateAngles )
  120. {
  121. CPointTemplate *pTemplate = FindTemplate();
  122. if (!pTemplate)
  123. return;
  124. // Spawn our template
  125. Vector vecSpawnOrigin = GetAbsOrigin();
  126. QAngle vecSpawnAngles = GetAbsAngles();
  127. if( vecAlternateOrigin != vec3_invalid )
  128. {
  129. // We have a valid alternate origin and angles. Use those instead
  130. // of spawning the items at my own origin and angles.
  131. vecSpawnOrigin = vecAlternateOrigin;
  132. vecSpawnAngles = vecAlternateAngles;
  133. }
  134. CUtlVector<CBaseEntity*> hNewEntities;
  135. if ( !pTemplate->CreateInstance( vecSpawnOrigin, vecSpawnAngles, &hNewEntities ) )
  136. return;
  137. //Adrian: oops we couldn't spawn the entity (or entities) for some reason!
  138. if ( hNewEntities.Count() == 0 )
  139. return;
  140. m_hCurrentInstance = hNewEntities[0];
  141. // Assume it'll block us
  142. m_hCurrentBlocker = m_hCurrentInstance;
  143. m_vecBlockerOrigin = m_hCurrentBlocker->GetAbsOrigin();
  144. // Store off the mins & maxs the first time we spawn
  145. if ( m_vecEntityMins == vec3_origin )
  146. {
  147. m_hCurrentInstance->CollisionProp()->WorldSpaceAABB( &m_vecEntityMins, &m_vecEntityMaxs );
  148. m_vecEntityMins -= m_hCurrentInstance->GetAbsOrigin();
  149. m_vecEntityMaxs -= m_hCurrentInstance->GetAbsOrigin();
  150. }
  151. // Fire our output
  152. m_pOutputOnSpawned.FireOutput( this, this );
  153. // Start thinking
  154. if ( m_spawnflags & SF_ENTMAKER_AUTOSPAWN )
  155. {
  156. SetThink( &CEnvEntityMaker::CheckSpawnThink );
  157. SetNextThink( gpGlobals->curtime + 0.5f );
  158. }
  159. // If we have a specified post spawn speed, apply it to all spawned entities
  160. if ( m_flPostSpawnSpeed )
  161. {
  162. for ( int i = 0; i < hNewEntities.Count(); i++ )
  163. {
  164. CBaseEntity *pEntity = hNewEntities[i];
  165. if ( pEntity->GetMoveType() == MOVETYPE_NONE )
  166. continue;
  167. // Calculate a velocity for this entity
  168. Vector vForward,vRight,vUp;
  169. QAngle angSpawnDir( m_angPostSpawnDirection );
  170. if ( m_bPostSpawnUseAngles )
  171. {
  172. if ( GetParent() )
  173. {
  174. angSpawnDir += GetParent()->GetAbsAngles();
  175. }
  176. else
  177. {
  178. angSpawnDir += GetAbsAngles();
  179. }
  180. }
  181. AngleVectors( angSpawnDir, &vForward, &vRight, &vUp );
  182. Vector vecShootDir = vForward;
  183. vecShootDir += vRight * random->RandomFloat(-1, 1) * m_flPostSpawnDirectionVariance;
  184. vecShootDir += vForward * random->RandomFloat(-1, 1) * m_flPostSpawnDirectionVariance;
  185. vecShootDir += vUp * random->RandomFloat(-1, 1) * m_flPostSpawnDirectionVariance;
  186. VectorNormalize( vecShootDir );
  187. vecShootDir *= m_flPostSpawnSpeed;
  188. // Apply it to the entity
  189. IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
  190. if ( pPhysicsObject )
  191. {
  192. pPhysicsObject->AddVelocity(&vecShootDir, NULL);
  193. }
  194. else
  195. {
  196. pEntity->SetAbsVelocity( vecShootDir );
  197. }
  198. }
  199. }
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Purpose: Returns whether or not the template entities can fit if spawned.
  203. // Input : pBlocker - Returns blocker unless NULL.
  204. //-----------------------------------------------------------------------------
  205. bool CEnvEntityMaker::HasRoomToSpawn()
  206. {
  207. // Do we have a blocker from last time?
  208. if ( m_hCurrentBlocker )
  209. {
  210. // If it hasn't moved, abort immediately
  211. if ( m_vecBlockerOrigin == m_hCurrentBlocker->GetAbsOrigin() )
  212. {
  213. return false;
  214. }
  215. }
  216. // Check to see if there's enough room to spawn
  217. trace_t tr;
  218. UTIL_TraceHull( GetAbsOrigin(), GetAbsOrigin(), m_vecEntityMins, m_vecEntityMaxs, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  219. if ( tr.m_pEnt || tr.startsolid )
  220. {
  221. // Store off our blocker to check later
  222. m_hCurrentBlocker = tr.m_pEnt;
  223. if ( m_hCurrentBlocker )
  224. {
  225. m_vecBlockerOrigin = m_hCurrentBlocker->GetAbsOrigin();
  226. }
  227. return false;
  228. }
  229. return true;
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Purpose: Returns true if the player is looking towards us.
  233. //-----------------------------------------------------------------------------
  234. bool CEnvEntityMaker::IsPlayerLooking()
  235. {
  236. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  237. {
  238. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  239. if ( pPlayer )
  240. {
  241. // Only spawn if the player's looking away from me
  242. Vector vLookDir = pPlayer->EyeDirection3D();
  243. Vector vTargetDir = GetAbsOrigin() - pPlayer->EyePosition();
  244. VectorNormalize( vTargetDir );
  245. float fDotPr = DotProduct( vLookDir,vTargetDir );
  246. if ( fDotPr > 0 )
  247. return true;
  248. }
  249. }
  250. return false;
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Purpose: Check to see if we should spawn another instance
  254. //-----------------------------------------------------------------------------
  255. void CEnvEntityMaker::CheckSpawnThink( void )
  256. {
  257. SetNextThink( gpGlobals->curtime + 0.5f );
  258. // Do we have an instance?
  259. if ( m_hCurrentInstance )
  260. {
  261. // If Wait-For-Destruction is set, abort immediately
  262. if ( m_spawnflags & SF_ENTMAKER_WAITFORDESTRUCTION )
  263. return;
  264. }
  265. // Check to see if there's enough room to spawn
  266. if ( !HasRoomToSpawn() )
  267. return;
  268. // We're clear, now check to see if the player's looking
  269. if ( !( HasSpawnFlags( SF_ENTMAKER_IGNOREFACING ) ) && IsPlayerLooking() )
  270. return;
  271. // Clear, no player watching, so spawn!
  272. SpawnEntity();
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose: Spawns the entities, checking for space if flagged to do so.
  276. //-----------------------------------------------------------------------------
  277. void CEnvEntityMaker::InputForceSpawn( inputdata_t &inputdata )
  278. {
  279. CPointTemplate *pTemplate = FindTemplate();
  280. if (!pTemplate)
  281. return;
  282. if ( HasSpawnFlags( SF_ENTMAKER_CHECK_FOR_SPACE ) && !HasRoomToSpawn() )
  283. {
  284. m_pOutputOnFailedSpawn.FireOutput( this, this );
  285. return;
  286. }
  287. if ( HasSpawnFlags( SF_ENTMAKER_CHECK_PLAYER_LOOKING ) && IsPlayerLooking() )
  288. {
  289. m_pOutputOnFailedSpawn.FireOutput( this, this );
  290. return;
  291. }
  292. SpawnEntity();
  293. }
  294. //-----------------------------------------------------------------------------
  295. //-----------------------------------------------------------------------------
  296. void CEnvEntityMaker::InputForceSpawnAtEntityOrigin( inputdata_t &inputdata )
  297. {
  298. CBaseEntity *pTargetEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller );
  299. if( pTargetEntity )
  300. {
  301. SpawnEntity( pTargetEntity->GetAbsOrigin(), pTargetEntity->GetAbsAngles() );
  302. }
  303. }