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.

407 lines
13 KiB

  1. //====== Copyright (c) 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. #include "tier1/fmtstr.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. //-----------------------------------------------------------------------------
  16. // Purpose: An entity that spawns and controls a particle system
  17. //-----------------------------------------------------------------------------
  18. class C_ParticleSystem : public C_BaseEntity
  19. {
  20. DECLARE_CLASS( C_ParticleSystem, C_BaseEntity );
  21. public:
  22. DECLARE_CLIENTCLASS();
  23. C_ParticleSystem( void );
  24. virtual void PreDataUpdate( DataUpdateType_t updateType );
  25. virtual void PostDataUpdate( DataUpdateType_t updateType );
  26. virtual void ClientThink( void );
  27. protected:
  28. ~C_ParticleSystem( void );
  29. int m_iEffectIndex;
  30. int m_nStopType;
  31. bool m_bActive;
  32. bool m_bOldActive;
  33. float m_flStartTime; // Time at which the effect started
  34. char m_szSnapshotFileName[ MAX_PATH ];
  35. //server controlled control points (variables in particle effects instead of literal follow points)
  36. Vector m_vServerControlPoints[4];
  37. uint8 m_iServerControlPointAssignments[4];
  38. CUtlReference< CNewParticleEffect > m_pEffect;
  39. CParticleSnapshot *m_pSnapshot;
  40. enum { kMAXCONTROLPOINTS = 63 }; ///< actually one less than the total number of cpoints since 0 is assumed to be me
  41. // stop types
  42. enum
  43. {
  44. STOP_NORMAL = 0,
  45. STOP_DESTROY_IMMEDIATELY,
  46. STOP_PLAY_ENDCAP,
  47. NUM_STOP_TYPES
  48. };
  49. EHANDLE m_hControlPointEnts[kMAXCONTROLPOINTS];
  50. // SendPropArray3( SENDINFO_ARRAY3(m_iControlPointParents), SendPropInt( SENDINFO_ARRAY(m_iControlPointParents), 3, SPROP_UNSIGNED ) ),
  51. unsigned char m_iControlPointParents[kMAXCONTROLPOINTS];
  52. };
  53. extern void RecvProxy_EffectFlags( const CRecvProxyData *pData, void *pStruct, void *pOut );
  54. IMPLEMENT_CLIENTCLASS(C_ParticleSystem, DT_ParticleSystem, CParticleSystem);
  55. BEGIN_RECV_TABLE_NOBASE( C_ParticleSystem, DT_ParticleSystem )
  56. RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
  57. RecvPropInt(RECVINFO(m_fEffects), 0, RecvProxy_EffectFlags ),
  58. RecvPropEHandle( RECVINFO(m_hOwnerEntity) ),
  59. RecvPropInt( RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent ),
  60. RecvPropInt( RECVINFO( m_iParentAttachment ) ),
  61. RecvPropQAngles( RECVINFO_NAME( m_angNetworkAngles, m_angRotation ) ),
  62. RecvPropInt( RECVINFO( m_iEffectIndex ) ),
  63. RecvPropBool( RECVINFO( m_bActive ) ),
  64. RecvPropInt( RECVINFO( m_nStopType ) ),
  65. RecvPropFloat( RECVINFO( m_flStartTime ) ),
  66. RecvPropString( RECVINFO( m_szSnapshotFileName ) ),
  67. RecvPropArray3( RECVINFO_ARRAY(m_vServerControlPoints), RecvPropVector( RECVINFO( m_vServerControlPoints[0] ) ) ),
  68. RecvPropArray3( RECVINFO_ARRAY(m_iServerControlPointAssignments), RecvPropInt( RECVINFO(m_iServerControlPointAssignments[0]))),
  69. RecvPropArray3( RECVINFO_ARRAY(m_hControlPointEnts), RecvPropEHandle( RECVINFO( m_hControlPointEnts[0] ) ) ),
  70. RecvPropArray3( RECVINFO_ARRAY(m_iControlPointParents), RecvPropInt( RECVINFO(m_iControlPointParents[0]))),
  71. END_RECV_TABLE();
  72. //-----------------------------------------------------------------------------
  73. // Purpose:
  74. //-----------------------------------------------------------------------------
  75. C_ParticleSystem::C_ParticleSystem( void )
  76. : m_pSnapshot( NULL )
  77. {
  78. memset( m_szSnapshotFileName, 0, sizeof( m_szSnapshotFileName ) );
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Purpose:
  82. //-----------------------------------------------------------------------------
  83. C_ParticleSystem::~C_ParticleSystem( void )
  84. {
  85. if ( m_pSnapshot )
  86. {
  87. delete m_pSnapshot;
  88. m_pSnapshot = NULL;
  89. }
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose:
  93. //-----------------------------------------------------------------------------
  94. void C_ParticleSystem::PreDataUpdate( DataUpdateType_t updateType )
  95. {
  96. m_bOldActive = m_bActive;
  97. BaseClass::PreDataUpdate( updateType );
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Purpose:
  101. //-----------------------------------------------------------------------------
  102. void C_ParticleSystem::PostDataUpdate( DataUpdateType_t updateType )
  103. {
  104. BaseClass::PostDataUpdate( updateType );
  105. // Always restart if just created and updated
  106. // FIXME: Does this play fairly with PVS?
  107. if ( updateType == DATA_UPDATE_CREATED )
  108. {
  109. // TODO: !!HACK HACK HACK!! .PSF files should be loaded/refcounted through the CParticleSystemMgr (ala .PCFs).
  110. // The current code will duplicate a given .PSF file in memory for every info_particle_system that uses it!
  111. if ( m_szSnapshotFileName[0] )
  112. {
  113. m_pSnapshot = new CParticleSnapshot();
  114. if ( !m_pSnapshot->Unserialize( CFmtStr( "particles/%s.psf", m_szSnapshotFileName ) ) )
  115. {
  116. delete m_pSnapshot;
  117. m_pSnapshot = NULL;
  118. }
  119. }
  120. if ( m_bActive )
  121. {
  122. // Delayed here so that we don't get invalid abs queries on level init with active particle systems
  123. SetNextClientThink( gpGlobals->curtime );
  124. }
  125. }
  126. else
  127. {
  128. if ( m_bOldActive != m_bActive )
  129. {
  130. if ( m_bActive )
  131. {
  132. // Delayed here so that we don't get invalid abs queries on level init with active particle systems
  133. SetNextClientThink( gpGlobals->curtime );
  134. }
  135. else
  136. {
  137. switch( m_nStopType )
  138. {
  139. case STOP_NORMAL:
  140. {
  141. ParticleProp()->StopEmission();
  142. }
  143. break;
  144. case STOP_DESTROY_IMMEDIATELY:
  145. {
  146. ParticleProp()->StopEmissionAndDestroyImmediately();
  147. }
  148. break;
  149. case STOP_PLAY_ENDCAP:
  150. {
  151. ParticleProp()->StopEmission( NULL, false, false, false, true);
  152. }
  153. break;
  154. }
  155. }
  156. }
  157. if( m_bActive && ParticleProp()->IsValidEffect( m_pEffect ) )
  158. {
  159. //server controlled control points (variables in particle effects instead of literal follow points)
  160. for( int i = 0; i != ARRAYSIZE( m_iServerControlPointAssignments ); ++i )
  161. {
  162. if( m_iServerControlPointAssignments[i] != 255 )
  163. {
  164. m_pEffect->SetControlPoint( m_iServerControlPointAssignments[i], m_vServerControlPoints[i] );
  165. }
  166. else
  167. {
  168. break;
  169. }
  170. }
  171. }
  172. }
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Purpose:
  176. //-----------------------------------------------------------------------------
  177. void C_ParticleSystem::ClientThink( void )
  178. {
  179. if ( m_bActive )
  180. {
  181. const char *pszName = GetParticleSystemNameFromIndex( m_iEffectIndex );
  182. if ( pszName && pszName[0] )
  183. {
  184. CNewParticleEffect *pEffect = ParticleProp()->Create( pszName, PATTACH_ABSORIGIN_FOLLOW );
  185. m_pEffect = pEffect;
  186. if (pEffect)
  187. {
  188. for ( int i = 0 ; i < kMAXCONTROLPOINTS ; ++i )
  189. {
  190. CBaseEntity *pOnEntity = m_hControlPointEnts[i].Get();
  191. if ( pOnEntity )
  192. {
  193. ParticleProp()->AddControlPoint( pEffect, i + 1, pOnEntity, PATTACH_ABSORIGIN_FOLLOW );
  194. }
  195. AssertMsg2( m_iControlPointParents[i] >= 0 && m_iControlPointParents[i] <= kMAXCONTROLPOINTS ,
  196. "Particle system specified bogus control point parent (%d) for point %d.",
  197. m_iControlPointParents[i], i );
  198. if (m_iControlPointParents[i] != 0)
  199. {
  200. pEffect->SetControlPointParent(i+1, m_iControlPointParents[i]);
  201. }
  202. }
  203. //server controlled control points (variables in particle effects instead of literal follow points)
  204. for( int i = 0; i != ARRAYSIZE( m_iServerControlPointAssignments ); ++i )
  205. {
  206. if( m_iServerControlPointAssignments[i] != 255 )
  207. {
  208. pEffect->SetControlPoint( m_iServerControlPointAssignments[i], m_vServerControlPoints[i] );
  209. }
  210. else
  211. {
  212. break;
  213. }
  214. }
  215. // Attach our particle snapshot if we have one
  216. Assert( m_pSnapshot || !m_szSnapshotFileName[0] ); // m_szSnapshotFileName shouldn't change after the create update
  217. if ( m_pSnapshot )
  218. {
  219. pEffect->SetControlPointSnapshot( 0, m_pSnapshot );
  220. }
  221. // NOTE: What we really want here is to compare our lifetime and that of our children and see if this delta is
  222. // already past the end of it, denoting that we're finished. In that case, just destroy us and be done. -- jdw
  223. // TODO: This can go when the SkipToTime code below goes
  224. ParticleProp()->OnParticleSystemUpdated( pEffect, 0.0f );
  225. // Skip the effect ahead if we're restarting it
  226. float flTimeDelta = gpGlobals->curtime - m_flStartTime;
  227. if ( flTimeDelta > 0.01f )
  228. {
  229. VPROF_BUDGET( "C_ParticleSystem::ClientThink SkipToTime", "Particle Simulation" );
  230. pEffect->SkipToTime( flTimeDelta );
  231. }
  232. }
  233. }
  234. }
  235. }
  236. //======================================================================================================================
  237. // PARTICLE SYSTEM DISPATCH EFFECT
  238. //======================================================================================================================
  239. //-----------------------------------------------------------------------------
  240. // Purpose:
  241. //-----------------------------------------------------------------------------
  242. void StartParticleEffect( const CEffectData &data, int nSplitScreenPlayerSlot /*= -1*/ )
  243. {
  244. // this needs to be before using data.m_nHitBox,
  245. // since that may be a serialized value that's past the end of the current particle system string table
  246. if ( SuppressingParticleEffects() )
  247. return;
  248. // Don't crash if we're passed an invalid particle system
  249. if ( data.m_nHitBox == 0 )
  250. return;
  251. if ( data.m_fFlags & PARTICLE_DISPATCH_FROM_ENTITY )
  252. {
  253. if ( data.m_hEntity.Get() )
  254. {
  255. C_BaseEntity *pEnt = C_BaseEntity::Instance( data.m_hEntity );
  256. // commented out assert. dormant entities have their particle system spawns stopped.
  257. //Assert( pEnt && !pEnt->IsDormant() );
  258. if ( pEnt && ( !pEnt->IsDormant() || ( data.m_fFlags & PARTICLE_DISPATCH_ALLOW_DORMANT ) ) )
  259. {
  260. if ( data.m_fFlags & PARTICLE_DISPATCH_RESET_PARTICLES )
  261. {
  262. pEnt->ParticleProp()->StopEmission();
  263. }
  264. CUtlReference<CNewParticleEffect> pEffect = pEnt->ParticleProp()->CreatePrecached( data.m_nHitBox, (ParticleAttachment_t)data.m_nDamageType, data.m_nAttachmentIndex );
  265. if ( pEffect.IsValid() && pEffect->IsValid() )
  266. {
  267. pEffect->SetDrawOnlyForSplitScreenUser( nSplitScreenPlayerSlot );
  268. if ( (ParticleAttachment_t)data.m_nDamageType == PATTACH_CUSTOMORIGIN )
  269. {
  270. pEffect->SetSortOrigin( data.m_vOrigin );
  271. pEffect->SetControlPoint( 0, data.m_vOrigin );
  272. pEffect->SetControlPoint( 1, data.m_vStart );
  273. Vector vecForward, vecRight, vecUp;
  274. AngleVectors( data.m_vAngles, &vecForward, &vecRight, &vecUp );
  275. pEffect->SetControlPointOrientation( 0, vecForward, vecRight, vecUp );
  276. }
  277. else if ( data.m_nOtherEntIndex > 0 )
  278. {
  279. C_BaseEntity *pOtherEnt = ClientEntityList().GetEnt( data.m_nOtherEntIndex );
  280. if ( pOtherEnt )
  281. {
  282. pEnt->ParticleProp()->AddControlPoint( pEffect, 1, pOtherEnt, PATTACH_ABSORIGIN_FOLLOW, NULL, Vector( 0, 0, 50 ) );
  283. }
  284. }
  285. }
  286. }
  287. }
  288. }
  289. else
  290. {
  291. CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindPrecachedParticleSystem( data.m_nHitBox );
  292. if ( pDef )
  293. {
  294. CUtlReference<CNewParticleEffect> pEffect = CNewParticleEffect::CreateOrAggregate( NULL, pDef, data.m_vOrigin, NULL, nSplitScreenPlayerSlot );
  295. if ( pEffect.IsValid() && pEffect->IsValid() )
  296. {
  297. pEffect->SetSortOrigin( data.m_vOrigin );
  298. pEffect->SetControlPoint( 0, data.m_vOrigin );
  299. pEffect->SetControlPoint( 1, data.m_vStart );
  300. Vector vecForward, vecRight, vecUp;
  301. AngleVectors( data.m_vAngles, &vecForward, &vecRight, &vecUp );
  302. pEffect->SetControlPointOrientation( 0, vecForward, vecRight, vecUp );
  303. }
  304. }
  305. else
  306. {
  307. Warning( "StartParticleEffect: Failed to find precached particle system for %d!!\n", data.m_nHitBox );
  308. }
  309. }
  310. }
  311. void ParticleEffectCallback( const CEffectData &data )
  312. {
  313. // NOTE: This is because this effect doesn't need to participate
  314. // in the precache validation tests. Particle systems used with this
  315. // effect must already be precached.
  316. g_pPrecacheSystem->EndLimitedResourceAccess( );
  317. // From networking always go draw for all local players
  318. StartParticleEffect( data, -1 );
  319. }
  320. DECLARE_CLIENT_EFFECT( ParticleEffect, ParticleEffectCallback )
  321. //======================================================================================================================
  322. // PARTICLE SYSTEM STOP EFFECT
  323. //======================================================================================================================
  324. //-----------------------------------------------------------------------------
  325. // Purpose:
  326. //-----------------------------------------------------------------------------
  327. void ParticleEffectStopCallback( const CEffectData &data )
  328. {
  329. if ( data.m_hEntity.Get() )
  330. {
  331. C_BaseEntity *pEnt = C_BaseEntity::Instance( data.m_hEntity );
  332. if ( pEnt )
  333. {
  334. if ( data.m_nHitBox > 0 )
  335. {
  336. if ( pEnt->IsWorld() )
  337. {
  338. if ( data.m_nHitBox > 0 )
  339. {
  340. CNewParticleEffect::RemoveParticleEffect( data.m_nHitBox );
  341. }
  342. }
  343. else
  344. {
  345. CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindPrecachedParticleSystem( data.m_nHitBox );
  346. if ( pDef )
  347. {
  348. pEnt->ParticleProp()->StopParticlesNamed( pDef->GetName(), true );
  349. }
  350. }
  351. }
  352. else
  353. {
  354. pEnt->ParticleProp()->StopEmission( NULL, true, true, false, true );
  355. }
  356. }
  357. }
  358. }
  359. DECLARE_CLIENT_EFFECT( ParticleEffectStop, ParticleEffectStopCallback );