/*
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	qhandle_t cl_mod_powerscreen;

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

FRAME PARSING

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

/*
==================
CL_DeltaEntity

Parses deltas from the given base and adds the resulting entity
to the current frame.

cl.frame should be set to current frame before calling this function.
==================
*/
static void CL_DeltaEntity( entity_state_t *state ) {
	centity_t	*ent;
	qboolean teleported;

	ent = &cl_entities[state->number];

	teleported = qfalse;
	if( state->number != cl.frame.ps.clientNum + 1 &&
		 ( 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 ) )
	{
		teleported = qtrue;
	}
	

	// 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
		|| teleported
		|| state->event == EV_PLAYER_TELEPORT
		|| state->event == EV_OTHER_TELEPORT )
	{
		ent->serverframe = -99;
	}

	if( ent->serverframe != cl.oldframe.serverFrame ) {
		// 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_PLAYER_TELEPORT ||
			state->event == EV_OTHER_TELEPORT )
		{
			// FIXME: teleportation = no interpolation
			VectorCopy( state->origin, ent->lerp_origin );
		} else {
			// old_origin is valid for new entities, so use it
			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;

	if( state->number == cl.frame.ps.clientNum + 1 ) {
		/* FIXME: this lags one frame */
		VectorCopy( cl.playerEntityOrigin, ent->current.origin );
		VectorCopy( cl.playerEntityAngles, ent->current.angles );
	}
}

/*
==================
CL_DeltaFrame

==================
*/
void CL_DeltaFrame( void ) {
	entity_state_t		*state;
	int					pnum, num;
	centity_t			*cent;

    VectorScale( cl.frame.ps.ps.pmove.origin, 0.125f, cl.playerEntityOrigin );

	CL_BuildSolidList();

	for( pnum = 0; pnum < cl.frame.numEntityStates; pnum++ ) {
		num = ( cl.frame.firstEntityState + pnum ) & PARSE_ENTITIES_MASK;
		state = &cl.entityStates[num];

		CL_DeltaEntity( state );

		cent = &cl_entities[state->number];
		/* fire events */
		if( state->event ) {
			CL_EntityEvent( &cent->current );
		}

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

}


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

INTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS

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

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

/*
===============
CL_AddPacketEntities

===============
*/
static void CL_AddPacketEntities( void ) {
	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 ) );

	for( pnum = 0; pnum < cl.frame.numEntityStates; pnum++ ) {
		i = ( cl.frame.firstEntityState + pnum ) & PARSE_ENTITIES_MASK;
		s1 = &cl.entityStates[i];

		cent = &cl_entities[s1->number];

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

		if( ( effects & EF_GIB ) && !cl_gibs->integer ) {
			continue;
		}

			// 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;

		// 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) {
			// step origin discretely, because the frames
			// do the animation properly
			VectorCopy (cent->current.origin, ent.origin);
			VectorCopy (cent->current.old_origin, ent.oldorigin);
		} else if( renderfx & RF_BEAM ) {
			// interpolate start and end points for beams
			for( i = 0; i < 3; i++ ) {
				ent.oldorigin[i] = cent->prev.old_origin[i] + cl.lerpfrac * 
					( cent->current.old_origin[i] - cent->prev.old_origin[i] );
				ent.origin[i] = cent->prev.origin[i] + cl.lerpfrac * 
					( cent->current.origin[i] - cent->prev.origin[i] );
			}
		} else if( s1->number == cl.frame.ps.clientNum + 1 ) {
			// use predicted origin
			VectorCopy( cl.playerEntityOrigin, ent.origin );
			VectorCopy( cl.playerEntityOrigin, 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 = 0;
		}
		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 = ref.RegisterSkin ("players/male/disguise.pcx");
						ent.model = ref.RegisterModel ("players/male/tris.md2");
					}
					else if(!strncmp((char *)ent.skin, "players/female", 14))
					{
						ent.skin = ref.RegisterSkin ("players/female/disguise.pcx");
						ent.model = ref.RegisterModel ("players/female/tris.md2");
					}
					else if(!strncmp((char *)ent.skin, "players/cyborg", 14))
					{
						ent.skin = ref.RegisterSkin ("players/cyborg/disguise.pcx");
						ent.model = ref.RegisterModel ("players/cyborg/tris.md2");
					}
				}
//PGM
//============
			}
			else
			{
				ent.skinnum = s1->skinnum;
				ent.skin = 0;
				ent.model = cl.model_draw[s1->modelindex];
			}
		}

		// only used for black hole model right now, FIXME: do better
		if ((renderfx & RF_TRANSLUCENT) && !(renderfx & RF_BEAM))
			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 if( s1->number == cl.frame.ps.clientNum + 1 ) {
			VectorCopy( cl.playerEntityAngles, ent.angles );	// use predicted angles
		} 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( s1->number == cl.frame.ps.clientNum + 1 ) {
			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.thirdPersonView ) {
#if 0
				ent.flags |= RF_VIEWERMODEL;	// only draw from mirrors
#else
				VectorCopy( ent.origin, cent->lerp_origin );
				continue;
#endif
			}
		}

		// if set to invisible, skip
		if (!s1->modelindex) {
			VectorCopy (ent.origin, cent->lerp_origin);
			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
		V_AddEntity (&ent);

		// color shells generate a seperate entity for the main model
		if (effects & EF_COLOR_SHELL)
		{
			// 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
//			if(Developer_searchpath(2) == 2)
//			{
				// 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);
		}

		ent.skin = 0;		// never use a custom skin on others
		ent.skinnum = 0;
		ent.flags = 0;
		ent.alpha = 0;

		// 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->integer || 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

			V_AddEntity (&ent);

			//PGM - make sure these get reset.
			ent.flags = 0;
			ent.alpha = 0;
			//PGM
		}
		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);
		}

		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)
			{
				if( !( cl_disable_particles->integer & NOPART_ROCKET_TRAIL ) ) {
					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)
			{
//				CL_BlasterTrail (cent->lerp_origin, ent.origin);
//PGM
				if (effects & EF_TRACKER)	// lame... problematic?
				{
					CL_BlasterTrail2 (cent->lerp_origin, ent.origin);
					V_AddLight (ent.origin, 200, 0, 1, 0);		
				}
				else
				{
					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)
			{
				if( !( cl_disable_particles->integer & NOPART_GRENADE_TRAIL ) ) {
					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)
			{
				CL_FlagTrail (cent->lerp_origin, ent.origin, 242);
				V_AddLight (ent.origin, 225, 1, 0.1, 0.1);
			}
			else if (effects & EF_FLAG2)
			{
				CL_FlagTrail (cent->lerp_origin, ent.origin, 115);
				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( scr_glconfig.renderer != GL_RENDERER_SOFTWARE )
						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)
			{
				CL_TrackerTrail (cent->lerp_origin, ent.origin, 0);
				// FIXME - check out this effect in rendition
				if( scr_glconfig.renderer != GL_RENDERER_SOFTWARE )
					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);
				}
				V_AddLight (ent.origin, 130, 1, 0.5, 0.5);
			}
		}

		VectorCopy (ent.origin, cent->lerp_origin);
	}
}



/*
==============
CL_AddViewWeapon
==============
*/
static void CL_AddViewWeapon( const player_state_t *ps, const player_state_t *ops ) {
	entity_t	gun;		// view model
	int			i;

	// allow the gun to be completely removed
	if( cl_gun->integer < 1 ) {
		return;
	}

	if( info_hand->integer == 2 ) {
		return;
	}

	// never draw in third person mode
	if( cl.thirdPersonView ) {
		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.flags = RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL;
	if( info_hand->integer == 1 ) {
		gun.flags |= RF_LEFTHAND;
	}
	gun.backlerp = 1.0 - cl.lerpfrac;
	VectorCopy( gun.origin, gun.oldorigin );	// don't lerp at all
	V_AddEntity( &gun );
}

trace_t CL_WorldTrace( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end );

/*
===============
CL_SetupThirdPersionView
===============
*/
static void CL_SetupThirdPersionView( void ) {
	vec3_t playerOrigin;
	vec3_t forward, right, up;
	vec3_t focusPoint;
	float forwardScale, rightScale;
	float dist;
	trace_t trace;
	vec3_t mins = { -4, -4, -4 }, maxs = { 4, 4, 4 };

	VectorCopy( cl.refdef.vieworg, playerOrigin );

	AngleVectors( cl.refdef.viewangles, forward, NULL, NULL );
	VectorMA( cl.refdef.vieworg, 512, forward, focusPoint );

	cl.refdef.vieworg[2] += 8;

	cl.refdef.viewangles[PITCH] *= 0.5f;
	AngleVectors( cl.refdef.viewangles, forward, right, up );

	forwardScale = cos( DEG2RAD( cl_thirdperson_angle->value ) );
	rightScale = sin( DEG2RAD( cl_thirdperson_angle->value ) );
	VectorMA( cl.refdef.vieworg, -cl_thirdperson_range->value * forwardScale, forward, cl.refdef.vieworg );
	VectorMA( cl.refdef.vieworg, -cl_thirdperson_range->value * rightScale, right, cl.refdef.vieworg );

	CM_BoxTrace( &trace, playerOrigin, cl.refdef.vieworg,
            mins, maxs, cl.cm.cache->nodes, MASK_SOLID );
	if( trace.fraction != 1.0f ) {
		VectorCopy( trace.endpos, cl.refdef.vieworg );
	}

	VectorSubtract( focusPoint, cl.refdef.vieworg, focusPoint );
	dist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );

	cl.refdef.viewangles[PITCH] = -180 / M_PI * atan2( focusPoint[2], dist );
	cl.refdef.viewangles[YAW] -= cl_thirdperson_angle->value;


}

/*
===============
CL_CalcCurrentState

Returns the interpolated or predicted state for specific playerNum
Called by sound and rendering code
===============
*/
static player_state_t *CL_CalcCurrentState( vec3_t vieworg, vec3_t viewoffset,
										   vec3_t viewangles )
{
	float		lerp, backlerp;
	player_state_t	*ps, *ops;
	qboolean demoplayback, predict;

	// find states to interpolate between
	ps = &cl.frame.ps.ps;
	ops = &cl.oldframe.ps.ps;
	if( !cl.oldframe.valid || cl.oldframe.serverFrame != cl.frame.serverFrame - 1 ) {
		ops = ps;
	}

	// see if the player entity was teleported this frame
	if( abs( 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 )
	{
		//Com_Printf( "teleported\n" );
		ops = ps;		// don't interpolate
	}

	if( ( ops->pmove.pm_flags ^ ps->pmove.pm_flags ) & PMF_TELEPORT_BIT ) {
		//Com_Printf( "teleported\n" );
		ops = ps;
	}

	lerp = cl.lerpfrac;

	// calculate the origin
	demoplayback = cls.demoplayback || cl.attractLoop == ATR_DEMO;
	predict = cl_predict->integer && !( ps->pmove.pm_flags & PMF_NO_PREDICTION );
	if( !demoplayback && predict ) {	
		// use predicted values
		int	delta;

		backlerp = 1.0 - lerp;
		
		vieworg[0] = cl.predicted_origin[0] - backlerp * cl.prediction_error[0];
		vieworg[1] = cl.predicted_origin[1] - backlerp * cl.prediction_error[1];
		vieworg[2] = cl.predicted_origin[2] - backlerp * cl.prediction_error[2];

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

	// if not running a demo or on a locked frame, add the local angle movement
	if( demoplayback ) {
		if( Key_IsDown( K_SHIFT ) ) {
			VectorCopy( cl.viewangles, viewangles );
		} else {
			viewangles[0] = LerpAngle( ops->viewangles[0], ps->viewangles[0], lerp );
			viewangles[1] = LerpAngle( ops->viewangles[1], ps->viewangles[1], lerp );
			viewangles[2] = LerpAngle( ops->viewangles[2], ps->viewangles[2], lerp );
		}
	} else if( ps->pmove.pm_type < PM_DEAD ) {	
		// use predicted values
		VectorCopy( cl.predicted_angles, viewangles );
	} else if( ops->pmove.pm_type < PM_DEAD && cls.serverProtocol > PROTOCOL_VERSION_DEFAULT ) {
		// lerp from predicted angles, since enhanced servers
		// do not send viewangles each frame
		viewangles[0] = LerpAngle( cl.predicted_angles[0], ps->viewangles[0], lerp );
		viewangles[1] = LerpAngle( cl.predicted_angles[1], ps->viewangles[1], lerp );
		viewangles[2] = LerpAngle( cl.predicted_angles[2], ps->viewangles[2], lerp );
	} else {
		// just use interpolated values
		viewangles[0] = LerpAngle( ops->viewangles[0], ps->viewangles[0], lerp );
		viewangles[1] = LerpAngle( ops->viewangles[1], ps->viewangles[1], lerp );
		viewangles[2] = LerpAngle( ops->viewangles[2], ps->viewangles[2], lerp );
	}

	
	viewoffset[0] = ops->viewoffset[0] +
		lerp * ( ps->viewoffset[0] - ops->viewoffset[0] );
	viewoffset[1] = ops->viewoffset[1] +
		lerp * ( ps->viewoffset[1] - ops->viewoffset[1] );
	viewoffset[2] = ops->viewoffset[2] +
		lerp * ( ps->viewoffset[2] - ops->viewoffset[2] );

	return ops;
	
}

/*
===============
CL_CalcViewValues

Sets cl.refdef view values
===============
*/
static void CL_CalcViewValues( void ) {
	player_state_t	*ps, *ops;
	vec3_t viewoffset;
	vec3_t kickangles;
    float fov;

	ps = &cl.frame.ps.ps;
	ops = CL_CalcCurrentState( cl.refdef.vieworg, viewoffset, cl.refdef.viewangles );

	// interpolate field of view
	if( cl_demo_local_fov->integer && cls.demoplayback ) {
		fov = info_fov->value;
        if( fov < 1 ) {
            fov = 90;
        } else if( fov > 160 ) {
            fov = 160;
        }
        cl.refdef.fov_x = fov;
	} else {
		cl.refdef.fov_x = ops->fov + cl.lerpfrac * ( ps->fov - ops->fov );
	}

	// don't interpolate blend color
	if( cl_add_blend->integer ) {
		cl.refdef.blend[0] = ps->blend[0];
		cl.refdef.blend[1] = ps->blend[1];
		cl.refdef.blend[2] = ps->blend[2];
		cl.refdef.blend[3] = ps->blend[3];
	} else {
		cl.refdef.blend[0] = 0;
		cl.refdef.blend[1] = 0;
		cl.refdef.blend[2] = 0;
		cl.refdef.blend[3] = 0;
	}

	cl.thirdPersonView = qfalse;
	if( cl_thirdperson->integer && cl.frame.ps.clientNum != CLIENTNUM_NONE ) {
		cl.thirdPersonView = qtrue;
	}

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

	VectorCopy( cl.refdef.vieworg, cl.playerEntityOrigin );
	VectorCopy( cl.refdef.viewangles, cl.playerEntityAngles );

	if( cl.playerEntityAngles[PITCH] > 180 ) {
		cl.playerEntityAngles[PITCH] -= 360;
	}

	cl.playerEntityAngles[PITCH] = cl.playerEntityAngles[PITCH] / 3;

	VectorAdd( cl.refdef.vieworg, viewoffset, cl.refdef.vieworg );

	if( cl.thirdPersonView ) {
		// if dead, set a nice view angle
		if( ps->stats[STAT_HEALTH] <= 0 ) {
			cl.refdef.viewangles[ROLL] = 0;
			cl.refdef.viewangles[PITCH] = 10;
		} 
		CL_SetupThirdPersionView();
	} else {
		// add kick angles
		if( cl_kickangles->integer ) {
			kickangles[0] = LerpAngle(
				ops->kick_angles[0], ps->kick_angles[0], cl.lerpfrac );
			kickangles[1] = LerpAngle(
				ops->kick_angles[1], ps->kick_angles[1], cl.lerpfrac );
			kickangles[2] = LerpAngle(
				ops->kick_angles[2], ps->kick_angles[2], cl.lerpfrac );

			VectorAdd( cl.refdef.viewangles, kickangles, cl.refdef.viewangles );
		}

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


/*
===============
CL_AddEntities

Emits all entities, particles, and lights to the refresh
===============
*/
void CL_AddEntities( void ) {
	CL_CalcViewValues();
	CL_AddPacketEntities();
	CL_AddTEnts();
	CL_AddParticles();
	CL_AddDLights();
	CL_AddLightStyles();
	LOC_AddLocationsToScene();
}



/*
===============
CL_GetEntitySoundOrigin

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

	if( entnum < 0 || entnum >= MAX_EDICTS ) {
		Com_Error( ERR_DROP, "CL_GetEntitySoundOrigin: bad entnum: %d", entnum );
	}
	if( !entnum ) {
		// should this ever happen?
		Com_DPrintf( "CL_GetEntitySoundOrigin: called on world entity\n" );
		VectorClear( org );
		return;
	}
	ent = &cl_entities[entnum];
	VectorCopy( ent->lerp_origin, org );

	if( ent->current.solid == 31 ) {
		cmodel = cl.model_clip[ent->current.modelindex];
		if( cmodel ) {
			mid[0] = ( cmodel->mins[0] + cmodel->maxs[0] ) * 0.5f;
			mid[1] = ( cmodel->mins[1] + cmodel->maxs[1] ) * 0.5f;
			mid[2] = ( cmodel->mins[2] + cmodel->maxs[2] ) * 0.5f;

			VectorAdd( org, mid, org );
		}
	}
}

/*
===============
CL_GetSoundInfo

Called to get the sound spatialization params
===============
*/
int CL_GetSoundInfo( vec3_t origin, vec3_t forward, vec3_t right, vec3_t up ) {
	vec3_t viewangles;
	vec3_t viewoffset;
	
	if( cl.clientNum == -1 ) {
		return -1; // showing cinematic or static pic
	}

	CL_CalcCurrentState( origin, viewoffset, viewangles );
	AngleVectors( viewangles, forward, right, up );

	VectorAdd( origin, viewoffset, origin );

	if( cl.frame.ps.clientNum == CLIENTNUM_NONE ) {
		return -1; // free demo movement
	}

	return cl.frame.ps.clientNum + 1;
}





