/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// cl_effects.c -- particle and decal effects parsing/generation

#include "client.h"
#include "particles.h"

static vec3_t avelocities [NUMVERTEXNORMALS];


//=================================================

/*
===============
CL_LightningBeam
===============
*/
/*void CL_LightningBeam(vec3_t start, vec3_t end, int srcEnt, int dstEnt, float size)
{
	cparticle_t *list;
	cparticle_t *p=NULL;
	
	for (list=active_particles; list; list=list->next)
		if (list->src_ent == srcEnt && list->dst_ent == dstEnt )
		{
			p=list;
			p->time = cl.time;
			VectorCopy(start, p->angle);
			VectorCopy(end, p->org);

			if (p->link)
			{
				p->link->time = cl.time;
				VectorCopy(start, p->link->angle);
				VectorCopy(end, p->link->org);
			}
			else
			{
				p->link = CL_SetupParticle (
					start[0],	start[1],	start[2],
					end[0],		end[1],		end[2],
					0,	0,	0,
					0,		0,		0,
					150,	150,	255,
					0,	0,	0,
					1,		-1.0,
					size,		0,			
					particle_beam,
					PART_BEAM,
					NULL,0);
				
			}
			break;
		}

	if (p)
		return;

	p = CL_SetupParticle (
		start[0],	start[1],	start[2],
		end[0],		end[1],		end[2],
		0,	0,	0,
		0,		0,		0,
		115,	115,	255,
		0,	0,	0,
		1,		-1.0,
		size,		0,			
		particle_lightning,
		PART_LIGHTNING,
		NULL,0);

	p->src_ent=srcEnt;
	p->dst_ent=dstEnt;

	p->link = CL_SetupParticle (
		start[0],	start[1],	start[2],
		end[0],		end[1],		end[2],
		0,	0,	0,
		0,		0,		0,
		150,	150,	255,
		0,	0,	0,
		1,		-1.0,
		size,		0,			
		particle_beam,
		PART_BEAM,
		NULL,0);
}*/
void CL_LightningBeam (vec3_t start, vec3_t end, int srcEnt, int dstEnt, float size)
{
	cparticle_t *list;
	cparticle_t *p=NULL;

	for (list=active_particles; list; list=list->next)
		if (list->src_ent == srcEnt && list->dst_ent == dstEnt && list->image == particle_lightning)
		{
			p=list;
			/*p->start =*/ p->time = cl.time;
			VectorCopy(start, p->angle);
			VectorCopy(end, p->org);

			return;
		}

	p = CL_SetupParticle (
		start[0],	start[1],	start[2],
		end[0],		end[1],		end[2],
		0,	0,	0,
		0,		0,		0,
		255,	255,	255,
		0,	0,	0,
		1,		-2,
		GL_SRC_ALPHA, GL_ONE,
		size,		0,		
		particle_lightning,
		PART_LIGHTNING,
		0, false);

	if (!p)
		return;

	p->src_ent=srcEnt;
	p->dst_ent=dstEnt;
}


/*
===============
CL_Explosion_Decal
===============
*/
void CL_Explosion_Decal (vec3_t org, float size, int decalnum)
{
	if (r_decals->value)
	{
		int			i, j, offset=8;	//size/2
		cparticle_t	*p;
		vec3_t		angle[6], ang;
		trace_t		trace1, trace2;
		vec3_t		end1, end2, normal, sorg, dorg;
		vec3_t		planenormals[6];

		VectorSet(angle[0], -1, 0, 0);
		VectorSet(angle[1], 1, 0, 0);
		VectorSet(angle[2], 0, 1, 0);
		VectorSet(angle[3], 0, -1, 0);
		VectorSet(angle[4], 0, 0, 1);
		VectorSet(angle[5], 0, 0, -1);

		for (i=0; i<6; i++)
		{
			VectorMA(org, -offset, angle[i], sorg); // move origin 8 units back
			VectorMA(sorg, size/2+offset, angle[i], end1);
			trace1 = CL_Trace (sorg, end1, 0, CONTENTS_SOLID);
			if (trace1.fraction < 1) // hit a surface
			{	// make sure we haven't hit this plane before
				VectorCopy(trace1.plane.normal, planenormals[i]);
				for (j=0; j<i; j++)
					if (VectorCompare(planenormals[j],planenormals[i])) continue;
				// try tracing directly to hit plane
				VectorNegate(trace1.plane.normal, normal);
				VectorMA(sorg, size/2, normal, end2);
				trace2 = CL_Trace (sorg, end2, 0, CONTENTS_SOLID);
				// if seond trace hit same plane
				if (trace2.fraction < 1 && VectorCompare(trace2.plane.normal, trace1.plane.normal))
					VectorCopy(trace2.endpos, dorg);
				else
					VectorCopy(trace1.endpos, dorg);
				//if (CM_PointContents(dorg,0) & MASK_WATER) // no scorch marks underwater
				//	continue;
				VecToAngleRolled(normal, rand()%360, ang);
				p = CL_SetupParticle (
					ang[0],	ang[1],	ang[2],
					dorg[0],dorg[1],dorg[2],
					0,		0,		0,
					0,		0,		0,
					255,	255,	255,
					0,		0,		0,
					1,		-1/r_decal_life->value,
					GL_ZERO, GL_ONE_MINUS_SRC_ALPHA,
					size,		0,			
					decalnum, // particle_burnmark
					PART_SHADED|PART_DECAL|PART_ALPHACOLOR,
					CL_DecalAlphaThink, true);
			}
			/*VecToAngleRolled(angle[i], rand()%360, ang);
			p = CL_SetupParticle (
				ang[0],	ang[1],	ang[2],
				org[0],	org[1],	org[2],
				0,		0,		0,
				0,		0,		0,
				255,	255,	255,
				0,		0,		0,
				1,		-0.001,
				GL_ZERO, GL_ONE_MINUS_SRC_ALPHA,
				size,		0,			
				particle_burnmark,
				PART_SHADED|PART_DECAL|PART_ALPHACOLOR,
				CL_DecalAlphaThink, true);*/
		}
	}
}


/*
===============
CL_ExplosionThink
===============
*/
void CL_ExplosionThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{
	if (*alpha>.85)
		*image = particle_rexplosion1;
	else if (*alpha>.7)
		*image = particle_rexplosion2;
	else if (*alpha>.5)
		*image = particle_rexplosion3;
	else if (*alpha>.4)
		*image = particle_rexplosion4;
	else if (*alpha>.25)
		*image = particle_rexplosion5;
	else if (*alpha>.1)
		*image = particle_rexplosion6;
	else 
		*image = particle_rexplosion7;

	*alpha *= 3.0;

	if (*alpha > 1.0)
		*alpha = 1;

	p->thinknext = true;
}

/*
===============
CL_ExplosionBubbleThink
===============
*/
void CL_ExplosionBubbleThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{

	if (CM_PointContents(org,0) & MASK_WATER)
		p->thinknext = true;
	else
	{
		p->think = NULL;
		p->alpha = 0;
	}
}

/*
===============
CL_Explosion_Particle

Explosion effect
===============
*/
void CL_Explosion_Particle (vec3_t org, float size, qboolean rocket)
{
	cparticle_t *p;

	p = CL_SetupParticle (
		0,		0,		0,
		org[0],	org[1],	org[2],
		0,		0,		0,
		0,		0,		0,
		255,	255,	255,
		0,		0,		0,
		1,		(rocket)? -2 : -1.5,
		GL_SRC_ALPHA, GL_ONE,
		//GL_ONE, GL_ONE,
		(size!=0)?size:(150-(!rocket)?75:0),	0,			
		particle_rexplosion1,
		PART_DEPTHHACK_SHORT,
		CL_ExplosionThink, true);
	
	if (p)
	{	// smooth color blend :D
	/*	CL_AddParticleLight (p, 225, 0, 1, 0, 0);
		CL_AddParticleLight (p, 250, 0, 1, 0.3, 0);
		CL_AddParticleLight (p, 275, 0, 1, 0.6, 0);
		CL_AddParticleLight (p, 300, 0, 1, 1, 0);*/
		// use just one, 4 lights kills the framerate
		CL_AddParticleLight (p, 300, 0, 1, 0.514, 0);
	}
}

/*
===============
CL_Explosion_FlashParticle

Explosion fash
===============
*/
void CL_Explosion_FlashParticle (vec3_t org, float size, qboolean large)
{
	if (large)
	{
		CL_SetupParticle (
			0,		0,		0,
			org[0],	org[1],	org[2],
			0,		0,		0,
			0,		0,		0,
			255,	175,	100,
			0,		0,		0,
			1,		-1.75,
			GL_SRC_ALPHA, GL_ONE,
			//GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
			(size!=0)?size:50,	-10,			
			//100-(!rocket)?50:0,	-10,			
			particle_rflash,
			PART_DEPTHHACK_SHORT,
			NULL,0);
	}
	else
	{
		CL_SetupParticle (
			0,		0,		0,
			org[0],	org[1],	org[2],
			0,		0,		0,
			0,		0,		0,
			255,	175,	100,
			0,		0,		0,
			1,		-1.75,
			GL_SRC_ALPHA, GL_ONE,
			(size!=0)?size:50,	-10,	
			//100-(!rocket)?50:0,	-10,			
			particle_blaster,
			0,
			NULL,0);
	}
}


/*
===============
CL_ParticleExplosionSparksThink
===============
*/
void CL_ParticleExplosionSparksThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{
	int i;

	//setting up angle for sparks
	{
		float time1, time2;

		time1 = *time;
		time2 = time1*time1;

		for (i=0;i<2;i++)
			angle[i] = 0.25*(p->vel[i]*time1 + (p->accel[i])*time2);
		angle[2] = 0.25*(p->vel[2]*time1 + (p->accel[2]-PARTICLE_GRAVITY)*time2);
	}

	p->thinknext = true;
}

/*
===============
CL_Explosion_Sparks
===============
*/
void CL_Explosion_Sparks (vec3_t org, int size, int count)
{
	int	i;

	for (i=0; i < (count/cl_particle_scale->value); i++) // was 256
	{
		CL_SetupParticle (
			0,	0,	0,
			org[0] + ((rand()%size)-16),	org[1] + ((rand()%size)-16),	org[2] + ((rand()%size)-16),
			(rand()%150)-75,	(rand()%150)-75,	(rand()%150)-75,
			0,		0,		0,
			255,	100,	25,
			0,	0,	0,
			1,		-0.8 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			size,		size*-1.5f,		// was 6, -9
			particle_solid,
			PART_GRAVITY|PART_SPARK,
			CL_ParticleExplosionSparksThink, true);
	}
}


/*
=====================

Blood effects

=====================
*/
void CL_ParticleBloodThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time);
void CL_BloodPuff (vec3_t org, vec3_t dir, int count);

#define MAXBLEEDSIZE 5
#define TIMEBLOODGROW 2.5f
#define BLOOD_DECAL_CHANCE 0.5F

/*
===============
CL_ParticleBloodDecalThink
===============
*/
void CL_ParticleBloodDecalThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{	// This REALLY slows things down
	/*if (*time<TIMEBLOODGROW)
	{
		vec3_t dir;

		*size *= sqrt(0.5 + 0.5*(*time/TIMEBLOODGROW));

		AngleVectors (angle, dir, NULL, NULL);
		VectorNegate(dir, dir);
		CL_ClipDecal(p, *size, angle[2], org, dir);
	}*/

	//now calc alpha
	CL_DecalAlphaThink (p, org, angle, alpha, size, image, time);
}

/*
===============
CL_ParticleBloodDropThink
===============
*/
void CL_ParticleBloodDropThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{
	float length;
	vec3_t len;

	VectorSubtract(p->angle, org, len);
	{
		CL_CalcPartVelocity(p, 0.2, time, angle);

		length = VectorNormalize(angle);
		if (length>MAXBLEEDSIZE) length = MAXBLEEDSIZE;
		VectorScale(angle, -length, angle);
	}

	//now to trace for impact...
	CL_ParticleBloodThink (p, org, angle, alpha, size, image, time);
}

/*
===============
CL_ParticleBloodPuffThink
===============
*/
void CL_ParticleBloodPuffThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{
	angle[2] =	angle[0] + *time*angle[1] + *time**time*angle[2];

	//now to trace for impact...
	CL_ParticleBloodThink (p, org, angle, alpha, size, image, time);
}

/*
===============
CL_ParticleBloodThink
===============
*/
void CL_ParticleBloodThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{
	trace_t	trace = CL_Trace (p->oldorg, org, 0, CONTENTS_SOLID); // was 0.1
	qboolean became_decal = false;

	if (trace.fraction < 1.0) // delete and stain...
	{
		if (r_decals->value && (p->flags & PART_LEAVEMARK)
			&& !VectorCompare(trace.plane.normal, vec3_origin)
			&& !(CM_PointContents(p->oldorg,0) & MASK_WATER)) // no blood splatters underwater...
		{
			vec3_t	normal, dir;
			int		i;
			qboolean greenblood = false;
			qboolean timedout = false;
			if (p->color[1] > 0 && p->color[2] > 0)
				greenblood = true;
			// time cutoff for gib trails
			if (p->flags & PART_GRAVITY && !(p->flags & PART_DIRECTION))
			{	// gekk gibs go flyin faster...
				if ((greenblood) && (cl.time - p->time)*0.001 > 1.0F)
					timedout = true;
				if ((!greenblood) && (cl.time - p->time)*0.001 > 0.5F)
					timedout = true;
			}

			if (!timedout)
			{
				VectorNegate(trace.plane.normal, normal);
				VecToAngleRolled(normal, rand()%360, p->angle);
				
				VectorCopy(trace.endpos, p->org);
				VectorClear(p->vel);
				VectorClear(p->accel);
				p->image = CL_GetRandomBloodParticle();
				p->blendfunc_src = GL_SRC_ALPHA; //GL_ZERO
				p->blendfunc_dst = GL_ONE_MINUS_SRC_ALPHA; //GL_ONE_MINUS_SRC_COLOR
				p->flags = PART_DECAL|PART_SHADED|PART_ALPHACOLOR;
				p->alpha = *alpha;
				p->alphavel = -1/r_decal_life->value;
				if (greenblood)
					p->color[1] = 210;
				else
					for (i=0; i<3; i++)
						p->color[i] *= 0.5;
				p->start = CL_NewParticleTime();
				p->think = CL_ParticleBloodDecalThink;
				p->thinknext = true;
				p->size = MAXBLEEDSIZE*0.5*(random()*5.0+5);
				//p->size = *size*(random()*5.0+5);
				p->sizevel = 0;
				
				p->decalnum = 0;
				p->decal = NULL;
				AngleVectors (p->angle, dir, NULL, NULL);
				VectorNegate(dir, dir);
				CL_ClipDecal(p, p->size, -p->angle[2], p->org, dir);
				if (p->decalnum)
					became_decal = true;
				//else
				//	Com_Printf(S_COLOR_YELLOW"Blood decal not clipped!\n");
			}
		}
		if (!became_decal)
		{
			*alpha = 0;
			*size = 0;
			p->alpha = 0;
		}
	}
	VectorCopy(org, p->oldorg);

	p->thinknext = true;
}


/*
===============
CL_BloodSmack
===============
*/
void CL_BloodSmack (vec3_t org, vec3_t dir)
{
	cparticle_t *p;

	p = CL_SetupParticle (
		crand()*180, crand()*100, 0,
		org[0],	org[1],	org[2],
		dir[0],	dir[1],	dir[2],
		0,		0,		0,
		255,	0,		0,
		0,		0,		0,
		1.0,		-1 / (0.5 + frand()*0.3), //was -0.75
		GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
		10,			0,			
		particle_redblood,
		PART_SHADED|PART_OVERBRIGHT,
		CL_ParticleRotateThink,true);

	CL_BloodPuff(org, dir, 1);
}


/*
===============
CL_BloodBleed
===============
*/
void CL_BloodBleed (vec3_t org, vec3_t dir, int count)
{
	cparticle_t *p;
	vec3_t	pos;
	int		i;

	VectorScale(dir, 10, pos);
	for (i=0; i<count; i++)
	{
		VectorSet(pos,
			dir[0]+random()*(cl_blood->value-2)*0.01,
			dir[1]+random()*(cl_blood->value-2)*0.01,
			dir[2]+random()*(cl_blood->value-2)*0.01);
		VectorScale(pos, 10 + (cl_blood->value-2)*0.0001*random(), pos);

		p = CL_SetupParticle (
			org[0], org[1], org[2],
			org[0] + ((rand()&7)-4) + dir[0],	org[1] + ((rand()&7)-4) + dir[1],	org[2] + ((rand()&7)-4) + dir[2],
			pos[0]*(random()*3+5),	pos[1]*(random()*3+5),	pos[2]*(random()*3+5),
			0,		0,		0,
			255,	0,		0,
			0,		0,		0,
			0.7,		-0.25 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
			MAXBLEEDSIZE*0.5,		0,			
			particle_blooddrip,
			PART_SHADED|PART_DIRECTION|PART_GRAVITY|PART_OVERBRIGHT,
			CL_ParticleBloodDropThink,true);

		if (p && i == 0 && random() < BLOOD_DECAL_CHANCE)
			p->flags |= PART_LEAVEMARK;
	}

}

/*
===============
CL_BloodPuff
===============
*/
void CL_BloodPuff (vec3_t org, vec3_t dir, int count)
{
	cparticle_t *p;
	int		i;
	float	d;

	for (i=0; i<count; i++)
	{
		d = rand()&31;
		p = CL_SetupParticle (
			crand()*180, crand()*100, 0,
			org[0] + ((rand()&7)-4) + d*dir[0],	org[1] + ((rand()&7)-4) + d*dir[1],	org[2] + ((rand()&7)-4) + d*dir[2],
			dir[0]*(crand()*3+5),	dir[1]*(crand()*3+5),	dir[2]*(crand()*3+5),
			0,			0,			-100,
			255,		0,			0,
			0,			0,			0,
			1.0,		-1.0,
			GL_SRC_ALPHA, GL_ONE,
			10,			0,
			particle_blood,
			PART_SHADED,
			CL_ParticleBloodPuffThink,true);

		if (p && i == 0 && random() < BLOOD_DECAL_CHANCE)
			p->flags |= PART_LEAVEMARK;
	}
}

/*
===============
CL_BloodHit
===============
*/
void CL_BloodHit (vec3_t org, vec3_t dir)
{
	if (cl_blood->value < 1) // disable blood option
		return;
	if (cl_blood->value == 2) // splat
		CL_BloodSmack(org, dir);
	else if (cl_blood->value == 3) // bleed
		CL_BloodBleed (org, dir, 6);
	else if (cl_blood->value == 4) // gore
		CL_BloodBleed (org, dir, 16);
	else // 1 = puff
		CL_BloodPuff(org, dir, 5);
}

/*
==================
CL_GreenBloodHit

green blood spray
==================
*/
void CL_GreenBloodHit (vec3_t org, vec3_t dir)
{
	cparticle_t *p;
	int		i;
	float	d;

	if (cl_blood->value < 1) // disable blood option
		return;

	for (i=0;i<5;i++)
	{
		d = rand()&31;
		p = CL_SetupParticle (
				crand()*180, crand()*100, 0,
				org[0] + ((rand()&7)-4) + d*dir[0],	org[1] + ((rand()&7)-4) + d*dir[1],	org[2] + ((rand()&7)-4) + d*dir[2],
				dir[0]*(crand()*3+5),	dir[1]*(crand()*3+5),	dir[2]*(crand()*3+5),
				0,		0,		-100,
				255,	180,	50,
				0,		0,		0,
				1,		-1.0,
				GL_SRC_ALPHA, GL_ONE,
				10,			0,			
				particle_blood,
				PART_SHADED|PART_OVERBRIGHT,
				CL_ParticleBloodPuffThink,true);

		if (p && i == 0 && random() < BLOOD_DECAL_CHANCE)
			p->flags |= PART_LEAVEMARK;
	}

}

/*
===============
CL_ParticleEffect

Wall impact puffs
===============
*/
void CL_ParticleEffect (vec3_t org, vec3_t dir, int color8, int count)
{
	int			i;
	float		d;
	vec3_t color = { color8red(color8), color8green(color8), color8blue(color8)};

	for (i=0 ; i<count ; i++)
	{
		d = rand()&31;
		CL_SetupParticle (
			0,		0,		0,
			org[0] + ((rand()&7)-4) + d*dir[0],	org[1] + ((rand()&7)-4) + d*dir[1],	org[2] + ((rand()&7)-4) + d*dir[2],
			crand()*20,			crand()*20,			crand()*20,
			0,		0,		0,
			color[0],		color[1],		color[2],
			0,	0,	0,
			1.0,		-1.0 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			1,			0,			
			particle_generic,
			PART_GRAVITY,
			NULL,0);
	}
}

/*
===============
CL_ParticleEffect2
===============
*/

#define colorAdd 25
void CL_ParticleEffect2 (vec3_t org, vec3_t dir, int color8, int count)
{
	int			i;
	float		d;
	vec3_t color = { color8red(color8), color8green(color8), color8blue(color8)};

	for (i=0 ; i<count ; i++)
	{
		d = rand()&7;
		CL_SetupParticle (
			0,	0,	0,
			org[0]+((rand()&7)-4)+d*dir[0],	org[1]+((rand()&7)-4)+d*dir[1],	org[2]+((rand()&7)-4)+d*dir[2],
			crand()*20,			crand()*20,			crand()*20,
			0,		0,		0,
			color[0] + colorAdd,		color[1] + colorAdd,		color[2] + colorAdd,
			0,	0,	0,
			1,		-1.0 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			1,			0,			
			particle_generic,
			PART_GRAVITY,
			NULL,0);
	}
}

// RAFAEL
/*
===============
CL_ParticleEffect3
===============
*/

void CL_ParticleEffect3 (vec3_t org, vec3_t dir, int color8, int count)
{
	int			i;
	float		d;
	vec3_t color = { color8red(color8), color8green(color8), color8blue(color8)};

	for (i=0 ; i<count ; i++)
	{
		d = rand()&7;
		CL_SetupParticle (
			0,	0,	0,
			org[0]+((rand()&7)-4)+d*dir[0],	org[1]+((rand()&7)-4)+d*dir[1],	org[2]+((rand()&7)-4)+d*dir[2],
			crand()*20,			crand()*20,			crand()*20,
			0,		0,		0,
			color[0] + colorAdd,		color[1] + colorAdd,		color[2] + colorAdd,
			0,	0,	0,
			1,		-0.25 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			2,			-0.25,			
			particle_generic,
			PART_GRAVITY,
			NULL, false);
	}
}


/*
===============
CL_ParticleSplashThink
===============
*/
#define SplashSize 7.5
void CL_ParticleSplashThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{
	int i;
	vec3_t len;
	VectorSubtract(p->angle, org, len);
	
//	*size *= (float)(SplashSize/VectorLength(len)) * 0.5/((4-*size));
//	if (*size > SplashSize)
//		*size = SplashSize;

	//setting up angle for sparks
	{
		float time1, time2;

		time1 = *time;
		time2 = time1*time1;

		for (i=0;i<2;i++)
			angle[i] = 0.5*(p->vel[i]*time1 + (p->accel[i])*time2);
		angle[2] = 0.5*(p->vel[2]*time1 + (p->accel[2]-PARTICLE_GRAVITY)*time2);
	}

	p->thinknext = true;
}

/*
===============
CL_ParticleEffectSplash

Water Splashing
===============
*/
void CL_ParticleEffectSplash (vec3_t org, vec3_t dir, int color8, int count)
{
	int			i;
	float		d;
	vec3_t color = {color8red(color8), color8green(color8), color8blue(color8)};

	for (i=0 ; i<count ; i++)
	{
		d = rand()&5;
		CL_SetupParticle (
			org[0],	org[1],	org[2],
			org[0]+d*dir[0],	org[1]+d*dir[1],	org[2]+d*dir[2],
			dir[0]*40 + crand()*10,	dir[1]*40 + crand()*10,	dir[2]*40 + crand()*10,
			0,		0,		0,
			color[0],	color[1],	color[2],
			0,	0,	0,
			1,		-0.75 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			5,			-7,			
			particle_smoke,
			PART_GRAVITY|PART_DIRECTION   /*|PART_TRANS|PART_SHADED*/,
			CL_ParticleSplashThink,true);
	}
}

/*
===============
CL_ParticleSparksThink
===============
*/
void CL_ParticleSparksThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{
	//vec3_t dir;
	int i;

	//setting up angle for sparks
	{
		float time1, time2;

		time1 = *time;
		time2 = time1*time1;

		for (i=0;i<2;i++)
			angle[i] = 0.25*(p->vel[i]*time1 + (p->accel[i])*time2);
		angle[2] = 0.25*(p->vel[2]*time1 + (p->accel[2]-PARTICLE_GRAVITY)*time2);
	}

	p->thinknext = true;
}

/*
===============
CL_ParticleEffectSparks
===============
*/
void CL_ParticleEffectSparks (vec3_t org, vec3_t dir, vec3_t color, int count)
{
	int			i;
	float		d;
	cparticle_t *p;

	for (i=0 ; i<count ; i++)
	{
		d = rand()&7;
		p = CL_SetupParticle (
			0,	0,	0,
			org[0]+((rand()&3)-2),	org[1]+((rand()&3)-2),	org[2]+((rand()&3)-2),
			crand()*20 + dir[0]*40,			crand()*20 + dir[1]*40,			crand()*20 + dir[2]*40,
			0,		0,		0,
			color[0],		color[1],		color[2],
			0,	0,	0,
			0.75,		-1.0 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			4,			0, //Knightmare- increase size
			particle_solid,
			PART_GRAVITY|PART_SPARK,
			CL_ParticleSparksThink,true);
	}
	if (p) // added light effect
		CL_AddParticleLight (p, (count>8)?130:65, 0, color[0]/255, color[1]/255, color[2]/255);
}


/*
===============
CL_ParticleBulletDecal
===============
*/
#define DECAL_OFFSET 0.5f
void CL_ParticleBulletDecal (vec3_t org, vec3_t dir, float size)
{
	cparticle_t	*p;
	vec3_t		ang, angle, end, origin;
	trace_t		tr;

	if (!r_decals->value)
		return;

	VectorMA(org, DECAL_OFFSET, dir, origin);
	VectorMA(org, -DECAL_OFFSET, dir, end);
	tr = CL_Trace (origin, end, 0, CONTENTS_SOLID);
	//tr = CL_Trace (origin, end, 1, 1);

	if (tr.fraction == 1)
	//if (!tr.allsolid)
		return;

	VectorNegate(tr.plane.normal, angle);
	//VectorNegate(dir, angle);
	VecToAngleRolled(angle, rand()%360, ang);
	VectorCopy(tr.endpos, origin);

	p = CL_SetupParticle (
		ang[0],	ang[1],	ang[2],
		origin[0],	origin[1],	origin[2],
		0,		0,		0,
		0,		0,		0,
		255,	255,	255,
		0,		0,		0,
		1,		-1/r_decal_life->value,
		GL_ZERO, GL_ONE_MINUS_SRC_ALPHA,
		size,			0,			
		particle_bulletmark,
		PART_SHADED|PART_DECAL|PART_ALPHACOLOR, // was part_saturate
		CL_DecalAlphaThink, true);
}


/*
===============
CL_ParticleRailDecal
===============
*/
#define RAIL_DECAL_OFFSET 2.0f
void CL_ParticleRailDecal (vec3_t org, vec3_t dir, float size, qboolean isRed)
{
	vec3_t		ang, angle, end, origin;
	trace_t		tr;

	if (!r_decals->value)
		return;

	VectorMA(org, -RAIL_DECAL_OFFSET, dir, origin);
	VectorMA(org, 2*RAIL_DECAL_OFFSET, dir, end);
	tr = CL_Trace (origin, end, 0, CONTENTS_SOLID);

	if (tr.fraction==1)
		return;
	if (VectorCompare(tr.plane.normal, vec3_origin))
		return;

	VectorNegate(tr.plane.normal, angle);
	VecToAngleRolled(angle, rand()%360, ang);
	VectorCopy(tr.endpos, origin);

	CL_SetupParticle (
		ang[0],	ang[1],	ang[2],
		origin[0],	origin[1],	origin[2],
		0,		0,		0,
		0,		0,		0,
		255,	255,	255,
		0,		0,		0,
		1,		-1/r_decal_life->value,
		GL_ZERO, GL_ONE_MINUS_SRC_ALPHA,
		size,			0,			
		particle_bulletmark,
		PART_SHADED|PART_DECAL|PART_ALPHACOLOR,
		CL_DecalAlphaThink, true);

	CL_SetupParticle (
		ang[0],	ang[1],	ang[2],
		origin[0],	origin[1],	origin[2],
		0,		0,		0,
		0,		0,		0,
		(isRed)?255:cl_railred->value,	(isRed)?20:cl_railgreen->value,	(isRed)?20:cl_railblue->value,
		0,		0,		0,
		1,		-0.25,
		GL_SRC_ALPHA, GL_ONE,
		size,			0,			
		particle_generic,
		PART_DECAL,
		NULL, false);

	CL_SetupParticle (
		ang[0],	ang[1],	ang[2],
		origin[0],	origin[1],	origin[2],
		0,		0,		0,
		0,		0,		0,
		255,	255,	255,
		0,		0,		0,
		1,		-0.25,
		GL_SRC_ALPHA, GL_ONE,
		size*0.67,		0,			
		particle_generic,
		PART_DECAL,
		NULL, false);
}


/*
===============
CL_ParticleBlasterDecal
===============
*/
void CL_ParticleBlasterDecal (vec3_t org, vec3_t dir, float size, int red, int green, int blue)
{
	cparticle_t	*p;
	vec3_t		ang, angle, end, origin;
	trace_t		tr;

	if (!r_decals->value)
		return;
 
	VectorMA(org, DECAL_OFFSET, dir, origin);
	VectorMA(org, -DECAL_OFFSET, dir, end);
	tr = CL_Trace (origin, end, 0, CONTENTS_SOLID);

	if (tr.fraction==1)
		return;
	if (VectorCompare(tr.plane.normal, vec3_origin))
		return;

	VectorNegate(tr.plane.normal, angle);
	VecToAngleRolled(angle, rand()%360, ang);
	VectorCopy(tr.endpos, origin);

	p = CL_SetupParticle (
		ang[0],	ang[1],	ang[2],
		origin[0],	origin[1],	origin[2],
		0,		0,		0,
		0,		0,		0,
		255,	255,	255,
		0,		0,		0,
		0.7,	-1/r_decal_life->value,
		GL_ZERO, GL_ONE_MINUS_SRC_ALPHA,
		size,		0,			
		particle_shadow,
		PART_SHADED|PART_DECAL,
		NULL, false);

	p = CL_SetupParticle (
		ang[0],	ang[1],	ang[2],
		origin[0],	origin[1],	origin[2],
		0,		0,		0,
		0,		0,		0,
		red,	green,	blue,
		0,		0,		0,
		1,		-0.3,
		GL_SRC_ALPHA, GL_ONE,
		size*0.4,	0,			
		particle_generic,
		PART_SHADED|PART_DECAL,
		NULL, false);

	p = CL_SetupParticle (
		ang[0],	ang[1],	ang[2],
		origin[0],	origin[1],	origin[2],
		0,		0,		0,
		0,		0,		0,
		red,	green,	blue,
		0,		0,		0,
		1,		-0.6,
		GL_SRC_ALPHA, GL_ONE,
		size*0.3,	0,			
		particle_generic,
		PART_SHADED|PART_DECAL,
		NULL, false);
}


/*
===============
CL_ParticlePlasmaBeamDecal
===============
*/
void CL_ParticlePlasmaBeamDecal (vec3_t org, vec3_t dir, float size)
{
	cparticle_t	*p;
	vec3_t		ang, angle, end, origin;
	trace_t		tr;

	if (!r_decals->value)
		return;
 
	VectorMA(org, DECAL_OFFSET, dir, origin);
	VectorMA(org, -DECAL_OFFSET, dir, end);
	tr = CL_Trace (origin, end, 0, CONTENTS_SOLID);

	if (tr.fraction==1)
		return;
	if (VectorCompare(tr.plane.normal, vec3_origin))
		return;

	VectorNegate(tr.plane.normal, angle);
	VecToAngleRolled(angle, rand()%360, ang);
	VectorCopy(tr.endpos, origin);

	p = CL_SetupParticle (
		ang[0],	ang[1],	ang[2],
		origin[0],	origin[1],	origin[2],
		0,		0,		0,
		0,		0,		0,
		255,	255,	255,
		0,		0,		0,
		0.85,	-1/r_decal_life->value,
		GL_ZERO, GL_ONE_MINUS_SRC_ALPHA,
		size,		0,			
		particle_shadow,
		PART_SHADED|PART_DECAL,
		NULL, false);
}


/*
===============
CL_TeleporterParticles
===============
*/
void CL_TeleporterParticles (entity_state_t *ent)
{
	int			i;

	for (i = 0; i < 8; i++)
	{
		CL_SetupParticle (
			0,	0,	0,
			ent->origin[0]-16+(rand()&31),	ent->origin[1]-16+(rand()&31),	ent->origin[2]-16+(rand()&31),
			crand()*14,		crand()*14,		80 + (rand()&7),
			0,		0,		0,
			230+crand()*25,	125+crand()*25,	25+crand()*25,
			0,		0,		0,
			1,		-0.5,
			GL_SRC_ALPHA, GL_ONE,
			2,		0,			
			particle_generic,
			PART_GRAVITY,
			NULL,0);
	}
}


/*
===============
CL_LogoutEffect
===============
*/
void CL_LogoutEffect (vec3_t org, int type)
{
	int			i;
	vec3_t	color;

	for (i=0 ; i<500 ; i++)
	{
		if (type == MZ_LOGIN)// green
		{
			color[0] = 20;
			color[1] = 200;
			color[2] = 20;
		}
		else if (type == MZ_LOGOUT)// red
		{
			color[0] = 200;
			color[1] = 20;
			color[2] = 20;
		}
		else// yellow
		{
			color[0] = 200;
			color[1] = 200;
			color[2] = 20;
		}
		
		CL_SetupParticle (
			0,	0,	0,
			org[0] - 16 + frand()*32,	org[1] - 16 + frand()*32,	org[2] - 24 + frand()*56,
			crand()*20,			crand()*20,			crand()*20,
			0,		0,		0,
			color[0],		color[1],		color[2],
			0,	0,	0,
			1,		-1.0 / (1.0 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			1,			0,			
			particle_generic,
			PART_GRAVITY,
			NULL,0);
	}
}


/*
===============
CL_ItemRespawnParticles
===============
*/
void CL_ItemRespawnParticles (vec3_t org)
{
	int			i;

	for (i=0 ; i<64 ; i++)
	{
		CL_SetupParticle (
			0,	0,	0,
			org[0] + crand()*8,	org[1] + crand()*8,	org[2] + crand()*8,
			crand()*8,			crand()*8,			crand()*8,
			0,		0,		PARTICLE_GRAVITY*0.2,
			0,		150+rand()*25,		0,
			0,	0,	0,
			1,		-1.0 / (1.0 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			1,			0,			
			particle_generic,
			PART_GRAVITY,
			NULL,0);
	}
}


/*
===============
CL_BigTeleportParticles
===============
*/
void CL_BigTeleportParticles (vec3_t org)
{
	int			i, index;
	float		angle, dist;
	static int colortable0[4] = {10,50,150,50};
	static int colortable1[4] = {150,150,50,10};
	static int colortable2[4] = {50,10,10,150};

	for (i=0; i<(1024/cl_particle_scale->value); i++) // was 4096
	{

		index = rand()&3;
		angle = M_PI*2*(rand()&1023)/1023.0;
		dist = rand()&31;
		CL_SetupParticle (
			0,	0,	0,
			org[0]+cos(angle)*dist,	org[1] + sin(angle)*dist,org[2] + 8 + (rand()%90),
			cos(angle)*(70+(rand()&63)),sin(angle)*(70+(rand()&63)),-100 + (rand()&31),
			-cos(angle)*100,	-sin(angle)*100,PARTICLE_GRAVITY*4,
			colortable0[index],	colortable1[index],	colortable2[index],
			0,	0,	0,
			1,		-0.1 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			5,		0.15 / (0.5 + frand()*0.3),	 // was 2, 0.05	
			particle_generic,
			0,
			NULL,0);
	}
}


/*
===============
CL_ParticleBlasterThink

Wall impact puffs
===============
*/
#define pBlasterMaxVelocity 100
#define pBlasterMinSize 1.0
#define pBlasterMaxSize 5.0

void CL_ParticleBlasterThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{
	vec_t  length;
	vec3_t len;
	float clipsize = 1.0;
	VectorSubtract(p->angle, org, len);

	*size *= (float)(pBlasterMaxSize/VectorLength(len)) * 1.0/((4-*size));
	*size += *time * p->sizevel;

	if (*size > pBlasterMaxSize)
		*size = pBlasterMaxSize;
	if (*size < pBlasterMinSize)
		*size = pBlasterMinSize;

	CL_ParticleBounceThink (p, org, angle, alpha, &clipsize, image, time); // was size

	length = VectorNormalize(p->vel);
	if (length>pBlasterMaxVelocity)
		VectorScale(p->vel,	pBlasterMaxVelocity,	p->vel);
	else
		VectorScale(p->vel,	length, p->vel);

/*	vec3_t len;
	VectorSubtract(p->angle, org, len);
	
	*size *= (float)(pBlasterMaxSize/VectorLength(len)) * 1.0/((4-*size));
	if (*size > pBlasterMaxSize)
		*size = pBlasterMaxSize;
	
	p->thinknext = true;*/
}


/*
===============
CL_BlasterParticles

Wall impact puffs
===============
*/
void CL_BlasterParticles (vec3_t org, vec3_t dir, int count, float size,
		int red, int green, int blue, int reddelta, int greendelta, int bluedelta)
{
	int			i;
	float		speed = .75;
	cparticle_t *p;
	vec3_t		origin;

	for (i = 0; i < count; i++)
	{
		VectorSet(origin,
			org[0] + dir[0]*(1 + random()*3 + pBlasterMaxSize/2.0),
			org[1] + dir[1]*(1 + random()*3 + pBlasterMaxSize/2.0),
			org[2] + dir[2]*(1 + random()*3 + pBlasterMaxSize/2.0)
			);

		p = CL_SetupParticle (
			org[0],	org[1],	org[2],
			origin[0],	origin[1],	origin[2],
			(dir[0]*75 + crand()*40)*speed,	(dir[1]*75 + crand()*40)*speed,	(dir[2]*75 + crand()*40)*speed,
			0,		0,		0,
			red,		green,		blue,
			reddelta,	greendelta,	bluedelta,
			1,		-0.5 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			size,	size*-0.125,		// was 4, -0.5
			particle_generic,
			PART_GRAVITY,
			CL_ParticleBlasterThink,true);
	
	/*	d = rand()&5;
		p = CL_SetupParticle (
			org[0],	org[1],	org[2],
			org[0]+((rand()&5)-2)+d*dir[0],	org[1]+((rand()&5)-2)+d*dir[1],	org[2]+((rand()&5)-2)+d*dir[2],
			(dir[0]*50 + crand()*20)*speed,	(dir[1]*50 + crand()*20)*speed,	(dir[2]*50 + crand()*20)*speed,
			0,			0,			0,
			red,		green,		blue,
			reddelta,	greendelta,	bluedelta,
			1,		-1.0 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			4,		-1.0,
			particle_generic,
			PART_GRAVITY,
			CL_ParticleBlasterThink,true);*/
	}
	if (p) // added light effect
		CL_AddParticleLight (p, 150, 0, ((float)red)/255, ((float)green)/255, ((float)blue)/255);
}


/*
===============
CL_BlasterTrail
===============
*/
void CL_BlasterTrail (vec3_t start, vec3_t end, int red, int green, int blue,
									int reddelta, int greendelta, int bluedelta)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	int			dec;

	VectorCopy (start, move);
//	VectorSubtract (end, start, vec);
	VectorSubtract (start, end, vec);
	len = VectorNormalize (vec);
	VectorMA (move, -5.0f, vec, move);

	dec = 4 * cl_particle_scale->value; 
	VectorScale (vec, dec, vec);

	// FIXME: this is a really silly way to have a loop
	while (len > 0)
	{
		len -= dec;

		CL_SetupParticle (
			0,			0,			0,
			move[0] + crand(),	move[1] + crand(),	move[2] + crand(),
			crand()*5,	crand()*5,	crand()*5,
			0,			0,			0,
			red,		green,		blue,
			reddelta,	greendelta,	bluedelta,
			1,			-1.0 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			3,			-7,	// was 4, -6;
			particle_generic,
			0,
			NULL,0);

		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_HyperBlasterGlow

Hyperblaster particle glow effect
===============
*/
#if 1
void CL_HyperBlasterGlow (vec3_t start, vec3_t end, int red, int green, int blue,
										int reddelta, int greendelta, int bluedelta)
{
	vec3_t		move, vec;
	float		len, dec, size;
	int			i;

	VectorCopy (start, move);
	VectorSubtract (start, end, vec);
	len = VectorNormalize (vec);
	VectorMA (move, -16.5f, vec, move);

	dec = 3.0f; // was 1, 5
	VectorScale (vec, dec, vec);

	for (i = 0; i < 12; i++) // was 18
	{
		size = 4.2f - (0.1f*(float)i);

		CL_SetupParticle (
			0,			0,			0,
			move[0] + 0.5*crand(),	move[1] + 0.5*crand(),	move[2] + 0.5*crand(),
			crand()*5,	crand()*5,	crand()*5,
			0,			0,			0,
			red,		green,		blue,
			reddelta,	greendelta,	bluedelta,
			1,			INSTANT_PARTICLE, // was -16.0 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			size,			0, // was 3, -36; 5, -60
			particle_generic,
			0,
			NULL,0);

		VectorAdd (move, vec, move);
	}
}
#else
void CL_HyperBlasterTrail (vec3_t start, vec3_t end, int red, int green, int blue,
										int reddelta, int greendelta, int bluedelta)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	int			dec;
	int			i;

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	VectorMA (move, 0.25, vec, move); // was 0.5
	len = VectorNormalize (vec);

	dec = 5; // was 1
	VectorScale (vec, dec, vec);

	for (i = 0; i < 5; i++) // was 18
	{
		len -= dec;

		CL_SetupParticle (
			0,			0,			0,
			move[0] + 0.5*crand(),	move[1] + 0.5*crand(),	move[2] + 0.5*crand(),
			crand()*5,	crand()*5,	crand()*5,
			0,			0,			0,
			red,		green,		blue,
			reddelta,	greendelta,	bluedelta,
			1,			-16.0 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			5,			-60, // was 3, -36			
			particle_generic,
			0,
			NULL,0);

		VectorAdd (move, vec, move);
	}
}
#endif

/*
===============
CL_BlasterTracer 
===============
*/
void CL_BlasterTracer (vec3_t origin, vec3_t angle, int red, int green, int blue, float len, float size)
{
//	int		i;
	vec3_t	dir;
	
	AngleVectors (angle, dir, NULL, NULL);
	VectorScale (dir, len,dir);

	//for (i=0; i<3; i++)
	CL_SetupParticle (
		dir[0],	dir[1],	dir[2],
		origin[0],	origin[1],	origin[2],
		0,		0,		0,
		0,		0,		0,
		red,	green,	blue,
		0,		0,		0,
		1,		INSTANT_PARTICLE,
		GL_ONE, GL_ONE, // was GL_SRC_ALPHA, GL_ONE
		size,		0,			
		particle_blasterblob, // was particle_generic
		PART_DIRECTION|PART_INSTANT|PART_OVERBRIGHT,
		NULL,0);
}

void CL_HyperBlasterEffect (vec3_t start, vec3_t end, vec3_t angle, int red, int green, int blue,
										int reddelta, int greendelta, int bluedelta, float len, float size)
{
	if (cl_particle_scale->value < 2)
	//	CL_HyperBlasterTrail (start, end, red, green, blue, reddelta, greendelta, bluedelta);
		CL_HyperBlasterGlow (start, end, red, green, blue, reddelta, greendelta, bluedelta);
	//else //if (cl_particle_scale->value >= 2)
		CL_BlasterTracer (end, angle, red, green, blue, len, size);
}


/*
===============
CL_QuadTrail
===============
*/
void CL_QuadTrail (vec3_t start, vec3_t end)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	int			dec;

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	dec = 5;
	VectorScale (vec, 5, vec);

	while (len > 0)
	{
		len -= dec;

		CL_SetupParticle (
			0,	0,	0,
			move[0] + crand()*16,	move[1] + crand()*16,	move[2] + crand()*16,
			crand()*5,	crand()*5,	crand()*5,
			0,		0,		0,
			0,		0,		200,
			0,	0,	0,
			1,		-1.0 / (0.8+frand()*0.2),
			GL_SRC_ALPHA, GL_ONE,
			1,			0,			
			particle_generic,
			0,
			NULL,0);

		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_FlagTrail
===============
*/
void CL_FlagTrail (vec3_t start, vec3_t end, qboolean isred, qboolean isgreen)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	int			dec;

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	dec = 5;
	VectorScale (vec, 5, vec);

	while (len > 0)
	{
		len -= dec;

		CL_SetupParticle (
			0,	0,	0,
			move[0] + crand()*16, move[1] + crand()*16, move[2] + crand()*16,
			crand()*5,	crand()*5, crand()*5,
			0,		0,		0,
			(isred)?255:0, (isgreen)?255:0, (!isred && !isgreen)?255:0,
			0,	0,	0,
			1,		-1.0 / (0.8+frand()*0.2),
			GL_SRC_ALPHA, GL_ONE,
			1,			0,			
			particle_generic,
			0,
			NULL,0);

		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_DiminishingTrail
===============
*/
void CL_DiminishingTrail (vec3_t start, vec3_t end, centity_t *old, int flags)
{
	cparticle_t	*p;
	vec3_t		move;
	vec3_t		vec;
	float		len, oldlen;
	float		dec;
	float		orgscale;
	float		velscale;

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = oldlen = VectorNormalize (vec);

	dec = (flags & EF_ROCKET) ? 10 : 2;
	dec *= cl_particle_scale->value;
	VectorScale (vec, dec, vec);

	if (old->trailcount > 900)
	{
		orgscale = 4;
		velscale = 15;
	}
	else if (old->trailcount > 800)
	{
		orgscale = 2;
		velscale = 10;
	}
	else
	{
		orgscale = 1;
		velscale = 5;
	}

	while (len > 0)
	{
		len -= dec;

		if (!free_particles)
			return;

		if (flags & EF_ROCKET)
		{
			if (CM_PointContents(move,0) & MASK_WATER)
				CL_SetupParticle (
					0,	0,	crand()*360,
					move[0],	move[1],	move[2],
					crand()*9,	crand()*9,	crand()*9+5,
					0,		0,		0,
					255,	255,	255,
					0,	0,	0,
					0.75,		-0.2 / (1 + frand() * 0.2),
					GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
					1+random()*3,	1,			
					particle_bubble,
					PART_TRANS|PART_SHADED,
					CL_ExplosionBubbleThink, true);
			else
				CL_SetupParticle (
					crand()*180, crand()*100, 0,
					move[0],	move[1],	move[2],
					crand()*5,	crand()*5,	crand()*10,
					0,		0,		5,
					255,	255,	255,
					-50,	-50,	-50,
					0.75,		-0.75,	// was 1, -0.5
					GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
					5,			15,			
					particle_smoke,
					PART_TRANS|PART_SHADED,
					CL_ParticleRotateThink, true);
		}
		else
		{
			// drop less particles as it flies
			if ((rand()&1023) < old->trailcount)
			{
				if (flags & EF_GIB)
				{
					if (cl_blood->value > 1)
						p = CL_SetupParticle (
							0,	0,	random()*360,
							move[0] + crand()*orgscale,	move[1] + crand()*orgscale,	move[2] + crand()*orgscale,
							crand()*velscale,	crand()*velscale,	crand()*velscale,
							0,		0,		0,
							255,	0,		0,
							0,		0,		0,
							0.75,	-0.75 / (1+frand()*0.4),
							GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
							3 + random()*2,			0,			
							particle_blooddrop,
							PART_OVERBRIGHT|PART_GRAVITY|PART_SHADED,
							CL_ParticleBloodThink,true);
							//NULL,0);
					else
						p = CL_SetupParticle (
							0,	0,	0,
							move[0] + crand()*orgscale,	move[1] + crand()*orgscale,	move[2] + crand()*orgscale,
							crand()*velscale,	crand()*velscale,	crand()*velscale,
							0,		0,		0,
							255,	0,		0,
							0,		0,		0,
							1,		-1.0 / (1+frand()*0.4),
							GL_SRC_ALPHA, GL_ONE,
							5,			-1,			
							particle_blood,
							PART_GRAVITY|PART_SHADED,
							CL_ParticleBloodThink,true);
							//NULL,0);
					if ( p && (crand() < (double)0.0001F) )
						p->flags |= PART_LEAVEMARK;
				}
				else if (flags & EF_GREENGIB)
				{
					p = CL_SetupParticle (
						0,	0,	0,
						move[0] + crand()*orgscale,	move[1] + crand()*orgscale,	move[2] + crand()*orgscale,
						crand()*velscale,	crand()*velscale,	crand()*velscale,
						0,		0,		0,
						255,	180,	50,
						0,		0,		0,
						1,		-0.5 / (1+frand()*0.4),
						GL_SRC_ALPHA, GL_ONE,
						5,			-1,			
						particle_blood,
						PART_OVERBRIGHT|PART_GRAVITY|PART_SHADED,
						CL_ParticleBloodThink,true);
						//NULL,0);
					if ( p && (crand() < (double)0.0001F) ) 
						p->flags |= PART_LEAVEMARK;

				}
				else if (flags & EF_GRENADE) // no overbrights on grenade trails
				{
					if (CM_PointContents(move,0) & MASK_WATER)
						CL_SetupParticle (
							0,	0,	crand()*360,
							move[0],	move[1],	move[2],
							crand()*9,	crand()*9,	crand()*9+5,
							0,		0,		0,
							255,	255,	255,
							0,	0,	0,
							0.75,		-0.2 / (1 + frand() * 0.2),
							GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
							1+random()*3,	1,			
							particle_bubble,
							PART_TRANS|PART_SHADED,
							CL_ExplosionBubbleThink,true);
					else
						CL_SetupParticle (
							crand()*180, crand()*50, 0,
							move[0] + crand()*orgscale,	move[1] + crand()*orgscale,	move[2] + crand()*orgscale,
							crand()*velscale,	crand()*velscale,	crand()*velscale,
							0,		0,		20,
							255,	255,	255,
							0,		0,		0,
							0.5,		-0.5,
							GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
							5,			5,			
							particle_smoke,
							PART_TRANS|PART_SHADED,
							CL_ParticleRotateThink,true);
				}
				else
				{
					CL_SetupParticle (
						crand()*180, crand()*50, 0,
						move[0] + crand()*orgscale,	move[1] + crand()*orgscale,	move[2] + crand()*orgscale,
						crand()*velscale,	crand()*velscale,	crand()*velscale,
						0,		0,		20,
						255,		255,		255,
						0,	0,	0,
						0.5,		-0.5,
						GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
						5,			5,			
						particle_smoke,
						PART_OVERBRIGHT|PART_TRANS|PART_SHADED,
						CL_ParticleRotateThink,true);
				}
			}

			old->trailcount -= 5;
			if (old->trailcount < 100)
				old->trailcount = 100;
		}

		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_RocketTrail
===============
*/
void CL_RocketTrail (vec3_t start, vec3_t end, centity_t *old)
{
	vec3_t		move;
	vec3_t		vec;
	float		len, totallen;
	float		dec;

	// smoke
	CL_DiminishingTrail (start, end, old, EF_ROCKET);

	// fire
	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	totallen = len = VectorNormalize (vec);

	dec = 1*cl_particle_scale->value;
	VectorScale (vec, dec, vec);

	while (len > 0)
	{
		len -= dec;

		if (!free_particles)
			return;

		// falling particles
		if ( (rand()&7) == 0)
		{
			CL_SetupParticle (
				0,	0,	0,
				move[0] + crand()*5,	move[1] + crand()*5,	move[2] + crand()*5,
				crand()*20,	crand()*20,	crand()*20,
				0,		0,		20,
				255,	255,	200,
				0,	-50,	0,
				1,		-1.0 / (1+frand()*0.2),
				GL_SRC_ALPHA, GL_ONE,
				2,			-2,			
				particle_blaster,
				PART_GRAVITY,
				NULL,0);
		}
		VectorAdd (move, vec, move);
	}

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	totallen = len = VectorNormalize (vec);
	dec = 1.5*cl_particle_scale->value;
	VectorScale (vec, dec, vec);
/*	len = totallen;
	VectorCopy (start, move);
	dec = 1.5;//*cl_particle_scale->value;
	VectorScale (vec, dec, vec);*/

	while (len > 0)
	{
		len -= dec;

		// flame
		CL_SetupParticle (
			crand()*180, crand()*100, 0,
			move[0],	move[1],	move[2],
			crand()*5,	crand()*5,	crand()*5,
			0,		0,		5,
			255,	225,	200,
			-50,	-50,	-50,
			0.5,		-2, // was 0.75, -3
			GL_SRC_ALPHA, GL_ONE,
			5,			5,
			particle_inferno,
			0,
			CL_ParticleRotateThink, true);

		VectorAdd (move, vec, move);
	}
}


/*
===============
FartherPoint
Returns true if the first vector
is farther from the viewpoint.
===============
*/
qboolean FartherPoint (vec3_t pt1, vec3_t pt2)
{
	vec3_t		distance1, distance2;

	VectorSubtract(pt1, cl.refdef.vieworg, distance1);
	VectorSubtract(pt2, cl.refdef.vieworg, distance2);
	return (VectorLength(distance1) > VectorLength(distance2));
}


/*
===============
CL_RailSprial
===============
*/
#define DEVRAILSTEPS 2
//this is the length of each piece...
#define RAILTRAILSPACE 15

void CL_RailSprial (vec3_t start, vec3_t end, qboolean isRed)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	vec3_t		right, up;
	int			i;
	float		d, c, s;
	vec3_t		dir;

	// Draw from closest point
	if (FartherPoint(start, end)) {
		VectorCopy (end, move);
		VectorSubtract (start, end, vec);
	}
	else {
		VectorCopy (start, move);
		VectorSubtract (end, start, vec);
	}
	len = VectorNormalize (vec);
	len = min (len, cl_rail_length->value);  // cap length
	MakeNormalVectors (vec, right, up);

	VectorScale(vec, cl_rail_space->value*cl_particle_scale->value, vec);

	for (i=0; i<len; i += cl_rail_space->value*cl_particle_scale->value)
	{
		d = i * 0.1;
		c = cos(d);
		s = sin(d);

		VectorScale (right, c, dir);
		VectorMA (dir, s, up, dir);

		CL_SetupParticle (
			0,	0,	0,
			move[0] + dir[0]*3,	move[1] + dir[1]*3,	move[2] + dir[2]*3,
			dir[0]*6,	dir[1]*6,	dir[2]*6,
			0,		0,		0,
			(isRed)?255:cl_railred->value,	(isRed)?20:cl_railgreen->value,	(isRed)?20:cl_railblue->value,
			0,	0,	0,
			1,		-1.0,
			GL_SRC_ALPHA, GL_ONE,
			3,	0,			
			particle_generic,
			0,
			NULL,0);

		VectorAdd (move, vec, move);
	}
}

/*
===============
CL_ParticleDevRailThink
===============
*/
void CL_ParticleDevRailThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{
	int i;
	vec3_t len;
	VectorSubtract(p->angle, org, len);
	
	*size *= (float)(SplashSize/VectorLength(len)) * 0.5/((4-*size));
	if (*size > SplashSize)
		*size = SplashSize;

	//setting up angle for sparks
	{
		float time1, time2;

		time1 = *time;
		time2 = time1*time1;

		for (i=0;i<2;i++)
			angle[i] = 3*(p->vel[i]*time1 + (p->accel[i])*time2);
		angle[2] = 3*(p->vel[2]*time1 + (p->accel[2]-PARTICLE_GRAVITY)*time2);
	}

	p->thinknext = true;
}

/*
===============
CL_DevRailTrail
===============
*/
void CL_DevRailTrail (vec3_t start, vec3_t end, qboolean isRed)
{
	vec3_t		move;
	vec3_t		vec, point;
	float		len;
	int			dec, i=0;

	// Draw from closest point
	if (FartherPoint(start, end)) {
		VectorCopy (end, move);
		VectorSubtract (start, end, vec);
	}
	else {
		VectorCopy (start, move);
		VectorSubtract (end, start, vec);
	}
	len = VectorNormalize (vec);
	len = min (len, cl_rail_length->value);  // cap length
	VectorCopy(vec, point);

	dec = 4;
	VectorScale (vec, dec, vec);

	// FIXME: this is a really silly way to have a loop
	while (len > 0)
	{
		len -= dec;
		i++;

		if (i>=DEVRAILSTEPS)
		{
			for (i=3;i>0;i--)
			CL_SetupParticle (
				point[0],	point[1],	point[2],
				move[0],	move[1],	move[2],
				0,		0,		0,
				0,		0,		0,
				(isRed)?255:cl_railred->value,	(isRed)?20:cl_railgreen->value,	(isRed)?20:cl_railblue->value,
				0,		-90,	-30,
				0.75,		-.75,
				GL_SRC_ALPHA, GL_ONE,
				dec*DEVRAILSTEPS*TWOTHIRDS,	0,			
				particle_beam2,
				PART_DIRECTION,
				NULL,0);
		}

		CL_SetupParticle (
			0,	0,	0,
			move[0],	move[1],	move[2],
			crand()*10,	crand()*10,	crand()*10+20,
			0,		0,		0,
			(isRed)?255:cl_railred->value,	(isRed)?20:cl_railgreen->value,	(isRed)?20:cl_railblue->value,
			0,	0,	0,
			1,		-0.75 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			2,			-0.25,			
			particle_solid,
			PART_GRAVITY|PART_SPARK,
			CL_ParticleDevRailThink,true);
		
		CL_SetupParticle (
			crand()*180, crand()*100, 0,
			move[0],	move[1],	move[2],
			crand()*10,	crand()*10,	crand()*10+20,
			0,		0,		5,
			255,	255,	255,
			0,	0,	0,
			0.25,		-0.25,
			GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
			5,			10,			
			particle_smoke,
			PART_TRANS|PART_GRAVITY|PART_OVERBRIGHT,
			CL_ParticleRotateThink, true);

		VectorAdd (move, vec, move);
	}
}

/*
===============
CL_RailTrail
===============
*/
void CL_RailTrail (vec3_t start, vec3_t end, qboolean isRed)
{
	vec3_t		move, last;
	vec3_t		vec, point;
	//vec3_t	right, up;
	int			i;
	int			beamred, beamgreen, beamblue;
	float		len;//, dec;
	qboolean	colored = (cl_railtype->value!=0);

	VectorSubtract (end, start, vec);
	VectorNormalize(vec);
	CL_ParticleRailDecal (end, vec, 7, isRed);

	if (cl_railtype->value == 2)
	{
		CL_DevRailTrail (start, end, isRed);
		return;
	}
	// Draw from closest point
	if (FartherPoint(start, end)) {
		VectorCopy (end, move);
		VectorSubtract (start, end, vec);
	}
	else {
		VectorCopy (start, move);
		VectorSubtract (end, start, vec);
	}
	len = VectorNormalize (vec);
	if (cl_railtype->value == 0)
		len = min (len, cl_rail_length->value);  // cap length
	VectorCopy (vec, point);
	VectorScale (vec, RAILTRAILSPACE, vec);
	//MakeNormalVectors (vec, right, up);

	if (colored) {
		if (isRed) {
			beamred = 255;
			beamgreen = beamblue = 20;
		}
		else {
			beamred = cl_railred->value;
			beamgreen = cl_railgreen->value;
			beamblue = cl_railblue->value;
		}
	}
	else
		beamred = beamgreen = beamblue = 255;

	while (len > 0)
	{
		VectorCopy (move, last);	
		VectorAdd (move, vec, move);

		len -= RAILTRAILSPACE;

		for (i=0;i<3;i++)
			CL_SetupParticle (
				last[0],	last[1],	last[2],
				move[0],	move[1],	move[2],
				0,	0,	0,
				0,	0,	0,
				beamred,	beamgreen,	beamblue,
				0,	0,	0,
				0.75,		-0.75,
				GL_SRC_ALPHA, GL_ONE,
				RAILTRAILSPACE*TWOTHIRDS,	(colored)?0:-5,			
				particle_beam2,
				PART_BEAM,
				NULL,0);
	}
	if (cl_railtype->value == 0)
		CL_RailSprial (start, end, isRed);
}


// RAFAEL
/*
===============
CL_IonripperTrail
===============
*/
void CL_IonripperTrail (vec3_t start, vec3_t ent)
{
	vec3_t	move;
	vec3_t	vec;
	vec3_t  leftdir,up;
	float	len;
	int		dec;
	int     left = 0;

	VectorCopy (start, move);
	VectorSubtract (ent, start, vec);
	len = VectorNormalize (vec);

	MakeNormalVectors (vec, leftdir, up);

	dec = 3*cl_particle_scale->value;
	VectorScale (vec, dec, vec);

	while (len > 0)
	{
		len -= dec;

		CL_SetupParticle (
			0,	0,	0,
			move[0],	move[1],	move[2],
			0,	0,	0,
			0,		0,		0,
			255,	75,		0,
			0,	0,	0,
			0.75,		-1.0 / (0.3 + frand() * 0.2),
			GL_SRC_ALPHA, GL_ONE,
			3,			0,			// was dec
			particle_generic,
			0,
			NULL,0);

		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_BubbleTrail

===============
*/
void CL_BubbleTrail (vec3_t start, vec3_t end)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	int			i;
	float		dec, size;

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	dec = 32;
	VectorScale (vec, dec, vec);

	for (i=0 ; i<len ; i+=dec)
	{
		size = (frand()>0.25)? 1 : (frand()>0.5) ? 2 : (frand()>0.75) ? 3 : 4;

		CL_SetupParticle (
			0,	0,	0,
			move[0]+crand()*2,	move[1]+crand()*2,	move[2]+crand()*2,
			crand()*5,	crand()*5,	crand()*5+6,
			0,		0,		0,
			255,	255,	255,
			0,	0,	0,
			0.75,		-0.5 / (1 + frand() * 0.2),
			GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
			size,	1,			
			particle_bubble,
			PART_TRANS|PART_SHADED,
			NULL,0);

		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_FlyParticles
===============
*/
#define	BEAMLENGTH			16


void CL_FlyParticles (vec3_t origin, int count)
{
	int			i;
	float		angle;
	float		sr, sp, sy, cr, cp, cy;
	vec3_t		forward;
	float		dist = 64;
	float		ltime;


	if (count > NUMVERTEXNORMALS)
		count = NUMVERTEXNORMALS;

	if (!avelocities[0][0])
	{
		for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
			avelocities[0][i] = (rand()&255) * 0.01;
	}


	ltime = (float)cl.time / 1000.0;
	for (i=0 ; i<count ; i+=2)
	{
		angle = ltime * avelocities[i][0];
		sy = sin(angle);
		cy = cos(angle);
		angle = ltime * avelocities[i][1];
		sp = sin(angle);
		cp = cos(angle);
		angle = ltime * avelocities[i][2];
		sr = sin(angle);
		cr = cos(angle);
	
		forward[0] = cp*cy;
		forward[1] = cp*sy;
		forward[2] = -sp;

		dist = sin(ltime + i)*64;

		CL_SetupParticle (
			0,	0,	0,
			origin[0] + bytedirs[i][0]*dist + forward[0]*BEAMLENGTH,origin[1] + bytedirs[i][1]*dist + forward[1]*BEAMLENGTH,
			origin[2] + bytedirs[i][2]*dist + forward[2]*BEAMLENGTH,
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			1,		-100,
			GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
			1+sin(i+ltime),	1,			
			particle_generic,
			PART_TRANS,
			NULL,0);
	}
}

/*
===============
CL_FlyEffect
===============
*/
void CL_FlyEffect (centity_t *ent, vec3_t origin)
{
	int		n;
	int		count;
	int		starttime;

	if (ent->fly_stoptime < cl.time)
	{
		starttime = cl.time;
		ent->fly_stoptime = cl.time + 60000;
	}
	else
	{
		starttime = ent->fly_stoptime - 60000;
	}

	n = cl.time - starttime;
	if (n < 20000)
		count = n * 162 / 20000.0;
	else
	{
		n = ent->fly_stoptime - cl.time;
		if (n < 20000)
			count = n * 162 / 20000.0;
		else
			count = 162;
	}

	CL_FlyParticles (origin, count);
}


/*
===============
CL_ParticleBFGThink
===============
*/
void CL_ParticleBFGThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{
	vec3_t len;
	VectorSubtract(p->angle, p->org, len);
	
	*size = (float)((300/VectorLength(len))*0.75);
}

#define	BEAMLENGTH			16

/*
===============
CL_BfgParticles
===============
*/
void CL_BfgParticles (entity_t *ent)
{
	int			i;
	cparticle_t	*p;
	float		angle;
	float		sr, sp, sy, cr, cp, cy;
	vec3_t		forward;
	float		dist = 64, dist2;
	vec3_t		v;
	float		ltime;
	
	if (!avelocities[0][0])
	{
		for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
			avelocities[0][i] = (rand()&255) * 0.01;
	}


	ltime = (float)cl.time / 1000.0;
	for (i=0 ; i<NUMVERTEXNORMALS ; i++)
	{
		angle = ltime * avelocities[i][0];
		sy = sin(angle);
		cy = cos(angle);
		angle = ltime * avelocities[i][1];
		sp = sin(angle);
		cp = cos(angle);
		angle = ltime * avelocities[i][2];
		sr = sin(angle);
		cr = cos(angle);
	
		forward[0] = cp*cy;
		forward[1] = cp*sy;
		forward[2] = -sp;

		dist2 = dist;
		dist = sin(ltime + i)*64;

		p = CL_SetupParticle (
			ent->origin[0],	ent->origin[1],	ent->origin[2],
			ent->origin[0] + bytedirs[i][0]*dist + forward[0]*BEAMLENGTH,ent->origin[1] + bytedirs[i][1]*dist + forward[1]*BEAMLENGTH,
			ent->origin[2] + bytedirs[i][2]*dist + forward[2]*BEAMLENGTH,
			0,	0,		0,
			0,	0,		0,
			50,	200*dist2,	20,
			0,	0,	0,
			1,		-100,
			GL_SRC_ALPHA, GL_ONE,
			1,			1,			
			particle_generic,
			0,
			CL_ParticleBFGThink, true);
		
		if (!p)
			return;

		VectorSubtract (p->org, ent->origin, v);
		dist = VectorLength(v) / 90.0;
	}
}


// RAFAEL
/*
===============
CL_TrapParticles
===============
*/
void CL_TrapParticles (entity_t *ent)
{
	vec3_t		move;
	vec3_t		vec;
	vec3_t		start, end;
	float		len;
	int			dec;

	ent->origin[2]-=14;
	VectorCopy (ent->origin, start);
	VectorCopy (ent->origin, end);
	end[2]+=64;

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	dec = 5;
	VectorScale (vec, 5, vec);

	// FIXME: this is a really silly way to have a loop
	while (len > 0)
	{
		len -= dec;

		CL_SetupParticle (
			0,	0,	0,
			move[0] + crand(),	move[1] + crand(),	move[2] + crand(),
			crand()*15,	crand()*15,	crand()*15,
			0,	0,	PARTICLE_GRAVITY,
			230+crand()*25,	125+crand()*25,	25+crand()*25,
			0,		0,		0,
			1,		-1.0 / (0.3+frand()*0.2),
			GL_SRC_ALPHA, GL_ONE,
			3,			-3,			
			particle_generic,
			0,
			NULL,0);

		VectorAdd (move, vec, move);
	}

	{
	int			i, j, k;
	float		vel;
	vec3_t		dir;
	vec3_t		org;

	
	ent->origin[2]+=14;
	VectorCopy (ent->origin, org);


	for (i=-2 ; i<=2 ; i+=4)
		for (j=-2 ; j<=2 ; j+=4)
			for (k=-2 ; k<=4 ; k+=4)
			{

				dir[0] = j * 8;
				dir[1] = i * 8;
				dir[2] = k * 8;

				VectorNormalize (dir);						
				vel = 50 + rand()&63;

				CL_SetupParticle (
					0,	0,	0,
					org[0] + i + ((rand()&23) * crand()), org[1] + j + ((rand()&23) * crand()),	org[2] + k + ((rand()&23) * crand()),
					dir[0]*vel,	dir[1]*vel,	dir[2]*vel,
					0,		0,		0,
					230+crand()*25,	125+crand()*25,	25+crand()*25,
					0,		0,		0,
					1,		-1.0 / (0.3+frand()*0.2),
					GL_SRC_ALPHA, GL_ONE,
					1,			1,			
					particle_generic,
					PART_GRAVITY,
					NULL,0);

			}
	}
}


/*
===============
CL_BFGExplosionParticles
===============
*/
//FIXME combined with CL_ExplosionParticles
void CL_BFGExplosionParticles (vec3_t org)
{
	int			i;

	for (i=0 ; i<256 ; i++)
	{
		CL_SetupParticle (
			0,	0,	0,
			org[0] + ((rand()%32)-16), org[1] + ((rand()%32)-16),	org[2] + ((rand()%32)-16),
			(rand()%150)-75,	(rand()%150)-75,	(rand()%150)-75,
			0,	0,	0,
			50,	100+rand()*50,	0, //Knightmare- made more green
			0,	0,	0,
			1,		-0.8 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			10,			-10,			
			particle_generic,
			PART_GRAVITY,
			NULL,0);
	}
}


/*
===============
CL_TeleportParticles
===============
*/
void CL_TeleportParticles (vec3_t org)
{
	int			i, j, k;
	float		vel;
	vec3_t		dir;

	for (i=-16 ; i<=16 ; i+=4)
		for (j=-16 ; j<=16 ; j+=4)
			for (k=-16 ; k<=32 ; k+=4)
			{
				dir[0] = j*16;
				dir[1] = i*16;
				dir[2] = k*16;

				VectorNormalize (dir);						
				vel = 150 + (rand()&63);

				CL_SetupParticle (
					0,	0,	0,
					org[0]+i+(rand()&3), org[1]+j+(rand()&3),	org[2]+k+(rand()&3),
					dir[0]*vel,	dir[1]*vel,	dir[2]*vel,
					0,		0,		0,
					200 + 55*rand(),	200 + 55*rand(),	200 + 55*rand(),
					0,		0,		0,
					1,		-1.0 / (0.3 + (rand()&7) * 0.02),
					GL_SRC_ALPHA, GL_ONE,
					1,			3,			
					particle_generic,
					PART_GRAVITY,
					NULL,0);
			}
}


/*
===============
CL_Flashlight
===============
*/
void CL_Flashlight (int ent, vec3_t pos)
{
	cdlight_t	*dl;

	dl = CL_AllocDlight (ent);
	VectorCopy (pos,  dl->origin);
	dl->radius = 400;
	dl->minlight = 250;
	dl->die = cl.time + 100;
	dl->color[0] = 1;
	dl->color[1] = 1;
	dl->color[2] = 1;
}


/*
===============
CL_ColorFlash - flash of light
===============
*/
void CL_ColorFlash (vec3_t pos, int ent, int intensity, float r, float g, float b)
{
	cdlight_t	*dl;

	dl = CL_AllocDlight (ent);
	VectorCopy (pos,  dl->origin);
	dl->radius = intensity;
	dl->minlight = 250;
	dl->die = cl.time + 100;
	dl->color[0] = r;
	dl->color[1] = g;
	dl->color[2] = b;
}


/*
===============
CL_DebugTrail
===============
*/
void CL_DebugTrail (vec3_t start, vec3_t end)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	float		dec;
	vec3_t		right, up;

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	MakeNormalVectors (vec, right, up);

	dec = 8; // was 2
	VectorScale (vec, dec, vec);
	VectorCopy (start, move);

	while (len > 0)
	{
		len -= dec;

		CL_SetupParticle (
			0,	0,	0,
			move[0],	move[1],	move[2],
			0,	0,	0,
			0,		0,		0,
			50,	50,	255,
			0,	0,	0,
			1,		-0.75,
			GL_SRC_ALPHA, GL_ONE,
			7.5,			0,			
			particle_generic,
			0,
			NULL,0);

		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_ForceWall
===============
*/
void CL_ForceWall (vec3_t start, vec3_t end, int color8)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	vec3_t color = { color8red(color8), color8green(color8), color8blue(color8)};

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	VectorScale (vec, 4, vec);

	// FIXME: this is a really silly way to have a loop
	while (len > 0)
	{
		len -= 4;
		
		if (frand() > 0.3)
			CL_SetupParticle (
				0,	0,	0,
				move[0] + crand()*3,	move[1] + crand()*3,	move[2] + crand()*3,
				0,	0,	-40 - (crand()*10),
				0,		0,		0,
				color[0]+5,	color[1]+5,	color[2]+5,
				0,	0,	0,
				1,		-1.0 / (3.0+frand()*0.5),
				GL_SRC_ALPHA, GL_ONE,
				5,			0,			
				particle_generic,
				0,
				NULL,0);

		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_BubbleTrail2 (lets you control the # of bubbles by setting the distance between the spawns)
===============
*/
void CL_BubbleTrail2 (vec3_t start, vec3_t end, int dist)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	int			i;
	float		dec, size;

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	dec = dist;
	VectorScale (vec, dec, vec);

	for (i=0 ; i<len ; i+=dec)
	{
		size = (frand()>0.25)? 1 : (frand()>0.5) ? 2 : (frand()>0.75) ? 3 : 4;
		CL_SetupParticle (
			0,	0,	0,
			move[0]+crand()*2,	move[1]+crand()*2,	move[2]+crand()*2,
			crand()*5,	crand()*5,	crand()*5+6,
			0,		0,		0,
			255,	255,	255,
			0,	0,	0,
			0.75,	-0.5 / (1 + frand() * 0.2),
			GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
			size,		1,			
			particle_bubble,
			PART_TRANS|PART_SHADED,
			NULL,0);

		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_HeatbeamParticles
===============
*/
void CL_HeatbeamParticles (vec3_t start, vec3_t forward)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	vec3_t		right, up;
	int			i;
	float		c, s;
	vec3_t		dir;
	float		ltime;
	float		step, rstep;
	float		start_pt;
	float		rot;
	float		variance;
	float		size;
	int			maxsteps;
	vec3_t		end;

	VectorMA (start, 4096, forward, end);

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	// FIXME - pmm - these might end up using old values?
	//MakeNormalVectors (vec, right, up);
	VectorCopy (cl.v_right, right);
	VectorCopy (cl.v_up, up);

	if (cg_thirdperson->value) {
		ltime = (float) cl.time/250.0;
		step = 96;
		maxsteps = 10;
		variance = 1.2;
		size = 2;
	}
	else {
		ltime = (float) cl.time/1000.0;
		step = 32;
		maxsteps = 7;
		variance = 0.5;
		size = 1;
	}

	// this just misaligns rings with beam
	//VectorMA (move, -0.5, right, move);
	//VectorMA (move, -0.5, up, move);

	//ltime = (float) cl.time/1000.0;
	start_pt = fmod(ltime*96.0,step);
	VectorMA (move, start_pt, vec, move);

	VectorScale (vec, step, vec);

	//Com_Printf ("%f\n", ltime);
	rstep = M_PI/10.0*min(cl_particle_scale->value, 2);
	for (i=start_pt; i<len; i+=step)
	{
		if (i>step*maxsteps) // don't bother after the nth ring
			break;

		for (rot = 0; rot < M_PI*2; rot += rstep)
		{
		//	variance = 0.5;
			c = cos(rot)*variance;
			s = sin(rot)*variance;
			
			// trim it so it looks like it's starting at the origin
			if (i < 10)
			{
				VectorScale (right, c*(i/10.0), dir);
				VectorMA (dir, s*(i/10.0), up, dir);
			}
			else
			{
				VectorScale (right, c, dir);
				VectorMA (dir, s, up, dir);
			}

			CL_SetupParticle (
				0,	0,	0,
				move[0]+dir[0]*2,	move[1]+dir[1]*2,	move[2]+dir[2]*2, //Knightmare- decreased radius
				0,	0,	0,
				0,		0,		0,
				200+rand()*50,	200+rand()*25,	rand()*50,
				0,	0,	0,
				0.25,	-1000.0, // decreased alpha
				GL_SRC_ALPHA, GL_ONE,
				size,		1,		// shrunk size
				particle_blaster,
				0,
				NULL,0);
		}
		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_ParticleSteamEffect

Puffs with velocity along direction, with some randomness thrown in
===============
*/
void CL_ParticleSteamEffect (vec3_t org, vec3_t dir, int red, int green, int blue,
							 int reddelta, int greendelta, int bluedelta, int count, int magnitude)
{
	int			i;
	cparticle_t	*p;
	float		d;
	vec3_t		r, u;
	//vec3_t color = { color8red(color8), color8green(color8), color8blue(color8)};

	//vectoangles2 (dir, angle_dir);
	//AngleVectors (angle_dir, f, r, u);

	MakeNormalVectors (dir, r, u);

	for (i=0 ; i<count ; i++)
	{
		p = CL_SetupParticle (
			0,	0,	0,
			org[0]+magnitude*0.1*crand(),	org[1]+magnitude*0.1*crand(),	org[2]+magnitude*0.1*crand(),
			0,	0,	0,
			0,		0,		0,
			red,	green,	blue,
			reddelta,	greendelta,	bluedelta,
			0.5,		-1.0 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			4,			-2,			
			particle_smoke,
			0,
			NULL,0);

		if (!p)
			return;

		VectorScale (dir, magnitude, p->vel);
		d = crand()*magnitude/3;
		VectorMA (p->vel, d, r, p->vel);
		d = crand()*magnitude/3;
		VectorMA (p->vel, d, u, p->vel);
	}
}


/*
===============
CL_ParticleSteamEffect2

Puffs with velocity along direction, with some randomness thrown in
===============
*/
void CL_ParticleSteamEffect2 (cl_sustain_t *self)
//vec3_t org, vec3_t dir, int color, int count, int magnitude)
{
	int			i;
	cparticle_t	*p;
	float		d;
	vec3_t		r, u;
	vec3_t		dir;
	int			color8 = self->color + (rand()&7);
	vec3_t color = { color8red(color8), color8green(color8), color8blue(color8)};

	//vectoangles2 (dir, angle_dir);
	//AngleVectors (angle_dir, f, r, u);

	VectorCopy (self->dir, dir);
	MakeNormalVectors (dir, r, u);

	for (i=0; i<self->count; i++)
	{
		p = CL_SetupParticle (
			0,		0,		0,
			self->org[0] + self->magnitude*0.1*crand(),	self->org[1] + self->magnitude*0.1*crand(),	self->org[2] + self->magnitude*0.1*crand(),
			0,		0,		0,
			0,		0,		0,
			color[0],	color[1],	color[2],
			0,		0,		0,
			1.0,		-1.0 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			4,			0,			
			particle_smoke,
			PART_GRAVITY,
			NULL,0);

		if (!p)
			return;

		VectorScale (dir, self->magnitude, p->vel);
		d = crand()*self->magnitude/3;
		VectorMA (p->vel, d, r, p->vel);
		d = crand()*self->magnitude/3;
		VectorMA (p->vel, d, u, p->vel);
	}
	self->nextthink += self->thinkinterval;
}


/*
===============
CL_TrackerTrail
===============
*/
void CL_TrackerTrail (vec3_t start, vec3_t end)
{
	vec3_t		move;
	vec3_t		vec;
	vec3_t		forward,right,up,angle_dir;
	float		len;
	cparticle_t	*p;
	int			dec;
	float		dist;

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	VectorCopy(vec, forward);
	vectoangles2 (forward, angle_dir);
	AngleVectors (angle_dir, forward, right, up);

	dec = 3*max(cl_particle_scale->value/2, 1);
	VectorScale (vec, 3*max(cl_particle_scale->value/2, 1), vec);

	// FIXME: this is a really silly way to have a loop
	while (len > 0)
	{
		len -= dec;

		p = CL_SetupParticle (
			0,	0,	0,
			0,	0,	0,
			0,	0,	5,
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			1.0,	-2.0,
			GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
			2,		0,			
			particle_generic,
			PART_TRANS,
			NULL,0);

		if (!p)
			return;

		dist = DotProduct(move, forward);
		VectorMA(move, 8 * cos(dist), up, p->org);

		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_TrackerShell
===============
*/
void CL_Tracker_Shell(vec3_t origin)
{
	vec3_t			dir;
	int				i;
	cparticle_t		*p;

	for(i=0; i < (300/cl_particle_scale->value); i++)
	{
		p = CL_SetupParticle (
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			1.0,	-2.0,
			GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
			1,		0,	//Knightmare- changed size		
			particle_generic,
			PART_TRANS,
			NULL,0);

		if (!p)
			return;

		dir[0] = crand();
		dir[1] = crand();
		dir[2] = crand();
		VectorNormalize(dir);
		VectorMA(origin, 40, dir, p->org);
	}
}


/*
======================
CL_MonsterPlasma_Shell
======================
*/
void CL_MonsterPlasma_Shell(vec3_t origin)
{
	vec3_t			dir;
	int				i;
	cparticle_t		*p;

	for(i=0; i<40; i++)
	{
		p = CL_SetupParticle (
			0,		0,		0,
			0,		0,		0,
			0,		0,		0,
			0,		0,		0,
			220,	140,	50, //Knightmare- this was all black
			0,		0,		0,
			1.0,	INSTANT_PARTICLE,
			GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
			2,		0,			
			particle_generic,
			PART_TRANS|PART_INSTANT,
			NULL,0);

		if (!p)
			return;

		dir[0] = crand();
		dir[1] = crand();
		dir[2] = crand();
		VectorNormalize(dir);
	
		VectorMA(origin, 10, dir, p->org);
	}
}


/*
===============
CL_Widowbeamout
===============
*/
void CL_Widowbeamout (cl_sustain_t *self)
{
	vec3_t			dir;
	int				i;
	static int colortable0[6] = {125,	255,	185,	125,	185,	255};
	static int colortable1[6] = {185,	125,	255,	255,	125,	185};
	static int colortable2[6] = {255,	185,	125,	185,	255,	125};
	cparticle_t		*p;
	float			ratio;
	int				index;

	ratio = 1.0 - (((float)self->endtime - (float)cl.time)/2100.0);

	for(i=0; i<300; i++)
	{
		index = rand()&5;
		p = CL_SetupParticle (
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			colortable0[index],	colortable1[index],	colortable2[index],
			0,	0,	0,
			1.0,	INSTANT_PARTICLE,
			GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
			2,		0,			
			particle_generic,
			PART_TRANS|PART_INSTANT,
			NULL,0);

		if (!p)
			return;

		dir[0] = crand();
		dir[1] = crand();
		dir[2] = crand();
		VectorNormalize(dir);
	
		VectorMA(self->org, (45.0 * ratio), dir, p->org);
	}
}


/*
============
CL_Nukeblast
============
*/
void CL_Nukeblast (cl_sustain_t *self)
{
	vec3_t			dir;
	int				i;
	cparticle_t		*p;
	static int colortable0[4] = {185,	155,	125,	95};
	static int colortable1[4] = {185,	155,	125,	95};
	static int colortable2[4] = {255,	255,	255,	255};
	float			ratio, size;
	int				index;

	ratio = 1.0 - (((float)self->endtime - (float)cl.time)/1000.0);
	size = ratio*ratio;

	for(i=0; i<(700/cl_particle_scale->value); i++)
	{
		index = rand()&3;
		p = CL_SetupParticle (
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			colortable0[index],	colortable1[index],	colortable2[index],
			0,	0,	0,
			1-size,	INSTANT_PARTICLE,
			GL_SRC_ALPHA, GL_ONE,
			10*(0.5+ratio*0.5),	-1,			
			particle_generic,
			PART_INSTANT,
			NULL,0);

		if (!p)
			return;


		dir[0] = crand();
		dir[1] = crand();
		dir[2] = crand();
		VectorNormalize(dir);
		VectorScale(dir, -1, p->angle);
		VectorMA(self->org, 200.0*size, dir, p->org); //was 100
		VectorMA(vec3_origin, 400.0*size, dir, p->vel); //was 100

	}
}


/*
==============
CL_WidowSplash
==============
*/
void CL_WidowSplash (vec3_t org)
{
	static int colortable[4] = {2*8,13*8,21*8,18*8};
	int			i;
	cparticle_t	*p;
	vec3_t		dir;

	for (i=0; i<256; i++)
	{
		p = CL_SetupParticle (
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			0,	0,	0,
			rand()&255,	rand()&255,	rand()&255,
			0,	0,	0,
			1.0,		-0.8 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			3,			0,			
			particle_generic,
			0,
			NULL,0);

		if (!p)
			return;

		dir[0] = crand();
		dir[1] = crand();
		dir[2] = crand();
		VectorNormalize(dir);
		VectorMA(org, 45.0, dir, p->org);
		VectorMA(vec3_origin, 40.0, dir, p->vel);
	}
}


/*
==================
CL_Tracker_Explode
==================
*/
void CL_Tracker_Explode (vec3_t	origin)
{
	vec3_t			dir, backdir;
	int				i;
	cparticle_t		*p;

	for (i=0; i<(300/cl_particle_scale->value); i++)
	{
		p = CL_SetupParticle (
			0,		0,		0,
			0,		0,		0,
			0,		0,		0,
			0,		0,		-10, //was 20
			0,		0,		0,
			0,		0,		0,
			1.0,	-0.5,
			GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
			2,		0,			
			particle_generic,
			PART_TRANS,
			NULL,0);

		if (!p)
			return;

		dir[0] = crand();
		dir[1] = crand();
		dir[2] = crand();
		VectorNormalize(dir);
		VectorScale(dir, -1, backdir);

		VectorCopy (origin, p->org); //Knightmare- start at center, not edge
	//	VectorMA(origin, 64, dir, p->org); 
		VectorScale(dir, (crand()*128), p->vel); //was backdir, 64
	}
	
}


/*
===============
CL_TagTrail
===============
*/
void CL_TagTrail (vec3_t start, vec3_t end, int color8)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	int			dec;
	vec3_t color = { color8red(color8), color8green(color8), color8blue(color8)};

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	dec = 5;
	VectorScale (vec, 5, vec);

	while (len >= 0)
	{
		len -= dec;

		CL_SetupParticle (
			0,	0,	0,
			move[0] + crand()*16,	move[1] + crand()*16,	move[2] + crand()*16,
			crand()*5,	crand()*5,	crand()*5,
			0,		0,		20,
			color[0],	color[1],	color[2],
			0,	0,	0,
			1.0,		-1.0 / (0.8+frand()*0.2),
			GL_SRC_ALPHA, GL_ONE,
			1.5,			0,			
			particle_generic,
			0,
			NULL,0);

		VectorAdd (move, vec, move);
	}
}


/*
==========================
CL_ColorExplosionParticles
==========================
*/
void CL_ColorExplosionParticles (vec3_t org, int color8, int run)
{
	int			i;
	vec3_t color = {color8red(color8), color8green(color8), color8blue(color8)};

	for (i=0 ; i<128 ; i++)
	{
		CL_SetupParticle (
			0,	0,	0,
			org[0] + ((rand()%32)-16),	org[1] + ((rand()%32)-16),	org[2] + ((rand()%32)-16),
			(rand()%256)-128,	(rand()%256)-128,	(rand()%256)-128,
			0,		0,		20,
			color[0] + (rand() % run),	color[1] + (rand() % run),	color[2] + (rand() % run),
			0,	0,	0,
			1.0,		-0.4 / (0.6 + frand()*0.2),
			GL_SRC_ALPHA, GL_ONE,
			2,			1,			
			particle_generic,
			0,
			NULL,0);
	}
}



/*
=======================
CL_ParticleSmokeEffect - like the steam effect, but unaffected by gravity
=======================
*/
void CL_ParticleSmokeEffect (vec3_t org, vec3_t dir, float size)
{
	float alpha = fabs(crand())*0.25 + 0.750;

	CL_SetupParticle (
		crand()*180, crand()*100, 0,
		org[0],	org[1],	org[2],
		dir[0],	dir[1],	dir[2],
		0,		0,		10,
		255,	255,	255,
		0,	0,	0,
		alpha,		-1.0,
		GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
		size,			5,			
		particle_smoke,
		PART_TRANS|PART_SHADED|PART_OVERBRIGHT,
		CL_ParticleRotateThink,true);
}


/*
===============
CL_ParticleElectricSparksThink
===============
*/
void CL_ParticleElectricSparksThink (cparticle_t *p, vec3_t org, vec3_t angle, float *alpha, float *size, int *image, float *time)
{
	int i;

	//setting up angle for sparks
	{
		float time1, time2;

		time1 = *time;
		time2 = time1*time1;

		for (i=0;i<2;i++)
			angle[i] = 0.25*(p->vel[i]*time1 + (p->accel[i])*time2);
		angle[2] = 0.25*(p->vel[2]*time1 + (p->accel[2]-PARTICLE_GRAVITY)*time2);
	}

	p->thinknext = true;
}

/*
===============
CL_ElectricParticles

new sparks for Rogue turrets
===============
*/
void CL_ElectricParticles (vec3_t org, vec3_t dir, int count)
{
	int			i, j;
	vec3_t		start;
	float d;

	for (i = 0; i < 40; i++)
	{
		d = rand()&31;
		for (j = 0; j < 3; j++)
			start[j] = org[j] + ((rand()&7)-4) + d*dir[j];
		CL_SetupParticle (
			0,			0,			0,
			start[0],	start[1],	start[2],
			crand()*20,	crand()*20,	crand()*20,
			0,			0,			-PARTICLE_GRAVITY,
			25,			100,		255,
			50,			50,			50,
			1,		-1.0 / (0.5 + frand()*0.3),
			GL_SRC_ALPHA, GL_ONE,
			6,		-3,			
			particle_solid,
			PART_GRAVITY|PART_SPARK,
			CL_ParticleElectricSparksThink, true);
	}
}


//Knightmare- removed for Psychospaz's enhanced particle code
#if 0
/*
===============
CL_SmokeTrail
===============
*/
void CL_SmokeTrail (vec3_t start, vec3_t end, int colorStart, int colorRun, int spacing)
{
	vec3_t		move;
	vec3_t		vec;
	float		len;
	int			j;
	cparticle_t	*p;

	VectorCopy (start, move);
	VectorSubtract (end, start, vec);
	len = VectorNormalize (vec);

	VectorScale (vec, spacing, vec);

	// FIXME: this is a really silly way to have a loop
	while (len > 0)
	{
		len -= spacing;

		if (!free_particles)
			return;
		p = free_particles;
		free_particles = p->next;
		p->next = active_particles;
		active_particles = p;
		VectorClear (p->accel);
		
		p->time = cl.time;

		p->alpha = 1.0;
		p->alphavel = -1.0 / (1+frand()*0.5);
		p->color = colorStart + (rand() % colorRun);
		for (j=0 ; j<3 ; j++)
		{
			p->org[j] = move[j] + crand()*3;
			p->accel[j] = 0;
		}
		p->vel[2] = 20 + crand()*5;

		VectorAdd (move, vec, move);
	}
}


/*
===============
CL_FlameEffects
===============
*/
void CL_FlameEffects (centity_t *ent, vec3_t origin)
{
	int			n, count;
	int			j;
	cparticle_t	*p;

	count = rand() & 0xF;

	for(n=0;n<count;n++)
	{
		if (!free_particles)
			return;
			
		p = free_particles;
		free_particles = p->next;
		p->next = active_particles;
		active_particles = p;
		
		VectorClear (p->accel);
		p->time = cl.time;

		p->alpha = 1.0;
		p->alphavel = -1.0 / (1+frand()*0.2);
		p->color = 226 + (rand() % 4);
		for (j=0 ; j<3 ; j++)
		{
			p->org[j] = origin[j] + crand()*5;
			p->vel[j] = crand()*5;
		}
		p->vel[2] = crand() * -10;
		p->accel[2] = -PARTICLE_GRAVITY;
	}

	count = rand() & 0x7;

	for(n=0;n<count;n++)
	{
		if (!free_particles)
			return;
		p = free_particles;
		free_particles = p->next;
		p->next = active_particles;
		active_particles = p;
		VectorClear (p->accel);
		
		p->time = cl.time;

		p->alpha = 1.0;
		p->alphavel = -1.0 / (1+frand()*0.5);
		p->color = 0 + (rand() % 4);
		for (j=0 ; j<3 ; j++)
		{
			p->org[j] = origin[j] + crand()*3;
		}
		p->vel[2] = 20 + crand()*5;
	}

}


/*
===============
CL_GenericParticleEffect
===============
*/
void CL_GenericParticleEffect (vec3_t org, vec3_t dir, int color, int count, int numcolors, int dirspread, float alphavel)
{
	int			i, j;
	cparticle_t	*p;
	float		d;

	for (i=0 ; i<count ; i++)
	{
		if (!free_particles)
			return;
		p = free_particles;
		free_particles = p->next;
		p->next = active_particles;
		active_particles = p;

		p->time = cl.time;
		if (numcolors > 1)
			p->color = color + (rand() & numcolors);
		else
			p->color = color;

		d = rand() & dirspread;
		for (j=0 ; j<3 ; j++)
		{
			p->org[j] = org[j] + ((rand()&7)-4) + d*dir[j];
			p->vel[j] = crand()*20;
		}

		p->accel[0] = p->accel[1] = 0;
		p->accel[2] = -PARTICLE_GRAVITY;
//		VectorCopy (accel, p->accel);
		p->alpha = 1.0;

		p->alphavel = -1.0 / (0.5 + frand()*alphavel);
//		p->alphavel = alphavel;
	}
}
#endif
//end Knightmare