/*
 * Copyright (C) 1997-2001 Id Software, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
/* cl_ents.c -- entity parsing and management */

#include "client.h"

extern struct model_s *cl_mod_powerscreen;
extern struct model_s *clientGun;

/* PGM */
int		vidref_val;
/* PGM */


/*
 * ==================== CL_Trace
 *
 * ====================
 */

/* Chase Cam */
trace_t
CL_Trace(vec3_t start, vec3_t end, float size, int contentmask)
{
	vec3_t		maxs, mins;

	VectorSet(maxs, size, size, size);
	VectorSet(mins, -size, -size, -size);

	return CM_BoxTrace(start, end, mins, maxs, 0, contentmask);
}

void
ClipCam(vec3_t start, vec3_t end, vec3_t newpos)
{
	int		i;

	trace_t		tr = CL_Trace(start, end, 5, 1);

	for (i = 0; i < 3; i++)
		newpos[i] = tr.endpos[i];
}

/*
 * ================= CL_ParseEntityBits
 *
 * Returns the entity number and the header bits =================
 */
int		bitcounts[32];/* / just for protocol profiling */
int
CL_ParseEntityBits(unsigned *bits)
{
	unsigned	b, total;
	int		i;
	int		number;

	total = MSG_ReadByte(&net_message);
	if (total & U_MOREBITS1) {
		b = MSG_ReadByte(&net_message);
		total |= b << 8;
	}
	if (total & U_MOREBITS2) {
		b = MSG_ReadByte(&net_message);
		total |= b << 16;
	}
	if (total & U_MOREBITS3) {
		b = MSG_ReadByte(&net_message);
		total |= b << 24;
	}
	/* count the bits for net profiling */
	for (i = 0; i < 32; i++)
		if (total & (1 << i))
			bitcounts[i]++;

	if (total & U_NUMBER16)
		number = MSG_ReadShort(&net_message);
	else
		number = MSG_ReadByte(&net_message);

	*bits = total;

	return number;
}

/*
 * ================== CL_ParseDelta
 *
 * Can go from either a baseline or a previous packet_entity ==================
 */
void
CL_ParseDelta(entity_state_t * from, entity_state_t * to, int number, int bits)
{
	/* set everything to the state we are delta'ing from */
	*to = *from;

	VectorCopy(from->origin, to->old_origin);
	to->number = number;

	if (bits & U_MODEL)
		to->modelindex = MSG_ReadByte(&net_message);
	if (bits & U_MODEL2)
		to->modelindex2 = MSG_ReadByte(&net_message);
	if (bits & U_MODEL3)
		to->modelindex3 = MSG_ReadByte(&net_message);
	if (bits & U_MODEL4)
		to->modelindex4 = MSG_ReadByte(&net_message);

	if (bits & U_FRAME8)
		to->frame = MSG_ReadByte(&net_message);
	if (bits & U_FRAME16)
		to->frame = MSG_ReadShort(&net_message);

	if ((bits & U_SKIN8) && (bits & U_SKIN16))	/* used for laser colors */
		to->skinnum = MSG_ReadLong(&net_message);
	else if (bits & U_SKIN8)
		to->skinnum = MSG_ReadByte(&net_message);
	else if (bits & U_SKIN16)
		to->skinnum = MSG_ReadShort(&net_message);

	if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16))
		to->effects = MSG_ReadLong(&net_message);
	else if (bits & U_EFFECTS8)
		to->effects = MSG_ReadByte(&net_message);
	else if (bits & U_EFFECTS16)
		to->effects = MSG_ReadShort(&net_message);

	if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16))
		to->renderfx = MSG_ReadLong(&net_message);
	else if (bits & U_RENDERFX8)
		to->renderfx = MSG_ReadByte(&net_message);
	else if (bits & U_RENDERFX16)
		to->renderfx = MSG_ReadShort(&net_message);

	if (bits & U_ORIGIN1)
		to->origin[0] = MSG_ReadCoord(&net_message);
	if (bits & U_ORIGIN2)
		to->origin[1] = MSG_ReadCoord(&net_message);
	if (bits & U_ORIGIN3)
		to->origin[2] = MSG_ReadCoord(&net_message);

	if (bits & U_ANGLE1)
		to->angles[0] = MSG_ReadAngle(&net_message);
	if (bits & U_ANGLE2)
		to->angles[1] = MSG_ReadAngle(&net_message);
	if (bits & U_ANGLE3)
		to->angles[2] = MSG_ReadAngle(&net_message);

	if (bits & U_OLDORIGIN)
		MSG_ReadPos(&net_message, to->old_origin);

	if (bits & U_SOUND)
		to->sound = MSG_ReadByte(&net_message);

	if (bits & U_EVENT)
		to->event = MSG_ReadByte(&net_message);
	else
		to->event = 0;

	if (bits & U_SOLID)
		to->solid = MSG_ReadShort(&net_message);
}

/*
 * ================== CL_DeltaEntity
 *
 * Parses deltas from the given base and adds the resulting entity to the
 * current frame ==================
 */
void
CL_DeltaEntity(frame_t * frame, int newnum, entity_state_t * old, int bits)
{
	centity_t      *ent;
	entity_state_t *state;

	ent = &cl_entities[newnum];

	state = &cl_parse_entities[cl.parse_entities & (MAX_PARSE_ENTITIES - 1)];
	cl.parse_entities++;
	frame->num_entities++;

	CL_ParseDelta(old, state, newnum, bits);

	/* some data changes will force no lerping */
	if (state->modelindex != ent->current.modelindex
	    || state->modelindex2 != ent->current.modelindex2
	    || state->modelindex3 != ent->current.modelindex3
	    || state->modelindex4 != ent->current.modelindex4
	    || abs(state->origin[0] - ent->current.origin[0]) > 512
	    || abs(state->origin[1] - ent->current.origin[1]) > 512
	    || abs(state->origin[2] - ent->current.origin[2]) > 512
	    || state->event == EV_PLAYER_TELEPORT
	    || state->event == EV_OTHER_TELEPORT
	    ) {
		ent->serverframe = -99;
	}
	if (ent->serverframe != cl.frame.serverframe - 1) {	/* wasn't in last
								 * update, so initialize
								 * some things */
								 
		ent->trailcount = 1024;				/* for diminishing rocket / grenade
						 		 * trails */

		/*
		 * duplicate the current state so lerping doesn't hurt
		 * anything
		 */
		ent->prev = *state;
		if (state->event == EV_OTHER_TELEPORT) {
			VectorCopy(state->origin, ent->prev.origin);
			VectorCopy(state->origin, ent->lerp_origin);
		} else {
			VectorCopy(state->old_origin, ent->prev.origin);
			VectorCopy(state->old_origin, ent->lerp_origin);
		}
	} else {		/* shuffle the last state to previous */
		ent->prev = ent->current;
	}

	ent->serverframe = cl.frame.serverframe;
	ent->current = *state;
}

/*
 * ================== CL_ParsePacketEntities
 *
 * An svc_packetentities has just been parsed, deal with the rest of the data
 * stream. ==================
 */
void
CL_ParsePacketEntities(frame_t * oldframe, frame_t * newframe)
{
	int		newnum;
	unsigned	bits;
	entity_state_t *oldstate = NULL;
	int		oldindex, oldnum;

	newframe->parse_entities = cl.parse_entities;
	newframe->num_entities = 0;

	/* delta from the entities present in oldframe */
	oldindex = 0;
	if (!oldframe)
		oldnum = 99999;
	else {
		if (oldindex >= oldframe->num_entities)
			oldnum = 99999;
		else {
			oldstate = &cl_parse_entities[(oldframe->parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)];
			oldnum = oldstate->number;
		}
	}

	while (1) {
		newnum = CL_ParseEntityBits(&bits);
		if (newnum >= MAX_EDICTS)
			Com_Error(ERR_DROP, "CL_ParsePacketEntities: bad number:%i", newnum);

		if (net_message.readcount > net_message.cursize)
			Com_Error(ERR_DROP, "CL_ParsePacketEntities: end of message");

		if (!newnum)
			break;

		while (oldnum < newnum) {	/* one or more entities from
						 * the old packet are
						 * unchanged */
			if (cl_shownet->value == 3)
				Com_Printf("   unchanged: %i\n", oldnum);
			CL_DeltaEntity(newframe, oldnum, oldstate, 0);

			oldindex++;

			if (oldindex >= oldframe->num_entities)
				oldnum = 99999;
			else {
				oldstate = &cl_parse_entities[(oldframe->parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)];
				oldnum = oldstate->number;
			}
		}

		if (bits & U_REMOVE) {	/* the entity present in oldframe is
					 * not in the current frame */
			if (cl_shownet->value == 3)
				Com_Printf("   remove: %i\n", newnum);
			if (oldnum != newnum)
				Com_Printf("U_REMOVE: oldnum != newnum\n");

			oldindex++;

			if (oldindex >= oldframe->num_entities)
				oldnum = 99999;
			else {
				oldstate = &cl_parse_entities[(oldframe->parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)];
				oldnum = oldstate->number;
			}
			continue;
		}
		if (oldnum == newnum) {	/* delta from previous state */
			if (cl_shownet->value == 3)
				Com_Printf("   delta: %i\n", newnum);
			CL_DeltaEntity(newframe, newnum, oldstate, bits);

			oldindex++;

			if (oldindex >= oldframe->num_entities)
				oldnum = 99999;
			else {
				oldstate = &cl_parse_entities[(oldframe->parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)];
				oldnum = oldstate->number;
			}
			continue;
		}
		if (oldnum > newnum) {	/* delta from baseline */
			if (cl_shownet->value == 3)
				Com_Printf("   baseline: %i\n", newnum);
			CL_DeltaEntity(newframe, newnum, &cl_entities[newnum].baseline, bits);
			continue;
		}
	}

	/* any remaining entities in the old frame are copied over */
	while (oldnum != 99999) {	/* one or more entities from the old
					 * packet are unchanged */
		if (cl_shownet->value == 3)
			Com_Printf("   unchanged: %i\n", oldnum);
		CL_DeltaEntity(newframe, oldnum, oldstate, 0);

		oldindex++;

		if (oldindex >= oldframe->num_entities)
			oldnum = 99999;
		else {
			oldstate = &cl_parse_entities[(oldframe->parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)];
			oldnum = oldstate->number;
		}
	}
}



/*
 * =================== CL_ParsePlayerstate ===================
 */
void
CL_ParsePlayerstate(frame_t * oldframe, frame_t * newframe)
{
	int		flags;
	player_state_t *state;
	int		i;
	int		statbits;

	state = &newframe->playerstate;

	/* clear to old value before delta parsing */
	if (oldframe)
		*state = oldframe->playerstate;
	else
		memset(state, 0, sizeof(*state));

	flags = MSG_ReadShort(&net_message);

	//
	/* parse the pmove_state_t */
	    //
	    if (flags & PS_M_TYPE)
		state->pmove.pm_type = MSG_ReadByte(&net_message);

	if (flags & PS_M_ORIGIN) {
		state->pmove.origin[0] = MSG_ReadShort(&net_message);
		state->pmove.origin[1] = MSG_ReadShort(&net_message);
		state->pmove.origin[2] = MSG_ReadShort(&net_message);
	}
	if (flags & PS_M_VELOCITY) {
		state->pmove.velocity[0] = MSG_ReadShort(&net_message);
		state->pmove.velocity[1] = MSG_ReadShort(&net_message);
		state->pmove.velocity[2] = MSG_ReadShort(&net_message);
	}
	if (flags & PS_M_TIME)
		state->pmove.pm_time = MSG_ReadByte(&net_message);

	if (flags & PS_M_FLAGS)
		state->pmove.pm_flags = MSG_ReadByte(&net_message);

	if (flags & PS_M_GRAVITY)
		state->pmove.gravity = MSG_ReadShort(&net_message);

	if (flags & PS_M_DELTA_ANGLES) {
		state->pmove.delta_angles[0] = MSG_ReadShort(&net_message);
		state->pmove.delta_angles[1] = MSG_ReadShort(&net_message);
		state->pmove.delta_angles[2] = MSG_ReadShort(&net_message);
	}
	if (cl.attractloop)
		state->pmove.pm_type = PM_FREEZE;	/* demo playback */


	/* parse the rest of the player_state_t */
	if (flags & PS_VIEWOFFSET) {
		state->viewoffset[0] = MSG_ReadChar(&net_message) * 0.25;
		state->viewoffset[1] = MSG_ReadChar(&net_message) * 0.25;
		state->viewoffset[2] = MSG_ReadChar(&net_message) * 0.25;
	}
	if (flags & PS_VIEWANGLES) {
		state->viewangles[0] = MSG_ReadAngle16(&net_message);
		state->viewangles[1] = MSG_ReadAngle16(&net_message);
		state->viewangles[2] = MSG_ReadAngle16(&net_message);
	}
	if (flags & PS_KICKANGLES) {
		state->kick_angles[0] = MSG_ReadChar(&net_message) * 0.25;
		state->kick_angles[1] = MSG_ReadChar(&net_message) * 0.25;
		state->kick_angles[2] = MSG_ReadChar(&net_message) * 0.25;
	}
	if (flags & PS_WEAPONINDEX) {
		state->gunindex = MSG_ReadByte(&net_message);
	}
	if (flags & PS_WEAPONFRAME) {
		state->gunframe = MSG_ReadByte(&net_message);
		state->gunoffset[0] = MSG_ReadChar(&net_message) * 0.25;
		state->gunoffset[1] = MSG_ReadChar(&net_message) * 0.25;
		state->gunoffset[2] = MSG_ReadChar(&net_message) * 0.25;
		state->gunangles[0] = MSG_ReadChar(&net_message) * 0.25;
		state->gunangles[1] = MSG_ReadChar(&net_message) * 0.25;
		state->gunangles[2] = MSG_ReadChar(&net_message) * 0.25;
	}
	if (flags & PS_BLEND) {
		state->blend[0] = MSG_ReadByte(&net_message) / 255.0;
		state->blend[1] = MSG_ReadByte(&net_message) / 255.0;
		state->blend[2] = MSG_ReadByte(&net_message) / 255.0;
		state->blend[3] = MSG_ReadByte(&net_message) / 255.0;
	}
	if (flags & PS_FOV)
		state->fov = MSG_ReadByte(&net_message);

	if (flags & PS_RDFLAGS)
		state->rdflags = MSG_ReadByte(&net_message);

	/* parse stats */
	statbits = MSG_ReadLong(&net_message);
	for (i = 0; i < MAX_STATS; i++)
		if (statbits & (1 << i))
			state->stats[i] = MSG_ReadShort(&net_message);
}


/*
 * ================== CL_FireEntityEvents
 *
 * ==================
 */
void
CL_FireEntityEvents(frame_t * frame)
{
	entity_state_t *s1;
	int		pnum, num;

	for (pnum = 0; pnum < frame->num_entities; pnum++) {
		num = (frame->parse_entities + pnum) & (MAX_PARSE_ENTITIES - 1);
		s1 = &cl_parse_entities[num];
		if (s1->event)
			CL_EntityEvent(s1);

		/*
		 * EF_TELEPORTER acts like an event, but is not cleared each
		 * frame
		 */
		if (s1->effects & EF_TELEPORTER)
			CL_TeleporterParticles(s1);
	}
}


/*
 * ================ CL_ParseFrame ================
 */
void
CL_ParseFrame(void)
{
	int		cmd;
	int		len;
	frame_t        *old;

	memset(&cl.frame, 0, sizeof(cl.frame));

	cl.frame.serverframe = MSG_ReadLong(&net_message);
	cl.frame.deltaframe = MSG_ReadLong(&net_message);
	cl.frame.servertime = cl.frame.serverframe * 100;

	/* BIG HACK to let old demos continue to work */
	if (cls.serverProtocol != 26)
		cl.surpressCount = MSG_ReadByte(&net_message);

	if (cl_shownet->value == 3)
		Com_Printf("   frame:%i  delta:%i\n", cl.frame.serverframe,
		    cl.frame.deltaframe);

	/* If the frame is delta compressed from data that we */
	/* no longer have available, we must suck up the rest of */
	/* the frame, but not use it, then ask for a non-compressed */
	/* message  */
	if (cl.frame.deltaframe <= 0) {
		cl.frame.valid = true;	/* uncompressed frame */
		old = NULL;
		cls.demowaiting = false;	/* we can start recording now */
	} else {
		old = &cl.frames[cl.frame.deltaframe & UPDATE_MASK];
		if (!old->valid) {	/* should never happen */
			Com_Printf("Delta from invalid frame (not supposed to happen!).\n");
		}
		if (old->serverframe != cl.frame.deltaframe) {	/* The frame that the
								 * server did the delta
								 * from is too old, so we can't 
								 * reconstruct it properly. */
			Com_Printf("Delta frame too old.\n");
		} else if (cl.parse_entities - old->parse_entities > MAX_PARSE_ENTITIES - 128) {
			Com_Printf("Delta parse_entities too old.\n");
		} else
			cl.frame.valid = true;	/* valid delta parse */
	}

	/* clamp time  */
	if (cl.time > cl.frame.servertime)
		cl.time = cl.frame.servertime;
	else if (cl.time < cl.frame.servertime - 100)
		cl.time = cl.frame.servertime - 100;

	/* read areabits */
	len = MSG_ReadByte(&net_message);
	MSG_ReadData(&net_message, &cl.frame.areabits, len);

	/* read playerinfo */
	cmd = MSG_ReadByte(&net_message);
	SHOWNET(svc_strings[cmd]);
	if (cmd != svc_playerinfo)
		Com_Error(ERR_DROP, "CL_ParseFrame: not playerinfo");
	CL_ParsePlayerstate(old, &cl.frame);

	/* read packet entities */
	cmd = MSG_ReadByte(&net_message);
	SHOWNET(svc_strings[cmd]);
	if (cmd != svc_packetentities)
		Com_Error(ERR_DROP, "CL_ParseFrame: not packetentities");
	CL_ParsePacketEntities(old, &cl.frame);

	/* save the frame off in the backup array for later delta comparisons */
	cl.frames[cl.frame.serverframe & UPDATE_MASK] = cl.frame;

	if (cl.frame.valid) {
		/* getting a valid frame message ends the connection process */
		if (cls.state != ca_active) {
			cls.state = ca_active;
			cl.force_refdef = true;
			cl.predicted_origin[0] = cl.frame.playerstate.pmove.origin[0] * 0.125;
			cl.predicted_origin[1] = cl.frame.playerstate.pmove.origin[1] * 0.125;
			cl.predicted_origin[2] = cl.frame.playerstate.pmove.origin[2] * 0.125;
			VectorCopy(cl.frame.playerstate.viewangles, cl.predicted_angles);
			if (cls.disable_servercount != cl.servercount
			    && cl.refresh_prepped)
				SCR_EndLoadingPlaque();	/* get rid of loading plaque */
		}
		cl.sound_prepped = true;	/* can start mixing ambient sounds */

		/* fire entity events */
		CL_FireEntityEvents(&cl.frame);
		CL_CheckPredictionError();
	}
}

/*
 * ==========================================================================
 *
 * INTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS
 *
 * ==========================================================================
 */

struct model_s *
S_RegisterSexedModel(entity_state_t * ent, char *base)
{
	int		n;
	char           *p;
	struct model_s *mdl;
	char		model[MAX_QPATH];
	char		buffer[MAX_QPATH];

	/* determine what model the client is using */
	model[0] = 0;
	n = CS_PLAYERSKINS + ent->number - 1;
	if (cl.configstrings[n][0]) {
		p = strchr(cl.configstrings[n], '\\');
		if (p) {
			p += 1;
			Q_strncpyz(model, p, sizeof(model));
			p = strchr(model, '/');
			if (p)
				*p = 0;
		}
	}
	/* if we can't figure it out, they're male */
	if (!model[0])
		Q_strncpyz(model, "male", sizeof(model));

	Com_sprintf(buffer, sizeof(buffer), "players/%s/%s", model, base + 1);
	mdl = re.RegisterModel(buffer);
	if (!mdl) {
		/* not found, try default weapon model */
		Com_sprintf(buffer, sizeof(buffer), "players/%s/weapon.md2", model);
		mdl = re.RegisterModel(buffer);
		if (!mdl) {
			/* no, revert to the male model */
			Com_sprintf(buffer, sizeof(buffer), "players/%s/%s", "male", base + 1);
			mdl = re.RegisterModel(buffer);
			if (!mdl) {
				/* last try, default male weapon.md2 */
				Com_sprintf(buffer, sizeof(buffer), "players/male/weapon.md2");
				mdl = re.RegisterModel(buffer);
			}
		}
	}
	return mdl;
}

/* PMM - used in shell code  */
extern int	Developer_searchpath(int who);

/* pmm */

/*
 * =============== CL_AddPacketEntities
 *
 * ===============
 */
void
CL_AddPacketEntities(frame_t * frame)
{
	entity_t	ent;
	entity_state_t *s1;
	float		autorotate;
	float		bob, bob_scale;
	int		i;
	int		pnum;
	centity_t      *cent;
	int		autoanim;
	clientinfo_t   *ci;
	unsigned int	effects, renderfx;

	/* bonus items rotate at a fixed rate */
	autorotate = anglemod(cl.time / 10);

	/* brush models can auto animate their frames */
	autoanim = 2 * cl.time / 1000;

	memset(&ent, 0, sizeof(ent));

	VectorCopy(vec3_origin, clientOrg);

	for (pnum = 0; pnum < frame->num_entities; pnum++) {
		qboolean	isclientviewer = false;
		qboolean	drawEnt = true;

		s1 = &cl_parse_entities[(frame->parse_entities + pnum) & (MAX_PARSE_ENTITIES - 1)];

		cent = &cl_entities[s1->number];

		effects = s1->effects;
		renderfx = s1->renderfx;

		/* if i want to multiply alpha, then id better do this... */
		ent.alpha = 1;
		/* reset this */
		ent.renderfx = 0;

		/* set frame */
		if (effects & EF_ANIM01)
			ent.frame = autoanim & 1;
		else if (effects & EF_ANIM23)
			ent.frame = 2 + (autoanim & 1);
		else if (effects & EF_ANIM_ALL)
			ent.frame = autoanim;
		else if (effects & EF_ANIM_ALLFAST)
			ent.frame = cl.time / 100;
		else
			ent.frame = s1->frame;

		if (effects & (EF_PLASMA | EF_HYPERBLASTER | EF_ROCKET | EF_BLASTER))
			ent.renderfx |= RF2_NOSHADOW;

		/* quad and pent can do different things on client */
		if (effects & EF_PENT) {
			effects &= ~EF_PENT;
			effects |= EF_COLOR_SHELL;
			renderfx |= RF_SHELL_RED;
		}
		if (effects & EF_QUAD) {
			effects &= ~EF_QUAD;
			effects |= EF_COLOR_SHELL;
			renderfx |= RF_SHELL_BLUE;
		}
		/* ====== */
		/* PMM */
		if (effects & EF_DOUBLE) {
			effects &= ~EF_DOUBLE;
			effects |= EF_COLOR_SHELL;
			renderfx |= RF_SHELL_DOUBLE;
		}
		if (effects & EF_HALF_DAMAGE) {
			effects &= ~EF_HALF_DAMAGE;
			effects |= EF_COLOR_SHELL;
			renderfx |= RF_SHELL_HALF_DAM;
		}
		/* pmm */
		/* ====== */
		ent.oldframe = cent->prev.frame;
		ent.backlerp = 1.0 - cl.lerpfrac;

		if (renderfx & (RF_FRAMELERP | RF_BEAM)) {	/* step origin
								 * discretely, because
								 * the frames */
			/* do the animation properly */
			VectorCopy(cent->current.origin, ent.origin);
			VectorCopy(cent->current.old_origin, ent.oldorigin);
		} else {	/* interpolate origin */
			for (i = 0; i < 3; i++) {
				ent.origin[i] = ent.oldorigin[i] = cent->prev.origin[i] + cl.lerpfrac *
				    (cent->current.origin[i] - cent->prev.origin[i]);
			}
		}

		/* create a new entity */

		/* tweak the color of beams */
		if (renderfx & RF_BEAM) {	/* the four beam colors are
						 * encoded in 32 bits of
						 * skinnum (hack) */
			ent.alpha = 0.30;
			ent.skinnum = (s1->skinnum >> ((rand() % 4) * 8)) & 0xff;
			ent.model = NULL;
		} else {
			/* set skin */
			if (s1->modelindex == 255) {	/* use custom player skin */
				ent.skinnum = 0;
				ci = &cl.clientinfo[s1->skinnum & 0xff];
				ent.skin = ci->skin;
				ent.model = ci->model;
				if (!ent.skin || !ent.model) {
					ent.skin = cl.baseclientinfo.skin;
					ent.model = cl.baseclientinfo.model;
				}
				/* ============ */
				/* PGM */
				if (renderfx & RF_USE_DISGUISE) {
					if (!strncmp((char *)ent.skin, "players/male", 12)) {
						ent.skin = re.RegisterSkin("players/male/disguise.pcx");
						ent.model = re.RegisterModel("players/male/tris.md2");
					} else if (!strncmp((char *)ent.skin, "players/female", 14)) {
						ent.skin = re.RegisterSkin("players/female/disguise.pcx");
						ent.model = re.RegisterModel("players/female/tris.md2");
					} else if (!strncmp((char *)ent.skin, "players/cyborg", 14)) {
						ent.skin = re.RegisterSkin("players/cyborg/disguise.pcx");
						ent.model = re.RegisterModel("players/cyborg/tris.md2");
					}
				}
				/* PGM */
				/* ============ */
			} else {
				ent.skinnum = s1->skinnum;
				ent.skin = NULL;
				ent.model = cl.model_draw[s1->modelindex];
			}
		}
#ifdef QMAX
		if (ent.model) {
		/***** MODEL / EFFECT SWAPPING ETC *** - per gametype... */
			extern cvar_t	*cl_flame_enh;
			extern cvar_t	*cl_flame_size;
			float	interpolframe = (float)ent.frame + cl.lerpfrac * (float)(ent.frame - ent.oldframe);
			float	size = (interpolframe) / cl_flame_size->value;
	
#if 1			
			if (modType("gloom")) {
				if (!Q_strcasecmp((char *)ent.model, "sprites/s_firea.sp2")
					|| !Q_strcasecmp((char *)ent.model, "sprites/s_fireb.sp2") 
					|| !Q_strcasecmp((char *)ent.model, "sprites/s_flame.sp2")) {
					CL_FlameTrail (cent->lerp_origin, ent.origin, 25, 25, false);
					VectorCopy (ent.origin, cent->lerp_origin);
					continue;
				}
				else if (!Q_strcasecmp((char *)ent.model, "sprites/s_flame.sp2")) {
					CL_Flame (ent.origin, false);
					VectorCopy (ent.origin, cent->lerp_origin);
					continue;
				}
				else if (!Q_strcasecmp((char *)ent.model, "models/objects/explode/tris.md2")) {
					CL_GloomFlame (ent.origin, false);
					VectorCopy (ent.origin, cent->lerp_origin);
					continue;
				}
				else if (!Q_strcasecmp((char *)ent.model, "models/objects/smokexp/tris.md2")) {
					CL_GloomFlameTrail (cent->lerp_origin, ent.origin, 25, 25, false);
					VectorCopy (ent.origin, cent->lerp_origin);
					continue;
				}
			}
#endif					
			if ((!Q_strcasecmp((char *)ent.model, "sprites/s_explod.sp2") || 
				(!Q_strcasecmp((char *)ent.model, "models/objects/r_explode/tris.md2"))) /* &&
				(!modType("awaken2")) */ && cl_flame_enh->value) {	/* flamethrower */
				
					renderfx |= RF_TRANS_ADDITIVE | RF_TRANSLUCENT;
					ent.alpha = 0.5;
					effects &= ~EF_ROCKET;
					
				if (size == 0) {
					vec3_t		vec;
					float		len;

					VectorSubtract(cent->lerp_origin, ent.origin, vec);
					len = VectorNormalize(vec);
					VectorScale(vec, 25, vec);
					VectorAdd(vec, cent->lerp_origin, vec);
					/*if (modType("dday"))*/
						CL_BlueFlameTrail(cent->lerp_origin, vec);
				} else {
					if  (modType("lox")) {
						CL_FlameTrail (cent->lerp_origin, ent.origin, 
								25, 25, true);
						VectorCopy (ent.origin, cent->lerp_origin);
						continue;
					}
					else
						CL_FlameTrail(cent->lerp_origin, ent.origin, 
						               3 + size * 25.0, 10.0, true);
				}

				drawEnt = false;
			} else if ((!Q_strcasecmp((char *)ent.model, "models/fire/tris.md2") ||
			            (!Q_strcasecmp((char *)ent.model, "sprites/faf/fire2.sp2")) ||
			            (!Q_strcasecmp((char *)ent.model, "fire.md2" /* WoD */))) /*&&
					(!modType("awaken2"))*/ && cl_flame_enh->value) {	/* flame */
				/* not smoke yet make two for flame strength	*/
				if (ent.frame != 39) {
					CL_Flame(ent.origin, false);
					CL_Flame(ent.origin, false);
					CL_Flame(ent.origin, true);
				} else
					CL_SmokeTrail(ent.origin, cent->lerp_origin);

				VectorCopy(ent.origin, cent->lerp_origin);
				continue;		
			}
			else if (!Q_strcasecmp((char *)ent.model, "models/objects/laser/tris.md2")) {	/* tracer */
				CL_BlasterTracer(ent.origin, s1->angles, 255, 200, 50, 25, 0.5);
				drawEnt = false;
			}
		
			if (!Q_strcasecmp((char *)ent.model, "models/objects/laser/tris.md2")
		    		&& !(effects & EF_BLASTER)) {
				if (cl_hyperblaster_color->value == 1) {
					/* give the bolt a red particle glow */
					CL_HyperBlasterEffect(cent->lerp_origin, ent.origin, s1->angles,
					235, 50, 50, 0, -90, -30, -10, 2);
					V_AddLight(ent.origin, 200, 1, 0, 0);
				} else if (cl_hyperblaster_color->value == 2) {
					/* give the bolt a green particle glow */
					CL_HyperBlasterEffect(cent->lerp_origin, ent.origin, s1->angles,
					50, 235, 50, -10, 0, -10, 10, 2);
					V_AddLight(ent.origin, 200, 0, 1, 0);
				} else if (cl_hyperblaster_color->value == 3) {
					/* give the bolt a blue particle glow */
					CL_HyperBlasterEffect(cent->lerp_origin, ent.origin, s1->angles,
					50, 50, 235, 0, -10, 0, -10, 2);
					V_AddLight(ent.origin, 200, 0, 0, 1);
				} else {
					CL_HyperBlasterEffect(cent->lerp_origin, ent.origin, s1->angles,
				    	255, 150, 50, 0, -90, -30, 10, 2);
				}
				drawEnt = false;
			}
		}
#endif		
		/* only used for black hole model right now, FIXME: do better */
		if (renderfx == RF_TRANSLUCENT)
			ent.alpha = 0.70;

		/* render effects (fullbright, translucent, etc) */
		if ((effects & EF_COLOR_SHELL))
			ent.flags = 0;	/* renderfx go on color shell entity */
		else
			ent.flags = renderfx;

		/* calculate angles */
		if (effects & EF_ROTATE) {	/* some bonus items auto-rotate */
			ent.angles[0] = 0;
			ent.angles[1] = autorotate;
			ent.angles[2] = 0;
			/* bobbing items */
			if (cl_bobbing->value) {
				bob_scale = (0.005 + s1->number * 0.00001) * 0.5;
				bob = 5 + cos((cl.time + 1000) * bob_scale) * 5;
				ent.oldorigin[2] += bob;
				ent.origin[2] += bob;
			}
		}
		/* RAFAEL */
		else if (effects & EF_SPINNINGLIGHTS) {
			ent.angles[0] = 0;
			ent.angles[1] = anglemod(cl.time / 2) + s1->angles[1];
			ent.angles[2] = 180;
			{
				vec3_t		forward;
				vec3_t		start;

				AngleVectors(ent.angles, forward, NULL, NULL);
				VectorMA(ent.origin, 64, forward, start);
				V_AddLight(start, 100, 1, 0, 0);
			}
		} else {		/* interpolate angles */
			float		a1      , a2;

			for (i = 0; i < 3; i++) {
				a1 = cent->current.angles[i];
				a2 = cent->prev.angles[i];
				ent.angles[i] = LerpAngle(a2, a1, cl.lerpfrac);
			}
		}

		if (cl_models_overbright->value) {
			ent.flags = renderfx | RF_FULLBRIGHT;
		}
	
		/* c14 illuminating shells */
		if (renderfx & RF_SHELL_RED)
			V_AddLight(ent.origin, 100, 1.0, 0.0, 0.0);
		if (renderfx & RF_SHELL_BLUE)
			V_AddLight(ent.origin, 100, 0.0, 0.0, 1.0);
		if (renderfx & RF_SHELL_GREEN)
			V_AddLight(ent.origin, 100, 0.0, 1.0, 0.0);
	
		if (s1->number == cl.playernum + 1) {
			ent.flags |= RF_VIEWERMODEL;	/* only draw from mirrors */
			isclientviewer = true;
			/* FIXME: still pass to refresh */

			if (effects & EF_FLAG1)
				V_AddLight(ent.origin, 225, 1.0, 0.1, 0.1);
			else if (effects & EF_FLAG2)
				V_AddLight(ent.origin, 225, 0.1, 0.1, 1.0);
			else if (effects & EF_TAGTRAIL)				/* PGM */
				V_AddLight(ent.origin, 225, 1.0, 1.0, 0.0);	/* PGM */
			else if (effects & EF_TRACKERTRAIL)			/* PGM */
				V_AddLight(ent.origin, 225, -1.0, -1.0, -1.0);	/* PGM */
			if (!cl_3dcam->value)
				continue;
		}
		
		/* Knightmare added for mirroring */
		if (renderfx & RF_MIRRORMODEL)
			ent.flags |= RF_MIRRORMODEL;
		
		/* if set to invisible, skip */
		if (!s1->modelindex)
			continue;

		if (effects & EF_BFG) {
			ent.flags |= RF_TRANSLUCENT;
			ent.alpha = 0.30;
		}
		/* RAFAEL */
		if (effects & EF_PLASMA) {
			ent.flags |= RF_TRANSLUCENT;
			ent.alpha = 0.6;
		}
		if (effects & EF_SPHERETRANS) {
			ent.flags |= RF_TRANSLUCENT;
			/* PMM - *sigh*  yet more EF overloading */
			if (effects & EF_TRACKERTRAIL)
				ent.alpha = 0.6;
			else
				ent.alpha = 0.3;
		}
		/* pmm */

		/* add to refresh list */
		if (drawEnt)
			V_AddEntity(&ent);


		/* color shells generate a seperate entity for the main model */
		/* if (effects & EF_COLOR_SHELL) */
		if (effects & EF_COLOR_SHELL && (!isclientviewer || (cl_3dcam->value && ((cl_predict->value) && !(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION)) && !(cl.attractloop && !(cl.cinematictime > 0 && cls.realtime - cl.cinematictime > 1000))))) {
			/* PMM - at this point, all of the shells have been handled 
			 * if we're in the rogue pack, set up the custom mixing, 
			 * otherwise just keep going */

			/*
		 	* all of the solo colors are fine.  we need to catch any of
		 	* the combinations that look bad
			* (double & half) and turn them into the appropriate color,
			* and make double/quad something special
			*/
			if (renderfx & RF_SHELL_HALF_DAM) {
				if (Developer_searchpath(2) == 2) {

					/*
					 * ditch the half damage shell if any of red,
					 * blue, or double are on
					 */
					if (renderfx & (RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_DOUBLE))
						renderfx &= ~RF_SHELL_HALF_DAM;
				}
			}
			if (renderfx & RF_SHELL_DOUBLE) {
				if (Developer_searchpath(2) == 2) {

					/*
					 * lose the yellow shell if we have a red,
					 * blue, or green shell
					 */
					if (renderfx & (RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_GREEN))
						renderfx &= ~RF_SHELL_DOUBLE;

					/*
					 * if we have a red shell, turn it to purple
					 * by adding blue
					 */
					if (renderfx & RF_SHELL_RED)
						renderfx |= RF_SHELL_BLUE;

					/*
					 * if we have a blue shell (and not a red
					 * shell), turn it to cyan by adding green
					 */
					else if (renderfx & RF_SHELL_BLUE) {

						/*
						 * go to green if it's on already,
						 * otherwise do cyan (flash green)
						 */
						if (renderfx & RF_SHELL_GREEN)
							renderfx &= ~RF_SHELL_BLUE;
						else
							renderfx |= RF_SHELL_GREEN;
					}
				}
			}
			/* pmm */
			ent.flags = renderfx | RF_TRANSLUCENT;
			ent.alpha = 0.30;
			V_AddEntity(&ent);

			/* c14 add linked models under shell */
			/* duplicate for linked models */
			if (s1->modelindex2) {
				if (s1->modelindex2 == 255) {	/* custom weapon */
					ci = &cl.clientinfo[s1->skinnum & 0xff];
					i = (s1->skinnum >> 8);	/* 0 is default weapon model */
					if (!cl_vwep->value || i > MAX_CLIENTWEAPONMODELS - 1)
						i = 0;
					ent.model = ci->weaponmodel[i];
					if (!ent.model) {
						if (i != 0)
							ent.model = ci->weaponmodel[0];
						if (!ent.model)
							ent.model = cl.baseclientinfo.weaponmodel[0];
					}
				} else
					ent.model = cl.model_draw[s1->modelindex2];

				V_AddEntity(&ent);

			}
			if (s1->modelindex3) {
				ent.model = cl.model_draw[s1->modelindex3];
				V_AddEntity(&ent);
			}
			if (s1->modelindex4) {
				ent.model = cl.model_draw[s1->modelindex4];
				V_AddEntity(&ent);
			}
		}
		ent.skin = NULL;	/* never use a custom skin on others */
		ent.skinnum = 0;
		ent.flags = 0;
		ent.alpha = 1;

		/* duplicate for linked models */
		if (s1->modelindex2) {

			if (isclientviewer)
				ent.flags |= RF_VIEWERMODEL;

			if (s1->modelindex2 == 255) {	/* custom weapon */
				ci = &cl.clientinfo[s1->skinnum & 0xff];
				i = (s1->skinnum >> 8);	/* 0 is default weapon model */
				if (!cl_vwep->value || i > MAX_CLIENTWEAPONMODELS - 1)
					i = 0;
				ent.model = ci->weaponmodel[i];
				if (!ent.model) {
					if (i != 0)
						ent.model = ci->weaponmodel[0];
					if (!ent.model)
						ent.model = cl.baseclientinfo.weaponmodel[0];
				}
			} else
				ent.model = cl.model_draw[s1->modelindex2];

			/*
			 * PMM - check for the defender sphere shell .. make it
			 * translucent
			 */

			/*
			 * replaces the previous version which used the high bit on
			 * modelindex2 to determine transparency
			 */
			if (!Q_strcasecmp(cl.configstrings[CS_MODELS + (s1->modelindex2)],
			     "models/items/shell/tris.md2")) {
				ent.alpha = 0.32;
				ent.flags = RF_TRANSLUCENT;
			}
			/* pmm */
			if (isclientviewer)
				ent.flags |= RF_VIEWERMODEL;
			
			/* Knightmare added for mirroring */
			if (renderfx & RF_MIRRORMODEL)
				ent.flags |= RF_MIRRORMODEL;
			
			if (drawEnt)
				V_AddEntity(&ent);

			/* PGM - make sure these get reset. */
			ent.flags = 0;
			ent.alpha = 0;
			/* PGM */
		}
		if (s1->modelindex3) {

			if (isclientviewer)
				ent.flags |= RF_VIEWERMODEL;
			
			/* Knightmare added for mirroring */
			if (renderfx & RF_MIRRORMODEL)
				ent.flags |= RF_MIRRORMODEL;

			ent.model = cl.model_draw[s1->modelindex3];

			if (drawEnt)
				V_AddEntity(&ent);
		}
		if (s1->modelindex4) {

			if (isclientviewer)
				ent.flags |= RF_VIEWERMODEL;
			
			/* Knightmare added for mirroring */
			if (renderfx & RF_MIRRORMODEL)
				ent.flags |= RF_MIRRORMODEL;

			ent.model = cl.model_draw[s1->modelindex4];

			if (drawEnt)
				V_AddEntity(&ent);
		}
		if (effects & EF_POWERSCREEN) {
			ent.model = cl_mod_powerscreen;
			ent.oldframe = 0;
			ent.frame = 0;
			ent.flags |= (RF_TRANSLUCENT | RF_SHELL_GREEN);
			ent.alpha = 0.30;
			if (drawEnt)
				V_AddEntity(&ent);
		}
		/* add automatic particle trails */
		if ((effects & ~EF_ROTATE)) {
			if (effects & EF_ROCKET) {
				CL_RocketTrail(cent->lerp_origin, ent.origin, cent);
				V_AddLight(ent.origin, 200, 1, 1, 0);
			}
			/* PGM - Do not reorder EF_BLASTER and EF_HYPERBLASTER.  */
	
			/*
			 * EF_BLASTER | EF_TRACKER is a special case for
			 * EF_BLASTER2... Cheese!
			 */
			else if (effects & EF_BLASTER) {
				/* PGM */
				if (effects & EF_TRACKER) {	/* lame... problematic? */
					CL_BlasterTrail2(cent->lerp_origin, ent.origin);
					V_AddLight(ent.origin, 200, 0, 1, 0);
				} else {
#ifdef QMAX
					if (cl_blaster_color->value == 1) {
						/* give the bolt a red particle glow */
						CL_BlasterTrailColor(cent->lerp_origin, ent.origin, 235, 50, 50,
						                     1, 0, 0);
						V_AddLight(ent.origin, 200, 1, 0, 0);
					} else if (cl_blaster_color->value == 2) {
						/* give the bolt a green particle glow */
						CL_BlasterTrailColor(cent->lerp_origin, ent.origin, 50, 235, 50,
						                     0, 1, 0);
						V_AddLight(ent.origin, 200, 0, 1, 0);
					} else if (cl_blaster_color->value == 3) {
						/* give the bolt a blue particle glow */
						CL_BlasterTrailColor(cent->lerp_origin, ent.origin, 50, 50, 235,
						                     0, 0, 1);
						V_AddLight (ent.origin, 200, 0, 0, 1);
					} else
#endif
					CL_BlasterTrail(cent->lerp_origin, ent.origin);
					V_AddLight(ent.origin, 200, 1, 1, 0);
				}
			/* PGM */
			} else if (effects & EF_HYPERBLASTER) {
				if (effects & EF_TRACKER)	/* PGM	overloaded for blaster2. */
					V_AddLight(ent.origin, 200, 0, 1, 0);	/* PGM */
				else	/* PGM  */
					V_AddLight(ent.origin, 200, 1, 1, 0);
			} else if (effects & EF_GIB) {
				CL_DiminishingTrail(cent->lerp_origin, ent.origin, cent, effects);
			} else if (effects & EF_GRENADE) {
				CL_DiminishingTrail(cent->lerp_origin, ent.origin, cent, effects);
			} else if (effects & EF_FLIES) {
				CL_FlyEffect(cent, ent.origin);
			} else if (effects & EF_BFG) {
				static int	bfg_lightramp[6] = {300, 400, 600, 300, 150, 75};

				if (effects & EF_ANIM_ALLFAST) {
					CL_BfgParticles(&ent);
					i = 200;
				} else {
					i = bfg_lightramp[s1->frame];
				}
				V_AddLight(ent.origin, i, 0, 1, 0);
			}
			/* RAFAEL */
			else if (effects & EF_TRAP) {
				ent.origin[2] += 32;
				CL_TrapParticles(&ent);
				i = (rand() % 100) + 100;
				V_AddLight(ent.origin, i, 1, 0.8, 0.1);
			} else if (effects & EF_FLAG1) {
				/* order to remove the particles */
#ifdef QMAX
				CL_FlagTrail(cent->lerp_origin, ent.origin, true, false);
#else
				CL_FlagTrail(cent->lerp_origin, ent.origin, 242);
#endif
				V_AddLight(ent.origin, 225, 1, 0.1, 0.1);
			} else if (effects & EF_FLAG2) {
				/* order to remove the particles */
#ifdef QMAX
				CL_FlagTrail(cent->lerp_origin, ent.origin, false, false);
#else
				CL_FlagTrail(cent->lerp_origin, ent.origin, 115);
#endif
				V_AddLight(ent.origin, 225, 0.1, 0.1, 1);
			}
			/* ROGUE */
			else if (effects & EF_TAGTRAIL) {
				CL_TagTrail(cent->lerp_origin, ent.origin, 220);
				V_AddLight(ent.origin, 225, 1.0, 1.0, 0.0);
			} else if (effects & EF_TRACKERTRAIL) {
				if (effects & EF_TRACKER) {
					float		intensity;

					intensity = 50 + (500 * (sin(cl.time / 500.0) + 1.0));
					/* FIXME - check out this effect in rendition */
					if (vidref_val == VIDREF_GL)
						V_AddLight(ent.origin, intensity, -1.0, -1.0, -1.0);
					else
						V_AddLight(ent.origin, -1.0 * intensity, 1.0, 1.0, 1.0);
				} else {
					CL_Tracker_Shell(cent->lerp_origin);
					V_AddLight(ent.origin, 155, -1.0, -1.0, -1.0);
				}
			} else if (effects & EF_TRACKER) {
#ifdef QMAX
				CL_TrackerTrail(cent->lerp_origin, ent.origin);
#else
				CL_TrackerTrail(cent->lerp_origin, ent.origin, 0);
#endif
				/* FIXME - check out this effect in rendition */
				if (vidref_val == VIDREF_GL)
					V_AddLight(ent.origin, 200, -1, -1, -1);
				else
					V_AddLight(ent.origin, -200, 1, 1, 1);
			}
			/* ROGUE */
			/* RAFAEL */
			else if (effects & EF_GREENGIB) {
				CL_DiminishingTrail(cent->lerp_origin, ent.origin, cent, effects);
			}
			/* RAFAEL */
			else if (effects & EF_IONRIPPER) {
				CL_IonripperTrail(cent->lerp_origin, ent.origin);
				V_AddLight(ent.origin, 100, 1, 0.5, 0.5);
			}
			/* RAFAEL */
			else if (effects & EF_BLUEHYPERBLASTER) {
				V_AddLight(ent.origin, 200, 0, 0, 1);
			}
			/* RAFAEL */
			else if (effects & EF_PLASMA) {
				if (effects & EF_ANIM_ALLFAST) {
					CL_BlasterTrail(cent->lerp_origin, ent.origin);
				}
#ifdef QMAX
				if (!cl_hyperblaster_color->value)
#endif
					V_AddLight(ent.origin, 130, 1, 0.5, 0.5);
			}
		}
		VectorCopy(ent.origin, cent->lerp_origin);
	}
}

/*
 * ============== CL_AddViewWeapon ==============
 */
void
CL_AddViewWeapon(player_state_t * ps, player_state_t * ops)
{
	entity_t	gun;	/* view model */
	int		i;
	int		pnum;	/* c14 add shell to view model */
	entity_state_t *s1;	/* c14 add shell to view model */
	extern cvar_t  *cl_gunalpha;
	clientGun = NULL;



	/* dont draw if outside body... */
	if (cl_3dcam->value && ((cl_predict->value) && !(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION)) && !(cl.attractloop && !(cl.cinematictime > 0 && cls.realtime - cl.cinematictime > 1000)))
		return;

	/* allow the gun to be completely removed */
	if (!cl_gun->value)
		return;

	/* don't draw gun if in wide angle view */
	if (ps->fov > 110)
		return;

	memset(&gun, 0, sizeof(gun));

	if (gun_model)
		gun.model = gun_model;	/* development tool */
	else
		gun.model = cl.model_draw[ps->gunindex];
	if (!gun.model)
		return;

	/* set up gun position */
	for (i = 0; i < 3; i++) {
		gun.origin[i] = cl.refdef.vieworg[i] + ops->gunoffset[i]
		    + cl.lerpfrac * (ps->gunoffset[i] - ops->gunoffset[i]);
		gun.angles[i] = cl.refdef.viewangles[i] + LerpAngle(ops->gunangles[i],
		    ps->gunangles[i], cl.lerpfrac);
	}

	if (gun_frame) {
		gun.frame = gun_frame;		/* development tool */
		gun.oldframe = gun_frame;	/* development tool */
	} else {
		gun.frame = ps->gunframe;
		if (gun.frame == 0)
			gun.oldframe = 0;	/* just changed weapons, don't lerp from old */
		else
			gun.oldframe = ops->gunframe;
	}

	gun.scale = 1;
	gun.flags = RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL;

	if (cl_gunalpha->value < 1) {
		gun.flags |= RF_TRANSLUCENT;
		gun.alpha = cl_gunalpha->value;
	}
	gun.backlerp = 1.0 - cl.lerpfrac;
	VectorCopy(gun.origin, gun.oldorigin);	/* don't lerp at all */
	V_AddEntity(&gun);

	if (weap_shell->value) {
		/* c14 add shell to view model */
		for (pnum = 0; pnum < cl.frame.num_entities; pnum++) {

			s1 = &cl_parse_entities[(cl.frame.parse_entities + pnum) & (MAX_PARSE_ENTITIES - 1)];

			if (s1->number != cl.playernum + 1)
				continue;

			if (s1->effects & (EF_COLOR_SHELL | EF_QUAD | EF_PENT | EF_DOUBLE | EF_HALF_DAMAGE | EF_POWERSCREEN)) {
				gun.flags |= (RF_TRANSLUCENT | s1->renderfx);
				if (s1->effects & EF_POWERSCREEN)
					gun.flags |= RF_SHELL_GREEN;
				if (s1->effects & EF_PENT)
					gun.flags |= RF_SHELL_RED;
				/* V_AddLight(gun.origin, 200, 1.0, 0.5, 0.5); */
				if (s1->effects & EF_QUAD)
					gun.flags |= RF_SHELL_BLUE;
				/* V_AddLight(gun.origin, 200, 0.5, 0.5, 1.0); */
				if (s1->effects & EF_DOUBLE)
					gun.flags |= RF_SHELL_DOUBLE;
				/* V_AddLight(gun.origin, 200, 0.9, 0.7, 0.0); */
				if (s1->effects & EF_HALF_DAMAGE)
					gun.flags |= RF_SHELL_HALF_DAM;
				/* V_AddLight(gun.origin, 200, 0.56, 0.59, 0.45); */
				gun.alpha = 0.1;
				V_AddEntity(&gun);
			}
		}
#if 0
		/* add shells for viewweaps (all of em!) */
		{
			int	oldeffects = gun.flags, pnum;
			entity_state_t *s1;

			for (pnum = 0; pnum < cl.frame.num_entities; pnum++)
				if ((s1 = &cl_parse_entities[(cl.frame.parse_entities + pnum) & (MAX_PARSE_ENTITIES - 1)])->number == cl.playernum + 1) {
					int		effects = s1->renderfx;

					if (effects & (RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_GREEN) || s1->effects & (EF_PENT | EF_QUAD)) {
						if (effects & RF_SHELL_RED)
							gun.flags |= RF_SHELL_RED;
						if (effects & RF_SHELL_BLUE)
							gun.flags |= RF_SHELL_BLUE;
						if (effects & RF_SHELL_GREEN)
							gun.flags |= RF_SHELL_GREEN;

						gun.flags |= RF_TRANSLUCENT;
						gun.alpha = 0.30;

						if (s1->effects & EF_COLOR_SHELL && gun.flags & (RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_GREEN)) {
							gun.skin = re.RegisterSkin("gfx/shell.pcx");
							V_AddEntity(&gun);
						}
						if (s1->effects & EF_PENT) {
							gun.skin = re.RegisterSkin("gfx/shell_pent.pcx");
							gun.flags = oldeffects | RF_TRANSLUCENT | RF_SHELL_RED;
							V_AddEntity(&gun);
						}
						if (s1->effects & EF_QUAD) {
							gun.skin = re.RegisterSkin("gfx/shell_quad.pcx");
							gun.flags = oldeffects | RF_TRANSLUCENT | RF_SHELL_BLUE;
							V_AddEntity(&gun);
						}
					}
				}
			gun.flags = oldeffects;
		}
#endif
	}
	clientGun = gun.model;
	
}

void		CalcViewerCamTrans(float dist);
/* Knightmare- backup of client angles */
vec3_t		old_viewangles;
void
SetUpCamera(void)
{
	if (cl_3dcam->value && ((cl_predict->value) && !(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION)) && !(cl.attractloop && !(cl.cinematictime > 0 && cls.realtime - cl.cinematictime > 1000))) {
		vec3_t		end, oldorg, _3dcamposition, _3dcamforward = {0};
		float		dist_up, dist_back, angle;

		if (cl_3dcam_dist->value < 0)
			Cvar_SetValue("cl_3dcam_dist", 0);

		/* Knightmare- backup old viewangles */
		VectorCopy(cl.refdef.viewangles, old_viewangles);

		/* trig stuff */
		angle = M_PI * cl_3dcam_angle->value / 180.0;
		dist_up = cl_3dcam_dist->value * sin(angle);
		dist_back = cl_3dcam_dist->value * cos(angle);
		/* finish polar vector */

		VectorCopy(cl.refdef.vieworg, oldorg);
		if (cl_3dcam_chase->value) {
			VectorMA(cl.refdef.vieworg, -dist_back, cl.v_forward, end);
			VectorMA(end, dist_up, cl.v_up, end);

			/*
			 * move back so looking straight down want make us
			 * look towards ourself
			 */
			{
				vec3_t		temp, temp2;

				vectoangles2(cl.v_forward, temp);
				temp[PITCH] = 0;
				temp[ROLL] = 0;
				AngleVectors(temp, temp2, NULL, NULL);
				VectorMA(end, -(dist_back / 1.8), temp2, end);
			}
			ClipCam(cl.refdef.vieworg, end, _3dcamposition);
		} else {
			vec3_t		temp, viewForward, viewUp;

			vectoangles2(cl.v_forward, temp);
			temp[PITCH] = 0;
			temp[ROLL] = 0;
			AngleVectors(temp, viewForward, NULL, viewUp);

			VectorScale(viewForward, dist_up * 0.5, _3dcamforward);

			VectorMA(cl.refdef.vieworg, -dist_back, viewForward, end);
			VectorMA(end, dist_up, viewUp, end);

			ClipCam(cl.refdef.vieworg, end, _3dcamposition);
		}


		VectorSubtract(_3dcamposition, oldorg, end);

		CalcViewerCamTrans(VectorLength(end));

		if (!cl_3dcam_chase->value) {
			vec3_t		newDir[2], newPos;

			VectorSubtract(cl.predicted_origin, _3dcamposition, newDir[0]);
			VectorNormalize(newDir[0]);
			vectoangles2(newDir[0], newDir[1]);
			VectorCopy(newDir[1], cl.refdef.viewangles);

			VectorAdd(_3dcamforward, cl.refdef.vieworg, newPos);
			ClipCam(cl.refdef.vieworg, newPos, cl.refdef.vieworg);
		} else {	/* now aim at where ever client is... */
			vec3_t		newDir, dir;

			if (cl_3dcam_adjust->value) {
				VectorMA(cl.refdef.vieworg, 8000, cl.v_forward, dir);
				ClipCam(cl.refdef.vieworg, dir, newDir);

				VectorSubtract(newDir, _3dcamposition, dir);
				VectorNormalize(dir);
				vectoangles2(dir, newDir);

				AngleVectors(newDir, cl.v_forward, cl.v_right, cl.v_up);
				VectorCopy(newDir, cl.refdef.viewangles);
			}
			VectorCopy(_3dcamposition, cl.refdef.vieworg);
		}
	}
}
void
CL_CalcViewValues(void)
{
	int		i;
	float		lerp, backlerp;
	centity_t      *ent;
	frame_t        *oldframe;
	player_state_t *ps, *ops;

	/* find the previous frame to interpolate from */
	ps = &cl.frame.playerstate;
	i = (cl.frame.serverframe - 1) & UPDATE_MASK;
	oldframe = &cl.frames[i];
	if (oldframe->serverframe != cl.frame.serverframe - 1 || !oldframe->valid)
		oldframe = &cl.frame;	/* previous frame was dropped or invalid */
	ops = &oldframe->playerstate;

	/* see if the player entity was teleported this frame */
	if (fabs(ops->pmove.origin[0] - ps->pmove.origin[0]) > 256 * 8
	    || abs(ops->pmove.origin[1] - ps->pmove.origin[1]) > 256 * 8
	    || abs(ops->pmove.origin[2] - ps->pmove.origin[2]) > 256 * 8)
		ops = ps;	/* don't interpolate */

	ent = &cl_entities[cl.playernum + 1];
	lerp = cl.lerpfrac;

	/* calculate the origin */
	if ((cl_predict->value) && !(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION)) { /* use predicted values */
		unsigned	delta;

		backlerp = 1.0 - lerp;
		for (i = 0; i < 3; i++) {
			cl.refdef.vieworg[i] = cl.predicted_origin[i] + ops->viewoffset[i]
			    + cl.lerpfrac * (ps->viewoffset[i] - ops->viewoffset[i])
			    - backlerp * cl.prediction_error[i];

			/* this smooths out platform riding */
			cl.predicted_origin[i] -= backlerp * cl.prediction_error[i];
		}

		/* smooth out stair climbing */
		delta = cls.realtime - cl.predicted_step_time;
		if (delta < 100) {
			cl.refdef.vieworg[2] -= cl.predicted_step * (100 - delta) * 0.01;
			cl.predicted_origin[2] -= cl.predicted_step * (100 - delta) * 0.01;
		}
	} else {		/* just use interpolated values */
		for (i = 0; i < 3; i++)
			cl.refdef.vieworg[i] = ops->pmove.origin[i] * 0.125 + ops->viewoffset[i]
			    + lerp * (ps->pmove.origin[i] * 0.125 + ps->viewoffset[i]
			    - (ops->pmove.origin[i] * 0.125 + ops->viewoffset[i]));
	}

	/*
	 * if not running a demo or on a locked frame, add the local angle
	 * movement
	 */
	if (cl.frame.playerstate.pmove.pm_type < PM_DEAD) {	/* use predicted values */
		for (i = 0; i < 3; i++)
			cl.refdef.viewangles[i] = cl.predicted_angles[i];
	} else {		/* just use interpolated values */
		for (i = 0; i < 3; i++)
			cl.refdef.viewangles[i] = LerpAngle(ops->viewangles[i], ps->viewangles[i], lerp);
	}

	for (i = 0; i < 3; i++)
		cl.refdef.viewangles[i] += LerpAngle(ops->kick_angles[i], ps->kick_angles[i], lerp);

	AngleVectors(cl.refdef.viewangles, cl.v_forward, cl.v_right, cl.v_up);

	/* interpolate field of view */
	cl.refdef.fov_x = ops->fov + lerp * (ps->fov - ops->fov);

	/* don't interpolate blend color */
	for (i = 0; i < 4; i++)
		cl.refdef.blend[i] = ps->blend[i];

	/* add the weapon */
	CL_AddViewWeapon(ps, ops);

	/*
	 * if (cl_3dcam->modified) { if (modType("dday") && cl_3dcam->value)
	 * Cvar_SetValue("cl_3dcam", 0); }
	 */
	SetUpCamera();
}

/*
 * =============== CL_AddEntities
 *
 * Emits all entities, particles, and lights to the refresh ===============
 */
void
CL_AddEntities(void)
{
	if (cls.state != ca_active)
		return;

	if (cl.time > cl.frame.servertime) {
		if (cl_showclamp->value)
			Com_Printf("high clamp %i\n", cl.time - cl.frame.servertime);
		cl.time = cl.frame.servertime;
		cl.lerpfrac = 1.0;
	} else if (cl.time < cl.frame.servertime - 100) {
		if (cl_showclamp->value)
			Com_Printf("low clamp %i\n", cl.frame.servertime - 100 - cl.time);
		cl.time = cl.frame.servertime - 100;
		cl.lerpfrac = 0;
	} else
		cl.lerpfrac = 1.0 - (cl.frame.servertime - cl.time) * 0.01;

	if (cl_timedemo->value)
		cl.lerpfrac = 1.0;

	/* CL_AddPacketEntities (&cl.frame); */
	/* CL_AddTEnts (); */
	/* CL_AddParticles (); */
	/* CL_AddDLights (); */
	/* CL_AddLightStyles (); */

	CL_CalcViewValues();

	/*
	 * PMM - moved this here so the heat beam has the right values for
	 * the vieworg, and can lock the beam to the gun
	 */
	CL_AddPacketEntities(&cl.frame);
	CL_AddViewLocs();
	CL_AddTEnts();
	CL_AddParticles();
	CL_AddDLights();
#ifdef QMAX
	if (cl.refdef.rdflags & RDF_IRGOGGLES) {
		CL_ClearDlights();
		CL_AddHeatDLights();
	}
#endif
	CL_AddLightStyles();
}

/*
 * =============== CL_GetEntitySoundOrigin
 *
 * Called to get the sound spatialization origin ===============
 */
void
CL_GetEntitySoundOrigin(int ent, vec3_t org)
{
	centity_t      *old;

	if (ent < 0 || ent >= MAX_EDICTS)
		Com_Error(ERR_DROP, "CL_GetEntitySoundOrigin: bad ent");
	old = &cl_entities[ent];
	VectorCopy(old->lerp_origin, org);

	/* FIXME: bmodel issues... */
}
