/**
* Transformation FX
* @version: 9-19-2002
* @author Joseph 'Pcjoe' Florencio
*/

#include "hud.h"
#include "parsemsg.h"
#include "const.h"
#include "cl_util.h"
#include "r_efx.h"
#include "pm_defs.h"
#include "pmtrace.h"
#include "event_api.h"
#include "transformfx.h"
#include "r_studioint.h"
// Added by Pcjoe, needed for ParticleTrace function to see if it hits water
extern "C"
{	// HLDM
	bool UTIL_GetWaterSurface( Vector vecStart, Vector vecEnd, float* vecReturn);
}
#define ENG_PM_POINT_CONTENTS gEngfuncs.PM_PointContents
extern HSPRITE g_hsprLighting;
extern cvar_t *cl_fxquality;
extern engine_studio_api_t IEngineStudio;
#define PI 3.1415926535897932384626433832795

part_manager_s *pPartJobs;

// TraceLines
enum trace_dir
{
	DOWN,
	UP,
	BACK,
	FRONT,
};

// --------------
//  Start of GFX
// --------------

// Collision detection
int CParticleFx::CollisionCheck( vec3_t vecOrigin, vec3_t vecDirection, float flSize )
{	// Setting everything up...
	VectorNormalize(vecDirection);
	vec3_t v_forward, v_right, v_up;
	AngleVectors( vecDirection, v_forward, v_right, v_up);
	// Chop size in 1/2, adds up to the same size after we do everything
	flSize /= 2;
	// Forming a Square infront of the particle for precise collision detection

	vec3_t vecCollision1 = vecOrigin + (v_right * flSize) + (v_up * flSize);
	vec3_t vecCollision2 = vecOrigin + (v_right * flSize) + (v_up * -flSize);
	vec3_t vecCollision3 = vecOrigin + (v_right * -flSize) + (v_up * -flSize);
	vec3_t vecCollision4 = vecOrigin + (v_right * -flSize) + (v_up * flSize);

	// Content ints
	int content1 = ENG_PM_POINT_CONTENTS(vecCollision1,NULL);
	int content2 = ENG_PM_POINT_CONTENTS(vecCollision2,NULL);
	int content3 = ENG_PM_POINT_CONTENTS(vecCollision3,NULL);
	int content4 = ENG_PM_POINT_CONTENTS(vecCollision4,NULL);
	// If it hit something, return true
	if((content1 == CONTENTS_SOLID || content2 == CONTENTS_SOLID || content3 == CONTENTS_SOLID || content4 == CONTENTS_SOLID)
		|| (content1 == CONTENTS_WATER || content2 == CONTENTS_WATER || content3 == CONTENTS_WATER || content4 == CONTENTS_WATER))
	{	return true;
	}
	else
	{	return false;
	}
}

vec3_t CParticleFx::ParticleTrace(vec3_t origin, bool &blwater, int direction)
{
	vec3_t vecDirTrace;
	vec3_t endpos;

	// Angle Vectors
	vec3_t v_up, v_right, v_forward;
	vec3_t vecViewAngles;
	// Only calculate the angle vectors if were going to be using front or back (only ones that need them)
	if(direction == FRONT || direction == BACK)
	{	gEngfuncs.GetViewAngles( (float *)vecViewAngles);
		AngleVectors( vecViewAngles, v_forward, v_right, v_up );
	}

	// Set to 0 for the up and down trace so it just has to set the z direction, overwritten by the front + back trace
	vecDirTrace.x = 0;
	vecDirTrace.y = 0;

	// What direction shall we send the traceline in? Down, up, front, or back?
	if(direction == DOWN)
	{	vecDirTrace.z = -1;
	}
	else if(direction == UP)
	{	vecDirTrace.z = 1;
	}
	else if(direction == FRONT)
	{	VectorCopy(v_forward, vecDirTrace);
	}
	else if(direction == BACK)
	{	VectorCopy(v_forward * -1, vecDirTrace);
	}

	VectorNormalize(vecDirTrace);

	// Using some insainly high value to make the traceline go... far
	vec3_t veltraceOrigin = origin + 8192 * vecDirTrace;

	pmtrace_t trOrigin;
	trOrigin = *(gEngfuncs.PM_TraceLine((float *)&origin, (float *)&veltraceOrigin , PM_TRACELINE_PHYSENTSONLY ,2, -1));
	vec3_t endpos_water;

	// If water is present, use the surface as the origin. If not, just use the trace line result
	if(UTIL_GetWaterSurface(origin, trOrigin.endpos, endpos_water))
	{	endpos = endpos_water;
		blwater = true;
	}
	else
	{	endpos = trOrigin.endpos;
		blwater = false;
	}

	// Return position of trace
	return endpos;
}

// ---------
//  Init FX
// ---------

part_manager_s *CParticleFx::CopyInfo( trans_event *pEvent )
{
	part_manager_s *pPartManager = new part_manager_s;
	if(!pPartManager)
	{
		ConsolePrint("Error, could not allocate space!\n");
		return NULL;
	}

	pPartManager->flTimeEnd = pEvent->flTimeEnd;
	pPartManager->flTimeStart = pEvent->flTimeStart;
	pPartManager->blDisplay = false;
	pPartManager->m_iFlag = pEvent->iFlag;
	pPartManager->m_iIndex = pEvent->iIndex;
	pPartManager->m_iCharacter = -1;
	pPartManager->flDieTime = -1;
	pPartManager->pNext = NULL;
	pPartManager->pParticle = NULL;
	pPartManager->pPartSys = NULL;

	return pPartManager;
}

void CParticleFx::InitializeSystem( int iNumParticles, part_manager_s *pPartManager )
{
//	ConsolePrint("Init System\n");
	// Player Pointer
	cl_entity_t *pClient = gEngfuncs.GetEntityByIndex(pPartManager->m_iIndex);

	// Setting the 2 particle structs to NULL
	pPartManager->pParticle = NULL;
	pPartManager->pPartSys = NULL;

	// General Initialization
	switch( pPartManager->m_iFlag )
	{
	case FLOATING_ROCKS:
	case DUSTTRAIL:
	case DUST:
	case COLUMN_DUST:
	case EXPANDED_DUST:
	case ROTATING_DUST:
	case STEAM:
	case STEAM_HEAD:
	case LIGHTING:
	case SPIRIT_BOMB:
		{
			partsys_s *pPartSys = new partsys_s;
			pPartManager->flEmitTime = 0;
			pPartSys->iMaxParticles = iNumParticles;
			VectorCopy( vec3_t(0, 0, 0), pPartSys->vecForce );
			VectorCopy( pClient->origin, pPartSys->vecOrigin );
			pPartSys->flFrameDelta = -1;
			pPartSys->iMaxFrames = 0;
			pPartSys->blvecOriginPlayer = true;
			// Init Particles
			particle_fx *pParticle = new particle_fx[iNumParticles];
			for( int i = 0; i < iNumParticles; i++ )
			{	pParticle[i].flEnergy = 0;
				pParticle[i].flFrameTime = 0;
			}

			pPartSys->pParticle = pParticle;
			pPartManager->pPartSys = pPartSys;
			break;
		}
	case ROTATE_LGHTING_SPHERE:
	case PURPLE_LGHTING_SPHERE:
	case PINK_SOLID_SPHERE:
	case FLASHY_AURA:
	case FLASHY_WHITE_AURA:
	case WHITE_SPHERE:
	case EXPLOSION_SPHERE:
	case SHOCKWAVE_FROM_CORE:
	case SHOCKWAVE_FROM_CORE_INVERSE:
	case SHOCKWAVE_CORE:
	case SHOCKWAVE_CORE_INVERSE:
	case SWOOP_DUSTTRAIL:
	case HARD_INNDER_SPHERE:
		{	
			particle_fx *pParticle = new particle_fx;
			// Unlimited Energy
			pParticle->flEnergy = -1;
			pPartManager->flEmitDelta = -1;
			pPartManager->flEmitTime = 0;
			// Reset colors
			for(int i = 0; i < 4; i++)
				pParticle->iColor[i] = 0;
			pParticle->flEnergyDelta = 0;
			pParticle->flSizeDelta = 0;
			pParticle->flSpeed = 0;
			pParticle->flWeight = 0;
			pParticle->iFrame = 0;
			pParticle->iSprite = 0;
			pParticle->flFrameDelta = -1;
			pParticle->iMaxFrames = 0;
			pParticle->flRotationSpeed = 0;
			pParticle->flSize = 0;
			pParticle->flFrameTime = 0;
			pParticle->iBounce = 0;
			pParticle->blCollision = false;
			VectorCopy( pClient->origin, pParticle->vecOrigin );
			VectorCopy( vec3_t(0, 0, 0), pParticle->vecOrigin2 );
			VectorCopy( vec3_t(0, 0, 0), pParticle->vecVelocity );
			VectorCopy( vec3_t(0,gEngfuncs.pfnRandomLong(0, 360),0), pParticle->vecRotation );
			VectorCopy( vec3_t(0,gEngfuncs.pfnRandomLong(-360, 0),0), pParticle->vecRotation2 );
			pPartManager->pParticle = pParticle;
			break;
		}

	}

	// Shorten up code a bit
	particle_fx *pParticle = pPartManager->pParticle;

	// Specific Initialization for Particles
	switch( pPartManager->m_iFlag )
	{
	case ROTATE_LGHTING_SPHERE:
		{
			pParticle->flSize = 27;
			pParticle->iColor[0] = 50;
			pParticle->iColor[1] = 50;
			pParticle->iColor[2] = 50;
			pParticle->iColor[3] = 50;
			pParticle->flRotationSpeed = 150;
			break;
		}
	case PURPLE_LGHTING_SPHERE:
		{
			pParticle->flSize = 15;
			pParticle->iColor[0] = 226;
			pParticle->iColor[1] = 9;
			pParticle->iColor[2] = 218;
			pParticle->iColor[3] = 60;
			pParticle->flRotationSpeed = 150;
			break;
		}
	case PINK_SOLID_SPHERE:
		{
			pParticle->flSize = 25;
			pParticle->iColor[0] = 225;
			pParticle->iColor[1] = 103;
			pParticle->iColor[2] = 235;
			pParticle->iColor[3] = 255;
			pParticle->flRotationSpeed = 100;
		}
	case FLASHY_AURA:
		{
			pParticle->flSize = 15;
			pParticle->iColor[0] = 12.5;
			pParticle->iColor[1] = 12.5;
			pParticle->iColor[2] = 12.5;
			pParticle->iColor[3] = 12.5;
			pParticle->flRotationSpeed = 150;
			break;
		}
	case FLASHY_WHITE_AURA:
		{
			pParticle->flSize = 15;
			pParticle->iColor[0] = 12.5;
			pParticle->iColor[1] = 12.5;
			pParticle->iColor[2] = 12.5;
			pParticle->iColor[3] = 12.5;
			pParticle->flRotationSpeed = 150;
			break;
		}
	case WHITE_SPHERE:
		{	
			pParticle->flSize = 32;
			pParticle->iColor[0] = 40;
			pParticle->iColor[1] = 40;
			pParticle->iColor[2] = 40;
			pParticle->iColor[3] = 40;
			pParticle->flRotationSpeed = 150;
			break;
		}
	case EXPLOSION_SPHERE:
		{
			pParticle->flEnergy = 1.2;
			pParticle->flEnergyDelta = 1.8;
			pParticle->flSize = 5;
			pParticle->flSizeDelta = 220;
			pParticle->iColor[0] = 80;
			pParticle->iColor[1] = 80;
			pParticle->iColor[2] = 80;
			pParticle->iColor[3] = 80;
			pParticle->flRotationSpeed = 40;
			break;
		}
	case SWOOP_DUSTTRAIL:
		{
			// Set to 0 here, controlled in the accual emitting function
			pPartManager->flEmitDelta = 0;

			// None of this matters, this is only used to emit particles
			pParticle->flSize = 0;	
			pParticle->iColor[0] = 0;
			pParticle->iColor[1] = 0;
			pParticle->iColor[2] = 0;
			pParticle->iColor[3] = 0;
			break;
		}
	case SHOCKWAVE_CORE:
		{
			pPartManager->flEmitDelta = 0.15;
			pParticle->flSize = 10;
			switch(pPartManager->m_iCharacter)
			{
			case CLASS_CELL:
				{
					pParticle->iColor[0] = 0;
					pParticle->iColor[1] = 255;
					pParticle->iColor[2] = 0;
					pParticle->iColor[3] = 255;
					break;
				}
			case CLASS_FRIEZA:
				{
					pParticle->iColor[0] = 255;
					pParticle->iColor[1] = 0;
					pParticle->iColor[2] = 255;
					pParticle->iColor[3] = 255;
					break;
				}
			case CLASS_PICCOLO:
				{
					pParticle->iColor[0] = 140;
					pParticle->iColor[1] = 140;
					pParticle->iColor[2] = 140;
					pParticle->iColor[3] = 140;
					break;
				}
			default:
				{
					pParticle->iColor[0] = 255;
					pParticle->iColor[1] = 255;
					pParticle->iColor[2] = 255;
					pParticle->iColor[3] = 255;
					break;
				}
			}
			pParticle->flRotationSpeed = 90;
			break;
		}
	case SHOCKWAVE_CORE_INVERSE:
		{
			// this wont even be visible, its just used for emitting
			pPartManager->flEmitDelta = 0.15;
			pParticle->flSize = 1;
			pParticle->iColor[0] = 0;
			pParticle->iColor[1] = 0;
			pParticle->iColor[2] = 0;
			pParticle->iColor[3] = 0;
			break;
		}
	case SHOCKWAVE_FROM_CORE:
	case SHOCKWAVE_FROM_CORE_INVERSE:
		{
			if(pPartManager->m_iFlag == SHOCKWAVE_FROM_CORE_INVERSE)
			{
			//	pParticle->flEnergy = 1.0;
				pParticle->flEnergy = 0.01;
			//	pParticle->flEnergyDelta = 1.25;
				pParticle->flEnergyDelta = -1.1;
				pParticle->flSize = 25;
				pParticle->flSizeDelta = -45;
			}
			else
			{
				pParticle->flEnergy = 1.0;
				pParticle->flEnergyDelta = 2.95;
				pParticle->flSize = 10;
				pParticle->flSizeDelta = 50;
			}

			// Lets color this according to character
			switch(pPartManager->m_iCharacter)
			{
			case CLASS_CELL:
				{
					pParticle->iColor[0] = 0;
					pParticle->iColor[1] = 80;
					pParticle->iColor[2] = 0;
					pParticle->iColor[3] = 80;
					break;
				}
			case CLASS_FRIEZA:
				{
					pParticle->iColor[0] = 80;
					pParticle->iColor[1] = 0;
					pParticle->iColor[2] = 80;
					pParticle->iColor[3] = 80;
					break;
				}
			default:
				{
					pParticle->iColor[0] = 80;
					pParticle->iColor[1] = 80;
					pParticle->iColor[2] = 80;
					pParticle->iColor[3] = 80;
					break;
				}
			}
			pParticle->flRotationSpeed = 60;
			break;
		}

	case HARD_INNDER_SPHERE:
		{
			pParticle->flSize = 25;
			pParticle->iColor[0] = 0;
			pParticle->iColor[1] = 255;
			pParticle->iColor[2] = 0;
			pParticle->iColor[3] = 255;
			pParticle->flRotationSpeed = -50;
			break;
		}
	case FLOATING_ROCKS:
		{	
			pPartManager->flEmitDelta = 0.1;
			break;
		}
	case DUST:
		{	
			pPartManager->flEmitDelta = 0.2;
			break;
		}
	case COLUMN_DUST:
		{
			pPartManager->flEmitDelta = 0.2;
			break;
		}
	case ROTATING_DUST:
		{
			pPartManager->flEmitDelta = 0.35;
			break;
		}
	case EXPANDED_DUST:
		{
			pPartManager->flEmitDelta = 0.1;
			break;
		}
	case DUSTTRAIL:
		{	// Vector angles
			cl_entity_t *pClient = gEngfuncs.GetEntityByIndex(pPartManager->m_iIndex);
			pPartManager->pPartSys->vecForce = pClient->angles;
			
			if(iQuality == LOW_QUALITY_FX)
				pPartManager->flEmitDelta = 0.2;
			else
				pPartManager->flEmitDelta = 0.1;

			break;
		}
	case STEAM:
		{	
			pPartManager->flEmitDelta = 0.275;
			break;
		}
	case STEAM_HEAD:
		{
			pPartManager->flEmitDelta = 0.025;
			break;
		}
	case LIGHTING:
		{	
			pPartManager->flEmitDelta = 0.65;
			pPartManager->pPartSys->flFrameDelta = 0.1;
			pPartManager->pPartSys->iMaxFrames = SPR_Frames(g_hsprLighting);
			break;
		}
	case SPIRIT_BOMB:
		{
			pPartManager->flEmitDelta = 0.01;
			break;
		}
	}
}

void CParticleFx::InitializeSystem( int iNumParticles, vec3_t vecOrigin, part_manager_s *pPartManager )
{
	// Do the regular Initialize system
	InitializeSystem(iNumParticles, pPartManager );

	// Set the origin
	if(pPartManager->pPartSys)
	{
		VectorCopy( vecOrigin, pPartManager->pPartSys->vecOrigin );
		pPartManager->pPartSys->blvecOriginPlayer = false;
	}
}


void CParticleFx::CreateParticleSystem( int iNumParticles, trans_event *pEvent )
{	// Dont allow software mode to run this
	if(!IEngineStudio.IsHardware())
		return;

	// Filling particle job...
	part_manager_s *pPartManager = CopyInfo( pEvent );

	// Initializing system
	InitializeSystem( iNumParticles, pPartManager );

	// Lets add it on the list
	// Is the first spot taken? If so, search for an empty slot
	if(!pPartJobs)
	{	pPartJobs = pPartManager;
	}
	else
	{
		// Add the job to the end
		part_manager_s *pCurrent = pPartJobs;

		while(pCurrent->pNext)
		{	pCurrent = pCurrent->pNext;
		}
		pCurrent->pNext = pPartManager;
	}

//	ConsolePrint("Created Particle System\n");
}

void CParticleFx::CreateParticleSystem( int iNumParticles, int iCharacter, trans_event *pEvent )
{	// Dont allow software mode to run this
	if(!IEngineStudio.IsHardware())
		return;

	// Filling particle job...
	part_manager_s *pPartManager = CopyInfo( pEvent );
	if(!pPartManager)
	{
		ConsolePrint("Error, could not allocate space!\n");
		return;
	}
	// Set character
	pPartManager->m_iCharacter = iCharacter;

	// Initializing system
	InitializeSystem( iNumParticles, pPartManager);

	// Lets add it on the list
	// Is the first spot taken? If so, search for an empty slot
	
	if(!pPartJobs)
	{	pPartJobs = pPartManager;
	}
	else
	{
		// Add the job to the end
		part_manager_s *pCurrent = pPartJobs;

		while(pCurrent->pNext)
		{	pCurrent = pCurrent->pNext;
		}
		pCurrent->pNext = pPartManager;
	}
}

void CParticleFx::CreateParticleSystem( int iNumParticles, vec3_t vecOrigin, trans_event *pEvent )
{	// Dont allow software mode to run this
	if(!IEngineStudio.IsHardware())
		return;

	// Filling particle job...
	part_manager_s *pPartManager = CopyInfo( pEvent );

	if(!pPartManager)
	{
		ConsolePrint("Error, could not allocate space!\n");
		return;
	}

	// Initializing system
	InitializeSystem( iNumParticles, vecOrigin, pPartManager );

	// Lets add it on the list
	// Is the first spot taken? If so, search for an empty slot
	
	if(!pPartJobs)
	{	pPartJobs = pPartManager;
	}
	else
	{
		// Add the job to the end
		part_manager_s *pCurrent = pPartJobs;

		while(pCurrent->pNext)
		{	pCurrent = pCurrent->pNext;
		}
		pCurrent->pNext = pPartManager;
	}
//	ConsolePrint("Created Particle System\n");

}


// ---------------
//  Kill Trans FX
// ---------------

void CParticleFx::KillSystem( float flDieTime, part_manager_s *pPartManager )
{
	if(pPartManager->pPartSys)
	{
		// Lets "Kill" the particles!
		switch(pPartManager->m_iFlag)
		{
			// Add extra life for rocks
		case FLOATING_ROCKS:
			{
				// Add Extra die time just for rocks
				pPartManager->flDieTime += 3;

				// Give all of the current rocks new life, so you can see them reverse direction + drop down
				for(int i = 0; i < pPartManager->pPartSys->iMaxParticles; i++)
				{	if(pPartManager->pPartSys->pParticle[i].flEnergy)
					{	pPartManager->pPartSys->pParticle[i].flEnergy = gEngfuncs.pfnRandomFloat(3.5, 4.5);
					}
				}
				break;
			}
		case SPIRIT_BOMB:
			{
				// Enable the energy delta
				for(int i = 0; i < pPartManager->pPartSys->iMaxParticles; i++)
				{	if(pPartManager->pPartSys->pParticle[i].flEnergy)
					{	
						pPartManager->pPartSys->pParticle[i].flEnergyDelta = gEngfuncs.pfnRandomFloat(2.75,3.25);
						pPartManager->pPartSys->pParticle[i].flSpeed /= 2.25;
					}
				}
				break;
			}
		case ROTATING_DUST:
			{
				// Enable the energy delta
				for(int i = 0; i < pPartManager->pPartSys->iMaxParticles; i++)
				{	
					pPartManager->pPartSys->pParticle[i].flEnergyDelta = gEngfuncs.pfnRandomFloat(8,10);
				}
				break;
			}
			
		case STEAM:
			{
				cl_entity_t *pClient = gEngfuncs.GetEntityByIndex(pPartManager->m_iIndex);
				// Shoot all particles outwards
				for(int i = 0; i < pPartManager->pPartSys->iMaxParticles; i++)
				{
					// Only push particles outwards if the transformation was completed
					if(pPartManager->flTimeEnd <= flTime)
					{	vec3_t vecDir = pClient->origin - pPartManager->pPartSys->pParticle[i].vecOrigin;
						VectorNormalize(vecDir);
						pPartManager->pPartSys->pParticle[i].vecVelocity = vecDir;
						pPartManager->pPartSys->pParticle[i].flSpeed = 325;
						pPartManager->pPartSys->pParticle[i].flEnergyDelta = 1.2;
						pPartManager->pPartSys->pParticle[i].flSizeDelta = 15;
					}
					else
					{	pPartManager->pPartSys->pParticle[i].flEnergyDelta = 2.0;
					}


				}
				break;
			}
		}

	}

	// Add die time
	pPartManager->flDieTime = flDieTime;
}

// ------------------
//  Create Particles
// ------------------

void CParticleFx::CreateRocks( int iNum, partsys_s *pPartSys, vec3_t vecOrigin )
{
	// Entering in general data
	pPartSys->pParticle[iNum].flEnergy = gEngfuncs.pfnRandomFloat(0.6, 1.1);
	pPartSys->pParticle[iNum].flEnergyDelta = gEngfuncs.pfnRandomFloat(0.5, 0.8);
	pPartSys->pParticle[iNum].flSize = gEngfuncs.pfnRandomLong(1, 3);
	pPartSys->pParticle[iNum].flSizeDelta = 0;
	pPartSys->pParticle[iNum].flSpeed = 170;
	pPartSys->pParticle[iNum].flWeight = 0.5;
	pPartSys->pParticle[iNum].iColor[0] = 167;
	pPartSys->pParticle[iNum].iColor[1] = 149;
	pPartSys->pParticle[iNum].iColor[2] = 145;
	pPartSys->pParticle[iNum].iColor[3] = 255;
	pPartSys->pParticle[iNum].iFrame = 0;
	pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(0, 5);
	pPartSys->pParticle[iNum].blCollision = false;
	pPartSys->pParticle[iNum].iBounce = 0;
	pPartSys->pParticle[iNum].flRotationSpeed = 0;

	// entering vector information
	pPartSys->pParticle[iNum].vecRotation = pPartSys->pParticle[iNum].vecRotation2 = vec3_t(
		gEngfuncs.pfnRandomLong(0, 360),gEngfuncs.pfnRandomLong(0, 360),gEngfuncs.pfnRandomLong(0, 360));

	// Give it a velocity...
	pPartSys->pParticle[iNum].vecVelocity = vec3_t(gEngfuncs.pfnRandomLong(-4, 4),gEngfuncs.pfnRandomLong(-4, 4), 20);

	// Set origin
	vec3_t vecEndPos = vecOrigin + vec3_t(gEngfuncs.pfnRandomLong(-250, 250),gEngfuncs.pfnRandomLong(-250, 250),0);

	// If its in the ground, move it out 6 times max
	for( int iCount = 0; iCount == 6; iCount++)
	{	
		int contentParticle = ENG_PM_POINT_CONTENTS(vecEndPos ,NULL);
		if(contentParticle == CONTENT_SOLID)
		{
			vecEndPos = vecEndPos + vec3_t(0,0,1) * 2;
		}
		else
		{	break;
		}
		if( iCount == 6 )
		{
			pPartSys->pParticle[iNum].flEnergy = 0;
			break;
		}
	}
	
	VectorCopy( vecEndPos, pPartSys->pParticle[iNum].vecOrigin );
}

void CParticleFx::CreateDust( int iNum, partsys_s *pPartSys, bool blWater, vec3_t vecOrigin, float flFarOut )
{
	// Entering in general data
	pPartSys->pParticle[iNum].flEnergy = 1.6;
	pPartSys->pParticle[iNum].flEnergyDelta = 1.5;
	pPartSys->pParticle[iNum].flSize = 12;
	pPartSys->pParticle[iNum].flSizeDelta = 30;
	pPartSys->pParticle[iNum].flSpeed = 115;
	pPartSys->pParticle[iNum].flWeight = 0;
	if(blWater)
	{
		pPartSys->pParticle[iNum].iColor[0] = 0;
		pPartSys->pParticle[iNum].iColor[1] = 61;
		pPartSys->pParticle[iNum].iColor[2] = 173;
		pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(1, 2);
	}
	else
	{
		pPartSys->pParticle[iNum].iColor[0] = 108;
		pPartSys->pParticle[iNum].iColor[1] = 78;
		pPartSys->pParticle[iNum].iColor[2] = 0;
		pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(-1, 0);
	}
	pPartSys->pParticle[iNum].iColor[3] = 255;
	pPartSys->pParticle[iNum].iFrame = 0;
	pPartSys->pParticle[iNum].blCollision = false;
	pPartSys->pParticle[iNum].iBounce = 0;
	pPartSys->pParticle[iNum].flRotationSpeed = 0;

	// entering vector information
	pPartSys->pParticle[iNum].vecRotation = pPartSys->pParticle[iNum].vecRotation2 = vec3_t(0,0,0);
	vec3_t vecDir;
	vecDir.x = gEngfuncs.pfnRandomFloat(-10, 10);
	vecDir.y = gEngfuncs.pfnRandomFloat(-10, 10);
	vecDir.z = 1.015;
	VectorCopy( vecDir, pPartSys->pParticle[iNum].vecVelocity );
	vecOrigin.z += pPartSys->pParticle[iNum].flSize/4;
	pPartSys->pParticle[iNum].vecOrigin = vecOrigin + vecDir.Normalize() * flFarOut;
	// Test origin...
	// Dont create particle if its about to hit something
	vec3_t vecTestCollision = pPartSys->pParticle[iNum].vecOrigin +
		vecDir.Normalize() * 30;
	vec3_t vecTestCollision2 = vecTestCollision;
	vecTestCollision2.z -= pPartSys->pParticle[iNum].flSize /2;
	vec3_t vecTestCollision3 = vecTestCollision;
	vecTestCollision3.z += pPartSys->pParticle[iNum].flSize /2;


	int contentParticle = ENG_PM_POINT_CONTENTS(vecTestCollision,NULL);
	int contentParticle2 = ENG_PM_POINT_CONTENTS(vecTestCollision2,NULL);
	int contentParticle3 = ENG_PM_POINT_CONTENTS(vecTestCollision3,NULL);
	if(contentParticle == CONTENT_SOLID || contentParticle2 == CONTENT_SOLID || contentParticle3 == CONTENT_SOLID)
	{	
		pPartSys->pParticle[iNum].flEnergy = 0;
		pPartSys->pParticle[iNum].flEnergyDelta = 0;
	}
}

void CParticleFx::CreateIncommingDust( int iNum, partsys_s *pPartSys, bool blWater, vec3_t vecOrigin, float flFarOut )
{
	// Entering in general data
	pPartSys->pParticle[iNum].flEnergy = 1.0;
	pPartSys->pParticle[iNum].flEnergyDelta = -1;
	pPartSys->pParticle[iNum].flSize = 2;
	pPartSys->pParticle[iNum].flSizeDelta = 10;
	pPartSys->pParticle[iNum].flSpeed = 150;
	pPartSys->pParticle[iNum].flWeight = 0;
	if(blWater)
	{
		pPartSys->pParticle[iNum].iColor[0] = 0;
		pPartSys->pParticle[iNum].iColor[1] = 61;
		pPartSys->pParticle[iNum].iColor[2] = 173;
		pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(1, 2);
	}
	else
	{
		pPartSys->pParticle[iNum].iColor[0] = 108;
		pPartSys->pParticle[iNum].iColor[1] = 78;
		pPartSys->pParticle[iNum].iColor[2] = 0;
		pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(-1, 0);
	}
	pPartSys->pParticle[iNum].iColor[3] = 255;
	pPartSys->pParticle[iNum].iFrame = 0;
	pPartSys->pParticle[iNum].blCollision = false;
	pPartSys->pParticle[iNum].iBounce = 0;
	pPartSys->pParticle[iNum].flRotationSpeed = 0;

	// entering vector information
	pPartSys->pParticle[iNum].vecRotation = pPartSys->pParticle[iNum].vecRotation2 = vec3_t(0,0,0);
	vec3_t vecVelocity;
	vecVelocity.x = gEngfuncs.pfnRandomFloat(-10, 10);
	vecVelocity.y = gEngfuncs.pfnRandomFloat(-10, 10);
	vecVelocity.z = 0;
	VectorNormalize(vecVelocity);

	// Set origin and inverse velocity
	vec3_t vecPos = (vecVelocity * flFarOut) + vecOrigin;
	vecPos.z += 4;
	vecVelocity = vecVelocity * -1;

	VectorCopy( vecPos, pPartSys->pParticle[iNum].vecOrigin );
	VectorCopy( vecVelocity, pPartSys->pParticle[iNum].vecVelocity );
}

// Recreting particle!
void CParticleFx::ReCreateColumnDust( int iNum, partsys_s *pPartSys )
{
	// Not used
	bool blWater = false;
	// Get the origin
	// Traceline
	vec3_t vecOrigin = ParticleTrace( pPartSys->vecOrigin, blWater, DOWN );

	// Entering in general data
	// Make the dust go higher if the player is high in the air
	{
		float flDist = (vecOrigin - pPartSys->vecOrigin).Length();
		if(flDist >= 150)
			pPartSys->pParticle[iNum].flEnergy = 1.8 + flDist / 115;
		else
			pPartSys->pParticle[iNum].flEnergy = 1.8 + flDist / 72;
		pPartSys->pParticle[iNum].flSpeed = 30 + flDist / 3.5;
	}

	pPartSys->pParticle[iNum].flEnergyDelta = 1.3;
	pPartSys->pParticle[iNum].flSizeDelta = 5.5;
	pPartSys->pParticle[iNum].flWeight = 0.5;

	// entering vector information
	pPartSys->pParticle[iNum].flRotationSpeed = gEngfuncs.pfnRandomFloat(0,PI);
	float flRotationVar = pPartSys->pParticle[iNum].flRotationSpeed;

	pPartSys->pParticle[iNum].vecRotation = vec3_t(float(cos(2*flRotationVar)), float(sin(2*flRotationVar)), float(0));
	pPartSys->pParticle[iNum].vecRotation2 = vec3_t(0,0,0);

	vec3_t vecDir;
	vecDir.x = 0;
	vecDir.y = 0;
	vecDir.z = 1;
	VectorNormalize(vecDir);
	VectorCopy( vecDir, pPartSys->pParticle[iNum].vecVelocity );

	// Setting origin...
	vec3_t vecRotate = pPartSys->pParticle[iNum].vecRotation;
	VectorNormalize(vecRotate);

	pPartSys->pParticle[iNum].vecOrigin = vecRotate*25;
	pPartSys->pParticle[iNum].vecOrigin.z -= 5;
	pPartSys->pParticle[iNum].vecOrigin2 = vecOrigin;
}

void CParticleFx::CreateColumnDust( int iNum, partsys_s *pPartSys, bool blWater, vec3_t vecOrigin )
{
	// Entering in general data
	// Make the dust go higher if the player is high in the air
	{
		float flDist = (vecOrigin - pPartSys->vecOrigin).Length();
		if(flDist >= 150)
			pPartSys->pParticle[iNum].flEnergy = 1.8 + flDist / 115;
		else
			pPartSys->pParticle[iNum].flEnergy = 1.8 + flDist / 72;
		pPartSys->pParticle[iNum].flSpeed = 30 + flDist / 3.5;
	}

	pPartSys->pParticle[iNum].flEnergyDelta = 1.3;
	pPartSys->pParticle[iNum].flSize = 7;
	pPartSys->pParticle[iNum].flSizeDelta = 5.5;
	pPartSys->pParticle[iNum].flWeight = 0.5;
	if(blWater)
	{
		pPartSys->pParticle[iNum].iColor[0] = 0;
		pPartSys->pParticle[iNum].iColor[1] = 61;
		pPartSys->pParticle[iNum].iColor[2] = 173;
		pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(1, 2);
	}
	else
	{
		pPartSys->pParticle[iNum].iColor[0] = 108;
		pPartSys->pParticle[iNum].iColor[1] = 78;
		pPartSys->pParticle[iNum].iColor[2] = 0;
		pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(-1, 0);
	}
	pPartSys->pParticle[iNum].iColor[3] = 255;
	pPartSys->pParticle[iNum].iFrame = 0;
	pPartSys->pParticle[iNum].blCollision = false;
	pPartSys->pParticle[iNum].iBounce = 0;
	pPartSys->pParticle[iNum].flRotationSpeed = 0;

	// entering vector information
	pPartSys->pParticle[iNum].flRotationSpeed = gEngfuncs.pfnRandomFloat(0,PI);
	float flRotationVar = pPartSys->pParticle[iNum].flRotationSpeed;

	pPartSys->pParticle[iNum].vecRotation = vec3_t(float(cos(2*flRotationVar)), float(sin(2*flRotationVar)), float(0));
	pPartSys->pParticle[iNum].vecRotation2 = vec3_t(0,0,0);

	vec3_t vecDir;
	vecDir.x = 0;
	vecDir.y = 0;
	vecDir.z = 1;
	VectorNormalize(vecDir);
	VectorCopy( vecDir, pPartSys->pParticle[iNum].vecVelocity );

	// Setting origin...
	vec3_t vecRotate = pPartSys->pParticle[iNum].vecRotation;
	VectorNormalize(vecRotate);

	pPartSys->pParticle[iNum].vecOrigin = vecRotate*25;
	pPartSys->pParticle[iNum].vecOrigin.z -= 5;
	pPartSys->pParticle[iNum].vecOrigin2 = vecOrigin;
}

void CParticleFx::CreateRotatingDust( int iNum, int iParticlesEmitted, int iMaxParticlesEmitted, partsys_s *pPartSys, bool blWater, vec3_t vecOrigin )
{
	// Entering in general data
	pPartSys->pParticle[iNum].flEnergyDelta = -1;
	pPartSys->pParticle[iNum].flEnergy = 1.0;
	pPartSys->pParticle[iNum].flSpeed = 40;
	pPartSys->pParticle[iNum].flSize = 25;
	pPartSys->pParticle[iNum].flSizeDelta = 0;
	pPartSys->pParticle[iNum].flWeight = 0;
	if(blWater)
	{
		pPartSys->pParticle[iNum].iColor[0] = 0;
		pPartSys->pParticle[iNum].iColor[1] = 61;
		pPartSys->pParticle[iNum].iColor[2] = 173;
		pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(1, 2);
	}
	else
	{
		pPartSys->pParticle[iNum].iColor[0] = 108;
		pPartSys->pParticle[iNum].iColor[1] = 78;
		pPartSys->pParticle[iNum].iColor[2] = 0;
		pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(-1, 0);
	}
	pPartSys->pParticle[iNum].iColor[3] = 255;
	pPartSys->pParticle[iNum].iFrame = 0;
	pPartSys->pParticle[iNum].blCollision = false;
	pPartSys->pParticle[iNum].iBounce = 0;
	pPartSys->pParticle[iNum].flRotationSpeed = 0;

	// entering vector information
	float flRotationVar = PI*(float(iParticlesEmitted)/float(iMaxParticlesEmitted));
	pPartSys->pParticle[iNum].flRotationSpeed = flRotationVar;

	pPartSys->pParticle[iNum].vecRotation = vec3_t(float(cos(2*flRotationVar)), float(sin(2*flRotationVar)), float(0));
	pPartSys->pParticle[iNum].vecRotation2 = vec3_t(0,0,0);

	pPartSys->pParticle[iNum].vecVelocity = vec3_t(0,0,0);

	// Setting origin...
	vec3_t vecRotate = pPartSys->pParticle[iNum].vecRotation;
	VectorNormalize(vecRotate);

	pPartSys->pParticle[iNum].vecOrigin = vecRotate*38;
	vecOrigin.z += pPartSys->pParticle[iNum].flSize/4;
	pPartSys->pParticle[iNum].vecOrigin2 = vecOrigin;
}

void CParticleFx::CreateSwoopDustTrail( int iNum, partsys_s *pPartSys, bool blWater, vec3_t vecOrigin, int iIndex )
{
	vec3_t v_forward, v_right, v_up;
	// Storing angles in vecForce
	AngleVectors( pPartSys->vecForce, v_forward, v_right, v_up);
	VectorNormalize(v_forward);
	VectorNormalize(v_right);
	VectorNormalize(v_up);
	v_up.x *= -1;
	v_up.y *= -1;

	// Entering in general data
	pPartSys->pParticle[iNum].flEnergy = 1.4;
	pPartSys->pParticle[iNum].flEnergyDelta = 1.5;
	pPartSys->pParticle[iNum].flSize = 15;
	pPartSys->pParticle[iNum].flSizeDelta = 55;
	pPartSys->pParticle[iNum].flSpeed = 160;
	pPartSys->pParticle[iNum].flWeight = 0;
	if(blWater)
	{
		pPartSys->pParticle[iNum].iColor[0] = 0;
		pPartSys->pParticle[iNum].iColor[1] = 61;
		pPartSys->pParticle[iNum].iColor[2] = 173;
		pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(1, 2);
	}
	else
	{
		pPartSys->pParticle[iNum].iColor[0] = 108;
		pPartSys->pParticle[iNum].iColor[1] = 78;
		pPartSys->pParticle[iNum].iColor[2] = 0;
		pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(-1, 0);
	}
	pPartSys->pParticle[iNum].iColor[3] = 255;
	pPartSys->pParticle[iNum].iFrame = 0;
	pPartSys->pParticle[iNum].blCollision = false;
	pPartSys->pParticle[iNum].iBounce = 0;
	pPartSys->pParticle[iNum].flRotationSpeed = 0;

	// entering vector information
	pPartSys->pParticle[iNum].vecRotation = pPartSys->pParticle[iNum].vecRotation2 = vec3_t(0,0,0);
	
	int iSwitch = gEngfuncs.pfnRandomLong(0,1);

	vec3_t vecDir = v_right * (iSwitch ? -6 : 6) + v_up * 0.5;
	VectorCopy( vecDir, pPartSys->pParticle[iNum].vecVelocity );
	vecOrigin.z += pPartSys->pParticle[iNum].flSize/4;
	pPartSys->pParticle[iNum].vecOrigin = vecOrigin + v_right * (iSwitch ? -24 : 24);
	
	// Test origin...
	// Dont create particle if its about to hit something
	vec3_t vecTestCollision = pPartSys->pParticle[iNum].vecOrigin;
	vec3_t vecTestCollision3 = vecTestCollision;
	vecTestCollision3.z += pPartSys->pParticle[iNum].flSize /3;


	int contentParticle = ENG_PM_POINT_CONTENTS(vecTestCollision,NULL);
	if(contentParticle == CONTENT_SOLID)
	{	
		// Move it up, and check again. If its still in a solid, delete 
		pPartSys->pParticle[iNum].vecOrigin.z += 2.5;
		contentParticle = ENG_PM_POINT_CONTENTS(vecTestCollision,NULL);
		if(contentParticle == CONTENT_SOLID)
		{	
			pPartSys->pParticle[iNum].flEnergy = 0;
			pPartSys->pParticle[iNum].flEnergyDelta = 0;
		}
	}
}

void CParticleFx::CreateLighting( int iNum, partsys_s *pPartSys, bool blWater, vec3_t vecOrigin )
{
	// Entering in general data
	pPartSys->pParticle[iNum].flEnergy = -1;
	pPartSys->pParticle[iNum].flEnergyDelta = -1;
	pPartSys->pParticle[iNum].flSize = gEngfuncs.pfnRandomLong(55,75);
	pPartSys->pParticle[iNum].flSizeDelta = -1;
	pPartSys->pParticle[iNum].flSpeed = 0;
	pPartSys->pParticle[iNum].flWeight = 0;
	pPartSys->pParticle[iNum].iColor[0] = 190;
	pPartSys->pParticle[iNum].iColor[1] = 190;
	pPartSys->pParticle[iNum].iColor[2] = 190;
	pPartSys->pParticle[iNum].iColor[3] = 190;
	pPartSys->pParticle[iNum].iFrame = 0;
	pPartSys->pParticle[iNum].iSprite = 0;
	pPartSys->pParticle[iNum].blCollision = false;
	pPartSys->pParticle[iNum].iBounce = 0;
	pPartSys->pParticle[iNum].flRotationSpeed = 0;
	pPartSys->pParticle[iNum].vecRotation = pPartSys->pParticle[iNum].vecRotation2 = vec3_t(0,0,0);
	pPartSys->pParticle[iNum].vecVelocity = vec3_t(0,0,0);

	// entering vector information
	// Doing defines early..
	pmtrace_t tr1, tr2;
	vec3_t vecMiddle, vecPartOrigin1, vecPartOrigin2, vecDir, vecVeloctyTrace;
	vecPartOrigin1 = vecPartOrigin2 = vecOrigin;

	// Getting origin...
	vecMiddle = vecOrigin;
	vecMiddle.x += gEngfuncs.pfnRandomLong(-500, 500);
	vecMiddle.y += gEngfuncs.pfnRandomLong(-500, 500);
	vecMiddle.z += 200;
	vecPartOrigin1 = vecPartOrigin2 = vecMiddle;

	// Making the 2 traces...
	// Down
	vecDir = vec3_t(0,0,-1);
	vecVeloctyTrace = vecPartOrigin1 + 8192 * vecDir;
	tr1 = *(gEngfuncs.PM_TraceLine((float *)&vecPartOrigin1, (float *)&vecVeloctyTrace , PM_TRACELINE_PHYSENTSONLY ,2, -1));
	vecPartOrigin1 = tr1.endpos;
	// Up
	vecDir = vec3_t(0,0,1);
	vecVeloctyTrace = vecPartOrigin2 + 8192 * vecDir;
	tr2 = *(gEngfuncs.PM_TraceLine((float *)&vecPartOrigin2, (float *)&vecVeloctyTrace , PM_TRACELINE_PHYSENTSONLY ,2, -1));
	vecPartOrigin2 = tr2.endpos;

	// Lets set the 2 origins...
	pPartSys->pParticle[iNum].vecOrigin = vecPartOrigin1;
	pPartSys->pParticle[iNum].vecOrigin2 = vecPartOrigin2;

	// Crackle Sound - Pretty loud
	gEngfuncs.pEventAPI->EV_PlaySound( -1, vecMiddle, 0, "ambience/thunderclap.wav", 1.0, ATTN_NORM, 0, PITCH_NORM );	
}

void CParticleFx::CreateSB_Charger( int iNum, partsys_s *pPartSys )
{
	// Entering in general data
	pPartSys->pParticle[iNum].flEnergy = gEngfuncs.pfnRandomFloat(1, 2);
	pPartSys->pParticle[iNum].flEnergyDelta = -1;
	pPartSys->pParticle[iNum].flSize = gEngfuncs.pfnRandomFloat(1,2);
	pPartSys->pParticle[iNum].flSizeDelta = 0;
	pPartSys->pParticle[iNum].flSpeed = gEngfuncs.pfnRandomFloat( 100, 180 );
	pPartSys->pParticle[iNum].flWeight = 0;
	pPartSys->pParticle[iNum].iColor[0] = 255;
	pPartSys->pParticle[iNum].iColor[1] = 255;
	pPartSys->pParticle[iNum].iColor[2] = 255;
	pPartSys->pParticle[iNum].iColor[3] = 255;
	pPartSys->pParticle[iNum].iFrame = 0;
	pPartSys->pParticle[iNum].iSprite = 0;
	pPartSys->pParticle[iNum].blCollision = false;
	pPartSys->pParticle[iNum].iBounce = 0;
	pPartSys->pParticle[iNum].flRotationSpeed = 0;

	// velocity + origin
	vec3_t vecDir;
	vecDir.x = gEngfuncs.pfnRandomFloat( -1, 1 );
	vecDir.y = gEngfuncs.pfnRandomFloat( -1, 1 );
	vecDir.z = gEngfuncs.pfnRandomFloat( -1, -0.05);
	VectorNormalize(vecDir);
	pmtrace_t tr;
	gEngfuncs.pEventAPI->EV_PlayerTrace( pPartSys->vecOrigin, pPartSys->vecOrigin+vecDir*8192, PM_NORMAL, -1, &tr );
	pPartSys->pParticle[iNum].vecOrigin = tr.endpos;
	pPartSys->pParticle[iNum].vecVelocity = pPartSys->vecOrigin - tr.endpos;
	VectorNormalize(pPartSys->pParticle[iNum].vecVelocity);
}

void CParticleFx::CreateSteamHead(int iNum, partsys_s *pPartSys, bool blSwitchSides, bool blHead, int iIndex, float flTimeStart, float flTimeEnd )
{
	// Vector angles
	cl_entity_t *pClient = gEngfuncs.GetEntityByIndex(iIndex);
	vec3_t vecAngles = pClient->angles;
	/*modified by harSens
	std::list<model_manip_s>::iterator pManip = gHUD.m_ManipModel.FindByPlayerAndOtherIndex(pClient->index);
	*/
	model_manip_s *pManip = gHUD.m_ManipModel.FindByPlayerAndOtherIndex(pClient->index);
	if(pManip!=NULL)
	{	if(pManip->blFixedAngles)
		{	vecAngles = pManip->vecAngles;
		}
	}
	vec3_t v_forward, v_right, v_up;
	AngleVectors( vecAngles, v_forward, v_right, v_up);
	VectorNormalize(v_forward);
	VectorNormalize(v_right);
	VectorNormalize(v_up);
	v_up.x *= -1;
	v_up.y *= -1;

	// Entering in general data
	pPartSys->pParticle[iNum].flEnergy = 1.15;
	pPartSys->pParticle[iNum].flEnergyDelta = 1.3;
	pPartSys->pParticle[iNum].flSize = 3;
	pPartSys->pParticle[iNum].flSizeDelta = gEngfuncs.pfnRandomFloat(15,20);
	pPartSys->pParticle[iNum].flSpeed = 25;
	pPartSys->pParticle[iNum].flWeight = 0;

	// Pink
	// R: 255
	// G: 103
	// B: 235
	
	// Fade to the color pink in 1/2 of the time
	float flDurration = (flTimeEnd - flTimeStart) / 3;
	float flCurTime = flTime - flTimeStart;
	float flFractionTime = flCurTime / flDurration;
	if(flFractionTime > 1)
		flFractionTime = 1;
	// Grey + Difference * Percent done
	pPartSys->pParticle[iNum].iColor[0] = 220 + (225-255) * flFractionTime;
	pPartSys->pParticle[iNum].iColor[1] = 220 + (103-220) * flFractionTime;
	pPartSys->pParticle[iNum].iColor[2] = 220 + (235-220) * flFractionTime;
	pPartSys->pParticle[iNum].iColor[3] = 255;

	pPartSys->pParticle[iNum].iSprite = 0;
	pPartSys->pParticle[iNum].iFrame = 0;
	pPartSys->pParticle[iNum].blCollision = false;
	pPartSys->pParticle[iNum].iBounce = 0;
	pPartSys->pParticle[iNum].flRotationSpeed = 0;

	// entering vector information
	pPartSys->pParticle[iNum].vecRotation = pPartSys->pParticle[iNum].vecRotation2 = vec3_t(0,0,0);

	// Setting Origin
	VectorCopy( pClient->origin, pPartSys->pParticle[iNum].vecOrigin );
	vec3_t vecTweakOrigin = pClient->origin;

	// Approx. position of buus holes
	if(blHead)
		vecTweakOrigin = vecTweakOrigin + (v_forward * 4) + v_right * (blSwitchSides ? -1.5 : 1.5) + (v_up * 8);
	else
		vecTweakOrigin = vecTweakOrigin + (v_forward * (blSwitchSides ? 0.75 : 0.75)) + v_right * (blSwitchSides ? -4.8 : 4.8) + (v_up * (blSwitchSides ? 5.3 : 4.5));

	VectorCopy(vecTweakOrigin,  pPartSys->pParticle[iNum].vecOrigin);
	// Setting velocity
	
	vec3_t vecDir;
	if(blHead)
		vecDir = v_right * (blSwitchSides ? -2 : 2) + v_up * 4;
	else
		vecDir = v_right * (blSwitchSides ? -6 : 6) + v_up * 2.5;
	VectorNormalize(vecDir);
	VectorCopy( vecDir, pPartSys->pParticle[iNum].vecVelocity );
}

void CParticleFx::CreateSteam( partsys_s *pPartSys, int iNum, float flTimeStart, float flTimeEnd )
{
	// Entering in general data
	pPartSys->pParticle[iNum].flEnergy = 1.2;
	pPartSys->pParticle[iNum].flEnergyDelta = -1;
	pPartSys->pParticle[iNum].flSize = gEngfuncs.pfnRandomFloat(20,35);
	pPartSys->pParticle[iNum].flSizeDelta = -1;
	pPartSys->pParticle[iNum].flSpeed = 0;
	pPartSys->pParticle[iNum].flWeight = 0;

	// Pink
	// R: 255
	// G: 103
	// B: 235

	pPartSys->pParticle[iNum].iColor[0] = 255;
	pPartSys->pParticle[iNum].iColor[1] = 103;
	pPartSys->pParticle[iNum].iColor[2] = 235;
	pPartSys->pParticle[iNum].iColor[3] = 255;
	pPartSys->pParticle[iNum].iSprite = gEngfuncs.pfnRandomLong(-1, 0);

	pPartSys->pParticle[iNum].iFrame = 0;
	pPartSys->pParticle[iNum].blCollision = false;
	pPartSys->pParticle[iNum].iBounce = 0;
	pPartSys->pParticle[iNum].flRotationSpeed = 0;

	// entering vector information
	pPartSys->pParticle[iNum].vecRotation = pPartSys->pParticle[iNum].vecRotation2 = vec3_t(0,0,0);
	pPartSys->pParticle[iNum].vecVelocity = vec3_t(0,0,0);

	// Calcuate fractions...
	float flDurration = flTimeEnd - flTimeStart;
	float flCurTime = flTime - flTimeStart;
	float flFractionTime = flCurTime / flDurration;
	if(flFractionTime > 1)
		flFractionTime = 1;

	int iMaxSmokeFormingHeight = 12;

	// Calculated by time left. The less time left, the lower the smoke forms.
	vec3_t vecOrigin = pPartSys->vecOrigin;
	vecOrigin.z += (iMaxSmokeFormingHeight) - (iMaxSmokeFormingHeight*2) * flFractionTime;
	vecOrigin.x += gEngfuncs.pfnRandomLong(-8,8);
	vecOrigin.y += gEngfuncs.pfnRandomLong(-8,8);

	pPartSys->pParticle[iNum].vecOrigin = vecOrigin;
}

void CParticleFx::Emit( part_manager_s *pPartManager )
{
	bool blWater = false;
	// See if the player is under water
	bool blWaterPlayer = false;
	int contentPlayer = 0;
	
	if(pPartManager->pParticle)
		contentPlayer = ENG_PM_POINT_CONTENTS(pPartManager->pParticle->vecOrigin,NULL);
	if(pPartManager->pPartSys)
		contentPlayer = ENG_PM_POINT_CONTENTS(pPartManager->pPartSys->vecOrigin,NULL);

	if(contentPlayer == CONTENT_WATER)
		blWaterPlayer = true;
	
	vec3_t vecEndPos;
	if(pPartManager->pPartSys)
	{
		// Traceline
		vecEndPos = ParticleTrace( pPartManager->pPartSys->vecOrigin, blWater, DOWN );
	}

	int iParticlesNeeded = 1;
	int iGenerated = 0;

	// Lets emit some particles!
	switch(pPartManager->m_iFlag)
	{
	case FLOATING_ROCKS:
		{	
			// If the player is under water, dont even bother
			if(blWaterPlayer)
				break;

			iGenerated = 0;
			int iMaxParticles = pPartManager->pPartSys->iMaxParticles;
			if(iQuality == LOW_QUALITY_FX)
				iParticlesNeeded = 3;
			else if(iQuality == MEDIUM_QUALITY_FX)
				iParticlesNeeded = 10;
			else
				iParticlesNeeded = 15;
			// Distance..
			float flDist = (vecEndPos - pPartManager->pPartSys->vecOrigin).Length();
			while( true )
			{
				// Make sure were not too high...
				if(flDist < 150)
				{	
					if(!blWater)
					{
						for(int i = 0; i < pPartManager->pPartSys->iMaxParticles; i++ )
						{	
							particle_fx pParticle = pPartManager->pPartSys->pParticle[i];
							if(pParticle.flEnergy == 0)
							{	
								CreateRocks( i, pPartManager->pPartSys, vecEndPos );
								iGenerated++;

								// If you made your specified amount of particles, quit.
								if(iGenerated >= iParticlesNeeded)
									break;
							}
						}

						if(iGenerated >= iParticlesNeeded)
							break;

						
						// if we dont have enough particles, allocate somemore!
						int iAddedParticles = iParticlesNeeded - iGenerated;

						// Transfer all data over
						particle_fx *pNewParticles = new particle_fx[iMaxParticles+iAddedParticles];
						if(!pNewParticles)
							break;

						int iNum;
						for( iNum = 0; iNum < iMaxParticles; iNum++ )
						{
							pNewParticles[iNum] = pPartManager->pPartSys->pParticle[iNum];
						}
						
						for( iNum = iMaxParticles; iNum < iMaxParticles + iAddedParticles; iNum++ )
						{
							pNewParticles[iNum].flEnergy = 0;
							pNewParticles[iNum].flEnergyDelta = -1;
							pNewParticles[iNum].vecOrigin = vec3_t(0,0,0);
						}
						// Delete + replace
						delete [] pPartManager->pPartSys->pParticle;
						pPartManager->pPartSys->pParticle = NULL;
						pPartManager->pPartSys->pParticle = pNewParticles;
						pPartManager->pPartSys->iMaxParticles = iMaxParticles + iAddedParticles;
						iMaxParticles = pPartManager->pPartSys->iMaxParticles;
						continue;
						
					}
					else
					{
						break;
					}
				}
				else
				{
					break;
				}
			}
			break;
		}
	case DUST:
	case COLUMN_DUST:
	case EXPANDED_DUST:
	case ROTATING_DUST:
	case DUSTTRAIL:
		{	
			// If the player is under water, dont even bother
			if(blWaterPlayer)
				break;
			if(!pPartManager->pPartSys)
				break;

			iGenerated = 0;
			int iMaxParticles = pPartManager->pPartSys->iMaxParticles;

			// Max amount of particles
			switch(pPartManager->m_iFlag)
			{
			case DUST:
				{
					if(iQuality == LOW_QUALITY_FX)
						iParticlesNeeded = 20;
					else if(iQuality == MEDIUM_QUALITY_FX)
						iParticlesNeeded = 35;
					else
						iParticlesNeeded = 50;
					break;
				}
			case EXPANDED_DUST:
				{
					if(iQuality == LOW_QUALITY_FX)
						iParticlesNeeded = 4;
					else if(iQuality == MEDIUM_QUALITY_FX)
						iParticlesNeeded = 10;
					else
						iParticlesNeeded = 15;
					break;
				}
			case ROTATING_DUST:
				{
					if(iQuality == LOW_QUALITY_FX)
						iParticlesNeeded = 10;
					else if(iQuality == MEDIUM_QUALITY_FX)
						iParticlesNeeded = 20;
					else
						iParticlesNeeded = 30;
					break;
				}
			case DUSTTRAIL:
				{
					iParticlesNeeded = 2;
					break;
				}
			default:
				{
					if(iQuality == LOW_QUALITY_FX)
						iParticlesNeeded = 10;
					else if(iQuality == MEDIUM_QUALITY_FX)
						iParticlesNeeded = 20;
					else
						iParticlesNeeded = 35;

					break;
				}
			}
			
			float flDist = (vecEndPos - pPartManager->pPartSys->vecOrigin).Length();
			while( true )
			{
				// Dont do dust if we are too high in the air
				if(pPartManager->m_iFlag == DUST || (flDist < 300 && pPartManager->m_iFlag == COLUMN_DUST) ||
					(flDist < 200 && (pPartManager->m_iFlag == ROTATING_DUST || pPartManager->m_iFlag == EXPANDED_DUST)) ||
					(flDist < 200 && (pPartManager->m_iFlag == DUSTTRAIL)))
				{	
					for(int i = 0; i < iMaxParticles; i++ )
					{	particle_fx pParticle = pPartManager->pPartSys->pParticle[i];

						if(!pParticle.flEnergy)
						{	
							if( pPartManager->m_iFlag == DUST )
								CreateDust( i, pPartManager->pPartSys, blWater, vecEndPos, 4 );
							else if( pPartManager->m_iFlag == DUSTTRAIL )
								CreateSwoopDustTrail( i, pPartManager->pPartSys, blWater, vecEndPos, pPartManager->m_iIndex );
							else if( pPartManager->m_iFlag == EXPANDED_DUST )
								CreateDust( i, pPartManager->pPartSys, blWater, vecEndPos, 38 );
							else if( pPartManager->m_iFlag == ROTATING_DUST )
								CreateRotatingDust(i, iGenerated, iParticlesNeeded, pPartManager->pPartSys, blWater, vecEndPos );
							else if( pPartManager->m_iFlag == COLUMN_DUST )
								CreateColumnDust( i, pPartManager->pPartSys, blWater, vecEndPos );


							iGenerated++;

							// If you made your specified amount of particles, quit.
							if(iGenerated == iParticlesNeeded)
								break;
						}
					}

					// If rotating dust has enough particles, dont generate anymore
					if(pPartManager->m_iFlag == ROTATING_DUST)
					{
						if(iQuality == LOW_QUALITY_FX)
						{
							if(iMaxParticles >= 10)
								break;
						}
						else if(iQuality == MEDIUM_QUALITY_FX)
						{
							if(iMaxParticles >= 20)
								break;
						}
						else
						{
							if(iMaxParticles >= 30)
								break;
						}
					}

					if(iGenerated >= iParticlesNeeded)
						break;

					// if we dont have enough particles, allocate somemore!
					int iAddedParticles = iParticlesNeeded - iGenerated;

					// Transfer all data over
					particle_fx *pNewParticles = new particle_fx[iMaxParticles+iAddedParticles];
					if(!pNewParticles)
						break;

					int iNum;
					for( iNum = 0; iNum < iMaxParticles; iNum++ )
					{
						pNewParticles[iNum] = pPartManager->pPartSys->pParticle[iNum];
					}
					
					for( iNum = iMaxParticles; iNum < iMaxParticles + iAddedParticles; iNum++ )
					{
						pNewParticles[iNum].flEnergy = 0;
						pNewParticles[iNum].flEnergyDelta = -1;
						pNewParticles[iNum].vecOrigin = vec3_t(0,0,0);
					}
					// Delete + replace
					delete [] pPartManager->pPartSys->pParticle;
					pPartManager->pPartSys->pParticle = NULL;
					pPartManager->pPartSys->pParticle = pNewParticles;
					pPartManager->pPartSys->iMaxParticles = iMaxParticles + iAddedParticles;
					iMaxParticles = pPartManager->pPartSys->iMaxParticles;
					continue;
				}
				else
				{	break;
				}
			}
			break;
		}
	case STEAM_HEAD:
		{
			iGenerated = 0;
			int iMaxParticles = pPartManager->pPartSys->iMaxParticles;
			iParticlesNeeded = 4;
			
			while( true )
			{
				for(int i = 0; i < iMaxParticles; i++ )
				{	particle_fx pParticle = pPartManager->pPartSys->pParticle[i];
					if(!pParticle.flEnergy)
					{	
						if(!iGenerated)
							CreateSteamHead( i, pPartManager->pPartSys, true, false, pPartManager->m_iIndex, pPartManager->flTimeStart, pPartManager->flTimeEnd );
						else if(iGenerated == 1)
							CreateSteamHead( i, pPartManager->pPartSys, false, false,pPartManager->m_iIndex, pPartManager->flTimeStart, pPartManager->flTimeEnd );
						else if(iGenerated == 2)
							CreateSteamHead( i, pPartManager->pPartSys, true, true, pPartManager->m_iIndex, pPartManager->flTimeStart, pPartManager->flTimeEnd );
						else if(iGenerated == 3)
							CreateSteamHead( i, pPartManager->pPartSys, false, true, pPartManager->m_iIndex, pPartManager->flTimeStart, pPartManager->flTimeEnd );

						iGenerated++;

						// If you made your specified amount of particles, quit.
						if(iGenerated == iParticlesNeeded)
							break;
					}
				}

				if(iGenerated >= iParticlesNeeded)
					break;

				// if we dont have enough particles, allocate somemore!
				int iAddedParticles = iParticlesNeeded - iGenerated;

				// Transfer all data over
				particle_fx *pNewParticles = new particle_fx[iMaxParticles+iAddedParticles];
				if(!pNewParticles)
					break;

				int iNum;
				for( iNum = 0; iNum < iMaxParticles; iNum++ )
				{
					pNewParticles[iNum] = pPartManager->pPartSys->pParticle[iNum];
				}
				
				for( iNum = iMaxParticles; iNum < iMaxParticles + iAddedParticles; iNum++ )
				{
					pNewParticles[iNum].flEnergy = 0;
					pNewParticles[iNum].flEnergyDelta = -1;
					pNewParticles[iNum].vecOrigin = vec3_t(0,0,0);
				}
				// Delete + replace
				delete [] pPartManager->pPartSys->pParticle;
				pPartManager->pPartSys->pParticle = NULL;
				pPartManager->pPartSys->pParticle = pNewParticles;
				pPartManager->pPartSys->iMaxParticles = iMaxParticles + iAddedParticles;
				iMaxParticles = pPartManager->pPartSys->iMaxParticles;
				continue;
			}
			break;
		}
	case STEAM:
		{
			iGenerated = 0;
			int iMaxParticles = pPartManager->pPartSys->iMaxParticles;
			iParticlesNeeded = 1;
			
			while( true )
			{
				for(int i = 0; i < iMaxParticles; i++ )
				{	particle_fx pParticle = pPartManager->pPartSys->pParticle[i];
					if(!pParticle.flEnergy)
					{	
						CreateSteam( pPartManager->pPartSys, i, pPartManager->flTimeStart, pPartManager->flTimeEnd );

						iGenerated++;

						// If you made your specified amount of particles, quit.
						if(iGenerated == iParticlesNeeded)
							break;
					}
				}

				if(iGenerated >= iParticlesNeeded)
					break;

				// if we dont have enough particles, allocate somemore!
				int iAddedParticles = iParticlesNeeded - iGenerated;

				// Transfer all data over
				particle_fx *pNewParticles = new particle_fx[iMaxParticles+iAddedParticles];
				if(!pNewParticles)
					break;

				int iNum;
				for(iNum = 0; iNum < iMaxParticles; iNum++ )
				{
					pNewParticles[iNum] = pPartManager->pPartSys->pParticle[iNum];
				}
				
				for( iNum = iMaxParticles; iNum < iMaxParticles + iAddedParticles; iNum++ )
				{
					pNewParticles[iNum].flEnergy = 0;
					pNewParticles[iNum].flEnergyDelta = -1;
					pNewParticles[iNum].vecOrigin = vec3_t(0,0,0);
				}
				// Delete + replace
				delete [] pPartManager->pPartSys->pParticle;
				pPartManager->pPartSys->pParticle = NULL;
				pPartManager->pPartSys->pParticle = pNewParticles;
				pPartManager->pPartSys->iMaxParticles = iMaxParticles + iAddedParticles;
				iMaxParticles = pPartManager->pPartSys->iMaxParticles;
				continue;
			}
			break;
		}
	case LIGHTING:
		{	
			for(int i = 0; i < pPartManager->pPartSys->iMaxParticles; i++ )
			{
				particle_fx pParticle = pPartManager->pPartSys->pParticle[i];
				if(pParticle.flEnergy == 0)
				{
					CreateLighting( i, pPartManager->pPartSys, blWater, vecEndPos );
					// Randomize next electric bolt
					pPartManager->flEmitDelta = gEngfuncs.pfnRandomFloat(0.65, 1.45);
					break;
				}
			}
			break;
		}
	case SPIRIT_BOMB:
		{
			for(int i = 0; i < pPartManager->pPartSys->iMaxParticles; i++ )
			{
				particle_fx pParticle = pPartManager->pPartSys->pParticle[i];
				if(!pParticle.flEnergy)
				{
					CreateSB_Charger( i, pPartManager->pPartSys );
					break;
				}
			}
			break;
		}
	case SHOCKWAVE_CORE:
		{
			// Lets create a temp event, just to use for creating a FX element
			trans_event *pNewEvent = new trans_event;
			if(!pNewEvent)
			{	ConsolePrint("Error, could not allocate space!\n");
				break;
			}
			pNewEvent->iFlag = SHOCKWAVE_FROM_CORE;
			pNewEvent->flTimeStart = flTime;
			pNewEvent->flTimeEnd = flTime+3;
			pNewEvent->iIndex = pPartManager->m_iIndex;
			pNewEvent->pNext = NULL;

			// Lets emit some shockwaves :-)
			CreateParticleSystem( 1, pPartManager->m_iCharacter, pNewEvent );
			
			// We are done with the temp event, we can delete it now
			delete pNewEvent;
			pNewEvent = NULL;

			break;
		}
	case SHOCKWAVE_CORE_INVERSE:
		{
			// Lets create a temp event, just to use for creating a FX element
			trans_event *pNewEvent = new trans_event;
			if(!pNewEvent)
			{	ConsolePrint("Error, could not allocate space!\n");
				break;
			}
			pNewEvent->iFlag = SHOCKWAVE_FROM_CORE_INVERSE;
			pNewEvent->flTimeStart = flTime;
			pNewEvent->flTimeEnd = flTime+3;
			pNewEvent->iIndex = pPartManager->m_iIndex;
			pNewEvent->pNext = NULL;

			// Lets emit some shockwaves :-)
			CreateParticleSystem( 1, pPartManager->m_iCharacter, pNewEvent );
			
			// We are done with the temp event, we can delete it now
			delete pNewEvent;
			pNewEvent = NULL;

			break;
		}
	case SWOOP_DUSTTRAIL:
		{
			cl_entity_t *pClient = gEngfuncs.GetEntityByIndex(pPartManager->m_iIndex);
			vec3_t vecAngles = pClient->curstate.angles;
			vec3_t v_forward;
			// Storing angles in vecForce
			AngleVectors( vecAngles, v_forward, NULL, NULL);
			VectorNormalize(v_forward);

			// Lets create trails between the ones we missed
			vec3_t vecNewOrigin = pClient->origin + v_forward * 300;
			vec3_t vecOldOrigin = pPartManager->pParticle->vecOrigin;
			vec3_t vecDirection = vecNewOrigin - pPartManager->pParticle->vecOrigin;
			VectorNormalize(vecDirection);
			vec3_t vecCurOrigin = vecOldOrigin;

			int iDistance = 100;
			if(iDistance < 20)
				iDistance = 20;

			int iCount = 0;
			
			// If the length is too small, dont even bother on drawing it
			if((vecNewOrigin-vecOldOrigin).Length() < iDistance)
				break;

			// Our distance gap is too large! lets create some links inbetween.
			if((vecNewOrigin-vecOldOrigin).Length() > iDistance)
			{
				while((vecCurOrigin-vecNewOrigin).Length() >= iDistance)
				{
					iCount++;
					// Lets create some links inbetween
					// Lets create a temp event, just to use for creating a FX element
					trans_event *pNewEvent = new trans_event;
					if(!pNewEvent)
					{	ConsolePrint("Error, could not allocate space!\n");
						break;
					}

					pNewEvent->iFlag = DUSTTRAIL;
					pNewEvent->flTimeStart = flTime;
					pNewEvent->flTimeEnd = flTime+5;
					pNewEvent->iIndex = pPartManager->m_iIndex;
					pNewEvent->pNext = NULL;
					
					// Lets emit some dust trails :-)
					CreateParticleSystem( 100, vecCurOrigin, pNewEvent );

					// Lets delete the events
					delete pNewEvent;
					pNewEvent = NULL;

					// Move the origin towrds the new origin
					vecCurOrigin = vecCurOrigin + vecDirection * iDistance;
					if(iCount >= 60)
					{
						break;
					}
				}
			}

			// Lets create a temp event, just to use for creating a FX element
			trans_event *pNewEvent = new trans_event;
			if(!pNewEvent)
			{	ConsolePrint("Error, could not allocate space!\n");
				break;
			}

			pNewEvent->iFlag = DUSTTRAIL;
			pNewEvent->flTimeStart = flTime;
			pNewEvent->flTimeEnd = flTime+5;
			pNewEvent->iIndex = pPartManager->m_iIndex;
			pNewEvent->pNext = NULL;
			
			if(pPartManager->pParticle)
				pPartManager->pParticle->vecOrigin = vecNewOrigin;

			// Lets emit some dust trails :-)
			CreateParticleSystem( 100, vecNewOrigin, pNewEvent );
			
			// We are done with the temp event, we can delete it now
			delete pNewEvent;
			pNewEvent = NULL;

			break;
		}
	}

}

// -------------------
//  Control framerate
// -------------------
void CParticleFx::AnimateSprite( int iNum, part_manager_s *pPartManager )
{
	if(pPartManager->pPartSys)
	{
		partsys_s *pPartSys = pPartManager->pPartSys;

		// If the particle doesnt want to be animated, dont animate it!
		if(pPartSys->flFrameDelta == -1)
			return;

		// Animate it!
		if(pPartSys->pParticle[iNum].flFrameTime < flTime - pPartSys->flFrameDelta)
		{	pPartSys->pParticle[iNum].flFrameTime = flTime;
			// Next Frame in the sprite
			if(pPartSys->pParticle[iNum].iFrame < pPartSys->iMaxFrames-1)
			{	pPartSys->pParticle[iNum].iFrame++;
			}
			// If it played its one lighting animation, kill it
			else
			{	pPartSys->pParticle[iNum].iFrame = 0;
				pPartSys->pParticle[iNum].flEnergy = 0;
			}
		}
	}

	if(pPartManager->pParticle)
	{
		particle_fx *pParticle = pPartManager->pParticle;
		// If the particle doesnt want to be animated, dont animate it!
		if(pParticle->flFrameDelta == -1)
			return;

		// Animate it!
		if(pParticle->flFrameTime < flTime - pParticle->flFrameDelta)
		{	pParticle->flFrameTime = flTime;
			// Next Frame in the sprite
			if(pParticle->iFrame < pParticle->iMaxFrames-1)
			{	pParticle->iFrame++;
			}
			// If it played its one lighting animation, kill it
			else
			{	pParticle->iFrame = 0;
				pParticle->flEnergy = 0;
			}
		}
	}
}

// ------------------
//  Update Particles
// ------------------


// Particle Specific updating
void CParticleFx::UpdateSpecific( float flTime, int iNum, float flTimeDifference, part_manager_s *pPartManager )
{
	switch(pPartManager->m_iFlag)
	{
	case FLOATING_ROCKS:
		{
			if(pPartManager->flDieTime > 0)
			{
				partsys_s *pPartSys = pPartManager->pPartSys;
				if(!pPartSys)
					break;
				particle_fx *pParticle = pPartSys->pParticle;
				if(!pParticle)
					break;

				if(pParticle[iNum].flEnergy && pParticle[iNum].iBounce <= 5)
				{
					// This s basically so we detect when the particle hits a surface before the rock touches it(making it look better)
					vec3_t vecOriginAdvanced = pParticle[iNum].vecOrigin;
					vecOriginAdvanced.z -= 2;

					// If speed is negative(going upwards), slowly reverse it, do the oppisite if not
					if(pParticle[iNum].flSpeed < 0)
					{	pParticle[iNum].flSpeed -= 300 * flTimeDifference;
					}
					else if(pParticle[iNum].flSpeed > 0)
					{	pParticle[iNum].flSpeed -= 250 * flTimeDifference;
					}

					// If rock is up in the air on its climax point, turn it
					if(pParticle[iNum].flSpeed > -19 && pParticle[iNum].flSpeed < 0)
					{	pParticle[iNum].flSpeed = 20;
					}
					// Reverse
					if(pParticle[iNum].flSpeed < 19 && pParticle[iNum].flSpeed > 0)
					{	pParticle[iNum].flSpeed = -20;
						pParticle[iNum].vecVelocity.x = pParticle[iNum].vecVelocity.y = 0;
					}

					// If rock hits the floor, reverse Z axis, so it will "bounce"
					int contentParticle = ENG_PM_POINT_CONTENTS(vecOriginAdvanced,NULL);
					if(contentParticle == CONTENT_SOLID)
					{	pParticle[iNum].flSpeed *= -0.2;
						// Move it up/down a bit so it wont be in the ground still
						pParticle[iNum].vecOrigin = pParticle[iNum].vecOrigin + pParticle[iNum].vecVelocity.Normalize() * pParticle[iNum].flSpeed * flTimeDifference;

						pParticle[iNum].iBounce++;
						// If particle bounced 3 times, stop it
						if(pParticle[iNum].iBounce == 5)
						{	pParticle[iNum].flSpeed = 0;
							pParticle[iNum].vecVelocity = vec3_t(0,0,0);
						}
					}

					// Speed Checks, don't let the rocks go down at 90 mph:-)
					if(pParticle[iNum].flSpeed > 500)
					{	pParticle[iNum].flSpeed = 500;
					}
					else if(pParticle[iNum].flSpeed < -500)
					{	pParticle[iNum].flSpeed = -500;
					}
				}
			}
			break;
		}
	case DUST:
	case EXPANDED_DUST:
		{
			partsys_s *pPartSys = pPartManager->pPartSys;
			if(!pPartSys)
				break;
			particle_fx *pParticle = pPartSys->pParticle;
			if(!pParticle)
				break;

			if(!pParticle[iNum].blCollision)
			{
				// Stop the particle if it hits something
				if(CollisionCheck(pParticle[iNum].vecOrigin, pParticle[iNum].vecVelocity, pParticle[iNum].flSize/3 ))
				{	
					pParticle[iNum].flSpeed = 0;
					pParticle[iNum].vecVelocity = vec3_t(0,0,0);
					pParticle[iNum].blCollision = true;
				}
			}
			break;
		}
	case COLUMN_DUST:
		{
			partsys_s *pPartSys = pPartManager->pPartSys;
			if(!pPartSys)
				break;
			particle_fx *pParticle = pPartSys->pParticle;
			if(!pParticle)
				break;

			pParticle[iNum].flRotationSpeed += 2 * flTimeDifference;
			if(pParticle[iNum].flRotationSpeed > PI)
				pParticle[iNum].flRotationSpeed -= PI;

			float flRotationVar = pParticle[iNum].flRotationSpeed;
			pParticle[iNum].vecRotation = vec3_t(float(cos(2*flRotationVar)), float(sin(2*flRotationVar)), float(0));
			vec3_t vecVelocity = pParticle[iNum].vecRotation;
			VectorNormalize(vecVelocity);
			vecVelocity = vecVelocity * 25;
			pParticle[iNum].vecVelocity = vec3_t(0,0,1);
			pParticle[iNum].vecOrigin2 = pParticle[iNum].vecOrigin2 + (pParticle[iNum].vecVelocity * pParticle[iNum].flSpeed * flTimeDifference);
			// Particle System origin + Rotation Velocity + Up Velocity
			pParticle[iNum].vecOrigin = vecVelocity + pParticle[iNum].vecOrigin2;
			break;
		}
	case ROTATING_DUST:
		{
			partsys_s *pPartSys = pPartManager->pPartSys;
			if(!pPartSys)
				break;
			particle_fx *pParticle = pPartSys->pParticle;
			if(!pParticle)
				break;

			pParticle[iNum].flRotationSpeed += 2 * flTimeDifference;
			if(pParticle[iNum].flRotationSpeed > PI)
				pParticle[iNum].flRotationSpeed -= PI;

			float flRotationVar = pParticle[iNum].flRotationSpeed;
			pParticle[iNum].vecRotation = vec3_t(float(cos(2*flRotationVar)), float(sin(2*flRotationVar)), float(0));
			vec3_t vecVelocity = pParticle[iNum].vecRotation;
			VectorNormalize(vecVelocity);
			vecVelocity = vecVelocity * 38;
			// Particle System origin + Rotation Velocity
			pParticle[iNum].vecOrigin =  pParticle[iNum].vecOrigin2 + vecVelocity;

			break;
		}
	case STEAM:
		{
			break;
		}
	case STEAM_HEAD:
		{
			break;
		}
	case DUSTTRAIL:
		{
			// Stop emitting particles after 0.5 seconds
			if(pPartManager->flDieTime == -1 && flTime - pPartManager->flTimeStart > 0.85)
				KillSystem(flTime + 2, pPartManager);

			// Collision check
			partsys_s *pPartSys = pPartManager->pPartSys;
			if(!pPartSys)
				break;
			particle_fx *pParticle = pPartSys->pParticle;
			if(!pParticle)
				break;

			// Increase Z velocity to give it a U shape
			pParticle[iNum].vecVelocity.z += 30 * flTimeDifference;

			if(!pParticle[iNum].blCollision)
			{
				// Stop the particle if it hits something
				if(CollisionCheck(pParticle[iNum].vecOrigin, pParticle[iNum].vecVelocity, pParticle[iNum].flSize/3 ))
				{	
					pParticle[iNum].flSpeed = 0;
					pParticle[iNum].vecVelocity = vec3_t(0,0,0);
					pParticle[iNum].blCollision = true;
				}
			}

			break;
		}
	case LIGHTING:
		{
			// Animate the sprite
			AnimateSprite( iNum, pPartManager );
			break;
		}
	case SPIRIT_BOMB:
		{
			partsys_s *pPartSys = pPartManager->pPartSys;
			if(!pPartSys)
				break;
			particle_fx *pParticle = pPartSys->pParticle;
			if(!pParticle)
				break;

			// If the particle has met the Spirit bomb, kill it
			if (Length(pPartSys->vecOrigin - pParticle[iNum].vecOrigin)<15)
				pParticle[iNum].flEnergy = 0;

			break;
		}
	case ROTATE_LGHTING_SPHERE:
		{	
			break;
		}
	case PURPLE_LGHTING_SPHERE:
		{	
			break;
		}
	case PINK_SOLID_SPHERE:
		{
			break;
		}
	case FLASHY_WHITE_AURA:
	case FLASHY_AURA:
		{	
			particle_fx *pParticle = pPartManager->pParticle;
			// Rotating second aura
			pParticle->vecRotation2.y -= pParticle->flRotationSpeed * flTimeDifference;
			if(pParticle->vecRotation2.y < -360)
			{
				pParticle->vecRotation2.y += 360;
			}
			break;
		}
	case WHITE_SPHERE:
		{	
			break;
		}
	case HARD_INNDER_SPHERE:
		{
			break;
		}
	case EXPLOSION_SPHERE:
	case SHOCKWAVE_FROM_CORE:
		{
			particle_fx *pParticle = pPartManager->pParticle;

			// Delete it when the shockwave runs out of energy
			if( pParticle->flEnergy <= 0 )
			{
				// telling the outside function to delete this
				pPartManager->m_iIndex = -1;
			}
			break;
		}
	case SHOCKWAVE_CORE:
		{
			break;
		}
	case SHOCKWAVE_CORE_INVERSE:
		{
			break;
		}
	case SWOOP_DUSTTRAIL:
		{
			break;
		}
	}

}

// Generalized Updating
void CParticleFx::Update( float flTime, float flTimeDifference, part_manager_s *pPartManager )
{
	switch(pPartManager->m_iFlag)
	{
	case FLOATING_ROCKS:
	case DUSTTRAIL:
	case DUST:
	case COLUMN_DUST:
	case ROTATING_DUST:
	case EXPANDED_DUST:
	case STEAM:
	case STEAM_HEAD:
	case LIGHTING:
	case SPIRIT_BOMB:
		{
			partsys_s *pPartSys = pPartManager->pPartSys;
			if(!pPartSys)
			{
				pPartManager->m_iIndex = -1;
				return;
			}

			particle_fx *pParticle = pPartSys->pParticle;
			if(!pParticle)
			{
				pPartManager->m_iIndex = -1;
				return;
			}
			for( int i = 0; i < pPartSys->iMaxParticles; i++ )
			{
				// Only bother with the particle if it has life
				if(pParticle[i].flEnergy)
				{
					// Subtract life
					if(pParticle[i].flEnergy != 0 && (pParticle[i].flEnergy != -1 || pParticle[i].flEnergyDelta != -1 ))
					{	
						pParticle[i].flEnergy -= pParticle[i].flEnergyDelta * flTimeDifference;
						// Make sure the energy doesnt go into the negatives
						if(pParticle[i].flEnergy < 0 )
						{	pParticle[i].flEnergy = 0;
						}
					}

					// Increase size
					if(pParticle[i].flSizeDelta != -1)
						pParticle[i].flSize += pParticle[i].flSizeDelta * flTimeDifference;

					// Make sure the particle still has some speed before we bother with velocity
					if(!pParticle[i].blCollision)
					{
						// Rotating dust is taken care of differently, dont use the regular velocity
						if(pPartManager->m_iFlag != COLUMN_DUST && pPartManager->m_iFlag != ROTATING_DUST )
							pParticle[i].vecOrigin = pParticle[i].vecOrigin +
								((pParticle[i].vecVelocity.Normalize() * pParticle[i].flSpeed) + (pPartSys->vecForce * pParticle[i].flWeight)) * flTimeDifference;
						
						// Specific particle updates
						UpdateSpecific( flTime, i, flTimeDifference, pPartManager );
					}
				}
			}
			break;
		}
	case ROTATE_LGHTING_SPHERE:
	case PURPLE_LGHTING_SPHERE:
	case PINK_SOLID_SPHERE:
	case FLASHY_AURA:
	case FLASHY_WHITE_AURA:
	case WHITE_SPHERE:
	case EXPLOSION_SPHERE:
	case SHOCKWAVE_FROM_CORE:
	case SHOCKWAVE_FROM_CORE_INVERSE:
	case HARD_INNDER_SPHERE:
		{	// Rotate
			particle_fx *pParticle = pPartManager->pParticle;

			if(pParticle->flRotationSpeed)
			{
				// Spin the object if it has a rotation speed
				pParticle->vecRotation.y += pParticle->flRotationSpeed * flTimeDifference;
				if(pParticle->vecRotation.y > 360)
				{	pParticle->vecRotation.y -= 360;
				}
				else if(pParticle->vecRotation.y < 0)
				{	pParticle->vecRotation.y += 360;
				}
			}

			// See if this thing has any type of Energy/Size characteristics
			// Subtract life
			if(pParticle->flEnergy != 0 && (pParticle->flEnergy != -1 || pParticle->flEnergyDelta != -1 ))
			{	
				pParticle->flEnergy -= pParticle->flEnergyDelta * flTimeDifference;
				// Make sure the energy doesnt go into the negatives
				if(pParticle->flEnergy < 0 )
				{	pParticle->flEnergy = 0;
				}
			}

			// Increase size
			if(pParticle->flSizeDelta != -1)
				pParticle->flSize += pParticle->flSizeDelta * flTimeDifference;

			UpdateSpecific( flTime, 0, flTimeDifference, pPartManager );

			break;
		}
	}
}

// -------------------
//  Remove Job System
// -------------------

part_manager_s *CParticleFx::FindPrevJob( int iIndex, int iFlag, float flTimeStart )
{
	part_manager_s *pPartManager = pPartJobs;

	// Lets search through everything... Return null if it doesnt exist
	while(pPartManager)
	{	
		if(pPartManager->pNext)
		{
			if(pPartManager->pNext->m_iIndex == iIndex && pPartManager->pNext->m_iFlag == iFlag &&
			pPartManager->pNext->flTimeStart == flTimeStart)
			{	return pPartManager;
			}
			else
			{	pPartManager = pPartManager->pNext;
			}
		}
		else
		{	break;
		}
	}

	return NULL;
}

part_manager_s *CParticleFx::RemoveSystem( part_manager_s *pPartManager )
{
//	ConsolePrint("Removed System\n");

	// Lets remove everything Inside the job first.
	if(pPartManager->pParticle)
	{
		delete pPartManager->pParticle;
		pPartManager->pParticle = NULL;
	}

	if(pPartManager->pPartSys)
	{
		if(pPartManager->pPartSys->pParticle)
		{
			delete [] pPartManager->pPartSys->pParticle;
			pPartManager->pPartSys->pParticle = NULL;
		}

		delete pPartManager->pPartSys;
		pPartManager->pPartSys = NULL;
	}

	// Delete the accual job
	// Lets find the one before the one we want to delete

	// First one in the list, take it out right away
	if(pPartManager == pPartJobs)
	{
		pPartManager = pPartManager->pNext;
		delete pPartJobs;
		pPartJobs = NULL;
		pPartJobs = pPartManager;
	//	ConsolePrint("Delete First Job\n");
		return pPartManager;
	}

	// Search...
	part_manager_s *pPrev = FindPrevJob( pPartManager->m_iIndex, pPartManager->m_iFlag, pPartManager->flTimeStart );

	// Make sure we found it
	if(!pPrev)
	{	return NULL;
	}

	// Delete the trans FX struct
	part_manager_s *pNext = pPartManager->pNext;
	delete pPartManager;
	pPartManager = NULL;
	pPrev->pNext = pNext;
	return pNext;
}

void CParticleFx::SearchAndDelete( int iType, int iIndex )
{
	// Lets Disable the particle system(s) linked to this player
	part_manager_s *pCurrent = pPartJobs;
	while(pCurrent)
	{
		// Linked to the player
		if(pCurrent->m_iIndex == iIndex)
		{
			// Dont delete Explosion spheres, they delete themselfs
			if(pCurrent->m_iFlag == iType)
			{
				if(pCurrent->flDieTime == -1)
				{
					if(pCurrent->pPartSys)
					{	
						KillSystem( flTime+4, pCurrent );
						pCurrent = pCurrent->pNext;
						continue;
					}
					else
					{
						pCurrent = RemoveSystem( pCurrent );
						continue;
					}
				}
			}
		}
		pCurrent = pCurrent->pNext;
	}
}

// ----------------
//  Draw/Update FX
// ----------------

void CParticleFx::Draw( float Time, float flTimeDifference )
{
	// Set Time
	flTime = Time;

	// Set quality...
	iQuality = cl_fxquality->value;
	if(iQuality > 3)
		iQuality = 3;
	if(iQuality < 1)
		iQuality = 1;

	part_manager_s *pCurrent = pPartJobs;

	while(pCurrent)
	{
		// First lets take care of regular FX that doesnt have a specific live/die time
		if(pCurrent->flTimeEnd == -1)
		{
			pCurrent->blDisplay = false;
			if(pCurrent->flDieTime != -1)
			{
				if(pCurrent->flDieTime <= flTime)
				{	pCurrent = RemoveSystem(pCurrent);
					continue;
				}
				else
				{	pCurrent->blDisplay = true;
				}
			}
			else
			{
				if(pCurrent->pPartSys)
						pCurrent->pPartSys->blvecOriginPlayer = false;

				// Something thats not transformation FX
				if(pCurrent->flTimeStart <= flTime)
				{
					pCurrent->blDisplay = true;
				}
				else
				{
					pCurrent->blDisplay = false;
					pCurrent = pCurrent->pNext;
					continue;
				}

			}
		}
		// Make sure its time to even display/Create this thing...
		else if((pCurrent->flTimeStart <= flTime && pCurrent->flTimeEnd >= flTime)
			|| pCurrent->flDieTime != -1 && pCurrent->flDieTime > flTime)
		{
			if(pCurrent->flDieTime == -1)
			{
				pCurrent->blDisplay = true;
			}
			else
			{
				if(pCurrent->flDieTime <= flTime)
				{
					// Remove the particle
					pCurrent = RemoveSystem(pCurrent);
					continue;
				}
				else
				{
					pCurrent->blDisplay = true;
				}
			}
		}
		else
		{	pCurrent->blDisplay = false;

			if(flTime >= pCurrent->flTimeEnd && pCurrent->flDieTime == -1)
			{
				// Add dying time if its a particle system
				if(pCurrent->pPartSys)
				{
					KillSystem(flTime + 5, pCurrent);
					pCurrent->blDisplay = true;
				}
				else
				{
					// Dont delete these, they will delete themselfs after they are done
					if(!(pCurrent->m_iFlag == EXPLOSION_SPHERE || pCurrent->m_iFlag == SHOCKWAVE_FROM_CORE || pCurrent->m_iFlag == SHOCKWAVE_FROM_CORE_INVERSE))
					{
						// Remove the particle
						pCurrent = RemoveSystem(pCurrent);
						continue;
					}
				}
			}
			else
			{
				if(pCurrent->flDieTime != -1 && pCurrent->flDieTime <= flTime)
				{
					pCurrent = RemoveSystem(pCurrent);
					continue;
				}
				if(pCurrent->flTimeStart > flTime)
				{
					pCurrent = pCurrent->pNext;
					continue;
				}
			}
		}

		// Used for Shockwave from core
		if(pCurrent->m_iFlag == SHOCKWAVE_FROM_CORE)
		{
			if(pCurrent->pParticle)
			{
				if(pCurrent->pParticle->flSize <= 10)
					pCurrent->blDisplay = false;
				else
					pCurrent->blDisplay = true;
			}
			else
			{	pCurrent = RemoveSystem(pCurrent);
				continue;
			}
		}
		// Used for Inverse Shockwave
		else if(pCurrent->m_iFlag == SHOCKWAVE_FROM_CORE_INVERSE)
		{
			if(pCurrent->pParticle)
			{
				if(pCurrent->pParticle->flSize <= 1)
				{	pCurrent = RemoveSystem(pCurrent);
					continue;
				}
				else
				{	pCurrent->blDisplay = true;
				}
			}
			else
			{	pCurrent = RemoveSystem(pCurrent);
				continue;
			}
		}
		else if(!pCurrent->blDisplay)
		{
			pCurrent = pCurrent->pNext;
			continue;
		}

		// See if its time to kill this FX 
		// - This should be killed by the transformation end message if its a single particle
		if(pCurrent->flTimeEnd != -1 && pCurrent->flTimeEnd <= flTime)
		{
			// If its a static particle, delete it off the bat
			if(pCurrent->pParticle)
			{
				if(pCurrent->flDieTime == -1)
				{
					// Dont delete these, they will delete themselfs after they are done
					if(!(pCurrent->m_iFlag == EXPLOSION_SPHERE || pCurrent->m_iFlag == SHOCKWAVE_FROM_CORE || pCurrent->m_iFlag == SHOCKWAVE_FROM_CORE_INVERSE))
					{
						pCurrent = RemoveSystem(pCurrent);
						continue;
					}
				}
			}
			// If its a particle system, that hasnt previously been sentanced to death, Kill it 5 seconds later
			// to give the stray particles a chance to die off
			else if( pCurrent->flDieTime == -1 && pCurrent->pPartSys )
			{	
				KillSystem( flTime + 5, pCurrent );
				pCurrent->blDisplay = true;
			}
		}

		// see if we should completly remove this system
		if(pCurrent->flDieTime != -1)
		{
			if(pCurrent->flDieTime <= flTime)
			{
				// Completly remove particle system
				pCurrent = RemoveSystem(pCurrent);
				continue;
			}
		}

		// Update the origin every frame...
		if(pCurrent->pPartSys)
		{	if(pCurrent->pPartSys->blvecOriginPlayer)
			{	cl_entity_t *pClient = gEngfuncs.GetEntityByIndex(pCurrent->m_iIndex);
				pCurrent->pPartSys->vecOrigin = pClient->origin;
			}
		}
		if(pCurrent->pParticle)
		{	cl_entity_t *pClient = gEngfuncs.GetEntityByIndex(pCurrent->m_iIndex);
			pCurrent->pParticle->vecOrigin = pClient->origin;
		}

		// Emit particles
		if(pCurrent->flEmitDelta != -1)
		{
			if(pCurrent->flDieTime == -1)
			{
				if(pCurrent->flEmitTime < flTime - pCurrent->flEmitDelta)
				{	Emit( pCurrent );
					pCurrent->flEmitTime = flTime;
				}
			}
		}

		// Lets update the particle(s)...
		Update( flTime, flTimeDifference, pCurrent );

		// This Job must be finished ( Before its terminated by its end time/termination from elsewere )
		// lets delete it
		if(pCurrent->m_iIndex == -1)
		{
			pCurrent = RemoveSystem(pCurrent);
			continue;
		}
		else
		{	pCurrent = pCurrent->pNext;
			continue;
		}
	}

}