/*
===========================================================================
Copyright (C) 1999 - 2005, Id Software, Inc.
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2005 - 2015, ioquake3 contributors
Copyright (C) 2013 - 2015, OpenJK contributors

This file is part of the OpenJK source code.

OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.

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, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

// cg_ents.c -- present snapshot entities, happens every single frame

#include "cg_local.h"
/*
Ghoul2 Insert Start
*/
#include "qcommon/q_shared.h"
#include "ghoul2/G2.h"
/*
Ghoul2 Insert end
*/

extern qboolean CG_InFighter( void );
static void CG_Missile( centity_t *cent );

/*
======================
CG_PositionEntityOnTag

Modifies the entities position and axis by the given
tag location
======================
*/
void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
							qhandle_t parentModel, char *tagName ) {
	int				i;
	orientation_t	lerped;

	// lerp the tag
	trap->R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
		1.0 - parent->backlerp, tagName );

	// FIXME: allow origin offsets along tag?
	VectorCopy( parent->origin, entity->origin );
	for ( i = 0 ; i < 3 ; i++ ) {
		VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
	}

	// had to cast away the const to avoid compiler problems...
	MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis );
	entity->backlerp = parent->backlerp;
}


/*
======================
CG_PositionRotatedEntityOnTag

Modifies the entities position and axis by the given
tag location
======================
*/
void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
							qhandle_t parentModel, char *tagName ) {
	int				i;
	orientation_t	lerped;
	matrix3_t		tempAxis;

//AxisClear( entity->axis );
	// lerp the tag
	trap->R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
		1.0 - parent->backlerp, tagName );

	// FIXME: allow origin offsets along tag?
	VectorCopy( parent->origin, entity->origin );
	for ( i = 0 ; i < 3 ; i++ ) {
		VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
	}

	// had to cast away the const to avoid compiler problems...
	MatrixMultiply( entity->axis, lerped.axis, tempAxis );
	MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis );
}



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

FUNCTIONS CALLED EACH FRAME

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

//only need to use the CG_S_ system when you want a looping sound that isn't going to add itself
//via trap each frame. Such as ones generated by events.
/*
======================
CG_SetEntitySoundPosition

Also called by event processing code
======================
*/
void CG_SetEntitySoundPosition( centity_t *cent ) {
	if ( cent->currentState.solid == SOLID_BMODEL )
	{
		vec3_t	origin;
		float	*v;

		v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
		VectorAdd( cent->lerpOrigin, v, origin );
		trap->S_UpdateEntityPosition( cent->currentState.number, origin );
	}
	else
	{
		trap->S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
	}
}

/*
==================
CG_S_AddLoopingSound

Set the current looping sounds on the entity.
==================
*/
void CG_S_AddLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx)
{
	centity_t *cent = &cg_entities[entityNum];
	cgLoopSound_t *cSound = NULL;
	int i = 0;
	qboolean alreadyPlaying = qfalse;

	//first see if we're already looping this sound handle.
	while (i < cent->numLoopingSounds)
	{
		cSound = &cent->loopingSound[i];

		if (cSound->sfx == sfx)
		{
			alreadyPlaying = qtrue;
			break;
		}

		i++;
	}

	if (alreadyPlaying && cSound)
	{ //if this is the case, just update the properties of the looping sound and return.
		VectorCopy(origin, cSound->origin);
		VectorCopy(velocity, cSound->velocity);
	}
	else if (cent->numLoopingSounds >= MAX_CG_LOOPSOUNDS)
	{ //Just don't add it then I suppose.
		return;
	}

	//Add a new looping sound.
	cSound = &cent->loopingSound[cent->numLoopingSounds];

	cSound->entityNum = entityNum;
	VectorCopy(origin, cSound->origin);
	VectorCopy(velocity, cSound->velocity);
	cSound->sfx = sfx;

	cent->numLoopingSounds++;
}

/*
==================
CG_S_AddLoopingSound

For now just redirect, might eventually do something different.
==================
*/
void CG_S_AddRealLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx)
{
	CG_S_AddLoopingSound(entityNum, origin, velocity, sfx);
}

/*
==================
CG_S_AddLoopingSound

Clear looping sounds.
==================
*/
void CG_S_StopLoopingSound(int entityNum, sfxHandle_t sfx)
{
	centity_t *cent = &cg_entities[entityNum];
	cgLoopSound_t *cSound;

	if (sfx == -1)
	{ //clear all the looping sounds on the entity
		cent->numLoopingSounds = 0;
	}
	else
	{ //otherwise, clear only the specified looping sound
		int i = 0;

		while (i < cent->numLoopingSounds)
		{
			cSound = &cent->loopingSound[i];

			if (cSound->sfx == sfx)
			{ //remove it then
				int x = i+1;

				while (x < cent->numLoopingSounds)
				{
					memcpy(&cent->loopingSound[x-1], &cent->loopingSound[x], sizeof(cent->loopingSound[x]));
					x++;
				}
				cent->numLoopingSounds--;
			}

			i++;
		}
	}
	//trap->S_StopLoopingSound(entityNum);
}

/*
==================
CG_S_UpdateLoopingSounds

Update any existing looping sounds on the entity.
==================
*/
void CG_S_UpdateLoopingSounds(int entityNum)
{
	centity_t *cent = &cg_entities[entityNum];
	cgLoopSound_t *cSound;
	vec3_t lerpOrg;
	float *v;
	int i = 0;

	if (!cent->numLoopingSounds)
	{
		return;
	}

	if (cent->currentState.eType == ET_MOVER)
	{
		v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
		VectorAdd( cent->lerpOrigin, v, lerpOrg );
	}
	else
	{
		VectorCopy(cent->lerpOrigin, lerpOrg);
	}

	if ( (cent->currentState.eFlags & EF_SOUNDTRACKER)
		&& (!cg.snap || cent->currentState.trickedentindex != cg.snap->ps.clientNum) )
	{//keep sound for this entity updated in accordance with its attached entity at all times
		//entity out of range
		if ( !cg_entities[cent->currentState.trickedentindex].currentValid )
			return;

		VectorCopy( cg_entities[cent->currentState.trickedentindex].lerpOrigin, lerpOrg );
	}

	while (i < cent->numLoopingSounds)
	{
		cSound = &cent->loopingSound[i];

		//trap->S_AddLoopingSound(entityNum, cSound->origin, cSound->velocity, cSound->sfx);
		//I guess just keep using lerpOrigin for now,
		trap->S_AddLoopingSound(entityNum, lerpOrg, cSound->velocity, cSound->sfx);
		i++;
	}
}

/*
==================
CG_EntityEffects

Add continuous entity effects, like local entity emission and lighting
==================
*/
static void CG_EntityEffects( centity_t *cent ) {

	if( !cent ) return;

	// update sound origins
	CG_SetEntitySoundPosition( cent );

	// add loop sound
	if ( cent->currentState.loopSound || ((cent->currentState.loopIsSoundset && cent->currentState.number >= MAX_CLIENTS)
		&& cent->currentState.loopSound < MAX_SOUNDS)) {
		sfxHandle_t realSoundIndex = -1;

		if (cent->currentState.loopIsSoundset && cent->currentState.number >= MAX_CLIENTS)
		{ //If this is so, then first get our soundset from the index, and loopSound actually contains which part of the set to
		  //use rather than a sound index (BMS_START [0], BMS_MID [1], or BMS_END [2]). Typically loop sounds will be BMS_MID.
			const char *soundSet;

			soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex );

			if (soundSet && soundSet[0])
			{
				realSoundIndex = trap->AS_GetBModelSound(soundSet, cent->currentState.loopSound);
			}
		}
		else
		{
			realSoundIndex = cgs.gameSounds[ cent->currentState.loopSound ];
		}

		//rww - doors and things with looping sounds have a crazy origin (being brush models and all)
		if (realSoundIndex != -1)
		{
			if ( cent->currentState.solid == SOLID_BMODEL )
			{
				vec3_t	origin;
				float	*v;

				v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
				VectorAdd( cent->lerpOrigin, v, origin );
				trap->S_AddLoopingSound( cent->currentState.number, origin, vec3_origin,
					realSoundIndex );
			}
			else if (cent->currentState.eType != ET_SPEAKER) {
				trap->S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, realSoundIndex );
			} else {
				trap->S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, realSoundIndex );
			//	trap->S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, realSoundIndex );
			}
		}
	}


	// constant light glow
	if ( cent->currentState.constantLight && cent->currentState.eType != ET_PLAYER && cent->currentState.eType != ET_BODY && cent->currentState.eType != ET_NPC && cent->currentState.eType != ET_INVISIBLE ) {
		int		cl;
		float	i, r, g, b;

		cl = cent->currentState.constantLight;
		r = (float) (cl & 0xFF) / 255.0;
		g = (float) ((cl >> 8) & 0xFF) / 255.0;
		b = (float) ((cl >> 16) & 0xFF) / 255.0;
		i = (float) ((cl >> 24) & 0xFF) * 4.0;
		trap->R_AddLightToScene( cent->lerpOrigin, i, r, g, b );
	}

}

localEntity_t *FX_AddOrientedLine(vec3_t start, vec3_t end, vec3_t normal, float stScale, float scale,
								  float dscale, float startalpha, float endalpha, float killTime, qhandle_t shader)
{
	localEntity_t	*le;

#ifdef _DEBUG
	if (!shader)
	{
		Com_Printf("FX_AddLine: NULL shader\n");
	}
#endif

	le = CG_AllocLocalEntity();
	le->leType = LE_OLINE;

	le->startTime = cg.time;
	le->endTime = le->startTime + killTime;
	le->data.line.width = scale;
	le->data.line.dwidth = dscale;

	le->alpha = startalpha;
	le->dalpha = endalpha - startalpha;

	le->refEntity.data.line.stscale = stScale;
	le->refEntity.data.line.width = scale;

	le->refEntity.customShader = shader;

	// set origin
	VectorCopy ( start, le->refEntity.origin);
	VectorCopy ( end, le->refEntity.oldorigin );

	AxisClear(le->refEntity.axis);
	VectorCopy( normal, le->refEntity.axis[0] );
	RotateAroundDirection( le->refEntity.axis, 0); // le->refEntity.data.sprite.rotation );	This is roll in quad land

	le->refEntity.shaderRGBA[0] = 0xff;
	le->refEntity.shaderRGBA[1] = 0xff;
	le->refEntity.shaderRGBA[2] = 0xff;
	le->refEntity.shaderRGBA[3] = 0xff;

	le->color[0] = 1.0;
	le->color[1] = 1.0;
	le->color[2] = 1.0;
	le->color[3] = 1.0;
	le->lifeRate = 1.0 / ( le->endTime - le->startTime );

	return(le);
}

void FX_DrawPortableShield(centity_t *cent)
{
	//rww - this code differs a bit from the draw code in EF, I don't know why I had to do
	//it this way yet it worked in EF the other way.

	int				xaxis, height, posWidth, negWidth, team;
	vec3_t			start, end, normal;
	qhandle_t		shader;

	if ( cl_paused.integer )
	{ //rww - fix to keep from rendering repeatedly while HUD menu is up
		return;
	}

	if (cent->currentState.eFlags & EF_NODRAW)
	{
		return;
	}

	// decode the data stored in time2
	xaxis = ((cent->currentState.time2 >> 24) & 1);
	height = ((cent->currentState.time2 >> 16) & 255);
	posWidth = ((cent->currentState.time2 >> 8) & 255);
	negWidth = (cent->currentState.time2 & 255);

	team = (cent->currentState.otherEntityNum2);

	VectorClear(normal);

	VectorCopy(cent->lerpOrigin, start);
	VectorCopy(cent->lerpOrigin, end);

	if (xaxis) // drawing along x-axis
	{
		start[0] -= negWidth;
		end[0] += posWidth;
	}
	else
	{
		start[1] -= negWidth;
		end[1] += posWidth;
	}

	normal[0] = 1;
	normal[1] = 1;

	start[2] += height/2;
	end[2] += height/2;

	if (team == TEAM_RED)
	{
		if (cent->currentState.trickedentindex)
		{
			shader = trap->R_RegisterShader( "gfx/misc/red_dmgshield" );
		}
		else
		{
			shader = trap->R_RegisterShader( "gfx/misc/red_portashield" );
		}
	}
	else
	{
		if (cent->currentState.trickedentindex)
		{
			shader = trap->R_RegisterShader( "gfx/misc/blue_dmgshield" );
		}
		else
		{
			shader = trap->R_RegisterShader( "gfx/misc/blue_portashield" );
		}
	}

	FX_AddOrientedLine(start, end, normal, 1.0f, height, 0.0f, 1.0f, 1.0f, 50.0, shader);
}

/*
==================
CG_Special
==================
*/
void CG_Special( centity_t *cent ) {
	entityState_t		*s1;

	s1 = &cent->currentState;

	if (!s1)
	{
		return;
	}

	// if set to invisible, skip
	if (!s1->modelindex) {
		return;
	}

	if (s1->modelindex == HI_SHIELD)
	{	// The portable shield should go through a different rendering function.
		FX_DrawPortableShield(cent);
		return;
	}
}

/*
Ghoul2 Insert Start
*/

// Copy the ghoul2 data into the ref ent correctly
void CG_SetGhoul2Info( refEntity_t *ent, centity_t *cent)
{

	ent->ghoul2 = cent->ghoul2;
	VectorCopy( cent->modelScale, ent->modelScale);
	ent->radius = cent->radius;
	VectorCopy (cent->lerpAngles, ent->angles);
}



// create 8 new points on screen around a model so we can see it's bounding box
void CG_CreateBBRefEnts(entityState_t *s1, vec3_t origin )
{
/*
//g2r
#if _DEBUG
	refEntity_t		point[8];
	int				i;
	vec3_t			angles = {0,0,0};

	for (i=0; i<8; i++)
	{
		memset (&point[i], 0, sizeof(refEntity_t));
		point[i].reType = RT_SPRITE;
		point[i].radius = 1;
		point[i].customShader = trap->R_RegisterShader("textures/tests/circle");
		point[i].shaderRGBA[0] = 255;
		point[i].shaderRGBA[1] = 255;
		point[i].shaderRGBA[2] = 255;
		point[i].shaderRGBA[3] = 255;

		AnglesToAxis( angles, point[i].axis );

		// now, we need to put the correct origins into each origin from the mins and max's
		switch(i)
		{
		case 0:
			VectorCopy(s1->mins, point[i].origin);
   			break;
		case 1:
			VectorCopy(s1->mins, point[i].origin);
			point[i].origin[0] = s1->maxs[0];
   			break;
		case 2:
			VectorCopy(s1->mins, point[i].origin);
			point[i].origin[1] = s1->maxs[1];
   			break;
		case 3:
			VectorCopy(s1->mins, point[i].origin);
			point[i].origin[0] = s1->maxs[0];
			point[i].origin[1] = s1->maxs[1];
   			break;
		case 4:
			VectorCopy(s1->maxs, point[i].origin);
   			break;
		case 5:
			VectorCopy(s1->maxs, point[i].origin);
			point[i].origin[0] = s1->mins[0];
   			break;
		case 6:
			VectorCopy(s1->maxs, point[i].origin);
			point[i].origin[1] = s1->mins[1];
   			break;
		case 7:
			VectorCopy(s1->maxs, point[i].origin);
			point[i].origin[0] = s1->mins[0];
			point[i].origin[1] = s1->mins[1];
   			break;
		}

		// add the original origin to each point and then stuff them out there
		VectorAdd(point[i].origin, origin, point[i].origin);

		trap->R_AddRefEntityToScene (&point[i]);
	}
#endif
	*/
}

// write in the axis and stuff
void G2_BoltToGhoul2Model(centity_t *cent, refEntity_t *ent)
{
		// extract the wraith ID from the bolt info
	int modelNum = cent->boltInfo >> MODEL_SHIFT;
	int boltNum	= cent->boltInfo >> BOLT_SHIFT;
	int	entNum = cent->boltInfo >> ENTITY_SHIFT;
 	mdxaBone_t 		boltMatrix;

	modelNum &= MODEL_AND;
	boltNum &= BOLT_AND;
	entNum &= ENTITY_AND;


	//NOTENOTE I put this here because the cgs.gamemodels array no longer gets initialized.
	assert(0);


 	// go away and get me the bolt position for this frame please
	trap->G2API_GetBoltMatrix(cent->ghoul2, modelNum, boltNum, &boltMatrix, cg_entities[entNum].currentState.angles, cg_entities[entNum].currentState.origin, cg.time, cgs.gameModels, cent->modelScale);

	// set up the axis and origin we need for the actual effect spawning
 	ent->origin[0] = boltMatrix.matrix[0][3];
 	ent->origin[1] = boltMatrix.matrix[1][3];
 	ent->origin[2] = boltMatrix.matrix[2][3];

 	ent->axis[0][0] = boltMatrix.matrix[0][0];
 	ent->axis[0][1] = boltMatrix.matrix[1][0];
 	ent->axis[0][2] = boltMatrix.matrix[2][0];

 	ent->axis[1][0] = boltMatrix.matrix[0][1];
 	ent->axis[1][1] = boltMatrix.matrix[1][1];
 	ent->axis[1][2] = boltMatrix.matrix[2][1];

 	ent->axis[2][0] = boltMatrix.matrix[0][2];
 	ent->axis[2][1] = boltMatrix.matrix[1][2];
 	ent->axis[2][2] = boltMatrix.matrix[2][2];
}

void ScaleModelAxis(refEntity_t	*ent)

{		// scale the model should we need to
		if (ent->modelScale[0] && ent->modelScale[0] != 1.0f)
		{
			VectorScale( ent->axis[0], ent->modelScale[0] , ent->axis[0] );
			ent->nonNormalizedAxes = qtrue;
		}
		if (ent->modelScale[1] && ent->modelScale[1] != 1.0f)
		{
			VectorScale( ent->axis[1], ent->modelScale[1] , ent->axis[1] );
			ent->nonNormalizedAxes = qtrue;
		}
		if (ent->modelScale[2] && ent->modelScale[2] != 1.0f)
		{
			VectorScale( ent->axis[2], ent->modelScale[2] , ent->axis[2] );
			ent->nonNormalizedAxes = qtrue;
		}
}
/*
Ghoul2 Insert End
*/

char *forceHolocronModels[NUM_FORCE_POWERS] = {
	"models/map_objects/mp/lt_heal.md3",		//FP_HEAL,
	"models/map_objects/mp/force_jump.md3",		//FP_LEVITATION,
	"models/map_objects/mp/force_speed.md3",	//FP_SPEED,
	"models/map_objects/mp/force_push.md3",		//FP_PUSH,
	"models/map_objects/mp/force_pull.md3",		//FP_PULL,
	"models/map_objects/mp/lt_telepathy.md3",	//FP_TELEPATHY,
	"models/map_objects/mp/dk_grip.md3",		//FP_GRIP,
	"models/map_objects/mp/dk_lightning.md3",	//FP_LIGHTNING,
	"models/map_objects/mp/dk_rage.md3",		//FP_RAGE,
	"models/map_objects/mp/lt_protect.md3",		//FP_PROTECT,
	"models/map_objects/mp/lt_absorb.md3",		//FP_ABSORB,
	"models/map_objects/mp/lt_healother.md3",	//FP_TEAM_HEAL,
	"models/map_objects/mp/dk_powerother.md3",	//FP_TEAM_FORCE,
	"models/map_objects/mp/dk_drain.md3",		//FP_DRAIN,
	"models/map_objects/mp/force_sight.md3",	//FP_SEE,
	"models/map_objects/mp/saber_attack.md3",	//FP_SABER_OFFENSE,
	"models/map_objects/mp/saber_defend.md3",	//FP_SABER_DEFENSE,
	"models/map_objects/mp/saber_throw.md3"		//FP_SABERTHROW
};

void CG_Disintegration(centity_t *cent, refEntity_t *ent)
{
	vec3_t tempAng, hitLoc;
	float tempLength;

	VectorCopy(cent->currentState.origin2, hitLoc);

	VectorSubtract( hitLoc, ent->origin, ent->oldorigin );

	tempLength = VectorNormalize( ent->oldorigin );
	vectoangles( ent->oldorigin, tempAng );
	tempAng[YAW] -= cent->lerpAngles[YAW];
	AngleVectors( tempAng, ent->oldorigin, NULL, NULL );
	VectorScale( ent->oldorigin, tempLength, ent->oldorigin );

	ent->endTime = cent->dustTrailTime;

	ent->renderfx |= RF_DISINTEGRATE2;
	ent->customShader = cgs.media.disruptorShader;
	trap->R_AddRefEntityToScene( ent );

	ent->renderfx &= ~(RF_DISINTEGRATE2);
	ent->renderfx |= (RF_DISINTEGRATE1);
	ent->customShader = 0;
	trap->R_AddRefEntityToScene( ent );

	if ( cg.time - ent->endTime < 1000 && (timescale.value * timescale.value * Q_flrand(0.0f, 1.0f)) > 0.05f )
	{
		vec3_t fxOrg, fxDir;
		mdxaBone_t	boltMatrix;
		int torsoBolt = trap->G2API_AddBolt(cent->ghoul2, 0, "lower_lumbar");

		VectorSet(fxDir, 0, 1, 0);

		trap->G2API_GetBoltMatrix( cent->ghoul2, 0, torsoBolt, &boltMatrix, cent->lerpAngles, cent->lerpOrigin, cg.time,
				cgs.gameModels, cent->modelScale);
				BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, fxOrg );

		VectorMA( fxOrg, -18, cg.refdef.viewaxis[0], fxOrg );
		fxOrg[2] += Q_flrand(-1.0f, 1.0f) * 20;
		trap->FX_PlayEffectID( cgs.effects.mDisruptorDeathSmoke, fxOrg, fxDir, -1, -1, qfalse );

		if ( Q_flrand(0.0f, 1.0f) > 0.5f )
		{
			trap->FX_PlayEffectID( cgs.effects.mDisruptorDeathSmoke, fxOrg, fxDir, -1, -1, qfalse );
		}
	}
}

extern int cgSiegeEntityRender;
static qboolean CG_RenderTimeEntBolt(centity_t *cent)
{
	int clientNum = cent->currentState.boltToPlayer-1;
	centity_t *cl;
	mdxaBone_t matrix;
	vec3_t boltOrg, boltAng;
	int getBolt = -1;

	if (clientNum >= MAX_CLIENTS || clientNum < 0)
	{
		assert(0);
		return qfalse;
	}

	cl = &cg_entities[clientNum];

	if (!cl->ghoul2)
	{
		assert(0);
		return qfalse;
	}

	if (clientNum == cg.predictedPlayerState.clientNum &&
		!cg.renderingThirdPerson)
	{ //If in first person and you have it then render the thing spinning around on your hud.
		cgSiegeEntityRender = cent->currentState.number; //set it to render at the end of the frame.
		return qfalse;
	}

	getBolt = trap->G2API_AddBolt(cl->ghoul2, 0, "lhand");

	trap->G2API_GetBoltMatrix(cl->ghoul2, 0, getBolt, &matrix, cl->turAngles, cl->lerpOrigin, cg.time, cgs.gameModels, cl->modelScale);

	BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg);
	BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng);
	vectoangles(boltAng, boltAng);
	boltAng[PITCH] = boltAng[ROLL] = 0;

	VectorCopy(boltOrg, cent->lerpOrigin);
	VectorCopy(boltAng, cent->lerpAngles);

	return qtrue;
}

/*
static void CG_SiegeEntRenderAboveHead(centity_t *cent)
{
	int clientNum = cent->currentState.boltToPlayer-1;
	centity_t *cl;
	refEntity_t ent;
	vec3_t renderAngles;

	if (clientNum >= MAX_CLIENTS || clientNum < 0)
	{
		assert(0);
		return;
	}

	cl = &cg_entities[clientNum];

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

	//Set the angles to the global auto rotating ones, and the origin to slightly above the client
	VectorCopy(cg.autoAngles, renderAngles);
	AnglesToAxis( renderAngles, ent.axis );
	VectorCopy(cl->lerpOrigin, ent.origin);
	ent.origin[2] += 50;

	//Set the model (ghoul2 or md3/other)
	if (cent->ghoul2)
	{
		ent.ghoul2 = cent->ghoul2;
		ent.hModel = 0;
	}
	else
	{
		ent.ghoul2 = NULL;
		ent.hModel = cgs.gameModels[cent->currentState.modelindex];
	}

	//Scale it up
	ent.modelScale[0] = 1.5f;
	ent.modelScale[1] = 1.5f;
	ent.modelScale[2] = 1.5f;
	ScaleModelAxis(&ent);

	//Make it transparent
	ent.renderfx = RF_FORCE_ENT_ALPHA;
	ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = 255;
	ent.shaderRGBA[3] = 100;

	//And finally add it
	trap->R_AddRefEntityToScene(&ent);
}
*/

void CG_AddRadarEnt(centity_t *cent)
{
	static const size_t numRadarEnts = ARRAY_LEN( cg.radarEntities );
	if (cg.radarEntityCount >= numRadarEnts)
	{
#ifdef _DEBUG
		Com_Printf("^3Warning: CG_AddRadarEnt full. (%d max)\n", numRadarEnts);
#endif
		return;
	}
	cg.radarEntities[cg.radarEntityCount++] = cent->currentState.number;
}

void CG_AddBracketedEnt(centity_t *cent)
{
	static const size_t numBracketEnts = ARRAY_LEN( cg.bracketedEntities );
	if (cg.bracketedEntityCount >= numBracketEnts)
	{
#ifdef _DEBUG
		Com_Printf("^3Warning: CG_AddBracketedEnt full. (%d max)\n", numBracketEnts);
#endif
		return;
	}
	cg.bracketedEntities[cg.bracketedEntityCount++] = cent->currentState.number;
}
/*
==================
CG_General
==================
*/
void CG_G2ServerBoneAngles(centity_t *cent);

extern qboolean BG_GetRootSurfNameWithVariant( void *ghoul2, const char *rootSurfName, char *returnSurfName, int returnSize );

static void CG_General( centity_t *cent ) {
	refEntity_t			ent;
	entityState_t		*s1;
	float				val;
	int					beamID;
	vec3_t				beamOrg;
	mdxaBone_t			matrix;
	qboolean			doNotSetModel = qfalse;

	if (cent->currentState.modelGhoul2 == 127)
	{ //not ready to be drawn or initialized..
		return;
	}

	if (cent->ghoul2 && !cent->currentState.modelGhoul2 && cent->currentState.eType != ET_BODY &&
		cent->currentState.number >= MAX_CLIENTS)
	{ //this is a bad thing
		if (trap->G2_HaveWeGhoul2Models(cent->ghoul2))
		{
			trap->G2API_CleanGhoul2Models(&(cent->ghoul2));
		}
	}

	if (cent->currentState.eFlags & EF_RADAROBJECT)
	{
		CG_AddRadarEnt(cent);
	}
	if (cent->currentState.eFlags2 & EF2_BRACKET_ENTITY)
	{
		if ( CG_InFighter() )
		{//only bracken when in a fighter
			CG_AddBracketedEnt(cent);
		}
	}

	if (cent->currentState.boltToPlayer)
	{ //Shove it into the player's left hand then.
		centity_t *pl = &cg_entities[cent->currentState.boltToPlayer-1];
		if (CG_IsMindTricked(pl->currentState.trickedentindex,
			pl->currentState.trickedentindex2,
			pl->currentState.trickedentindex3,
			pl->currentState.trickedentindex4,
			cg.predictedPlayerState.clientNum))
		{ //don't show if this guy is mindtricking
            return;
		}
		if (!CG_RenderTimeEntBolt(cent))
		{ //If this function returns qfalse we shouldn't render this ent at all.
			if (cent->currentState.boltToPlayer > 0 &&
				cent->currentState.boltToPlayer <= MAX_CLIENTS)
			{
				VectorCopy(pl->lerpOrigin, cent->lerpOrigin);

				if (cent->currentState.eFlags & EF_CLIENTSMOOTH)
				{ //if it's set to smooth keep the smoothed lerp origin updated, as we don't want to smooth while bolted.
					VectorCopy(cent->lerpOrigin, cent->turAngles);
				}
			}
			return;
		}

		if (cent->currentState.eFlags & EF_CLIENTSMOOTH)
		{ //if it's set to smooth keep the smoothed lerp origin updated, as we don't want to smooth while bolted.
			VectorCopy(cent->lerpOrigin, cent->turAngles);
		}

/* disabled for now
		if (pl->currentState.number != cg.predictedPlayerState.clientNum)
		{ //don't render thing above head to self
			CG_SiegeEntRenderAboveHead(cent);
		}
*/
	}
	else if (cent->currentState.eFlags & EF_CLIENTSMOOTH)
	{
		if (cent->currentState.groundEntityNum >= ENTITYNUM_WORLD)
		{
			float smoothFactor = 0.5f*timescale.value;
			int k = 0;
			vec3_t posDif;

			//Use origin smoothing since dismembered limbs use ExPhys
			if (DistanceSquared(cent->turAngles,cent->lerpOrigin)>18000.0f)
			{
				VectorCopy(cent->lerpOrigin, cent->turAngles);
			}

			VectorSubtract(cent->lerpOrigin, cent->turAngles, posDif);

			for (k=0;k<3;k++)
			{
				cent->turAngles[k]=(cent->turAngles[k]+posDif[k]*smoothFactor);
				cent->lerpOrigin[k]=cent->turAngles[k];
			}
		}
		else
		{ //if we're sitting on an entity like a moving plat then we don't want to smooth either
			VectorCopy(cent->lerpOrigin, cent->turAngles);
		}
	}

	//rww - now do ragdoll stuff
	if (cent->ghoul2 &&
		(cent->currentState.eType == ET_BODY || (cent->currentState.eFlags & EF_RAG)))
	{
		if (!(cent->currentState.eFlags & EF_NODRAW) &&
			!(cent->currentState.eFlags & EF_DISINTEGRATION) &&
			cent->bodyFadeTime <= cg.time)
		{
			vec3_t forcedAngles;

			VectorClear(forcedAngles);
			forcedAngles[YAW] = cent->lerpAngles[YAW];

			CG_RagDoll(cent, forcedAngles);
		}
	}
	else if (cent->isRagging)
	{
		cent->isRagging = qfalse;

		if (cent->ghoul2 && trap->G2_HaveWeGhoul2Models(cent->ghoul2))
		{ //May not be valid, in the case of a ragged entity being removed and a non-g2 ent filling its slot.
			trap->G2API_SetRagDoll(cent->ghoul2, NULL); //calling with null parms resets to no ragdoll.
		}
	}

	if (cent->currentState.boneOrient && cent->ghoul2)
	{ //server sent us some bone angles to use
		CG_G2ServerBoneAngles(cent);
	}

	if ((cent->currentState.eFlags & EF_G2ANIMATING) && cent->ghoul2)
	{ //mini-animation routine for general objects that want to play quick ghoul2 anims
		//obviously lacks much of the functionality contained in player/npc animation.
		//we actually use torsoAnim as the start frame and legsAnim as the end frame and
		//always play the anim on the root bone.
		if (cent->currentState.torsoAnim != cent->pe.torso.animationNumber ||
			cent->currentState.legsAnim != cent->pe.legs.animationNumber ||
			cent->currentState.torsoFlip != cent->pe.torso.lastFlip)
		{
			trap->G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", cent->currentState.torsoAnim,
				cent->currentState.legsAnim, (BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND), 1.0f, cg.time, -1, 100);

			cent->pe.torso.animationNumber = cent->currentState.torsoAnim;
			cent->pe.legs.animationNumber = cent->currentState.legsAnim;
			cent->pe.torso.lastFlip = cent->currentState.torsoFlip;
		}
	}

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

	ent.shaderRGBA[0] = cent->currentState.customRGBA[0];
	ent.shaderRGBA[1] = cent->currentState.customRGBA[1];
	ent.shaderRGBA[2] = cent->currentState.customRGBA[2];
	ent.shaderRGBA[3] = cent->currentState.customRGBA[3];

	if (cent->currentState.modelGhoul2 >= G2_MODELPART_HEAD &&
		cent->currentState.modelGhoul2 <= G2_MODELPART_RLEG &&
		/*cent->currentState.modelindex < MAX_CLIENTS &&*/
		cent->currentState.weapon == G2_MODEL_PART)
	{ //special case for client limbs
		centity_t *clEnt;
		int dismember_settings = cg_dismember.integer;
		float smoothFactor = 0.5f*timescale.value;
		int k = 0;
		vec3_t posDif;

		doNotSetModel = qtrue;

		if (cent->currentState.modelindex >= 0)
		{
			clEnt = &cg_entities[cent->currentState.modelindex];
		}
		else
		{
			clEnt = &cg_entities[cent->currentState.otherEntityNum2];
		}

		if (!dismember_settings)
		{ //This client does not wish to see dismemberment.
			return;
		}

		if (dismember_settings < 2 && (cent->currentState.modelGhoul2 == G2_MODELPART_HEAD || cent->currentState.modelGhoul2 == G2_MODELPART_WAIST))
		{ //dismember settings are not high enough to display decaps and torso slashes
			return;
		}

		if (!cent->ghoul2)
		{
			const char *rotateBone;
			char	limbName[MAX_QPATH];
			char	stubName[MAX_QPATH];
			char	limbCapName[MAX_QPATH];
			char	stubCapName[MAX_QPATH];
			char *limbTagName;
			char *stubTagName;
			int newBolt;
			int limbBit = (1 << (cent->currentState.modelGhoul2-10));

			if (clEnt && (clEnt->torsoBolt & limbBit))
			{ //already have this limb missing!
				return;
			}


			if (clEnt && !(clEnt->currentState.eFlags & EF_DEAD))
			{ //death flag hasn't made it through yet for the limb owner, we cannot create the limb until he's flagged as dead
				return;
			}

			if (clEnt && (!BG_InDeathAnim(clEnt->currentState.torsoAnim) || !BG_InDeathAnim(clEnt->pe.torso.animationNumber)))
			{ //don't make it unless we're in an actual death anim already
				if (clEnt->currentState.torsoAnim != BOTH_RIGHTHANDCHOPPEDOFF)
				{ //exception
					return;
				}
			}

			cent->bolt4 = -1;
			cent->trailTime = 0;

			if (cent->currentState.modelGhoul2 == G2_MODELPART_HEAD)
			{
				rotateBone = "cranium";
				Q_strncpyz( limbName , "head", sizeof( limbName  ) );
				Q_strncpyz( limbCapName, "head_cap_torso", sizeof( limbCapName ) );
				Q_strncpyz( stubCapName, "torso_cap_head", sizeof( stubCapName ) );
				limbTagName = "*head_cap_torso";
				stubTagName = "*torso_cap_head";
			}
			else if (cent->currentState.modelGhoul2 == G2_MODELPART_WAIST)
			{

				if (clEnt->localAnimIndex <= 1)
				{ //humanoid/rtrooper
					rotateBone = "thoracic";
				}
				else
				{
					rotateBone = "pelvis";
				}
				Q_strncpyz( limbName, "torso", sizeof( limbName ) );
				Q_strncpyz( limbCapName, "torso_cap_hips", sizeof( limbCapName ) );
				Q_strncpyz( stubCapName, "hips_cap_torso", sizeof( stubCapName ) );
				limbTagName = "*torso_cap_hips";
				stubTagName = "*hips_cap_torso";
			}
			else if (cent->currentState.modelGhoul2 == G2_MODELPART_LARM)
			{
				rotateBone = "lradius";
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "l_arm", limbName, sizeof(limbName) );
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "torso", stubName, sizeof(stubName) );
				Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_torso", limbName );
				Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_arm", stubName );
				limbTagName = "*l_arm_cap_torso";
				stubTagName = "*torso_cap_l_arm";
			}
			else if (cent->currentState.modelGhoul2 == G2_MODELPART_RARM)
			{
				rotateBone = "rradius";
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_arm", limbName, sizeof(limbName) );
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "torso", stubName, sizeof(stubName) );
				Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_torso", limbName );
				Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_arm", stubName );
				limbTagName = "*r_arm_cap_torso";
				stubTagName = "*torso_cap_r_arm";
			}
			else if (cent->currentState.modelGhoul2 == G2_MODELPART_RHAND)
			{
				rotateBone = "rhand";
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_hand", limbName, sizeof(limbName) );
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_arm", stubName, sizeof(stubName) );
				Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_r_arm", limbName );
				Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_hand", stubName );
				limbTagName = "*r_hand_cap_r_arm";
				stubTagName = "*r_arm_cap_r_hand";
			}
			else if (cent->currentState.modelGhoul2 == G2_MODELPART_LLEG)
			{
				rotateBone = "ltibia";
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "l_leg", limbName, sizeof(limbName) );
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "hips", stubName, sizeof(stubName) );
				Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_hips", limbName );
				Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_leg", stubName );
				limbTagName = "*l_leg_cap_hips";
				stubTagName = "*hips_cap_l_leg";
			}
			else if (cent->currentState.modelGhoul2 == G2_MODELPART_RLEG)
			{
				rotateBone = "rtibia";
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_leg", limbName, sizeof(limbName) );
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "hips", stubName, sizeof(stubName) );
				Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_hips", limbName );
				Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_leg", stubName );
				limbTagName = "*r_leg_cap_hips";
				stubTagName = "*hips_cap_r_leg";
			}
			else
			{//umm... just default to the right leg, I guess (same as on server)
				rotateBone = "rtibia";
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_leg", limbName, sizeof(limbName) );
				BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "hips", stubName, sizeof(stubName) );
				Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_hips", limbName );
				Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_leg", stubName );
				limbTagName = "*r_leg_cap_hips";
				stubTagName = "*hips_cap_r_leg";
			}

			if (clEnt && clEnt->ghoul2)
			{
				if (trap->G2API_HasGhoul2ModelOnIndex(&(clEnt->ghoul2), 2))
				{ //don't want to bother dealing with a second saber on limbs and stuff, just remove the thing
					trap->G2API_RemoveGhoul2Model(&(clEnt->ghoul2), 2);
				}

				if (trap->G2API_HasGhoul2ModelOnIndex(&(clEnt->ghoul2), 3))
				{ //turn off jetpack also I suppose
					trap->G2API_RemoveGhoul2Model(&(clEnt->ghoul2), 3);
				}

				if (clEnt->localAnimIndex <= 0)
				{ //humanoid
					trap->G2API_SetBoneAngles(clEnt->ghoul2, 0, "model_root", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
					trap->G2API_SetBoneAngles(clEnt->ghoul2, 0, "pelvis", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
					trap->G2API_SetBoneAngles(clEnt->ghoul2, 0, "thoracic", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
					trap->G2API_SetBoneAngles(clEnt->ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
					trap->G2API_SetBoneAngles(clEnt->ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
					trap->G2API_SetBoneAngles(clEnt->ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, cgs.gameModels, 100, cg.time);
				}
				else
				{
					trap->G2API_SetBoneAngles(clEnt->ghoul2, 0, "model_root", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
					trap->G2API_SetBoneAngles(clEnt->ghoul2, 0, "pelvis", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
					trap->G2API_SetBoneAngles(clEnt->ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
					trap->G2API_SetBoneAngles(clEnt->ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
				}

				trap->G2API_DuplicateGhoul2Instance(clEnt->ghoul2, &cent->ghoul2);
			}

			if (!cent->ghoul2)
			{
				return;
			}

			newBolt = trap->G2API_AddBolt( cent->ghoul2, 0, limbTagName );
			if ( newBolt != -1 )
			{
				vec3_t boltOrg, boltAng;

				trap->G2API_GetBoltMatrix(cent->ghoul2, 0, newBolt, &matrix, cent->lerpAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);

				BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg);
				BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng);

				trap->FX_PlayEffectID(cgs.effects.mBlasterSmoke, boltOrg, boltAng, -1, -1, qfalse);
			}

			cent->bolt4 = newBolt;

			trap->G2API_SetRootSurface(cent->ghoul2, 0, limbName);

			trap->G2API_SetNewOrigin(cent->ghoul2, trap->G2API_AddBolt(cent->ghoul2, 0, rotateBone));

			trap->G2API_SetSurfaceOnOff(cent->ghoul2, limbCapName, 0);

			trap->G2API_SetSurfaceOnOff(clEnt->ghoul2, limbName, 0x00000100);
			trap->G2API_SetSurfaceOnOff(clEnt->ghoul2, stubCapName, 0);

			newBolt = trap->G2API_AddBolt( clEnt->ghoul2, 0, stubTagName );
			if ( newBolt != -1 )
			{
				vec3_t boltOrg, boltAng;

				trap->G2API_GetBoltMatrix(clEnt->ghoul2, 0, newBolt, &matrix, clEnt->lerpAngles, clEnt->lerpOrigin, cg.time, cgs.gameModels, clEnt->modelScale);

				BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg);
				BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng);

				trap->FX_PlayEffectID(cgs.effects.mBlasterSmoke, boltOrg, boltAng, -1, -1, qfalse);
			}

			if (cent->currentState.modelGhoul2 == G2_MODELPART_RARM || cent->currentState.modelGhoul2 == G2_MODELPART_RHAND || cent->currentState.modelGhoul2 == G2_MODELPART_WAIST)
			{ //Cut his weapon holding arm off, so remove the weapon
				if (trap->G2API_HasGhoul2ModelOnIndex(&(clEnt->ghoul2), 1))
				{
					trap->G2API_RemoveGhoul2Model(&(clEnt->ghoul2), 1);
				}
			}

			clEnt->torsoBolt |= limbBit; //reinit model after copying limbless one to queue
			//This causes issues after respawning.. just keep track of limbs cut/made on server or something.
			/*
			if (cent->currentState.modelGhoul2 == G2_MODELPART_WAIST)
			{
				clEnt->torsoBolt |= (1 << (G2_MODELPART_HEAD-10));
				clEnt->torsoBolt |= (1 << (G2_MODELPART_RARM-10));
				clEnt->torsoBolt |= (1 << (G2_MODELPART_LARM-10));
				clEnt->torsoBolt |= (1 << (G2_MODELPART_RHAND-10));
			}
			else if (cent->currentState.modelGhoul2 == G2_MODELPART_RARM)
			{
				clEnt->torsoBolt |= (1 << (G2_MODELPART_RHAND-10));
			}
			*/

			VectorCopy(cent->lerpOrigin, cent->turAngles);
		//	return;
		}

		//Use origin smoothing since dismembered limbs use ExPhys
		if (DistanceSquared(cent->turAngles,cent->lerpOrigin)>18000.0f)
		{
			VectorCopy(cent->lerpOrigin, cent->turAngles);
		}

		VectorSubtract(cent->lerpOrigin, cent->turAngles, posDif);

		for (k=0;k<3;k++)
		{
			cent->turAngles[k]=(cent->turAngles[k]+posDif[k]*smoothFactor);
			cent->lerpOrigin[k]=cent->turAngles[k];
		}

		if (cent->ghoul2 && cent->bolt4 != -1 && cent->trailTime < cg.time)
		{
			if ( cent->bolt4 != -1 &&
				(cent->currentState.pos.trDelta[0] || cent->currentState.pos.trDelta[1] || cent->currentState.pos.trDelta[2]) )
			{
				vec3_t boltOrg, boltAng;

				trap->G2API_GetBoltMatrix(cent->ghoul2, 0, cent->bolt4, &matrix, cent->lerpAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);

				BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg);
				BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng);

				if (!boltAng[0] && !boltAng[1] && !boltAng[2])
				{
					boltAng[1] = 1;
				}
				trap->FX_PlayEffectID(cgs.effects.mBlasterSmoke, boltOrg, boltAng, -1, -1, qfalse);

				cent->trailTime = cg.time + 400;
			}
		}

		ent.radius = cent->currentState.g2radius;
		ent.hModel = 0;
	}

	if (cent->currentState.number >= MAX_CLIENTS &&
		cent->currentState.activeForcePass == NUM_FORCE_POWERS+1&&
		cent->currentState.NPC_class != CLASS_VEHICLE )
	{
		vec3_t				empAngles;
		centity_t			*empOwn;

		empOwn = &cg_entities[cent->currentState.emplacedOwner];

		if (cg.snap->ps.clientNum == empOwn->currentState.number &&
			!cg.renderingThirdPerson)
		{
			VectorCopy(cg.refdef.viewangles, empAngles);
		}
		else
		{
			VectorCopy(empOwn->lerpAngles, empAngles);
		}

		if (empAngles[PITCH] > 40)
		{
			empAngles[PITCH] = 40;
		}
		empAngles[YAW] -= cent->currentState.angles[YAW];

		trap->G2API_SetBoneAngles( cent->ghoul2, 0, "Bone02", empAngles, BONE_ANGLES_REPLACE, NEGATIVE_Y, NEGATIVE_X, POSITIVE_Z, NULL, 0, cg.time);
	}

	s1 = &cent->currentState;

	// if set to invisible, skip
	if ((!s1->modelindex) && !(trap->G2_HaveWeGhoul2Models(cent->ghoul2)))
	{
		return;
	}

	if ( ( s1->eFlags & EF_NODRAW ) )
	{
		return;
	}

	// set frame
	if ( s1->eFlags & EF_SHADER_ANIM )
	{
		// Deliberately setting it up so that shader anim will completely override any kind of model animation frame setting.
		ent.renderfx|=RF_SETANIMINDEX;
		ent.skinNum = s1->frame;
	}
	else
	{
		ent.frame = s1->frame;
	}
	ent.oldframe = ent.frame;
	ent.backlerp = 0;

/*
Ghoul2 Insert Start
*/
	CG_SetGhoul2Info(&ent, cent);

/*
Ghoul2 Insert End
*/
	VectorCopy( cent->lerpOrigin, ent.origin);
	VectorCopy( cent->lerpOrigin, ent.oldorigin);

	if (cent->currentState.modelGhoul2)
	{ //If the game says this guy uses a ghoul2 model and the g2 instance handle is null, then initialize it
		if (!cent->ghoul2 && !cent->currentState.bolt1)
		{
			char skinName[MAX_QPATH];
			const char *modelName = CG_ConfigString( CS_MODELS+cent->currentState.modelindex );
			int l;
			int skin = 0;

			trap->G2API_InitGhoul2Model(&cent->ghoul2, modelName, 0, 0, 0, 0, 0);
			if (cent->ghoul2 && trap->G2API_SkinlessModel(cent->ghoul2, 0))
			{ //well, you'd never want a skinless model, so try to get his skin...
				Q_strncpyz(skinName, modelName, MAX_QPATH);
				l = strlen(skinName);
				while (l > 0 && skinName[l] != '/')
				{ //parse back to first /
					l--;
				}
				if (skinName[l] == '/')
				{ //got it
					l++;
					skinName[l] = 0;
					Q_strcat(skinName, MAX_QPATH, "model_default.skin");

					skin = trap->R_RegisterSkin(skinName);
				}
				trap->G2API_SetSkin(cent->ghoul2, 0, skin, skin);
			}
		}
		else if (cent->currentState.bolt1)
		{
			TurretClientRun(cent);
		}

		if (cent->ghoul2)
		{ //give us a proper radius
			ent.radius = cent->currentState.g2radius;
		}
	}

	if (s1->eType == ET_BODY)
	{ //bodies should have a radius as well
		ent.radius = cent->currentState.g2radius;

		if (cent->ghoul2)
		{ //all bodies should already have a ghoul2 instance. Use it to set the torso/head angles to 0.
			cent->lerpAngles[PITCH] = 0;
			cent->lerpAngles[ROLL] = 0;
			trap->G2API_SetBoneAngles(cent->ghoul2, 0, "pelvis", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
			trap->G2API_SetBoneAngles(cent->ghoul2, 0, "thoracic", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
			trap->G2API_SetBoneAngles(cent->ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
			trap->G2API_SetBoneAngles(cent->ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
			trap->G2API_SetBoneAngles(cent->ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, cgs.gameModels, 100, cg.time);
		}
	}

	if (s1->eType == ET_HOLOCRON && s1->modelindex < -100)
	{ //special render, it's a holocron
		//Using actual models now:
		ent.hModel = trap->R_RegisterModel(forceHolocronModels[s1->modelindex+128]);

		//Rotate them
		VectorCopy( cg.autoAngles, cent->lerpAngles );
		AxisCopy( cg.autoAxis, ent.axis );
	}
	else if (!doNotSetModel)
	{
		ent.hModel = cgs.gameModels[s1->modelindex];
	}

	// player model
	if (s1->number == cg.snap->ps.clientNum) {
		ent.renderfx |= RF_THIRD_PERSON;	// only draw from mirrors
	}

	// convert angles to axis
	AnglesToAxis( cent->lerpAngles, ent.axis );

	if (cent->currentState.iModelScale)
	{ //if the server says we have a custom scale then set it now.
		cent->modelScale[0] = cent->modelScale[1] = cent->modelScale[2] = cent->currentState.iModelScale/100.0f;
		VectorCopy(cent->modelScale, ent.modelScale);
		ScaleModelAxis(&ent);
	}
	else
	{
		VectorClear(cent->modelScale);
	}

	if ( cent->currentState.time > cg.time && cent->currentState.weapon == WP_EMPLACED_GUN )
	{
		// make the gun pulse red to warn about it exploding
		val = (1.0f - (float)(cent->currentState.time - cg.time) / 3200.0f ) * 0.3f;

		ent.customShader = trap->R_RegisterShader( "gfx/effects/turretflashdie" );
		ent.shaderRGBA[0] = (sin( cg.time * 0.04f ) * val * 0.4f + val) * 255;
		ent.shaderRGBA[1] = ent.shaderRGBA[2] = 0;

		ent.shaderRGBA[3] = 100;
		trap->R_AddRefEntityToScene( &ent );
		ent.customShader = 0;
	}
	else if ( cent->currentState.time == -1 && cent->currentState.weapon == WP_EMPLACED_GUN)
	{
		ent.customShader = trap->R_RegisterShader( "models/map_objects/imp_mine/turret_chair_dmg.tga" );
		//trap->R_AddRefEntityToScene( &ent );
	}

	if ((cent->currentState.eFlags & EF_DISINTEGRATION) && cent->currentState.eType == ET_BODY)
	{
		if (!cent->dustTrailTime)
		{
			cent->dustTrailTime = cg.time;
		}

		CG_Disintegration(cent, &ent);
		return;
	}
	else if (cent->currentState.eType == ET_BODY)
	{
		if (cent->bodyFadeTime > cg.time)
		{
			qboolean lightSide = (cent->teamPowerType != 0) ? qtrue : qfalse;
			vec3_t hitLoc, tempAng;
			float tempLength;
			int curTimeDif = ((cg.time + 60000) - cent->bodyFadeTime);
			int tMult = curTimeDif*0.08;

			ent.renderfx |= RF_FORCE_ENT_ALPHA;

			/*
			if (!cent->bodyHeight)
			{
				cent->bodyHeight = ent.origin[2];
			}
			*/

			if (curTimeDif*0.1 > 254)
			{
				ent.shaderRGBA[3] = 0;
			}
			else
			{
				ent.shaderRGBA[3] = (254 - tMult);
			}

			if (ent.shaderRGBA[3] >= 1)
			{ //add the transparent body section
				trap->R_AddRefEntityToScene (&ent);
			}

			ent.renderfx &= ~RF_FORCE_ENT_ALPHA;
			ent.renderfx |= RF_RGB_TINT;

			if (tMult > 200)
			{ //begin the disintegration effect
				ent.shaderRGBA[3] = 200;
				if (!cent->dustTrailTime)
				{
					cent->dustTrailTime = cg.time;
					if (lightSide)
					{
						trap->S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, trap->S_RegisterSound("sound/weapons/force/see.wav") );
					}
					else
					{
						trap->S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, trap->S_RegisterSound("sound/weapons/force/lightning") );
					}
				}
				ent.endTime = cent->dustTrailTime;
				ent.renderfx |= RF_DISINTEGRATE2;
			}
			else
			{ //set the alpha on the to-be-disintegrated layer
				ent.shaderRGBA[3] = tMult;
				if (ent.shaderRGBA[3] < 1)
				{
					ent.shaderRGBA[3] = 1;
				}
			}
			//Set everything up on the disint ref
			ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = ent.shaderRGBA[3];
			VectorCopy(cent->lerpOrigin, hitLoc);

			VectorSubtract( hitLoc, ent.origin, ent.oldorigin );

			tempLength = VectorNormalize( ent.oldorigin );
			vectoangles( ent.oldorigin, tempAng );
			tempAng[YAW] -= cent->lerpAngles[YAW];
			AngleVectors( tempAng, ent.oldorigin, NULL, NULL );
			VectorScale( ent.oldorigin, tempLength, ent.oldorigin );

			if (lightSide)
			{ //might be temporary, dunno.
				ent.customShader = cgs.media.playerShieldDamage;
			}
			else
			{
				ent.customShader = cgs.media.redSaberGlowShader;
			}

			//slowly move the glowing part upward, out of the fading body
			/*
			cent->bodyHeight += 0.4f;
			ent.origin[2] = cent->bodyHeight;
			*/

			trap->R_AddRefEntityToScene( &ent );
			ent.renderfx &= ~RF_DISINTEGRATE2;
			ent.customShader = 0;

			if (curTimeDif < 3400)
			{
				if (lightSide)
				{
					if (curTimeDif < 2200)
					{ //probably temporary
						trap->S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, trap->S_RegisterSound( "sound/weapons/saber/saberhum1.wav" ) );
					}
				}
				else
				{ //probably temporary as well
					ent.renderfx |= RF_RGB_TINT;
					ent.shaderRGBA[0] = 255;
					ent.shaderRGBA[1] = ent.shaderRGBA[2] = 0;
					ent.shaderRGBA[3] = 255;
					if ( rand() & 1 )
					{
						ent.customShader = cgs.media.electricBodyShader;
					}
					else
					{
						ent.customShader = cgs.media.electricBody2Shader;
					}
					if ( Q_flrand(0.0f, 1.0f) > 0.9f )
					{
						trap->S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, cgs.media.crackleSound );
					}
					trap->R_AddRefEntityToScene( &ent );
				}
			}

			return;
		}
		else
		{
			cent->dustTrailTime = 0;
		}
	}

	if (cent->currentState.modelGhoul2 &&
		!ent.ghoul2 &&
		!ent.hModel)
	{
		return;
	}

	// add to refresh list
	trap->R_AddRefEntityToScene (&ent);

	if (cent->bolt3 == 999)
	{ //this is an in-flight saber being rendered manually
		vec3_t org;
		float wv;
		int i;
		addspriteArgStruct_t fxSArgs;
		//refEntity_t sRef;
		//memcpy( &sRef, &ent, sizeof( sRef ) );

		ent.customShader = cgs.media.solidWhite;
		ent.renderfx = RF_RGB_TINT;
		wv = sin( cg.time * 0.003f ) * 0.08f + 0.1f;
		ent.shaderRGBA[0] = wv * 255;
		ent.shaderRGBA[1] = wv * 255;
		ent.shaderRGBA[2] = wv * 0;
		trap->R_AddRefEntityToScene (&ent);

		for ( i = -4; i < 10; i += 1 )
		{
			VectorMA( ent.origin, -i, ent.axis[2], org );

			VectorCopy(org, fxSArgs.origin);
			VectorClear(fxSArgs.vel);
			VectorClear(fxSArgs.accel);
			fxSArgs.scale = 5.5f;
			fxSArgs.dscale = 5.5f;
			fxSArgs.sAlpha = wv;
			fxSArgs.eAlpha = wv;
			fxSArgs.rotation = 0.0f;
			fxSArgs.bounce = 0.0f;
			fxSArgs.life = 1.0f;
			fxSArgs.shader = cgs.media.yellowDroppedSaberShader;
			fxSArgs.flags = 0x08000000;

			//trap->FX_AddSprite( org, NULL, NULL, 5.5f, 5.5f, wv, wv, 0.0f, 0.0f, 1.0f, cgs.media.yellowSaberGlowShader, 0x08000000 );
			trap->FX_AddSprite(&fxSArgs);
		}
	}
	else if (cent->currentState.trickedentindex3)
	{ //holocron special effects
		vec3_t org;
		float wv;
		addspriteArgStruct_t fxSArgs;
		//refEntity_t sRef;
		//memcpy( &sRef, &ent, sizeof( sRef ) );

		ent.customShader = cgs.media.solidWhite;
		ent.renderfx = RF_RGB_TINT;
		wv = sin( cg.time * 0.005f ) * 0.08f + 0.1f; //* 0.08f + 0.1f;

		if (cent->currentState.trickedentindex3 == 1)
		{ //dark
			ent.shaderRGBA[0] = wv*255;
			ent.shaderRGBA[1] = 0;
			ent.shaderRGBA[2] = 0;
		}
		else if (cent->currentState.trickedentindex3 == 2)
		{ //light
			ent.shaderRGBA[0] = wv*255;
			ent.shaderRGBA[1] = wv*255;
			ent.shaderRGBA[2] = wv*255;
		}
		else
		{ //neutral
			if ((s1->modelindex+128) == FP_SABER_OFFENSE ||
				(s1->modelindex+128) == FP_SABER_DEFENSE ||
				(s1->modelindex+128) == FP_SABERTHROW)
			{ //saber power
				ent.shaderRGBA[0] = 0;
				ent.shaderRGBA[1] = wv*255;
				ent.shaderRGBA[2] = 0;
			}
			else
			{
				ent.shaderRGBA[0] = 0;
				ent.shaderRGBA[1] = wv*255;
				ent.shaderRGBA[2] = wv*255;
			}
		}

		ent.modelScale[0] = 1.1f;
		ent.modelScale[1] = 1.1f;
		ent.modelScale[2] = 1.1f;

		ent.origin[2] -= 2;
		ScaleModelAxis(&ent);

		trap->R_AddRefEntityToScene (&ent);

		VectorMA( ent.origin, 1, ent.axis[2], org );

		org[2] += 18;

		wv = sin( cg.time * 0.002f ) * 0.08f + 0.1f; //* 0.08f + 0.1f;

		VectorCopy(org, fxSArgs.origin);
		VectorClear(fxSArgs.vel);
		VectorClear(fxSArgs.accel);
		fxSArgs.scale = wv*120;//16.0f;
		fxSArgs.dscale = wv*120;//16.0f;
		fxSArgs.sAlpha = wv*12;
		fxSArgs.eAlpha = wv*12;
		fxSArgs.rotation = 0.0f;
		fxSArgs.bounce = 0.0f;
		fxSArgs.life = 1.0f;

		fxSArgs.flags = 0x08000000|0x00000001;

		if (cent->currentState.trickedentindex3 == 1)
		{ //dark
			fxSArgs.sAlpha *= 3;
			fxSArgs.eAlpha *= 3;
			fxSArgs.shader = cgs.media.redSaberGlowShader;
			trap->FX_AddSprite(&fxSArgs);
		}
		else if (cent->currentState.trickedentindex3 == 2)
		{ //light
			fxSArgs.sAlpha *= 1.5;
			fxSArgs.eAlpha *= 1.5;
			fxSArgs.shader = cgs.media.redSaberGlowShader;
			trap->FX_AddSprite(&fxSArgs);
			fxSArgs.shader = cgs.media.greenSaberGlowShader;
			trap->FX_AddSprite(&fxSArgs);
			fxSArgs.shader = cgs.media.blueSaberGlowShader;
			trap->FX_AddSprite(&fxSArgs);
		}
		else
		{ //neutral
			if ((s1->modelindex+128) == FP_SABER_OFFENSE ||
				(s1->modelindex+128) == FP_SABER_DEFENSE ||
				(s1->modelindex+128) == FP_SABERTHROW)
			{ //saber power
				fxSArgs.sAlpha *= 1.5;
				fxSArgs.eAlpha *= 1.5;
				fxSArgs.shader = cgs.media.greenSaberGlowShader;
				trap->FX_AddSprite(&fxSArgs);
			}
			else
			{
				fxSArgs.sAlpha *= 0.5;
				fxSArgs.eAlpha *= 0.5;
				fxSArgs.shader = cgs.media.greenSaberGlowShader;
				trap->FX_AddSprite(&fxSArgs);
				fxSArgs.shader = cgs.media.blueSaberGlowShader;
				trap->FX_AddSprite(&fxSArgs);
			}
		}
	}

	if ( cent->currentState.time == -1 && cent->currentState.weapon == WP_TRIP_MINE && (cent->currentState.eFlags & EF_FIRING) )
	{ //if force sight is active, render the laser multiple times up to the force sight level to increase visibility
		if (cent->currentState.bolt2 == 1)
		{
			VectorMA( ent.origin, 6.6f, ent.axis[0], beamOrg );// forward
			beamID = cgs.effects.tripmineGlowFX;
			trap->FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1, qfalse );
		}
		else
		{
			int i = 0;

			VectorMA( ent.origin, 6.6f, ent.axis[0], beamOrg );// forward
			beamID = cgs.effects.tripmineLaserFX;

			if (cg.snap->ps.fd.forcePowersActive & (1 << FP_SEE))
			{
				i = cg.snap->ps.fd.forcePowerLevel[FP_SEE];

				while (i > 0)
				{
					trap->FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1, qfalse );
					trap->FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1, qfalse );
					i--;
				}
			}

			trap->FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1, qfalse );
		}
	}
/*
Ghoul2 Insert Start
*/

	if (debugBB.integer)
	{
		CG_CreateBBRefEnts(s1, cent->lerpOrigin);
	}
/*
Ghoul2 Insert End
*/
}

/*
==================
CG_Speaker

Speaker entities can automatically play sounds
==================
*/
static void CG_Speaker( centity_t *cent ) {
	if (cent->currentState.trickedentindex)
	{
		CG_S_StopLoopingSound(cent->currentState.number, -1);
	}

	if ( ! cent->currentState.clientNum ) {	// FIXME: use something other than clientNum...
		return;		// not auto triggering
	}

	if ( cg.time < cent->miscTime ) {
		return;
	}

	trap->S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] );

	//	ent->s.frame = ent->wait * 10;
	//	ent->s.clientNum = ent->random * 10;
	cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * Q_flrand(-1.0f, 1.0f);
}

qboolean CG_GreyItem(int type, int tag, int plSide)
{
	if (type == IT_POWERUP &&
		(tag == PW_FORCE_ENLIGHTENED_LIGHT || tag == PW_FORCE_ENLIGHTENED_DARK))
	{
		if (plSide == FORCE_LIGHTSIDE)
		{
			if (tag == PW_FORCE_ENLIGHTENED_DARK)
			{
				return qtrue;
			}
		}
		else if (plSide == FORCE_DARKSIDE)
		{
			if (tag == PW_FORCE_ENLIGHTENED_LIGHT)
			{
				return qtrue;
			}
		}
	}

	return qfalse;
}

/*
==================
CG_Item
==================
*/
static void CG_Item( centity_t *cent ) {
	refEntity_t		ent;
	entityState_t	*es;
	gitem_t			*item;
	int				msec;
	float			scale;
	weaponInfo_t	*wi;

	es = &cent->currentState;
	if ( es->modelindex >= bg_numItems ) {
		trap->Error( ERR_DROP, "Bad item index %i on entity", es->modelindex );
	}

/*
Ghoul2 Insert Start
*/

	if ((es->eFlags & EF_NODRAW) && (es->eFlags & EF_ITEMPLACEHOLDER))
	{
		es->eFlags &= ~EF_NODRAW;
	}

	if ( !es->modelindex )
	{
		return;
	}

	item = &bg_itemlist[ es->modelindex ];

	if ((item->giType == IT_WEAPON || item->giType == IT_POWERUP) &&
		!(cent->currentState.eFlags & EF_DROPPEDWEAPON) &&
		!cg_simpleItems.integer)
	{
		vec3_t uNorm;
		qboolean doGrey;

		VectorClear(uNorm);

		uNorm[2] = 1;

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

		ent.customShader = 0;
		VectorCopy(cent->lerpOrigin, ent.origin);
		VectorCopy( cent->currentState.angles, cent->lerpAngles );
		AnglesToAxis(cent->lerpAngles, ent.axis);
		ent.hModel = cgs.media.itemHoloModel;

		doGrey = CG_GreyItem(item->giType, item->giTag, cg.snap->ps.fd.forceSide);

		if (doGrey)
		{
			ent.renderfx |= RF_RGB_TINT;

			ent.shaderRGBA[0] = 150;
			ent.shaderRGBA[1] = 150;
			ent.shaderRGBA[2] = 150;
		}

		trap->R_AddRefEntityToScene(&ent);

		if (!doGrey)
		{
			trap->FX_PlayEffectID(cgs.effects.itemCone, ent.origin, uNorm, -1, -1, qfalse);
		}
	}

	// if set to invisible, skip
	if ( ( es->eFlags & EF_NODRAW ) )
	{
		return;
	}
/*
Ghoul2 Insert End
*/

	if ( cg_simpleItems.integer && item->giType != IT_TEAM ) {
		memset( &ent, 0, sizeof( ent ) );
		ent.reType = RT_SPRITE;
		VectorCopy( cent->lerpOrigin, ent.origin );
		ent.radius = 14;
		ent.customShader = cg_items[es->modelindex].icon;
		ent.shaderRGBA[0] = 255;
		ent.shaderRGBA[1] = 255;
		ent.shaderRGBA[2] = 255;

		ent.origin[2] += 16;

		if (item->giType != IT_POWERUP || item->giTag != PW_FORCE_BOON)
		{
			ent.renderfx |= RF_FORCE_ENT_ALPHA;
		}

		if ( es->eFlags & EF_ITEMPLACEHOLDER )
		{
			if (item->giType == IT_POWERUP && item->giTag == PW_FORCE_BOON)
			{
				return;
			}
			ent.shaderRGBA[0] = 200;
			ent.shaderRGBA[1] = 200;
			ent.shaderRGBA[2] = 200;
			ent.shaderRGBA[3] = 150 + sin(cg.time*0.01)*30;
		}
		else
		{
			ent.shaderRGBA[3] = 255;
		}

		if (CG_GreyItem(item->giType, item->giTag, cg.snap->ps.fd.forceSide))
		{
			ent.shaderRGBA[0] = 100;
			ent.shaderRGBA[1] = 100;
			ent.shaderRGBA[2] = 100;

			ent.shaderRGBA[3] = 200;

			if (item->giTag == PW_FORCE_ENLIGHTENED_LIGHT)
			{
				ent.customShader = trap->R_RegisterShader("gfx/misc/mp_light_enlight_disable");
			}
			else
			{
				ent.customShader = trap->R_RegisterShader("gfx/misc/mp_dark_enlight_disable");
			}
		}
		trap->R_AddRefEntityToScene(&ent);
		return;
	}

	if ((item->giType == IT_WEAPON || item->giType == IT_POWERUP) &&
		!(cent->currentState.eFlags & EF_DROPPEDWEAPON))
	{
		cent->lerpOrigin[2] += 16;
	}

	if ((!(cent->currentState.eFlags & EF_DROPPEDWEAPON) || item->giType == IT_POWERUP) &&
		(item->giType == IT_WEAPON || item->giType == IT_POWERUP))
	{
		// items bob up and down continuously
		scale = 0.005 + cent->currentState.number * 0.00001;
		cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) *  scale ) * 4;
	}
	else
	{
		if (item->giType == IT_HOLDABLE)
		{
			if (item->giTag == HI_SEEKER)
			{
				cent->lerpOrigin[2] += 5;
			}
			if (item->giTag == HI_SHIELD)
			{
				cent->lerpOrigin[2] += 2;
			}
			if (item->giTag == HI_BINOCULARS)
			{
				cent->lerpOrigin[2] += 2;
			}
		}
		if (item->giType == IT_HEALTH)
		{
			cent->lerpOrigin[2] += 2;
		}
		if (item->giType == IT_ARMOR)
		{
			if (item->quantity == 100)
			{
				cent->lerpOrigin[2] += 7;
			}
		}
	}

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

	if ( (!(cent->currentState.eFlags & EF_DROPPEDWEAPON) || item->giType == IT_POWERUP) &&
		(item->giType == IT_WEAPON || item->giType == IT_POWERUP) )
	{ //only weapons and powerups rotate now
		// autorotate at one of two speeds
		VectorCopy( cg.autoAngles, cent->lerpAngles );
		AxisCopy( cg.autoAxis, ent.axis );
	}
	else
	{
		VectorCopy( cent->currentState.angles, cent->lerpAngles );
		AnglesToAxis(cent->lerpAngles, ent.axis);
	}

	wi = NULL;
	// the weapons have their origin where they attatch to player
	// models, so we need to offset them or they will rotate
	// eccentricly
	if (!(cent->currentState.eFlags & EF_DROPPEDWEAPON))
	{
		if ( item->giType == IT_WEAPON ) {
			wi = &cg_weapons[item->giTag];
			cent->lerpOrigin[0] -=
				wi->weaponMidpoint[0] * ent.axis[0][0] +
				wi->weaponMidpoint[1] * ent.axis[1][0] +
				wi->weaponMidpoint[2] * ent.axis[2][0];
			cent->lerpOrigin[1] -=
				wi->weaponMidpoint[0] * ent.axis[0][1] +
				wi->weaponMidpoint[1] * ent.axis[1][1] +
				wi->weaponMidpoint[2] * ent.axis[2][1];
			cent->lerpOrigin[2] -=
				wi->weaponMidpoint[0] * ent.axis[0][2] +
				wi->weaponMidpoint[1] * ent.axis[1][2] +
				wi->weaponMidpoint[2] * ent.axis[2][2];

			cent->lerpOrigin[2] += 8;	// an extra height boost
		}
	}
	else
	{
		wi = &cg_weapons[item->giTag];

		switch(item->giTag)
		{
		case WP_BLASTER:
			cent->lerpOrigin[2] -= 12;
			break;
		case WP_DISRUPTOR:
			cent->lerpOrigin[2] -= 13;
			break;
		case WP_BOWCASTER:
			cent->lerpOrigin[2] -= 16;
			break;
		case WP_REPEATER:
			cent->lerpOrigin[2] -= 12;
			break;
		case WP_DEMP2:
			cent->lerpOrigin[2] -= 10;
			break;
		case WP_FLECHETTE:
			cent->lerpOrigin[2] -= 6;
			break;
		case WP_ROCKET_LAUNCHER:
			cent->lerpOrigin[2] -= 11;
			break;
		case WP_THERMAL:
			cent->lerpOrigin[2] -= 12;
			break;
		case WP_TRIP_MINE:
			cent->lerpOrigin[2] -= 16;
			break;
		case WP_DET_PACK:
			cent->lerpOrigin[2] -= 16;
			break;
		default:
			cent->lerpOrigin[2] -= 8;
			break;
		}
	}

	ent.hModel = cg_items[es->modelindex].models[0];
/*
Ghoul2 Insert Start
*/
	ent.ghoul2 = cg_items[es->modelindex].g2Models[0];
	ent.radius = cg_items[es->modelindex].radius[0];
	VectorCopy (cent->lerpAngles, ent.angles);
/*
Ghoul2 Insert End
*/
	VectorCopy( cent->lerpOrigin, ent.origin);
	VectorCopy( cent->lerpOrigin, ent.oldorigin);

	ent.nonNormalizedAxes = qfalse;

	// if just respawned, slowly scale up

	msec = cg.time - cent->miscTime;

	if (CG_GreyItem(item->giType, item->giTag, cg.snap->ps.fd.forceSide))
	{
		ent.renderfx |= RF_RGB_TINT;

		ent.shaderRGBA[0] = 150;
		ent.shaderRGBA[1] = 150;
		ent.shaderRGBA[2] = 150;

		ent.renderfx |= RF_FORCE_ENT_ALPHA;

		ent.shaderRGBA[3] = 200;

		if (item->giTag == PW_FORCE_ENLIGHTENED_LIGHT)
		{
			ent.customShader = trap->R_RegisterShader("gfx/misc/mp_light_enlight_disable");
		}
		else
		{
			ent.customShader = trap->R_RegisterShader("gfx/misc/mp_dark_enlight_disable");
		}

		trap->R_AddRefEntityToScene( &ent );
		return;
	}

	if ( es->eFlags & EF_ITEMPLACEHOLDER )		// item has been picked up
	{
		if ( es->eFlags & EF_DEAD )				// if item had been droped, don't show at all
			return;

		ent.renderfx |= RF_RGB_TINT;
		ent.shaderRGBA[0] = 0;
		ent.shaderRGBA[1] = 200;
		ent.shaderRGBA[2] = 85;
		ent.customShader = cgs.media.itemRespawningPlaceholder;
	}

	// increase the size of the weapons when they are presented as items
	if ( item->giType == IT_WEAPON ) {
		VectorScale( ent.axis[0], 1.5, ent.axis[0] );
		VectorScale( ent.axis[1], 1.5, ent.axis[1] );
		VectorScale( ent.axis[2], 1.5, ent.axis[2] );
		ent.nonNormalizedAxes = qtrue;
		//trap->S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound );
	}

	if (!(cent->currentState.eFlags & EF_DROPPEDWEAPON) &&
		(item->giType == IT_WEAPON || item->giType == IT_POWERUP))
	{
		ent.renderfx |= RF_MINLIGHT;
	}

	if (item->giType != IT_TEAM && msec >= 0 && msec < ITEM_SCALEUP_TIME && !(es->eFlags & EF_ITEMPLACEHOLDER) && !(es->eFlags & EF_DROPPEDWEAPON))
	{	// if just respawned, fade in, but don't do this for flags.
		float alpha;
		int a;

		alpha = (float)msec / ITEM_SCALEUP_TIME;
		a = alpha * 255.0;
		if (a <= 0)
			a=1;

		ent.shaderRGBA[3] = a;
		if (item->giType != IT_POWERUP || item->giTag != PW_FORCE_BOON)
		{ //boon model uses a different blending mode for the sprite inside and doesn't look proper with this method
			ent.renderfx |= RF_FORCE_ENT_ALPHA;
		}
		trap->R_AddRefEntityToScene(&ent);

		ent.renderfx &= ~RF_FORCE_ENT_ALPHA;

		// Now draw the static shader over it.
		// Alpha in over half the time, out over half.

		//alpha = sin(M_PI*alpha);
		a = alpha * 255.0;

		a = 255 - a;

		if (a <= 0)
			a=1;
		if (a > 255)
			a=255;

		ent.customShader = cgs.media.itemRespawningRezOut;

		/*
		ent.shaderRGBA[0] = 0;
		ent.shaderRGBA[1] = a;
		ent.shaderRGBA[2] = a-100;

		if (ent.shaderRGBA[2] < 0)
		{
			ent.shaderRGBA[2] = 0;
		}
		*/

		/*
		ent.shaderRGBA[0] =
		ent.shaderRGBA[1] =
		ent.shaderRGBA[2] = a;
		*/

		ent.renderfx |= RF_RGB_TINT;
		ent.shaderRGBA[0] = 0;
		ent.shaderRGBA[1] = 200;
		ent.shaderRGBA[2] = 85;

		trap->R_AddRefEntityToScene( &ent );
	}
	else
	{	// add to refresh list  -- normal item
		if (item->giType == IT_TEAM &&
			(item->giTag == PW_REDFLAG || item->giTag == PW_BLUEFLAG))
		{
			ent.modelScale[0] = 0.7f;
			ent.modelScale[1] = 0.7f;
			ent.modelScale[2] = 0.7f;
			ScaleModelAxis(&ent);
		}
		trap->R_AddRefEntityToScene(&ent);
	}

	//rww - As far as I can see, this is useless.
	/*
	if ( item->giType == IT_WEAPON && wi->barrelModel ) {
		refEntity_t	barrel;

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

		barrel.hModel = wi->barrelModel;

		VectorCopy( ent.lightingOrigin, barrel.lightingOrigin );
		barrel.shadowPlane = ent.shadowPlane;
		barrel.renderfx = ent.renderfx;

		barrel.customShader = ent.customShader;

		CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" );

		AxisCopy( ent.axis, barrel.axis );
		barrel.nonNormalizedAxes = ent.nonNormalizedAxes;

		trap->R_AddRefEntityToScene( &barrel );
	}
	*/

	// accompanying rings / spheres for powerups
	if ( !cg_simpleItems.integer )
	{
		vec3_t spinAngles;

		VectorClear( spinAngles );

		if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP )
		{
			if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 )
			{
				if ( item->giType == IT_POWERUP )
				{
					ent.origin[2] += 12;
					spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f;
				}
				AnglesToAxis( spinAngles, ent.axis );

				trap->R_AddRefEntityToScene( &ent );
			}
		}
	}
}

//============================================================================

void CG_CreateDistortionTrailPart(centity_t *cent, float scale, vec3_t pos)
{
	refEntity_t ent;
	vec3_t ang;
	float vLen;

	if (!cg_renderToTextureFX.integer)
	{
		return;
	}
	memset( &ent, 0, sizeof( ent ) );

	VectorCopy( pos, ent.origin );

	VectorSubtract(ent.origin, cg.refdef.vieworg, ent.axis[0]);
	vLen = VectorLength(ent.axis[0]);
	if (VectorNormalize(ent.axis[0]) <= 0.1f)
	{	// Entity is right on vieworg.  quit.
		return;
	}

	VectorCopy(cent->lerpAngles, ang);
	ang[PITCH] += 90.0f;
	AnglesToAxis(ang, ent.axis);

	//radius must be a power of 2, and is the actual captured texture size
	if (vLen < 512)
	{
		ent.radius = 256;
	}
	else if (vLen < 1024)
	{
		ent.radius = 128;
	}
	else if (vLen < 2048)
	{
		ent.radius = 64;
	}
	else
	{
		ent.radius = 32;
	}

	ent.modelScale[0] = scale;
	ent.modelScale[1] = scale;
	ent.modelScale[2] = scale*16.0f;
	ScaleModelAxis(&ent);

	ent.hModel = trap->R_RegisterModel("models/weapons2/merr_sonn/trailmodel.md3");
	ent.customShader = cgs.media.itemRespawningRezOut;//cgs.media.cloakedShader;//cgs.media.halfShieldShader;

#if 1
	ent.renderfx = (RF_DISTORTION|RF_FORCE_ENT_ALPHA);
	ent.shaderRGBA[0] = 255.0f;
	ent.shaderRGBA[1] = 255.0f;
	ent.shaderRGBA[2] = 255.0f;
	ent.shaderRGBA[3] = 100.0f;
#else //no alpha
	ent.renderfx = RF_DISTORTION;
#endif

	trap->R_AddRefEntityToScene( &ent );
}

//distortion trail effect for rockets -rww
/*
static void CG_DistortionTrail( centity_t *cent )
{
	vec3_t fwd;
	vec3_t pos;
	float overallScale = 4.0f;

	VectorCopy(cent->currentState.pos.trDelta, fwd);
	VectorNormalize(fwd);

	VectorMA(cent->lerpOrigin, -8.0f*overallScale, fwd, pos);
	CG_CreateDistortionTrailPart(cent, 0.5f*overallScale, pos);
	VectorMA(cent->lerpOrigin, -12.0f*overallScale, fwd, pos);
	CG_CreateDistortionTrailPart(cent, 0.6f*overallScale, pos);
	VectorMA(cent->lerpOrigin, -16.0f*overallScale, fwd, pos);
	CG_CreateDistortionTrailPart(cent, 0.7f*overallScale, pos);
	VectorMA(cent->lerpOrigin, -20.0f*overallScale, fwd, pos);
	CG_CreateDistortionTrailPart(cent, 0.8f*overallScale, pos);
	VectorMA(cent->lerpOrigin, -30.0f*overallScale, fwd, pos);
	CG_CreateDistortionTrailPart(cent, 0.9f*overallScale, pos);
	VectorMA(cent->lerpOrigin, -40.0f*overallScale, fwd, pos);
	CG_CreateDistortionTrailPart(cent, 1.0f*overallScale, pos);
}
*/

/*
===============
CG_Missile
===============
*/
static void CG_Missile( centity_t *cent ) {
	refEntity_t			ent;
	entityState_t		*s1;
	const weaponInfo_t		*weapon;
//	int	col;

	s1 = &cent->currentState;
	if ( s1->weapon > WP_NUM_WEAPONS && s1->weapon != G2_MODEL_PART ) {
		s1->weapon = 0;
	}

	if (cent->ghoul2 && s1->weapon == G2_MODEL_PART)
	{
		weapon = &cg_weapons[WP_SABER];
	}
	else
	{
		weapon = &cg_weapons[s1->weapon];
	}

	if (cent->currentState.eFlags & EF_RADAROBJECT)
	{
		CG_AddRadarEnt(cent);
	}

	if (s1->weapon == WP_SABER)
	{
		if ((cent->currentState.modelindex != cent->serverSaberHitIndex || !cent->ghoul2) && !(s1->eFlags & EF_NODRAW))
		{ //no g2, or server changed the model we are using
			const char *saberModel = CG_ConfigString( CS_MODELS+cent->currentState.modelindex );

			cent->serverSaberHitIndex = cent->currentState.modelindex;

			if (cent->ghoul2)
			{ //clean if we already have one (because server changed model string index)
				trap->G2API_CleanGhoul2Models(&(cent->ghoul2));
				cent->ghoul2 = 0;
			}

			if (saberModel && saberModel[0])
			{
				trap->G2API_InitGhoul2Model(&cent->ghoul2, saberModel, 0, 0, 0, 0, 0);
			}
			else
			{
				trap->G2API_InitGhoul2Model(&cent->ghoul2, DEFAULT_SABER_MODEL, 0, 0, 0, 0, 0);
			}
			return;
		}
		else if (s1->eFlags & EF_NODRAW)
		{
			return;
		}
	}

	if (cent->ghoul2)
	{ //give us a proper radius
		ent.radius = cent->currentState.g2radius;
	}

	// calculate the axis
	VectorCopy( s1->angles, cent->lerpAngles);

	if ( s1->otherEntityNum2 && s1->weapon != WP_SABER )
	{//using an over-ridden trail effect!
		vec3_t forward;

		if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
		{
			forward[2] = 1.0f;
		}
		if ((s1->eFlags&EF_JETPACK_ACTIVE)//hack so we know we're a vehicle Weapon shot
			&& (g_vehWeaponInfo[s1->otherEntityNum2].iShotFX
				|| g_vehWeaponInfo[s1->otherEntityNum2].iModel != NULL_HANDLE) )
		{ //a vehicle with an override for the weapon trail fx or model
			trap->FX_PlayEffectID( g_vehWeaponInfo[s1->otherEntityNum2].iShotFX, cent->lerpOrigin, forward, -1, -1, qfalse );
			if ( g_vehWeaponInfo[s1->otherEntityNum2].iLoopSound )
			{
				vec3_t	velocity;
				BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time, velocity );
				trap->S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, g_vehWeaponInfo[s1->otherEntityNum2].iLoopSound );
			}
			//add custom model
			if ( g_vehWeaponInfo[s1->otherEntityNum2].iModel == NULL_HANDLE )
			{
				return;
			}
		}
		else
		{//a regular missile
			trap->FX_PlayEffectID( cgs.gameEffects[s1->otherEntityNum2], cent->lerpOrigin, forward, -1, -1, qfalse );
			if ( s1->loopSound )
			{
				vec3_t	velocity;
				BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time, velocity );
				trap->S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, s1->loopSound );
			}
			//FIXME: if has a custom model, too, then set it and do rest of code below?
			return;
		}
	}
	else if ( cent->currentState.eFlags & EF_ALT_FIRING )
	{
		// add trails
		if ( weapon->altMissileTrailFunc )
		{
			weapon->altMissileTrailFunc( cent, weapon );
		}

		// add dynamic light
		if ( weapon->altMissileDlight )
		{
			trap->R_AddLightToScene(cent->lerpOrigin, weapon->altMissileDlight,
				weapon->altMissileDlightColor[0], weapon->altMissileDlightColor[1], weapon->altMissileDlightColor[2] );
		}

		// add missile sound
		if ( weapon->altMissileSound ) {
			vec3_t	velocity;

			BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time, velocity );

			trap->S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->altMissileSound );
		}

		//Don't draw something without a model
		if ( weapon->altMissileModel == NULL_HANDLE )
			return;
	}
	else
	{
		// add trails
		if ( weapon->missileTrailFunc )
		{
			weapon->missileTrailFunc( cent, weapon );
		}

		// add dynamic light
		if ( weapon->missileDlight )
		{
			trap->R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
				weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] );
		}

		// add missile sound
		if ( weapon->missileSound )
		{
			vec3_t	velocity;

			BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time, velocity );

			trap->S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound );
		}

		//Don't draw something without a model
		if ( weapon->missileModel == NULL_HANDLE && s1->weapon != WP_SABER && s1->weapon != G2_MODEL_PART ) //saber uses ghoul2 model, doesn't matter
			return;
	}

	// create the render entity
	memset (&ent, 0, sizeof(ent));
	VectorCopy( cent->lerpOrigin, ent.origin);
	VectorCopy( cent->lerpOrigin, ent.oldorigin);
/*
Ghoul2 Insert Start
*/
	CG_SetGhoul2Info(&ent, cent);

/*
Ghoul2 Insert End
*/

	// flicker between two skins
	ent.skinNum = cg.clientFrame & 1;
	ent.renderfx = /*weapon->missileRenderfx | */RF_NOSHADOW;

	if ( !(s1->eFlags&EF_JETPACK_ACTIVE) )
	{
		if (s1->weapon != WP_SABER && s1->weapon != G2_MODEL_PART)
		{
			//if ( cent->currentState.eFlags | EF_ALT_FIRING )
			//rww - why was this like this?
			if ( cent->currentState.eFlags & EF_ALT_FIRING )
			{
				ent.hModel = weapon->altMissileModel;
			}
			else
			{
				ent.hModel = weapon->missileModel;
			}
		}
	}
	//add custom model
	else
	{
		if ( g_vehWeaponInfo[s1->otherEntityNum2].iModel != NULL_HANDLE )
		{
			ent.hModel = g_vehWeaponInfo[s1->otherEntityNum2].iModel;
		}
		else
		{//wtf?  how did we get here?
			return;
		}
	}

	// spin as it moves
	if ( s1->apos.trType != TR_INTERPOLATE )
	{
		// convert direction of travel into axis
		if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
			ent.axis[0][2] = 1;
		}

		// spin as it moves
		if ( s1->pos.trType != TR_STATIONARY )
		{
			if ( s1->eFlags & EF_MISSILE_STICK )
			{
				RotateAroundDirection( ent.axis, cg.time * 0.5f );//Did this so regular missiles don't get broken
			}
			else
			{
				RotateAroundDirection( ent.axis, cg.time * 0.25f );//JFM:FLOAT FIX
			}
		}
		else
		{
			if ( s1->eFlags & EF_MISSILE_STICK )
			{
				RotateAroundDirection( ent.axis, (float)s1->pos.trTime * 0.5f );
			}
			else
			{
				RotateAroundDirection( ent.axis, (float)s1->time );
			}
		}
	}
	else
	{
		AnglesToAxis( cent->lerpAngles, ent.axis );
	}

	if (s1->weapon == WP_SABER)
	{
		ent.radius = s1->g2radius;
	}

	// add to refresh list, possibly with quad glow
	CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE );

	if (s1->weapon == WP_SABER && cgs.gametype == GT_JEDIMASTER)
	{ //in jedimaster always make the saber glow when on the ground
		vec3_t org;
		float wv;
		int i;
		addspriteArgStruct_t fxSArgs;
		//refEntity_t sRef;
		//memcpy( &sRef, &ent, sizeof( sRef ) );

		ent.customShader = cgs.media.solidWhite;
		ent.renderfx = RF_RGB_TINT;
		wv = sin( cg.time * 0.003f ) * 0.08f + 0.1f;
		ent.shaderRGBA[0] = wv * 255;
		ent.shaderRGBA[1] = wv * 255;
		ent.shaderRGBA[2] = wv * 0;
		trap->R_AddRefEntityToScene (&ent);

		for ( i = -4; i < 10; i += 1 )
		{
			VectorMA( ent.origin, -i, ent.axis[2], org );

			VectorCopy(org, fxSArgs.origin);
			VectorClear(fxSArgs.vel);
			VectorClear(fxSArgs.accel);
			fxSArgs.scale = 5.5f;
			fxSArgs.dscale = 5.5f;
			fxSArgs.sAlpha = wv;
			fxSArgs.eAlpha = wv;
			fxSArgs.rotation = 0.0f;
			fxSArgs.bounce = 0.0f;
			fxSArgs.life = 1.0f;
			fxSArgs.shader = cgs.media.yellowDroppedSaberShader;
			fxSArgs.flags = 0x08000000;

			//trap->FX_AddSprite( org, NULL, NULL, 5.5f, 5.5f, wv, wv, 0.0f, 0.0f, 1.0f, cgs.media.yellowSaberGlowShader, 0x08000000 );
			trap->FX_AddSprite(&fxSArgs);
		}

		if (cgs.gametype == GT_JEDIMASTER)
		{
			ent.shaderRGBA[0] = 255;
			ent.shaderRGBA[1] = 255;
			ent.shaderRGBA[2] = 0;

			ent.renderfx |= RF_DEPTHHACK;
			ent.customShader = cgs.media.forceSightBubble;

			trap->R_AddRefEntityToScene( &ent );
		}
	}

	if ( s1->eFlags & EF_FIRING )
	{//special code for adding the beam to the attached tripwire mine
		vec3_t	beamOrg;

		VectorMA( ent.origin, 8, ent.axis[0], beamOrg );// forward
		trap->FX_PlayEffectID( cgs.effects.mTripMineLaser, beamOrg, ent.axis[0], -1, -1, qfalse );
	}
}

int	CG_BMS_START = 0;
int	CG_BMS_MID = 1;
int	CG_BMS_END = 2;

/*
-------------------------
CG_PlayDoorLoopSound
-------------------------
*/

void CG_PlayDoorLoopSound( centity_t *cent )
{
	sfxHandle_t	sfx;
	const char *soundSet;
	vec3_t	origin;
	float	*v;

	if ( !cent->currentState.soundSetIndex )
	{
		return;
	}

	soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex );

	if (!soundSet || !soundSet[0])
	{
		return;
	}

	sfx = trap->AS_GetBModelSound( soundSet, CG_BMS_MID );

	if ( sfx == -1 )
	{
		return;
	}

	if (cent->currentState.eType == ET_MOVER) //shouldn't be in here otherwise, but just in case.
	{
		v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
		VectorAdd( cent->lerpOrigin, v, origin );
	}
	else
	{
		VectorCopy(cent->lerpOrigin, origin);
	}

	//ent->s.loopSound = sfx;
	CG_S_AddRealLoopingSound(cent->currentState.number, origin, vec3_origin, sfx);
}

/*
-------------------------
CG_PlayDoorSound
-------------------------
*/

void CG_PlayDoorSound( centity_t *cent, int type )
{
	sfxHandle_t	sfx;
	const char *soundSet;

	if ( !cent->currentState.soundSetIndex )
	{
		return;
	}

	soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex );

	if (!soundSet || !soundSet[0])
	{
		return;
	}

	sfx = trap->AS_GetBModelSound( soundSet, type );

	if ( sfx == -1 )
	{
		return;
	}

	trap->S_StartSound( NULL, cent->currentState.number, CHAN_AUTO, sfx );
}

/*
===============
CG_Mover
===============
*/
static void CG_Mover( centity_t *cent ) {
	refEntity_t			ent;
	entityState_t		*s1;

	s1 = &cent->currentState;

	// create the render entity
	memset (&ent, 0, sizeof(ent));

	if ( (cent->currentState.eFlags2&EF2_HYPERSPACE) )
	{//I'm the hyperspace brush
		qboolean drawMe = qfalse;
		if ( cg.predictedPlayerState.m_iVehicleNum
			&& cg.predictedVehicleState.hyperSpaceTime
			&& (cg.time-cg.predictedVehicleState.hyperSpaceTime) < HYPERSPACE_TIME
			&& (cg.time-cg.predictedVehicleState.hyperSpaceTime) > 1000 )
		{
			if ( cg.snap
				&& cg.snap->ps.pm_type == PM_INTERMISSION )
			{//in the intermission, stop drawing hyperspace ent
			}
			else if ( (cg.predictedVehicleState.eFlags2&EF2_HYPERSPACE) )
			{//actually hyperspacing now
				float timeFrac = ((float)(cg.time-cg.predictedVehicleState.hyperSpaceTime-1000))/(HYPERSPACE_TIME-1000);
				if ( timeFrac < (HYPERSPACE_TELEPORT_FRAC+0.1f) )
				{//still in hyperspace or just popped out
					const float	alpha = timeFrac<0.5f?timeFrac/0.5f:1.0f;
					drawMe = qtrue;
					VectorMA( cg.refdef.vieworg, 1000.0f+((1.0f-timeFrac)*1000.0f), cg.refdef.viewaxis[0], cent->lerpOrigin );
					VectorSet( cent->lerpAngles, cg.refdef.viewangles[PITCH], cg.refdef.viewangles[YAW]-90.0f, 0 );//cos( ( cg.time + 1000 ) *  scale ) * 4 );
					ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = 255;
					ent.shaderRGBA[3] = alpha*255;
				}
			}
		}
		if ( !drawMe )
		{//else, never draw
			return;
		}
	}

	if (cent->currentState.eFlags & EF_RADAROBJECT)
	{
		CG_AddRadarEnt(cent);
	}

	VectorCopy( cent->lerpOrigin, ent.origin);
	VectorCopy( cent->lerpOrigin, ent.oldorigin);
	AnglesToAxis( cent->lerpAngles, ent.axis );

	ent.renderfx = RF_NOSHADOW;
/*
Ghoul2 Insert Start
*/

	CG_SetGhoul2Info(&ent, cent);
/*
Ghoul2 Insert End
*/
	// flicker between two skins (FIXME?)
	ent.skinNum = ( cg.time >> 6 ) & 1;

	// get the model, either as a bmodel or a modelindex
	if ( s1->solid == SOLID_BMODEL )
	{
		ent.hModel = cgs.inlineDrawModel[s1->modelindex];
	}
	else
	{
		ent.hModel = cgs.gameModels[s1->modelindex];
	}

	if ( s1->eFlags & EF_SHADER_ANIM )
	{
		ent.renderfx|=RF_SETANIMINDEX;
		ent.skinNum = s1->frame;
		//ent.shaderTime = cg.time*0.001f - s1->frame/s1->time;//NOTE: s1->time is number of frames
	}

	// add to refresh list
	trap->R_AddRefEntityToScene(&ent);

	// add the secondary model
	if ( s1->modelindex2 && s1->modelindex2 < MAX_MODELS )
	{
		ent.skinNum = 0;
		ent.hModel = cgs.gameModels[s1->modelindex2];
		if (s1->iModelScale)
		{ //custom model2 scale
			ent.modelScale[0] = ent.modelScale[1] = ent.modelScale[2] = s1->iModelScale/100.0f;
			ScaleModelAxis(&ent);
		}
		trap->R_AddRefEntityToScene(&ent);
	}

}

/*
===============
CG_Beam

Also called as an event
===============
*/
void CG_Beam( centity_t *cent ) {
	refEntity_t			ent;
	entityState_t		*s1;

	s1 = &cent->currentState;

	// create the render entity
	memset (&ent, 0, sizeof(ent));
	VectorCopy( s1->pos.trBase, ent.origin );
	VectorCopy( s1->origin2, ent.oldorigin );
	AxisClear( ent.axis );
	ent.reType = RT_BEAM;

	ent.renderfx = RF_NOSHADOW;
/*
Ghoul2 Insert Start
*/
	CG_SetGhoul2Info(&ent, cent);

/*
Ghoul2 Insert End
*/
	// add to refresh list
	trap->R_AddRefEntityToScene(&ent);
}


/*
===============
CG_Portal
===============
*/
static void CG_Portal( centity_t *cent ) {
	refEntity_t			ent;
	entityState_t		*s1;

	s1 = &cent->currentState;

	// create the render entity
	memset (&ent, 0, sizeof(ent));
	VectorCopy( cent->lerpOrigin, ent.origin );
	VectorCopy( s1->origin2, ent.oldorigin );
	ByteToDir( s1->eventParm, ent.axis[0] );
	PerpendicularVector( ent.axis[1], ent.axis[0] );

	// negating this tends to get the directions like they want
	// we really should have a camera roll value
	VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] );

	CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] );
	ent.reType = RT_PORTALSURFACE;
	ent.oldframe = s1->powerups;
	ent.frame = s1->frame;		// rotation speed
	ent.skinNum = s1->clientNum/256.0 * 360;	// roll offset
/*
Ghoul2 Insert Start
*/
	CG_SetGhoul2Info(&ent, cent);
/*
Ghoul2 Insert End
*/
	// add to refresh list
	trap->R_AddRefEntityToScene(&ent);
}


/*
=========================
CG_AdjustPositionForMover

Also called by client movement prediction code
=========================
*/
void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) {
	centity_t	*cent;
	vec3_t	oldOrigin, origin, deltaOrigin;
	vec3_t	oldAngles, angles, deltaAngles;

	if ( cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_SPECTATOR )
	{
		VectorCopy( in, out );
		return;
	}

	if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
		VectorCopy( in, out );
		return;
	}

	cent = &cg_entities[ moverNum ];
	if ( cent->currentState.eType != ET_MOVER ) {
		VectorCopy( in, out );
		return;
	}

	BG_EvaluateTrajectory( &cent->currentState.pos, fromTime, oldOrigin );
	BG_EvaluateTrajectory( &cent->currentState.apos, fromTime, oldAngles );

	BG_EvaluateTrajectory( &cent->currentState.pos, toTime, origin );
	BG_EvaluateTrajectory( &cent->currentState.apos, toTime, angles );

	VectorSubtract( origin, oldOrigin, deltaOrigin );
	VectorSubtract( angles, oldAngles, deltaAngles );

	VectorAdd( in, deltaOrigin, out );

	// FIXME: origin change when on a rotating object
}

/*
=============================
CG_InterpolateEntityPosition
=============================
*/
static void CG_InterpolateEntityPosition( centity_t *cent ) {
	vec3_t		current, next;
	float		f;

	// it would be an internal error to find an entity that interpolates without
	// a snapshot ahead of the current one
	if ( cg.nextSnap == NULL ) {
		trap->Error( ERR_DROP, "CG_InterpoateEntityPosition: cg.nextSnap == NULL" );
		return;
	}

	f = cg.frameInterpolation;

	// this will linearize a sine or parabolic curve, but it is important
	// to not extrapolate player positions if more recent data is available
	BG_EvaluateTrajectory( &cent->currentState.pos, cg.snap->serverTime, current );
	BG_EvaluateTrajectory( &cent->nextState.pos, cg.nextSnap->serverTime, next );

	cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] );
	cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] );
	cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] );

	BG_EvaluateTrajectory( &cent->currentState.apos, cg.snap->serverTime, current );
	BG_EvaluateTrajectory( &cent->nextState.apos, cg.nextSnap->serverTime, next );

	cent->lerpAngles[0] = LerpAngle( current[0], next[0], f );
	cent->lerpAngles[1] = LerpAngle( current[1], next[1], f );
	cent->lerpAngles[2] = LerpAngle( current[2], next[2], f );
}

/*
===============
CG_CalcEntityLerpPositions

===============
*/
void CG_CalcEntityLerpPositions( centity_t *cent ) {
	qboolean goAway = qfalse;

	// if this player does not want to see extrapolated players
	if ( !cg_smoothClients.integer ) {
		// make sure the clients use TR_INTERPOLATE
		if ( (cent->currentState.number != cg.clientNum && cent->currentState.number < MAX_CLIENTS) || cent->currentState.eType == ET_NPC ) {
			cent->currentState.pos.trType = TR_INTERPOLATE;
			cent->nextState.pos.trType = TR_INTERPOLATE;
		}
	}

	if (cg.predictedPlayerState.m_iVehicleNum &&
		cg.predictedPlayerState.m_iVehicleNum == cent->currentState.number &&
		cent->currentState.eType == ET_NPC && cent->currentState.NPC_class == CLASS_VEHICLE)
	{ //special case for vehicle we are riding
		centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];

		if (veh->currentState.owner == cg.predictedPlayerState.clientNum)
		{ //only do this if the vehicle is pilotted by this client and predicting properly
			BG_EvaluateTrajectory( &cent->currentState.pos, cg.time, cent->lerpOrigin );
			BG_EvaluateTrajectory( &cent->currentState.apos, cg.time, cent->lerpAngles );
			return;
		}
	}

	if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) {
		CG_InterpolateEntityPosition( cent );
		return;
	}

	// first see if we can interpolate between two snaps for
	// linear extrapolated clients
	if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP
		&& ((cent->currentState.number != cg.clientNum && cent->currentState.number < MAX_CLIENTS) || cent->currentState.eType == ET_NPC) ) {
		CG_InterpolateEntityPosition( cent );
		goAway = qtrue;
	}
	else if (cent->interpolate &&
		cent->currentState.eType == ET_NPC && cent->currentState.NPC_class == CLASS_VEHICLE)
	{
		CG_InterpolateEntityPosition( cent );
		goAway = qtrue;
	}
	else
	{
		// just use the current frame and evaluate as best we can
		BG_EvaluateTrajectory( &cent->currentState.pos, cg.time, cent->lerpOrigin );
		BG_EvaluateTrajectory( &cent->currentState.apos, cg.time, cent->lerpAngles );
	}

#if 0
	if (cent->hasRagOffset && cent->ragOffsetTime < cg.time)
	{ //take all of the offsets from last frame and normalize the total direction and add it in
		vec3_t slideDir;
		vec3_t preOffset;
		vec3_t addedOffset;
		vec3_t	playerMins = {-15, -15, DEFAULT_MINS_2};
		vec3_t	playerMaxs = {15, 15, DEFAULT_MAXS_2};
		trace_t tr;

		//VectorSubtract(cent->lerpOrigin, callData->bonePos, slideDir);
		VectorCopy(cent->ragOffsets, slideDir);
		VectorNormalize(slideDir);

		//Store it in case we want to go back
		VectorCopy(cent->lerpOriginOffset, preOffset);

		//just add a little at a time
		VectorMA(cent->lerpOriginOffset, 0.4f, slideDir, cent->lerpOriginOffset);

		if (VectorLength(cent->lerpOriginOffset) > 10.0f)
		{ //don't go too far away
			VectorCopy(preOffset, cent->lerpOriginOffset);
		}
		else
		{
			//Let's trace there to make sure we can make it
			VectorAdd(cent->lerpOrigin, cent->lerpOriginOffset, addedOffset);
			CG_Trace(&tr, cent->lerpOrigin, playerMins, playerMaxs, addedOffset, cent->currentState.number, MASK_PLAYERSOLID);

			if (tr.startsolid || tr.allsolid || tr.fraction != 1.0f)
			{ //can't get there
				VectorCopy(preOffset, cent->lerpOriginOffset);
			}
			else
			{
				/*
				if (cent->lerpOriginOffset[2] > 4.0f)
				{ //don't go too far off the ground
					cent->lerpOriginOffset[2] = 4.0f;
				}
				*/
				//I guess I just don't want this happening.
				cent->lerpOriginOffset[2] = 0.0f;
			}
		}

		//done with this bit
		cent->hasRagOffset = qfalse;
		VectorClear(cent->ragOffsets);
		cent->ragOffsetTime = cg.time + 50;
	}

	//See if we should add in the offset for ragdoll
	if (cent->isRagging && ((cent->currentState.eFlags & EF_DEAD) || (cent->currentState.eFlags & EF_RAG)))
	{
		VectorAdd(cent->lerpOrigin, cent->lerpOriginOffset, cent->lerpOrigin);
	}
#endif

	if (goAway)
	{
		return;
	}

	// adjust for riding a mover if it wasn't rolled into the predicted
	// player state
	if ( cent->currentState.number != cg.clientNum ) {
		CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum,
		cg.snap->serverTime, cg.time, cent->lerpOrigin );
	}
}

void CG_G2Animated( centity_t *cent );

static void CG_FX( centity_t *cent )
{
	vec3_t			fxDir;
	int				efxIndex = 0;
	entityState_t	*s1;
	const char		*s;

	if (cent->miscTime > cg.time)
	{
		return;
	}

	s1 = &cent->currentState;

	if (!s1)
	{
		return;
	}

	if (s1->modelindex2 == FX_STATE_OFF)
	{	// fx not active
		return;
	}

	if (s1->modelindex2 < FX_STATE_ONE_SHOT_LIMIT)
	{	// fx is single shot
		if (cent->muzzleFlashTime == s1->modelindex2)
		{
			return;
		}

		cent->muzzleFlashTime = s1->modelindex2;
	}

	cent->miscTime = cg.time + s1->speed + Q_flrand(0.0f, 1.0f) * s1->time;

	AngleVectors(s1->angles, fxDir, 0, 0);

	if (!fxDir[0] && !fxDir[1] && !fxDir[2])
	{
		fxDir[1] = 1;
	}

	if ( cgs.gameEffects[ s1->modelindex ] )
	{
		efxIndex = cgs.gameEffects[s1->modelindex];
	}
	else
	{
		s = CG_ConfigString( CS_EFFECTS + s1->modelindex );
		if (s && s[0])
		{
			efxIndex = trap->FX_RegisterEffect(s);
			cgs.gameEffects[s1->modelindex] = efxIndex;
		}
	}

	if (efxIndex)
	{
		if (s1->isPortalEnt)
		{
			trap->FX_PlayEffectID(efxIndex, cent->lerpOrigin, fxDir, -1, -1, qtrue );
		}
		else
		{
			trap->FX_PlayEffectID(efxIndex, cent->lerpOrigin, fxDir, -1, -1, qfalse );
		}
	}


}


/*
===============
CG_AddCEntity

===============
*/
static void CG_AddCEntity( centity_t *cent ) {
	// event-only entities will have been dealt with already
	if ( cent->currentState.eType >= ET_EVENTS ) {
		return;
	}

	if (cg.predictedPlayerState.pm_type == PM_INTERMISSION)
	{ //don't render anything then
		if (cent->currentState.eType == ET_GENERAL ||
			cent->currentState.eType == ET_PLAYER ||
			cent->currentState.eType == ET_INVISIBLE)
		{
			return;
		}
		if ( cent->currentState.eType == ET_NPC )
		{//NPC in intermission
			if ( cent->currentState.NPC_class == CLASS_VEHICLE )
			{//don't render vehicles in intermissions, allow other NPCs for scripts
				return;
			}
		}
	}

	// don't render when we are in spec, happens occasionally on map_restart and such
	if ( cg.predictedPlayerState.clientNum == cent->currentState.number && cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_SPECTATOR )
		return;


	// calculate the current origin
	CG_CalcEntityLerpPositions( cent );

	// add automatic effects
	CG_EntityEffects( cent );
/*
Ghoul2 Insert Start
*/

	// add local sound set if any
	if ( cent->currentState.soundSetIndex && cent->currentState.eType != ET_MOVER )
	{
		const char *soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex );

		if (soundSet && soundSet[0])
		{
			trap->S_AddLocalSet(soundSet, cg.refdef.vieworg, cent->lerpOrigin, cent->currentState.number, cg.time);
		}
	}
/*
Ghoul2 Insert End
*/
	switch ( cent->currentState.eType ) {
	default:
		trap->Error( ERR_DROP, "Bad entity type: %i\n", cent->currentState.eType );
		break;

	case ET_FX:
		CG_FX( cent );
		break;

	case ET_INVISIBLE:
	case ET_PUSH_TRIGGER:
	case ET_TELEPORT_TRIGGER:
	case ET_TERRAIN:
		break;
	case ET_GENERAL:
		CG_General( cent );
		break;
	case ET_PLAYER:
		CG_Player( cent );
		break;
	case ET_ITEM:
		CG_Item( cent );
		break;
	case ET_MISSILE:
		CG_Missile( cent );
		break;
	case ET_SPECIAL:
		CG_Special( cent );
		break;
	case ET_HOLOCRON:
		CG_General( cent );
		break;
	case ET_MOVER:
		CG_Mover( cent );
		break;
	case ET_BEAM:
		CG_Beam( cent );
		break;
	case ET_PORTAL:
		CG_Portal( cent );
		break;
	case ET_SPEAKER:
		CG_Speaker( cent );
		break;
	case ET_NPC: //An entity that wants to be able to use ghoul2 humanoid (and other) anims. Like a player, but not.
		CG_G2Animated( cent );
		break;
	case ET_TEAM:
		break;
	case ET_BODY:
		CG_General( cent );
		break;
	}
}

void CG_ManualEntityRender(centity_t *cent)
{
	CG_AddCEntity(cent);
}

/*
===============
CG_AddPacketEntities

===============
*/
void CG_AddPacketEntities( qboolean isPortal ) {
	int					num;
	centity_t			*cent;
	playerState_t		*ps;

	if (isPortal)
	{
		for ( num = 0 ; num < cg.snap->numEntities ; num++ )
		{
			cent = &cg_entities[ cg.snap->entities[ num ].number ];

			if (cent->currentState.isPortalEnt)
			{
				CG_AddCEntity( cent );
			}
		}
		return;
	}

	// set cg.frameInterpolation
	if ( cg.nextSnap ) {
		int		delta;

		delta = (cg.nextSnap->serverTime - cg.snap->serverTime);
		if ( delta == 0 ) {
			cg.frameInterpolation = 0;
		} else {
			cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta;
		}
	} else {
		cg.frameInterpolation = 0;	// actually, it should never be used, because
									// no entities should be marked as interpolating
	}

	// the auto-rotating items will all have the same axis
	cg.autoAngles[0] = 0;
	cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0;
	cg.autoAngles[2] = 0;

	cg.autoAnglesFast[0] = 0;
	cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
	cg.autoAnglesFast[2] = 0;

	AnglesToAxis( cg.autoAngles, cg.autoAxis );
	AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast );

	// Reset radar entities
	cg.radarEntityCount = 0;
	cg.bracketedEntityCount = 0;

	// generate and add the entity from the playerstate
	ps = &cg.predictedPlayerState;

	CG_CheckPlayerG2Weapons(ps, &cg_entities[cg.predictedPlayerState.clientNum]);
	BG_PlayerStateToEntityState( ps, &cg_entities[cg.predictedPlayerState.clientNum].currentState, qfalse );

	if (cg.predictedPlayerState.m_iVehicleNum)
	{ //add the vehicle I'm riding first
		//BG_PlayerStateToEntityState( &cg.predictedVehicleState, &cg_entities[cg.predictedPlayerState.m_iVehicleNum].currentState, qfalse );
		//cg_entities[cg.predictedPlayerState.m_iVehicleNum].currentState.eType = ET_NPC;
		centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];

		if (veh->currentState.owner == cg.predictedPlayerState.clientNum)
		{
			BG_PlayerStateToEntityState( &cg.predictedVehicleState, &veh->currentState, qfalse );
			veh->currentState.eType = ET_NPC;

			veh->currentState.pos.trType = TR_INTERPOLATE;
		}
        CG_AddCEntity(veh);
		veh->bodyHeight = cg.time; //indicate we have already been added
	}

	CG_AddCEntity( &cg_entities[cg.predictedPlayerState.clientNum] );

	/*
	// lerp the non-predicted value for lightning gun origins
	CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] );
	*/
	//No longer have to do this.

	// add each entity sent over by the server
	for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
		// Don't re-add ents that have been predicted.
		if (cg.snap->entities[ num ].number != cg.snap->ps.clientNum)
		{
			cent = &cg_entities[ cg.snap->entities[ num ].number ];
			if (cent->currentState.eType == ET_PLAYER &&
				cent->currentState.m_iVehicleNum)
			{ //add his veh first
				int j = 0;

				while (j < cg.snap->numEntities)
				{
					if (cg.snap->entities[j].number == cent->currentState.m_iVehicleNum)
					{
						centity_t *veh = &cg_entities[cg.snap->entities[j].number];

						CG_AddCEntity(veh);
						veh->bodyHeight = cg.time; //indicate we have already been added
						break;
					}

					j++;
				}
			}
			else if (cent->currentState.eType == ET_NPC &&
				cent->currentState.m_iVehicleNum &&
				cent->bodyHeight == cg.time)
			{ //never add a vehicle with a pilot, his pilot entity will get him added first.
				//if we were to add the vehicle after the pilot, the pilot's bolt would lag a frame behind.
				continue;
			}
			CG_AddCEntity( cent );
		}
	}

	for(num=0;num<cg_numpermanents;num++)
	{
		cent = cg_permanents[num];
		if (cent->currentValid)
		{
			CG_AddCEntity( cent );
		}
	}
}

void CG_ROFF_NotetrackCallback( centity_t *cent, const char *notetrack)
{
	int i = 0, r = 0, objectID = 0, anglesGathered = 0, posoffsetGathered = 0;
	char type[256];
	char argument[512];
	char addlArg[512];
	char errMsg[256];
	char t[64];
	int addlArgs = 0;
	vec3_t parsedAngles, parsedOffset, useAngles, useOrigin, forward, right, up;

	if (!cent || !notetrack)
	{
		return;
	}

	//notetrack = "effect effects/explosion1.efx 0+0+64 0-0-1";

	while (notetrack[i] && notetrack[i] != ' ')
	{
		type[i] = notetrack[i];
		i++;
	}

	type[i] = '\0';

	if (notetrack[i] != ' ')
	{ //didn't pass in a valid notetrack type, or forgot the argument for it
		return;
	}

	i++;

	while (notetrack[i] && notetrack[i] != ' ')
	{
		argument[r] = notetrack[i];
		r++;
		i++;
	}
	argument[r] = '\0';

	if (!r)
	{
		return;
	}

	if (notetrack[i] == ' ')
	{ //additional arguments...
		addlArgs = 1;

		i++;
		r = 0;
		while (notetrack[i])
		{
			addlArg[r] = notetrack[i];
			r++;
			i++;
		}
		addlArg[r] = '\0';
	}

	if (strcmp(type, "effect") == 0)
	{
		if (!addlArgs)
		{
			//sprintf(errMsg, "Offset position argument for 'effect' type is invalid.");
			//goto functionend;
			VectorClear(parsedOffset);
			goto defaultoffsetposition;
		}

		i = 0;

		while (posoffsetGathered < 3)
		{
			r = 0;
			while (addlArg[i] && addlArg[i] != '+' && addlArg[i] != ' ')
			{
				t[r] = addlArg[i];
				r++;
				i++;
			}
			t[r] = '\0';
			i++;
			if (!r)
			{ //failure..
				//sprintf(errMsg, "Offset position argument for 'effect' type is invalid.");
				//goto functionend;
				VectorClear(parsedOffset);
				i = 0;
				goto defaultoffsetposition;
			}
			parsedOffset[posoffsetGathered] = atof(t);
			posoffsetGathered++;
		}

		if (posoffsetGathered < 3)
		{
			Com_sprintf(errMsg, sizeof(errMsg), "Offset position argument for 'effect' type is invalid.");
			goto functionend;
		}

		i--;

		if (addlArg[i] != ' ')
		{
			addlArgs = 0;
		}

defaultoffsetposition:

		objectID = trap->FX_RegisterEffect(argument);

		if (objectID)
		{
			if (addlArgs)
			{ //if there is an additional argument for an effect it is expected to be XANGLE-YANGLE-ZANGLE
				i++;
				while (anglesGathered < 3)
				{
					r = 0;
					while (addlArg[i] && addlArg[i] != '-')
					{
						t[r] = addlArg[i];
						r++;
						i++;
					}
					t[r] = '\0';
					i++;

					if (!r)
					{ //failed to get a new part of the vector
						anglesGathered = 0;
						break;
					}

					parsedAngles[anglesGathered] = atof(t);
					anglesGathered++;
				}

				if (anglesGathered)
				{
					VectorCopy(parsedAngles, useAngles);
				}
				else
				{ //failed to parse angles from the extra argument provided..
					VectorCopy(cent->lerpAngles, useAngles);
				}
			}
			else
			{ //if no constant angles, play in direction entity is facing
				VectorCopy(cent->lerpAngles, useAngles);
			}

			AngleVectors(useAngles, forward, right, up);

			VectorCopy(cent->lerpOrigin, useOrigin);

			//forward
			useOrigin[0] += forward[0]*parsedOffset[0];
			useOrigin[1] += forward[1]*parsedOffset[0];
			useOrigin[2] += forward[2]*parsedOffset[0];

			//right
			useOrigin[0] += right[0]*parsedOffset[1];
			useOrigin[1] += right[1]*parsedOffset[1];
			useOrigin[2] += right[2]*parsedOffset[1];

			//up
			useOrigin[0] += up[0]*parsedOffset[2];
			useOrigin[1] += up[1]*parsedOffset[2];
			useOrigin[2] += up[2]*parsedOffset[2];

			trap->FX_PlayEffectID(objectID, useOrigin, useAngles, -1, -1, qfalse);
		}
	}
	else if (strcmp(type, "sound") == 0)
	{
		objectID = trap->S_RegisterSound(argument);
		trap->S_StartSound(cent->lerpOrigin, cent->currentState.number, CHAN_BODY, objectID);
	}
	else if (strcmp(type, "loop") == 0)
	{ //handled server-side
		return;
	}
	//else if ...
	else
	{
		if (type[0])
		{
			Com_Printf("^3Warning: \"%s\" is an invalid ROFF notetrack function\n", type);
		}
		else
		{
			Com_Printf("^3Warning: Notetrack is missing function and/or arguments\n");
		}
	}

	return;

functionend:
	Com_Printf("^3Type-specific notetrack error: %s\n", errMsg);
	return;
}

void CG_Cube( vec3_t mins, vec3_t maxs, vec3_t color, float alpha )
{
	vec3_t	rot={0,0,0};
	int		vec[3];
	int		axis, i;
	addpolyArgStruct_t apArgs;

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

	for ( axis = 0, vec[0] = 0, vec[1] = 1, vec[2] = 2; axis < 3; axis++, vec[0]++, vec[1]++, vec[2]++ )
	{
		for ( i = 0; i < 3; i++ )
		{
			if ( vec[i] > 2 )
			{
				vec[i] = 0;
			}
		}

		apArgs.p[0][vec[1]] = mins[vec[1]];
		apArgs.p[0][vec[2]] = mins[vec[2]];

		apArgs.p[1][vec[1]] = mins[vec[1]];
		apArgs.p[1][vec[2]] = maxs[vec[2]];

		apArgs.p[2][vec[1]] = maxs[vec[1]];
		apArgs.p[2][vec[2]] = maxs[vec[2]];

		apArgs.p[3][vec[1]] = maxs[vec[1]];
		apArgs.p[3][vec[2]] = mins[vec[2]];

		//- face
		apArgs.p[0][vec[0]] = apArgs.p[1][vec[0]] = apArgs.p[2][vec[0]] = apArgs.p[3][vec[0]] = mins[vec[0]];

		apArgs.numVerts = 4;
		apArgs.alpha1 = apArgs.alpha2 = alpha;
		VectorCopy( color, apArgs.rgb1 );
		VectorCopy( color, apArgs.rgb2 );
		VectorCopy( rot, apArgs.rotationDelta );
		apArgs.killTime = cg.frametime;
		apArgs.shader = cgs.media.solidWhite;

		trap->FX_AddPoly( &apArgs );

		//+ face
		apArgs.p[0][vec[0]] = apArgs.p[1][vec[0]] = apArgs.p[2][vec[0]] = apArgs.p[3][vec[0]] = maxs[vec[0]];

		trap->FX_AddPoly( &apArgs );
	}
}
