//-----------------------------------------------------------------------------
//
// $Id$
//
//-----------------------------------------------------------------------------
//
// $Log$
// Revision 1.50  2007/02/03 15:02:21  jbravo
// Renamed RQ3 to Reaction, Dropped loading of various baseq3 media, disabled the follow command, fixed grenades killing teammates and some cleanups
//
// Revision 1.49  2005/02/15 16:33:38  makro
// Tons of updates (entity tree attachment system, UI vectors)
//
// Revision 1.48  2004/01/26 21:26:08  makro
// no message
//
// Revision 1.47  2003/09/10 21:40:35  makro
// Cooler breath puffs. Locked r_fastSky on maps with global fog.
// Some other things I can't remember.
//
// Revision 1.46  2003/09/07 19:51:39  makro
// no message
//
// Revision 1.45  2003/08/26 19:28:37  makro
// target_speakers
//
// Revision 1.44  2003/08/10 20:13:26  makro
// no message
//
// Revision 1.43  2003/04/19 15:27:30  jbravo
// Backing out of most of unlagged.  Only optimized prediction and smooth clients
// remains.
//
// Revision 1.42  2003/04/02 17:58:03  jbravo
// Ammo skin only replacements now work.
//
// Revision 1.41  2003/03/28 22:25:10  makro
// no message
//
// Revision 1.40  2003/03/10 07:07:58  jbravo
// Small unlagged fixes and voting delay added.
//
// Revision 1.39  2003/03/09 21:30:38  jbravo
// Adding unlagged.   Still needs work.
//
// Revision 1.38  2003/03/08 09:58:08  niceass
// Changes to make my "position on tag with offset" work correctly with orientation matrices crap for CTB tag_weapon2
//
// Revision 1.37  2003/02/27 20:51:14  makro
// no message
//
// Revision 1.36  2003/02/27 19:52:34  makro
// dlights
//
// Revision 1.35  2003/02/01 02:15:31  jbravo
// Replacement models and items
//
// Revision 1.34  2003/01/08 04:46:25  jbravo
// Wrote a new hackish model replacement system
//
// Revision 1.33  2002/09/01 21:14:36  makro
// Sky portal tweaks
//
// Revision 1.32  2002/08/29 23:58:27  makro
// Sky portals
//
// Revision 1.31  2002/08/27 05:10:42  niceass
// new ctb marker shader names
//
// Revision 1.30  2002/07/19 04:33:52  niceass
// laser fog fix and ctb markers
//
// Revision 1.29  2002/06/21 21:06:20  niceass
// laserfog stuff
//
// Revision 1.28  2002/06/21 04:11:17  niceass
// fog laser
//
// Revision 1.27  2002/06/16 20:06:13  jbravo
// Reindented all the source files with "indent -kr -ut -i8 -l120 -lc120 -sob -bad -bap"
//
// Revision 1.26  2002/06/16 19:12:52  jbravo
// Removed the MISSIONPACK ifdefs and missionpack only code.
//
// Revision 1.25  2002/04/23 06:09:18  niceass
// pressure stuff
//
// Revision 1.24  2002/04/09 18:52:13  makro
// Target_speakers can now be toggled on/off
//
// Revision 1.23  2002/04/03 03:13:48  blaze
// NEW BREAKABLE CODE - will break all old breakables(wont appear in maps)
//
// Revision 1.22  2002/03/31 13:52:48  jbravo
// More cleanups
//
// Revision 1.21  2002/03/31 03:31:24  jbravo
// Compiler warning cleanups
//
// Revision 1.20  2002/03/31 02:03:35  niceass
// new testing tag command
//
// Revision 1.19  2002/03/16 21:50:49  niceass
// I tabbed a function properly. It was pissing me off
//
// Revision 1.18  2002/01/14 01:19:23  niceass
// No more default 800 gravity on items - NiceAss
//
// Revision 1.17  2002/01/11 19:48:29  jbravo
// Formatted the source in non DOS format.
//
// Revision 1.16  2001/12/31 16:28:41  jbravo
// I made a Booboo with the Log tag.
//
//
//-----------------------------------------------------------------------------
// Copyright (C) 1999-2000 Id Software, Inc.
//
// cg_ents.c -- present snapshot entities, happens every single frame

#include "cg_local.h"

static void CG_LaserSight(centity_t * cent);
static void CG_Dlight(centity_t * cent);
static void CG_FakeShadow(centity_t * cent);
static void CG_Corona(centity_t * cent);
extern char rq3_breakables[RQ3_MAX_BREAKABLES][80];
extern void trap_R_AddAdditiveLightToScene(const vec3_t org, float intensity, float r, float g, float b);


/*
 * ================
 * RotatePoint
 * ================
 * */
void RotatePoint(vec3_t point, /*const*/ vec3_t matrix[3]) { // FIXME 
	vec3_t tvec;

	VectorCopy(point, tvec);
	point[0] = DotProduct(matrix[0], tvec);
	point[1] = DotProduct(matrix[1], tvec);
	point[2] = DotProduct(matrix[2], tvec);
}

/*
 * ================
 * TransposeMatrix
 * ================
 * */
void TransposeMatrix(/*const*/ vec3_t matrix[3], vec3_t transpose[3]) { // FIXME
	int i, j;
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 3; j++) {
			transpose[i][j] = matrix[j][i];
		}
	}
}

/*
 * ================
 * CreateRotationMatrix
 * ================
 * */
void CreateRotationMatrix(const vec3_t angles, vec3_t matrix[3]) {
	AngleVectors(angles, matrix[0], matrix[1], matrix[2]);
	VectorInverse(matrix[1]);
}

/*
======================
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;
}

/* [QUARANTINE] - CG_PositionWeaponOnTag
======================
CG_PositionWeaponOnTag

Changed from CG_PositionEntityOnTag function to prevent backlerp change in animations
======================
*/
void CG_PositionWeaponOnTag(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;
	vec3_t tempAxis[3];

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

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

Modifies the entities position and axis by the given
tag location
======================
*/
void CG_PositionRotatedOffsetEntityOnTag(refEntity_t * entity, const refEntity_t * parent,
					 qhandle_t parentModel, char *tagName, vec3_t Offset)
{
	int i;
	orientation_t lerped;
	vec3_t tempAxis[3];	//, tmp;

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

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

	for (i = 0; i < 3; i++)
		VectorMA(entity->origin, Offset[i], entity->axis[i], entity->origin);
}

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

FUNCTIONS CALLED EACH FRAME

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

/*
======================
CG_SetEntitySoundPosition

Also called by event processing code
======================
*/
void CG_SetEntitySoundPosition(centity_t * cent)
{
	if (cent->currentState.solid == SOLID_BMODEL)
	{
		vec3_t origin;
		
		VectorCopy(cgs.inlineModelMidpoints[cent->currentState.modelindex], origin);
		//Makro - rotate if needed
		if (cent->lerpAngles[YAW] || cent->lerpAngles[PITCH] || cent->lerpAngles[ROLL])
		{
			vec3_t axis[3];
			AnglesToAxis(cent->lerpAngles, axis);
			ChangeRefSystem(origin, NULL, axis, origin);
		}
		VectorAdd(cent->lerpOrigin, origin, origin);
		trap_S_UpdateEntityPosition(cent->currentState.number, origin);
	} else {
		trap_S_UpdateEntityPosition(cent->currentState.number, cent->lerpOrigin);
	}
}

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

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

	// update sound origins
	CG_SetEntitySoundPosition(cent);

	// add loop sound
	if (cent->currentState.loopSound) {
		//Makro - for speakers, s.weapon == 1 => no PVS check
		if (cent->currentState.eType != ET_SPEAKER || cent->currentState.weapon == 0) {
			trap_S_AddLoopingSound(cent->currentState.number, cent->lerpOrigin, vec3_origin,
				       cgs.gameSounds[cent->currentState.loopSound]);
		} else {
		    //Makro - note: doesn't take into account PVS info
			trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
		              cgs.gameSounds[ cent->currentState.loopSound ] );
		}
	}

	// constant light glow
	if (cent->currentState.constantLight && cent->currentState.eType != ET_DLIGHT) {
		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);
	}

}

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

	s1 = &cent->currentState;

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

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

	// set frame

	ent.frame = s1->frame;
	ent.oldframe = ent.frame;
	ent.backlerp = 0;

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

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

	// add to refresh list
	trap_R_AddRefEntityToScene(&ent);
}

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

Speaker entities can automatically play sounds
==================
*/
static void CG_Speaker(centity_t * cent)
{
	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 * crandom();
}

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

	int boundIndex = 0;
	float offsetMul = 1.f;
	int boundAxis = 2;

	es = &cent->currentState;
	if (es->modelindex >= bg_numItems) {
		CG_Error("Bad item index %i on entity", es->modelindex);
	}
	// if set to invisible, skip
	if (!es->modelindex || (es->eFlags & EF_NODRAW)) {
		return;
	}

	item = &bg_itemlist[es->modelindex];
	if (cg_simpleItems.integer && item->giType != IT_TEAM) {
		memset(&ent, 0, sizeof(ent));
		ent.reType = RT_SPRITE;
		VectorCopy(cent->lerpOrigin, ent.origin);
		// Elder: lower them slightly
		ent.origin[2] -= 6;
		// Elder: make auto-sprites smaller, especially grenades and knifes
		if (item->giType == IT_WEAPON) {
			if (item->giTag == WP_GRENADE || item->giTag == WP_KNIFE)
				ent.radius = 4;
			else
				ent.radius = 14;
		} else if (item->giType == IT_HOLDABLE)
			ent.radius = 10;
		else
			ent.radius = 6;
		//ent.radius = 14;
		ent.customShader = cg_items[es->modelindex].icon;
		ent.shaderRGBA[0] = 255;
		ent.shaderRGBA[1] = 255;
		ent.shaderRGBA[2] = 255;
		ent.shaderRGBA[3] = 255;
		trap_R_AddRefEntityToScene(&ent);
		return;
	}

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

	//Elder: knife rotation code so it's always "in" the wall
	if (item->giTag == WP_KNIFE && ((es->eFlags & FL_THROWN_KNIFE) == FL_THROWN_KNIFE)) {
		VectorCopy(es->angles, cent->lerpAngles);
		//Elder: nudges out a bit so it's more visible
		VectorCopy(es->origin, cent->lerpOrigin);
		AnglesToAxis(cent->lerpAngles, ent.axis);
	} else {
		//Elder: default item orientation
		VectorCopy(cg.autoAngles, cent->lerpAngles);
		AxisCopy(cg.autoAxis, ent.axis);
	}

	if (item->giType == IT_WEAPON && item->giTag != WP_KNIFE &&
	    (es->pos.trDelta[0] != 0 || es->pos.trDelta[1] != 0 || es->pos.trDelta[2] != 0)) {
		vec3_t myvec;

		//Elder: old fashioned AQ2 weapon drop rotating-style code :)
		//It's out here because the wi calculations mess up the lerpOrigin
		VectorCopy(cg.autoAnglesFast, cent->lerpAngles);
		AxisCopy(cg.autoAxisFast, ent.axis);

		VectorCopy(ent.axis[1], myvec);
		VectorNegate(ent.axis[2], ent.axis[1]);
		VectorCopy(myvec, ent.axis[2]);
	} else if (item->giType == IT_HOLDABLE) {
		//Elder: rotate item if moving
		if (es->pos.trDelta[0] != 0 || es->pos.trDelta[1] != 0 || es->pos.trDelta[2] != 0) {
			VectorCopy(cg.autoAnglesFast, cent->lerpAngles);
			AxisCopy(cg.autoAxisFast, ent.axis);
		} else
			AnglesToAxis(es->angles, 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
	//Elder: added knife conditional
	if (item->giType == IT_WEAPON &&
	    !(item->giTag == WP_KNIFE && ((es->eFlags & FL_THROWN_KNIFE) == FL_THROWN_KNIFE))) {

		vec3_t myvec;

		// Elder: bad hack -- but oh well.
		if (es->pos.trDelta[0] == 0 && es->pos.trDelta[1] == 0 && es->pos.trDelta[2] == 0)
			AnglesToAxis(es->angles, ent.axis);

		//CG_Printf("Should not be in here if it's a thrown knife\n");
		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];
		// Makro - did we really need this?
		/*
		cent->lerpOrigin[2] -=
		    wi->weaponMidpoint[0] * ent.axis[0][2] +
		    wi->weaponMidpoint[1] * ent.axis[1][2] + wi->weaponMidpoint[2] * ent.axis[2][2];
			*/
		
		if (es->pos.trDelta[0] == 0 && es->pos.trDelta[1] == 0 && es->pos.trDelta[2] == 0) {
			// Blaze: rotate the gun by 90 degrees to place it on the ground
			VectorCopy(ent.axis[1], myvec);
			VectorNegate(ent.axis[2], ent.axis[1]);
			VectorCopy(myvec, ent.axis[2]);
			boundIndex = 1;
			boundAxis = 1;
			offsetMul = -1.f;
		}
	}
	// JBravo: world skins
	if (item->giType == IT_WEAPON) {
		wi = &cg_weapons[item->giTag];
		if (wi->customSkin)
			ent.customSkin = wi->customSkin;
	}
	if (item->giType == IT_HOLDABLE || item->giType == IT_AMMO) {
		itemInfo = &cg_items[item - bg_itemlist];
		if (itemInfo->customSkin)
			ent.customSkin = itemInfo->customSkin;
	}

	ent.hModel = cg_items[es->modelindex].models[0];

	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 (msec >= 0 && msec < ITEM_SCALEUP_TIME) {
		frac = (float) msec / ITEM_SCALEUP_TIME;
		VectorScale(ent.axis[0], frac, ent.axis[0]);
		VectorScale(ent.axis[1], frac, ent.axis[1]);
		VectorScale(ent.axis[2], frac, ent.axis[2]);
		ent.nonNormalizedAxes = qtrue;
	} else {
		frac = 1.0;
	}

	// Elder: special items and ammo should have minimum light too
	// items without glow textures need to keep a minimum light value
	// so they are always visible
	if ((item->giType == IT_WEAPON) ||
	    (item->giType == IT_ARMOR) || (item->giType == IT_AMMO) || (item->giType == IT_HOLDABLE)) {
		ent.renderfx |= RF_MINLIGHT;
	}
	// increase the size of the weapons when they are presented as items

	// Elder: only for knives, which are hard to spot
	// NiceAss: Scale code modified for weapons too.
	if (item->giTag == WP_KNIFE)
		scale = WEAPON_KNIFE_SCALE;
	else if (item->giType == IT_WEAPON && item->giTag != WP_KNIFE)
		scale = (float) WEAPON_GUN_SCALE;
	else
		scale = WEAPON_OTHER_SCALE;

	VectorScale(ent.axis[0], scale, ent.axis[0]);
	VectorScale(ent.axis[1], scale, ent.axis[1]);
	VectorScale(ent.axis[2], scale, ent.axis[2]);

	// Makro - offset based on bounding box, not magic numbers
	{
		vec3_t bound[2];
		float offset;
		
		trap_R_ModelBounds(ent.hModel, bound[0], bound[1]);
		offset = -ITEM_RADIUS - bound[boundIndex][boundAxis] * offsetMul * scale;

		// Makro - nudge them up a bit
		// the strobe effect looks a lot better if it isn't clipped by the ground
		// Note - magic number :)
		offset += 0.5f;

		ent.origin[2] += offset;
		ent.oldorigin[2] += offset;
	}

	// add strobe effect -- should make this toggle?
	// NiceAss: Temp Cvar usage for strobe shader.
	if ((item->giType == IT_WEAPON ||
	     item->giType == IT_ARMOR ||
	     item->giType == IT_AMMO || item->giType == IT_HOLDABLE) && cg_RQ3_strobe.integer) {
		const float MAX_DIST = 512.f;
		vec3_t delta;
		float dist;
		
		VectorSubtract(cg.refdef.vieworg, ent.origin, delta);
		dist = VectorLengthSquared(delta);

		if (dist <= MAX_DIST * MAX_DIST) {
			refEntity_t glow = ent;
			float frac = 1.f - sqrt(dist) * (1.f / MAX_DIST);
			const float freq = 1.f;
			vec4_t color;

			frac *= frac;

			VectorCopy(g_color_table[cg_RQ3_strobeColor.integer % ARRAY_LEN(g_color_table)], color);
			
			// Makro - constant alpha for values >= 2, otherwise pulse
			if (cg_RQ3_strobe.integer > 1)
				color[3] = Com_Clamp(0.f, 1.f, frac);
			else
				color[3] = Com_Clamp(0.f, 1.f, frac * (sin(freq * 2.f * M_PI * cg.time / 1000.f) * 0.5f + 0.5f));
			
			glow.shaderRGBA[0] = (int) (color[0] * 255.f + 0.5f);
			glow.shaderRGBA[1] = (int) (color[1] * 255.f + 0.5f);
			glow.shaderRGBA[2] = (int) (color[2] * 255.f + 0.5f);
			glow.shaderRGBA[3] = (int) (color[3] * 255.f + 0.5f);
			glow.customShader = cgs.media.itemStrobeShader;
			trap_R_AddRefEntityToScene(&glow);
		}
	}

	// add to refresh list
	trap_R_AddRefEntityToScene(&ent);

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

				// scale up if respawning
				if (frac != 1.0) {
					VectorScale(ent.axis[0], frac, ent.axis[0]);
					VectorScale(ent.axis[1], frac, ent.axis[1]);
					VectorScale(ent.axis[2], frac, ent.axis[2]);
					ent.nonNormalizedAxes = qtrue;
				}
				trap_R_AddRefEntityToScene(&ent);
			}
		}
	}
}

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

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

	s1 = &cent->currentState;
	if (s1->weapon >= WP_NUM_WEAPONS) {
		s1->weapon = 0;
	}
	weapon = &cg_weapons[s1->weapon];

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

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

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

		trap_S_AddLoopingSound(cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound);
	}
	// create the render entity
	memset(&ent, 0, sizeof(ent));
	VectorCopy(cent->lerpOrigin, ent.origin);
	VectorCopy(cent->lerpOrigin, ent.oldorigin);

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

	// 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) {
		//Spin knife like a wheel
		if (s1->weapon == WP_KNIFE) {
			vec3_t knifeVelocity;

			CG_EvaluateTrajectoryDelta(&s1->pos, cg.time, knifeVelocity);
			vectoangles(knifeVelocity, cent->lerpAngles);
			cent->lerpAngles[0] += cg.time;	// was / 6

			AnglesToAxis(cent->lerpAngles, ent.axis);

			// NiceAss: Added for scaling of the knife in flight and not just when sticking in a wall.
			VectorScale(ent.axis[0], WEAPON_KNIFE_SCALE, ent.axis[0]);
			VectorScale(ent.axis[1], WEAPON_KNIFE_SCALE, ent.axis[1]);
			VectorScale(ent.axis[2], WEAPON_KNIFE_SCALE, ent.axis[2]);
		} else
			RotateAroundDirection(ent.axis, cg.time / 4);
	} else {
		RotateAroundDirection(ent.axis, s1->time);
	}

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

/*
===============
CG_Grapple

This is called when the grapple is sitting up against the wall
===============
*/
static void CG_Grapple(centity_t * cent)
{
	refEntity_t ent;
	entityState_t *s1;
	const weaponInfo_t *weapon;

	s1 = &cent->currentState;
	if (s1->weapon >= WP_NUM_WEAPONS) {
		s1->weapon = 0;
	}
	weapon = &cg_weapons[s1->weapon];

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

#if 0				// FIXME add grapple pull sound here..?
	// add missile sound
	if (weapon->missileSound) {
		trap_S_AddLoopingSound(cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound);
	}
#endif

	// Will draw cable if needed
//Blaze: Not needed
//      CG_GrappleTrail ( cent, weapon );

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

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

	// convert direction of travel into axis
	if (VectorNormalize2(s1->pos.trDelta, ent.axis[0]) == 0) {
		ent.axis[0][2] = 1;
	}

	trap_R_AddRefEntityToScene(&ent);
}

/*
===============
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));
	VectorCopy(cent->lerpOrigin, ent.origin);
	VectorCopy(cent->lerpOrigin, ent.oldorigin);
	AnglesToAxis(cent->lerpAngles, ent.axis);

	ent.renderfx = RF_NOSHADOW;

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

	// add to refresh list
	trap_R_AddRefEntityToScene(&ent);

	// add the secondary model
	if (s1->modelindex2) {
		ent.skinNum = 0;
		ent.hModel = cgs.gameModels[s1->modelindex2];
		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;

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

	// add to refresh list
	trap_R_AddRefEntityToScene(&ent);
}

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

Also called by client movement prediction code
=========================
*/

//Makro - made it so that angles get adjusted, too
void CG_AdjustPositionForMover(const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out, vec3_t angleOut)
{
	centity_t *cent;
	vec3_t oldOrigin, origin, deltaOrigin;
	vec3_t oldAngles, angles, deltaAngles;

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

	cent = &cg_entities[moverNum];
	//if (cent->currentState.eType != ET_MOVER) {
	//Makro - adjust for movers and attached entities
	if ((cent->currentState.eType != ET_PLAYER && ((cent->currentState.eFlags & EF_ATTACHED) == 0)) //if not attached
		&& cent->currentState.eType != ET_MOVER)	//and not a mover, either
	{
		VectorCopy(in, out);
		return;
	}

	//Makro - if mover is blocked, don't do anything
	if (cent->currentState.eFlags & EF_MOVER_BLOCKED)
	{
		VectorCopy(in, out);
		return;
	}

	//CG_EvaluateTrajectory(&cent->currentState.pos, fromTime, oldOrigin);
	//CG_EvaluateTrajectory(&cent->currentState.apos, fromTime, oldAngles);
	CG_EvaluateTrajectoryEx(cent, fromTime, oldOrigin, oldAngles);

	//CG_EvaluateTrajectory(&cent->currentState.pos, toTime, origin);
	//CG_EvaluateTrajectory(&cent->currentState.apos, toTime, angles)
	CG_EvaluateTrajectoryEx(cent, toTime, origin, angles);

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

	VectorAdd(in, deltaOrigin, out);
	

	// FIXME: origin change when on a rotating object
	//Makro - okay
	if (angleOut)
		VectorAdd(angleOut, deltaAngles, angleOut);

	if (deltaAngles[0] || deltaAngles[1] || deltaAngles[2])
	{
		vec3_t matrix[3], transpose[3];
		VectorSubtract(in, oldOrigin, oldOrigin);
		VectorCopy(oldOrigin, deltaOrigin);
		CreateRotationMatrix(deltaAngles, transpose);
		TransposeMatrix(transpose, matrix);
		RotatePoint(deltaOrigin, matrix);
		VectorSubtract(deltaOrigin, oldOrigin, deltaOrigin);
		VectorAdd(out, deltaOrigin, out);
/*
		float norm;
		VectorSubtract(in, origin, deltaOrigin);
		norm = VectorLength(deltaOrigin);
		vectoangles(deltaOrigin, angles);
		VectorAdd(angles, deltaAngles, angles);		
		AngleVectors(angles, origin, NULL, NULL);
		VectorScale(origin, norm, origin);
		VectorSubtract(origin, deltaOrigin, deltaOrigin);
		VectorAdd(out, deltaOrigin, out);
*/
	}
}

/*
=============================
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) {
		CG_Error("CG_InterpolateEntityPosition: cg.nextSnap == NULL");
	}

	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
	CG_EvaluateTrajectory(&cent->currentState.pos, cg.snap->serverTime, current);
	CG_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]);

	CG_EvaluateTrajectory(&cent->currentState.apos, cg.snap->serverTime, current);
	CG_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

===============
*/
static void CG_CalcEntityLerpPositions(centity_t * cent)
{
	// this is done server-side now - cg_smoothClients is undefined
	// players will always be TR_INTERPOLATE
	// if this player does not want to see extrapolated players
// JBravo: done serverside now via unlagged
/*	if (!cg_smoothClients.integer) {
		// make sure the clients use TR_INTERPOLATE
		if (cent->currentState.number < MAX_CLIENTS) {
			cent->currentState.pos.trType = TR_INTERPOLATE;
			cent->nextState.pos.trType = TR_INTERPOLATE;
		}
	}
*/
	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 < MAX_CLIENTS) {
		CG_InterpolateEntityPosition(cent);
		return;
	}
// JBravo: unlagged
	if (cent->currentState.number < MAX_CLIENTS &&
			cent->currentState.clientNum != cg.predictedPlayerState.clientNum) {
		cent->currentState.pos.trType = TR_LINEAR_STOP;
		cent->currentState.pos.trTime = cg.snap->serverTime;
		cent->currentState.pos.trDuration = 1000 / sv_fps.integer;
	}

	// just use the current frame and evaluate as best we can
	//Makro - if this is a mover, it might be blocked
	if (cent->currentState.eType == ET_MOVER && (cent->currentState.eFlags & EF_MOVER_BLOCKED) != 0)
	{
		//no prediction in this case
		//CG_EvaluateTrajectory(&cent->currentState.pos, cg.snap->serverTime, cent->lerpOrigin);
		//CG_EvaluateTrajectory(&cent->currentState.apos, cg.snap->serverTime, cent->lerpAngles);
		CG_EvaluateTrajectoryEx(cent, cg.snap->serverTime, cent->lerpOrigin, cent->lerpAngles);
	} else {
		//CG_EvaluateTrajectory(&cent->currentState.pos, cg.time, cent->lerpOrigin);
		//CG_EvaluateTrajectory(&cent->currentState.apos, cg.time, cent->lerpAngles);
		CG_EvaluateTrajectoryEx(cent, cg.time, cent->lerpOrigin, cent->lerpAngles);
	}

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

/*
===============
CG_TeamBase
===============
*/
static void CG_TeamBase(centity_t * cent)
{
	refEntity_t model;

	if (cgs.gametype == GT_CTF) {
		// show the flag base
		memset(&model, 0, sizeof(model));
		model.reType = RT_MODEL;
		VectorCopy(cent->lerpOrigin, model.lightingOrigin);
		VectorCopy(cent->lerpOrigin, model.origin);
		AnglesToAxis(cent->currentState.angles, model.axis);
		if (cent->currentState.modelindex == TEAM_RED) {
			model.hModel = cgs.media.redFlagBaseModel;
		} else if (cent->currentState.modelindex == TEAM_BLUE) {
			model.hModel = cgs.media.blueFlagBaseModel;
		} else {
			model.hModel = cgs.media.neutralFlagBaseModel;
		}
		trap_R_AddRefEntityToScene(&model);
	}
}

/*
===============
CG_DrawDecal	By NiceAss
===============
*/
static void CG_DrawDecal(centity_t * cent)
{
	trace_t	trace;
	vec3_t	end;
	//float	alpha, radius;
	float	radius;

	if (cgs.gametype == GT_CTF) {
		//cent->currentState.modelindex 
		
		VectorCopy(cent->lerpOrigin, end);
		end[2] -= 8192;

//		alpha = sin(cg.time / 160.0f) * 0.25 + 0.75f;
		radius = 48 + (cos(cg.time / 320.0f) * 4.0f);

		// Project downward to the ground
		CG_Trace(&trace, cent->lerpOrigin, NULL, NULL, end, 0, CONTENTS_SOLID | CONTENTS_PLAYERCLIP);

		if (cent->currentState.modelindex == TEAM_RED)
			CG_ImpactMark(cgs.media.ctbXMarkSilver, trace.endpos, trace.plane.normal, 45.0f,
				      1, 1, 1, 1, qfalse, radius, qtrue);
		else
			CG_ImpactMark(cgs.media.ctbOMarkBlack, trace.endpos, trace.plane.normal, 45.0f,
				      1, 1, 1, 1, qfalse, radius, qtrue);
	}
}
/*
===============
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;
	}
	// calculate the current origin
	CG_CalcEntityLerpPositions(cent);

	// add automatic effects
	CG_EntityEffects(cent);

	switch (cent->currentState.eType) {
	default:
		CG_Error("Bad entity type: %i", cent->currentState.eType);
		break;
	case ET_INVISIBLE:
	case ET_PUSH_TRIGGER:
	case ET_TELEPORT_TRIGGER:
		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_MOVER:
		CG_Mover(cent);
		break;
	case ET_BREAKABLE:
		CG_Mover(cent);
		break;
	case ET_PRESSURE:
		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_GRAPPLE:
		CG_Grapple(cent);
		break;
	case ET_TEAM:
		CG_TeamBase(cent);
		break;
	case ET_DECAL:
		CG_DrawDecal(cent);
		break;
	case ET_LASER:
		//Elder: the local laser call is checked in playerstate unless it is disabled
		//if (!cg_RQ3_laserAssist.integer || cent->currentState.clientNum != cg.snap->ps.clientNum)
		CG_LaserSight(cent);
		break;
	case ET_DLIGHT:
		CG_Dlight(cent);
		break;
	case ET_CORONA:
		CG_Corona(cent);
		break;
	case ET_SHADOW:
		CG_FakeShadow(cent);
		break;
	}
}

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

Makro - added skyportal param
===============
*/

//Makro - added
int cmpSnapEntities(const void *a, const void *b)
{
	return cg_moveParentRanks[((entityState_t*)b)->number] - cg_moveParentRanks[((entityState_t*)a)->number];
}

void CG_AddPacketEntities(int mode)
{
	centity_t *cent;
	int num;

	//Makro - if we're rendering the entities in a sky portal, we don't need this stuff
	if (mode != ADDENTS_SKYPORTAL) {
		playerState_t *ps;

		// 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] = 0;	//Blaze: changed this ( cg.time & 2047 ) * 360 / 2048.0; to 0;
		cg.autoAngles[2] = 0;
	
		cg.autoAnglesFast[0] = 0;
		//Elder: restored for weapon rotation
		cg.autoAnglesFast[1] = (cg.time & 1023) * 360 / 1024.0f;
		cg.autoAnglesFast[2] = 0;

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

		// generate and add the entity from the playerstate
		ps = &cg.predictedPlayerState;
		BG_PlayerStateToEntityState(ps, &cg.predictedPlayerEntity.currentState, qfalse);
		CG_AddCEntity(&cg.predictedPlayerEntity);

		// lerp the non-predicted value for lightning gun origins
		CG_CalcEntityLerpPositions(&cg_entities[cg.snap->ps.clientNum]);
// JBravo: unlagged
		if (cg.nextSnap) {
			for (num = 0 ; num < cg.nextSnap->numEntities ; num++) {
				//Makro - use pre-determined order so that attached entities are added before their "parents"
				cent = &cg_entities[cg.nextSnap->entities[cg_nextSnapEntityOrder[num]].number];
				if (cent->nextState.eType == ET_MISSILE || cent->nextState.eType == ET_GENERAL) {
					CG_TransitionEntity(cent);
					cent->interpolate = qtrue;
					CG_AddCEntity(cent);
				}
			}
		}
	}

	//Makro - if we have a sky portal
	if (mode != ADDENTS_NOSKYPORTAL) {
		// add each entity sent over by the server
		for (num = 0; num < cg.snap->numEntities; num++) {
			//Makro - use pre-determined order so that attached entities are added before their "parents"
			cent = &cg_entities[cg.snap->entities[cg_snapEntityOrder[num]].number];
			//if we're adding sky portal entities
			if (mode == ADDENTS_SKYPORTAL) {
				if (cent->currentState.eFlags & EF_HEADLESS) {
					CG_AddCEntity(cent);
				}
			//if not, we're adding normal entities
			} else {
				if (!(cent->currentState.eFlags & EF_HEADLESS) || cent->currentState.eType == ET_PLAYER) {
					CG_AddCEntity(cent);
				}
			}
		}
	//no sky portal defined, just add all the entities without checking if they're
	//sky portal entities or not (faster)
	} else {
		for (num = 0; num < cg.snap->numEntities; num++) {
			//Makro - use pre-determined order so that attached entities are added before their "parents"
			cent = &cg_entities[cg.snap->entities[cg_snapEntityOrder[num]].number];
			if (!cg.nextSnap || (cent->nextState.eType != ET_MISSILE && cent->nextState.eType != ET_GENERAL)) {
				CG_AddCEntity(cent);
			}
		}
	}
}

/*
==================
CG_LaserSight

Creates the laser dot
Elder's Note: Client does not use this if the dot is his/her own -- see CG_LocalLaser
NiceAss's Note: Lies?! It seems to for me =P I don't even think CG_LocalLaser gets called...
==================
*/

static void CG_LaserSight(centity_t * cent)
{
	refEntity_t ent;

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

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

	if (cent->currentState.eventParm == 1) {
		ent.reType = RT_SPRITE;
		ent.radius = 3;
		ent.rotation = 0;
		ent.customShader = cgs.media.laserShader;

		// NiceAss: If it's your laser dot, draw it
		// or if not in the fog and not your laser dot draw it
		if ( ( cent->currentState.clientNum == cg.clientNum || 
			(!(trap_CM_PointContents(cent->lerpOrigin, 0) & CONTENTS_FOG) && 
			cent->currentState.clientNum != cg.clientNum) ) &&	
			cg_enableLaserFog.integer )
			trap_R_AddRefEntityToScene(&ent);
	} else {
		trap_R_AddLightToScene(ent.origin, 200, 1, 1, 1);
	}
}

/*
=================
CG_Dlight
Added by Elder.

Use sparingly.
=================
*/
//Makro - dlight styles
char dlightStyles[MAX_DLIGHT_STYLES][MAX_DLIGHT_STLE_LEN];
int dlightStyleCount;

#define DLIGHT_FRAMETIME	50

float Dlight_IntensityForChar(char c)
{
	if (c>= 'a' && c<='z')
		return ((float)(c-'a'))/((float)('z'-'a'));
	else if (c>= 'A' && c<='Z')
		return ((float)(c-'A'))/((float)('Z'-'A'));
	else if (c>= '0' && c<='9')
		return ((float)(c-'0'))/((float)('9'-'0'));
	else
		return 1.0f;
}

static void CG_Dlight(centity_t * cent)
{
	//Makro - kinda hackish, but oh well...
	//allows us to trigger them on off; SVF_NOCLIENT should've done this already, though
	if (!(cent->currentState.eFlags & EF_NODRAW)) {
		int cl, dls;
		float i, r, g, b, i2;

		dls = cent->currentState.eventParm & DLIGHT_CUSTOMSTYLE;
		cl = cent->currentState.constantLight;
		r = (cl & 255) / 255.0f;
		g = ((cl >> 8) & 255) / 255.0f;
		b = ((cl >> 16) & 255) / 255.0f;
		i = ((cl >> 24) & 255) * 8;
		i2 = cent->currentState.weapon;

		if (cent->currentState.eventParm & DLIGHT_FLICKER)
			i += random() * (i2-i);

		if (cent->currentState.eventParm & DLIGHT_PULSE)
		{
			float frequency = cent->currentState.powerups / 1000.0f;
			float phase = 2 * M_PI * (frequency * cg.time / 1000.0f + cent->currentState.otherEntityNum2 / 1000.0f);
			//CG_Printf("cgame: (%f %f %f) %f -> %f = ", r, g, b, i, i2);
			i += (sin(phase) + 1) * (i2-i) / 2.0f;
			//CG_Printf("%f\n", i);
		}

		if (dls>0)
		{
			int slen = strlen(dlightStyles[dls-1]);
			if (slen)
			{
				int index = (cg.time / DLIGHT_FRAMETIME) % slen, nindex = index+1 % slen;
				int dtime = cg.time % DLIGHT_FRAMETIME;
				float f1 = Dlight_IntensityForChar(dlightStyles[dls-1][index]);
				float f2 = Dlight_IntensityForChar(dlightStyles[dls-1][nindex]);
				float frac = (f2 * dtime + f1 * (DLIGHT_FRAMETIME-dtime)) / DLIGHT_FRAMETIME;
				r *= frac;
				g *= frac;
				b *= frac;
			}
		}

		if (cent->currentState.eventParm & DLIGHT_ADDITIVE)
			trap_R_AddAdditiveLightToScene(cent->lerpOrigin, i, r, g, b);
		else
			trap_R_AddLightToScene(cent->lerpOrigin, i, r, g, b);
	}
}

static void CG_FakeShadow(centity_t * cent)
{
	return;
}

static void CG_Corona(centity_t * cent)
{
	refEntity_t ent;
	
	memset(&ent, 0, sizeof(ent));
	VectorCopy(cent->currentState.origin, ent.origin);
	ent.reType = RT_SPRITE;
	ent.customShader = cgs.media.coronaShader;
	ent.radius = 32;
	if (0)
		ent.renderfx = RF_DEPTHHACK;
	ent.shaderRGBA[0] = 255;
	ent.shaderRGBA[1] = ent.shaderRGBA[0];
	ent.shaderRGBA[2] = ent.shaderRGBA[0];
	ent.shaderRGBA[3] = ent.shaderRGBA[0];
	trap_R_AddRefEntityToScene(&ent);
}
