text: /*
  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;
  
  
  /*
  =========================================================================
  
  FRAME PARSING
  
  =========================================================================
  */
  
  #if 0
  
  typedef struct
  {
  	int		modelindex;
  	int		num; // entity number
  	int		effects;
  	vec3_t	origin;
  	vec3_t	oldorigin;
  	vec3_t	angles;
  	qboolean present;
  } projectile_t;
  
  #define	MAX_PROJECTILES	512 //Knightmare- was 64
  projectile_t	cl_projectiles[MAX_PROJECTILES];
  
  void CL_ClearProjectiles (void)
  {
  	int i;
  
  	for (i = 0; i < MAX_PROJECTILES; i++) {
  //		if (cl_projectiles[i].present)
  //			Com_DPrintf("PROJ: %d CLEARED\n", cl_projectiles[i].num);
  		cl_projectiles[i].present = false;
  	}
  }
  
  /*
  =====================
  CL_ParseProjectiles
  
  Flechettes are passed as efficient temporary entities
  =====================
  */
  void CL_ParseProjectiles (void)
  {
  	int		i, c, j;
  	byte	bits[8];
  	byte	b;
  	projectile_t	pr;
  	int lastempty = -1;
  	qboolean old = false;
  
  	c = MSG_ReadByte (&net_message);
  	for (i=0 ; i<c ; i++)
  	{
  		bits[0] = MSG_ReadByte (&net_message);
  		bits[1] = MSG_ReadByte (&net_message);
  		bits[2] = MSG_ReadByte (&net_message);
  		bits[3] = MSG_ReadByte (&net_message);
  		bits[4] = MSG_ReadByte (&net_message);
  		pr.origin[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096;
  		pr.origin[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096;
  		pr.origin[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096;
  		VectorCopy(pr.origin, pr.oldorigin);
  
  		if (bits[4] & 64)
  			pr.effects = EF_BLASTER;
  		else
  			pr.effects = 0;
  
  		if (bits[4] & 128) {
  			old = true;
  			bits[0] = MSG_ReadByte (&net_message);
  			bits[1] = MSG_ReadByte (&net_message);
  			bits[2] = MSG_ReadByte (&net_message);
  			bits[3] = MSG_ReadByte (&net_message);
  			bits[4] = MSG_ReadByte (&net_message);
  			pr.oldorigin[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096;
  			pr.oldorigin[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096;
  			pr.oldorigin[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096;
  		}
  
  		bits[0] = MSG_ReadByte (&net_message);
  		bits[1] = MSG_ReadByte (&net_message);
  		bits[2] = MSG_ReadByte (&net_message);
  
  		pr.angles[0] = 360*bits[0]/256;
  		pr.angles[1] = 360*bits[1]/256;
  		pr.modelindex = bits[2];
  
  		b = MSG_ReadByte (&net_message);
  		pr.num = (b & 0x7f);
  		if (b & 128) // extra entity number byte
  			pr.num |= (MSG_ReadByte (&net_message) << 7);
  
  		pr.present = true;
  
  		// find if this projectile already exists from previous frame 
  		for (j = 0; j < MAX_PROJECTILES; j++) {
  			if (cl_projectiles[j].modelindex) {
  				if (cl_projectiles[j].num == pr.num) {
  					// already present, set up oldorigin for interpolation
  					if (!old)
  						VectorCopy(cl_projectiles[j].origin, pr.oldorigin);
  					cl_projectiles[j] = pr;
  					break;
  				}
  			} else
  				lastempty = j;
  		}
  
  		// not present previous frame, add it
  		if (j == MAX_PROJECTILES) {
  			if (lastempty != -1) {
  				cl_projectiles[lastempty] = pr;
  			}
  		}
  	}
  }
  
  /*
  =============
  CL_LinkProjectiles
  
  =============
  */
  void CL_AddProjectiles (void)
  {
  	int		i, j;
  	projectile_t	*pr;
  	entity_t		ent;
  
  	memset (&ent, 0, sizeof(ent));
  
  	for (i=0, pr=cl_projectiles ; i < MAX_PROJECTILES ; i++, pr++)
  	{
  		// grab an entity to fill in
  		if (pr->modelindex < 1)
  			continue;
  		if (!pr->present) {
  			pr->modelindex = 0;
  			continue; // not present this frame (it was in the previous frame)
  		}
  
  		ent.model = cl.model_draw[pr->modelindex];
  
  		// interpolate origin
  		for (j=0 ; j<3 ; j++)
  		{
  			ent.origin[j] = ent.oldorigin[j] = pr->oldorigin[j] + cl.lerpfrac * 
  				(pr->origin[j] - pr->oldorigin[j]);
  
  		}
  
  		if (pr->effects & EF_BLASTER)
  		CL_BlasterTrail (pr->oldorigin, ent.origin, 255, 150, 50, 0, -90, -30);
  		V_AddLight (pr->origin, 200, 1, 1, 0);
  
  		VectorCopy (pr->angles, ent.angles);
  		V_AddEntity (&ent);
  	}
  }
  #endif
  
  /*
  =================
  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;
  
  	// Knightmare- read deltas the old way if playing old demos or
  	// connected to server using old protocol
  	if ( LegacyProtocol() )
  	{
  		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);
  		// end old CL_ParseDelta code
  	}	
  	else //new CL_ParseDelta code
  	{
  	#ifndef NEW_ENTITY_STATE_MEMBERS
  		int ignore;	// holder for messages to be ignored
  	#endif
  		// Knightmare- 12/23/2001- read model indices as shorts 
  		if (bits & U_MODEL)
  			to->modelindex = MSG_ReadShort (&net_message);
  		if (bits & U_MODEL2)
  			to->modelindex2 = MSG_ReadShort (&net_message);
  		if (bits & U_MODEL3)
  			to->modelindex3 = MSG_ReadShort (&net_message);
  		if (bits & U_MODEL4)
  			to->modelindex4 = MSG_ReadShort (&net_message);
  
  		//Knightmare- 1/18/2002- extra model indices
  #ifdef NEW_ENTITY_STATE_MEMBERS
  		if (bits & U_MODEL5)
  			to->modelindex5 = MSG_ReadShort (&net_message);
  		if (bits & U_MODEL6)
  			to->modelindex6 = MSG_ReadShort (&net_message);
  		if (bits & U_MODEL7_8) {
  			to->modelindex7 = MSG_ReadShort (&net_message);
  			to->modelindex8 = MSG_ReadShort (&net_message);
  		}
  #else // we need to read and ignore this for eraser client compatibility
  		if (bits & U_MODEL5)
  			ignore = MSG_ReadShort (&net_message);
  		if (bits & U_MODEL6)
  			ignore = MSG_ReadShort (&net_message);
  		if (bits & U_MODEL7_8) {
  			ignore = MSG_ReadShort (&net_message);
  			ignore = MSG_ReadShort (&net_message);
  		}
  #endif
  		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);
  
  #ifdef NEW_ENTITY_STATE_MEMBERS
  		//Knightmare- 5/11/2002- added alpha
  		if (bits & U_ALPHA)
  			to->alpha = (float)(MSG_ReadByte (&net_message) / 255.0);
  #else // we need to read and ignore this for eraser client compatibility
  		if (bits & U_ALPHA)
  			ignore = (float)(MSG_ReadByte (&net_message) / 255.0);
  #endif
  		//Knightmare 12/23/2001- read sound indices as shorts
  		if (bits & U_SOUND)
  			to->sound = MSG_ReadShort (&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);
  
  	}	//end new CL_ParseDelta code
  }
  
  /*
  ==================
  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
  #ifdef NEW_ENTITY_STATE_MEMBERS
  		//Knightmare- 1/18/2002- extra model indices
  		|| state->modelindex5 != ent->current.modelindex5
  		|| state->modelindex6 != ent->current.modelindex6
  		|| state->modelindex7 != ent->current.modelindex7
  		|| state->modelindex8 != ent->current.modelindex8
  		//end Knightmare
  #endif
  		|| 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;
  	int			bits;
  	entity_state_t	*oldstate;
  	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));
  
  	// Knightmare- read player state info the old way if playing old demos or
  	// connected to server using old protocol
  	if ( LegacyProtocol() )
  	{
  		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) // FIXME- map size
  		{
  			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)*DIV255;
  			state->blend[1] = MSG_ReadByte (&net_message)*DIV255;
  			state->blend[2] = MSG_ReadByte (&net_message)*DIV255;
  			state->blend[3] = MSG_ReadByte (&net_message)*DIV255;
  		}
  
  		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 < OLD_MAX_STATS; i++) //Knightmare- use old max_stats
  			if (statbits & (1<<i) )
  				state->stats[i] = MSG_ReadShort(&net_message);
  	}	//end old CL_ParsePlayerstate code
  	else //new CL_ParsePlayerstate code
  	{
  		// Knightmare 4/5/2002- read as long
  		flags = MSG_ReadLong (&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)
  		{
  #ifdef LARGE_MAP_SIZE
  			state->pmove.origin[0] = MSG_ReadPMCoordNew (&net_message);
  			state->pmove.origin[1] = MSG_ReadPMCoordNew (&net_message);
  			state->pmove.origin[2] = MSG_ReadPMCoordNew (&net_message);
  #else
  			state->pmove.origin[0] = MSG_ReadShort (&net_message);
  			state->pmove.origin[1] = MSG_ReadShort (&net_message);
  			state->pmove.origin[2] = MSG_ReadShort (&net_message);
  #endif
  		}
  
  		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;
  		}
  
  		//Knightmare 4/5/2002- read as short
  		if (flags & PS_WEAPONINDEX)
  			state->gunindex = MSG_ReadShort (&net_message);
  
  	#ifdef NEW_PLAYER_STATE_MEMBERS	// Knightmare- gunindex2 support
  		if (flags & PS_WEAPONINDEX2)
  			state->gunindex2 = MSG_ReadShort (&net_message);
  	#endif
  
  	// Knightmare- gunframe2 support
  	#ifdef NEW_PLAYER_STATE_MEMBERS
  		if ((flags & PS_WEAPONFRAME) || (flags & PS_WEAPONFRAME2))
  		{
  			if (flags & PS_WEAPONFRAME)
  				state->gunframe = MSG_ReadByte (&net_message);
  			if (flags & PS_WEAPONFRAME2)
  				state->gunframe2 = 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;
  		}
  	#else
  		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;
  		}
  	#endif
  
  	#ifdef NEW_PLAYER_STATE_MEMBERS
  		if (flags & PS_WEAPONSKIN)	// Knightmare- gunskin support
  			state->gunskin = MSG_ReadShort (&net_message);
  
  		if (flags & PS_WEAPONSKIN2)
  			state->gunskin2 = MSG_ReadShort (&net_message);
  
  		// server-side speed control!
  		if (flags & PS_MAXSPEED)
  			state->maxspeed = MSG_ReadShort (&net_message);
  
  		if (flags & PS_DUCKSPEED)
  			state->duckspeed = MSG_ReadShort (&net_message);
  
  		if (flags & PS_WATERSPEED)
  			state->waterspeed = MSG_ReadShort (&net_message);
  
  		if (flags & PS_ACCEL)
  			state->accel = MSG_ReadShort (&net_message);
  
  		if (flags & PS_STOPSPEED)
  			state->stopspeed = MSG_ReadShort (&net_message);
  	#endif	// end Knightmare
  
  		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);
  	} //end new CL_ParsePlayerstate code
  }
  
  
  /*
  ==================
  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);
  	}
  }
  
  
  // Knightmare- var for saving speed controls
  player_state_t *clientstate;
  
  /*
  ================
  CL_ParseFrame
  ================
  */
  void CL_ParseFrame (void)
  {
  	int			cmd;
  	int			len;
  	frame_t		*old;
  
  	memset (&cl.frame, 0, sizeof(cl.frame));
  
  #if 0
  	CL_ClearProjectiles(); // clear projectiles for new frame
  #endif
  
  	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);
  
  	// Knightmare- set pointer to player state for movement code
  	clientstate = &cl.frame.playerstate;
  
  	// 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);
  
  #if 0
  	if (cmd == svc_packetentities2)
  		CL_ParseProjectiles();
  #endif
  
  	// 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;
  
  	// Knightmare- BIG UGLY HACK for old connected to server using old protocol
  	// Changed config strings require different parsing
  	if ( LegacyProtocol() )
  		n = OLD_CS_PLAYERSKINS + ent->number - 1;
  	else
  		n = CS_PLAYERSKINS + ent->number - 1;
  
  	if (cl.configstrings[n][0])
  	{
  		p = strchr(cl.configstrings[n], '\\');
  		if (p)
  		{
  			p += 1;
  			strcpy(model, p);
  			p = strchr(model, '/');
  			if (p)
  				*p = 0;
  		}
  	}
  	// if we can't figure it out, they're male
  	if (!model[0])
  		strcpy(model, "male");
  
  	Com_sprintf (buffer, sizeof(buffer), "players/%s/%s", model, base+1);
  	mdl = R_RegisterModel(buffer);
  	if (!mdl) {
  		// not found, try default weapon model
  		Com_sprintf (buffer, sizeof(buffer), "players/%s/weapon.md2", model);
  		mdl = R_RegisterModel(buffer);
  		if (!mdl) {
  			// no, revert to the male model
  			Com_sprintf (buffer, sizeof(buffer), "players/%s/%s", "male", base+1);
  			mdl = R_RegisterModel(buffer);
  			if (!mdl) {
  				// last try, default male weapon.md2
  				Com_sprintf (buffer, sizeof(buffer), "players/male/weapon.md2");
  				mdl = R_RegisterModel(buffer);
  			}
  		} 
  	}
  
  	return mdl;
  }
  
  // Knightmare- save off current player weapon model for player config menu
  extern	char	*currentweaponmodel;
  extern	char	cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH];
  
  /*
  ===============
  CL_AddPacketEntities
  
  ===============
  */
  void CL_AddPacketEntities (frame_t *frame)
  {
  	entity_t			ent;
  	entity_state_t		*s1;
  	float				autorotate;
  	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));
  
  	// Knightmare added
  	VectorCopy( vec3_origin, clientOrg);
  
  	// Knightmare- reset current weapon model
  	currentweaponmodel = NULL;
  
  	for (pnum = 0 ; pnum<frame->num_entities ; pnum++)
  	{
  		qboolean isclientviewer = false;
  		qboolean drawEnt = true; //Knightmare added
  
  		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.0F;
  		// 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;
  
  		// Knightmare- ents that cast light don't give off shadows
  		if (effects & (EF_BLASTER|EF_HYPERBLASTER|EF_BLUEHYPERBLASTER|EF_TRACKER|EF_ROCKET|EF_PLASMA|EF_IONRIPPER))
  			renderfx |= RF_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 == MAX_MODELS-1 //was 255
  				//Knightmare- GROSS HACK for old demos, use modelindex 255
  				|| ( LegacyProtocol() && s1->modelindex == OLD_MAX_MODELS-1 ) )
  			{	// 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 = R_RegisterSkin ("players/male/disguise.pcx");
  						ent.model = R_RegisterModel ("players/male/tris.md2");
  					}
  					else if(!strncmp((char *)ent.skin, "players/female", 14))
  					{
  						ent.skin = R_RegisterSkin ("players/female/disguise.pcx");
  						ent.model = R_RegisterModel ("players/female/tris.md2");
  					}
  					else if(!strncmp((char *)ent.skin, "players/cyborg", 14))
  					{
  						ent.skin = R_RegisterSkin ("players/cyborg/disguise.pcx");
  						ent.model = R_RegisterModel ("players/cyborg/tris.md2");
  					}
  				}
  //PGM
  //============
  			}
  			else
  			{
  				ent.skinnum = s1->skinnum;
  				ent.skin = NULL;
  				ent.model = cl.model_draw[s1->modelindex];
  			}
  		}
  		
  
  		//Knightmare added
  		//**** MODEL / EFFECT SWAPPING ETC *** - per gametype...
  		if (ent.model)
  		{
  			if (!Q_strcasecmp((char *)ent.model, "models/objects/laser/tris.md2")
  				&& !(effects & EF_BLASTER))
  			{	// give the bolt an orange particle glow
  				CL_HyperBlasterEffect (cent->lerp_origin, ent.origin, s1->angles,
  					255, 150, 50, 0, -90, -30, 10, 2);
  				drawEnt = false;
  			}
  
  
  			if (!Q_strcasecmp((char *)ent.model, "sprites/freonparticle.sp2"))				
  			{	//freon particle.
  				CL_FreonEffect (cent->lerp_origin, ent.origin, s1->angles,
  					255, 150, 50, 0, -90, -30, 10, 2);
  				drawEnt = false;
  			}
  
  
  
  			if ( (!Q_strcasecmp((char *)ent.model, "models/proj/laser2/tris.md2")
  				|| !Q_strcasecmp((char *)ent.model, "models/objects/laser2/tris.md2")
  				|| !Q_strcasecmp((char *)ent.model, "models/objects/glaser/tris.md2") )
  				&& !(effects & EF_BLASTER))
  			{	// give the bolt a green particle glow
  				CL_HyperBlasterEffect (cent->lerp_origin, ent.origin, s1->angles,
  					50, 235, 50, -10, 0, -10, 10, 2);
  				drawEnt = false;
  			}
  			if (!Q_strcasecmp((char *)ent.model, "models/objects/blaser/tris.md2")
  				&& !(effects & EF_BLASTER))
  			{	// give the bolt a blue particle glow
  				CL_HyperBlasterEffect (cent->lerp_origin, ent.origin, s1->angles,
  					50, 50, 235, 0, -10, 0, -10, 2);
  				drawEnt = false;
  			}
  			if (!Q_strcasecmp((char *)ent.model, "models/objects/rlaser/tris.md2")
  				&& !(effects & EF_BLASTER))
  			{	// give the bolt a red particle glow
  				CL_HyperBlasterEffect (cent->lerp_origin, ent.origin, s1->angles,
  					235, 50, 50, 0, -90, -30, -10, 2);
  				drawEnt = false;
  			}
  		}
  
  		// 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;
  		}
  		// 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);
  			}
  		}
  		//AnglesToAxis(ent.angles, ent.axis);
  
  		if (s1->number == cl.playernum+1)
  		{
  			ent.flags |= RF_VIEWERMODEL;	// only draw from mirrors
  			isclientviewer = true;
  
  			// EF_FLAG1|EF_FLAG2 is a special case for EF_FLAG3...  plus de fromage!
  			if (effects & EF_FLAG1) {
  				if (effects & EF_FLAG2)
  					V_AddLight (ent.origin, 255, 0.1, 1.0, 0.1);
  				else
  					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
  
  			// Knightmare- save off current player weapon model for player config menu
  			if (s1->modelindex2 == MAX_MODELS-1
  				|| ( LegacyProtocol() && s1->modelindex2 == OLD_MAX_MODELS-1 ) )
  			{
  				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;
  				currentweaponmodel = cl_weaponmodels[i];
  			}
  
  			if (!cl_3dcam->value)
  				continue;
  		}
  
  		// if set to invisible, skip
  		if (!s1->modelindex)
  			continue;
  
  		if (effects & EF_BFG)
  		{
  			ent.flags |= RF_TRANSLUCENT;
  			ent.alpha = 0.30;
  			//ent.alpha *= 0.5;*/
  		}
  
  		// 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
  
  // Knightmare- read server-assigned alpha, overriding effects flags
  #ifdef NEW_ENTITY_STATE_MEMBERS
  		if (s1->alpha > 0.0F && s1->alpha < 1.0F)
  		{
  			//ent.alpha = s1->alpha;
  			// lerp alpha :p
  			ent.alpha = cent->prev.alpha + cl.lerpfrac * (cent->current.alpha - cent->prev.alpha);
  			ent.flags |= RF_TRANSLUCENT;
  		}
  #endif
  		// Knightmare added for mirroring
  		if (renderfx & RF_MIRRORMODEL)
  			ent.flags |= RF_MIRRORMODEL;
  
  		// if set to invisible, skip
  		if (!s1->modelindex)
  			continue;
  
  		// add to refresh list
  		if (drawEnt) //Knightmare added
  			V_AddEntity (&ent);
  
  
  		// color shells generate a seperate entity for the main model
  		if (effects & EF_COLOR_SHELL && (!isclientviewer || (cl_3dcam->value
  			&& !(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
  			// Knightmare 6/06/2002
  			if (roguepath())
  			{
  				// 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)
  				{			
  					// 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)
  				{
  					// 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);
  		}
  
  		ent.skin = NULL;		// never use a custom skin on others
  		ent.skinnum = 0;
  		ent.flags = 0;
  		ent.alpha = 1.0F;
  
  		// duplicate for linked models
  		if (s1->modelindex2)
  		{
  			if (s1->modelindex2 == MAX_MODELS-1
  				//Knightmare- GROSS HACK for old demos, use modelindex 255
  				|| ( LegacyProtocol() && s1->modelindex2 == OLD_MAX_MODELS-1 ) )
  			{	// 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
  
  			// Knightmare added for Psychospaz's chasecam
  			if (isclientviewer)
  				ent.flags |= RF_VIEWERMODEL;
  
  			// Knightmare added for mirroring
  			if (renderfx & RF_MIRRORMODEL)
  				ent.flags |= RF_MIRRORMODEL;
  
  			V_AddEntity (&ent);
  
  			//PGM - make sure these get reset.
  			ent.flags = 0;
  			ent.alpha = 1.0F;
  			//PGM
  		}
  
  		if (s1->modelindex3)
  		{
  			// Knightmare added for Psychospaz's chasecam
  			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];
  			V_AddEntity (&ent);
  		}
  		if (s1->modelindex4)
  		{
  			// Knightmare added for Psychospaz's chasecam
  			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];
  			V_AddEntity (&ent);
  		}
  #ifdef NEW_ENTITY_STATE_MEMBERS
  	//Knightmare- 1/18/2002- extra model indices
  		if (s1->modelindex5)
  		{
  			// Knightmare added for Psychospaz's chasecam
  			if (isclientviewer)
  				ent.flags |= RF_VIEWERMODEL;
  
  			// Knightmare added for mirroring
  			if (renderfx & RF_MIRRORMODEL)
  				ent.flags |= RF_MIRRORMODEL;
  
  			ent.model = cl.model_draw[s1->modelindex5];
  			V_AddEntity (&ent);
  		}
  		if (s1->modelindex6)
  		{
  			// Knightmare added for Psychospaz's chasecam
  			if (isclientviewer)
  				ent.flags |= RF_VIEWERMODEL;
  
  			// Knightmare added for mirroring
  			if (renderfx & RF_MIRRORMODEL)
  				ent.flags |= RF_MIRRORMODEL;
  
  			ent.model = cl.model_draw[s1->modelindex6];
  			V_AddEntity (&ent);
  		}
  		if (s1->modelindex7)
  		{
  			// Knightmare added for Psychospaz's chasecam
  			if (isclientviewer)
  				ent.flags |= RF_VIEWERMODEL;
  
  			// Knightmare added for mirroring
  			if (renderfx & RF_MIRRORMODEL)
  				ent.flags |= RF_MIRRORMODEL;
  
  			ent.model = cl.model_draw[s1->modelindex7];
  			V_AddEntity (&ent);
  		}
  		if (s1->modelindex8)
  		{
  			// Knightmare added for Psychospaz's chasecam
  			if (isclientviewer)
  				ent.flags |= RF_VIEWERMODEL;
  
  			// Knightmare added for mirroring
  			if (renderfx & RF_MIRRORMODEL)
  				ent.flags |= RF_MIRRORMODEL;
  
  			ent.model = cl.model_draw[s1->modelindex8];
  			V_AddEntity (&ent);
  		}
  	//end Knightmare
  #endif
  		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;
  			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)
  			{
  				if (effects & EF_TRACKER)	// lame... problematic?
  				{
  					CL_BlasterTrail (cent->lerp_origin, ent.origin, 50, 235, 50, -10, 0, -10);
  					V_AddLight (ent.origin, 200, 0.15, 1, 0.15);		
  				}
  				//Knightmare- behold, the power of cheese!!
  				else if (effects & EF_BLUEHYPERBLASTER) // EF_BLUEBLASTER
  				{
  					CL_BlasterTrail (cent->lerp_origin, ent.origin, 50, 50, 235, -10, 0, -10);
  					V_AddLight (ent.origin, 200, 0.15, 0.15, 1);		
  				}
  				//Knightmare- behold, the power of cheese!!
  				else if (effects & EF_IONRIPPER) // EF_REDBLASTER
  				{
  					CL_BlasterTrail (cent->lerp_origin, ent.origin, 235, 50, 50, 0, -90, -30);
  					V_AddLight (ent.origin, 200, 1, 0.15, 0.15);		
  				}
  				else
  				{
  					if ((effects & EF_GREENGIB) && cl_blood->value >= 1) // EF_BLASTER|EF_GREENGIB effect
  						CL_DiminishingTrail (cent->lerp_origin, ent.origin, cent, effects);
  					else
  						CL_BlasterTrail (cent->lerp_origin, ent.origin, 255, 150, 50, 0, -90, -30);
  					V_AddLight (ent.origin, 200, 1, 1, 0.15);
  				}
  
  			//PGM
  			}
  			else if (effects & EF_HYPERBLASTER)
  			{
  				if (effects & EF_TRACKER)						// PGM overloaded for hyperblaster2
  				{
  					V_AddLight (ent.origin, 200, 0.15, 1, 0.15);// PGM
  				}
  				else if (effects & EF_IONRIPPER) // Knightmare- overloaded for EF_REDHYPERBLASTER
  				{
  					V_AddLight (ent.origin, 200, 1, 0.15, 0.15);		
  				}
  				else											// PGM
  				{
  					V_AddLight (ent.origin, 200, 1, 1, 0.15);
  				}
  			}
  			else if (effects & EF_GIB)
  			{
  				if (cl_blood->value >= 1)
  					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)
  			{	// Knightmare 1/3/2002
  				// EF_FLAG1|EF_FLAG2 is a special case for EF_FLAG3...  More cheese!
  				if (effects & EF_FLAG2)
  				{	//Knightmare- Psychospaz's enhanced particle code
  					CL_FlagTrail (cent->lerp_origin, ent.origin, false, true);
  					V_AddLight (ent.origin, 255, 0.1, 1, 0.1);
  				}
  				else
  				{	//Knightmare- Psychospaz's enhanced particle code
  					CL_FlagTrail (cent->lerp_origin, ent.origin, true, false);
  					V_AddLight (ent.origin, 225, 1, 0.1, 0.1);
  				}
  				//end Knightmare
  			}
  			else if (effects & EF_FLAG2)
  			{	//Knightmare- Psychospaz's enhanced particle code
  				CL_FlagTrail (cent->lerp_origin, ent.origin, false, false);
  				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));
  					V_AddLight (ent.origin, 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)
  			{	//Knightmare- this is replaced for Psychospaz's enhanced particle code
  				CL_TrackerTrail (cent->lerp_origin, ent.origin);
  				V_AddLight (ent.origin, 200, -1, -1, -1);
  			}
  //ROGUE
  //======
  			// RAFAEL
  			else if (effects & EF_GREENGIB)
  			{
  				if (cl_blood->value >= 1) // disable blood option
  					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, 255, 150, 50, 0, -90, -30);
  				V_AddLight (ent.origin, 130, 1, 0.5, 0.5);
  			}
  		}
  
  		VectorCopy (ent.origin, cent->lerp_origin);
  	} //end for
  }
  
  
  // 23 = weap_borges
  
  void CL_AddPlayerAttachment ( player_state_t *ps, player_state_t *ops, int modelIndex, int skin , int frame, int offset_y)
  {
  	entity_t	gun;		// view model
  	int			i;
  
  	//dont draw if outside body...
  	//if (cl_3dcam->value && !(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 > 180) //Knightmare 1/4/2002 - was 90
  		return;
  
  	memset (&gun, 0, sizeof(gun));
  
  
  	gun.model = cl.model_draw[modelIndex];
  
  	if (!gun.model)
  		return;
  
  	if (gun.model)
  	{
  		// 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]);
  
  			//do not pitch.
  			if (i == 0)
  				continue;
  
  			gun.angles[i] = cl.refdef.viewangles[i] + LerpAngle (ops->gunangles[i], ps->gunangles[i], cl.lerpfrac);
  		}
  
  		gun.origin[2] += offset_y;
  
  		gun.frame = frame;	// development tool
  		gun.oldframe = frame;	// development tool
  
  		//Knightmare- added changeable skin
  		gun.skinnum = skin;
  
  		//gun.flags = RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL;
  		gun.flags = RF_MINLIGHT | RF_DEPTHHACK ;
  
  		gun.backlerp = 1.0 - cl.lerpfrac;
  		VectorCopy (gun.origin, gun.oldorigin);	// don't lerp at all
  		V_AddEntity (&gun);
  	}
  }
  
  /*
  ==============
  CL_AddViewWeapon
  ==============
  */
  void CL_AddViewWeapon (player_state_t *ps, player_state_t *ops)
  {
  	entity_t	gun;		// view model
  	int			i;
  #ifdef NEW_PLAYER_STATE_MEMBERS //Knightmare- second gun model
  	entity_t	gun2;		// view model
  #endif
  
  	//dont draw if outside body...
  	if (cl_3dcam->value
  		&& !(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 > 180) //Knightmare 1/4/2002 - was 90
  		return;
  
  	memset (&gun, 0, sizeof(gun));
  #ifdef NEW_PLAYER_STATE_MEMBERS //Knightmare- second gun model
  	memset (&gun2, 0, sizeof(gun2));
  #endif
  
  	if (gun_model)
  		gun.model = gun_model;	// development tool
  	else
  		gun.model = cl.model_draw[ps->gunindex];
  
  #ifdef NEW_PLAYER_STATE_MEMBERS //Knightmare- second gun model
  	gun2.model = cl.model_draw[ps->gunindex2];
  	if (!gun.model && !gun2.model)
  		return;
  #else
  	if (!gun.model)
  		return;
  #endif
  
  	if (gun.model)
  	{
  		// 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;
  		}
  		//Knightmare- added changeable skin
  #ifdef NEW_PLAYER_STATE_MEMBERS
  		if (ps->gunskin)
  			gun.skinnum = ops->gunskin;
  #endif
  
  		gun.flags = RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL;
  		gun.backlerp = 1.0 - cl.lerpfrac;
  		VectorCopy (gun.origin, gun.oldorigin);	// don't lerp at all
  		V_AddEntity (&gun);
  
  		//add shells for viewweaps (all of em!)
  		if (cl_weapon_shells->value)
  		{
  			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 (s1->effects&(EF_PENT|EF_QUAD|EF_DOUBLE|EF_HALF_DAMAGE))
  					{
  						gun.flags = 0;
  						if (s1->effects & EF_QUAD)
  							gun.flags |= oldeffects | RF_TRANSLUCENT | RF_SHELL_BLUE;
  						if (s1->effects & EF_PENT)
  							gun.flags |= oldeffects | RF_TRANSLUCENT | RF_SHELL_RED;
  						if (s1->effects & EF_DOUBLE && !(s1->effects&(EF_PENT|EF_QUAD)) )
  							gun.flags |= oldeffects | RF_TRANSLUCENT | RF_SHELL_DOUBLE;
  						if (s1->effects & EF_HALF_DAMAGE && !(s1->effects&(EF_PENT|EF_QUAD|EF_DOUBLE)) )
  							gun.flags |= oldeffects | RF_TRANSLUCENT | RF_SHELL_HALF_DAM;
  
  						gun.flags |= RF_TRANSLUCENT;
  						gun.alpha = 0.30;
  											
  						V_AddEntity (&gun);
  						/*if (s1->effects & EF_COLOR_SHELL && gun.flags & (RF_SHELL_RED|RF_SHELL_BLUE|RF_SHELL_GREEN))
  						{
  							gun.skin = R_RegisterSkin ("gfx/shell_generic.pcx");
  
  							V_AddEntity (&gun);
  						}
  						if (s1->effects & EF_PENT)
  						{
  							gun.skin = R_RegisterSkin ("gfx/shell_generic.pcx");
  							gun.flags = oldeffects | RF_TRANSLUCENT | RF_SHELL_RED;
  							V_AddLight (gun.origin, 130, 1, 0.25, 0.25);
  							V_AddLight (gun.origin, 100, 1, 0, 0);
  
  							V_AddEntity (&gun);
  						}
  						if (s1->effects & EF_QUAD)
  						{
  							gun.skin = R_RegisterSkin ("gfx/shell_generic.pcx");
  							gun.flags = oldeffects | RF_TRANSLUCENT | RF_SHELL_BLUE;
  							V_AddLight (gun.origin, 130, 0.25, 0.5, 1);
  							V_AddLight (gun.origin, 100, 0, 0.25, 1);
  
  							V_AddEntity (&gun);
  						}*/
  					}
  					break; // early termination
  				}
  			gun.flags = oldeffects;
  		}
  	}
  	//Knightmare- second gun model
  #ifdef NEW_PLAYER_STATE_MEMBERS
  	if (gun2.model)
  	{
  		// set up gun2 position
  		for (i=0 ; i<3 ; i++)
  		{
  			gun2.origin[i] = cl.refdef.vieworg[i] + ops->gunoffset[i]
  				+ cl.lerpfrac * (ps->gunoffset[i] - ops->gunoffset[i]);
  			gun2.angles[i] = cl.refdef.viewangles[i] + LerpAngle (ops->gunangles[i],
  				ps->gunangles[i], cl.lerpfrac);
  		}
  
  		gun2.frame = ps->gunframe2;
  		if (gun2.frame == 0)
  			gun2.oldframe = 0;	// just changed weapons, don't lerp from old
  		else
  			gun2.oldframe = ops->gunframe2;
  
  		if (ps->gunskin2)
  			gun2.skinnum = ops->gunskin2;
  
  		gun2.flags = RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL;
  		gun2.backlerp = 1.0 - cl.lerpfrac;
  		VectorCopy (gun2.origin, gun2.oldorigin);	// don't lerp at all
  		V_AddEntity (&gun2);
  
  		//add shells for viewweaps (all of em!)
  		if (cl_weapon_shells->value)
  		{
  			int oldeffects = gun2.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 (s1->effects&(EF_PENT|EF_QUAD|EF_DOUBLE|EF_HALF_DAMAGE))
  					{
  						gun2.flags = 0;
  						if (s1->effects & EF_QUAD)
  							gun2.flags |= oldeffects | RF_TRANSLUCENT | RF_SHELL_BLUE;
  						if (s1->effects & EF_PENT)
  							gun2.flags |= oldeffects | RF_TRANSLUCENT | RF_SHELL_RED;
  						if (s1->effects & EF_DOUBLE && !(s1->effects&(EF_PENT|EF_QUAD)) )
  							gun2.flags |= oldeffects | RF_TRANSLUCENT | RF_SHELL_DOUBLE;
  						if (s1->effects & EF_HALF_DAMAGE && !(s1->effects&(EF_PENT|EF_QUAD|EF_DOUBLE)) )
  							gun2.flags |= oldeffects | RF_TRANSLUCENT | RF_SHELL_HALF_DAM;
  
  						gun2.flags |= RF_TRANSLUCENT;
  						gun2.alpha = 0.30;
  											
  						V_AddEntity (&gun2);
  						/*if (s1->effects & EF_COLOR_SHELL && gun2.flags & (RF_SHELL_RED|RF_SHELL_BLUE|RF_SHELL_GREEN))
  						{
  							gun2.skin = R_RegisterSkin ("gfx/shell_generic.pcx");
  
  							V_AddEntity (&gun2);
  						}
  						
  						if (s1->effects & EF_PENT)
  						{
  							gun2.skin = R_RegisterSkin ("gfx/shell_generic.pcx");
  							gun2.flags = oldeffects | RF_TRANSLUCENT | RF_SHELL_RED;
  							V_AddLight (gun2.origin, 130, 1, 0.25, 0.25);
  							V_AddLight (gun2.origin, 100, 1, 0, 0);
  
  							V_AddEntity (&gun2);
  						}
  						if (s1->effects & EF_QUAD)
  						{
  							gun2.skin = R_RegisterSkin ("gfx/shell_generic.pcx");
  							gun2.flags = oldeffects | RF_TRANSLUCENT | RF_SHELL_BLUE;
  							V_AddLight (gun2.origin, 130, 0.25, 0.5, 1);
  							V_AddLight (gun2.origin, 100, 0, 0.25, 1);
  
  							V_AddEntity (&gun2);
  						}*/
  					}
  					break; // early termination
  				}
  			gun2.flags = oldeffects;
  		}
  	}
  #endif
  }
  
  /*
  ===============
  SetUpCamera
  
  ===============
  */
  
  void CalcViewerCamTrans(float dist);
  void ClipCam (vec3_t start, vec3_t end, vec3_t newpos);
  // Knightmare- backup of client angles
  vec3_t old_viewangles;
  
  void SetUpCamera (void)
  {
  	if (cl_3dcam->value && !(cl.attractloop && !(cl.cinematictime > 0 && cls.realtime - cl.cinematictime > 1000)))
  	{
  		vec3_t end, oldorg, _3dcamposition, _3dcamforward;
  		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);
  
  		//and who said trig was pointless?		
  		angle = M_PI * cl_3dcam_angle->value/180.0f;
  
  		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, tempright;
  
  				vectoangles2(cl.v_forward, temp);
  				temp[PITCH]=0;
  				temp[ROLL]=0;
  
  				AngleVectors(temp, temp2, tempright, NULL);
  				VectorMA(end, -(dist_back/1.8f), temp2, end);
  
  
  				if (cl_3dcam_yaw->value != 0)
  					VectorMA(end, cl_3dcam_yaw->value, tempright, 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.5f, _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_DrawGunArms( player_state_t *ps, player_state_t *ops, int modelIndex, float yaw, float pitch)
  {
  	int k;
  
  	for (k=0; k<2; k++)
  	{
  
  	entity_t	gun;		// view model
  	int			i;
  
  	// allow the gun to be completely removed
  	if (!cl_gun->value)
  		return;
  
  	// don't draw gun if in wide angle view
  	if (ps->fov > 180) //Knightmare 1/4/2002 - was 90
  		return;
  
  	memset (&gun, 0, sizeof(gun));
  
  	gun.model = cl.model_draw[modelIndex];
  
  	if (!gun.model)
  		return;
  
  	if (gun.model)
  	{
  		vec3_t forward, right, viewang;
  		vec3_t start;
  
  		vec3_t muzzleang, muzzlepos, muzzleforward, gunpos;
  
  		vec3_t color = { 200, 100, 10 };
  
  		// 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]);
  
  			//do not pitch.
  			if (i == 0)
  				continue;
  
  			gun.angles[i] = cl.refdef.viewangles[i] + LerpAngle (ops->gunangles[i], ps->gunangles[i], cl.lerpfrac);
  		}
  
  		gun.angles[YAW] += yaw;
  		gun.angles[PITCH] += pitch;
  
  		VectorCopy (cl.refdef.viewangles, viewang);
  		viewang[PITCH] = 0;
  		AngleVectors (viewang, forward, right, NULL);
  
  		if (k <= 0)
  			VectorMA (gun.origin, 4.6, right, gun.origin);
  		else
  			VectorMA (gun.origin, -9.2, right, gun.origin);
  
  		VectorMA (gun.origin, 23, forward, gun.origin);
  		gun.origin[2] -= 20;
  
  
  		//gun.flags = RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL;
  		//gun.flags = RF_MINLIGHT | RF_DEPTHHACK ;
  		gun.backlerp = 1.0 - cl.lerpfrac;
  		VectorCopy (gun.origin, gun.oldorigin);	// don't lerp at all
  		V_AddEntity (&gun);
  	}
  	}
  }
  
  /*
  ===============
  CL_CalcViewValues
  
  Sets cl.refdef view values
  ===============
  */
  
  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 involid
  	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]) );
  
  		// Knightmare- set predicted origin anyway, it's needed for the chasecam
  		VectorCopy(cl.refdef.vieworg, cl.predicted_origin);
  		cl.predicted_origin[2] -= ops->viewoffset[2]; 
  	}
  
  	// 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.frame.playerstate.stats[STAT_ATTACHMENT1] > 0)
  	{
  		CL_AddPlayerAttachment( ps, ops, cl.frame.playerstate.stats[STAT_ATTACHMENT1],
  			cl.frame.playerstate.stats[STAT_ATTACHMENT1SKIN],
  			cl.frame.playerstate.stats[STAT_ATTACHMENT1FRAME],
  			cl.frame.playerstate.stats[STAT_ATTACHMENT1HEIGHT]);
  	}
  
  	if (cl.frame.playerstate.stats[STAT_ATTACHMENT2] > 0)
  	{
  		CL_AddPlayerAttachment( ps, ops, cl.frame.playerstate.stats[STAT_ATTACHMENT2],
  			cl.frame.playerstate.stats[STAT_ATTACHMENT2SKIN],
  			cl.frame.playerstate.stats[STAT_ATTACHMENT2FRAME],
  			cl.frame.playerstate.stats[STAT_ATTACHMENT2HEIGHT]);
  	}
  
  
  	if (cl.frame.playerstate.stats[STAT_GUNARMS] > 0)
  	{
  		CL_DrawGunArms(ps, ops, cl.frame.playerstate.stats[STAT_GUNARMS],
  			cl.frame.playerstate.stats[STAT_GUNARM_YAW],
  			cl.frame.playerstate.stats[STAT_GUNARM_PITCH]);
  	}
  
  
  	//Com_Printf ("borg %d\n", cl.frame.playerstate.stats[STAT_BORGESCARRY]);
  
  	//Com_Printf ("gunindex %d\n", ps->gunindex);
  
  	// set up chase cam
  	SetUpCamera();
  }
  
  
  
  
  
  
  
  /*
  ===============
  CL_AddEntities
  
  Emits all entities, particles, and lights to the refresh
  ===============
  */
  //void CalcViewerCamTrans (void);
  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 ();
  
  	// Knightmare- added Psychospaz's chasecam
  	//if (cl_3dcam->value)
  	//	CalcViewerCamTrans ();
  
  	// 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_AddProjectiles ();
  	CL_AddTEnts ();
  	CL_AddParticles ();
  	CL_AddDLights ();
  	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...
  }
  