aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/client/c_env_particlescript.cpp
blob: daf6d9e68a9573a421106b9fc1672ccc29f7d5ad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//===========================================================================//

#include "cbase.h"
#include "c_baseanimating.h"
#include "particlemgr.h"
#include "materialsystem/imaterialvar.h"
#include "cl_animevent.h"
#include "particle_util.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

//-----------------------------------------------------------------------------
// An entity which emits other entities at points 
//-----------------------------------------------------------------------------
class C_EnvParticleScript : public C_BaseAnimating, public IParticleEffect
{
public:
	DECLARE_CLASS( C_EnvParticleScript, C_BaseAnimating );
	DECLARE_CLIENTCLASS();

	C_EnvParticleScript();

// IParticleEffect overrides.
public:
	virtual bool	ShouldSimulate() const { return m_bSimulate; }
	virtual void	SetShouldSimulate( bool bSim ) { m_bSimulate = bSim; }

	virtual void RenderParticles( CParticleRenderIterator *pIterator );
	virtual void SimulateParticles( CParticleSimulateIterator *pIterator );

	virtual const Vector &GetSortOrigin();

// C_BaseAnimating overrides
public:
	// NOTE: Ths enclosed particle effect binding will do all the drawing
	// But we have to return true, unlike other particle systems, for the animation events to work
	virtual bool ShouldDraw() { return true; }
	virtual int	DrawModel( int flags ) { return 0; }
	virtual int	GetFxBlend( void ) { return 0; }

	virtual void FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options );
	virtual void OnPreDataChanged( DataUpdateType_t updateType );
	virtual void OnDataChanged( DataUpdateType_t updateType );
	
private:

	// Creates, destroys particles attached to an attachment
	void CreateParticle( const char *pAttachmentName, const char *pSpriteName );
	void DestroyAllParticles( const char *pAttachmentName );
	void DestroyAllParticles( );

private:
	struct ParticleScriptParticle_t : public Particle
	{
		int m_nAttachment;
		float m_flSize;
	};

	CParticleEffectBinding	m_ParticleEffect;
	float m_flMaxParticleSize;
	int m_nOldSequence;
	float m_flSequenceScale;
	bool m_bSimulate;
};

REGISTER_EFFECT( C_EnvParticleScript );

//-----------------------------------------------------------------------------
// Datatable
//-----------------------------------------------------------------------------
IMPLEMENT_CLIENTCLASS_DT( C_EnvParticleScript, DT_EnvParticleScript, CEnvParticleScript )
	RecvPropFloat( RECVINFO(m_flSequenceScale) ),
END_RECV_TABLE()


//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
C_EnvParticleScript::C_EnvParticleScript()
{
	m_flMaxParticleSize = 0.0f;
	m_bSimulate = true;
}


//-----------------------------------------------------------------------------
// Check for changed sequence numbers
//-----------------------------------------------------------------------------
void C_EnvParticleScript::OnPreDataChanged( DataUpdateType_t updateType )
{
	BaseClass::OnPreDataChanged( updateType );

	m_nOldSequence = GetSequence();
}


//-----------------------------------------------------------------------------
// Starts up the particle system
//-----------------------------------------------------------------------------
void C_EnvParticleScript::OnDataChanged( DataUpdateType_t updateType )
{		
	BaseClass::OnDataChanged( updateType );

	if(updateType == DATA_UPDATE_CREATED)
	{
		ParticleMgr()->AddEffect( &m_ParticleEffect, this );
	}

	if ( m_nOldSequence != GetSequence() )
	{
		DestroyAllParticles();
	}
}


//-----------------------------------------------------------------------------
// Creates, destroys particles attached to an attachment
//-----------------------------------------------------------------------------
void C_EnvParticleScript::CreateParticle( const char *pAttachmentName, const char *pSpriteName )
{
	// Find the attachment
	int nAttachment = LookupAttachment( pAttachmentName );
	if ( nAttachment <= 0 )
		return;

	// Get the sprite materials
	PMaterialHandle hMat = m_ParticleEffect.FindOrAddMaterial( pSpriteName );
	ParticleScriptParticle_t *pParticle = 
		(ParticleScriptParticle_t*)m_ParticleEffect.AddParticle(sizeof(ParticleScriptParticle_t), hMat);

	if ( pParticle == NULL )
		return;
	
	// Get the sprite size from the material's materialvars
	bool bFound = false;
	IMaterialVar *pMaterialVar = NULL;
	IMaterial *pMaterial = ParticleMgr()->PMaterialToIMaterial( hMat );
	if ( pMaterial )
	{
		pMaterialVar = pMaterial->FindVar( "$spritesize", &bFound, false );
	}

	if ( bFound )
	{
		pParticle->m_flSize = pMaterialVar->GetFloatValue();
	}
	else
	{
		pParticle->m_flSize = 100.0f;
	}

	// Make sure the particle cull size reflects our particles
	if ( pParticle->m_flSize > m_flMaxParticleSize )
	{
		m_flMaxParticleSize = pParticle->m_flSize;
		m_ParticleEffect.SetParticleCullRadius( m_flMaxParticleSize );
	}

	// Place the particle on the attachment specified
	pParticle->m_nAttachment = nAttachment;
	QAngle vecAngles;
	GetAttachment( nAttachment, pParticle->m_Pos, vecAngles );

	if ( m_flSequenceScale != 1.0f )
	{
		pParticle->m_Pos -= GetAbsOrigin();
		pParticle->m_Pos *= m_flSequenceScale;
		pParticle->m_Pos += GetAbsOrigin();
	}
}

void C_EnvParticleScript::DestroyAllParticles( const char *pAttachmentName )
{
	int nAttachment = LookupAttachment( pAttachmentName );
	if ( nAttachment <= 0 )
		return;

	int nCount = m_ParticleEffect.GetNumActiveParticles();
	Particle** ppParticles = (Particle**)stackalloc( nCount * sizeof(Particle*) );
	int nActualCount = m_ParticleEffect.GetActiveParticleList( nCount, ppParticles );
	Assert( nActualCount == nCount );

	for ( int i = 0; i < nActualCount; ++i )
	{
		ParticleScriptParticle_t *pParticle = (ParticleScriptParticle_t*)ppParticles[i];
		if ( pParticle->m_nAttachment == nAttachment )
		{
			// Mark for deletion
			pParticle->m_nAttachment = -1;
		}
	}
}

void C_EnvParticleScript::DestroyAllParticles( )
{
	int nCount = m_ParticleEffect.GetNumActiveParticles();
	Particle** ppParticles = (Particle**)stackalloc( nCount * sizeof(Particle*) );
	int nActualCount = m_ParticleEffect.GetActiveParticleList( nCount, ppParticles );
	Assert( nActualCount == nCount );

	for ( int i = 0; i < nActualCount; ++i )
	{
		ParticleScriptParticle_t *pParticle = (ParticleScriptParticle_t*)ppParticles[i];

		// Mark for deletion
		pParticle->m_nAttachment = -1;
	}
}


//-----------------------------------------------------------------------------
// The animation events will create particles on the attachment points
//-----------------------------------------------------------------------------
void C_EnvParticleScript::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
{
	// Handle events to create + destroy particles
	switch( event )
	{
	case CL_EVENT_SPRITEGROUP_CREATE:
		{
			char pAttachmentName[256];
			char pSpriteName[256];
			int nArgs = sscanf( options, "%255s %255s", pAttachmentName, pSpriteName );
			if ( nArgs == 2 )
			{
				CreateParticle( pAttachmentName, pSpriteName );
			}
		}
		return;

	case CL_EVENT_SPRITEGROUP_DESTROY:
		{
			char pAttachmentName[256];
			int nArgs = sscanf( options, "%255s", pAttachmentName );
			if ( nArgs == 1 )
			{
				DestroyAllParticles( pAttachmentName );
			}
		}
		return;
	}

	// Fall back
	BaseClass::FireEvent( origin, angles, event, options );
}


//-----------------------------------------------------------------------------
// Simulate the particles
//-----------------------------------------------------------------------------
void C_EnvParticleScript::RenderParticles( CParticleRenderIterator *pIterator )
{
	const ParticleScriptParticle_t* pParticle = (const ParticleScriptParticle_t*)pIterator->GetFirst();
	while ( pParticle )
	{
		Vector vecRenderPos;
		TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, vecRenderPos );
		float sortKey = vecRenderPos.z;

		Vector color( 1, 1, 1 );
		RenderParticle_ColorSize( pIterator->GetParticleDraw(), vecRenderPos, color, 1.0f, pParticle->m_flSize );
		
		pParticle = (const ParticleScriptParticle_t*)pIterator->GetNext( sortKey );
	}
}

void C_EnvParticleScript::SimulateParticles( CParticleSimulateIterator *pIterator )
{
	ParticleScriptParticle_t* pParticle = (ParticleScriptParticle_t*)pIterator->GetFirst();
	while ( pParticle )
	{
		// Here's how we retire particles
		if ( pParticle->m_nAttachment == -1 )
		{
			pIterator->RemoveParticle( pParticle );
		}
		else
		{
			// Move the particle to the attachment point
			QAngle vecAngles;
			GetAttachment( pParticle->m_nAttachment, pParticle->m_Pos, vecAngles );

			if ( m_flSequenceScale != 1.0f )
			{
				pParticle->m_Pos -= GetAbsOrigin();
				pParticle->m_Pos *= m_flSequenceScale;
				pParticle->m_Pos += GetAbsOrigin();
			}
		}
		
		pParticle = (ParticleScriptParticle_t*)pIterator->GetNext();
	}
}

const Vector &C_EnvParticleScript::GetSortOrigin()
{
	return GetAbsOrigin();
}