// cl_tent.c -- client side temporary entities
#include "client.h"

#include "dk_cl_curve.h"

#define TE_MAX       40

#define MAX_GLOBAL_POINTS           400         // max global lightning points
#define MAX_LIGHTNING_POINTS        40          // max points per lightning bolt
#define MAX_LIGHTNING_BOLTS         20          // max visible lightning bolts

/*
typedef struct lightningPoint_s {
   vec3_t position;
   vec3_t velocity;
} lightningPoint_t;
*/

typedef struct lightningInfo_s {
   vec3_t position[MAX_LIGHTNING_POINTS];       // list of positions
   vec3_t velocity[MAX_LIGHTNING_POINTS];       // list of velocities
   char numPoints;                              // -1 == free to use
   float moveofs;                               //
} lightningInfo_t;

typedef struct te_track_s {
   char type;                    // (1=normal ent   2=laser)                     // will have #defines soon
   long flags;                   // flags
   long renderfx;                // renderfx flags
   int track_index;              // index of ent src_index should track
   int src_index;                // index of ent to track track_index
   float forward;                // forward offset
   float right;                  // right offset
   float up;                     // up offset
   int dst_index;                // index of dst ent to track to
   vec3_t dstpos;                // dstpos to track to (if dst_index isn't used)
   float length;                 // length of something (varies)
   short lightning;              // index into lightningInfo
} te_tracker_t;

te_tracker_t trackerList[TE_MAX];
vec3_t forward,right,up;
vec3_t zero_vector={0,0,0};

//lightningPoint_t lightningPoints[MAX_GLOBAL_POINTS];
lightningInfo_t lightningInfo[MAX_LIGHTNING_BOLTS];

te_tracker_t *CL_FindTracker(int track_id);
void CL_AddTrackEnts(void);
void CL_TrackEntityMsg(void);
int doLightning(te_tracker_t *tracker,entity_t *ent);

typedef enum
{
	ex_free, 
	ex_misc, 
	ex_flash, 
	ex_mflash, 
	ex_poly, 
	ex_sprite,
	ex_scale_sprite, 	//	Nelno:	only one frame, scales from large to 0.1
   ex_scale_alpha,
   ex_scale_rotate,
   ex_explosion,
   ex_explosion_alpha,
   ex_tracer,
   ex_explosion_time,         // looped anim, until time expires, alpha's out during last second
} exptype_t;

typedef struct
{
	exptype_t	type;
	entity_t	ent;

   vec3_t   pos;
   vec3_t   end;
   vec3_t   forward;
	int			frames;
	float		light;
	vec3_t	lightcolor;
	float		start;
   float    killtime;
	int		baseframe;
	float		scale_factor;
	float		end_scale;
	float		alpha_scale;
	float		end_alpha;
} explosion_t;


#define	MAX_EXPLOSIONS	96
explosion_t	cl_explosions[MAX_EXPLOSIONS];
#define	MAX_BEAMS	32
typedef struct
{
	int		entity;
	struct model_s	*model;
	int		endtime;
	vec3_t	offset;
	vec3_t	start, end;
} beam_t;
beam_t		cl_beams[MAX_BEAMS];

#define	MAX_LASERS	32
typedef struct
{
	entity_t	ent;
	int endtime;
   int track_dst;             // pulls dst position from cl_entities[track_index].current
} laser_t;
laser_t		cl_lasers[MAX_LASERS];


void CL_BlasterParticles (vec3_t org, vec3_t dir);
void CL_ExplosionParticles (vec3_t org);
void CL_SmallExplosionParticles (vec3_t org);
void CL_BFGExplosionParticles (vec3_t org);
struct sfx_s	*cl_sfx_ric1;
struct sfx_s	*cl_sfx_ric2;
struct sfx_s	*cl_sfx_ric3;
struct sfx_s	*cl_sfx_lashit;
struct sfx_s	*cl_sfx_spark5;
struct sfx_s	*cl_sfx_spark6;
struct sfx_s	*cl_sfx_spark7;
struct sfx_s	*cl_sfx_railg;
struct sfx_s	*cl_sfx_rockexp;
struct sfx_s	*cl_sfx_grenexp;
struct sfx_s	*cl_sfx_watrexp;

struct sfx_s	*cl_sfx_footsteps[4];
struct model_s	*cl_mod_explode;
struct model_s	*cl_mod_smoke;
struct model_s	*cl_mod_flash;
struct model_s	*cl_mod_parasite_segment;
struct model_s	*cl_mod_grapple_cable;
struct model_s	*cl_mod_parasite_tip;
struct model_s	*cl_mod_explo4;
struct model_s	*cl_mod_bfg_explo;
struct model_s	*cl_mod_powerscreen;

//	Nelno:	Daikatana temp entity models
struct	model_s	*cl_mod_explosion [2];
struct	sfx_s	*cl_sfx_explosion [1];
struct	model_s	*cl_mod_novahit;
struct	sfx_s	*cl_sfx_novahit;
struct	model_s	*cl_mod_kcorehit;
struct	model_s	*cl_mod_disctrail;
struct   model_s  *cl_mod_firesmoke;

/*
=================
CL_RegisterTEntSounds
=================
*/
void CL_RegisterTEntSounds (void)
{
//	int		i;
//	char	name[MAX_QPATH];

	//	Nelno:	don't need Quake 2 sounds any longer.  Buh BYE!
/*
	cl_sfx_ric1 = S_RegisterSound ("world/ric1.wav");
	cl_sfx_ric2 = S_RegisterSound ("world/ric2.wav");
	cl_sfx_ric3 = S_RegisterSound ("world/ric3.wav");
	cl_sfx_lashit = S_RegisterSound("weapons/lashit.wav");
	cl_sfx_spark5 = S_RegisterSound ("world/spark5.wav");
	cl_sfx_spark6 = S_RegisterSound ("world/spark6.wav");
	cl_sfx_spark7 = S_RegisterSound ("world/spark7.wav");
	cl_sfx_railg = S_RegisterSound ("weapons/railgf1a.wav");
	cl_sfx_rockexp = S_RegisterSound ("weapons/rocklx1a.wav");
	cl_sfx_grenexp = S_RegisterSound ("weapons/grenlx1a.wav");
	cl_sfx_watrexp = S_RegisterSound ("weapons/xpld_wat.wav");

	S_RegisterSound ("player/land1.wav");

	S_RegisterSound ("player/fall2.wav");
	S_RegisterSound ("player/fall1.wav");

	for (i=0 ; i<4 ; i++)
	{
		Com_sprintf (name, sizeof(name), "player/step%i.wav", i+1);
		cl_sfx_footsteps[i] = S_RegisterSound (name);
	}
*/
	cl_sfx_explosion [0] = S_RegisterSound ("explosions/explode1.wav");
}	
/*
=================
CL_RegisterTEntModels
=================
*/
void CL_RegisterTEntModels (void)
{
	//	Nelno:	don't need Quake 2 models any longer
	cl_mod_explode = NULL;
	cl_mod_smoke = NULL;
	cl_mod_flash = NULL;

	//	cache explosion sprite
	cl_mod_explosion [0] = re.RegisterModel ("models/global/we_expl.sp2", RESOURCE_GLOBAL);
	cl_mod_explosion [1] = re.RegisterModel ("models/global/we_expla.sp2", RESOURCE_GLOBAL);

	cl_mod_novahit = re.RegisterModel ("models/e4/we_novahit.sp2", RESOURCE_GLOBAL);
	cl_mod_disctrail = re.RegisterModel ("models/e2/we_disctr.sp2", RESOURCE_GLOBAL);
	cl_mod_kcorehit = re.RegisterModel ("models/e4/we_kcorehit.sp2", RESOURCE_GLOBAL);
	cl_mod_firesmoke = re.RegisterModel ("models/global/we_expla.sp2", RESOURCE_GLOBAL);
}	
/*
=================
CL_ClearTEnts
=================
*/
void CL_ClearTEnts (void)
{
	memset (cl_beams, 0, sizeof(cl_beams));
	memset (cl_explosions, 0, sizeof(cl_explosions));
	memset (cl_lasers, 0, sizeof(cl_lasers));
   memset(trackerList,0,sizeof(trackerList));

//   memset(lightningPoints,0,sizeof(lightningPoints));
   memset(lightningInfo,-1,sizeof(lightningInfo));          // make sure numpoints inits to -1
}
/*
=================
CL_AllocExplosion
=================
*/
explosion_t *CL_AllocExplosion (void)
{
	int		i;
	int		time;
	int		index;
	
	for (i=0 ; i<MAX_EXPLOSIONS ; i++)
	{
		if (cl_explosions[i].type == ex_free)
		{
			memset (&cl_explosions[i], 0, sizeof (cl_explosions[i]));
			return &cl_explosions[i];
		}
	}
// find the oldest explosion
	time = cl.time;
	index = 0;
	for (i=0 ; i<MAX_EXPLOSIONS ; i++)
		if (cl_explosions[i].start < time)
		{
			time = cl_explosions[i].start;
			index = i;
		}
	memset (&cl_explosions[index], 0, sizeof (cl_explosions[index]));
	return &cl_explosions[index];
}

///////////////////////////////////////////////////////////////////////////////
//	CL_NovaHit
//
///////////////////////////////////////////////////////////////////////////////

void	CL_NovaHit (vec3_t org, vec3_t angles)
{
	explosion_t	*ex;

	ex = CL_AllocExplosion ();

	ex->ent.origin [0] = org [0];
	ex->ent.origin [1] = org [1];
	ex->ent.origin [2] = org [2];

	ex->ent.angles [0] = angles [0];
	ex->ent.angles [1] = angles [1];
	ex->ent.angles [2] = angles [2];

	ex->type = ex_scale_sprite;
	ex->ent.flags = RF_FULLBRIGHT | RF_TRANSLUCENT | SPR_ORIENTED;

	ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;

	ex->light = 0;
	ex->lightcolor[0] = 1.0;
	ex->lightcolor[1] = 0.25;
	ex->lightcolor[2] = 0.25;
	
	ex->ent.render_scale [0] = ex->ent.render_scale [1] = ex->ent.render_scale [2] = (rand () / (RAND_MAX << 1)) + 1.0;
	ex->ent.alpha = 0.9;
	ex->scale_factor = 0.995;
	ex->end_scale = 0.2;
	ex->alpha_scale = 0.95;
	ex->end_alpha = 0.025;

	ex->ent.model = cl_mod_novahit;

	ex->frames = 1;

	//	Nelno:	start cool hissing sound here
//		S_StartSound (pos, 0, 0, cl_sfx_explosion [0], 1, ATTN_NORM, 0);
}

///////////////////////////////////////////////////////////////////////////////
//	CL_DiscusTrail
//
///////////////////////////////////////////////////////////////////////////////

void	CL_DiscusTrail (vec3_t org, vec3_t angles)
{
	explosion_t	*ex;

	ex = CL_AllocExplosion ();

	ex->ent.origin [0] = org [0];
	ex->ent.origin [1] = org [1];
	ex->ent.origin [2] = org [2];

	ex->ent.angles [0] = angles [0];
	ex->ent.angles [1] = angles [1];
	ex->ent.angles [2] = angles [2];

	ex->type = ex_scale_sprite;
	ex->ent.flags = RF_FULLBRIGHT | RF_TRANSLUCENT | SPR_ORIENTED;

	ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;

	ex->light = 0;
	ex->lightcolor[0] = 1.0;
	ex->lightcolor[1] = 0.25;
	ex->lightcolor[2] = 0.25;
	
	ex->ent.render_scale [0] = ex->ent.render_scale [1] = ex->ent.render_scale [2] = (rand () / (RAND_MAX << 1)) + 1.0;
	ex->ent.alpha = 1.0;
	ex->scale_factor = 0.95;
	ex->end_scale = 0.2;
	ex->alpha_scale = 0.88;
	ex->end_alpha = 0.025;
//	ex->alpha_scale = 1.0;
//	ex->end_alpha = 0.0;

	ex->ent.model = cl_mod_disctrail;

	ex->frames = 1;

	//	Nelno:	start cool hissing sound here
//		S_StartSound (pos, 0, 0, cl_sfx_explosion [0], 1, ATTN_NORM, 0);
}

///////////////////////////////////////////////////////////////////////////////
//	CL_KcoreHit
///////////////////////////////////////////////////////////////////////////////
void	CL_KcoreHit (vec3_t org, vec3_t angles)
{
	explosion_t	*ex;

	ex = CL_AllocExplosion ();

	ex->ent.origin [0] = org [0];
	ex->ent.origin [1] = org [1];
	ex->ent.origin [2] = org [2];

	ex->ent.angles [0] = angles [0];
	ex->ent.angles [1] = angles [1];
	ex->ent.angles [2] = angles [2];

	ex->type = ex_scale_sprite;
	ex->ent.flags = RF_FULLBRIGHT | SPR_ALPHACHANNEL | RF_TRANSLUCENT | SPR_ORIENTED;

	ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;

	ex->light = 0;
	ex->lightcolor[0] = 1.0;
	ex->lightcolor[1] = 0.25;
	ex->lightcolor[2] = 0.25;
	
	ex->ent.render_scale [0] = ex->ent.render_scale [1] = ex->ent.render_scale [2] = (rand () / (RAND_MAX << 1)) + 1.0;
	ex->ent.alpha = 1.0;
	ex->scale_factor = 0.90;
	ex->end_scale = 0.05;
	ex->alpha_scale = 0.90;
	ex->end_alpha = 0.05;

	ex->ent.model = cl_mod_kcorehit;

	ex->frames = 1;

	//	Nelno:	start cool hissing sound here
//		S_StartSound (pos, 0, 0, cl_sfx_explosion [0], 1, ATTN_NORM, 0);
}

//---------------------------------------------------------------------------
// CL_Explosion()
//---------------------------------------------------------------------------
void CL_Explosion(vec3_t org,vec3_t angles,float scale,int numframes,int modelindex,short flags)
{
	explosion_t	*ex;

	ex = CL_AllocExplosion ();

	ex->ent.origin [0] = org [0];
	ex->ent.origin [1] = org [1];
	ex->ent.origin [2] = org [2];

	ex->ent.angles [0] = angles [0];
	ex->ent.angles [1] = angles [1];
	ex->ent.angles [2] = angles [2];

   if (flags & CLE_FADEALPHA)
	   ex->type=ex_explosion_alpha;
   else
      ex->type=ex_explosion;

	ex->ent.flags = RF_FULLBRIGHT|SPR_ALPHACHANNEL|RF_TRANSLUCENT;
   if (angles[0] >= 0 || angles[1] >= 0 || angles[2] >= 0)
      ex->ent.flags |= SPR_ORIENTED;
	ex->light = 0;
//	ex->lightcolor[0] = 1.0;
//	ex->lightcolor[1] = 0.25;
//	ex->lightcolor[2] = 0.25;

	ex->ent.render_scale[0]=ex->ent.render_scale[1]=ex->ent.render_scale[2]=scale;
   ex->ent.model=cl.model_draw[modelindex];
	ex->frames=numframes;
   ex->baseframe=0;
	ex->start=cl.frame.servertime-CL_FRAME_MILLISECONDS;
}

//---------------------------------------------------------------------------
// CL_ScaleRotate()
//---------------------------------------------------------------------------
void CL_ScaleRotate(vec3_t pos,float start_scale,float end_scale,float scale_time,int modelindex)
{
	explosion_t	*ex;

	ex = CL_AllocExplosion ();

	ex->ent.origin [0] = pos [0];
	ex->ent.origin [1] = pos [1];
	ex->ent.origin [2] = pos [2];

	ex->ent.angles [0] = crand()*90;
	ex->ent.angles [1] = crand()*90;
	ex->ent.angles [2] = 0;

	ex->type = ex_scale_rotate;
	ex->ent.flags = RF_FULLBRIGHT | RF_TRANSLUCENT | SPR_ORIENTED;

	ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;

	ex->light = 0;
	ex->lightcolor[0] = 7+crand()*7;
	ex->lightcolor[1] = 7+crand()*7;
	ex->lightcolor[2] = 0;
	
	ex->ent.alpha = .2;
	ex->ent.render_scale [0] = ex->ent.render_scale [1] = ex->ent.render_scale [2] = start_scale;
	
   ex->scale_factor = scale_time*1000;          // length of time to scale
   ex->alpha_scale = start_scale;               //
	ex->end_scale = end_scale;                   // 

   ex->ent.model=cl.model_draw[modelindex];

	ex->frames = 1;
}

//---------------------------------------------------------------------------
// CL_ScaleAlpha()
//---------------------------------------------------------------------------
void CL_ScaleAlpha(vec3_t org,vec3_t angles,float scale,int numframes,int modelindex,
                   float alpha_scale,float alpha_end,short light,vec3_t color)
{
	explosion_t	*ex;

	ex = CL_AllocExplosion ();

	ex->ent.origin [0] = org [0];
	ex->ent.origin [1] = org [1];
	ex->ent.origin [2] = org [2];

	ex->ent.angles [0] = angles [0];
	ex->ent.angles [1] = angles [1];
	ex->ent.angles [2] = angles [2];

	ex->type = ex_scale_alpha;
	ex->ent.flags = RF_FULLBRIGHT | RF_TRANSLUCENT | SPR_ORIENTED;

	ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;

	ex->light = light;
	ex->lightcolor[0] = color[0];
	ex->lightcolor[1] = color[1];
	ex->lightcolor[2] = color[2];
	
	ex->ent.render_scale [0] = ex->ent.render_scale [1] = ex->ent.render_scale [2] = scale;

	ex->ent.alpha = 1.0;
	ex->alpha_scale = alpha_scale;
	ex->end_alpha = alpha_end;

   ex->ent.model=cl.model_draw[modelindex];

	ex->frames = numframes;
   ex->baseframe=0;
}

//---------------------------------------------------------------------------
// CL_TimedFire()
//---------------------------------------------------------------------------
void CL_TimedFire(vec3_t org,float burntime,short firstFrame)
{
	explosion_t	*ex;

	ex = CL_AllocExplosion ();

	ex->ent.origin [0] = org [0];
	ex->ent.origin [1] = org [1];
	ex->ent.origin [2] = org [2];

	ex->ent.flags = RF_TRANSLUCENT;
   ex->ent.alpha=.6;

   ex->type=ex_explosion_time;

	ex->light = 0;
//	ex->lightcolor[0] = 1.0;            // 1
//	ex->lightcolor[1] = 0.25;           // .74
//	ex->lightcolor[2] = 0.25;           // .11

	ex->ent.render_scale[0]=ex->ent.render_scale[1]=ex->ent.render_scale[2]=1;
	ex->ent.model = cl_mod_firesmoke;
	ex->frames=8;
   ex->baseframe=0;
	ex->start=cl.frame.servertime-CL_FRAME_MILLISECONDS;
   ex->start += firstFrame*CL_FRAME_MILLISECONDS;           // sync start with time where 'firstframe' would be
   ex->killtime = ex->start + burntime*1000;
   ex->end_alpha=ex->ent.alpha;
}

//---------------------------------------------------------------------------
// CL_Smoke2()
//---------------------------------------------------------------------------
void CL_Smoke2(vec3_t origin, float scale, float count, int spread, float maxvel)
{
	cparticle_t *p;
	float r;
	int j;

	while (count--)
	{
	   	if (!free_comparticles)
			return;

   		// drop less particles as it flies
   		p = free_comparticles;
		free_comparticles = p->next;
		p->next = active_comparticles;
		active_comparticles = p;
   		VectorClear (p->accel);

		p->time = cl.time;
      p->pscale=scale;

		p->type = PARTICLE_SMOKE;
		p->alpha = .5;
   		p->alphavel = -1.0 / (1+frand()*0.2);
		p->color = 4 + (rand()&7);
		for (j=0 ; j<3 ; j++)
		{
			//r = crand() > .5 ? crand()*10 : -crand()*10;
         r = spread ? (rand () % (spread << 1)) - spread : 0;
   			p->org[j] = origin[j] + r;
			if (j)
				p->vel[j] = crand()*maxvel;
   		}

		p->accel[2] = maxvel;
	}
}

//---------------------------------------------------------------------------
// CL_Smoke()
//---------------------------------------------------------------------------
void CL_Smoke(vec3_t origin, float scale, float count, int spread, float maxvel)
{
	cparticle_t *p;
	float r;
	int j;

	while (count--)
	{
	   	if (!free_comparticles)
			return;

   		// drop less particles as it flies
   		p = free_comparticles;
		free_comparticles = p->next;
		p->next = active_comparticles;
		active_comparticles = p;
   		VectorClear (p->accel);

		p->time = cl.time;
      p->pscale=scale;

		p->type = PARTICLE_SMOKE;
		p->alpha = 1.0;
   		p->alphavel = -1.0 / (1+frand()*0.2);
		p->color = 4 + (rand()&7);
		for (j=0 ; j<3 ; j++)
		{
			//r = crand() > .5 ? crand()*10 : -crand()*10;
         r = spread ? (rand () % (spread << 1)) - spread : 0;
   			p->org[j] = origin[j] + r;
			if (j)
				p->vel[j] = crand()*maxvel;
   		}

		p->accel[2] = maxvel;
	}
}

//---------------------------------------------------------------------------
// CL_Fire()
//---------------------------------------------------------------------------
void CL_Fire(vec3_t origin,float size)
{
   cparticle_t *p;
   float r;
   int j;

   while (size--)
   {
   	if (!free_comparticles)
			return;

   	// drop less particles as it flies
	   {
   		p = free_comparticles;
			free_comparticles = p->next;
	   	p->next = active_comparticles;
		   active_comparticles = p;
   		VectorClear (p->accel);
	
			p->time = cl.time;

	   	p->type = PARTICLE_SPARKS;
		   p->alpha = 1.0;
   		p->alphavel = -1.0 / (1+frand()*0.2);
			p->color = 4 + (rand()&7);
	   	for (j=0 ; j<3 ; j++)
		   {
            r = crand() > .5 ? crand()*10 : -crand()*10;
   			p->org[j] = origin[j] + r;
            if (j)
	   		   p->vel[j] = crand()*20;
   		}
         p->accel[2] = -40;
   	}
	}
}

//---------------------------------------------------------------------------
// CL_Tracer()
//---------------------------------------------------------------------------
void CL_Tracer(vec3_t start,vec3_t end,vec3_t forward,float tracer_len,float killtime)
{
	explosion_t	*ex;
   float length;

   length=sqrt((end[0]-start[0])*(end[0]-start[0]) + 
               (end[1]-start[1])*(end[1]-start[1]) + 
               (end[2]-start[2])*(end[2]-start[2]));

	ex = CL_AllocExplosion ();
	ex->type = ex_tracer;

   VectorCopy(start,ex->pos);
   VectorCopy(end,ex->end);
   VectorCopy(forward,ex->forward);
   VectorCopy(forward,ex->ent.mins);

   ex->ent.flags=RF_TRANSLUCENT|RF_BEAM|RF_TRACER;
	ex->ent.render_scale[0]=ex->ent.render_scale[1]=ex->ent.render_scale[2]=60;         // LENGTH
	ex->ent.alpha = .6;
   ex->ent.frame = -.8 * 1000;                     // LASER SIZE
   ex->ent.skinnum=GC_WHITE;

   ex->end_scale = tracer_len;                     // LENGTH OF LASER
   ex->scale_factor = ex->end_scale / length;      // BACKSTEP (percent to move back, for calcing end tail)

	ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;
   ex->killtime = ex->start + killtime*1000;
}

/*
=================
CL_SmokeAndFlash
=================
*/
void CL_SmokeAndFlash(vec3_t origin)
{
	explosion_t	*ex;

	ex = CL_AllocExplosion ();
	VectorCopy (origin, ex->ent.origin);
	ex->type = ex_misc;
	ex->frames = 4;
	ex->ent.flags = RF_TRANSLUCENT;
	ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;
	ex->ent.model = cl_mod_smoke;
	ex->ent.render_scale [0] = ex->ent.render_scale [1] = ex->ent.render_scale [2] = 1.0;

	ex = CL_AllocExplosion ();
	VectorCopy (origin, ex->ent.origin);
	ex->type = ex_flash;
	ex->ent.flags = RF_FULLBRIGHT;
	ex->frames = 2;
	ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;
	ex->ent.render_scale [0] = ex->ent.render_scale [1] = ex->ent.render_scale [2] = 1.0;
	ex->ent.model = cl_mod_flash;
}
/*
=================
CL_ParseParticles
=================
*/
void CL_ParseParticles (void)
{
	int		color, count;
	vec3_t	pos, dir;
	MSG_ReadPos (&net_message, pos);
	MSG_ReadDir (&net_message, dir);
	color = MSG_ReadByte (&net_message);
	count = MSG_ReadByte (&net_message);
	CL_ParticleEffect (pos, dir, color, count);
}
/*
=================
CL_ParseBeam
=================
*/
int CL_ParseBeam (struct model_s *model)
{
	int		ent;
	vec3_t	start, end;
	beam_t	*b;
	int		i;
	
	ent = MSG_ReadShort (&net_message);
	
	MSG_ReadPos (&net_message, start);
	MSG_ReadPos (&net_message, end);
// override any beam with the same entity
	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
		if (b->entity == ent)
		{
			b->entity = ent;
			b->model = model;
			b->endtime = cl.time + 200;
			VectorCopy (start, b->start);
			VectorCopy (end, b->end);
			VectorClear (b->offset);
			return ent;
		}
// find a free beam
	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
	{
		if (!b->model || b->endtime < cl.time)
		{
			b->entity = ent;
			b->model = model;
			b->endtime = cl.time + 200;
			VectorCopy (start, b->start);
			VectorCopy (end, b->end);
			VectorClear (b->offset);
			return ent;
		}
	}
	Com_Printf ("beam list overflow!\n");	
	return ent;
}
/*
=================
CL_ParseBeam2
=================
*/
int CL_ParseBeam2 (struct model_s *model)
{
	int		ent;
	vec3_t	start, end, offset;
	beam_t	*b;
	int		i;
	
	ent = MSG_ReadShort (&net_message);
	
	MSG_ReadPos (&net_message, start);
	MSG_ReadPos (&net_message, end);
	MSG_ReadPos (&net_message, offset);

// override any beam with the same entity
	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
		if (b->entity == ent)
		{
			b->entity = ent;
			b->model = model;
			b->endtime = cl.time + 200;
			VectorCopy (start, b->start);
			VectorCopy (end, b->end);
			VectorCopy (offset, b->offset);
			return ent;
		}

// find a free beam
	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
	{
		if (!b->model || b->endtime < cl.time)
		{
			b->entity = ent;
			b->model = model;
			b->endtime = cl.time + 200;
			VectorCopy (start, b->start);
			VectorCopy (end, b->end);
			VectorCopy (offset, b->offset);
			return ent;
		}
	}
	Com_Printf ("beam list overflow!\n");	
	return ent;
}

/*
=================
CL_ParseLaser
=================
*/
void CL_ParseLaser(vec3_t start,vec3_t end,int track_dst)
{
	laser_t	*l;
	int		i;

	for (i=0, l=cl_lasers ; i< MAX_LASERS ; i++, l++)
	{
		if (l->endtime < cl.time)
		{
			l->ent.flags = RF_TRANSLUCENT | RF_BEAM;
			VectorCopy (start, l->ent.origin);
			VectorCopy (end, l->ent.oldorigin);
			l->ent.alpha = 0.30;
			l->ent.model = NULL;
			l->ent.frame = 4;
			l->endtime = cl.time + CL_FRAME_MILLISECONDS;
         l->track_dst=track_dst;
			return;
		}
	}
}


/*
=================
CL_ParseTEnt
=================
*/
static byte splash_color[] = {0x00, 0xe0, 0xb0, 0x50, 0xd0, 0xe0, 0xe8};
void CL_ParseTEnt (void)
{
	short	light;
	int		type;
	vec3_t	pos, pos2, dir, angles, lt_color;
	explosion_t	*ex;
	int		cnt, color, r, ent, speed;
	float	scale,alpha_scale,alpha_end;
	
	//	read type
	type = MSG_ReadByte (&net_message);
	
	switch (type)
	{
	case TE_BLOOD:			// bullet hitting flesh
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		CL_BloodSplash (pos, dir, 0xe8, 60);
		break;
	case TE_BLOOD_FADE:
		MSG_ReadPos(&net_message, pos);
		MSG_ReadDir(&net_message, dir);
		CL_BloodFade(pos, dir, 0xe8, 60);
		break;
	case TE_BLUE_SPARKS:			// disruptor rod
		MSG_ReadPos (&net_message, pos);
		//MSG_ReadDir (&net_message, dir);
		CL_ParticleEffect_Complex (pos, vec3_origin, 0x00, 16, PARTICLE_BLUE_SPARKS);
		break;
	case TE_GUNSHOT:			// bullet hitting wall
	case TE_SPARKS:
	case TE_BULLET_SPARKS:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		if (type == TE_GUNSHOT)
			CL_ParticleEffect (pos, dir, 0, 40);
		else
			CL_ParticleEffect_Complex (pos, dir, 0x10, 6, PARTICLE_SPARKS);
		if (type != TE_SPARKS)
		{
			CL_SmokeAndFlash(pos);
			// impact sound
			cnt = rand()&15;
			if (cnt == 1)
				S_StartSound (pos, 0, 0, cl_sfx_ric1, 1, ATTN_NORM, 0);
			else if (cnt == 2)
				S_StartSound (pos, 0, 0, cl_sfx_ric2, 1, ATTN_NORM, 0);
			else if (cnt == 3)
				S_StartSound (pos, 0, 0, cl_sfx_ric3, 1, ATTN_NORM, 0);
		}
		break;
		
	case TE_SCREEN_SPARKS:
	case TE_SHIELD_SPARKS:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		if (type == TE_SCREEN_SPARKS)
			CL_ParticleEffect (pos, dir, 0xd0, 40);
		else
			CL_ParticleEffect (pos, dir, 0xb0, 40);
		//FIXME : replace or remove this sound
		S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
		break;
		
	case TE_SHOTGUN:			// bullet hitting wall
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		CL_ParticleEffect (pos, dir, 0, 20);
		CL_SmokeAndFlash(pos);
		break;
	case TE_SPLASH:			// bullet hitting water
		cnt = MSG_ReadByte (&net_message);
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		r = MSG_ReadByte (&net_message);
		if (r > 6)
			color = 0x00;
		else
			color = splash_color[r];
		CL_ParticleEffect (pos, dir, color, cnt);
		if (r == SPLASH_SPARKS)
		{
			r = rand() & 3;
			if (r == 0)
				S_StartSound (pos, 0, 0, cl_sfx_spark5, 1, ATTN_STATIC, 0);
			else if (r == 1)
				S_StartSound (pos, 0, 0, cl_sfx_spark6, 1, ATTN_STATIC, 0);
			else
				S_StartSound (pos, 0, 0, cl_sfx_spark7, 1, ATTN_STATIC, 0);
		}
		break;
	case TE_LASER_SPARKS:
		cnt = MSG_ReadByte (&net_message);
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		color = MSG_ReadByte (&net_message);
		CL_ParticleEffect_Complex (pos, dir, 0x10, cnt, PARTICLE_SPARKS);
//		CL_ParticleEffect2 (pos, dir, color, cnt);
		break;
	case TE_ICE_CRYSTALS:
		MSG_ReadPos(&net_message, pos);
		MSG_ReadDir(&net_message, dir);
		cnt=MSG_ReadByte(&net_message);
		CL_IceCrystals(pos,dir,cnt);
		break;
	case TE_BLASTER:			// blaster hitting wall
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		CL_BlasterParticles (pos, dir);
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->ent.angles[0] = acos(dir[2])/M_PI*180;
		ex->ent.angles[2] = atan2(dir[1], dir[0])/M_PI*180;
		ex->type = ex_misc;
		ex->ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
		ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;
		ex->light = 150;
		ex->lightcolor[0] = 1;
		ex->lightcolor[1] = 1;
		ex->ent.model = cl_mod_explode;
		ex->frames = 4;
		S_StartSound (pos,  0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
		break;
		
	case TE_RAILTRAIL:			// railgun effect
		MSG_ReadPos (&net_message, pos);
		MSG_ReadPos (&net_message, pos2);
		CL_RailTrail (pos, pos2);
		S_StartSound (pos2, 0, 0, cl_sfx_railg, 1, ATTN_NORM, 0);
		break;

	case TE_PLASMATRAIL:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadPos (&net_message, pos2);
		CL_PlasmaTrail (pos, pos2);
		S_StartSound (pos2, 0, 0, cl_sfx_railg, 1, ATTN_NORM, 0);
		break;
		//	Nelno:	TE_EXPLOSION2 is now just the same as TE_EXPLOSION1 but without sound
	case TE_EXPLOSION2:
	case TE_EXPLOSION1:
	case TE_ROCKET_EXPLOSION:
	case TE_ROCKET_EXPLOSION_WATER:
		MSG_ReadPos (&net_message, pos);
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->type = ex_poly;
		ex->ent.flags = RF_FULLBRIGHT | SPR_ALPHACHANNEL | RF_TRANSLUCENT;
		ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;
		ex->light = 200;
		ex->lightcolor[0] = 1.0;
		ex->lightcolor[1] = 0.5;
		ex->lightcolor[2] = 0.0;

		ex->ent.render_scale [0] = ex->ent.render_scale [1] = ex->ent.render_scale [2] = 1.0;//(rand () / (RAND_MAX << 1)) + 0.75;

		if (rand () & 1)
		{
			ex->ent.model = cl_mod_explosion [1];
			ex->frames = 8;
		}
		else
		{
			ex->ent.model = cl_mod_explosion [0];
			ex->frames = 7;
		}

		CL_ExplosionParticles (pos);
		
		if (type != TE_EXPLOSION2)
		{
			if (type == TE_ROCKET_EXPLOSION_WATER)
				S_StartSound (pos, 0, 0, cl_sfx_explosion [0], 1, ATTN_NORM, 0);
			else
				S_StartSound (pos, 0, 0, cl_sfx_explosion [0], 1, ATTN_NORM, 0);
		}
		break;

	case TE_EXPLOSION_SMALL1:
	case TE_EXPLOSION_SMALL2:
		MSG_ReadPos (&net_message, pos);
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->type = ex_poly;
		ex->ent.flags = RF_FULLBRIGHT | SPR_ALPHACHANNEL | RF_TRANSLUCENT;
		ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;
		ex->light = 50;
		ex->lightcolor[0] = 1.0;
		ex->lightcolor[1] = 0.5;
		ex->lightcolor[2] = 0.0;

		ex->ent.render_scale [0] = ex->ent.render_scale [1] = ex->ent.render_scale [2] = (rand () / (RAND_MAX << 1)) + 0.55;

		if (rand () & 1)
		{
			ex->ent.model = cl_mod_explosion [1];
			ex->frames = 8;
		}
		else
		{
			ex->ent.model = cl_mod_explosion [0];
			ex->frames = 7;
		}

		CL_SmallExplosionParticles (pos);

		if (type != TE_EXPLOSION_SMALL2)
		{
			if (type == TE_ROCKET_EXPLOSION_WATER)
				S_StartSound (pos, 0, 0, cl_sfx_explosion [0], 1, ATTN_NORM, 0);
			else
				S_StartSound (pos, 0, 0, cl_sfx_explosion [0], 1, ATTN_NORM, 0);
		}
		break;

	case TE_NOVAHIT:
		//	read position
		MSG_ReadPos (&net_message, pos);

		//	read facing angle
		angles [0] = MSG_ReadAngle16 (&net_message);
		angles [1] = MSG_ReadAngle16 (&net_message);
		angles [2] = MSG_ReadAngle16 (&net_message);

//		CL_NovaHit (pos, angles);
		CL_KcoreHit (pos, angles);
		break;

	case TE_EXPLOSION:
		//	read position
		MSG_ReadPos (&net_message, pos);

		//	read facing angle
		angles [0] = MSG_ReadAngle16 (&net_message);
		angles [1] = MSG_ReadAngle16 (&net_message);
		angles [2] = MSG_ReadAngle16 (&net_message);

      scale=MSG_ReadFloat(&net_message);

      cnt=MSG_ReadByte(&net_message);              // SCALE
      r=MSG_ReadLong(&net_message);                // NUMFRAMES
      light=MSG_ReadByte(&net_message);            // FLAGS

      CL_Explosion(pos,angles,scale,cnt,r,light);
		break;

	case TE_SMOKE:
		//	read position
		MSG_ReadPos (&net_message, pos);
		scale=MSG_ReadFloat(&net_message);
		cnt=MSG_ReadFloat(&net_message);
      alpha_scale=MSG_ReadFloat(&net_message);
      alpha_end=MSG_ReadFloat(&net_message);
		CL_Smoke (pos, scale, cnt, alpha_scale, alpha_end);
		break;

	case TE_FIRE:
		//	read position
		MSG_ReadPos (&net_message, pos);
      scale=MSG_ReadFloat(&net_message);
      CL_Fire(pos,scale);
		break;

	case TE_FIRESMOKE:
		//	read position
		MSG_ReadPos(&net_message, pos);           // POSITION
      scale=MSG_ReadFloat(&net_message);        // BURN TIME
      light=MSG_ReadByte(&net_message);         // FIRST FRAME
      CL_TimedFire(pos,scale,light);
		break;

	case TE_SCALEROTATE:
		//	read position
		MSG_ReadPos (&net_message, pos);
      scale=MSG_ReadFloat(&net_message);           // START scale size
      alpha_scale=MSG_ReadFloat(&net_message);     // END scale size
      alpha_end=MSG_ReadFloat(&net_message);       // period of TIME to scale
      r=MSG_ReadLong(&net_message);                // MODELINDEX

      CL_ScaleRotate(pos,scale,alpha_scale,alpha_end,r);
		break;

	case TE_ALPHASCALE:
		//	read position
		MSG_ReadPos (&net_message, pos);

		//	read facing angle
		angles [0] = MSG_ReadAngle16 (&net_message);
		angles [1] = MSG_ReadAngle16 (&net_message);
		angles [2] = MSG_ReadAngle16 (&net_message);

      scale=MSG_ReadFloat(&net_message);

      cnt=MSG_ReadByte(&net_message);
      r=MSG_ReadLong(&net_message);

      alpha_scale=MSG_ReadFloat(&net_message);
      alpha_end=MSG_ReadFloat(&net_message);

      light=MSG_ReadShort(&net_message);
      if (light)
         MSG_ReadPos(&net_message,lt_color);

      CL_ScaleAlpha(pos,angles,scale,cnt,r,alpha_scale,alpha_end,light,lt_color);
		break;

	case TE_TRACER:
		MSG_ReadPos(&net_message,pos);               // START
		MSG_ReadPos(&net_message,pos2);              // END
		MSG_ReadPos(&net_message,dir);               // FORWARD vector
      alpha_scale=MSG_ReadFloat(&net_message);     // LENGTH
      scale=MSG_ReadFloat(&net_message);           // killtime

      CL_Tracer(pos,pos2,dir,alpha_scale,scale);
   break;

	case TE_BFG_EXPLOSION:
		MSG_ReadPos (&net_message, pos);
		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->type = ex_poly;
		ex->ent.flags = RF_FULLBRIGHT;
		ex->start = cl.frame.servertime - CL_FRAME_MILLISECONDS;
		ex->light = 350;
		ex->lightcolor[0] = 0.0;
		ex->lightcolor[1] = 1.0;
		ex->lightcolor[2] = 0.0;
		ex->ent.model = cl_mod_bfg_explo;
		ex->ent.flags |= RF_TRANSLUCENT;
		ex->ent.alpha = 0.30;
		ex->frames = 4;
		break;

	case TE_BFG_BIGEXPLOSION:
		MSG_ReadPos (&net_message, pos);
		CL_BFGExplosionParticles (pos);
		break;

	case TE_BFG_LASER:
      MSG_ReadPos(&net_message, pos);
      MSG_ReadPos(&net_message, pos2);
      cnt=MSG_ReadLong(&net_message);           // index of ent to track... if 0, always uses pos2 as dst origin

		CL_ParseLaser (pos,pos2,cnt);
		break;

	case TE_BUBBLETRAIL:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadPos (&net_message, pos2);
		CL_BubbleTrail (pos, pos2);
		break;
	case TE_PARASITE_ATTACK:
	case TE_MEDIC_CABLE_ATTACK:
		ent = CL_ParseBeam (cl_mod_parasite_segment);
		break;

	case TE_BOSSTPORT:			// boss teleporting to station
		MSG_ReadPos (&net_message, pos);
		CL_BigTeleportParticles (pos);
		S_StartSound (pos, 0, 0, S_RegisterSound ("misc/bigtele.wav"), 1, ATTN_NONE, 0);
		break;

	case TE_GRAPPLE_CABLE:
		ent = CL_ParseBeam2 (cl_mod_grapple_cable);
		break;

	case TE_WELDING_SPARKS:
		cnt = MSG_ReadByte (&net_message);
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		color = MSG_ReadByte (&net_message);
		CL_ParticleEffect2 (pos, dir, color, cnt);

		ex = CL_AllocExplosion ();
		VectorCopy (pos, ex->ent.origin);
		ex->type = ex_flash;
		// note to self
		// we need a better no draw flag
		ex->ent.flags = RF_BEAM;
		ex->start = cl.frame.servertime - 0.1;
		ex->light = 100 + (rand()%75);
		ex->lightcolor[0] = 1.0;
		ex->lightcolor[1] = 1.0;
		ex->lightcolor[2] = 0.3;
		ex->ent.model = cl_mod_flash;
		ex->frames = 2;
		break;

	case TE_GREENBLOOD:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		CL_ParticleEffect2 (pos, dir, 0xc0, 30);
		break;

	case TE_TRACK_ENTITY:
		CL_TrackEntityMsg();
		break;

	case	TE_PARTICLE_FALL:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		speed = MSG_ReadByte (&net_message);
		cnt = MSG_ReadByte (&net_message);
		color = MSG_ReadByte (&net_message);
		type = MSG_ReadByte (&net_message);
		
		CL_ParticleEffect_Complex2 (pos, dir, speed * 10, color, cnt, type, -PARTICLE_GRAVITY);
		break;

	case	TE_PARTICLE_RISE:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		speed = MSG_ReadByte (&net_message);
		cnt = MSG_ReadByte (&net_message);
		color = MSG_ReadByte (&net_message);
		type = MSG_ReadByte (&net_message);

		CL_ParticleEffect_Complex2 (pos, dir, speed * 10, color, cnt, type, PARTICLE_GRAVITY);
		break;

	case	TE_PARTICLE_FLOAT:
		MSG_ReadPos (&net_message, pos);
		MSG_ReadDir (&net_message, dir);
		speed = MSG_ReadByte (&net_message);
		cnt = MSG_ReadByte (&net_message);
		color = MSG_ReadByte (&net_message);
		type = MSG_ReadByte (&net_message);
		
		CL_ParticleEffect_Complex2 (pos, dir, speed * 10, color, cnt, type, 0);
		break;

	default:
		Com_Error (ERR_DROP, "CL_ParseTEnt: bad type");
	}
}

#define VectorMul(vec,val)    vec[0] *= val; vec[1] *= val; vec[2] *= val
#define VectorDiv(vec,val)    vec[0] /= val; vec[1] /= val; vec[2] /= val

//---------------------------------------------------------------------------
// CL_FindTracker()
//---------------------------------------------------------------------------
te_tracker_t *CL_FindTracker(int track_index)
{
   te_tracker_t *tracker;
   int loop;

   tracker=trackerList;
   for (loop=0; loop<TE_MAX; loop++,tracker++)
      if (tracker->track_index==track_index)
         break;

   if (loop==TE_MAX)
   {
      tracker=trackerList;
      for (loop=0; loop<TE_MAX; loop++,tracker++)
         if (!tracker->track_index)
            break;

      if (loop==TE_MAX)
         return(NULL);
   }

   tracker->track_index=track_index;
   return(tracker);
}

//---------------------------------------------------------------------------
// CL_AddTrackEnts()
//---------------------------------------------------------------------------
void CL_AddTrackEnts(void)
{
   #define RF_SHAKE_VAL       .07

   centity_t	*owner,*track,*dstent;
   entity_t		ent;
   te_tracker_t *tracker=trackerList;
   vec3_t		origin, angles, fwd, end, shake;
   int			loop,totalLightningEnts=0;
   trace_t		trace;

   // random shake ofs
   shake[0] = rand() & 1 ? -(rand() & 1) * RF_SHAKE_VAL : (rand() & 1) * RF_SHAKE_VAL;
   shake[1] = rand() & 1 ? -(rand() & 1) * RF_SHAKE_VAL : (rand() & 1) * RF_SHAKE_VAL;
   shake[2] = rand() & 1 ? -(rand() & 1) * RF_SHAKE_VAL : (rand() & 1) * RF_SHAKE_VAL;

	for (loop=0; loop<TE_MAX; loop++,tracker++)
	{
		if (!tracker->track_index)
		 continue;

		owner=&cl_entities[tracker->src_index];
		track=&cl_entities[tracker->track_index];
		dstent=&cl_entities[tracker->dst_index];

      // init entity
		memset(&ent,0,sizeof(ent));
      ent.flags=track->current.renderfx;

		if (tracker->src_index==cl.playernum+1)      // when src is owner, show based on refdef values
		{
			if (cl.frame.playerstate.pmove.pm_flags & PMF_CAMERAMODE)
			{
            // don't draw weapons
            if (tracker->renderfx & RF_WEAPONMODEL)
				   continue;

				VectorCopy(owner->current.origin,origin);
				VectorCopy(owner->current.angles,angles);
			}
			else
			{
				VectorCopy(cl.refdef.vieworg,origin);
				VectorCopy(cl.refdef.viewangles,angles);
			}
		}
		else                                         // when src isn't owner, use slower, server-updated values
		{
         int i;

         for (i=0 ; i<3 ; i++)
            origin[i] = owner->prev.origin[i] + cl.lerpfrac * (owner->current.origin[i] - owner->prev.origin[i]);

			VectorCopy(owner->current.angles,angles);
			angles[0] *= 3;
		}

		// set position  (adds forward,right,up to origin)
		AngleVectors(angles,forward,right,up);
		VectorCopy(forward,fwd);
		VectorMul(forward,tracker->forward);
		VectorMul(right,tracker->right);
		VectorMul(up,tracker->up);
		VectorAdd(origin,forward,ent.origin);
		VectorAdd(ent.origin,right,ent.origin);
		VectorAdd(ent.origin,up,ent.origin);

/*
      // spray poison
      if (tracker->flags & TEF_POISON)
      {
         CL_PoisonSpray(ent.origin,fwd,400);
         continue;
      }
*/

      // light follows ent?  (length,dstpos hold size,r,g,b)
      if (tracker->flags & TEF_ADDLIGHT)
      {
			V_AddLight(track->current.origin,tracker->length,tracker->dstpos[0],tracker->dstpos[1],tracker->dstpos[2]);
         continue;
      }

      // flashlight projected from ent?  (length,dstpos hold size_percentage,r,g,b)
      if (tracker->flags & TEF_FLASHLIGHT)
      {
         #define FLASHLIGHT_MAX_RAYS   5

         float xofs[FLASHLIGHT_MAX_RAYS] = {0,-50, 50,  0,  0};
         float yofs[FLASHLIGHT_MAX_RAYS] = {0,  0,  0,-50, 50};
         float xang[FLASHLIGHT_MAX_RAYS] = {0, -4,  4,  0,  0};
         float yang[FLASHLIGHT_MAX_RAYS] = {0,  0,  0, -4,  4};

         vec3_t startpos[FLASHLIGHT_MAX_RAYS],endpos[FLASHLIGHT_MAX_RAYS];
         int endpos_flags[FLASHLIGHT_MAX_RAYS];

         vec3_t start,ang;
//         vec3_t yaddvec, xaddvec;
         float len;

   		AngleVectors(angles,forward,right,up);

#if 0
         // build list of endpoints
         for (loop=0; loop<FLASHLIGHT_MAX_RAYS; loop++)
         {
            VectorScale(right,xofs[loop],xaddvec);
            VectorScale(up,yofs[loop],yaddvec);
            VectorAdd(ent.origin,xaddvec,start);
            VectorAdd(start,yaddvec,start);

            VectorMA(start,2000.0,fwd,end);
            trace=CL_Trace(start,NULL,NULL,end,tracker->src_index,MASK_SOLID);

            VectorCopy(start,startpos[loop]);               // save startpos
            VectorCopy(trace.endpos,endpos[loop]);          // save endpos
            endpos_flags[loop]=trace.surface->flags;        // save flags
         }
#else
         // build list of endpoints
         for (loop=0; loop<FLASHLIGHT_MAX_RAYS; loop++)
         {
            ang[0]=angles[0]+xang[loop];
            if (ang[0] >= 360)
               ang[0] -= 360;
            if (ang[0] < 0)
               ang[0] += 360;
            ang[1]=angles[1]+yang[loop];
            if (ang[1] >= 360)
               ang[1] -= 360;
            if (ang[1] < 0)
               ang[1] += 360;
            ang[2]=angles[2];
            AngleVectors(ang,forward,right,up);

            VectorMA(ent.origin,2000.0,forward,end);
            trace=CL_Trace(ent.origin,NULL,NULL,end,tracker->src_index,MASK_SOLID);

            VectorCopy(ent.origin,startpos[loop]);          // save startpos
            VectorCopy(trace.endpos,endpos[loop]);          // save endpos
            endpos_flags[loop]=trace.surface->flags;        // save flags
         }
#endif

//         VectorToAngles(angles,trace.plane.normal);

         // determine which lights to show (don't have lights too close to each other)

         // show all lights with non-zero flags
         for (loop=0; loop<FLASHLIGHT_MAX_RAYS; loop++)
         {
            float size;
            float perc;

            if (!(endpos_flags[loop] & SURF_SKY))
            {
               // keep all lights on the same linear path
               VectorSubtract(endpos[loop],startpos[loop],start);       // end-start
               len=VectorLength(start);                                 // length of trace
               VectorScale(fwd,len,forward);                            // scale forward to this length
               VectorAdd(ent.origin,forward,start);                     // start=startpos+forward

               size=(75+len*.15) * tracker->length;
               if (size < 75)
                  size=75;

               perc = (1e-6*len*len);
               if (perc)
                  perc = 1/perc/5;
               if (perc > 1)
                  perc=1;
               V_AddLight(endpos[loop],size,tracker->dstpos[0]*perc,tracker->dstpos[1]*perc,tracker->dstpos[2]*perc);
            }
         }

         continue;
      }

/*
      // SPR_ORIENTED?
      if (ent.flags & SPR_ORIENTED)
      {
         static float mikex=0;
         static float mikez=0;

         angles[2] = -angles[0];    //mikez;      //track->current.angles[2];
         angles[0] = mikex;      //track->current.angles[0];
         angles[1] += track->current.angles[1];
      }
*/

      // shake?
      if (track->current.renderfx & RF_SHAKE)
         VectorAdd(ent.origin,shake,ent.origin);

		// set angle and scale
		VectorCopy(angles,ent.angles);
		VectorCopy(track->current.render_scale,ent.render_scale);

      // special cases
		if (tracker->renderfx & (RF_BEAM | RF_LASERBEAM | RF_NOVALASER))
		{
			ent.frame=track->current.frame;
			ent.skinnum = track->current.skinnum;
			ent.model=NULL;

			//	VectorMul(fwd,tracker->length);
			//	VectorAdd(ent.origin,fwd,ent.oldorigin);

			VectorMA (ent.origin, 2000.0, fwd, end);
			trace = CL_Trace (ent.origin, NULL, NULL, end, tracker->src_index, MASK_SOLID);

	  		VectorCopy (trace.endpos, ent.oldorigin);
      	VectorToAngles (angles, trace.plane.normal);
         if (!(trace.surface->flags & SURF_SKY))
         {
            vec3_t pos;

            for (loop=0; loop<3; loop++)
            {
               float ofs;

               ofs=crand()>.5?crand():-crand();
               pos[loop]=trace.endpos[loop]+ofs*4;
            }

   			CL_ParticleEffect_Complex(pos,fwd,72,50,PARTICLE_SPARKS);
			   CL_NovaHit(trace.endpos,angles);
         }

			V_AddLight(trace.endpos,200,.8,.4,.2);
		}
		else if (tracker->renderfx & RF_SPOTLIGHT)
		{
			ent.frame=track->current.frame;
			ent.skinnum = (track->current.skinnum >> ((rand() % 4)*8)) & 0xff;
			ent.alpha=.80;
			ent.model=NULL;

			VectorMul(fwd,tracker->length);
			VectorAdd(ent.origin,fwd,ent.oldorigin);

			//	Nelno:	spawn a dymamic light here
		}
		else if (tracker->renderfx & RF_LIGHTNING)
		{
			ent.flags |= RF_LIGHTNING;                  // for now just use a beam
			ent.frame=4;      //track->current.frame;
			ent.skinnum = (track->current.skinnum >> ((rand() % 4)*8)) & 0xff;
			ent.model=NULL;

			VectorMul(fwd,tracker->length);
			VectorAdd(ent.origin,fwd,ent.oldorigin);

			CL_ParticleEffect2 (dstent->current.origin, fwd, rand()&31, 50);
		}
		else     // nope, just standard model
		{
			ent.model=cl.model_draw[track->current.modelindex];
			ent.frame=track->current.frame;

         // LERP ... THE FUCKING .... WEAPONS!!!!!!
         if (track->current.modelindex != track->prev.modelindex)
            ent.backlerp=0;
         else
            ent.backlerp=track->current.mins[0];         // hack: backlerp placed here by AddPacketEntities()
         ent.oldframe=track->prev.frame;
         ent.frame = track->current.mins [1];            // hack: frame placed here by AddPacketEntities()
         VectorCopy(ent.origin,ent.oldorigin);
		}

		if (tracker->flags & TEF_DSTINDEX)
			VectorCopy(dstent->current.origin,ent.oldorigin);

		ent.alpha=track->current.alpha;
		ent.flags |= tracker->renderfx;

//      Com_Printf ("prev = %i cur = %i\n", ent.oldframe, ent.frame);

      if (tracker->renderfx & RF_LIGHTNING)
         totalLightningEnts += doLightning(tracker,&ent);
      else
         V_AddEntity(&ent);

      if (ent.flags & RF_TRAILS)
         CL_AddTrails(ent.origin,track);
	}
}

//---------------------------------------------------------------------------
// CL_TrackEntityMsg()
//---------------------------------------------------------------------------
void CL_TrackEntityMsg(void)
{
   te_tracker_t *tracker;
   vec3_t fru;
   long track_index;

   track_index=MSG_ReadLong(&net_message);
   if (!(tracker=CL_FindTracker(track_index)))
      return;

   switch (MSG_ReadByte(&net_message))             // read mode
   {
      case 1:                                      // new / update tracking
         tracker->flags=MSG_ReadLong(&net_message);            // flags
         tracker->renderfx=MSG_ReadLong(&net_message);            // render flags

         if (tracker->flags & TEF_SRCINDEX)
            tracker->src_index=MSG_ReadLong(&net_message);        // src ent index

         if (tracker->flags & TEF_FRU)
         {
            MSG_ReadPos(&net_message,fru);                        // forward, right, up offsets
            tracker->forward=fru[0];
            tracker->right=fru[1];
            tracker->up=fru[2];
         }

         if (tracker->flags & TEF_DSTINDEX)
            tracker->dst_index=MSG_ReadLong(&net_message);     // dst ent index

         if (tracker->flags & TEF_DSTPOS)
            MSG_ReadPos(&net_message,tracker->dstpos);         // dst position

         if (tracker->flags & TEF_LENGTH)
            tracker->length=MSG_ReadFloat(&net_message);       // length
      break;

      case 2:                                      // kill tracking
         tracker->track_index=0;

         // clean up lightning
         lightningInfo[tracker->lightning].numPoints=-1;
         tracker->lightning=-1;
      break;
   }

   // for now, always init to -1 -- if needed, define an init flag
   tracker->lightning = -1;
}

///////////////////////////////////////////////////////////////////////////////
//	CL_FreeTrackEntity
//	
//	frees a track entity
//	used when an entity that is a track entity gets removed by the server
///////////////////////////////////////////////////////////////////////////////

void	CL_FreeTrackEntity (int track_entity_index)
{
	te_tracker_t	*tracker = trackerList;
	int				i;

	for (i = 0; i < TE_MAX; i++, tracker++)
	{
		if (tracker->src_index == track_entity_index)
		{
			tracker->track_index = 0;
			break;
		}
	}
}

//---------------------------------------------------------------------------
// doLightning()
//---------------------------------------------------------------------------
int doLightning(te_tracker_t *tracker,entity_t *ent)
{
   lightningInfo_t *linfo;
   vec3_t pos,temp,fwd,rt,up,fwdnorm,posofs,ofs;
   float len,pointDist;
   int i;

   // find new lightning bolt?
   if (tracker->lightning == -1)
   {
      for (i=0; i<MAX_LIGHTNING_BOLTS; i++)
         if (lightningInfo[i].numPoints == -1)
         {
            tracker->lightning=i;
            lightningInfo[i].numPoints=0;          // shouldn't need this
            lightningInfo[i].moveofs=0;
            break;
         }

      if (i==MAX_LIGHTNING_BOLTS)
         return(0);
   }

   linfo=&lightningInfo[tracker->lightning];

   // calc number of positions
   pointDist=100;
   VectorSubtract(ent->oldorigin,ent->origin,temp);
   len=(VectorLength(temp) / pointDist)+1;
   if (len > MAX_LIGHTNING_POINTS)
   {
      len=MAX_LIGHTNING_POINTS;
      pointDist=VectorLength(temp) / (MAX_LIGHTNING_POINTS-1);
   }

   // starting point and forward vec
   VectorCopy(ent->origin,pos);
   VectorToAngles(temp,temp);
   AngleVectors(temp,fwd,rt,up);
   VectorCopy(fwd,fwdnorm);
   VectorScale(fwd,pointDist,fwd);

   // setup posofs, added to positions so points move down lightning bolt
   VectorScale(fwdnorm,linfo->moveofs,posofs);
   linfo->moveofs += 30;
   if (linfo->moveofs > pointDist)
      linfo->moveofs -= pointDist;

   // set positions and velocities
   for (i=0; i<len; i++)
   {
      VectorCopy(fwdnorm,temp);
      if (!i || i==len-1)
      {
         temp[0] += .2 * crand() - .10;
         temp[1] += .2 * crand() - .10;
         temp[2] += .2 * crand() - .10;

         if (!i)
            VectorCopy(zero_vector,ofs);           // first point is unshifted
         else
         {
            float rnddst=crand() * pointDist;      // last point is random shift
            VectorScale(fwdnorm,rnddst,posofs);
         }
      }
      else
      {
         temp[0] += .5 * crand() - .25;
         temp[1] += .5 * crand() - .25;
         temp[2] += .5 * crand() - .25;

         VectorCopy(posofs,ofs);
      }

      // set position
      VectorAdd(pos,ofs,linfo->position[i]);

      // move pos to next position
      VectorAdd(pos,fwd,pos);

      // set velocity
      linfo->velocity[i][0]=temp[0];
      linfo->velocity[i][1]=temp[1];
      linfo->velocity[i][2]=temp[2];
   }
   linfo->numPoints=len;

   // spline me
   return(MakeCurve(linfo->numPoints,linfo->position,linfo->velocity,ent,30,64));
}

/*
=================
CL_AddBeams
=================
*/
void CL_AddBeams (void)
{
	int			i,j;
	beam_t		*b;
	vec3_t		dist, org;
	float		d;
	entity_t	ent;
	float		yaw, pitch;
	float		forward;
// update beams
	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
	{
		if (!b->model || b->endtime < cl.time)
			continue;
	// if coming from the player, update the start position
		if (b->entity == cl.playernum+1)	// entity 0 is the world
		{
			VectorCopy (cl.refdef.vieworg, b->start);
			b->start[2] -= 22;	// adjust for view height
		}
		VectorAdd (b->start, b->offset, org);
	// calculate pitch and yaw
		VectorSubtract (b->end, org, dist);
		if (dist[1] == 0 && dist[0] == 0)
		{
			yaw = 0;
			if (dist[2] > 0)
				pitch = 90;
			else
				pitch = 270;
		}
		else
		{
			yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI);
			if (yaw < 0)
				yaw += 360;
	
			forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
			pitch = (int) (atan2(dist[2], forward) * -180 / M_PI);
			if (pitch < 0)
				pitch += 360;
		}
	// add new entities for the beams
		d = VectorNormalize(dist);
		memset (&ent, 0, sizeof(ent));
		while (d > 0)
		{
			VectorCopy (org, ent.origin);
			ent.model = b->model;
			ent.angles[0] = pitch;
			ent.angles[1] = yaw;
			ent.angles[2] = rand()%360;
			V_AddEntity (&ent);
			for (j=0 ; j<3 ; j++)
				org[j] += dist[j]*30;
			d -= 30;
		}
	}
	
}
/*
=================
CL_AddExplosions
=================
*/
void CL_AddExplosions (void)
{
	entity_t	*ent;
	int			i,loop;
	explosion_t	*ex;
	float		frac,perc,scale;
	int			f;
	memset (&ent, 0, sizeof(ent));
	for (i=0, ex=cl_explosions ; i< MAX_EXPLOSIONS ; i++, ex++)
	{

		if (ex->type == ex_free)
			continue;
		frac = (cl.time - ex->start) / CL_FRAME_MILLISECONDS;
		f = floor(frac);
		ent = &ex->ent;
		switch (ex->type)
		{
		case ex_mflash:
			if (f >= ex->frames-1)
				ex->type = ex_free;
			break;
		case ex_misc:
			if (f >= ex->frames-1)
			{
				ex->type = ex_free;
				break;
			}
			ent->alpha = 1.0 - frac/(ex->frames-1);
			break;
		case ex_flash:
			if (f >= 1)
			{
				ex->type = ex_free;
				break;
			}
			ent->alpha = 1.0;
			break;
		case ex_poly:
			if (f >= ex->frames-1)
			{
				ex->type = ex_free;
				break;
			}
			ent->alpha = (16.0 - (float)f)/16.0;
			if (f < 10)
			{
				ent->skinnum = (f>>1);
				if (ent->skinnum < 0)
					ent->skinnum = 0;
			}
			else
			{
				ent->flags |= RF_TRANSLUCENT;
				if (f < 13)
					ent->skinnum = 5;
				else
					ent->skinnum = 6;
			}
			break;

		case	ex_sprite:
			ent->alpha = ((ex->frames) - (float)f)/ex->frames;
//         ent->alpha=.1;
			if (f >= ex->frames-1)
			{
				ex->type = ex_free;
				break;
			}
			break;

		case	ex_explosion:
         ent->alpha=1;
			if (f >= ex->frames)
			{
				ex->type = ex_free;
				break;
			}
			break;

		case	ex_explosion_time:
         if (f<0)                   // my little timeshift (for starting at any frame) produces
            f=ex->frames+f;         // negative frames until the first time (f >= ex->frames)

			if (f >= ex->frames)
         {
            f=0;
         	ex->start=cl.frame.servertime-CL_FRAME_MILLISECONDS;
         }

         if (cl.time > ex->killtime-1000)
            ent->alpha=ex->end_alpha*((ex->killtime-cl.time)/1000);
         else
         {
            vec3_t pos;

            pos[0]=ex->ent.origin[0];
            pos[1]=ex->ent.origin[1];
            pos[2]=ex->ent.origin[2]+40;
            CL_Smoke2(pos,2,2,25,20);
         }

         if (cl.time > ex->killtime)
         {
			   ex->type = ex_free;
            break;
         }
			break;

		case	ex_explosion_alpha:
			ent->alpha = ((ex->frames) - (float)f)/ex->frames;
			if (f >= ex->frames-1)
			{
				ex->type = ex_free;
				break;
			}
			break;

      case ex_scale_rotate:         // one frame, scales from 1 to max, lightcolor is used as avelocity
         perc = (cl.time-ex->start) / ex->scale_factor;                          // perc=(time-start_time)/scale_time
         scale = ex->alpha_scale + (ex->end_scale-ex->alpha_scale) * perc;       // scale=start_scale+(end_scale-start_scale)*perc
         ent->render_scale[0]=ent->render_scale[1]=ent->render_scale[2]=scale;

			if (cl.time >= ex->start+ex->scale_factor)
			{
				ex->type = ex_free;
				break;
			}

         scale = (cl.time-ex->end_alpha) / CL_FRAME_MILLISECONDS;    // scale = ms since last frame / ms per frame
         ent->angles[0] += ex->lightcolor[0] * scale;
         ent->angles[1] += ex->lightcolor[1] * scale;
         ent->angles[2] += ex->lightcolor[2] * scale;

         ex->end_alpha=cl.time;

         f=0;
      break;

		case	ex_scale_alpha:            // cycles through frames, scales alpha based on settings
			ent->alpha *= ex->alpha_scale;

			if (f > ex->frames-1)
         {
         	ex->start=cl.frame.servertime-CL_FRAME_MILLISECONDS;
            f=0;
         }

			if (ent->alpha < ex->end_alpha)
			{
				ex->type = ex_free;
				break;
			}
			break;

      case ex_tracer:
         // calc laser front
         perc = 1 - ((ex->killtime-cl.time) / (ex->killtime-ex->start));
         if (perc > 1)
            perc=1;
         for (loop=0; loop<3; loop++)
            ent->origin[loop] = ex->pos[loop] + (ex->end[loop]-ex->pos[loop]) * perc;

         // calc laser end
         perc = 1 - ((ex->killtime-cl.time) / (ex->killtime-ex->start)) - ex->scale_factor;
         if (perc < 0)
            perc=0;
         for (loop=0; loop<3; loop++)
            ent->oldorigin[loop] = ex->pos[loop] + (ex->end[loop]-ex->pos[loop]) * perc;

         if (perc > 1)
            ex->type=ex_free;
      break;

		case	ex_scale_sprite:           // only one frame, scales from large to 0.1
			ent->render_scale [0] *= ex->scale_factor;
			ent->alpha *= ex->alpha_scale;

			if (ent->render_scale [0] < ex->end_scale || ent->alpha < ex->end_alpha)
			{
				ex->type = ex_free;
				break;
			}

			ent->render_scale [1] *= ex->scale_factor;
			ent->render_scale [2] *= ex->scale_factor;
			break;

		}
		if (ex->type == ex_free)
			continue;
		if (ex->light)
		{
			V_AddLight (ent->origin, ex->light*ent->alpha,
				ex->lightcolor[0], ex->lightcolor[1], ex->lightcolor[2]);
		}

      if (ex->type != ex_tracer)
      {
   		VectorCopy (ent->origin, ent->oldorigin);
		
   		if (ex->type != ex_scale_sprite)
	   	{
		   	if (f < 0)
			   	f = 0;
   			ent->frame = ex->baseframe + f;     // + 1; mike:   ???? why add 1??? this is zero based
	   		ent->oldframe = ex->baseframe + f;
		   	ent->backlerp = 1.0 - cl.lerpfrac;
   		}
	   	else
   		{
		   	ent->frame = 0;
	   		ent->oldframe = 0;
		   	ent->backlerp = 1.0;
   		}
      }

		V_AddEntity (ent);
	}
}

/*
=================
CL_AddLasers
=================
*/

__declspec(dllimport) int __stdcall GetTickCount(void);

void CL_AddLasers (void)
{

    centity_t *dstent;
	laser_t		*l;
	int			i,j;

	for (i=0, l=cl_lasers ; i< MAX_LASERS ; i++, l++)
	{
		if (l->endtime >= cl.time)
      {
         if (l->track_dst > 0)
         {
   		   dstent=&cl_entities[l->track_dst];

            // use refdef position if ent is client, and not in 3rd person view
			   if (!(cl.frame.playerstate.pmove.pm_flags & PMF_CAMERAMODE) && (l->track_dst==cl.playernum+1))
            {
	   			VectorCopy(cl.refdef.vieworg,l->ent.oldorigin);
               l->ent.oldorigin[2] -= 16;
            }
            else
            {
               for (j=0 ; j<3 ; j++)
                  l->ent.oldorigin[j] = dstent->prev.origin[j] + cl.lerpfrac * (dstent->current.origin[j] - dstent->prev.origin[j]);
//               VectorCopy(dstent->lerp_origin,l->ent.oldorigin);
//               VectorCopy(dstent->current.origin,l->ent.oldorigin);
            }
         }
			V_AddEntity (&l->ent);
      }
	}
}


/*
=================
CL_AddTEnts
=================
*/
void CL_AddTEnts (void)
{
	CL_AddBeams ();
	CL_AddExplosions ();
	CL_AddLasers ();
}
