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.

281 lines
9.5 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "particles/particles.h"
  8. #include "c_te_effect_dispatch.h"
  9. #include "particles_new.h"
  10. #include "networkstringtable_clientdll.h"
  11. #include "tier0/vprof.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //-----------------------------------------------------------------------------
  15. // Purpose: An entity that spawns and controls a particle system
  16. //-----------------------------------------------------------------------------
  17. class C_ParticleSystem : public C_BaseEntity
  18. {
  19. DECLARE_CLASS( C_ParticleSystem, C_BaseEntity );
  20. public:
  21. DECLARE_CLIENTCLASS();
  22. C_ParticleSystem();
  23. void PreDataUpdate( DataUpdateType_t updateType );
  24. void PostDataUpdate( DataUpdateType_t updateType );
  25. void ClientThink( void );
  26. protected:
  27. int m_iEffectIndex;
  28. bool m_bActive;
  29. bool m_bOldActive;
  30. float m_flStartTime; // Time at which the effect started
  31. enum { kMAXCONTROLPOINTS = 63 }; ///< actually one less than the total number of cpoints since 0 is assumed to be me
  32. EHANDLE m_hControlPointEnts[kMAXCONTROLPOINTS];
  33. // SendPropArray3( SENDINFO_ARRAY3(m_iControlPointParents), SendPropInt( SENDINFO_ARRAY(m_iControlPointParents), 3, SPROP_UNSIGNED ) ),
  34. unsigned char m_iControlPointParents[kMAXCONTROLPOINTS];
  35. bool m_bWeatherEffect;
  36. };
  37. IMPLEMENT_CLIENTCLASS(C_ParticleSystem, DT_ParticleSystem, CParticleSystem);
  38. BEGIN_RECV_TABLE_NOBASE( C_ParticleSystem, DT_ParticleSystem )
  39. RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
  40. RecvPropEHandle( RECVINFO(m_hOwnerEntity) ),
  41. RecvPropInt( RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent ),
  42. RecvPropInt( RECVINFO( m_iParentAttachment ) ),
  43. RecvPropQAngles( RECVINFO_NAME( m_angNetworkAngles, m_angRotation ) ),
  44. RecvPropInt( RECVINFO( m_iEffectIndex ) ),
  45. RecvPropBool( RECVINFO( m_bActive ) ),
  46. RecvPropFloat( RECVINFO( m_flStartTime ) ),
  47. RecvPropArray3( RECVINFO_ARRAY(m_hControlPointEnts), RecvPropEHandle( RECVINFO( m_hControlPointEnts[0] ) ) ),
  48. RecvPropArray3( RECVINFO_ARRAY(m_iControlPointParents), RecvPropInt( RECVINFO(m_iControlPointParents[0]))),
  49. RecvPropBool( RECVINFO( m_bWeatherEffect ) ),
  50. END_RECV_TABLE();
  51. //-----------------------------------------------------------------------------
  52. // Purpose:
  53. //-----------------------------------------------------------------------------
  54. C_ParticleSystem::C_ParticleSystem()
  55. {
  56. m_bWeatherEffect = false;
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Purpose:
  60. //-----------------------------------------------------------------------------
  61. void C_ParticleSystem::PreDataUpdate( DataUpdateType_t updateType )
  62. {
  63. m_bOldActive = m_bActive;
  64. BaseClass::PreDataUpdate( updateType );
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose:
  68. //-----------------------------------------------------------------------------
  69. void C_ParticleSystem::PostDataUpdate( DataUpdateType_t updateType )
  70. {
  71. BaseClass::PostDataUpdate( updateType );
  72. // Always restart if just created and updated
  73. // FIXME: Does this play fairly with PVS?
  74. if ( updateType == DATA_UPDATE_CREATED )
  75. {
  76. if ( m_bActive )
  77. {
  78. // Delayed here so that we don't get invalid abs queries on level init with active particle systems
  79. SetNextClientThink( gpGlobals->curtime );
  80. }
  81. }
  82. else
  83. {
  84. if ( m_bOldActive != m_bActive )
  85. {
  86. if ( m_bActive )
  87. {
  88. // Delayed here so that we don't get invalid abs queries on level init with active particle systems
  89. SetNextClientThink( gpGlobals->curtime );
  90. }
  91. else
  92. {
  93. ParticleProp()->StopEmission();
  94. }
  95. }
  96. }
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Purpose:
  100. //-----------------------------------------------------------------------------
  101. void C_ParticleSystem::ClientThink( void )
  102. {
  103. if ( m_bActive )
  104. {
  105. const char *pszName = GetParticleSystemNameFromIndex( m_iEffectIndex );
  106. if ( pszName && pszName[0] )
  107. {
  108. if ( !GameRules()->AllowMapParticleEffect( pszName ) )
  109. return;
  110. if ( m_bWeatherEffect && !GameRules()->AllowWeatherParticles() )
  111. return;
  112. CNewParticleEffect *pEffect = ParticleProp()->Create( pszName, PATTACH_ABSORIGIN_FOLLOW );
  113. AssertMsg1( pEffect, "Particle system couldn't make %s", pszName );
  114. if (pEffect)
  115. {
  116. for ( int i = 0 ; i < kMAXCONTROLPOINTS ; ++i )
  117. {
  118. CBaseEntity *pOnEntity = m_hControlPointEnts[i].Get();
  119. if ( pOnEntity )
  120. {
  121. ParticleProp()->AddControlPoint( pEffect, i + 1, pOnEntity, PATTACH_ABSORIGIN_FOLLOW );
  122. }
  123. AssertMsg2( m_iControlPointParents[i] >= 0 && m_iControlPointParents[i] <= kMAXCONTROLPOINTS ,
  124. "Particle system specified bogus control point parent (%d) for point %d.",
  125. m_iControlPointParents[i], i );
  126. if (m_iControlPointParents[i] != 0)
  127. {
  128. pEffect->SetControlPointParent(i+1, m_iControlPointParents[i]);
  129. }
  130. }
  131. // NOTE: What we really want here is to compare our lifetime and that of our children and see if this delta is
  132. // already past the end of it, denoting that we're finished. In that case, just destroy us and be done. -- jdw
  133. // TODO: This can go when the SkipToTime code below goes
  134. ParticleProp()->OnParticleSystemUpdated( pEffect, 0.0f );
  135. // Skip the effect ahead if we're restarting it
  136. float flTimeDelta = gpGlobals->curtime - m_flStartTime;
  137. if ( flTimeDelta > 0.01f )
  138. {
  139. VPROF_BUDGET( "C_ParticleSystem::ClientThink SkipToTime", "Particle Simulation" );
  140. pEffect->SkipToTime( flTimeDelta );
  141. }
  142. }
  143. }
  144. }
  145. }
  146. //======================================================================================================================
  147. // PARTICLE SYSTEM DISPATCH EFFECT
  148. //======================================================================================================================
  149. //-----------------------------------------------------------------------------
  150. // Purpose:
  151. //-----------------------------------------------------------------------------
  152. void ParticleEffectCallback( const CEffectData &data )
  153. {
  154. if ( SuppressingParticleEffects() )
  155. return; // this needs to be before using data.m_nHitBox, since that may be a serialized value that's past the end of the current particle system string table
  156. const char *pszName = GetParticleSystemNameFromIndex( data.m_nHitBox );
  157. CSmartPtr<CNewParticleEffect> pEffect = NULL;
  158. if ( data.m_fFlags & PARTICLE_DISPATCH_FROM_ENTITY )
  159. {
  160. if ( data.m_hEntity.Get() )
  161. {
  162. C_BaseEntity *pEnt = C_BaseEntity::Instance( data.m_hEntity );
  163. if ( pEnt && !pEnt->IsDormant() )
  164. {
  165. if ( data.m_fFlags & PARTICLE_DISPATCH_RESET_PARTICLES )
  166. {
  167. pEnt->ParticleProp()->StopEmission();
  168. }
  169. Vector vOffset = vec3_origin;
  170. ParticleAttachment_t iAttachType = (ParticleAttachment_t)data.m_nDamageType;
  171. if ( iAttachType == PATTACH_ABSORIGIN_FOLLOW || iAttachType == PATTACH_POINT_FOLLOW || iAttachType == PATTACH_ROOTBONE_FOLLOW )
  172. {
  173. vOffset = data.m_vStart;
  174. }
  175. pEffect = pEnt->ParticleProp()->Create( pszName, iAttachType, data.m_nAttachmentIndex, vOffset );
  176. AssertMsg2( pEffect.IsValid() && pEffect->IsValid(), "%s could not create particle effect %s",
  177. C_BaseEntity::Instance( data.m_hEntity )->GetDebugName(), pszName );
  178. if ( pEffect.IsValid() && pEffect->IsValid() )
  179. {
  180. if ( iAttachType == PATTACH_CUSTOMORIGIN )
  181. {
  182. pEffect->SetSortOrigin( data.m_vOrigin );
  183. pEffect->SetControlPoint( 0, data.m_vOrigin );
  184. pEffect->SetControlPoint( 1, data.m_vStart );
  185. Vector vecForward, vecRight, vecUp;
  186. AngleVectors( data.m_vAngles, &vecForward, &vecRight, &vecUp );
  187. pEffect->SetControlPointOrientation( 0, vecForward, vecRight, vecUp );
  188. }
  189. }
  190. }
  191. }
  192. }
  193. else
  194. {
  195. if ( GameRules() )
  196. {
  197. pszName = GameRules()->TranslateEffectForVisionFilter( "particles", pszName );
  198. }
  199. pEffect = CNewParticleEffect::Create( NULL, pszName );
  200. if ( pEffect->IsValid() )
  201. {
  202. pEffect->SetSortOrigin( data.m_vOrigin );
  203. pEffect->SetControlPoint( 0, data.m_vOrigin );
  204. pEffect->SetControlPoint( 1, data.m_vStart );
  205. Vector vecForward, vecRight, vecUp;
  206. AngleVectors( data.m_vAngles, &vecForward, &vecRight, &vecUp );
  207. pEffect->SetControlPointOrientation( 0, vecForward, vecRight, vecUp );
  208. }
  209. }
  210. if ( pEffect.IsValid() && pEffect->IsValid() )
  211. {
  212. if ( data.m_bCustomColors )
  213. {
  214. pEffect->SetControlPoint( CUSTOM_COLOR_CP1, data.m_CustomColors.m_vecColor1 );
  215. pEffect->SetControlPoint( CUSTOM_COLOR_CP2, data.m_CustomColors.m_vecColor2 );
  216. }
  217. if ( data.m_bControlPoint1 )
  218. {
  219. pEffect->SetControlPoint( 1, data.m_ControlPoint1.m_vecOffset );
  220. }
  221. }
  222. }
  223. DECLARE_CLIENT_EFFECT( "ParticleEffect", ParticleEffectCallback );
  224. //======================================================================================================================
  225. // PARTICLE SYSTEM STOP EFFECT
  226. //======================================================================================================================
  227. //-----------------------------------------------------------------------------
  228. // Purpose:
  229. //-----------------------------------------------------------------------------
  230. void ParticleEffectStopCallback( const CEffectData &data )
  231. {
  232. if ( data.m_hEntity.Get() )
  233. {
  234. C_BaseEntity *pEnt = C_BaseEntity::Instance( data.m_hEntity );
  235. if ( pEnt )
  236. {
  237. pEnt->ParticleProp()->StopEmission();
  238. }
  239. }
  240. }
  241. DECLARE_CLIENT_EFFECT( "ParticleEffectStop", ParticleEffectStopCallback );