// ----------------------------------------------------------------------- //
//
// MODULE  : ParticleSystemFX.cpp
//
// PURPOSE : ParticleSystem special FX - Implementation
//
// CREATED : 10/24/97
//
// ----------------------------------------------------------------------- //

#include "ParticleSystemFX.h"
#include "cpp_client_de.h"
#include "ClientUtilities.h"
#include "ClientServerShared.h"


#define MAX_PS_TIME_DELTA 0.2f



// ----------------------------------------------------------------------- //
//
//	ROUTINE:	CParticleSystemFX::CParticleSystemFX
//
//	PURPOSE:	Construct
//
// ----------------------------------------------------------------------- //

CParticleSystemFX::CParticleSystemFX() : CBaseParticleSystemFX()
{
	m_bFirstUpdate			= DTRUE;
	m_fLastTime				= 0.0f;
	m_fNextUpdate			= 0.01f;

	VEC_INIT(m_vMinOffset);
	VEC_INIT(m_vMaxOffset);
	VEC_INIT(m_vMinVel);
	VEC_INIT(m_vMaxVel);

	m_dwLastFrameUserFlags = USRFLG_VISIBLE;
}


// ----------------------------------------------------------------------- //
//
//	ROUTINE:	CParticleSystemFX::Init
//
//	PURPOSE:	Init the particle system
//
// ----------------------------------------------------------------------- //

DBOOL CParticleSystemFX::Init(SFXCREATESTRUCT* psfxCreateStruct)
{
	if (!CBaseParticleSystemFX::Init(psfxCreateStruct)) return DFALSE;

	// Set up our creation struct...

	PSCREATESTRUCT* pPS = (PSCREATESTRUCT*)psfxCreateStruct;
	m_cs = *pPS;

	// Set our (parent's) flags...

	m_dwFlags  = m_cs.dwFlags;
	m_fRadius  = m_cs.fParticleRadius;
	m_fGravity = m_cs.fGravity;

	return DTRUE;
}


// ----------------------------------------------------------------------- //
//
//	ROUTINE:	CParticleSystemFX::CreateObject
//
//	PURPOSE:	Create object associated the particle system.
//
// ----------------------------------------------------------------------- //

DBOOL CParticleSystemFX::CreateObject(CClientDE *pClientDE)
{
	if (!pClientDE ) return DFALSE;

	if (m_cs.hstrTextureName)
	{
		m_pTextureName = pClientDE->GetStringData(m_cs.hstrTextureName);
	}

	DBOOL bRet = CBaseParticleSystemFX::CreateObject(pClientDE);

	if (bRet && m_hObject && m_hServerObject)
	{
		DRotation rRot;
		pClientDE->GetObjectRotation(m_hServerObject, &rRot);
		pClientDE->SetObjectRotation(m_hObject, &rRot);

		pClientDE->GetObjectUserFlags(m_hServerObject, &m_dwLastFrameUserFlags);
		if (!(m_dwLastFrameUserFlags & USRFLG_VISIBLE))
		{
			pClientDE->SetObjectFlags(m_hObject, 0);
		}
	}

	return bRet;
}


// ----------------------------------------------------------------------- //
//
//	ROUTINE:	CParticleSystemFX::Update
//
//	PURPOSE:	Update the particle system
//
// ----------------------------------------------------------------------- //

DBOOL CParticleSystemFX::Update()
{
	if (!m_hObject || !m_pClientDE || m_bWantRemove) return DFALSE;

	DFLOAT fTime = m_pClientDE->GetTime();

	// Hide/show the particle system if necessary...

	if (m_hServerObject)
	{
		DDWORD dwUserFlags;
		m_pClientDE->GetObjectUserFlags(m_hServerObject, &dwUserFlags);

		DBOOL bRet = DFALSE;

		if (dwUserFlags != m_dwLastFrameUserFlags)
		{
			if (!(dwUserFlags & USRFLG_VISIBLE))
			{
				m_pClientDE->SetObjectFlags(m_hObject, 0);
				bRet = DTRUE;
			}
			else
			{
				m_pClientDE->SetObjectFlags(m_hObject, FLAG_VISIBLE);
			}
		}

		m_dwLastFrameUserFlags = dwUserFlags;

		if (bRet) 
		{
			m_fLastTime = fTime;
			return DTRUE;
		}
	}


	if (m_bFirstUpdate)
	{
		m_fLastTime = fTime;

		VEC_SET(m_vMinOffset, -m_cs.fEmissionRadius, -m_cs.fEmissionRadius, -m_cs.fEmissionRadius);
		VEC_SET(m_vMaxOffset, m_cs.fEmissionRadius, m_cs.fEmissionRadius, m_cs.fEmissionRadius);

		VEC_SET(m_vMinVel, -m_cs.fVelocityOffset, m_cs.fMinimumVelocity, -m_cs.fVelocityOffset);
		VEC_SET(m_vMaxVel, m_cs.fVelocityOffset, m_cs.fMaximumVelocity, m_cs.fVelocityOffset);
						
		m_bFirstUpdate = DFALSE;
	}


	// Make sure it is time to update...

	if (fTime < m_fLastTime + m_fNextUpdate)
	{
		return DTRUE;
	}


	// Ok, how many to add this frame....
	float timeDelta = DMIN(fTime - m_fLastTime, MAX_PS_TIME_DELTA);
	int nToAdd = (int) floor(m_cs.fParticlesPerSecond * timeDelta);

	m_pClientDE->AddParticles(m_hObject, nToAdd,
		&m_vMinOffset, &m_vMaxOffset,			// Position offset
		&m_vMinVel, &m_vMaxVel,					// Velocity
		&(m_cs.vColor1), &(m_cs.vColor2),		// Color
		m_cs.fParticleLifetime, m_cs.fParticleLifetime);


	// Determine when next update should occur...

	if (m_cs.fBurstWait > 0.001f) 
	{
		m_fNextUpdate = m_cs.fBurstWait * GetRandom(0.01f, 1.0f);
	}
	else 
	{
		m_fNextUpdate = 0.01f;
	}

	
	// Rotate the particle system...

	if (m_cs.fRotationVelocity != 0.0f)
	{
		DRotation rRot;
		m_pClientDE->GetObjectRotation(m_hObject, &rRot);
		m_pClientDE->EulerRotateY(&rRot, m_pClientDE->GetFrameTime() * m_cs.fRotationVelocity);
		m_pClientDE->SetObjectRotation(m_hObject, &rRot);
	}

	m_fLastTime = fTime;

	return DTRUE;
}


