
//**************************************************************************
//**
//** cl_effect.c
//**
//** Client side effects.
//**
//** $Header: /HexenWorld/Client/cl_effect.c 89    5/25/98 1:29p Mgummelt $
//**
//**************************************************************************

// HEADER FILES ------------------------------------------------------------

#include "quakedef.h"

// MACROS ------------------------------------------------------------------

// TYPES -------------------------------------------------------------------

#define MAX_EFFECT_ENTITIES		256

// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
extern void CreateStream(int type, int ent, int flags, int tag, float duration, int skin, vec3_t source, vec3_t dest);
extern void CLTENT_XbowImpact(vec3_t pos, vec3_t vel, int chType, int damage, int arrowType);//so xbow effect can use tents
extern void CLTENT_SpawnDeathBubble(vec3_t pos);
entity_state_t *FindState(int EntNum);
int	TempSoundChannel();
void setseed(unsigned int seed);
float seedrand(void);

// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------

// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------

static int NewEffectEntity(void);
static void FreeEffectEntity(int index);

// EXTERNAL DATA DECLARATIONS ----------------------------------------------

// PUBLIC DATA DEFINITIONS -------------------------------------------------

// PRIVATE DATA DEFINITIONS ------------------------------------------------

static entity_t EffectEntities[MAX_EFFECT_ENTITIES];
static qboolean EntityUsed[MAX_EFFECT_ENTITIES];
static int EffectEntityCount;

// CODE --------------------------------------------------------------------

static void vectoangles(vec3_t vec, vec3_t ang)
{
	float	forward;
	float	yaw, pitch;
	
	if (vec[1] == 0 && vec[0] == 0)
	{
		yaw = 0;
		if (vec[2] > 0)
			pitch = 90;
		else
			pitch = 270;
	}
	else
	{
		yaw = (int) (atan2(vec[1], vec[0]) * 180 / M_PI);
		if (yaw < 0)
			yaw += 360;

		forward = sqrt (vec[0]*vec[0] + vec[1]*vec[1]);
		pitch = (int) (atan2(vec[2], forward) * 180 / M_PI);
		if (pitch < 0)
			pitch += 360;
	}

	ang[0] = pitch;
	ang[1] = yaw;
	ang[2] = 0;
}


//==========================================================================
//
// CL_InitTEnts
//
//==========================================================================

sfx_t	*cl_fxsfx_bone;
sfx_t	*cl_fxsfx_bonefpow;
sfx_t	*cl_fxsfx_xbowshoot;
sfx_t	*cl_fxsfx_xbowfshoot;
sfx_t	*cl_fxsfx_explode;
sfx_t	*cl_fxsfx_mmfire;
sfx_t	*cl_fxsfx_eidolon;
sfx_t	*cl_fxsfx_scarabwhoosh;
sfx_t	*cl_fxsfx_scarabgrab;
sfx_t	*cl_fxsfx_scarabhome;
sfx_t	*cl_fxsfx_scarabrip;
sfx_t	*cl_fxsfx_scarabbyebye;
sfx_t	*cl_fxsfx_ravensplit;
sfx_t	*cl_fxsfx_ravenfire;
sfx_t	*cl_fxsfx_ravengo;
sfx_t	*cl_fxsfx_drillashoot;
sfx_t	*cl_fxsfx_drillaspin;
sfx_t	*cl_fxsfx_drillameat;


sfx_t			*cl_fxsfx_arr2flsh;
sfx_t			*cl_fxsfx_arr2wood;
sfx_t			*cl_fxsfx_met2stn;

sfx_t			*cl_fxsfx_ripple;
sfx_t			*cl_fxsfx_splash;
void CL_InitEffects(void)
{
	cl_fxsfx_bone = S_PrecacheSound ("necro/bonefnrm.wav");
	cl_fxsfx_bonefpow = S_PrecacheSound ("necro/bonefpow.wav");
	cl_fxsfx_xbowshoot = S_PrecacheSound ("assassin/firebolt.wav");
	cl_fxsfx_xbowfshoot = S_PrecacheSound ("assassin/firefblt.wav");
	cl_fxsfx_explode = S_PrecacheSound ("weapons/explode.wav");
	cl_fxsfx_mmfire = S_PrecacheSound ("necro/mmfire.wav");
	cl_fxsfx_eidolon = S_PrecacheSound ("eidolon/spell.wav");
	cl_fxsfx_scarabwhoosh = S_PrecacheSound ("misc/whoosh.wav");
	cl_fxsfx_scarabgrab = S_PrecacheSound ("assassin/chn2flsh.wav");
	cl_fxsfx_scarabhome = S_PrecacheSound ("assassin/chain.wav");
	cl_fxsfx_scarabrip = S_PrecacheSound ("assassin/chntear.wav");
	cl_fxsfx_scarabbyebye = S_PrecacheSound ("items/itmspawn.wav");
	cl_fxsfx_ravensplit = S_PrecacheSound ("raven/split.wav");
	cl_fxsfx_ravenfire = S_PrecacheSound ("raven/rfire1.wav");
	cl_fxsfx_ravengo = S_PrecacheSound ("raven/ravengo.wav");
	cl_fxsfx_drillashoot = S_PrecacheSound ("assassin/pincer.wav");
	cl_fxsfx_drillaspin = S_PrecacheSound ("assassin/spin.wav");
	cl_fxsfx_drillameat = S_PrecacheSound ("assassin/core.wav");

	cl_fxsfx_arr2flsh = S_PrecacheSound ("assassin/arr2flsh.wav");
	cl_fxsfx_arr2wood = S_PrecacheSound ("assassin/arr2wood.wav");
	cl_fxsfx_met2stn = S_PrecacheSound ("weapons/met2stn.wav");

	cl_fxsfx_ripple = S_PrecacheSound ("misc/drip.wav");
	cl_fxsfx_splash = S_PrecacheSound ("raven/outwater.wav");
}

void CL_ClearEffects(void)
{
	memset(cl.Effects,0,sizeof(cl.Effects));
	memset(EntityUsed,0,sizeof(EntityUsed));
	EffectEntityCount = 0;
}

void CL_FreeEffect(int index)
{	
	int i;

	switch(cl.Effects[index].type)
	{
		case CE_RAIN:
			break;

		case CE_FOUNTAIN:
			break;

		case CE_QUAKE:
			break;

		case CE_TELESMK1:
			FreeEffectEntity(cl.Effects[index].Smoke.entity_index2);
			//no break wanted here
		case CE_WHITE_SMOKE:
		case CE_GREEN_SMOKE:
		case CE_GREY_SMOKE:
		case CE_RED_SMOKE:
		case CE_SLOW_WHITE_SMOKE:
		case CE_TELESMK2:
		case CE_GHOST:
		case CE_REDCLOUD:
		case CE_ACID_MUZZFL:
		case CE_FLAMESTREAM:
		case CE_FLAMEWALL:
		case CE_FLAMEWALL2:
		case CE_ONFIRE:
		case CE_RIPPLE:
			FreeEffectEntity(cl.Effects[index].Smoke.entity_index);
			break;

		case CE_DEATHBUBBLES:
			break;
		// Just go through animation and then remove
		case CE_SM_WHITE_FLASH:
		case CE_YELLOWRED_FLASH:
		case CE_BLUESPARK:
		case CE_YELLOWSPARK:
		case CE_SM_CIRCLE_EXP:
		case CE_BG_CIRCLE_EXP:
		case CE_SM_EXPLOSION:
		case CE_SM_EXPLOSION2:
		case CE_BG_EXPLOSION:
		case CE_FLOOR_EXPLOSION:
		case CE_BLUE_EXPLOSION:
		case CE_REDSPARK:
		case CE_GREENSPARK:
		case CE_ICEHIT:
		case CE_MEDUSA_HIT:
		case CE_MEZZO_REFLECT:
		case CE_FLOOR_EXPLOSION2:
		case CE_XBOW_EXPLOSION:
		case CE_NEW_EXPLOSION:
		case CE_MAGIC_MISSILE_EXPLOSION:
		case CE_BONE_EXPLOSION:
		case CE_BLDRN_EXPL:
		case CE_BRN_BOUNCE:
		case CE_LSHOCK:
		case CE_ACID_HIT:
		case CE_ACID_SPLAT:
		case CE_ACID_EXPL:
		case CE_LBALL_EXPL:
		case CE_FBOOM:
		case CE_BOMB:
		case CE_FIREWALL_SMALL:
		case CE_FIREWALL_MEDIUM:
		case CE_FIREWALL_LARGE:
			FreeEffectEntity(cl.Effects[index].Smoke.entity_index);
			break;

		// Go forward then backward through animation then remove
		case CE_WHITE_FLASH:
		case CE_BLUE_FLASH:
		case CE_SM_BLUE_FLASH:
		case CE_HWSPLITFLASH:
		case CE_RED_FLASH:
			FreeEffectEntity(cl.Effects[index].Flash.entity_index);
			break;

		case CE_RIDER_DEATH:
			break;

		case CE_TELEPORTERPUFFS:
			for (i=0;i<8;++i)
				FreeEffectEntity(cl.Effects[index].Teleporter.entity_index[i]);
			break;

		case CE_HWSHEEPINATOR:
			for (i=0;i<5;++i)
				FreeEffectEntity(cl.Effects[index].Xbow.ent[i]);
			break;

		case CE_HWXBOWSHOOT:
			for (i=0;i<cl.Effects[index].Xbow.bolts;++i)
				FreeEffectEntity(cl.Effects[index].Xbow.ent[i]);
			break;

		case CE_TELEPORTERBODY:
			FreeEffectEntity(cl.Effects[index].Teleporter.entity_index[0]);
			break;

		case CE_HWDRILLA:
		case CE_BONESHARD:
		case CE_BONESHRAPNEL:
		case CE_HWBONEBALL:
		case CE_HWRAVENSTAFF:
		case CE_HWRAVENPOWER:
			FreeEffectEntity(cl.Effects[index].Missile.entity_index);
			break;
		case CE_TRIPMINESTILL:
//			Con_DPrintf("Ditching chain\n");
			FreeEffectEntity(cl.Effects[index].Chain.ent1);
			break;
		case CE_SCARABCHAIN:
		case CE_TRIPMINE:
			FreeEffectEntity(cl.Effects[index].Chain.ent1);
			break;
		case CE_HWMISSILESTAR:
			FreeEffectEntity(cl.Effects[index].Star.ent2);
			//no break wanted here
		case CE_HWEIDOLONSTAR:
			FreeEffectEntity(cl.Effects[index].Star.ent1);
			FreeEffectEntity(cl.Effects[index].Star.entity_index);

			break;
		default:
//			Con_Printf("Freeing unknown effect type\n");
			break;
	}

	memset(&cl.Effects[index],0,sizeof(struct EffectT));
}

//==========================================================================
//
// CL_ParseEffect
//
//==========================================================================

// All changes need to be in SV_SendEffect(), SV_ParseEffect(),
// SV_SaveEffects(), SV_LoadEffects(), CL_ParseEffect()
void CL_ParseEffect(void)
{
	int index,i;
	qboolean ImmediateFree;
	entity_t *ent;
	int dir;
	float	angleval, sinval, cosval;
	float skinnum;
	int play_sound;
	vec3_t forward, right, up, vtemp;
	vec3_t forward2, right2, up2;
	vec3_t origin;

	ImmediateFree = false;

	index = MSG_ReadByte();
	if (cl.Effects[index].type)
		CL_FreeEffect(index);

	memset(&cl.Effects[index],0,sizeof(struct EffectT));

	cl.Effects[index].type = MSG_ReadByte();

	switch(cl.Effects[index].type)
	{
		case CE_RAIN:
			cl.Effects[index].Rain.min_org[0] = MSG_ReadCoord();
			cl.Effects[index].Rain.min_org[1] = MSG_ReadCoord();
			cl.Effects[index].Rain.min_org[2] = MSG_ReadCoord();
			cl.Effects[index].Rain.max_org[0] = MSG_ReadCoord();
			cl.Effects[index].Rain.max_org[1] = MSG_ReadCoord();
			cl.Effects[index].Rain.max_org[2] = MSG_ReadCoord();
			cl.Effects[index].Rain.e_size[0] = MSG_ReadCoord();
			cl.Effects[index].Rain.e_size[1] = MSG_ReadCoord();
			cl.Effects[index].Rain.e_size[2] = MSG_ReadCoord();
			cl.Effects[index].Rain.dir[0] = MSG_ReadCoord();
			cl.Effects[index].Rain.dir[1] = MSG_ReadCoord();
			cl.Effects[index].Rain.dir[2] = MSG_ReadCoord();
			cl.Effects[index].Rain.color = MSG_ReadShort();
			cl.Effects[index].Rain.count = MSG_ReadShort();
			cl.Effects[index].Rain.wait = MSG_ReadFloat();
			break;

		case CE_FOUNTAIN:
			cl.Effects[index].Fountain.pos[0] = MSG_ReadCoord ();
			cl.Effects[index].Fountain.pos[1] = MSG_ReadCoord ();
			cl.Effects[index].Fountain.pos[2] = MSG_ReadCoord ();
			cl.Effects[index].Fountain.angle[0] = MSG_ReadAngle ();
			cl.Effects[index].Fountain.angle[1] = MSG_ReadAngle ();
			cl.Effects[index].Fountain.angle[2] = MSG_ReadAngle ();
			cl.Effects[index].Fountain.movedir[0] = MSG_ReadCoord ();
			cl.Effects[index].Fountain.movedir[1] = MSG_ReadCoord ();
			cl.Effects[index].Fountain.movedir[2] = MSG_ReadCoord ();
			cl.Effects[index].Fountain.color = MSG_ReadShort ();
			cl.Effects[index].Fountain.cnt = MSG_ReadByte ();
			AngleVectors (cl.Effects[index].Fountain.angle, 
						  cl.Effects[index].Fountain.vforward,
						  cl.Effects[index].Fountain.vright,
						  cl.Effects[index].Fountain.vup);
			break;

		case CE_QUAKE:
			cl.Effects[index].Quake.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Quake.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Quake.origin[2] = MSG_ReadCoord ();
			cl.Effects[index].Quake.radius = MSG_ReadFloat ();
			break;

		case CE_WHITE_SMOKE:
		case CE_GREEN_SMOKE:
		case CE_GREY_SMOKE:
		case CE_RED_SMOKE:
		case CE_SLOW_WHITE_SMOKE:
		case CE_TELESMK1:
		case CE_TELESMK2:
		case CE_GHOST:
		case CE_REDCLOUD:
		case CE_ACID_MUZZFL:
		case CE_FLAMESTREAM:
		case CE_FLAMEWALL:
		case CE_FLAMEWALL2:
		case CE_ONFIRE:
		case CE_RIPPLE:
			cl.Effects[index].Smoke.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Smoke.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Smoke.origin[2] = MSG_ReadCoord ();

			cl.Effects[index].Smoke.velocity[0] = MSG_ReadFloat ();
			cl.Effects[index].Smoke.velocity[1] = MSG_ReadFloat ();
			cl.Effects[index].Smoke.velocity[2] = MSG_ReadFloat ();

			cl.Effects[index].Smoke.framelength = MSG_ReadFloat ();

			if ((cl.Effects[index].Smoke.entity_index = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Smoke.entity_index];
				VectorCopy(cl.Effects[index].Smoke.origin, ent->origin);

				if ((cl.Effects[index].type == CE_WHITE_SMOKE) || 
					(cl.Effects[index].type == CE_SLOW_WHITE_SMOKE))
					ent->model = Mod_ForName("models/whtsmk1.spr", true);
				else if (cl.Effects[index].type == CE_GREEN_SMOKE)
					ent->model = Mod_ForName("models/grnsmk1.spr", true);
				else if (cl.Effects[index].type == CE_GREY_SMOKE)
					ent->model = Mod_ForName("models/grysmk1.spr", true);
				else if (cl.Effects[index].type == CE_RED_SMOKE)
					ent->model = Mod_ForName("models/redsmk1.spr", true);
				else if (cl.Effects[index].type == CE_TELESMK1)
					ent->model = Mod_ForName("models/telesmk1.spr", true);
				else if (cl.Effects[index].type == CE_TELESMK2)
					ent->model = Mod_ForName("models/telesmk2.spr", true);
				else if (cl.Effects[index].type == CE_REDCLOUD)
					ent->model = Mod_ForName("models/rcloud.spr", true);
				else if (cl.Effects[index].type == CE_FLAMESTREAM)
					ent->model = Mod_ForName("models/flamestr.spr", true);
				else if (cl.Effects[index].type == CE_ACID_MUZZFL)
				{
					ent->model = Mod_ForName("models/muzzle1.spr", true);
					ent->drawflags=DRF_TRANSLUCENT|MLS_ABSLIGHT;
					ent->abslight=0.2;
				}
				else if (cl.Effects[index].type == CE_FLAMEWALL)
					ent->model = Mod_ForName("models/firewal1.spr", true);
				else if (cl.Effects[index].type == CE_FLAMEWALL2)
					ent->model = Mod_ForName("models/firewal2.spr", true);
				else if (cl.Effects[index].type == CE_ONFIRE)
				{
					float rdm = rand() & 3;

					if (rdm < 1)
						ent->model = Mod_ForName("models/firewal1.spr", true);
					else if (rdm < 2)
						ent->model = Mod_ForName("models/firewal2.spr", true);
					else
						ent->model = Mod_ForName("models/firewal3.spr", true);
					
					ent->drawflags = DRF_TRANSLUCENT;
					ent->abslight = 1;
					ent->frame = cl.Effects[index].Smoke.frame;
				}
				else if (cl.Effects[index].type == CE_RIPPLE)
				{
					if(cl.Effects[index].Smoke.framelength==2)
					{
						R_SplashParticleEffect (cl.Effects[index].Smoke.origin, 200, 406+rand()%8, pt_slowgrav, 40);//splash
						S_StartSound (TempSoundChannel(), 1, cl_fxsfx_splash, cl.Effects[index].Smoke.origin, 1, 1);
					}
					else if(cl.Effects[index].Smoke.framelength==1)
						R_SplashParticleEffect (cl.Effects[index].Smoke.origin, 100, 406+rand()%8, pt_slowgrav, 20);//splash
					else
						S_StartSound (TempSoundChannel(), 1, cl_fxsfx_ripple, cl.Effects[index].Smoke.origin, 1, 1);
					cl.Effects[index].Smoke.framelength=0.05;
					ent->model = Mod_ForName("models/ripple.spr", true);
					ent->drawflags = DRF_TRANSLUCENT;//|SCALE_TYPE_XYONLY|SCALE_ORIGIN_CENTER;
					ent->angles[0]=90;
					//ent->scale = 1;
				}
				else
				{
					ImmediateFree = true;
					Con_Printf ("Bad effect type %d\n",(int)cl.Effects[index].type);
				}

				if (cl.Effects[index].type != CE_REDCLOUD&&cl.Effects[index].type != CE_ACID_MUZZFL&&cl.Effects[index].type != CE_FLAMEWALL&&cl.Effects[index].type != CE_RIPPLE)
					ent->drawflags = DRF_TRANSLUCENT;

				if (cl.Effects[index].type == CE_FLAMESTREAM)
				{
					ent->drawflags = DRF_TRANSLUCENT | MLS_ABSLIGHT;
					ent->abslight = 1;
					ent->frame = cl.Effects[index].Smoke.frame;
				}

				if (cl.Effects[index].type == CE_GHOST)
				{		
					ent->model = Mod_ForName("models/ghost.spr", true);
					ent->drawflags = DRF_TRANSLUCENT | MLS_ABSLIGHT;
					ent->abslight = .5;
				}
				if (cl.Effects[index].type == CE_TELESMK1)
				{
					S_StartSound (TempSoundChannel(), 1, cl_fxsfx_ravenfire, cl.Effects[index].Smoke.origin, 1, 1);

					if ((cl.Effects[index].Smoke.entity_index2 = NewEffectEntity()) != -1)
					{
						ent = &EffectEntities[cl.Effects[index].Smoke.entity_index2];
						VectorCopy(cl.Effects[index].Smoke.origin, ent->origin);
						ent->model = Mod_ForName("models/telesmk1.spr", true);
						ent->drawflags = DRF_TRANSLUCENT;
					}
				}
			}
			else
				ImmediateFree = true;
			break;

		case CE_SM_WHITE_FLASH:
		case CE_YELLOWRED_FLASH:
		case CE_BLUESPARK:
		case CE_YELLOWSPARK:
		case CE_SM_CIRCLE_EXP:
		case CE_BG_CIRCLE_EXP:
		case CE_SM_EXPLOSION:
		case CE_SM_EXPLOSION2:
		case CE_BG_EXPLOSION:
		case CE_FLOOR_EXPLOSION:
		case CE_BLUE_EXPLOSION:
		case CE_REDSPARK:
		case CE_GREENSPARK:
		case CE_ICEHIT:
		case CE_MEDUSA_HIT:
		case CE_MEZZO_REFLECT:
		case CE_FLOOR_EXPLOSION2:
		case CE_XBOW_EXPLOSION:
		case CE_NEW_EXPLOSION:
		case CE_MAGIC_MISSILE_EXPLOSION:
		case CE_BONE_EXPLOSION:
		case CE_BLDRN_EXPL:
		case CE_BRN_BOUNCE:
		case CE_LSHOCK:
		case CE_ACID_HIT:
		case CE_ACID_SPLAT:
		case CE_ACID_EXPL:
		case CE_LBALL_EXPL:
		case CE_FBOOM:
		case CE_BOMB:
		case CE_FIREWALL_SMALL:
		case CE_FIREWALL_MEDIUM:
		case CE_FIREWALL_LARGE:

			cl.Effects[index].Smoke.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Smoke.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Smoke.origin[2] = MSG_ReadCoord ();
			if ((cl.Effects[index].Smoke.entity_index = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Smoke.entity_index];
				VectorCopy(cl.Effects[index].Smoke.origin, ent->origin);

				if (cl.Effects[index].type == CE_BLUESPARK)
					ent->model = Mod_ForName("models/bspark.spr", true);
				else if (cl.Effects[index].type == CE_YELLOWSPARK)
					ent->model = Mod_ForName("models/spark.spr", true);
				else if (cl.Effects[index].type == CE_SM_CIRCLE_EXP)
					ent->model = Mod_ForName("models/fcircle.spr", true);
				else if (cl.Effects[index].type == CE_BG_CIRCLE_EXP)
					ent->model = Mod_ForName("models/xplod29.spr", true);
				else if (cl.Effects[index].type == CE_SM_WHITE_FLASH)
					ent->model = Mod_ForName("models/sm_white.spr", true);
				else if (cl.Effects[index].type == CE_YELLOWRED_FLASH)
				{
					ent->model = Mod_ForName("models/yr_flsh.spr", true);
					ent->drawflags = DRF_TRANSLUCENT;
				}
				else if (cl.Effects[index].type == CE_SM_EXPLOSION)
					ent->model = Mod_ForName("models/sm_expld.spr", true);
				else if (cl.Effects[index].type == CE_SM_EXPLOSION2)
				{
					ent->model = Mod_ForName("models/sm_expld.spr", true);
					S_StartSound (TempSoundChannel(), 1, cl_fxsfx_explode, cl.Effects[index].Smoke.origin, 1, 1);

				}
				else if (cl.Effects[index].type == CE_BG_EXPLOSION)
				{
					ent->model = Mod_ForName("models/bg_expld.spr", true);
					S_StartSound (TempSoundChannel(), 1, cl_fxsfx_explode, cl.Effects[index].Smoke.origin, 1, 1);
				}
				else if (cl.Effects[index].type == CE_FLOOR_EXPLOSION)
					ent->model = Mod_ForName("models/fl_expld.spr", true);
				else if (cl.Effects[index].type == CE_BLUE_EXPLOSION)
					ent->model = Mod_ForName("models/xpspblue.spr", true);
				else if (cl.Effects[index].type == CE_REDSPARK)
					ent->model = Mod_ForName("models/rspark.spr", true);
				else if (cl.Effects[index].type == CE_GREENSPARK)
					ent->model = Mod_ForName("models/gspark.spr", true);
				else if (cl.Effects[index].type == CE_ICEHIT)
					ent->model = Mod_ForName("models/icehit.spr", true);
				else if (cl.Effects[index].type == CE_MEDUSA_HIT)
					ent->model = Mod_ForName("models/medhit.spr", true);
				else if (cl.Effects[index].type == CE_MEZZO_REFLECT)
					ent->model = Mod_ForName("models/mezzoref.spr", true);
				else if (cl.Effects[index].type == CE_FLOOR_EXPLOSION2)
					ent->model = Mod_ForName("models/flrexpl2.spr", true);
				else if (cl.Effects[index].type == CE_XBOW_EXPLOSION)
					ent->model = Mod_ForName("models/xbowexpl.spr", true);
				else if (cl.Effects[index].type == CE_NEW_EXPLOSION)
					ent->model = Mod_ForName("models/gen_expl.spr", true);
				else if (cl.Effects[index].type == CE_MAGIC_MISSILE_EXPLOSION)
				{
					ent->model = Mod_ForName("models/mm_expld.spr", true);
					S_StartSound (TempSoundChannel(), 1, cl_fxsfx_explode, cl.Effects[index].Smoke.origin, 1, 1);
				}
				else if (cl.Effects[index].type == CE_BONE_EXPLOSION)
					ent->model = Mod_ForName("models/bonexpld.spr", true);
				else if (cl.Effects[index].type == CE_BLDRN_EXPL)
					ent->model = Mod_ForName("models/xplsn_1.spr", true);
				else if (cl.Effects[index].type == CE_ACID_HIT)
					ent->model = Mod_ForName("models/axplsn_2.spr", true);
				else if (cl.Effects[index].type == CE_ACID_SPLAT)
					ent->model = Mod_ForName("models/axplsn_1.spr", true);
				else if (cl.Effects[index].type == CE_ACID_EXPL)
				{
					ent->model = Mod_ForName("models/axplsn_5.spr", true);
					ent->drawflags = MLS_ABSLIGHT;
					ent->abslight = 1;
				}
				else if (cl.Effects[index].type == CE_FBOOM)
					ent->model = Mod_ForName("models/fboom.spr", true);
				else if (cl.Effects[index].type == CE_BOMB)
					ent->model = Mod_ForName("models/pow.spr", true);
				else if (cl.Effects[index].type == CE_LBALL_EXPL)
					ent->model = Mod_ForName("models/Bluexp3.spr", true);
				else if (cl.Effects[index].type == CE_FIREWALL_SMALL)
					ent->model = Mod_ForName("models/firewal1.spr", true);
				else if (cl.Effects[index].type == CE_FIREWALL_MEDIUM)
					ent->model = Mod_ForName("models/firewal5.spr", true);
				else if (cl.Effects[index].type == CE_FIREWALL_LARGE)
					ent->model = Mod_ForName("models/firewal4.spr", true);
				else if (cl.Effects[index].type == CE_BRN_BOUNCE)
					ent->model = Mod_ForName("models/spark.spr", true);
				else if (cl.Effects[index].type == CE_LSHOCK)
				{
					ent->model = Mod_ForName("models/vorpshok.mdl", true);
					ent->drawflags=MLS_TORCH;
					ent->angles[2]=90;
					ent->scale=255;
				}
			}
			else
			{
				ImmediateFree = true;
			}
			break;

		case CE_WHITE_FLASH:
		case CE_BLUE_FLASH:
		case CE_SM_BLUE_FLASH:
		case CE_HWSPLITFLASH:
		case CE_RED_FLASH:
			cl.Effects[index].Flash.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Flash.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Flash.origin[2] = MSG_ReadCoord ();
			cl.Effects[index].Flash.reverse = 0;
			if ((cl.Effects[index].Flash.entity_index = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Flash.entity_index];
				VectorCopy(cl.Effects[index].Flash.origin, ent->origin);

				if (cl.Effects[index].type == CE_WHITE_FLASH)
					ent->model = Mod_ForName("models/gryspt.spr", true);
				else if (cl.Effects[index].type == CE_BLUE_FLASH)
					ent->model = Mod_ForName("models/bluflash.spr", true);
				else if (cl.Effects[index].type == CE_SM_BLUE_FLASH)
					ent->model = Mod_ForName("models/sm_blue.spr", true);
				else if (cl.Effects[index].type == CE_RED_FLASH)
					ent->model = Mod_ForName("models/redspt.spr", true);
				else if (cl.Effects[index].type == CE_HWSPLITFLASH)
				{
					ent->model = Mod_ForName("models/sm_blue.spr", true);
					S_StartSound (TempSoundChannel(), 1, cl_fxsfx_ravensplit, cl.Effects[index].Flash.origin, 1, 1);
				}
				ent->drawflags = DRF_TRANSLUCENT;

			}
			else
			{
				ImmediateFree = true;
			}
			break;

		case CE_RIDER_DEATH:
			cl.Effects[index].RD.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].RD.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].RD.origin[2] = MSG_ReadCoord ();
			break;

		case CE_TELEPORTERPUFFS:
			cl.Effects[index].Teleporter.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Teleporter.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Teleporter.origin[2] = MSG_ReadCoord ();
				
			cl.Effects[index].Teleporter.framelength = .05;
			dir = 0;
			for (i=0;i<8;++i)
			{		
				if ((cl.Effects[index].Teleporter.entity_index[i] = NewEffectEntity()) != -1)
				{
					ent = &EffectEntities[cl.Effects[index].Teleporter.entity_index[i]];
					VectorCopy(cl.Effects[index].Teleporter.origin, ent->origin);

					angleval = dir * M_PI*2 / 360;

					sinval = sin(angleval);
					cosval = cos(angleval);

					cl.Effects[index].Teleporter.velocity[i][0] = 10*cosval;
					cl.Effects[index].Teleporter.velocity[i][1] = 10*sinval;
					cl.Effects[index].Teleporter.velocity[i][2] = 0;
					dir += 45;

					ent->model = Mod_ForName("models/telesmk2.spr", true);
					ent->drawflags = DRF_TRANSLUCENT;
				}
			}
			break;

		case CE_TELEPORTERBODY:
			cl.Effects[index].Teleporter.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Teleporter.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Teleporter.origin[2] = MSG_ReadCoord ();

			cl.Effects[index].Teleporter.velocity[0][0] = MSG_ReadFloat ();
			cl.Effects[index].Teleporter.velocity[0][1] = MSG_ReadFloat ();
			cl.Effects[index].Teleporter.velocity[0][2] = MSG_ReadFloat ();

			skinnum = MSG_ReadFloat ();
			
			cl.Effects[index].Teleporter.framelength = .05;
			dir = 0;
			if ((cl.Effects[index].Teleporter.entity_index[0] = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Teleporter.entity_index[0]];
				VectorCopy(cl.Effects[index].Teleporter.origin, ent->origin);

				ent->model = Mod_ForName("models/teleport.mdl", true);
				ent->drawflags = SCALE_TYPE_XYONLY | DRF_TRANSLUCENT;
				ent->scale = 100;
				ent->skinnum = skinnum;
			}
			break;

		case CE_BONESHRAPNEL:
		case CE_HWBONEBALL:
			cl.Effects[index].Missile.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Missile.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Missile.origin[2] = MSG_ReadCoord ();

			cl.Effects[index].Missile.velocity[0] = MSG_ReadFloat ();
			cl.Effects[index].Missile.velocity[1] = MSG_ReadFloat ();
			cl.Effects[index].Missile.velocity[2] = MSG_ReadFloat ();

			cl.Effects[index].Missile.angle[0] = MSG_ReadFloat ();
			cl.Effects[index].Missile.angle[1] = MSG_ReadFloat ();
			cl.Effects[index].Missile.angle[2] = MSG_ReadFloat ();

			cl.Effects[index].Missile.avelocity[0] = MSG_ReadFloat ();
			cl.Effects[index].Missile.avelocity[1] = MSG_ReadFloat ();
			cl.Effects[index].Missile.avelocity[2] = MSG_ReadFloat ();

			if ((cl.Effects[index].Missile.entity_index = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Missile.entity_index];
				VectorCopy(cl.Effects[index].Missile.origin, ent->origin);
				VectorCopy(cl.Effects[index].Missile.angle, ent->angles);
				if (cl.Effects[index].type == CE_BONESHRAPNEL)
					ent->model = Mod_ForName("models/boneshrd.mdl", true);
				else if (cl.Effects[index].type == CE_HWBONEBALL)
				{
					ent->model = Mod_ForName("models/bonelump.mdl", true);
					S_StartSound (TempSoundChannel(), 1, cl_fxsfx_bonefpow, cl.Effects[index].Missile.origin, 1, 1);
				}
			}
			else
				ImmediateFree = true;

			break;
		case CE_BONESHARD:
		case CE_HWRAVENSTAFF:
		case CE_HWRAVENPOWER:
			cl.Effects[index].Missile.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Missile.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Missile.origin[2] = MSG_ReadCoord ();

			cl.Effects[index].Missile.velocity[0] = MSG_ReadFloat ();
			cl.Effects[index].Missile.velocity[1] = MSG_ReadFloat ();
			cl.Effects[index].Missile.velocity[2] = MSG_ReadFloat ();
			vectoangles(cl.Effects[index].Missile.velocity, cl.Effects[index].Missile.angle);

			if ((cl.Effects[index].Missile.entity_index = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Missile.entity_index];
				VectorCopy(cl.Effects[index].Missile.origin, ent->origin);
				VectorCopy(cl.Effects[index].Missile.angle, ent->angles);
				if (cl.Effects[index].type == CE_HWRAVENSTAFF)
				{
					cl.Effects[index].Missile.avelocity[2] = 1000;
					ent->model = Mod_ForName("models/vindsht1.mdl", true);
				}
				else if (cl.Effects[index].type == CE_BONESHARD)
				{
					cl.Effects[index].Missile.avelocity[0] = (rand() % 1554) - 777;
					ent->model = Mod_ForName("models/boneshot.mdl", true);
					S_StartSound (TempSoundChannel(), 1, cl_fxsfx_bone, cl.Effects[index].Missile.origin, 1, 1);
				}
				else if (cl.Effects[index].type == CE_HWRAVENPOWER)
				{
					ent->model = Mod_ForName("models/ravproj.mdl", true);
					S_StartSound (TempSoundChannel(), 1, cl_fxsfx_ravengo, cl.Effects[index].Missile.origin, 1, 1);
				}
			}
			else
				ImmediateFree = true;
			break;
		case CE_DEATHBUBBLES:
			cl.Effects[index].Bubble.owner = MSG_ReadShort();
			cl.Effects[index].Bubble.offset[0] = MSG_ReadByte();
			cl.Effects[index].Bubble.offset[1] = MSG_ReadByte();
			cl.Effects[index].Bubble.offset[2] = MSG_ReadByte();
			cl.Effects[index].Bubble.count = MSG_ReadByte();//num of bubbles
			cl.Effects[index].Bubble.time_amount = 0;
			break;
		case CE_HWXBOWSHOOT:
			origin[0] = MSG_ReadCoord ();
			origin[1] = MSG_ReadCoord ();
			origin[2] = MSG_ReadCoord ();

			cl.Effects[index].Xbow.angle[0] = MSG_ReadAngle ();
			cl.Effects[index].Xbow.angle[1] = MSG_ReadAngle ();
			cl.Effects[index].Xbow.angle[2] = 0;//MSG_ReadFloat ();

			cl.Effects[index].Xbow.bolts = MSG_ReadByte();
			cl.Effects[index].Xbow.randseed = MSG_ReadByte();

			cl.Effects[index].Xbow.turnedbolts = MSG_ReadByte();

			cl.Effects[index].Xbow.activebolts= MSG_ReadByte();

			setseed(cl.Effects[index].Xbow.randseed);

			AngleVectors (cl.Effects[index].Xbow.angle, forward, right, up);

			VectorNormalize(forward);
			VectorCopy(forward, cl.Effects[index].Xbow.velocity);
//			VectorScale(forward, 1000, cl.Effects[index].Xbow.velocity);

			if (cl.Effects[index].Xbow.bolts == 3)
			{
				S_StartSound (TempSoundChannel(), 1, cl_fxsfx_xbowshoot, origin, 1, 1);
			}
			else if (cl.Effects[index].Xbow.bolts == 5)
			{
				S_StartSound (TempSoundChannel(), 1, cl_fxsfx_xbowfshoot, origin, 1, 1);
			}

			for (i=0;i<cl.Effects[index].Xbow.bolts;i++)
			{
				cl.Effects[index].Xbow.gonetime[i] = 1 + seedrand()*2;
				cl.Effects[index].Xbow.state[i] = 0;

				if ((1<<i)&	cl.Effects[index].Xbow.turnedbolts)
				{
					cl.Effects[index].Xbow.origin[i][0]=MSG_ReadCoord();
					cl.Effects[index].Xbow.origin[i][1]=MSG_ReadCoord();
					cl.Effects[index].Xbow.origin[i][2]=MSG_ReadCoord();
					vtemp[0]=MSG_ReadAngle();
					vtemp[1]=MSG_ReadAngle();
					vtemp[2]=0;
					AngleVectors (vtemp, forward2, right2, up2);
					VectorScale(forward2, 800 + seedrand()*500, cl.Effects[index].Xbow.vel[i]);
				}
				else
				{
					VectorScale(forward, 800 + seedrand()*500, cl.Effects[index].Xbow.vel[i]);

					VectorScale(right,i*100-(cl.Effects[index].Xbow.bolts-1)*50,vtemp);

					//this should only be done for deathmatch:
					VectorScale(vtemp,0.333,vtemp);

					VectorAdd(cl.Effects[index].Xbow.vel[i],vtemp,cl.Effects[index].Xbow.vel[i]);

					//start me off a bit out
					VectorScale(vtemp,0.05,cl.Effects[index].Xbow.origin[i]);
					VectorAdd(origin,cl.Effects[index].Xbow.origin[i],cl.Effects[index].Xbow.origin[i]);
				}

				if ((cl.Effects[index].Xbow.ent[i] = NewEffectEntity()) != -1)
				{
					ent = &EffectEntities[cl.Effects[index].Xbow.ent[i]];
					VectorCopy(cl.Effects[index].Xbow.origin[i], ent->origin);
					vectoangles(cl.Effects[index].Xbow.vel[i],ent->angles);
					if (cl.Effects[index].Xbow.bolts == 5)
						ent->model = Mod_ForName("models/flaming.mdl", true);
					else
						ent->model = Mod_ForName("models/arrow.mdl", true);
				}
			}

			break;
		case CE_HWSHEEPINATOR:
			origin[0] = MSG_ReadCoord ();
			origin[1] = MSG_ReadCoord ();
			origin[2] = MSG_ReadCoord ();

			cl.Effects[index].Xbow.angle[0] = MSG_ReadAngle ();
			cl.Effects[index].Xbow.angle[1] = MSG_ReadAngle ();
			cl.Effects[index].Xbow.angle[2] = 0;//MSG_ReadFloat ();

			cl.Effects[index].Xbow.turnedbolts = MSG_ReadByte();

			cl.Effects[index].Xbow.activebolts= MSG_ReadByte();

			cl.Effects[index].Xbow.bolts = 5;
			cl.Effects[index].Xbow.randseed = 0;

			AngleVectors (cl.Effects[index].Xbow.angle, forward, right, up);

			VectorNormalize(forward);
			VectorCopy(forward, cl.Effects[index].Xbow.velocity);

//			S_StartSound (TempSoundChannel(), 1, cl_fxsfx_xbowshoot, origin, 1, 1);

			for (i=0;i<cl.Effects[index].Xbow.bolts;i++)
			{
				cl.Effects[index].Xbow.gonetime[i] = 0;
				cl.Effects[index].Xbow.state[i] = 0;


				if ((1<<i)&	cl.Effects[index].Xbow.turnedbolts)
				{
					cl.Effects[index].Xbow.origin[i][0]=MSG_ReadCoord();
					cl.Effects[index].Xbow.origin[i][1]=MSG_ReadCoord();
					cl.Effects[index].Xbow.origin[i][2]=MSG_ReadCoord();
					vtemp[0]=MSG_ReadAngle();
					vtemp[1]=MSG_ReadAngle();
					vtemp[2]=0;
					AngleVectors (vtemp, forward2, right2, up2);
					VectorScale(forward2, 700, cl.Effects[index].Xbow.vel[i]);
				}
				else
				{
					VectorCopy(origin,cl.Effects[index].Xbow.origin[i]);
					VectorScale(forward, 700, cl.Effects[index].Xbow.vel[i]);
					VectorScale(right,i*75-150,vtemp);
					VectorAdd(cl.Effects[index].Xbow.vel[i],vtemp,cl.Effects[index].Xbow.vel[i]);
				}

				if ((cl.Effects[index].Xbow.ent[i] = NewEffectEntity()) != -1)
				{
					ent = &EffectEntities[cl.Effects[index].Xbow.ent[i]];
					VectorCopy(cl.Effects[index].Xbow.origin[i], ent->origin);
					vectoangles(cl.Effects[index].Xbow.vel[i],ent->angles);
					ent->model = Mod_ForName("models/polymrph.spr", true);
				}
			}

			break;
		case CE_HWDRILLA:
			cl.Effects[index].Missile.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Missile.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Missile.origin[2] = MSG_ReadCoord ();

			cl.Effects[index].Missile.angle[0] = MSG_ReadAngle ();
			cl.Effects[index].Missile.angle[1] = MSG_ReadAngle ();
			cl.Effects[index].Missile.angle[2] = 0;

			cl.Effects[index].Missile.speed = MSG_ReadShort();

			S_StartSound (TempSoundChannel(), 1, cl_fxsfx_drillashoot, cl.Effects[index].Missile.origin, 1, 1);

			AngleVectors(cl.Effects[index].Missile.angle,cl.Effects[index].Missile.velocity,right,up);

			VectorScale(cl.Effects[index].Missile.velocity,cl.Effects[index].Missile.speed,cl.Effects[index].Missile.velocity);

			if ((cl.Effects[index].Missile.entity_index = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Missile.entity_index];
				VectorCopy(cl.Effects[index].Missile.origin, ent->origin);
				VectorCopy(cl.Effects[index].Missile.angle, ent->angles);
				ent->model = Mod_ForName("models/scrbstp1.mdl", true);
			}
			else
				ImmediateFree = true;
			break;
		case CE_SCARABCHAIN:
			cl.Effects[index].Chain.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Chain.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Chain.origin[2] = MSG_ReadCoord ();

			cl.Effects[index].Chain.owner = MSG_ReadShort ();
			cl.Effects[index].Chain.tag = MSG_ReadByte();
			cl.Effects[index].Chain.material = cl.Effects[index].Chain.owner>>12;
			cl.Effects[index].Chain.owner &= 0x0fff;
			cl.Effects[index].Chain.height = 16;//MSG_ReadByte ();

			cl.Effects[index].Chain.sound_time = cl.time;

			cl.Effects[index].Chain.state = 0;//state 0: move slowly toward owner

			S_StartSound (TempSoundChannel(), 1, cl_fxsfx_scarabwhoosh, cl.Effects[index].Chain.origin, 1, 1);

			if ((cl.Effects[index].Chain.ent1 = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Chain.ent1];
				VectorCopy(cl.Effects[index].Chain.origin, ent->origin);
				ent->model = Mod_ForName("models/scrbpbdy.mdl", true);
			}
			else
				ImmediateFree = true;
			break;
		case CE_TRIPMINE:
			cl.Effects[index].Chain.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Chain.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Chain.origin[2] = MSG_ReadCoord ();

			cl.Effects[index].Chain.velocity[0] = MSG_ReadFloat ();
			cl.Effects[index].Chain.velocity[1] = MSG_ReadFloat ();
			cl.Effects[index].Chain.velocity[2] = MSG_ReadFloat ();

			if ((cl.Effects[index].Chain.ent1 = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Chain.ent1];
				VectorCopy(cl.Effects[index].Chain.origin, ent->origin);
				ent->model = Mod_ForName("models/twspike.mdl", true);
			}
			else
				ImmediateFree = true;
			break;
		case CE_TRIPMINESTILL:
//			Con_DPrintf("Allocating chain effect...\n");
			cl.Effects[index].Chain.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Chain.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Chain.origin[2] = MSG_ReadCoord ();

			cl.Effects[index].Chain.velocity[0] = MSG_ReadFloat ();
			cl.Effects[index].Chain.velocity[1] = MSG_ReadFloat ();
			cl.Effects[index].Chain.velocity[2] = MSG_ReadFloat ();

			if ((cl.Effects[index].Chain.ent1 = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Chain.ent1];
				VectorCopy(cl.Effects[index].Chain.velocity, ent->origin);
				ent->model = Mod_ForName("models/twspike.mdl", true);
			}
			else
			{
//				Con_DPrintf("ERROR: Couldn't allocate chain effect!\n");
				ImmediateFree = true;
			}
			break;
		case CE_HWMISSILESTAR:
		case CE_HWEIDOLONSTAR:
			cl.Effects[index].Star.origin[0] = MSG_ReadCoord ();
			cl.Effects[index].Star.origin[1] = MSG_ReadCoord ();
			cl.Effects[index].Star.origin[2] = MSG_ReadCoord ();

			cl.Effects[index].Star.velocity[0] = MSG_ReadFloat ();
			cl.Effects[index].Star.velocity[1] = MSG_ReadFloat ();
			cl.Effects[index].Star.velocity[2] = MSG_ReadFloat ();
			vectoangles(cl.Effects[index].Star.velocity, cl.Effects[index].Star.angle);
			cl.Effects[index].Missile.avelocity[2] = 300 + rand()%300;

			if ((cl.Effects[index].Star.entity_index = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Star.entity_index];
				VectorCopy(cl.Effects[index].Star.origin, ent->origin);
				VectorCopy(cl.Effects[index].Star.angle, ent->angles);
				ent->model = Mod_ForName("models/ball.mdl", true);
			}
			else
				ImmediateFree = true;

			cl.Effects[index].Star.scaleDir = 1;
			cl.Effects[index].Star.scale = 0.3;
			
			if ((cl.Effects[index].Star.ent1 = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Star.ent1];
				VectorCopy(cl.Effects[index].Star.origin, ent->origin);
				ent->drawflags |= MLS_ABSLIGHT;
				ent->abslight = 0.5;
				ent->angles[2] = 90;
				if(cl.Effects[index].type == CE_HWMISSILESTAR)
				{
					ent->model = Mod_ForName("models/star.mdl", true);	
					ent->scale = 0.3;
					S_StartSound (TempSoundChannel(), 1, cl_fxsfx_mmfire, ent->origin, 1, 1);

				}
				else
				{
					ent->model = Mod_ForName("models/glowball.mdl", true);	
					S_StartSound (TempSoundChannel(), 1, cl_fxsfx_eidolon, ent->origin, 1, 1);
				}
			}
			if(cl.Effects[index].type == CE_HWMISSILESTAR)
			{
				if ((cl.Effects[index].Star.ent2 = NewEffectEntity()) != -1)
				{
					ent = &EffectEntities[cl.Effects[index].Star.ent2];
					VectorCopy(cl.Effects[index].Star.origin, ent->origin);
					ent->model = Mod_ForName("models/star.mdl", true);
					ent->drawflags |= MLS_ABSLIGHT;
					ent->abslight = 0.5;
					ent->scale = 0.3;
				}
			}
			break;
		default:
			Sys_Error ("CL_ParseEffect: bad type");
	}

	if (ImmediateFree)
	{
		cl.Effects[index].type = CE_NONE;
	}
}

// these are in cl_tent.c
void CreateRavenDeath(vec3_t pos);
void CreateExplosionWithSound(vec3_t pos);

void CL_EndEffect(void)
{
	int index;
	entity_t *ent;

	index = MSG_ReadByte();

	switch(cl.Effects[index].type )
	{
	case CE_HWRAVENPOWER:
		if(cl.Effects[index].Missile.entity_index > -1)
		{
			ent = &EffectEntities[cl.Effects[index].Missile.entity_index];
			CreateRavenDeath(ent->origin);
		}
		break;
	case CE_HWRAVENSTAFF:
		if(cl.Effects[index].Missile.entity_index > -1)
		{
			ent = &EffectEntities[cl.Effects[index].Missile.entity_index];
			CreateExplosionWithSound(ent->origin);
		}
		break;
	}
	CL_FreeEffect(index);
}

void XbowImpactPuff(vec3_t origin, int material)//hopefully can use this with xbow & chain both
{
	int		part_color;

	switch(material)
	{
	case XBOW_IMPACT_REDFLESH:
		part_color = 256 + 8 * 16 + rand()%9;				//Blood red
		break;
	case XBOW_IMPACT_STONE:
		part_color = 256 + 20 + rand()%8;			// Gray
		break;
	case XBOW_IMPACT_METAL:
		part_color = 256 + (8 * 15);			// Sparks
		break;
	case XBOW_IMPACT_WOOD:
		part_color = 256 + (5 * 16) + rand()%8;			// Wood chunks
		break;
	case XBOW_IMPACT_ICE:
		part_color = 406+rand()%8;				// Ice particles
		break;
	case XBOW_IMPACT_GREENFLESH:
		part_color = 256 + 183 + rand()%8;		// Spider's have green blood
		break;
	default:
		part_color = 256 + (3 * 16) + 4;		// Dust Brown
		break;
	}

	R_RunParticleEffect4 (origin, 24, part_color, pt_fastgrav, 20);
}

void CL_ReviseEffect(void)	// be sure to read, in the switch statement, everything
							// in this message, even if the effect is not the right kind or invalid,
							// or else client is sure to crash.	
{
	int index,type,revisionCode;
	int curEnt,material,takedamage;
	entity_t	*ent;
	vec3_t	forward,right,up,pos;
	float	dist,speed;
	entity_state_t	*es;


	index = MSG_ReadByte ();
	type = MSG_ReadByte ();

	if (cl.Effects[index].type==type)
		switch(type)
		{
		case CE_SCARABCHAIN://attach to new guy or retract if new guy is world
			curEnt = MSG_ReadShort();
			if (cl.Effects[index].type==type)
			{
				cl.Effects[index].Chain.material = curEnt>>12;
				curEnt &= 0x0fff;

				if (curEnt)
				{
					cl.Effects[index].Chain.state = 1;
					cl.Effects[index].Chain.owner = curEnt;
					es = FindState(cl.Effects[index].Chain.owner);
					if (es)
					{
						ent = &EffectEntities[cl.Effects[index].Chain.ent1];
						XbowImpactPuff(ent->origin, cl.Effects[index].Chain.material);
					}
				}
				else
					cl.Effects[index].Chain.state = 2;
			}
			break;
		case CE_HWXBOWSHOOT:
			revisionCode = MSG_ReadByte();
			//this is one packed byte!
			//highest bit: for impact revision, indicates whether damage is done
			//				for redirect revision, indicates whether new origin was sent
			//next 3 high bits: for all revisions, indicates which bolt is to be revised
			//highest 3 of the low 4 bits: for impact revision, indicates the material that was hit
			//lowest bit: indicates whether revision is of impact or redirect variety


			curEnt = (revisionCode>>4)&7;
			if (revisionCode & 1)//impact effect: 
			{
				cl.Effects[index].Xbow.activebolts &= ~(1<<curEnt);
				dist = MSG_ReadCoord();
				if (cl.Effects[index].Xbow.ent[curEnt]!= -1)
				{
					ent = &EffectEntities[cl.Effects[index].Xbow.ent[curEnt]];

					//make sure bolt is in correct position
					VectorCopy(cl.Effects[index].Xbow.vel[curEnt],forward);
					VectorNormalize(forward);
					VectorScale(forward,dist,forward);
					VectorAdd(cl.Effects[index].Xbow.origin[curEnt],forward,ent->origin);

					material = (revisionCode & 14);
					takedamage = (revisionCode & 128);

					if (takedamage)
					{
						cl.Effects[index].Xbow.gonetime[curEnt] = cl.time;
					}
					else
					{
						cl.Effects[index].Xbow.gonetime[curEnt] += cl.time;
					}
					
					VectorCopy(cl.Effects[index].Xbow.vel[curEnt],forward);
					VectorNormalize(forward);
					VectorScale(forward,8,forward);

					// do a particle effect here, with the color depending on chType
					XbowImpactPuff(ent->origin,material);

					// impact sound:
					switch (material)
					{
					case XBOW_IMPACT_GREENFLESH:
					case XBOW_IMPACT_REDFLESH:
					case XBOW_IMPACT_MUMMY:
						S_StartSound (TempSoundChannel(), 0, cl_fxsfx_arr2flsh, ent->origin, 1, 1);
						break;
					case XBOW_IMPACT_WOOD:
						S_StartSound (TempSoundChannel(), 0, cl_fxsfx_arr2wood, ent->origin, 1, 1);
						break;
					default:
						S_StartSound (TempSoundChannel(), 0, cl_fxsfx_met2stn, ent->origin, 1, 1);
						break;
					}

					CLTENT_XbowImpact(ent->origin, forward, material, takedamage, cl.Effects[index].Xbow.bolts);
				}
			}
			else
			{
				if (cl.Effects[index].Xbow.ent[curEnt]!=-1)
				{
					ent = &EffectEntities[cl.Effects[index].Xbow.ent[curEnt]];
					ent->angles[0] = MSG_ReadAngle();
					if (ent->angles[0] < 0)
						ent->angles[0] += 360;
					ent->angles[0]*=-1;
					ent->angles[1] = MSG_ReadAngle();
					if (ent->angles[1] < 0)
						ent->angles[1] += 360;
					ent->angles[2] = 0;

					if (revisionCode &128)//new origin
					{
						cl.Effects[index].Xbow.origin[curEnt][0]=MSG_ReadCoord();
						cl.Effects[index].Xbow.origin[curEnt][1]=MSG_ReadCoord();
						cl.Effects[index].Xbow.origin[curEnt][2]=MSG_ReadCoord();
					}

					AngleVectors(ent->angles,forward,right,up);
					speed = Length(cl.Effects[index].Xbow.vel[curEnt]);
					VectorScale(forward,speed,cl.Effects[index].Xbow.vel[curEnt]);
					VectorCopy(cl.Effects[index].Xbow.origin[curEnt],ent->origin);
				}
				else
				{
					pos[0] = MSG_ReadAngle();
					if (pos[0] < 0)
						pos[0] += 360;
					pos[0]*=-1;
					pos[1] = MSG_ReadAngle();
					if (pos[1] < 0)
						pos[1] += 360;
					pos[2] = 0;

					if (revisionCode &128)//new origin
					{
						cl.Effects[index].Xbow.origin[curEnt][0]=MSG_ReadCoord();
						cl.Effects[index].Xbow.origin[curEnt][1]=MSG_ReadCoord();
						cl.Effects[index].Xbow.origin[curEnt][2]=MSG_ReadCoord();
					}

					AngleVectors(pos,forward,right,up);
					speed = Length(cl.Effects[index].Xbow.vel[curEnt]);
					VectorScale(forward,speed,cl.Effects[index].Xbow.vel[curEnt]);
				}
			}
			break;

		case CE_HWSHEEPINATOR:
			revisionCode = MSG_ReadByte();
			curEnt = (revisionCode>>4)&7;
			if (revisionCode & 1)//impact
			{
				dist = MSG_ReadCoord();
				cl.Effects[index].Xbow.activebolts &= ~(1<<curEnt);
				if (cl.Effects[index].Xbow.ent[curEnt] != -1)
				{
					ent = &EffectEntities[cl.Effects[index].Xbow.ent[curEnt]];

					//make sure bolt is in correct position
					VectorCopy(cl.Effects[index].Xbow.vel[curEnt],forward);
					VectorNormalize(forward);
					VectorScale(forward,dist,forward);
					VectorAdd(cl.Effects[index].Xbow.origin[curEnt],forward,ent->origin);
					R_ColoredParticleExplosion(ent->origin,(rand()%16)+144/*(144,159)*/,20,30);
				}
			}
			else//direction change
			{
				if (cl.Effects[index].Xbow.ent[curEnt] != -1)
				{
					ent = &EffectEntities[cl.Effects[index].Xbow.ent[curEnt]];
					ent->angles[0] = MSG_ReadAngle();
					if (ent->angles[0] < 0)
						ent->angles[0] += 360;
					ent->angles[0]*=-1;
					ent->angles[1] = MSG_ReadAngle();
					if (ent->angles[1] < 0)
						ent->angles[1] += 360;
					ent->angles[2] = 0;

					if (revisionCode &128)//new origin
					{
						cl.Effects[index].Xbow.origin[curEnt][0]=MSG_ReadCoord();
						cl.Effects[index].Xbow.origin[curEnt][1]=MSG_ReadCoord();
						cl.Effects[index].Xbow.origin[curEnt][2]=MSG_ReadCoord();
					}

					AngleVectors(ent->angles,forward,right,up);
					speed = Length(cl.Effects[index].Xbow.vel[curEnt]);
					VectorScale(forward,speed,cl.Effects[index].Xbow.vel[curEnt]);
					VectorCopy(cl.Effects[index].Xbow.origin[curEnt],ent->origin);
				}
				else
				{
					pos[0] = MSG_ReadAngle();
					if (pos[0] < 0)
						pos[0] += 360;
					pos[0]*=-1;
					pos[1] = MSG_ReadAngle();
					if (pos[1] < 0)
						pos[1] += 360;
					pos[2] = 0;

					if (revisionCode &128)//new origin
					{
						cl.Effects[index].Xbow.origin[curEnt][0]=MSG_ReadCoord();
						cl.Effects[index].Xbow.origin[curEnt][1]=MSG_ReadCoord();
						cl.Effects[index].Xbow.origin[curEnt][2]=MSG_ReadCoord();
					}

					AngleVectors(pos,forward,right,up);
					speed = Length(cl.Effects[index].Xbow.vel[curEnt]);
					VectorScale(forward,speed,cl.Effects[index].Xbow.vel[curEnt]);
				}
			}
			break;


		case CE_HWDRILLA:
			revisionCode = MSG_ReadByte();
			if (revisionCode == 0)//impact
			{
				pos[0] = MSG_ReadCoord();
				pos[1] = MSG_ReadCoord();
				pos[2] = MSG_ReadCoord();
				material = MSG_ReadByte();

				//throw lil bits of victim at entry
				XbowImpactPuff(pos,material);

				if ((material == XBOW_IMPACT_GREENFLESH) || (material == XBOW_IMPACT_GREENFLESH))
				{//meaty sound and some chunks too
					S_StartSound (TempSoundChannel(), 0, cl_fxsfx_drillameat, pos, 1, 1);
					
					//todo: the chunks
				}

				//lil bits at exit
				VectorCopy(cl.Effects[index].Missile.velocity,forward);
				VectorNormalize(forward);
				VectorScale(forward,36,forward);
				VectorAdd(forward,pos,pos);
				XbowImpactPuff(pos,material);
			}
			else//turn
			{
				if (cl.Effects[index].Missile.entity_index!=-1)
				{
					ent = &EffectEntities[cl.Effects[index].Missile.entity_index];
					ent->angles[0] = MSG_ReadAngle();
					if (ent->angles[0] < 0)
						ent->angles[0] += 360;
					ent->angles[0]*=-1;
					ent->angles[1] = MSG_ReadAngle();
					if (ent->angles[1] < 0)
						ent->angles[1] += 360;
					ent->angles[2] = 0;

					cl.Effects[index].Missile.origin[0]=MSG_ReadCoord();
					cl.Effects[index].Missile.origin[1]=MSG_ReadCoord();
					cl.Effects[index].Missile.origin[2]=MSG_ReadCoord();

					AngleVectors(ent->angles,forward,right,up);
					speed = Length(cl.Effects[index].Missile.velocity);
					VectorScale(forward,speed,cl.Effects[index].Missile.velocity);
					VectorCopy(cl.Effects[index].Missile.origin,ent->origin);
				}
				else
				{
					pos[0] = MSG_ReadAngle();
					if (pos[0] < 0)
						pos[0] += 360;
					pos[0]*=-1;
					pos[1] = MSG_ReadAngle();
					if (pos[1] < 0)
						pos[1] += 360;
					pos[2] = 0;

					cl.Effects[index].Missile.origin[0]=MSG_ReadCoord();
					cl.Effects[index].Missile.origin[1]=MSG_ReadCoord();
					cl.Effects[index].Missile.origin[2]=MSG_ReadCoord();

					AngleVectors(pos,forward,right,up);
					speed = Length(cl.Effects[index].Missile.velocity);
					VectorScale(forward,speed,cl.Effects[index].Missile.velocity);
				}
			}
			break;
		}
	else
	{
//		Con_DPrintf("Received Unrecognized Effect Update!\n");
		switch(type)
		{
		case CE_SCARABCHAIN://attach to new guy or retract if new guy is world
			curEnt = MSG_ReadShort();
			break;
		case CE_HWXBOWSHOOT:
			revisionCode = MSG_ReadByte();

			curEnt = (revisionCode>>4)&7;
			if (revisionCode & 1)//impact effect: 
			{
				MSG_ReadCoord();
			}
			else
			{
				MSG_ReadAngle();
				MSG_ReadAngle();
				if (revisionCode &128)//new origin
				{
					MSG_ReadCoord();
					MSG_ReadCoord();
					MSG_ReadCoord();

					// create a clc message to retrieve effect information
//					MSG_WriteByte (&cls.netchan.message, clc_get_effect);
//					MSG_WriteByte (&cls.netchan.message, index);
				}
			}
			break;
		case CE_HWSHEEPINATOR:
			revisionCode = MSG_ReadByte();
			curEnt = (revisionCode>>4)&7;
			if (revisionCode & 1)//impact
			{
				MSG_ReadCoord();
			}
			else//direction change
			{
				MSG_ReadAngle();
				MSG_ReadAngle();
				if (revisionCode &128)//new origin
				{
					MSG_ReadCoord();
					MSG_ReadCoord();
					MSG_ReadCoord();

					// create a clc message to retrieve effect information
//					MSG_WriteByte (&cls.netchan.message, clc_get_effect);
//					MSG_WriteByte (&cls.netchan.message, index);
				}
			}
			break;
		case CE_HWDRILLA:
			revisionCode = MSG_ReadByte();
			if (revisionCode == 0)//impact
			{
				MSG_ReadCoord();
				MSG_ReadCoord();
				MSG_ReadCoord();
				MSG_ReadByte();
			}
			else//turn
			{
				MSG_ReadAngle();
				MSG_ReadAngle();

				MSG_ReadCoord();
				MSG_ReadCoord();
				MSG_ReadCoord();

				// create a clc message to retrieve effect information
//				MSG_WriteByte (&cls.netchan.message, clc_get_effect);
//				MSG_WriteByte (&cls.netchan.message, index);

			}
			break;
		}
	}
}

void UpdateMissilePath(vec3_t oldorg, vec3_t neworg, vec3_t newvel, float time)
{
	vec3_t endpos;	//the position it should be at currently
	float delta;

	delta = cl.time - time;
	
	VectorMA(neworg, delta, newvel, endpos); 
	VectorCopy(neworg, oldorg);	//set orig, maybe vel too
}


void CL_TurnEffect(void)
{
	int index;
	entity_t *ent;
	vec3_t pos, vel;
	float time;

	index = MSG_ReadByte ();
	time = MSG_ReadFloat ();
	pos[0] = MSG_ReadCoord();
	pos[1] = MSG_ReadCoord();
	pos[2] = MSG_ReadCoord();
	vel[0] = MSG_ReadCoord();
	vel[1] = MSG_ReadCoord();
	vel[2] = MSG_ReadCoord();
	switch(cl.Effects[index].type)
	{
	case CE_HWRAVENSTAFF:
	case CE_HWRAVENPOWER:
	case CE_BONESHARD:
	case CE_BONESHRAPNEL:
	case CE_HWBONEBALL:
		if(cl.Effects[index].Missile.entity_index > -1)
		{
			ent = &EffectEntities[cl.Effects[index].Missile.entity_index];
			UpdateMissilePath(ent->origin, pos, vel, time);
			VectorCopy(vel, cl.Effects[index].Missile.velocity);
			vectoangles(cl.Effects[index].Missile.velocity, cl.Effects[index].Missile.angle);
		}
		break;
	case CE_HWMISSILESTAR:
	case CE_HWEIDOLONSTAR:
		if(cl.Effects[index].Star.entity_index > -1)
		{
			ent = &EffectEntities[cl.Effects[index].Star.entity_index];
			UpdateMissilePath(ent->origin, pos, vel, time);
			VectorCopy(vel, cl.Effects[index].Star.velocity);
		}
		break;
	case 0:
		// create a clc message to retrieve effect information
//		MSG_WriteByte (&cls.netchan.message, clc_get_effect);
//		MSG_WriteByte (&cls.netchan.message, index);
//		Con_Printf("CL_TurnEffect: null effect %d\n", index);
		break;
	default:
		Con_Printf ("CL_TurnEffect: bad type %d\n", cl.Effects[index].type);
		break;
	}

}

void CL_LinkEntity(entity_t *ent)
{
	if (cl_numvisedicts == MAX_VISEDICTS)
	{	// object list is full
		//Sys_Error("CL_LinkEntity: Out of vis edicts\n");
		return;
	}

	//debug: if visedicts getting messed up, it should appear here.
//	if (ent->model < 10)
//	{
//		ent->model = 3;
//	}

	cl_visedicts[cl_numvisedicts++] = *ent;
}

void R_RunQuakeEffect (vec3_t org, float distance);
void RiderParticle(int count, vec3_t origin);

void CL_UpdateEffects(void)
{
	int			index,cur_frame;
	vec3_t		mymin,mymax;
	float		frametime;
//	edict_t		test;
//	trace_t		trace;
	vec3_t		org,org2,old_origin;
	int			x_dir,y_dir;
	entity_t	*ent, *ent2;
	float		distance,smoketime;
	int			i;
	entity_state_t	*es;
	mleaf_t		*l;

	frametime = host_frametime;
	if (!frametime) return;
//	Con_Printf("Here at %f\n",cl.time);

	for(index=0;index<MAX_EFFECTS;index++)
	{
		if (!cl.Effects[index].type) 
			continue;

		switch(cl.Effects[index].type)
		{
			case CE_RAIN:
				org[0] = cl.Effects[index].Rain.min_org[0];
				org[1] = cl.Effects[index].Rain.min_org[1];
				org[2] = cl.Effects[index].Rain.max_org[2];

				org2[0] = cl.Effects[index].Rain.e_size[0];
				org2[1] = cl.Effects[index].Rain.e_size[1];
				org2[2] = cl.Effects[index].Rain.e_size[2];

				x_dir = cl.Effects[index].Rain.dir[0];
				y_dir = cl.Effects[index].Rain.dir[1];
				
				cl.Effects[index].Rain.next_time += frametime;
				if (cl.Effects[index].Rain.next_time >= cl.Effects[index].Rain.wait)
				{		
					R_RainEffect(org,org2,x_dir,y_dir,cl.Effects[index].Rain.color,
						cl.Effects[index].Rain.count);
					cl.Effects[index].Rain.next_time = 0;
				}
				break;

			case CE_FOUNTAIN:
				mymin[0] = (-3 * cl.Effects[index].Fountain.vright[0] * cl.Effects[index].Fountain.movedir[0]) +
						   (-3 * cl.Effects[index].Fountain.vforward[0] * cl.Effects[index].Fountain.movedir[1]) +
						   (2 * cl.Effects[index].Fountain.vup[0] * cl.Effects[index].Fountain.movedir[2]);
				mymin[1] = (-3 * cl.Effects[index].Fountain.vright[1] * cl.Effects[index].Fountain.movedir[0]) +
						   (-3 * cl.Effects[index].Fountain.vforward[1] * cl.Effects[index].Fountain.movedir[1]) +
						   (2 * cl.Effects[index].Fountain.vup[1] * cl.Effects[index].Fountain.movedir[2]);
				mymin[2] = (-3 * cl.Effects[index].Fountain.vright[2] * cl.Effects[index].Fountain.movedir[0]) +
						   (-3 * cl.Effects[index].Fountain.vforward[2] * cl.Effects[index].Fountain.movedir[1]) +
						   (2 * cl.Effects[index].Fountain.vup[2] * cl.Effects[index].Fountain.movedir[2]);
				mymin[0] *= 15;
				mymin[1] *= 15;
				mymin[2] *= 15;

				mymax[0] = (3 * cl.Effects[index].Fountain.vright[0] * cl.Effects[index].Fountain.movedir[0]) +
						   (3 * cl.Effects[index].Fountain.vforward[0] * cl.Effects[index].Fountain.movedir[1]) +
						   (10 * cl.Effects[index].Fountain.vup[0] * cl.Effects[index].Fountain.movedir[2]);
				mymax[1] = (3 * cl.Effects[index].Fountain.vright[1] * cl.Effects[index].Fountain.movedir[0]) +
						   (3 * cl.Effects[index].Fountain.vforward[1] * cl.Effects[index].Fountain.movedir[1]) +
						   (10 * cl.Effects[index].Fountain.vup[1] * cl.Effects[index].Fountain.movedir[2]);
				mymax[2] = (3 * cl.Effects[index].Fountain.vright[2] * cl.Effects[index].Fountain.movedir[0]) +
						   (3 * cl.Effects[index].Fountain.vforward[2] * cl.Effects[index].Fountain.movedir[1]) +
						   (10 * cl.Effects[index].Fountain.vup[2] * cl.Effects[index].Fountain.movedir[2]);
				mymax[0] *= 15;
				mymax[1] *= 15;
				mymax[2] *= 15;

				R_RunParticleEffect2 (cl.Effects[index].Fountain.pos,mymin,mymax,
					                  cl.Effects[index].Fountain.color,2,cl.Effects[index].Fountain.cnt);

/*				memset(&test,0,sizeof(test));
				trace = SV_Move (cl.Effects[index].Fountain.pos, mymin, mymax, mymin, false, &test);
				Con_Printf("Fraction is %f\n",trace.fraction);*/
				break;

			case CE_QUAKE:
				R_RunQuakeEffect (cl.Effects[index].Quake.origin,cl.Effects[index].Quake.radius);
				break;

			case CE_RIPPLE:
				cl.Effects[index].Smoke.time_amount += frametime;
				ent = &EffectEntities[cl.Effects[index].Smoke.entity_index];

				smoketime = cl.Effects[index].Smoke.framelength;
				if (!smoketime)
					smoketime = HX_FRAME_TIME*2;

				while(cl.Effects[index].Smoke.time_amount >= smoketime&&ent->scale<250)
				{
					ent->frame++;
					ent->angles[1]+=1;
					cl.Effects[index].Smoke.time_amount -= smoketime;
				}

				if (ent->frame >= 10)
				{
					CL_FreeEffect(index);
				}
				else
					CL_LinkEntity(ent);
				break;


			case CE_WHITE_SMOKE:
			case CE_GREEN_SMOKE:
			case CE_GREY_SMOKE:
			case CE_RED_SMOKE:
			case CE_SLOW_WHITE_SMOKE:
			case CE_TELESMK1:
			case CE_TELESMK2:
			case CE_GHOST:
			case CE_REDCLOUD:
			case CE_FLAMESTREAM:
			case CE_ACID_MUZZFL:
			case CE_FLAMEWALL:
			case CE_FLAMEWALL2:
			case CE_ONFIRE:
				cl.Effects[index].Smoke.time_amount += frametime;
				ent = &EffectEntities[cl.Effects[index].Smoke.entity_index];

				smoketime = cl.Effects[index].Smoke.framelength;
				if (!smoketime)
					smoketime = HX_FRAME_TIME;

				ent->origin[0] += (frametime/smoketime) * cl.Effects[index].Smoke.velocity[0];
				ent->origin[1] += (frametime/smoketime) * cl.Effects[index].Smoke.velocity[1];
				ent->origin[2] += (frametime/smoketime) * cl.Effects[index].Smoke.velocity[2];
	
				i=0;
				while(cl.Effects[index].Smoke.time_amount >= smoketime)
				{
					ent->frame++;
					i++;
					cl.Effects[index].Smoke.time_amount -= smoketime;
				}

				if (ent->frame >= ent->model->numframes)
				{
					CL_FreeEffect(index);
				}
				else
					CL_LinkEntity(ent);

				
				if(cl.Effects[index].type == CE_TELESMK1)
				{
					ent = &EffectEntities[cl.Effects[index].Smoke.entity_index2];

					ent->origin[0] -= (frametime/smoketime) * cl.Effects[index].Smoke.velocity[0];
					ent->origin[1] -= (frametime/smoketime) * cl.Effects[index].Smoke.velocity[1];
					ent->origin[2] -= (frametime/smoketime) * cl.Effects[index].Smoke.velocity[2];

					ent->frame += i;
					if (ent->frame < ent->model->numframes)
					{
						CL_LinkEntity(ent);
					}
				}
				break;

			// Just go through animation and then remove
			case CE_SM_WHITE_FLASH:
			case CE_YELLOWRED_FLASH:
			case CE_BLUESPARK:
			case CE_YELLOWSPARK:
			case CE_SM_CIRCLE_EXP:
			case CE_BG_CIRCLE_EXP:
			case CE_SM_EXPLOSION:
			case CE_SM_EXPLOSION2:
			case CE_BG_EXPLOSION:
			case CE_FLOOR_EXPLOSION:
			case CE_BLUE_EXPLOSION:
			case CE_REDSPARK:
			case CE_GREENSPARK:
			case CE_ICEHIT:
			case CE_MEDUSA_HIT:
			case CE_MEZZO_REFLECT:
			case CE_FLOOR_EXPLOSION2:
			case CE_XBOW_EXPLOSION:
			case CE_NEW_EXPLOSION:
			case CE_MAGIC_MISSILE_EXPLOSION:
			case CE_BONE_EXPLOSION:
			case CE_BLDRN_EXPL:
			case CE_BRN_BOUNCE:
			case CE_ACID_HIT:
			case CE_ACID_SPLAT:
			case CE_ACID_EXPL:
			case CE_LBALL_EXPL:
			case CE_FBOOM:
			case CE_BOMB:
			case CE_FIREWALL_SMALL:
			case CE_FIREWALL_MEDIUM:
			case CE_FIREWALL_LARGE:

				cl.Effects[index].Smoke.time_amount += frametime;
				ent = &EffectEntities[cl.Effects[index].Smoke.entity_index];

				if (cl.Effects[index].type != CE_BG_CIRCLE_EXP)
				{
					while(cl.Effects[index].Smoke.time_amount >= HX_FRAME_TIME)
					{
						ent->frame++;
						cl.Effects[index].Smoke.time_amount -= HX_FRAME_TIME;
					}
				}
				else
				{
					while(cl.Effects[index].Smoke.time_amount >= HX_FRAME_TIME * 2)
					{
						ent->frame++;
						cl.Effects[index].Smoke.time_amount -= HX_FRAME_TIME * 2;
					}
				}


				if (ent->frame >= ent->model->numframes)
				{
					CL_FreeEffect(index);
				}
				else
					CL_LinkEntity(ent);

				break;

			// Go forward then backward through animation then remove
			case CE_WHITE_FLASH:
			case CE_BLUE_FLASH:
			case CE_SM_BLUE_FLASH:
			case CE_HWSPLITFLASH:
			case CE_RED_FLASH:
				cl.Effects[index].Flash.time_amount += frametime;
				ent = &EffectEntities[cl.Effects[index].Flash.entity_index];

				while(cl.Effects[index].Flash.time_amount >= HX_FRAME_TIME)
				{
					if (!cl.Effects[index].Flash.reverse)
					{
						if (ent->frame >= ent->model->numframes-1)  // Ran through forward animation
						{
							cl.Effects[index].Flash.reverse = 1;
							ent->frame--;
						}
						else
							ent->frame++;

					}	
					else
						ent->frame--;

					cl.Effects[index].Flash.time_amount -= HX_FRAME_TIME;
				}

				if ((ent->frame <= 0) && (cl.Effects[index].Flash.reverse))
				{
					CL_FreeEffect(index);
				}
				else
					CL_LinkEntity(ent);
				break;

			case CE_RIDER_DEATH:
				cl.Effects[index].RD.time_amount += frametime;
				if (cl.Effects[index].RD.time_amount >= 1)
				{
					cl.Effects[index].RD.stage++;
					cl.Effects[index].RD.time_amount -= 1;
				}

				VectorCopy(cl.Effects[index].RD.origin,org);
				org[0] += sin(cl.Effects[index].RD.time_amount * 2 * M_PI) * 30;
				org[1] += cos(cl.Effects[index].RD.time_amount * 2 * M_PI) * 30;

				if (cl.Effects[index].RD.stage <= 6)
				{
					RiderParticle(cl.Effects[index].RD.stage+1,org);
				}
				else
				{
					// To set the rider's origin point for the particles
					RiderParticle(0,org);
					if (cl.Effects[index].RD.stage == 7) 
					{
						cl.cshifts[CSHIFT_BONUS].destcolor[0] = 255;
						cl.cshifts[CSHIFT_BONUS].destcolor[1] = 255;
						cl.cshifts[CSHIFT_BONUS].destcolor[2] = 255;
						cl.cshifts[CSHIFT_BONUS].percent = 256;
					}
					else if (cl.Effects[index].RD.stage > 13) 
					{
//						cl.Effects[index].RD.stage = 0;
						CL_FreeEffect(index);
					}
				}
				break;

			case CE_TELEPORTERPUFFS:
				cl.Effects[index].Teleporter.time_amount += frametime;
				smoketime = cl.Effects[index].Teleporter.framelength;

				ent = &EffectEntities[cl.Effects[index].Teleporter.entity_index[0]];
				while(cl.Effects[index].Teleporter.time_amount >= HX_FRAME_TIME)
				{
					ent->frame++;
					cl.Effects[index].Teleporter.time_amount -= HX_FRAME_TIME;
				}
				cur_frame = ent->frame;

				if (cur_frame >= ent->model->numframes)
				{
					CL_FreeEffect(index);
					break;
				}

				for (i=0;i<8;++i)
				{
					ent = &EffectEntities[cl.Effects[index].Teleporter.entity_index[i]];

					ent->origin[0] += (frametime/smoketime) * cl.Effects[index].Teleporter.velocity[i][0];
					ent->origin[1] += (frametime/smoketime) * cl.Effects[index].Teleporter.velocity[i][1];
					ent->origin[2] += (frametime/smoketime) * cl.Effects[index].Teleporter.velocity[i][2];
					ent->frame = cur_frame;

					CL_LinkEntity(ent);
				}
				break;
			case CE_TELEPORTERBODY:
				cl.Effects[index].Teleporter.time_amount += frametime;
				smoketime = cl.Effects[index].Teleporter.framelength;

				ent = &EffectEntities[cl.Effects[index].Teleporter.entity_index[0]];
				while(cl.Effects[index].Teleporter.time_amount >= HX_FRAME_TIME)
				{
					ent->scale -= 15;
					cl.Effects[index].Teleporter.time_amount -= HX_FRAME_TIME;
				}

				ent = &EffectEntities[cl.Effects[index].Teleporter.entity_index[0]];
				ent->angles[1] += 45;

				if (ent->scale <= 10)
				{
					CL_FreeEffect(index);
				}
				else
				{
					CL_LinkEntity(ent);
				}
				break;

			case CE_HWDRILLA:
				cl.Effects[index].Missile.time_amount += frametime;
				ent = &EffectEntities[cl.Effects[index].Missile.entity_index];

				if ((int)(cl.time) != (int)(cl.time - host_frametime))
				{
					S_StartSound (TempSoundChannel(), 1, cl_fxsfx_drillaspin, ent->origin, 1, 1);
				}

				ent->angles[0] += frametime * cl.Effects[index].Missile.avelocity[0];
				ent->angles[1] += frametime * cl.Effects[index].Missile.avelocity[1];
				ent->angles[2] += frametime * cl.Effects[index].Missile.avelocity[2];

				VectorCopy(ent->origin,old_origin);

				ent->origin[0] += frametime * cl.Effects[index].Missile.velocity[0];
				ent->origin[1] += frametime * cl.Effects[index].Missile.velocity[1];
				ent->origin[2] += frametime * cl.Effects[index].Missile.velocity[2];

				R_RocketTrail (old_origin, ent->origin, rt_setstaff);

				CL_LinkEntity(ent);
				break;
			case CE_HWXBOWSHOOT:
				cl.Effects[index].Xbow.time_amount += frametime;
				for (i=0;i<cl.Effects[index].Xbow.bolts;i++)
				{
					if (cl.Effects[index].Xbow.ent[i] != -1)//only update valid effect ents
					{
						if (cl.Effects[index].Xbow.activebolts & (1<<i))//bolt in air, simply update position
						{
							ent = &EffectEntities[cl.Effects[index].Xbow.ent[i]];

							ent->origin[0] += frametime * cl.Effects[index].Xbow.vel[i][0];
							ent->origin[1] += frametime * cl.Effects[index].Xbow.vel[i][1];
							ent->origin[2] += frametime * cl.Effects[index].Xbow.vel[i][2];

							CL_LinkEntity(ent);
						}
						else if (cl.Effects[index].Xbow.bolts == 5)//fiery bolts don't just go away
						{
							if (cl.Effects[index].Xbow.state[i] == 0)//waiting to explode state
							{
								if (cl.Effects[index].Xbow.gonetime[i] > cl.time)//fiery bolts stick around for a while
								{
									ent = &EffectEntities[cl.Effects[index].Xbow.ent[i]];
									CL_LinkEntity(ent);
								}
								else//when time's up on fiery guys, they explode
								{
									//set state to exploding
									cl.Effects[index].Xbow.state[i] = 1;

									ent = &EffectEntities[cl.Effects[index].Xbow.ent[i]];

									//move bolt back a little to make explosion look better
									VectorNormalize(cl.Effects[index].Xbow.vel[i]);
									VectorScale(cl.Effects[index].Xbow.vel[i],-8,cl.Effects[index].Xbow.vel[i]);
									VectorAdd(ent->origin,cl.Effects[index].Xbow.vel[i],ent->origin);

									//turn bolt entity into an explosion
									ent->model = Mod_ForName("models/xbowexpl.spr", true);
									ent->frame = 0;

									//set frame change counter
									cl.Effects[index].Xbow.gonetime[i] = cl.time + HX_FRAME_TIME * 2;

									//play explosion sound
									S_StartSound (TempSoundChannel(), 1, cl_fxsfx_explode, ent->origin, 1, 1);

									CL_LinkEntity(ent);
								}
							}
							else if (cl.Effects[index].Xbow.state[i] == 1)//fiery bolt exploding state
							{
								ent = &EffectEntities[cl.Effects[index].Xbow.ent[i]];

								//increment frame if it's time
								while(cl.Effects[index].Xbow.gonetime[i] <= cl.time)
								{
									ent->frame++;
									cl.Effects[index].Xbow.gonetime[i] += HX_FRAME_TIME * 0.75;
								}


								if (ent->frame >= ent->model->numframes)
								{
									cl.Effects[index].Xbow.state[i] = 2;//if anim is over, set me to inactive state
								}
								else
									CL_LinkEntity(ent);
							}
						}
					}
				}
				break;
			case CE_HWSHEEPINATOR:
				cl.Effects[index].Xbow.time_amount += frametime;
				for (i=0;i<cl.Effects[index].Xbow.bolts;i++)
				{
					if (cl.Effects[index].Xbow.ent[i] != -1)//only update valid effect ents
					{
						if (cl.Effects[index].Xbow.activebolts & (1<<i))//bolt in air, simply update position
						{
							ent = &EffectEntities[cl.Effects[index].Xbow.ent[i]];

							ent->origin[0] += frametime * cl.Effects[index].Xbow.vel[i][0];
							ent->origin[1] += frametime * cl.Effects[index].Xbow.vel[i][1];
							ent->origin[2] += frametime * cl.Effects[index].Xbow.vel[i][2];

							R_RunParticleEffect4(ent->origin,7,(rand()%15)+144,pt_explode2,(rand()%5)+1);

							CL_LinkEntity(ent);
						}
					}
				}
				break;
			case CE_DEATHBUBBLES:
				cl.Effects[index].Bubble.time_amount += frametime;
				if (cl.Effects[index].Bubble.time_amount > 0.1)//10 bubbles a sec
				{
					cl.Effects[index].Bubble.time_amount = 0;
					cl.Effects[index].Bubble.count--;
					es = FindState(cl.Effects[index].Bubble.owner);
					if (es)
					{
						VectorCopy(es->origin,org);
						VectorAdd(org,cl.Effects[index].Bubble.offset,org);

						l = Mod_PointInLeaf (org, cl.worldmodel);
						if(l->contents!=CONTENTS_WATER) 
						{	//not in water anymore
							CL_FreeEffect(index);
							break;
						}
						else
						{
							CLTENT_SpawnDeathBubble(org);
						}
					}
				}
				if (cl.Effects[index].Bubble.count <= 0)
					CL_FreeEffect(index);
				break;
			case CE_SCARABCHAIN:
				cl.Effects[index].Chain.time_amount += frametime;
				ent = &EffectEntities[cl.Effects[index].Chain.ent1];

				switch (cl.Effects[index].Chain.state)
				{
				case 0://zooming in toward owner
					es = FindState(cl.Effects[index].Chain.owner);
					if (cl.Effects[index].Chain.sound_time <= cl.time)
					{
						cl.Effects[index].Chain.sound_time = cl.time + 0.5;
						S_StartSound (TempSoundChannel(), 1, cl_fxsfx_scarabhome, ent->origin, 1, 1);
					}
					if (es)
					{
						VectorCopy(es->origin,org);
						org[2]+=cl.Effects[index].Chain.height;
						VectorSubtract(org,ent->origin,org);
						if (fabs(VectorNormalize(org))<500*frametime)
						{
							S_StartSound (TempSoundChannel(), 1, cl_fxsfx_scarabgrab, ent->origin, 1, 1);
							cl.Effects[index].Chain.state = 1;
							VectorCopy(es->origin, ent->origin);
							ent->origin[2] += cl.Effects[index].Chain.height;
							XbowImpactPuff(ent->origin, cl.Effects[index].Chain.material);
						}
						else
						{
							VectorScale(org,500*frametime,org);
							VectorAdd(ent->origin,org,ent->origin);
						}
					}
					break;
				case 1://attached--snap to owner's pos
					es = FindState(cl.Effects[index].Chain.owner);
					if (es)
					{
						VectorCopy(es->origin, ent->origin);
						ent->origin[2] += cl.Effects[index].Chain.height;
					}
					break;
				case 2://unattaching, server needs to set this state
					VectorCopy(ent->origin,org);
					VectorSubtract(cl.Effects[index].Chain.origin,org,org);
					if (fabs(VectorNormalize(org))>350*frametime)//closer than 30 is too close?
					{
						VectorScale(org,350*frametime,org);
						VectorAdd(ent->origin,org,ent->origin);
					}
					else//done--flash & git outa here (change type to redflash)
					{
						S_StartSound (TempSoundChannel(), 1, cl_fxsfx_scarabbyebye, ent->origin, 1, 1);
						cl.Effects[index].Flash.entity_index = cl.Effects[index].Chain.ent1;
						cl.Effects[index].type = CE_RED_FLASH;
						VectorCopy(ent->origin,cl.Effects[index].Flash.origin);
						cl.Effects[index].Flash.reverse = 0;
						ent->model = Mod_ForName("models/redspt.spr", true);
						ent->frame = 0;
						ent->drawflags = DRF_TRANSLUCENT;
					}
					break;
				}

				CL_LinkEntity(ent);

				//damndamndamn--add stream stuff here!
				VectorCopy(cl.Effects[index].Chain.origin, org);
				VectorCopy(ent->origin, org2);
				CreateStream(TE_STREAM_CHAIN, cl.Effects[index].Chain.ent1, 1, cl.Effects[index].Chain.tag, 0.1, 0, org, org2);

				break;
			case CE_TRIPMINESTILL:
				cl.Effects[index].Chain.time_amount += frametime;
				ent = &EffectEntities[cl.Effects[index].Chain.ent1];

//				if (cl.Effects[index].Chain.ent1 < 0)//fixme: remove this!!!
//					Con_DPrintf("OHSHITOHSHIT--bad chain ent\n");

				CL_LinkEntity(ent);
//				Con_DPrintf("Chain Ent at: %d %d %d\n",(int)cl.Effects[index].Chain.origin[0],(int)cl.Effects[index].Chain.origin[1],(int)cl.Effects[index].Chain.origin[2]);

				//damndamndamn--add stream stuff here!
				VectorCopy(cl.Effects[index].Chain.origin, org);
				VectorCopy(ent->origin, org2);
				CreateStream(TE_STREAM_CHAIN, cl.Effects[index].Chain.ent1, 1, 1, 0.1, 0, org, org2);

				break;
			case CE_TRIPMINE:
				cl.Effects[index].Chain.time_amount += frametime;
				ent = &EffectEntities[cl.Effects[index].Chain.ent1];

				ent->origin[0] += frametime * cl.Effects[index].Chain.velocity[0];
				ent->origin[1] += frametime * cl.Effects[index].Chain.velocity[1];
				ent->origin[2] += frametime * cl.Effects[index].Chain.velocity[2];

				CL_LinkEntity(ent);

				//damndamndamn--add stream stuff here!
				VectorCopy(cl.Effects[index].Chain.origin, org);
				VectorCopy(ent->origin, org2);
				CreateStream(TE_STREAM_CHAIN, cl.Effects[index].Chain.ent1, 1, 1, 0.1, 0, org, org2);

				break;
			case CE_BONESHARD:
			case CE_BONESHRAPNEL:
			case CE_HWBONEBALL:
			case CE_HWRAVENSTAFF:
			case CE_HWRAVENPOWER:
				cl.Effects[index].Missile.time_amount += frametime;
				ent = &EffectEntities[cl.Effects[index].Missile.entity_index];

//		ent->angles[0] = cl.Effects[index].Missile.angle[0];
//		ent->angles[1] = cl.Effects[index].Missile.angle[1];
//		ent->angles[2] = cl.Effects[index].Missile.angle[2];

				ent->angles[0] += frametime * cl.Effects[index].Missile.avelocity[0];
				ent->angles[1] += frametime * cl.Effects[index].Missile.avelocity[1];
				ent->angles[2] += frametime * cl.Effects[index].Missile.avelocity[2];

				ent->origin[0] += frametime * cl.Effects[index].Missile.velocity[0];
				ent->origin[1] += frametime * cl.Effects[index].Missile.velocity[1];
				ent->origin[2] += frametime * cl.Effects[index].Missile.velocity[2];
				if(cl.Effects[index].type == CE_HWRAVENPOWER)
				{
					while(cl.Effects[index].Missile.time_amount >= HX_FRAME_TIME)
					{
						ent->frame++;
						cl.Effects[index].Missile.time_amount -= HX_FRAME_TIME;
					}

					if (ent->frame > 7)
					{
						ent->frame = 0;
						break;
					}
				}
				CL_LinkEntity(ent);
				if(cl.Effects[index].type == CE_HWBONEBALL)
				{
					R_RunParticleEffect4 (ent->origin, 10, 368 + rand() % 16, pt_slowgrav, 3);

				}
				break;
			case CE_HWMISSILESTAR:
			case CE_HWEIDOLONSTAR:
				// update scale
				if(cl.Effects[index].Star.scaleDir)
				{
					cl.Effects[index].Star.scale += 0.05;
					if(cl.Effects[index].Star.scale >= 1)
					{
						cl.Effects[index].Star.scaleDir = 0;
					}
				}
				else
				{
					cl.Effects[index].Star.scale -= 0.05;
					if(cl.Effects[index].Star.scale <= 0.01)
					{
						cl.Effects[index].Star.scaleDir = 1;
					}
				}
				
				cl.Effects[index].Star.time_amount += frametime;
				ent = &EffectEntities[cl.Effects[index].Star.entity_index];

				ent->angles[0] += frametime * cl.Effects[index].Star.avelocity[0];
				ent->angles[1] += frametime * cl.Effects[index].Star.avelocity[1];
				ent->angles[2] += frametime * cl.Effects[index].Star.avelocity[2];

				ent->origin[0] += frametime * cl.Effects[index].Star.velocity[0];
				ent->origin[1] += frametime * cl.Effects[index].Star.velocity[1];
				ent->origin[2] += frametime * cl.Effects[index].Star.velocity[2];

				CL_LinkEntity(ent);
				
				if (cl.Effects[index].Star.ent1 != -1)
				{
					ent2= &EffectEntities[cl.Effects[index].Star.ent1];
					VectorCopy(ent->origin, ent2->origin);
					ent2->scale = cl.Effects[index].Star.scale;
					ent2->angles[1] += frametime * 300;
					ent2->angles[2] += frametime * 400;
					CL_LinkEntity(ent2);
				}
				if(cl.Effects[index].type == CE_HWMISSILESTAR)
				{
					if (cl.Effects[index].Star.ent2 != -1)
					{
						ent2 = &EffectEntities[cl.Effects[index].Star.ent2];
						VectorCopy(ent->origin, ent2->origin);
						ent2->scale = cl.Effects[index].Star.scale;
						ent2->angles[1] += frametime * -300;
						ent2->angles[2] += frametime * -400;
						CL_LinkEntity(ent2);
					}
				}					
				if(rand() % 10 < 3)		
				{
					R_RunParticleEffect4 (ent->origin, 7, 148 + rand() % 11, pt_grav, 10 + rand() % 10);
				}
				break;
		}
	}
//	Con_DPrintf("Effect Ents: %d\n",EffectEntityCount);
}

// this creates multi effects from one packet
void CreateRavenExplosions(vec3_t pos);
void CL_ParseMultiEffect(void)
{
	int type, index, count;
	vec3_t	orig, vel, right;
	entity_t *ent;

	type = MSG_ReadByte();
	switch(type)
	{
	case CE_HWRAVENPOWER:
		orig[0] = MSG_ReadCoord();
		orig[1] = MSG_ReadCoord();
		orig[2] = MSG_ReadCoord();
		vel[0] = MSG_ReadCoord();
		vel[1] = MSG_ReadCoord();
		vel[2] = MSG_ReadCoord();
		for(count=0;count<3;count++)
		{
			index = MSG_ReadByte();
			// create the effect
			cl.Effects[index].type = type;
			VectorCopy(orig, cl.Effects[index].Missile.origin);
			VectorCopy(vel, cl.Effects[index].Missile.velocity);
			vectoangles(cl.Effects[index].Missile.velocity, cl.Effects[index].Missile.angle);
			if ((cl.Effects[index].Missile.entity_index = NewEffectEntity()) != -1)
			{
				ent = &EffectEntities[cl.Effects[index].Missile.entity_index];
				VectorCopy(cl.Effects[index].Missile.origin, ent->origin);
				VectorCopy(cl.Effects[index].Missile.angle, ent->angles);
				ent->model = Mod_ForName("models/ravproj.mdl", true);
				S_StartSound (TempSoundChannel(), 1, cl_fxsfx_ravengo, cl.Effects[index].Missile.origin, 1, 1);
			}
		}
		CreateRavenExplosions(orig);
		break;
	default:
		Sys_Error ("CL_ParseMultiEffect: bad type");

	}	
	
}


//==========================================================================
//
// NewEffectEntity
//
//==========================================================================

static int NewEffectEntity(void)
{
	entity_t	*ent;
	int counter;

	if(cl_numvisedicts == MAX_VISEDICTS)
	{
		return -1;
	}
	if(EffectEntityCount == MAX_EFFECT_ENTITIES)
	{
		return -1;
	}

	for(counter=0;counter<MAX_EFFECT_ENTITIES;counter++)
		if (!EntityUsed[counter]) 
			break;

	EntityUsed[counter] = true;
	EffectEntityCount++;
	ent = &EffectEntities[counter];
	memset(ent, 0, sizeof(*ent));
	ent->colormap = vid.colormap;

	return counter;
}

static void FreeEffectEntity(int index)
{
	if (index != -1 && EntityUsed[index])
	{
		EntityUsed[index] = false;
		EffectEntityCount--;
	}
	else if (index != -1)
	{
		EffectEntityCount--;//still decrement counter; since value is -1, counter was incremented 
//		Con_DPrintf("ERROR: Redeleting Effect Entity: %d!\n",index);//fixme: this should not be in final version
	}
	else
	{
//		Con_DPrintf("ERROR: Deleting Invalid Effect Entity: %d!\n",index);//fixme: this should not be in final version
	}
}


/*
 * $Log: /HexenWorld/Client/cl_effect.c $
 * 
 * 89    5/25/98 1:29p Mgummelt
 * 
 * 88    5/07/98 8:26p Mgummelt
 * 
 * 87    5/05/98 8:33p Mgummelt
 * Added 6th playerclass for Siege only- Dwarf
 * 
 * 86    4/24/98 1:31a Mgummelt
 * Siege version 0.02 4/24/98 1:31 AM
 * 
 * 85    4/23/98 5:15p Mgummelt
 * Fixes for Siege
 * 
 * 84    4/20/98 8:16p Mgummelt
 * Added CE_Ripple
 * 
 * 83    4/20/98 2:02p Rjohnson
 * Big update
 * 
 * 82    4/10/98 3:45p Ssengele
 * made xbolt explosions fast...Way too fast...
 * 
 * 81    4/01/98 3:03p Ssengele
 * faster xbolts, initial optimizing of forcecube.
 * 
 * 80    3/26/98 5:00p Mgummelt
 * Added new Mission Pack client effects for succubus' weapons 
 * 
 * 79    3/20/98 1:41p Ssengele
 * commented debug messages
 * 
 * 78    3/19/98 8:32p Ssengele
 * scarab chains instantly attach to ya, to alleviate possible problems
 * with them setting values on a target they might not ever hit.
 * 
 * 77    3/14/98 3:17p Ssengele
 * lambinator doesn't make xbow sound when firing anymore--old cut 'n'
 * paste thing.  is there a lambinator sound?
 * 
 * 76    3/13/98 1:20a Ssengele
 * debug message out
 * 
 * 75    3/13/98 1:19a Ssengele
 * client effect crash fixed--xbow bolts were using visedicts even tho
 * they were set to -1
 * 
 * 74    3/12/98 12:31p Ssengele
 * same fix as previous check-in, in a different spot.
 * 
 * 73    3/12/98 12:30p Ssengele
 * fixed bug with drilla--new coordinates always sent, but weren't always
 * looked for.
 * 
 * 72    3/11/98 9:46p Ssengele
 * xbolts (& theoretically, setstaff & lambinator) teleport cleanly.
 * currently cause a spike, which can and will be fixed soon.
 * server by default only sends the effects close enough to client to
 * matter to that client.
 * 
 * 71    3/10/98 8:00p Ssengele
 * debug print
 * 
 * 70    3/10/98 5:31p Ssengele
 * client asks for effect if origin changes and client doesn't recognize
 * effect.  more info for effect kept updated on server.  almost ready for
 * effects to work thru teleporters.  hmm...will look into how much
 * traffic the arrows, lambinator, and drilla would take if they used
 * nailgun code.
 * 
 * 69    3/10/98 3:54p Ssengele
 * fixed client effects getting screwed up--xbolt effect ents were getting
 * removed without being set validly
 * 
 * 68    3/09/98 4:47p Ssengele
 * started solidifying server tracking of updateable effects: not used
 * yet; made recoil from daggerdive less extreme than jab; made sure
 * update message doesn't write over effect info if the type updated is
 * different from the type already there.
 * 
 * 67    3/09/98 2:47p Rmidthun
 * when turneffect is called and the effect does not exist, the client
 * will ask for the effect.  
 * 
 * 66    3/07/98 3:52p Rjohnson
 * Doesn't stop the game if max vis edicts is reached
 * 
 * 65    3/07/98 3:07p Rjohnson
 * Increased the max number of vis edicts, and added a check to make sure
 * we don't overflow
 * 
 * 64    3/06/98 4:07p Ssengele
 * impact effect for ovinomancer jobby
 * 
 * 63    3/06/98 12:04p Ssengele
 * fixed bug in failsafe--cut-and-paste made drilla completely screwed
 * 
 * 62    3/06/98 11:25a Rmidthun
 * ravens needed their animation put back
 * 
 * 61    3/05/98 9:58p Ssengele
 * attempt to limit flickering of scarab chains; minimum of 3 b&g mana for
 * untomed scarab
 * 
 * 60    3/05/98 5:57p Ssengele
 * failsafes for updating effects
 * 
 * 59    3/05/98 12:19p Ssengele
 * sheepinator spead corrected
 * 
 * 58    3/04/98 9:58a Rmidthun
 * moved ravenshot explosion into its remove effect
 * 
 * 57    3/03/98 8:49p Ssengele
 * untomed scarab staff should turn now
 * 
 * 56    3/03/98 8:09p Ssengele
 * reflection code for xbow bolts and sheepinator; also, a little variance
 * for where bolts start out, so autoaim doesn't make them clump up quite
 * as much (variance is consistent between sv & cl)
 * 
 * 55    3/03/98 4:43p Ssengele
 * sheepinator uses xbow code now.
 * 
 * 54    3/03/98 3:01p Rmidthun
 * client effect missiles needed an update function to be called when
 * other things change their velocity, this was put into invntory.hc.
 * 
 * 53    3/03/98 1:58p Ssengele
 * er, took a break from weapon stuff to move the bubble code over to a
 * client effect
 * 
 * 52    3/03/98 11:51a Ssengele
 * moved untomed setstaff drilling to client (will add meat chunks to
 * client stuff if time permits)
 * 
 * 51    3/02/98 6:02p Ssengele
 * made scarab chains disappear w/o being burden on nettraffic
 * 
 * 50    3/02/98 4:12p Ssengele
 * minor improvement for xbow, punchdagger sheep gone away
 * 
 * 49    3/02/98 2:35p Ssengele
 * set staff worst-case not bad at all anymore--the drilla is an effect
 * 
 * 48    3/02/98 12:36p Ssengele
 * shaved a few bytes from powered up setstaff (moving puffs to client),
 * have begun working on worst-case unpowered setstaff (explosions are now
 * tempent
 * vs effect & sound they were before). unpowered ss still clogs up net
 * when firing fast--i believe i'll make the whole thing an effect.
 * 
 * 47    2/28/98 3:40p Rjohnson
 * Added protos for all the particle effects, and changed the radius for
 * the crossbow hit
 * 
 * 46    2/28/98 3:26p Ssengele
 * some work on set staff
 * 
 * 45    2/27/98 7:29p Ssengele
 * shaved off a couple more bytes from the xbow
 * 
 * 44    2/27/98 7:12p Ssengele
 * grrrrrrrr...xbow more optimized than ever (plus chunks, or did i add
 * those yesterday?), still spiking not-so-pretty (nudges first red line)
 * occasionally in the worst case scenario.
 * 
 * 43    2/27/98 1:48p Rmidthun
 * seems to be a problem when turneffect is lagged, trying to correct it
 * 
 * 42    2/26/98 7:47p Ssengele
 * redirected arrows' angles correct
 * 
 * 41    2/26/98 6:55p Ssengele
 * arrows fall into bits
 * 
 * 40    2/26/98 5:55p Rmidthun
 * started work on correction mechanism for missile client effects
 * 
 * 39    2/26/98 4:19p Rmidthun
 * added pos to turneffect so it can correct if need be, still working on
 * the actual correction
 * 
 * 38    2/26/98 3:55p Rmidthun
 * powered ravenstaff uses multieffect, also moved tempent stuff to be
 * called from client effects, there is STILL a little red and I'm not
 * sure if the client effects are always in the correct place, but it
 * should be close anyway.
 * 
 * 37    2/26/98 2:35p Ssengele
 * added rubbly particles for xbow impact; er, they last kinda long right
 * now...
 * 
 * 36    2/26/98 2:03p Ssengele
 * further optimizing of xbow--at impact, i send distance it travelled
 * rather than end pos; pack alot of little pieces of info into a byte
 * 
 * 35    2/26/98 1:34p Rmidthun
 * working on multiple effects with one net message
 * 
 * 34    2/25/98 7:21p Ssengele
 * scarab chains look normal when they hit somebody other than their
 * target.
 * 
 * 33    2/25/98 4:00p Rmidthun
 * ravens are now client effect too, still need to reduce the large
 * creation spike
 * 
 * 32    2/25/98 12:22p Rjohnson
 * Put back some particle effects
 * 
 * 31    2/24/98 2:26p Ssengele
 * xbow impact in correct position. !whew!
 * 
 * 30    2/24/98 11:44a Rmidthun
 * magic missiles are client effects, the homing function sends updates
 * 
 * 29    2/24/98 11:07a Ssengele
 * debugging
 * 
 * 28    2/24/98 10:18a Rmidthun
 * telesmk1 will now do both smoke puffs and the sound
 * 
 * 27    2/23/98 8:04p Ssengele
 * erg--checking in so's not to hog these files TOO much.  cleffect that
 * handles multiple xbow bolts is IN, but impact is neither consistant nor
 * in the right spot.
 * 
 * 26    2/23/98 10:44a Rmidthun
 * bone shards should rotate differently
 * 
 * 25    2/23/98 9:48a Rmidthun
 * combined small blue flash and split sound for raven staff
 * 
 * 24    2/20/98 7:32p Ssengele
 * scarab staff faster, lookin' good.  much like the xbow stuff, it spikes
 * and then becomes fine; will work more on it later (movin back to xbow
 * improving).
 * 
 * 23    2/20/98 4:45p Rmidthun
 * moved rest of sounds to client side
 * 
 * 22    2/20/98 3:53p Ssengele
 * scarab staff ok, still needs a good chunk of work: only one chain seems
 * to be rendering
 * 
 * 21    2/20/98 1:21p Rmidthun
 * missile rotation
 * 
 * 20    2/20/98 1:05p Rmidthun
 * sounds use TempSoundChannel so they don't cut each other off, fixed
 * angles for missile effects
 * 
 * 19    2/20/98 12:16p Ssengele
 * client is ready for scarab chain to be a client effect--feel free to
 * make ce_scarabchain be some other value
 * 
 * 18    2/20/98 10:08a Rmidthun
 * bone shards don't need to send angle or angular vel
 * 
 * 17    2/19/98 8:07p Ssengele
 * tripmine client effect in--added access to streams in cl_effect
 * 
 * 16    2/19/98 7:01p Rmidthun
 * ravenstaff is almost all client effect, added a "turneffect" builtin to
 * reduce net traffic further
 * 
 * 15    2/19/98 4:52p Rmidthun
 * added ravenstaff missile, it isn't used just yet
 * 
 * 14    2/19/98 3:55p Rmidthun
 * boneball added
 * 
 * 13    2/19/98 3:00p Ssengele
 * very basic framework for tripmine chain--no access to streams from
 * cl_effects, tho, which causes me great pain and sorrow
 * 
 * 12    2/19/98 2:08p Rmidthun
 * 
 * 11    2/19/98 12:45p Ssengele
 * put in client effect for powered-up xbow; spikes even bigger than
 * normal xbow,tho, so i'd like to chunk all of the arrows into one big
 * client effect, rather than give each of them their own.  this i'll do
 * later.
 * 
 * 10    2/19/98 11:13a Ssengele
 * made xbow sound-playing a little nicer
 * 
 * 9     2/18/98 8:25p Ssengele
 * xbow client effect (for normal mode) lookin good--but it trades off
 * significantly less traffic when arrows are in air for a slightly bigger
 * spike when they're fired.  will figure out how to minimize that spike
 * yet.
 * 
 * 8     2/18/98 5:37p Ssengele
 * start at xbow bolt client effect
 * 
 * 7     2/18/98 2:14p Rmidthun
 * moved sound to client effect
 * 
 * 6     2/18/98 11:50a Rmidthun
 * eidolon missile (in case someone wants coop) 
 * 
 * 5     2/18/98 10:29a Rmidthun
 * 
 * 4     2/17/98 6:52p Rmidthun
 * started moving star to client effect, its about halfway done, I checked
 * in so others could use it
 * 
 * 3     2/06/98 4:43p Rjohnson
 * Got client interpolation in
 * 
 * 2     2/05/98 1:23a Rjohnson
 * Got network protocols aligned
 * 
 * 1     2/04/98 11:33p Rjohnson
 * 
 * 32    10/13/97 10:28a Rlove
 * Made the bone shard powerup more network friendly.
 * 
 * 31    10/10/97 9:13a Rlove
 * Moved bone shard shrapnel to client side effect
 * 
 * 30    10/08/97 11:14a Rlove
 * 
 * 29    10/06/97 12:19p Rjohnson
 * Fixed a name
 * 
 * 28    10/06/97 12:14p Rlove
 * 
 * 27    10/06/97 11:41a Rjohnson
 * Partically fixed teleporter effect
 * 
 * 26    9/25/97 2:31p Rlove
 * 
 * 25    9/04/97 4:44p Rjohnson
 * Updates
 * 
 * 24    8/22/97 7:25a Rlove
 * Added red cloud temp effect
 * 
 * 23    8/07/97 9:00a Rlove
 * 
 * 22    8/06/97 3:46p Rjohnson
 * Rider death update
 * 
 * 21    8/04/97 4:44p Rjohnson
 * Rider's Death doesn't repeat
 * 
 * 20    7/31/97 2:49p Rlove
 * Added a few new effect entities
 * 
 * 19    7/25/97 3:12p Rlove
 * 
 * 18    7/25/97 3:05p Rlove
 * 
 * 17    7/24/97 3:31p Rlove
 * 
 * 16    7/24/97 3:28p Rlove
 * 
 * 15    7/01/97 4:09p Rjohnson
 * Saving / Loading of client effects
 * 
 * 14    6/30/97 10:29a Rlove
 * Added wait to rain entity
 * 
 * 13    6/13/97 11:42a Rlove
 * 
 * 12    6/12/97 12:13p Rlove
 * Added red and green sparks
 * 
 * 11    6/12/97 6:42a Rlove
 * Smoke entities are being deleted (before they weren't)
 * 
 * 10    6/04/97 9:59a Rjohnson
 * Added flash effect for rider's death
 * 
 * 9     6/03/97 10:26a Rlove
 * 
 * 8     6/03/97 9:00a Rlove
 * Added fx_smoke_generator entity
 * 
 * 7     5/30/97 12:01p Rlove
 * New blue explosion
 * 
 * 6     5/30/97 11:42a Rjohnson
 * Removed the message field of the effects
 * 
 * 5     5/28/97 10:45a Rlove
 * Moved sprite effects to client side - smoke, explosions, and flashes.
 * 
 * 4     5/27/97 4:46p Rjohnson
 * Added the smoke puff effect
 * 
 * 3     5/23/97 3:05p Rjohnson
 * Update to effects / particle types
 * 
 * 2     5/20/97 11:32a Rjohnson
 * Revised Effects
 * 
 * 1     5/19/97 2:54p Rjohnson
 * Initial Version
 */
