
#include "../hud.h"
#include "../cl_util.h"
#include "../parsemsg.h"
#include "r_studioint.h"
#include "com_model.h"
#include "const.h"
#include "pm_shared.h"
#include "pmtrace.h"
#include "pm_defs.h"
#include "event_api.h"
#include "fxsys.h"
#include "fxparticlesystem.h"
#include "../bonerec.h"
extern void Draw_WorldSprite( HSPRITE sprite, vec3_t pos, float size, int RenderMode, TRICULLSTYLE tricullstyle,int r,int g, int b, int a, int frame);
extern void VectorAngles( const float *forward, float *angles );
extern vec3_t v_right,v_up,v_forward;
extern vec3_t g_CamOrigin;

extern "C"
{	// HLDM
	bool UTIL_GetWaterSurface( Vector vecStart, Vector vecEnd, float* vecReturn);
}

// Constructors
CFXParticleSystem::CFXParticleSystem()
{	initData();
}

CFXParticleSystem::CFXParticleSystem( CFXParticleEmitter *pEmitter )
{	initData();
	setParticleEmitter(pEmitter);
	pEmitter->setParticleSystem(this);
}

// Init data
void CFXParticleSystem::initData( void )
{	setParticleEmitter(NULL);
	setParticleListHead(NULL);
	setFreeListHead(NULL);
	setEntity(NULL);
	setBoneAccessor(NULL);
	setRelative(FXPARTSYS_RELATIVE_NONE);
	setEmitOriginType(FXPARTSYS_ORIGIN_CENTER);
	setEmitAngleType(FXPARTSYS_ANGLE_NONE);
	setTraceCheck(FXPARTSYS_TRACE_WORLD);
	setParticleCount(0);
	setFreeCount(0);
	setLastTime(0);
	setTimeDelta(0);
	setNoEmitOnNoHitTrace(false);
	setEmit(true);
	setDieOut(false);
	setOrigin(vec3_t(0,0,0));
	setOriginOffset(vec3_t(0,0,0));
	setAngleOffset(vec3_t(0,0,0));
	setEmitOrigin(vec3_t(0,0,0));
	setAngles(vec3_t(0,0,0));
	setTraceDir(vec3_t(0,0,0));
	setNormal(vec3_t(0,0,0));
}

// Destructor
CFXParticleSystem::~CFXParticleSystem()
{	// Delete particles
	deleteParticleList();
	deleteFreeList();
	// Delete particle emitter
	CFXParticleEmitter *pParticleEmitter = getParticleEmitter();
	if(pParticleEmitter)
	{	delete pParticleEmitter;
		setParticleEmitter(NULL);
	}
}

// Parse Data
void CFXParticleSystem::parseData( CFXStorage *pData )
{	// Set origin offset
	setOriginOffset(pData->getFXDataVector("origin_offset"));

	// Set angle offset
	setAngleOffset(pData->getFXDataVector("angle_offset"));

	// Relative type
	std::string strRelativeType = pData->getFXDataString("relative");
	if(strRelativeType == FXPARTICLEPARSE_REL_NONE)
		setRelative(FXPARTSYS_RELATIVE_NONE);
	else if(strRelativeType == FXPARTICLEPARSE_REL_ORG)
		setRelative(FXPARTSYS_RELATIVE_ORIGIN);
	else if(strRelativeType == FXPARTICLEPARSE_REL_ORGANG)
		setRelative(FXPARTSYS_RELATIVE_ORIGIN_ANGLE);

	// Origin type
	std::string strOriginType = pData->getFXDataString("origin_type");
	if(strOriginType == FXPARTICLEPARSE_ORG_CENT)
		setEmitOriginType(FXPARTSYS_ORIGIN_CENTER);
	else if(strOriginType == FXPARTICLEPARSE_ORG_ENT)
		setEmitOriginType(FXPARTSYS_ORIGIN_ENTITY);
	else if(strOriginType == FXPARTICLEPARSE_ORG_BONE)
		setEmitOriginType(FXPARTSYS_ORIGIN_BONE);
	else if(strOriginType == FXPARTICLEPARSE_ORG_TRACE)
		setEmitOriginType(FXPARTSYS_ORIGIN_TRACE);

	// Angle type
	std::string strAngleType = pData->getFXDataString("angle_type");
	if(strAngleType == FXPARTICLEPARSE_ANG_ENT)
		setEmitAngleType(FXPARTSYS_ANGLE_ENTITY);
	else if(strAngleType == FXPARTICLEPARSE_ANG_BONE)
		setEmitAngleType(FXPARTSYS_ANGLE_BONE);
	else if(strAngleType == FXPARTICLEPARSE_ANG_NORM)
		setEmitAngleType(FXPARTSYS_ANGLE_NORMAL);
	else if(strAngleType == FXPARTICLEPARSE_ANG_NONE)
		setEmitAngleType(FXPARTSYS_ANGLE_NONE);

	// Parameters match up, get bone data
	if(getRelative() == FXPARTSYS_RELATIVE_ORIGIN_ANGLE && getEmitAngleType()==FXPARTSYS_ANGLE_BONE)
	{	// get array
		CFXData *pBoneData = pData->getFXData("angle_bone");
		if(pBoneData && (pBoneData->getType() == FXDATA_ARRAY))
		{	// Ensure there are only two entries
			if(pBoneData->getArraySize() == 2)
			{	// Get both entries
				CFXData *pS = pBoneData->getArrayAtIndex(0);
				CFXData *pE = pBoneData->getArrayAtIndex(1);
				// Make sure they're string values, get data
				if((pS->getType() == FXDATA_SINGLE) && (pE->getType() == FXDATA_SINGLE))
				{	getAngleBones()[0] = pS->getStringValue();
					getAngleBones()[1] = pE->getStringValue();
				}
			}
		}
	}

	// origin type
	switch(getEmitOriginType())
	{
		// Origin bone
	case FXPARTSYS_ORIGIN_BONE:
		setOriginBone(pData->getFXDataString("origin_bone"));
		break;
		// Origin bone
	case FXPARTSYS_ORIGIN_TRACE:
		// get trace dir
		vec3_t vecTraceDir = pData->getFXDataVector("trace_dir");
		// get trace length. If it exists, normalize trace dir and use length for magnitude
		std::string strTraceLength = pData->getFXDataString("trace_length");
		if(strTraceLength != "")
		{	VectorNormalize(vecTraceDir);
			vecTraceDir = vecTraceDir * atof(strTraceLength.c_str());
		}
		setTraceDir(vecTraceDir);

		// set trace check
		std::string strTraceCheck = pData->getFXDataString("trace_check");
		if(strTraceCheck == FXPARTICLEPARSE_TRC_WRLD)
		{	setTraceCheck(FXPARTSYS_TRACE_WORLD);
		}
		else if(strTraceCheck == FXPARTICLEPARSE_TRC_WRLDWTR)
		{	setTraceCheck(FXPARTSYS_TRACE_WORLD_WATER);
		}

		// Set if traces should still emit if they dont hit a surface
		std::string strNoEmitNoHit = pData->getFXDataString("trace_nohitnoemit");
		if(strNoEmitNoHit == "true")
		{	setNoEmitOnNoHitTrace(true);
		}
		else
		{	setNoEmitOnNoHitTrace(false);
		}
		break;

	}

	// Parse particle emitter data
	getParticleEmitter()->parseData(pData);
}

// Post create (call after entity/bones are set)
void CFXParticleSystem::postCreate( void )
{	// Entity is specified, fetch bone accessor.
	if(getEntity() && !getBoneAccessor())
	{	setBoneAccessor(gBoneManager.addAccessor(getEntity()->index));
	}
}

// Calculate time delta
void CFXParticleSystem::calculateTimeDelta( void )
{	// First time running
	if(getLastTime() == 0)
		setTimeDelta(0);
	else
		setTimeDelta(gEngfuncs.GetClientTime()-getLastTime());
	
	setLastTime(gEngfuncs.GetClientTime());
}


// Update positional data
bool CFXParticleSystem::updatePositionalData( void )
{	if(updateSystemAngle() && updateSystemOrigin())
	{	return true;
	}
	return false;
}

// Update system angle
bool CFXParticleSystem::updateSystemAngle( void )
{
	vec3_t vecAngle = vec3_t(0,0,0);
	// Update angles
	switch(getEmitAngleType())
	{	// no angle modifications
	default:
	case FXPARTSYS_ANGLE_NONE:
		AngleMatrix(getAngles(), m_rmatrix);
		return true;
		break;
		// Use angle from entity
	case FXPARTSYS_ANGLE_ENTITY:
		if(getEntity())
		{	vecAngle = getEntity()->curstate.angles;
			AngleMatrix(vecAngle,m_rmatrix);
		}
		else
			return false;

		break;

		// Get angle from bone A to bone B
	case FXPARTSYS_ANGLE_BONE:
		if(getBoneAccessor())
		{	// get two bones
			CBoneData *pStart = getBoneAccessor()->getBone(getAngleBones()[0]);
			CBoneData *pEnd = getBoneAccessor()->getBone(getAngleBones()[1]);
			if(pStart && pEnd)
			{	// get direction from bone A to bone B
				vec3_t vecDir = pEnd->getPosition() - pStart->getPosition();
				VectorNormalize(vecDir);
				// Use a quaternion to convert the vector to a rotation matrix
				CQuaternion convertVecToMatrix;
				convertVecToMatrix.VectorToQuaternion(vecDir);
				convertVecToMatrix.QuaternionToMatrix(m_rmatrix);
				vecAngle = convertVecToMatrix.QuaternionToEuler();
			}
		}
		else
		{	return false;
		}
		break;

	case FXPARTSYS_ANGLE_NORMAL:
		return false;
		break;
	}

	// Add offset
	if(getAngleOffset() != vec3_t(0,0,0))
	{	float offset_matrix[3][4];
		AngleMatrix(getAngleOffset(),offset_matrix);
		ConcatTransforms(m_rmatrix,offset_matrix,m_rmatrix);
		vecAngle = vecAngle + getAngleOffset();
	}
	setAngles(vecAngle);

	return true;
}

// Update system origin
bool CFXParticleSystem::updateSystemOrigin( void )
{	// Transform offset position
	vec3_t vecOffset = vec3_t(0,0,0);
	if(getOriginOffset() != vec3_t(0,0,0))
	{	VectorTransform(getOriginOffset(), m_rmatrix, vecOffset);
	}
	
	// Get origin
	vec3_t vecOrigin;
	switch(getEmitOriginType())
	{
	default:
	case FXPARTSYS_ORIGIN_CENTER:
		vecOrigin = getOrigin();

		break;
	case FXPARTSYS_ORIGIN_ENTITY:
		if(getEntity())
		{	vecOrigin = getEntity()->origin;
		}

		break;
	case FXPARTSYS_ORIGIN_BONE:
		if(getBoneAccessor() && getBoneAccessor()->hasValidData())
		{	CBoneData *pBone = getBoneAccessor()->getBone(getOriginBone());
			if(pBone)
			{	vecOrigin = pBone->getPosition();
			}
		}
		else
			return false;
		break;
		// traceline
	case FXPARTSYS_ORIGIN_TRACE:
		if(getTraceDir() == vec3_t(0,0,0))
		{	if(getEntity())
				vecOrigin = getEntity()->curstate.origin;
			else
				vecOrigin = getOrigin();
			break;
		}
		// Get origin
		vec3_t vecTraceStart = getOrigin();
		// If entity exist, use that origin
		if(getEntity())
		{	vecTraceStart = getEntity()->curstate.origin;
		}
		// Make trace dir relative
		vec3_t vecTraceEnd;
		VectorTransform(getTraceDir(), m_rmatrix, vecTraceEnd);
		vecTraceEnd = vecTraceEnd + vecTraceStart;

		// *TRACE
		bool bHit = false;
		pmtrace_t tr;
		gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
		gEngfuncs.pEventAPI->EV_PlayerTrace( vecTraceStart, vecTraceEnd, PM_WORLD_ONLY, -1, &tr );
	
		// Set hit status
		if(tr.fraction < 1.0)
			bHit = true;

		// Get trace type
		switch(getTraceCheck())
		{	// Only hit world
		default:
		case FXPARTSYS_TRACE_WORLD:
			// set origin
			vecOrigin = tr.endpos;
			// set normal
			setNormal(tr.plane.normal);

			break;
			// Hit world and water surfaces
		case FXPARTSYS_TRACE_WORLD_WATER:
			// Set end pos to end of trace result
			vecTraceEnd = vecOrigin = tr.endpos;
			
			// Check for water between points. If water is found, set origin to surface of water
			bool bWaterHit = UTIL_GetWaterSurface( vecTraceStart, vecTraceEnd, vecOrigin);
			// We did come in contact with water, register it as a valid hit
			if(bWaterHit)
				bHit = true;
			// Normal is set at 0,0,1 because we can't obtain surface normals of water surfaces
			setNormal(vec3_t(0,0,1));
			break;
		}

		// Stop emitting if trace doesn't hit anything valid
		if(getNoEmitOnNoHitTrace())
		{	setEmit(bHit);
		}

		break;
	}

	// set final emit origin
	setEmitOrigin(vecOrigin+vecOffset);
	return true;
}

// Think particles
void CFXParticleSystem::thinkParticles( void )
{	CFXParticle *pParticles = getParticleListHead();
	float flTimeDelta = getTimeDelta();
	while(pParticles)
	{	// Particle think. If dead, send to free list
		if(!pParticles->Think(flTimeDelta))
		{	pParticles = freeParticle(pParticles);
			continue;
		}
		pParticles = pParticles->m_pNext;
	}
}

// Think
void CFXParticleSystem::Think( void )
{	// Calculate time delta
	calculateTimeDelta();

	// Update system origin and angles
	if(updatePositionalData())
	{	// Emit particles only if we're not dying out
		if(!getDieOut())
		{	getParticleEmitter()->emitParticles();
		}

		// Think
		thinkParticles();
	}
}

// Finalize Position
vec3_t CFXParticleSystem::finalizePosition( const vec3_t &vecOrg )
{	switch(getRelative())
	{
	default:
	case FXPARTSYS_RELATIVE_NONE:
		return vecOrg;
	case FXPARTSYS_RELATIVE_ORIGIN:
		return (getEmitOrigin() + vecOrg);
	case FXPARTSYS_RELATIVE_ORIGIN_ANGLE:
		// Transform position
		vec3_t vecFinal;
		VectorTransform(vecOrg, m_rmatrix, vecFinal);
		return (getEmitOrigin() + vecFinal);
	}
	return vec3_t(0,0,0);
}

// Render
void CFXParticleSystem::Render( void )
{		
	CFXParticle *pParticles = getParticleListHead();
	CFXParticleEmitter *pEmitter = getParticleEmitter();

	switch(pEmitter->getDrawStyle())
	{
	case FXDRAWSTYLE_NORMAL:
		{	while(pParticles)
			{	float *RGBA = pParticles->getRGBA();
				Draw_WorldSprite( pParticles->getSprite(), finalizePosition(pParticles->getOrigin()), pParticles->getSize(), pEmitter->getRenderType(), pEmitter->getCulling(), RGBA[0], RGBA[1], RGBA[2], RGBA[3], 0);
				pParticles = pParticles->m_pNext;
			}
			break;
		}
	case FXDRAWSTYLE_ELONGATE:
		{	
			vec3_t temporg,start,end;
			float size;

			while(pParticles)
			{	float *RGBA = pParticles->getRGBA();

				// Set sprite
				if ( !gEngfuncs.pTriAPI->SpriteTexture( (struct model_s *)gEngfuncs.GetSpritePointer( pParticles->getSprite() ), 0 ))
				{	pParticles = pParticles->m_pNext;
					continue;
				}
				start = pParticles->getOrigin();
				float flDist = pParticles->getVelocity().Length();
				if(flDist > 100)
					flDist = 100;
				end = pParticles->getOrigin() - (pParticles->getVelocity().Normalize() * (flDist/100.0))*8;
				
				size = pParticles->getSize() * 0.5;

				
				vec3_t W = start-g_CamOrigin;
				vec3_t U = end-g_CamOrigin;
				double wx=DotProduct(W,v_right);
				double wy=DotProduct(W,v_up);
				double ux=DotProduct(U,v_right);
				double uy=DotProduct(U,v_up);
				
				Vector2D onPlane = Vector2D(ux-wx,uy-wy);
				
				//if(onPlane.Length()<0.5)
				//{
				//	return vec3_t(0,0,0);
				//}
				
				Vector2D Q=Vector2D(uy-wy,wx-ux);
				Vector2D q=Q.Normalize();
				vec3_t vecUp = q.x*v_right+q.y*v_up;
				vecUp=vecUp.Normalize();

				gEngfuncs.pTriAPI->RenderMode( pEmitter->getRenderType() );
				gEngfuncs.pTriAPI->CullFace( pEmitter->getCulling() );
				gEngfuncs.pTriAPI->Begin( TRI_QUADS );
				gEngfuncs.pTriAPI->Color4ub(RGBA[0],RGBA[1],RGBA[2],RGBA[3]);

				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 0, 0 );
				temporg = end + (vecUp * size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );
				
				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 0, 1 );	
				temporg = start + (vecUp * size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );

				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 1, 1 );	
				temporg = start - (vecUp * size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );
				
				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 1, 0 );
				temporg = end - (vecUp * size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );

				/*
				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 0, 0 );
				temporg = start + (v_right * size) + (v_up * size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );
				
				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 0, 1 );	
				temporg = start + (v_right * size) + (v_up * -size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );

				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 1, 1 );	
				temporg = start + (v_right * -size) + (v_up * -size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );
				
				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 1, 0 );
				temporg = start + (v_right * -size) + (v_up * size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );

				
				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 0, 0 );
				temporg = end + (v_right * size) + (v_up * size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );
				
				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 0, 1 );	
				temporg = end + (v_right * size) + (v_up * -size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );

				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 1, 1 );	
				temporg = end + (v_right * -size) + (v_up * -size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );
				
				gEngfuncs.pTriAPI->Brightness( 1 );
				gEngfuncs.pTriAPI->TexCoord2f( 1, 0 );
				temporg = end + (v_right * -size) + (v_up * size);
				gEngfuncs.pTriAPI->Vertex3f( temporg.x, temporg.y, temporg.z );*/
					
				gEngfuncs.pTriAPI->End();	
				gEngfuncs.pTriAPI->RenderMode( kRenderNormal );


				pParticles = pParticles->m_pNext;
			}



			break;
		}
	}
}

/*
* Linked list functionality
*/
// Add to particle list
CFXParticle *CFXParticleSystem::addToParticleList( CFXParticle *pParticle )
{	if(!pParticle)
		return NULL;
	// No head, start list
	if(!getParticleListHead())
	{	pParticle->m_pNext = NULL;
		pParticle->m_pPrev = NULL;
		setParticleListHead(pParticle);
	}
	// Push infront of head particle
	else
	{	getParticleListHead()->m_pPrev = pParticle;
		pParticle->m_pNext = getParticleListHead();
		pParticle->m_pPrev = NULL;
		setParticleListHead(pParticle);
	}
	// reset particle data and increase count
	pParticle->Reset();
	incParticleCount();
	return pParticle;
}

// Add to free list
CFXParticle *CFXParticleSystem::addToFreeList( CFXParticle *pParticle )
{	if(!pParticle)
		return NULL;
	// No head, start list
	if(!getFreeListHead())
	{	pParticle->m_pNext = NULL;
		pParticle->m_pPrev = NULL;
		setFreeListHead(pParticle);
	}
	// Push infront of head particle
	else
	{	getFreeListHead()->m_pPrev = pParticle;
		pParticle->m_pNext = getFreeListHead();
		pParticle->m_pPrev = NULL;
		setFreeListHead(pParticle);
	}
	// increase count
	incFreeCount();
	return pParticle;
}

// Remove from Particle List
CFXParticle *CFXParticleSystem::removeFromParticleList( CFXParticle *pRemove )
{	// Particle is head of the list, just take it out
	if(pRemove == getParticleListHead())
	{	setParticleListHead(pRemove->m_pNext);
		if(getParticleListHead())
			getParticleListHead()->m_pPrev = NULL;
	}
	// Particle is somewhere in the middle/end, take it out and fill in space
	else
	{	if(pRemove->m_pPrev)
			pRemove->m_pPrev->m_pNext = pRemove->m_pNext;
		if(pRemove->m_pNext)
			pRemove->m_pNext->m_pPrev = pRemove->m_pPrev;
	}
	pRemove->m_pNext = NULL;
	pRemove->m_pPrev = NULL;
	decParticleCount();
	return pRemove;
}

// Remove from Free List
CFXParticle *CFXParticleSystem::removeFromFreeList( CFXParticle *pRemove )
{	// Particle is head of the list, just take it out
	if(pRemove == getFreeListHead())
	{	setFreeListHead(pRemove->m_pNext);
		if(getFreeListHead())
		{	getFreeListHead()->m_pPrev = NULL;
		}
	}
	// Particle is somewhere in the middle/end, take it out and fill in space
	else
	{	if(pRemove->m_pPrev)
			pRemove->m_pPrev->m_pNext = pRemove->m_pNext;
		if(pRemove->m_pNext)
			pRemove->m_pNext->m_pPrev = pRemove->m_pPrev;
	}
	pRemove->m_pNext = NULL;
	pRemove->m_pPrev = NULL;
	decFreeCount();
	return pRemove;
}

// Delete free particles
void CFXParticleSystem::deleteFreeList( void )
{	CFXParticle *pParticle = getFreeListHead();
	CFXParticle *pRemove = NULL;
	while(pParticle)
	{	pRemove = pParticle;
		pParticle = pParticle->m_pNext;
		delete pRemove;
		pRemove=NULL;
	}
	setFreeListHead(NULL);
	setFreeCount(0);
}

// Delete particle list
void CFXParticleSystem::deleteParticleList( void )
{	CFXParticle *pParticle = getParticleListHead();
	CFXParticle *pRemove = NULL;
	while(pParticle)
	{	pRemove = pParticle;
		pParticle = pParticle->m_pNext;
		delete pRemove;
		pRemove=NULL;
	}
	setParticleListHead(NULL);
	setParticleCount(0);
}

// Free particle (transfer from particle to free list)
CFXParticle *CFXParticleSystem::freeParticle( CFXParticle *pFree )
{	CFXParticle *pNext = pFree->m_pNext;
	addToFreeList( removeFromParticleList( pFree ) );
	return pNext;
}

// Get particle from free list
CFXParticle *CFXParticleSystem::getFreeParticle( void )
{	CFXParticle *pHead = getFreeListHead();
	// Nothing to get
	if(!pHead)
	{	return NULL;
	}

	// remove from free list
	removeFromFreeList(pHead);
	return pHead;
}

// Increase free particle list
void CFXParticleSystem::increaseFreeParticles( int iIncrease )
{	if(iIncrease <= 0)
		return;

	for(int i = 0; i < iIncrease; i++)
		addToFreeList(getParticleEmitter()->getNewParticle());
}

// Get new particle
CFXParticle *CFXParticleSystem::getNewParticle( void )
{	// get new free particles first
	if(getFreeCount() <= 0)
	{	// brand new particle system, allocate the start amount of particles
		if(getParticleCount() <= 0)
		{	increaseFreeParticles(FXPARTSYS_STARTPARTICLES);
		}
		// Allocate the amount of regular particles (doubling our total amount)
		else
		{	if(getParticleCount() < FXPARTSYS_DOUBLELIM)
				increaseFreeParticles(getParticleCount());
			else
				increaseFreeParticles(getParticleCount()*FXPARTSYS_OVERDOUBLE_PERINC);
		}
	}
	return addToParticleList(getFreeParticle());
}