//-----------------------------------------------------------------------------
//
// $Id$
//
//-----------------------------------------------------------------------------
//
// $Log: cg_draw.c,v $
// Revision 1.80  2007/01/26 21:17:21  makro
// Made the 'vignetting' effect zoom-dependent
//
// Revision 1.79  2006/04/14 18:16:31  makro
// no message
//
// Revision 1.78  2005/02/15 16:33:38  makro
// Tons of updates (entity tree attachment system, UI vectors)
//
// Revision 1.77  2004/01/26 21:26:08  makro
// no message
//
// Revision 1.76  2003/09/20 19:38:16  makro
// Lens flares, what else ?
//
// Revision 1.75  2003/09/19 00:54:23  makro
// Flares again
//
// Revision 1.74  2003/09/17 23:49:29  makro
// Lens flares. Opendoor trigger_multiple fixes
//
// Revision 1.73  2003/07/30 16:05:46  makro
// no message
//
// Revision 1.72  2003/03/28 22:25:10  makro
// no message
//
// Revision 1.71  2003/03/28 10:36:02  jbravo
// Tweaking the replacement system a bit.  Reactionmale now the default model
//
// Revision 1.70  2003/03/09 21:30:38  jbravo
// Adding unlagged.   Still needs work.
//
// Revision 1.69  2002/10/30 21:24:47  jbravo
// Minor helmet tweaking
//
// Revision 1.68  2002/10/26 22:03:42  jbravo
// Made TeamDM work RQ3 style.
//
// Revision 1.67  2002/10/26 00:37:18  jbravo
// New multiple item code and added PB support to the UI
//
// Revision 1.66  2002/08/30 06:23:57  niceass
// disabled wallhack protection #2!!!
//
// Revision 1.65  2002/08/29 14:24:26  niceass
// new wallhack thing
//
// Revision 1.64  2002/08/29 04:45:25  niceass
// color changes for new outlined font
//
// Revision 1.63  2002/08/22 03:32:10  niceass
// countdown code added
//
// Revision 1.62  2002/08/21 07:00:07  jbravo
// Added CTB respawn queue and fixed game <-> cgame synch problem in CTB
//
// Revision 1.61  2002/08/07 04:46:20  niceass
// ctb changes
//
// Revision 1.60  2002/07/22 06:31:32  niceass
// cleaned up the powerup code
//
// Revision 1.59  2002/07/19 04:34:48  niceass
// drawping fixed
//
// Revision 1.58  2002/07/16 04:31:18  niceass
// I think I fixed cg_drawping
//
// Revision 1.57  2002/07/11 04:26:12  niceass
// testing new cg_drawping code
//
// Revision 1.56  2002/07/09 05:44:08  niceass
// ctb fixes
//
// Revision 1.55  2002/07/03 02:40:14  niceass
// fix cg_drawping problem
//
// Revision 1.54  2002/06/29 21:58:54  niceass
// big changes in cg_drawping
//
// Revision 1.53  2002/06/29 04:15:15  jbravo
// CTF is now CTB.  no weapons while the case is in hand other than pistol or knife
//
// Revision 1.52  2002/06/24 05:55:50  niceass
// drawping
//
// Revision 1.51  2002/06/23 23:09:20  niceass
// modified upper right scores
//
// Revision 1.50  2002/06/23 21:44:07  jbravo
// Fixed shots fired stats for non TP modes and some cleanups
//
// Revision 1.49  2002/06/21 23:20:34  blaze
// cg_rq3_overlaycrosshair when set to 1 will draw your non zoomed crosshair overtop of your ssg crosshair
//
// Revision 1.48  2002/06/20 21:25:06  slicer
// Fixed issues with cg_drawCrosshair 0, for SSG Scope and teammates Names
//
// Revision 1.47  2002/06/17 04:59:50  niceass
// redid the voting. Hope you like it.
//
// Revision 1.46  2002/06/16 20:06:13  jbravo
// Reindented all the source files with "indent -kr -ut -i8 -l120 -lc120 -sob -bad -bap"
//
// Revision 1.45  2002/06/16 19:12:52  jbravo
// Removed the MISSIONPACK ifdefs and missionpack only code.
//
// Revision 1.44  2002/06/03 05:23:47  niceass
// spectator changes
//
// Revision 1.43  2002/06/03 00:48:32  niceass
// match scoreboard changes
//
// Revision 1.42  2002/05/19 18:32:11  jbravo
// Made new cvars for regular xhair colors.
//
// Revision 1.41  2002/05/18 03:55:35  niceass
// many misc. changes
//
// Revision 1.40  2002/05/13 05:24:54  jbravo
// the ssg color cvars now also control normal xhair color
//
// Revision 1.39  2002/05/01 21:14:59  jbravo
// Misc fixes
//
// Revision 1.38  2002/05/01 03:27:17  niceass
// centerprint fix + prettier
//
// Revision 1.37  2002/04/29 06:10:48  niceass
// better centerprint
//
// Revision 1.36  2002/04/29 01:24:34  niceass
// frag counter added
//
// Revision 1.35  2002/04/23 00:21:44  jbravo
// Cleanups of the new model code.  Removed the spectator bar for zcam modes.
//
// Revision 1.34  2002/04/06 21:43:58  makro
// New surfaceparm system
//
// Revision 1.33  2002/03/31 03:31:24  jbravo
// Compiler warning cleanups
//
// Revision 1.32  2002/03/30 02:29:43  jbravo
// Lots of spectator code updates. Removed debugshit, added some color.
//
// Revision 1.31  2002/03/24 22:50:52  niceass
// misc. 2d screen stuff changed
//
// Revision 1.30  2002/03/23 05:17:42  jbravo
// Major cleanup of game -> cgame communication with LCA vars.
//
// Revision 1.29  2002/03/21 15:02:05  jbravo
// More teamname cleanups and fix for fraglines.
//
// Revision 1.28  2002/03/17 21:32:23  jbravo
// Fixed the dynamic teamnames system up a bit to reduce traffic
//
// Revision 1.27  2002/03/17 13:18:14  jbravo
// Replace the fraglimit on the HUD with the clients score
//
// Revision 1.26  2002/03/17 00:40:23  jbravo
// Adding variable team names. g_RQ3_team1name and g_RQ3_team2name. Fixed
// Slicers fraglimit check.
//
// Revision 1.25  2002/03/04 20:50:59  jbravo
// No floating scores over dead bodies, triangles disabled, and no viewing
// names of enemys just of teammates.
//
// Revision 1.24  2002/03/04 19:28:21  jbravo
// Fixing follownames up as suggested in the forums.
//
// Revision 1.23  2002/03/02 14:54:24  jbravo
// Added the skin and model names to the name of the player thats being
// followed, as in AQ
//
// Revision 1.22  2002/02/28 05:41:54  blaze
// weapons stats on client side
//
// Revision 1.21  2002/02/25 19:41:53  jbravo
// Fixed the use ESC and join menu to join teams when dead players are
// spectating in TP mode.
// Tuned the autorespawn system a bit.  Now dead ppl. are dead for a very
// small time before they are made into spectators.
//
// Revision 1.20  2002/02/23 18:07:46  slicer
// Changed Sniper code and Cam code
//
// Revision 1.19  2002/01/11 20:20:57  jbravo
// Adding TP to main branch
//
// Revision 1.18  2002/01/11 19:48:29  jbravo
// Formatted the source in non DOS format.
//
// Revision 1.17  2001/12/31 16:28:41  jbravo
// I made a Booboo with the Log tag.
//
//
//-----------------------------------------------------------------------------
// Copyright (C) 1999-2000 Id Software, Inc.
//
// cg_draw.c -- draw all of the graphical elements during
// active (after loading) gameplay

#include "cg_local.h"

// JBravo: warning fix
void CG_DrawWeaponStats(void);

int drawTeamOverlayModificationCount = -1;
int sortedTeamPlayers[TEAM_MAXOVERLAY];
int numSortedTeamPlayers;
char systemChat[256];
char teamChat1[256];
char teamChat2[256];



#define	LAG_SAMPLES		128

typedef struct {
	int frameSamples[LAG_SAMPLES];
	int frameCount;
	int snapshotFlags[LAG_SAMPLES];
	int snapshotSamples[LAG_SAMPLES];
	int snapshotCount;
} lagometer_t;

lagometer_t lagometer;


/*
==============
CG_DrawField

Draws large numbers for status bar and powerups
==============
*/
static void CG_DrawField(int x, int y, int width, int value)
{
	char num[16], *ptr;
	int l;
	int frame;

	if (width < 1) {
		return;
	}
	// draw number string
	if (width > 5) {
		width = 5;
	}

	switch (width) {
	case 1:
		value = value > 9 ? 9 : value;
		value = value < 0 ? 0 : value;
		break;
	case 2:
		value = value > 99 ? 99 : value;
		value = value < -9 ? -9 : value;
		break;
	case 3:
		value = value > 999 ? 999 : value;
		value = value < -99 ? -99 : value;
		break;
	case 4:
		value = value > 9999 ? 9999 : value;
		value = value < -999 ? -999 : value;
		break;
	}

	Com_sprintf(num, sizeof(num), "%i", value);
	l = strlen(num);
	if (l > width)
		l = width;
	x += 2 + CHAR_WIDTH * (width - l);

	ptr = num;
	while (*ptr && l) {
		if (*ptr == '-')
			frame = STAT_MINUS;
		else
			frame = *ptr - '0';

		CG_DrawPic(x, y, CHAR_WIDTH, CHAR_HEIGHT, cgs.media.numberShaders[frame]);
		x += CHAR_WIDTH;
		ptr++;
		l--;
	}
}

/*
================
CG_Draw3DModel

================
*/
void CG_Draw3DModel(float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles)
{
	refdef_t refdef;
	refEntity_t ent;

	if (!cg_draw3dIcons.integer || !cg_drawIcons.integer) {
		return;
	}

	CG_AdjustFrom640(&x, &y, &w, &h);

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

	memset(&ent, 0, sizeof(ent));
	AnglesToAxis(angles, ent.axis);
	VectorCopy(origin, ent.origin);
	ent.hModel = model;
	ent.customSkin = skin;
	ent.renderfx = RF_NOSHADOW;	// no stencil shadows

	refdef.rdflags = RDF_NOWORLDMODEL;

	AxisClear(refdef.viewaxis);

	refdef.fov_x = 30;
	refdef.fov_y = 30;

	refdef.x = x;
	refdef.y = y;
	refdef.width = w;
	refdef.height = h;

	refdef.time = cg.time;

	trap_R_ClearScene();
	trap_R_AddRefEntityToScene(&ent);
	trap_R_RenderScene(&refdef);
}

/*
================
CG_DrawHead

Used for both the status bar and the scoreboard
================
*/
void CG_DrawHead(float x, float y, float w, float h, int clientNum, vec3_t headAngles)
{
	clipHandle_t cm;
	clientInfo_t *ci;
	float len;
	vec3_t origin;
	vec3_t mins, maxs;

	ci = &cgs.clientinfo[clientNum];

	if (cg_draw3dIcons.integer) {
		cm = ci->headModel;
		if (!cm) {
			return;
		}
		// offset the origin y and z to center the head
		trap_R_ModelBounds(cm, mins, maxs);

		origin[2] = -0.5 * (mins[2] + maxs[2]);
		origin[1] = 0.5 * (mins[1] + maxs[1]);

		// calculate distance so the head nearly fills the box
		// assume heads are taller than wide
		len = 0.7 * (maxs[2] - mins[2]);
		origin[0] = len / 0.268;	// len / tan( fov/2 )

		// allow per-model tweaking
		VectorAdd(origin, ci->headOffset, origin);

		CG_Draw3DModel(x, y, w, h, ci->headModel, ci->headSkin, origin, headAngles);
	} else if (cg_drawIcons.integer) {
		CG_DrawPic(x, y, w, h, ci->modelIcon);
	}
	// if they are deferred, draw a cross out
	if (ci->deferred) {
		CG_DrawPic(x, y, w, h, cgs.media.deferShader);
	}
}

/*
================
CG_DrawFlagModel

Used for both the status bar and the scoreboard
================
*/
void CG_DrawFlagModel(float x, float y, float w, float h, int team, qboolean force2D)
{
	qhandle_t cm;
	float len;
	vec3_t origin, angles;
	vec3_t mins, maxs;
	qhandle_t handle;

	if (!force2D && cg_draw3dIcons.integer) {

		VectorClear(angles);

		cm = cgs.media.redFlagModel;

		// offset the origin y and z to center the flag
		trap_R_ModelBounds(cm, mins, maxs);

		origin[2] = -0.5 * (mins[2] + maxs[2]);
		origin[1] = 0.5 * (mins[1] + maxs[1]);

		// calculate distance so the flag nearly fills the box
		// assume heads are taller than wide
		len = 0.5 * (maxs[2] - mins[2]);
		origin[0] = len / 0.268;	// len / tan( fov/2 )

//		angles[YAW] = 60 * sin(cg.time / 2000.0);
		angles[YAW] = cg.time / 8.0f;

		if (team == TEAM_RED) {
			handle = cgs.media.redFlagModel;
		} else if (team == TEAM_BLUE) {
			handle = cgs.media.blueFlagModel;
		} else if (team == TEAM_FREE) {
			handle = cgs.media.neutralFlagModel;
		} else {
			return;
		}
		CG_Draw3DModel(x, y, w, h, handle, 0, origin, angles);
	} else if (cg_drawIcons.integer) {
		gitem_t *item;

		if (team == TEAM_RED) {
			item = BG_FindItemForPowerup(PW_REDFLAG);
		} else if (team == TEAM_BLUE) {
			item = BG_FindItemForPowerup(PW_BLUEFLAG);
		} else if (team == TEAM_FREE) {
			item = BG_FindItemForPowerup(PW_NEUTRALFLAG);
		} else {
			return;
		}
		if (item) {
			CG_DrawPic(x, y, w, h, cg_items[ITEM_INDEX(item)].icon);
		}
	}
}

/*
================
CG_DrawTeamBackground

================
*/
void CG_DrawTeamBackground(int x, int y, int w, int h, float alpha, int team)
{
	vec4_t hcolor;

	hcolor[3] = alpha;
	if (team == TEAM_RED) {
		hcolor[0] = 1;
		hcolor[1] = 0;
		hcolor[2] = 0;
	} else if (team == TEAM_BLUE) {
		hcolor[0] = 0;
		hcolor[1] = 0;
		hcolor[2] = 1;
	} else {
		return;
	}
	trap_R_SetColor(hcolor);
	CG_DrawPic(x, y, w, h, cgs.media.teamStatusBar);
	trap_R_SetColor(NULL);
}

/*
================
CG_DrawStatusBar

================
*/
static void CG_DrawStatusBar(void)
{
	int style;
	centity_t *cent;
	playerState_t *ps;
	int value, max;
	vec4_t hcolor;
	qhandle_t hicon;
	qhandle_t icon;
	//Makro - added x and y for weapon drawing
	int i, x = cgs.screenXMax - SMICON_SIZE - 8, y = 400;

	//Makro - now using the same colors for both health and ammo
	static float colors[5][4] = {
		{1.0f, 1.0f, 1.0f, 1.0f},	// full green
		{1.0f, 1.0f, 0.0f, 1.0f},	// firing
		{0.7f, 0.7f, 0.7f, 1.0f},	// not maximum
		{0.8f, 0.0f, 0.0f, 1.0f},	// out of ammo
		{0.0f, 1.0f, 0.0f, 1.0f}	//Makro - reloading
	};
	
	//Makro - health colors
	static float hcolors[3][4] = {
		{1.0f, 1.0f, 1.0f, 1.0f},
		{1.0f, 1.0f, 0.0f, 1.0f},
		{0.8f, 0.0f, 0.0f, 1.0f}
	};

	cent = &cg_entities[cg.snap->ps.clientNum];
	ps = &cg.snap->ps;

	//Draw health
	value = ps->stats[STAT_HEALTH];
	style = UI_LEFT | UI_DROPSHADOW;

	if (value <= 25)
		style |= UI_PULSE;

	//Elder: Need bandaging?
	if ((ps->stats[STAT_RQ3] & RQ3_BANDAGE_NEED) == RQ3_BANDAGE_NEED)
		hicon = cgs.media.rq3_healthicon2;
	else
		hicon = cgs.media.rq3_healthicon;

	//Makro - old code
	//Elder: dynamic health color ramps
	//Blends from green to yellow to red algebraically
	//100 - Green, 50 - Yellow, 25 - Red, 0 - Faded Red
	//Note: These formulas are clamped from 0.0 to 1.0 algebraically
	if (value > 50) {
		float frac = (value - 50) / 50.0f, ifrac = (1.0f - frac);
		hcolor[0] = frac * hcolors[0][0] + ifrac * hcolors[1][0];
		hcolor[1] = frac * hcolors[0][1] + ifrac * hcolors[1][1];
		hcolor[2] = frac * hcolors[0][2] + ifrac * hcolors[1][2];
		hcolor[3] = frac * hcolors[0][3] + ifrac * hcolors[1][3];
	} else {
		float frac = value / 50.0f, ifrac = (1.0f - frac);
		hcolor[0] = frac * hcolors[1][0] + ifrac * hcolors[2][0];
		hcolor[1] = frac * hcolors[1][1] + ifrac * hcolors[2][1];
		hcolor[2] = frac * hcolors[1][2] + ifrac * hcolors[2][2];
		hcolor[3] = frac * hcolors[1][3] + ifrac * hcolors[2][3];
	}

	CG_DrawPic(cgs.screenXMin + 8, 440, SMICON_SIZE, SMICON_SIZE, hicon);
	//CG_DrawStringExt(44, 444, va("%d", value), hcolor, qtrue, qtrue, 24, 24, 3);
	//UI_DrawProportionalString(44, 444, va("%d", value), style, hcolor);
	UI_DrawProportionalString(cgs.screenXMin + 40, 444, va("%d", value), style, hcolor);

	//Elder: Draw weapon ammo and clips
	style = UI_LEFT | UI_DROPSHADOW;
/*
	icon = cg_weapons[ cg.predictedPlayerState.weapon ].weaponIcon;

	origin[0] = 40;
	origin[1] = 0;
	origin[2] = -30;

	if (icon)
		CG_DrawPic(152, 440, SMICON_SIZE, SMICON_SIZE, icon);
*/

	icon = cg_weapons[cg.predictedPlayerState.weapon].ammoIcon;
	//Don't draw ammo icon if holding grenade or knife
	//if (icon && cg.predictedPlayerState.weapon != WP_KNIFE && cg.predictedPlayerState.weapon != WP_GRENADE)
	if (icon)
		//CG_DrawPic(252, 440, SMICON_SIZE, SMICON_SIZE, icon);
		CG_DrawPic(288, 440, SMICON_SIZE, SMICON_SIZE, icon);

	if (cent->currentState.weapon) {
		value = ps->ammo[cent->currentState.weapon];

		// Select colour
		//Makro - change from white to yellow to red now, just like health display
#if 0
		if (cg.predictedPlayerState.weaponstate == WEAPON_FIRING && cg.predictedPlayerState.weaponTime > 100)
			color = 1;
		//Makro - added reloading check
		else if (cg.predictedPlayerState.weaponstate == WEAPON_RELOADING)
			color = 4;
		else if (ps->ammo[cent->currentState.weapon] == 0)
			color = 3;
		else if (ps->ammo[cent->currentState.weapon] < ClipAmountForAmmo(cent->currentState.weapon))
			color = 2;
		else
			color = 0;

		if (value >= 0)
			UI_DrawProportionalString(200, 444, va("%d", value), style, colors[color]);
#else
		//Makro - new code
		max = ClipAmountForAmmo(cent->currentState.weapon);
		if (value > (max / 2))
		{
			float frac = (value - (max / 2.0f)) / (max / 2.0f), ifrac = (1.0f - frac);
			hcolor[0] = frac * hcolors[0][0] + ifrac * hcolors[1][0];
			hcolor[1] = frac * hcolors[0][1] + ifrac * hcolors[1][1];
			hcolor[2] = frac * hcolors[0][2] + ifrac * hcolors[1][2];
			hcolor[3] = frac * hcolors[0][3] + ifrac * hcolors[1][3];
		} else {
			float frac = value / (max/2.0f), ifrac = (1.0f - frac);
			hcolor[0] = frac * hcolors[1][0] + ifrac * hcolors[2][0];
			hcolor[1] = frac * hcolors[1][1] + ifrac * hcolors[2][1];
			hcolor[2] = frac * hcolors[1][2] + ifrac * hcolors[2][2];
			hcolor[3] = frac * hcolors[1][3] + ifrac * hcolors[2][3];
		}

		if (value >= 0)
		{
			const char* str = va("%d", value);
			int width = UI_ProportionalStringWidth(str) * UI_ProportionalSizeScale(style);
			UI_DrawProportionalString(288 - width, 444, str, style, hcolor);
		}
#endif

		//UI_DrawProportionalString(188, 444, "/"), style, colors[0]);

		value = ps->stats[STAT_CLIPS];
		if (value > -1 &&
		    cg.predictedPlayerState.weapon != WP_KNIFE && cg.predictedPlayerState.weapon != WP_GRENADE)
				//Makro - pretty colours !
				UI_DrawProportionalString(320, 444, va("%d", value), style, (value != 0) ? colors[0] : colors[3]);
	}
	// Elder: temporary
	//if (cg.snap->ps.stats[STAT_RELOADTIME] > 0)
	//UI_DrawProportionalString( 10, 400, va("%i", cg.snap->ps.stats[STAT_RELOADTIME]), style, colors[2]);

	//Elder: draw a special weapon, if any, on the side
	for (i = 1; i < MAX_WEAPONS; i++) {
		int flag = (1 << i);
		if (i == WP_KNIFE || i == WP_PISTOL || i == WP_GRENADE || i == WP_AKIMBO)
			continue;

		if (flag == (flag & cg.snap->ps.stats[STAT_WEAPONS]))
		{
			//Makro - old code
			//break;
			icon = cg_weapons[i].weaponIcon;
			if (icon)
			{
				CG_DrawPic(x, y, SMICON_SIZE, SMICON_SIZE, icon);
				y -= SMICON_SIZE;
			}
		}
	}

	//Elder: draw grenades, if any, on the side
	//Makro - changed the code a bit; now it's drawn above the special weapons
	if (cg.snap->ps.ammo[WP_GRENADE] > 0) {
		icon = cg_weapons[WP_GRENADE].weaponIcon;
		if (icon)
			CG_DrawPic(x, y, SMICON_SIZE, SMICON_SIZE, icon);
		UI_DrawProportionalString(x + 2, y + 3, va("%d", cg.snap->ps.ammo[WP_GRENADE]), UI_RIGHT | UI_DROPSHADOW, colors[0]);
		y -= SMICON_SIZE;
	}

	//Makro - old code
	/*
	if (i < MAX_WEAPONS) {
		icon = cg_weapons[i].weaponIcon;
		if (icon)
			CG_DrawPic(640 - SMICON_SIZE, 400, SMICON_SIZE, SMICON_SIZE, icon);
	}
	*/
}

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

  UPPER RIGHT CORNER

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

static float CG_DrawCTBCountDown(float y)
{
	char *s;
	int w;
	int x = 0;
	float Color[4];

	if ( cg.CTBcountdowntime - cg.time < 0 ) {
		cg.CTBcountdowntime = 0;
		return y;
	}

	y += 4;
	s = va("Respawn in %d", (int)ceil( (cg.CTBcountdowntime - cg.time) / 1000.0f) );
	w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH;
	x = w;

	MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 0.4f);
	CG_FillRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, Color);

	MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 1.0f);
	CG_DrawCleanRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, 1, Color);

	CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, s, 1.0F);

	return y + SMALLCHAR_HEIGHT + 4;
}


/*
================
CG_DrawAttacker

================
*/
static float CG_DrawAttacker(float y)
{
	int t;
	float size;
	vec3_t angles;
	const char *info;
	const char *name;
	int clientNum;

	if (cg.predictedPlayerState.stats[STAT_HEALTH] <= 0) {
		return y;
	}

	if (!cg.attackerTime) {
		return y;
	}

	clientNum = cg.predictedPlayerState.persistant[PERS_ATTACKER];
	if (clientNum < 0 || clientNum >= MAX_CLIENTS || clientNum == cg.snap->ps.clientNum) {
		return y;
	}

	t = cg.time - cg.attackerTime;
	if (t > ATTACKER_HEAD_TIME) {
		cg.attackerTime = 0;
		return y;
	}

	size = ICON_SIZE * 1.25;

	angles[PITCH] = 0;
	angles[YAW] = 180;
	angles[ROLL] = 0;
	CG_DrawHead(cgs.screenXMax - size - 9, y, size, size, clientNum, angles);

	info = CG_ConfigString(CS_PLAYERS + clientNum);
	name = Info_ValueForKey(info, "n");
	y += size;
	CG_DrawBigString(cgs.screenXMax - (Q_PrintStrlen(name) * BIGCHAR_WIDTH) * 9, y, name, 0.5);

	return y + BIGCHAR_HEIGHT + 2;
}

static float CG_DrawSpeed(float y)
{
	char *s;
	int w;
	int x = 0;
	float Color[4];
	vec3_t vel;

	VectorCopy( cg.snap->ps.velocity, vel );
	vel[2] = 0.0f;

	y += 4;
	s = va("%3.0f UPS", VectorLength(vel) );
	w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH;
	x = w;

	MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 0.4f);
	CG_FillRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, Color);

	MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 1.0f);
	CG_DrawCleanRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, 1, Color);

	CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, s, 1.0F);

	return y + SMALLCHAR_HEIGHT + 4;
}

/*
==================
CG_DrawSnapshot
==================
*/
static float CG_DrawSnapshot(float y)
{
	char *s;
	int w;

	s = va("time:%i snap:%i cmd:%i", cg.snap->serverTime, cg.latestSnapshotNum, cgs.serverCommandSequence);
	w = CG_DrawStrlen(s) * BIGCHAR_WIDTH;

	CG_DrawBigString(635 - w, y + 2, s, 1.0F);

	return y + BIGCHAR_HEIGHT + 4;
}

static float CG_DrawScore(float y)
{
	char *s;
	int w, x = 0;
	float BColor[4], FColor[4];
	int	team;

	y += 4;

	if (cgs.gametype >= GT_TEAM)
		team = cg.snap->ps.persistant[PERS_SAVEDTEAM];
	else
		team = cg.snap->ps.persistant[PERS_TEAM];

	MAKERGBA(FColor, 0.0f, 0.0f, 0.0f, 1.0f);

	if (cgs.gametype >= GT_TEAM) {
		// The other team:
		if (team == TEAM_RED) {
			s = va("%i", cgs.scores2);							// Blue
			CG_TeamColor(TEAM_BLUE, BColor);
		}
		else {
			s = va("%i", cgs.scores1);							// Red
			CG_TeamColor(TEAM_RED, BColor);
		}
		BColor[3] = 0.4f;

			//MAKERGBA(BColor, 0.0f, 0.0f, 1.0f, 0.4f);

		w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH;
		x = w;

		CG_FillRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, BColor);
		CG_DrawCleanRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, 1, FColor);
		CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, s, 1.0f);
	
		// Your team:
		if (team == TEAM_RED) {
			s = va("%i", cgs.scores1);							// Red
			CG_TeamColor(TEAM_RED, BColor);
		}
		else {
			s = va("%i", cgs.scores2);							// Blue
			CG_TeamColor(TEAM_BLUE, BColor);
		}
		BColor[3] = 0.4f;

		w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH;
		x += w + 9;

		CG_FillRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, BColor);
		CG_DrawCleanRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, 1, FColor);
		CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, s, 1.0f);
	}

	s = va("%i", cg.snap->ps.persistant[PERS_SCORE]);
	w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH;

	if (cgs.gametype >= GT_TEAM) {
		x += w + 5;
		if (team == TEAM_SPECTATOR)
			x += 4;
	}
	else
		x = w;

	//MAKERGBA(BColor, 1.0f, 1.0f, 1.0f, 1.0f);
	//CG_FillRect(631 - x - 3, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, BColor);

	CG_DrawCleanRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, 1, FColor);

	MAKERGBA(FColor, 0.75f, 0.75f, 0.75f, 1.0f);
	CG_DrawStringExt(cgs.screenXMax - x - 9, y + 2, s, FColor, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0);	
	//CG_DrawSmallString(631 - x, y + 2, s, 1.0F);


	return y + SMALLCHAR_HEIGHT + 4;
}

/*
==================
CG_DrawFPSandPing
==================
*/
#define	FPS_FRAMES	8			// NiceAss: Increased from 4 for a smoother average.
#define PING_SNAPS 16

static float CG_DrawFPSandPing(float y)
{
	char *sfps = NULL, *sping = NULL;
	int w;
	static int previousTimes[FPS_FRAMES];
	static int index;
	int i, total, l;
	int fps;
	static int previous;
	int t, frameTime, x = 0, num = 0;
	float Color[4];
	
	const int expand = 4;
	const int corner = 12;
	const float shadowAlpha = 0.75f;

	/*static int Pings[PING_SNAPS];
	static int currentSnapshotNum;
	static int index2;*/
	int avgping = 0;


	// don't use serverTime, because that will be drifting to
	// correct for internet lag changes, timescales, timedemos, etc
	t = trap_Milliseconds();
	frameTime = t - previous;
	previous = t;

	y += 4;

	previousTimes[index % FPS_FRAMES] = frameTime;

	/*if (cg.latestSnapshotNum != currentSnapshotNum && cg.snap) {
		Pings[index2 % PING_SNAPS] = cg.snap->ping;
		currentSnapshotNum = cg.latestSnapshotNum;
		index2++;
	}*/

	index++;

	if (index > FPS_FRAMES) {
		// average multiple frames together to smooth changes out a bit
		total = 0;
		for (i = 0; i < FPS_FRAMES; i++) {
			total += previousTimes[i];
		}
		if (!total) {
			total = 1;
		}
		fps = 1000 * FPS_FRAMES / total;

		if (cg_drawFPS.integer) {
			sfps = va("%ifps", fps);
			w = CG_DrawStrlen(sfps) * SMALLCHAR_WIDTH;
			x = w;

			CG_DrawFuzzyShadow(cgs.screenXMax - x - 12 - expand, y - 1 - expand, w + 6 + expand + expand, SMALLCHAR_HEIGHT + 6 + expand + expand, 12, shadowAlpha);
			CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, sfps, 1.0F);

			x += 9;
		}
	}

	// Draw ping here:
	//Makro - not during demo playback
	if (cg_drawPing.integer && !cg.demoPlayback) {	
		for (i = 0; i < (LAG_SAMPLES / 2); i++) {
			l = (lagometer.frameCount & (LAG_SAMPLES - 1)) - i;
			if (l < 0) l += LAG_SAMPLES;

			if (lagometer.snapshotSamples[l] >= 0)  {
				avgping += lagometer.snapshotSamples[l];
				num++;
			}
		}

		if (num)
			avgping /= num;
		else
			avgping = 0;

		sping = va("%ims", avgping);
		w = CG_DrawStrlen(sping) * SMALLCHAR_WIDTH;
		x += w;

		l = (lagometer.frameCount & (LAG_SAMPLES - 1)) - 1;
		if (l < 0) l += LAG_SAMPLES;


		CG_DrawFuzzyShadow(cgs.screenXMax - x - 12 - expand, y - 1 - expand, w + 6 + expand + expand, SMALLCHAR_HEIGHT + 6 + expand + expand, corner, shadowAlpha);

		MAKERGBA(Color, 0.0f, 1.0f, 0.0f, 1.0f);								// Green, All good

		if (lagometer.snapshotSamples[l] < 0)
			MAKERGBA(Color, 1.0f, 0.0f, 0.0f, 1.0f);							// Red. Missed packet

		if (lagometer.snapshotFlags[l] & SNAPFLAG_RATE_DELAYED)					// Yellow. Delayed packet
			MAKERGBA(Color, 1.0f, 1.0f, 0.0f, 1.0f);

		CG_DrawStringExt(cgs.screenXMax - x - 9, y + 2, sping, Color, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0);
	}
	
	if (!cg_drawFPS.integer && (!cg_drawPing.integer || cg.demoPlayback))
		return y;

	return y + SMALLCHAR_HEIGHT + 4;
}

/*
=================
CG_DrawTimer
=================
*/
static float CG_DrawTimer(float y)
{
	char *s = NULL;
	int w;
	int mins, seconds, tens;
	int msec;
	int x = 0;
//	float Color[4];

	const int expand = 4;
//	const int corner = 12;
	const float shadowAlpha = 0.75f;

	y += 4;
	msec = cg.time - cgs.levelStartTime;

	seconds = msec / 1000;
	mins = seconds / 60;
	seconds -= mins * 60;
	tens = seconds / 10;
	seconds -= tens * 10;

	s = va("%i:%i%i", mins, tens, seconds);
	w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH;
	x = w;

	CG_DrawFuzzyShadow(cgs.screenXMax - x - 12 - expand, 
		y - 1 - expand, 
		w + 6 + expand + expand, 
		SMALLCHAR_HEIGHT + 6 + expand + expand, 
		12, 
		shadowAlpha
	);

	CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, s, 1.0F);

	return y + BIGCHAR_HEIGHT + 4;
}

/*
=================
CG_DrawTeamOverlay
=================
*/

static float CG_DrawTeamOverlay(float y, qboolean right, qboolean upper)
{
	int x, w, h, xx;
	int i, j, len;
	const char *p;
	vec4_t hcolor;
	int pwidth, lwidth;
	int plyrs;
	char st[16];
	clientInfo_t *ci;
	gitem_t *item;
	int ret_y, count;

	if (!cg_drawTeamOverlay.integer) {
		return y;
	}

	if (cg.snap->ps.persistant[PERS_TEAM] != TEAM_RED && cg.snap->ps.persistant[PERS_TEAM] != TEAM_BLUE) {
		return y;	// Not on any team
	}

	plyrs = 0;

	// max player name width
	pwidth = 0;
	count = (numSortedTeamPlayers > 8) ? 8 : numSortedTeamPlayers;
	for (i = 0; i < count; i++) {
		ci = cgs.clientinfo + sortedTeamPlayers[i];
		if (ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) {
			plyrs++;
			len = CG_DrawStrlen(ci->name);
			if (len > pwidth)
				pwidth = len;
		}
	}

	if (!plyrs)
		return y;

	if (pwidth > TEAM_OVERLAY_MAXNAME_WIDTH)
		pwidth = TEAM_OVERLAY_MAXNAME_WIDTH;

	// max location name width
	lwidth = 0;
	for (i = 1; i < MAX_LOCATIONS; i++) {
		p = CG_ConfigString(CS_LOCATIONS + i);
		if (p && *p) {
			len = CG_DrawStrlen(p);
			if (len > lwidth)
				lwidth = len;
		}
	}

	if (lwidth > TEAM_OVERLAY_MAXLOCATION_WIDTH)
		lwidth = TEAM_OVERLAY_MAXLOCATION_WIDTH;

	w = (pwidth + lwidth + 4 + 7) * TINYCHAR_WIDTH;

	if (right)
		x = cgs.screenXMax - w;
	else
		x = cgs.screenXMin;

	h = plyrs * TINYCHAR_HEIGHT;

	if (upper) {
		ret_y = y + h;
	} else {
		y -= h;
		ret_y = y;
	}

	CG_TeamColor(cg.snap->ps.persistant[PERS_TEAM], hcolor);
	hcolor[3] = 0.33f;

	trap_R_SetColor(hcolor);
	CG_DrawPic(x, y, w, h, cgs.media.teamStatusBar);
	trap_R_SetColor(NULL);

	for (i = 0; i < count; i++) {
		ci = cgs.clientinfo + sortedTeamPlayers[i];
		if (ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) {

			hcolor[0] = hcolor[1] = hcolor[2] = hcolor[3] = 1.0;

			xx = x + TINYCHAR_WIDTH;

			CG_DrawStringExt(xx, y,
					 ci->name, hcolor, qfalse, qfalse,
					 TINYCHAR_WIDTH, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXNAME_WIDTH);

			if (lwidth) {
				p = CG_ConfigString(CS_LOCATIONS + ci->location);
				if (!p || !*p)
					p = "unknown";
				//len = CG_DrawStrlen(p);
				//if (len > lwidth)
			//		len = lwidth;

//                              xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth + 
//                                      ((lwidth/2 - len/2) * TINYCHAR_WIDTH);
				xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth;
				CG_DrawStringExt(xx, y,
						 p, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT,
						 TEAM_OVERLAY_MAXLOCATION_WIDTH);
			}

			CG_GetColorForHealth(ci->health, ci->armor, hcolor);

			Com_sprintf(st, sizeof(st), "%3i %3i", ci->health, ci->armor);

			xx = x + TINYCHAR_WIDTH * 3 + TINYCHAR_WIDTH * pwidth + TINYCHAR_WIDTH * lwidth;

			CG_DrawStringExt(xx, y, st, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0);

			// draw weapon icon
			xx += TINYCHAR_WIDTH * 3;

			if (cg_weapons[ci->curWeapon].weaponIcon) {
				CG_DrawPic(xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT,
					   cg_weapons[ci->curWeapon].weaponIcon);
			} else {
				CG_DrawPic(xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, cgs.media.deferShader);
			}

			// Draw powerup icons
			if (right) {
				xx = x;
			} else {
				xx = x + w - TINYCHAR_WIDTH;
			}
			for (j = 0; j <= PW_NUM_POWERUPS; j++) {
				if (ci->powerups & (1 << j)) {

					item = BG_FindItemForPowerup(j);

					if (item) {
						CG_DrawPic(xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT,
							   trap_R_RegisterShader(item->icon));
						if (right) {
							xx -= TINYCHAR_WIDTH;
						} else {
							xx += TINYCHAR_WIDTH;
						}
					}
				}
			}

			y += TINYCHAR_HEIGHT;
		}
	}

	return ret_y;
//#endif
}

/*
=====================
CG_DrawUpperRight

=====================
*/
static void CG_DrawUpperRight(void)
{
	float y;

	y = 0;

	if (cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 1)
		y = CG_DrawTeamOverlay(y, qtrue, qtrue);

	if (cg_drawSnapshot.integer)
		y = CG_DrawSnapshot(y);

	y = CG_DrawScore(y);
	y = CG_DrawFPSandPing(y);

	if (cg_drawTimer.integer)
		y = CG_DrawTimer(y);

	if (cg.CTBcountdowntime)
		y = CG_DrawCTBCountDown(y);

	if (cg_drawAttacker.integer)
		y = CG_DrawAttacker(y);

	if (cg_RQ3_drawSpeed.integer)
		y = CG_DrawSpeed(y);
}

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

  LOWER RIGHT CORNER

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

/*
================
CG_DrawPowerups
================
*/
static float CG_DrawPowerups(float y)
{
	int sorted[MAX_POWERUPS];
	int sortedTime[MAX_POWERUPS];
	int i, j, k;
	int active;
	playerState_t *ps;
	int t;
	gitem_t *item;
	int x;
	int color;
	float size;
	float f;
	static float colors[2][4] = {
		{0.2f, 1.0f, 0.2f, 1.0f},
		{1.0f, 0.2f, 0.2f, 1.0f}
	};

	ps = &cg.snap->ps;

	if (ps->stats[STAT_HEALTH] <= 0) {
		return y;
	}
	// sort the list by time remaining
	active = 0;
	for (i = 0; i < MAX_POWERUPS; i++) {
		if (!ps->powerups[i]) {
			continue;
		}
		t = ps->powerups[i] - cg.time;
		// ZOID--don't draw if the power up has unlimited time (999 seconds)
		// This is true of the CTF flags
		if (t < 0 || t > 999000) {
			continue;
		}
		// insert into the list
		for (j = 0; j < active; j++) {
			if (sortedTime[j] >= t) {
				for (k = active - 1; k >= j; k--) {
					sorted[k + 1] = sorted[k];
					sortedTime[k + 1] = sortedTime[k];
				}
				break;
			}
		}
		sorted[j] = i;
		sortedTime[j] = t;
		active++;
	}

	// draw the icons and timers
	x = 640 - ICON_SIZE - CHAR_WIDTH * 2;
	for (i = 0; i < active; i++) {
		item = BG_FindItemForPowerup(sorted[i]);

		if (item) {

			color = 1;

			y -= ICON_SIZE;

			trap_R_SetColor(colors[color]);
			CG_DrawField(x, y, 2, sortedTime[i] / 1000);

			t = ps->powerups[sorted[i]];
			if (t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME) {
				trap_R_SetColor(NULL);
			} else {
				vec4_t modulate;

				f = (float) (t - cg.time) / POWERUP_BLINK_TIME;
				f -= (int) f;
				modulate[0] = modulate[1] = modulate[2] = modulate[3] = f;
				trap_R_SetColor(modulate);
			}

			if (cg.powerupActive == sorted[i] && cg.time - cg.powerupTime < PULSE_TIME) {
				f = 1.0 - (((float) cg.time - cg.powerupTime) / PULSE_TIME);
				size = ICON_SIZE * (1.0 + (PULSE_SCALE - 1.0) * f);
			} else {
				size = ICON_SIZE;
			}

			CG_DrawPic(640 - size, y + ICON_SIZE / 2 - size / 2,
				   size, size, trap_R_RegisterShader(item->icon));
		}
	}
	trap_R_SetColor(NULL);

	return y;
}

/*
=====================
CG_DrawLowerRight

=====================
*/
static void CG_DrawLowerRight(void)
{
	float y;

	//y = 480 - ICON_SIZE;
	y = 320 + SMICON_SIZE;

	if (cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2) {
		y = CG_DrawTeamOverlay(y, qtrue, qfalse);
	}
	// NiceAss: Taken out. Kinda ugly. I'll probably recode this later....
//      y = CG_DrawScores( y );
	y = CG_DrawPowerups(y);
}

/*
===================
CG_DrawPickupItem
===================
*/
static int CG_DrawPickupItem(int y)
{
	int value;
	float *fadeColor;

	if (cg.snap->ps.stats[STAT_HEALTH] <= 0) {
		return y;
	}

	value = cg.itemPickup;
	if (value) {
		fadeColor = CG_FadeColor(cg.itemPickupTime, 3000);
		if (fadeColor) {
			CG_RegisterItemVisuals(value);
			trap_R_SetColor(fadeColor);
			y -= ICON_SIZE;
			CG_DrawPic(cgs.screenXMin + 8, y, ICON_SIZE, ICON_SIZE, cg_items[value].icon);
			CG_DrawBigString(cgs.screenXMin + ICON_SIZE + 16, y + (ICON_SIZE / 2 - BIGCHAR_HEIGHT / 2),
					 bg_itemlist[value].pickup_name, fadeColor[0]);
			trap_R_SetColor(NULL);
		}
	}

	return y;
}

/*
=================
CG_DrawMessageQueue
=================
*/
static float CG_DrawMessageQueue(float y)
{
	int w, h;
	int i, len;
	vec4_t hcolor;
	int chatHeight;
	int maxtime = 0;
	float div;

#define CHATLOC_Y y
#define CHATLOC_X cgs.screenXMin

	if (!cg_messageQueue.integer || cg_messageQueueTime.integer <= 0)
		return y;
	
	chatHeight = MSGQUEUE_HEIGHT;

	while (cgs.teamLastChatPos < cgs.teamChatPos && cg.time - cgs.teamChatMsgTimes[cgs.teamLastChatPos % chatHeight] > cg_messageQueueTime.integer)
		cgs.teamLastChatPos++;
	
	if (cgs.teamLastChatPos == cgs.teamChatPos)
		return y;

	y -= 32;

	h = (cgs.teamChatPos - cgs.teamLastChatPos) * TINYCHAR_HEIGHT;

	w = 0;

	div = 1.f / cg_messageQueueTime.integer;

	for (i = cgs.teamLastChatPos; i < cgs.teamChatPos; i++) {
		int index = i % chatHeight;
		len = CG_DrawStrlen(cgs.teamChatMsgs[index]);
		if (len > w)
			w = len;
		if (cgs.teamChatMsgTimes[index] > maxtime)
			maxtime = cgs.teamChatMsgTimes[index];
	}
	w *= TINYCHAR_WIDTH;
	w += TINYCHAR_WIDTH * 2;
	
	{
		float frac = SmoothLerp(Com_Clamp(0.f, 1.f, 8.f * (1.f - (cg.time - maxtime) * div)));
		const int expand = 8;
		CG_DrawFuzzyShadow(CHATLOC_X - expand, CHATLOC_Y - h + TINYCHAR_HEIGHT - expand, w + expand + expand, h + expand + expand, 12.f, 0.5f * frac);
	}

	hcolor[0] = hcolor[1] = hcolor[2] = 1.0f;
	hcolor[3] = 1.0f;

	for (i = cgs.teamChatPos - 1; i >= cgs.teamLastChatPos; i--) {
		int index = i % chatHeight;
		float frac = SmoothLerp(Com_Clamp(0.f, 1.f, 8.f * (1.f - (cg.time - cgs.teamChatMsgTimes[index]) * div)));
		hcolor[3] = frac;
		CG_DrawStringExt(CHATLOC_X + TINYCHAR_WIDTH, CHATLOC_Y,
			cgs.teamChatMsgs[index], hcolor, qfalse, qtrue,
			TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0);
		y -= TINYCHAR_HEIGHT;
	}

	return y;
}

/*
=====================
CG_DrawLowerLeft

=====================
*/
static void CG_DrawLowerLeft(void)
{
	float y;

	y = 480 - ICON_SIZE;

	if (cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 3) {
		y = CG_DrawTeamOverlay(y, qfalse, qfalse);
	}

	y = CG_DrawMessageQueue(y);
	y = CG_DrawPickupItem(y);
}

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

/*
===================
CG_DrawHoldableItem
===================
*/
static void CG_DrawHoldableItem(void)
{
	int value, item;

	item = 0;
// JBravo: new items code.  The order here is the same as in g_active so you
// drop the same item that you see on your HUD.
	if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM]  & (1 << HI_BANDOLIER))
		item = HI_BANDOLIER;
	else if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM]  & (1 << HI_SLIPPERS))
		item = HI_SLIPPERS;
	else if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM]  & (1 << HI_SILENCER))
		item = HI_SILENCER;
	else if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM]  & (1 << HI_HELMET))
		item = HI_HELMET;
	else if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM]  & (1 << HI_LASER))
		item = HI_LASER;
	else if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM]  & (1 << HI_KEVLAR))
		item = HI_KEVLAR;

	if (item) {
		value = BG_FindItemForHoldable(item) - bg_itemlist;
		CG_RegisterItemVisuals(value);
		CG_DrawPic(cgs.screenXMax - SMICON_SIZE - 8, 440, SMICON_SIZE, SMICON_SIZE, cg_items[value].icon);
	}
}

/*
===================
CG_DrawReward
===================
*/
static void CG_DrawReward(void)
{
	float *color;
	int i, count;
	float x, y;
	char buf[32];

	if (!cg_drawRewards.integer) {
		return;
	}

	color = CG_FadeColor(cg.rewardTime, REWARD_TIME);
	if (!color) {
		if (cg.rewardStack > 0) {
			for (i = 0; i < cg.rewardStack; i++) {
				cg.rewardSound[i] = cg.rewardSound[i + 1];
				cg.rewardShader[i] = cg.rewardShader[i + 1];
				cg.rewardCount[i] = cg.rewardCount[i + 1];
			}
			cg.rewardTime = cg.time;
			cg.rewardStack--;
			color = CG_FadeColor(cg.rewardTime, REWARD_TIME);
			trap_S_StartLocalSound(cg.rewardSound[0], CHAN_ANNOUNCER);
		} else {
			return;
		}
	}

	trap_R_SetColor(color);

	/*
	   count = cg.rewardCount[0]/10;                                // number of big rewards to draw

	   if (count) {
	   y = 4;
	   x = 320 - count * ICON_SIZE;
	   for ( i = 0 ; i < count ; i++ ) {
	   CG_DrawPic( x, y, (ICON_SIZE*2)-4, (ICON_SIZE*2)-4, cg.rewardShader[0] );
	   x += (ICON_SIZE*2);
	   }
	   }

	   count = cg.rewardCount[0] - count*10;                // number of small rewards to draw
	 */

	if (cg.rewardCount[0] >= 10) {
		y = 56;
		x = 320 - ICON_SIZE / 2;
		CG_DrawPic(x, y, ICON_SIZE - 4, ICON_SIZE - 4, cg.rewardShader[0]);
		Com_sprintf(buf, sizeof(buf), "%d", cg.rewardCount[0]);
		x = (SCREEN_WIDTH - SMALLCHAR_WIDTH * CG_DrawStrlen(buf)) / 2;
		CG_DrawStringExt(x, y + ICON_SIZE, buf, color, qfalse, qtrue, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0);
	} else {

		count = cg.rewardCount[0];

		y = 56;
		x = 320 - count * ICON_SIZE / 2;
		for (i = 0; i < count; i++) {
			CG_DrawPic(x, y, ICON_SIZE - 4, ICON_SIZE - 4, cg.rewardShader[0]);
			x += ICON_SIZE;
		}
	}
	trap_R_SetColor(NULL);
}

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

LAGOMETER

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


/*
==============
CG_AddLagometerFrameInfo

Adds the current interpolate / extrapolate bar for this frame
==============
*/
void CG_AddLagometerFrameInfo(void)
{
	int offset;

	offset = cg.time - cg.latestSnapshotTime;
	lagometer.frameSamples[lagometer.frameCount & (LAG_SAMPLES - 1)] = offset;
	lagometer.frameCount++;
}

/*
==============
CG_AddLagometerSnapshotInfo

Each time a snapshot is received, log its ping time and
the number of snapshots that were dropped before it.

Pass NULL for a dropped packet.
==============
*/
void CG_AddLagometerSnapshotInfo(snapshot_t * snap)
{
	// dropped packet
	if (!snap) {
		lagometer.snapshotSamples[lagometer.snapshotCount & (LAG_SAMPLES - 1)] = -1;
		lagometer.snapshotCount++;
		return;
	}
	// add this snapshot's info
	lagometer.snapshotSamples[lagometer.snapshotCount & (LAG_SAMPLES - 1)] = snap->ping;
	lagometer.snapshotFlags[lagometer.snapshotCount & (LAG_SAMPLES - 1)] = snap->snapFlags;
	lagometer.snapshotCount++;
}

/*
==============
CG_DrawDisconnect

Should we draw something differnet for long lag vs no packets?
==============
*/
static void CG_DrawDisconnect(void)
{
	float x, y;
	int cmdNum;
	usercmd_t cmd;
	const char *s;
	int w;			// bk010215 - FIXME char message[1024];

	// draw the phone jack if we are completely past our buffers
	cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1;
	trap_GetUserCmd(cmdNum, &cmd);
	if (cmd.serverTime <= cg.snap->ps.commandTime || cmd.serverTime > cg.time) {	// special check for map_restart // bk 0102165 - FIXME
		return;
	}
	// also add text in center of screen
	s = "Connection Interrupted";	// bk 010215 - FIXME
	w = CG_DrawStrlen(s) * BIGCHAR_WIDTH;
	CG_DrawBigString(320 - w / 2, 100, s, 1.0F);

	// blink the icon
	if ((cg.time >> 9) & 1) {
		return;
	}
	//Elder: changed position
	x = 0;
	y = 0;
	//x = 640 - 48;
	//y = 480 - 48;

	CG_DrawPic(cgs.screenXMin + x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga"));
}

#define	MAX_LAGOMETER_PING	900
#define	MAX_LAGOMETER_RANGE	300

/*
==============
CG_DrawLagometer
==============
*/
static void CG_DrawLagometer(void)
{
	int a, x, y, i;
	float v;
	float ax, ay, aw, ah, mid, range;
	int color;
	float vscale;

// JBravo: unlagged
	if (!cg_lagometer.integer /*|| cgs.localServer*/) {
		CG_DrawDisconnect();
		return;
	}
	//
	// draw the graph
	//
	//Elder: changed position
	x = 0;
	y = 0;

	trap_R_SetColor(NULL);
	CG_DrawPic(cgs.screenXMin + x, y, 48, 48, cgs.media.lagometerShader);

	ax = cgs.screenXMin + x;
	ay = y;
	aw = 48;
	ah = 48;
	CG_AdjustFrom640(&ax, &ay, &aw, &ah);

	color = -1;
	range = ah / 3;
	mid = ay + range;

	vscale = range / MAX_LAGOMETER_RANGE;

	// draw the frame interpoalte / extrapolate graph
	for (a = 0; a < aw; a++) {
		i = (lagometer.frameCount - 1 - a) & (LAG_SAMPLES - 1);
		v = lagometer.frameSamples[i];
		v *= vscale;
		if (v > 0) {
			if (color != 1) {
				color = 1;
				trap_R_SetColor(g_color_table[ColorIndex(COLOR_YELLOW)]);
			}
			if (v > range) {
				v = range;
			}
			trap_R_DrawStretchPic(ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader);
		} else if (v < 0) {
			if (color != 2) {
				color = 2;
				trap_R_SetColor(g_color_table[ColorIndex(COLOR_BLUE)]);
			}
			v = -v;
			if (v > range) {
				v = range;
			}
			trap_R_DrawStretchPic(ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader);
		}
	}

	// draw the snapshot latency / drop graph
	range = ah / 2;
	vscale = range / MAX_LAGOMETER_PING;

	for (a = 0; a < aw; a++) {
		i = (lagometer.snapshotCount - 1 - a) & (LAG_SAMPLES - 1);
		v = lagometer.snapshotSamples[i];
		if (v > 0) {
			if (lagometer.snapshotFlags[i] & SNAPFLAG_RATE_DELAYED) {
				if (color != 5) {
					color = 5;	// YELLOW for rate delay
					trap_R_SetColor(g_color_table[ColorIndex(COLOR_YELLOW)]);
				}
			} else {
				if (color != 3) {
					color = 3;
					trap_R_SetColor(g_color_table[ColorIndex(COLOR_GREEN)]);
				}
			}
			v = v * vscale;
			if (v > range) {
				v = range;
			}
			trap_R_DrawStretchPic(ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader);
		} else if (v < 0) {
			if (color != 4) {
				color = 4;	// RED for dropped snapshots
				trap_R_SetColor(g_color_table[ColorIndex(COLOR_RED)]);
			}
			trap_R_DrawStretchPic(ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0,
					      cgs.media.whiteShader);
		}
	}

	trap_R_SetColor(NULL);

	if (cg_nopredict.integer || cg_synchronousClients.integer) {
		CG_DrawBigString(x, y, "snc", 1.0);
	}

	CG_DrawDisconnect();
}

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

CENTER PRINTING

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

/*
==============
CG_CenterPrint

Called for important messages that should stay in the center of the screen
for a few moments
==============
*/
void CG_CenterPrint(const char *str, int y, int charWidth)
{
	char *s;
	int length = 0;

	Q_strncpyz(cg.centerPrint, str, sizeof(cg.centerPrint));

	cg.centerPrintTime = cg.time;
	cg.centerPrintY = y;
	cg.centerPrintCharWidth = charWidth;
	cg.centerPrintMaxLen = 0;

	// count the number of lines for centering
	cg.centerPrintLines = 1;
	s = cg.centerPrint;
	while (*s) {
		if (*s == '\n') {
			cg.centerPrintLines++;
			if (length > cg.centerPrintMaxLen)
				cg.centerPrintMaxLen = length;
			length = 0;
		} else {
			length++;
		}
		s++;
	}
	if (cg.centerPrintMaxLen == 0)
		cg.centerPrintMaxLen = CG_DrawStrlen(str);

	// Last character a linefeed
	if (*(s - 1) == '\n')
		cg.centerPrintLines--;
}

/*
===================
CG_DrawCenterString
===================
*/
static void CG_DrawCenterString(void)
{
	char *start;
	int l;
	int x, y, w;
	float *color, color2[4];
	int windowHeight;

	if (!cg.centerPrintTime) {
		return;
	}

	color = CG_FadeColor(cg.centerPrintTime, 1000 * cg_centertime.value);
	if (!color) {
		return;
	}

	trap_R_SetColor(color);

	start = cg.centerPrint;

	windowHeight = cg.centerPrintLines * (int) (cg.centerPrintCharWidth * 1.5);

	y = cg.centerPrintY - windowHeight / 2;

	if (cg.centerPrint[0] != '\n') {
		MAKERGBA(color2, 0.0f, 0.0f, 0.0f, 0.4f * color[3]);
		CG_FillRect(320 - (cg.centerPrintMaxLen * cg.centerPrintCharWidth) * 0.5f - 3, y - 3,
			    cg.centerPrintCharWidth * cg.centerPrintMaxLen + 6, windowHeight + 6, color2);
		MAKERGBA(color2, 0.0f, 0.0f, 0.0f, 1.0f * color[3]);
		CG_DrawCleanRect(320 - (cg.centerPrintMaxLen * cg.centerPrintCharWidth) * 0.5f - 3, y - 3,
				 cg.centerPrintCharWidth * cg.centerPrintMaxLen + 6, windowHeight + 6, 1, color2);
	}

	while (1) {
		char linebuffer[1024];

		for (l = 0; l < 50; l++) {
			if (!start[l] || start[l] == '\n') {
				break;
			}
			linebuffer[l] = start[l];
		}
		linebuffer[l] = 0;

		w = cg.centerPrintCharWidth * CG_DrawStrlen(linebuffer);

		x = (SCREEN_WIDTH - w) / 2;

		CG_DrawStringExt(x, y, linebuffer, color, qfalse, qtrue,
				 cg.centerPrintCharWidth, (int) (cg.centerPrintCharWidth * 1.5), 0);

		y += cg.centerPrintCharWidth * 1.5;
		while (*start && (*start != '\n')) {
			start++;
		}
		if (!*start) {
			break;
		}
		start++;
	}

	trap_R_SetColor(NULL);
}

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

CROSSHAIR

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

static void CG_DrawVignetting(float scale)
{
	if (cgs.media.zoomMask)
	{
		float sx = cgs.glconfig.vidWidth / (float) SCREEN_WIDTH;
		float sy = cgs.glconfig.vidHeight / (float) SCREEN_HEIGHT;

		float tex[4];

		if (sx >= sy)
		{
			tex[0] = 0.f;
			tex[1] = 0.5f * (1.f - sy / sx);
		}
		else
		{
			tex[0] = 0.5f * (1.f - sx / sy);
			tex[1] = 0.f;
		}

		tex[2] = 1.f - tex[0];
		tex[3] = 1.f - tex[1];
		
		tex[0] = (tex[0] - 0.5f) * scale + 0.5f;
		tex[1] = (tex[1] - 0.5f) * scale + 0.5f;
		tex[2] = (tex[2] - 0.5f) * scale + 0.5f;
		tex[3] = (tex[3] - 0.5f) * scale + 0.5f;

		trap_R_DrawStretchPic(0, 0, cg.refdef.width, cg.refdef.height, tex[0], tex[1], tex[2], tex[3], cgs.media.zoomMask);
	}
}

/*
=================
CG_DrawCrosshair
=================
*/
static void CG_DrawCrosshair(void)
{
	float w, h;
	qhandle_t hShader;
	float f;
	float x, y;
	int ca, i, drawSSG;
	vec4_t crosshairColor;

	//Slicer: HOW ABOUT SSG SCOPE !! GAHHH !!!!! - moved below
	//if (!cg_drawCrosshair.integer) {
	//	return;
	//}
	//Slicer: Adding Crosshair to FOLLOW SPECS
	drawSSG = 0;
	if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR && !(cg.snap->ps.pm_flags & PMF_FOLLOW)) {
		return;
	}

	if (cg.renderingThirdPerson) {
		return;
	}

	//Makro - moved some code below to prevent some variables from getting overwritten by the ssg code
	
	//Elder: Sniper crosshairs - lots of hardcoded values :/
	//if ( cg.snap->ps.weapon==WP_SSG3000 && cg.zoomLevel > 0 && cg.zoomLevel < 4) {
	// some pile of crap
	// using SSG and zoomed in
	if (cg.snap->ps.weapon == WP_SSG3000 && ((cg.zoomLevel & RQ3_ZOOM_LOW) || (cg.zoomLevel & RQ3_ZOOM_MED))) {
		if ((cg.zoomFirstReturn == -1 || cg.snap->ps.weaponTime < ZOOM_TIME) &&
		    cg.snap->ps.stats[STAT_RELOADTIME] <= 0) {
			//Makro - wasn't initialized, caused a warning in MSVC
			int zoomMag = 0;

			x = SCREEN_WIDTH / 2;
			y = SCREEN_HEIGHT / 2;

			//derive zoom level
			if ((cg.zoomLevel & RQ3_ZOOM_LOW) == RQ3_ZOOM_LOW &&
			    (cg.zoomLevel & RQ3_ZOOM_MED) == RQ3_ZOOM_MED) {
				zoomMag = 2;
			} else if ((cg.zoomLevel & RQ3_ZOOM_LOW) == RQ3_ZOOM_LOW) {
				zoomMag = 0;
			} else if ((cg.zoomLevel & RQ3_ZOOM_MED) == RQ3_ZOOM_MED) {
				zoomMag = 1;
			} else {
				//Shouldn't need to be here
				CG_Error("CG_DrawCrosshair: received no zoom value\n");
			}

			//Elder: Setup crosshair colours
			crosshairColor[0] = cg_RQ3_ssgColorR.value;
			crosshairColor[1] = cg_RQ3_ssgColorG.value;
			crosshairColor[2] = cg_RQ3_ssgColorB.value;
			crosshairColor[3] = cg_RQ3_ssgColorA.value;

			//Clamp
			for (i = 0; i < 4; i++) {
				if (crosshairColor[i] > 1.0f)
					crosshairColor[i] = 1.0f;
				else if (crosshairColor[i] < 0)
					crosshairColor[i] = 0;
			}

			trap_R_SetColor(crosshairColor);
			//I can probably scale the zoom with the screen width -/+ keys
			//But I'll do it later.
			//Makro - h = half width, w = width
			h = cg_RQ3_ssgCrosshairSize.integer >> 1;
			w = cg_RQ3_ssgCrosshairSize.integer;
			if ( (hShader = cgs.media.ssgCrosshair[zoomMag]) )
			{
				float dim = cg_RQ3_ssgCrosshairSize.integer * cg.refdef.height / SCREEN_HEIGHT;
				trap_R_DrawStretchPic((cg.refdef.width - dim) * 0.5f, (cg.refdef.height - dim) * 0.5f, dim, dim, 0.f, 0.f, 1.f, 1.f, hShader);
			}

			trap_R_SetColor(NULL);
			// vignetting
			{
				const float ZMC_NORMAL = 0.f;
				const float ZMC_MAX = 0.175f;
				
				float fov_frac = (cg.refdef.fov_x - 10.f) / (90.f - 10.f);
				float frac = 1.f - 2.f * (ZMC_NORMAL * fov_frac + ZMC_MAX * (1.f - fov_frac));

				CG_DrawVignetting(frac);
			}

			drawSSG = 1;
		}
	}

	//Slicer if no crosshair, and not using SSG, dont draw crosshair
	if(!cg_drawCrosshair.integer)
		return;
	
	//Makro - this was above the SSG code
	// set color based on health
	if (cg_crosshairHealth.integer) {
		vec4_t hcolor;

		CG_ColorForHealth(hcolor);
		trap_R_SetColor(hcolor);
	} else {
		trap_R_SetColor(NULL);
	}

	w = h = cg_crosshairSize.value;

	// pulse the size of the crosshair when picking up items
	f = cg.time - cg.itemPickupBlendTime;
	if (f > 0 && f < ITEM_BLOB_TIME) {
		f /= ITEM_BLOB_TIME;
		w *= (1 + f);
		h *= (1 + f);
	}

	if (drawSSG == 0 || (drawSSG == 1 && cg_RQ3_overlaycrosshair.integer == 1)) {
		float half = w * 0.5f;
		x = cg_crosshairX.integer + SCREEN_WIDTH / 2;
		y = cg_crosshairY.integer + SCREEN_HEIGHT / 2;
		
		ca = cg_drawCrosshair.integer;
		if (ca < 0) {
			ca = 0;
		}
		hShader = cgs.media.crosshairShader[ca % NUM_CROSSHAIRS];
		
		crosshairColor[0] = cg_RQ3_crosshairColorR.value;
		crosshairColor[1] = cg_RQ3_crosshairColorG.value;
		crosshairColor[2] = cg_RQ3_crosshairColorB.value;
		crosshairColor[3] = cg_RQ3_crosshairColorA.value;
		for (i = 0; i < 4; i++) {
			if (crosshairColor[i] > 1.0f)
				crosshairColor[i] = 1.0f;
			else if (crosshairColor[i] < 0)
				crosshairColor[i] = 0;
		}
		
		trap_R_SetColor(crosshairColor);
		CG_DrawPic(x - half, y - half, w, h, hShader);
		trap_R_SetColor(NULL);
	}
}

/*
=================
CG_ScanForCrosshairEntity
=================
*/
static void CG_ScanForCrosshairEntity(void)
{
	trace_t trace;
	vec3_t start, end;
	int content;

	VectorCopy(cg.refdef.vieworg, start);
	VectorMA(start, 131072, cg.refdef.viewaxis[0], end);

	CG_Trace(&trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, CONTENTS_SOLID | CONTENTS_BODY);
	if (trace.entityNum >= MAX_CLIENTS) {
		return;
	}
	// if the player is in fog, don't show it
	//content = trap_CM_PointContents(trace.endpos, 0);
	content = CG_PointContents( trace.endpos, 0 );
	if (content & CONTENTS_FOG) {
		return;
	}
	
	// update the fade timer
	cg.crosshairClientNum = trace.entityNum;
	cg.crosshairClientTime = cg.time;
}

/*
=====================
CG_DrawCrosshairNames
=====================
*/
static void CG_DrawCrosshairNames(void)
{
	float *color;
	char *name;
	float w;

	//Slicer, on RQ3 it doesnt depend on crosshair
	/*if (!cg_drawCrosshair.integer) {
		return;
	}*/
	if (!cg_drawCrosshairNames.integer) {
		return;
	}
	if (cg.renderingThirdPerson) {
		return;
	}
// JBravo: no names for zcam users.
	if (cg.snap->ps.stats[STAT_RQ3] & RQ3_ZCAM) {
		return;
	}
	// scan the known entities to see if the crosshair is sighted on one
	CG_ScanForCrosshairEntity();

	// draw the name of the player being looked at
	color = CG_FadeColor(cg.crosshairClientTime, 1000);
	if (!color) {
		trap_R_SetColor(NULL);
		return;
	}
// JBravo: Lets not show player names of opponents
	if (cgs.clientinfo[cg.clientNum].team != cgs.clientinfo[cg.crosshairClientNum].team && cgs.gametype >= GT_TEAM) {
		return;
	}

	name = cgs.clientinfo[cg.crosshairClientNum].name;
	w = CG_DrawStrlen(name) * BIGCHAR_WIDTH;
	CG_DrawBigString(320 - w / 2, 170, name, color[3] * 0.5f);
	trap_R_SetColor(NULL);
}

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

/*
=================
CG_DrawSpectator
=================
*/
static void CG_DrawSpectator(void)
{
	float	Color[4];
	int		team;

	MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 0.4f);

	if (cgs.gametype >= GT_TEAM) {
		team = (cgs.gametype == GT_TEAMPLAY) ? cg.snap->ps.persistant[PERS_SAVEDTEAM] : cg.snap->ps.persistant[PERS_TEAM];
		CG_TeamColor(team, Color);
		Color[0] *= 0.7f;
		Color[1] *= 0.7f;
		Color[2] *= 0.7f;
		Color[3] = 0.3f;
	}

	CG_FillRect(cgs.screenXMin, 420, cgs.screenWidth, 60, Color);

	MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 1.0f);
	CG_DrawCleanLine(cgs.screenXMin, 420, cgs.screenWidth, 1, Color);

	if (cgs.gametype == GT_CTF && (cg.snap->ps.persistant[PERS_SAVEDTEAM] == TEAM_RED ||
				cg.snap->ps.persistant[PERS_SAVEDTEAM] == TEAM_BLUE))
		CG_DrawBigString(320 - 26 * 8, 425, "Waiting for a Team Respawn", 1.0F);
	else
		CG_DrawBigString(320 - 10 * 8, 425, "Spectating", 1.0F);

	if (cg.snap->ps.persistant[PERS_SAVEDTEAM] == TEAM_RED || cg.snap->ps.persistant[PERS_SAVEDTEAM] == TEAM_BLUE)
		return;

	if (cgs.gametype == GT_TOURNAMENT) {
		CG_DrawBigString(320 - 15 * 8, 455, "Waiting to play...", 1.0F);
	} else if (cgs.gametype >= GT_TEAM) {
		CG_DrawBigString(320 - 19 * 8, 455, "Join a team to play", 1.0F);
	}
}

/*
=================
CG_DrawVote
=================
*/
static void CG_DrawVote(void)
{
	const char *s;
	int len;
	int sec, y = 58;
	vec4_t color1;
	float xmin;
	int lines = 3;
	int offset = 0;
	float alpha = 1.f;

	const int previewWidth = 64;
	const int previewHeight = 48;

	int height;

	if (!cgs.voteTime) {
		return;
	}

	// play a talk beep whenever it is modified
	if (cgs.voteModified) {
		cgs.voteModified = qfalse;
		trap_S_StartLocalSound(cgs.media.talkSound, CHAN_LOCAL_SOUND);
	}

	sec = (VOTE_TIME - (cg.time - cgs.voteTime) + 999) / 1000;
	if (sec < 0) {
		sec = 0;
	}

#	define VOTE_FMT(msg) msg "(%d)"

	if ((cg.snap && EF_VOTED == (cg.snap->ps.eFlags & EF_VOTED)) || !cgs.voteYesKey[0] || !cgs.voteNoKey[0])
	{
		s = va(	"Vote called: %s\n"
				VOTE_FMT("Yes") "  " VOTE_FMT("No") "\n"
				"%d %s left\n",
				cgs.voteString,
				cgs.voteYes, cgs.voteNo,
				sec, sec == 1 ? "second" : "seconds"
				);
	}
	else
	{
#		define KEY_FMT S_COLOR_CYAN "%s" S_COLOR_RESET
		
		s = va(	"Vote called: %s\n"
				KEY_FMT "=" VOTE_FMT("Yes") "  " KEY_FMT "=" VOTE_FMT("No") "\n"
				"%d %s left\n",
				cgs.voteString,
				cgs.voteYesKey, cgs.voteYes, cgs.voteNoKey, cgs.voteNo,
				sec, sec == 1 ? "second" : "seconds"
				);

#		undef KEY_FMT
	}
#	undef VOTE_FMT

	len = CG_DrawStrlen(s) * SMALLCHAR_WIDTH;
	height = lines * SMALLCHAR_HEIGHT + 4;

	xmin = cgs.screenXMin + 4;

	if (cgs.media.voteMapShader)
	{
		offset += previewWidth + 4 + 4;
		if (height < previewHeight + 4 + 4)
			height = previewHeight + 4;
	}

	{
		int begin = cg.time - cgs.voteTime;
		int end = VOTE_TIME - begin;
		int min = begin < end ? begin : end;
		alpha = SmoothLerp(Com_Clamp(0.f, 1.f, min / (1000.f * 0.125f)));
	}
	
	// slide in //
	xmin -= (1.f - alpha) * (len + offset);

	{
		const int inflatex = 32;
		const int inflatey = 24;
		CG_DrawFuzzyShadow(xmin + 1 - inflatex, y - inflatey, len + 4 + offset + inflatex + inflatex, height + inflatey + inflatey, 32, 0.75f * alpha);
	}

	MAKERGBA(color1, 1.0f, 1.0f, 1.0f, alpha);
	
	if (cgs.media.voteMapShader)
	{
		CG_FillRect(xmin + 1, y, previewWidth + 4, previewHeight + 4, color1);
		trap_R_SetColor(color1);
		CG_DrawPic(xmin + 3, y + 2, previewWidth, previewHeight, cgs.media.voteMapShader);

		xmin += offset;
	}
	
	CG_DrawStringExt(xmin + 3, y+2, s, color1, qfalse, qfalse, 
		SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 100);

	trap_R_SetColor(NULL);
}

/*
=================
CG_DrawTeamVote
=================
*/
static void CG_DrawTeamVote(void)
{
	char *s, *s2;
	int sec, cs_offset, y = 100;
	float Color1[4];

	if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED )
		cs_offset = 0;
	else if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE )
		cs_offset = 1;
	else
		return;

	if (!cgs.teamVoteTime[cs_offset]) {
		return;
	}
	// play a talk beep whenever it is modified
	if (cgs.teamVoteModified[cs_offset]) {
		cgs.teamVoteModified[cs_offset] = qfalse;
		trap_S_StartLocalSound(cgs.media.talkSound, CHAN_LOCAL_SOUND);
	}

	sec = (VOTE_TIME - (cg.time - cgs.teamVoteTime[cs_offset])) / 1000;
	if (sec < 0) {
		sec = 0;
	}
	/*
	s = va("TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[cs_offset],
	       cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset]);
	CG_DrawSmallString(0, 90, s, 1.0F);
	*/
	s = va("TeamVote[%i seconds left] Yes[%i] No[%i]", sec, cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset]);
	s2 = va("Vote: %s", cgs.teamVoteString[cs_offset]);

	MAKERGBA(Color1, 0.0f, 0.0f, 0.0f, 0.4f);

	CG_FillRect(cgs.screenXMin + 1, y, CG_DrawStrlen(s) * SMALLCHAR_WIDTH + 4, 
		SMALLCHAR_HEIGHT + 4, Color1);
	CG_DrawCleanRect(cgs.screenXMin + 1, y, CG_DrawStrlen(s) * SMALLCHAR_WIDTH + 4, 
		SMALLCHAR_HEIGHT + 4, 1, colorBlack);
	CG_DrawStringExt(cgs.screenXMin + 3, y+2, s, colorWhite, qtrue, qfalse, 
		SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT,  100);
	y += SMALLCHAR_HEIGHT + 3;
	
	CG_FillRect(cgs.screenXMin + 1, y, CG_DrawStrlen(s2) * SMALLCHAR_WIDTH + 4, 
		SMALLCHAR_HEIGHT + 4, Color1);
	CG_DrawCleanRect(cgs.screenXMin + 1, y, CG_DrawStrlen(s2) * SMALLCHAR_WIDTH + 4, 
		SMALLCHAR_HEIGHT + 4, 1, colorBlack);
	CG_DrawStringExt(cgs.screenXMin + 3, y + 2, s2, colorWhite, qtrue, qfalse, 
		SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT,  100);
}

static qboolean CG_DrawScoreboard( void )
{
	return CG_DrawOldScoreboard();
}

/*
=================
CG_DrawIntermission
=================
*/
static void CG_DrawIntermission(void)
{
	if (cgs.gametype == GT_SINGLE_PLAYER) {
		CG_DrawCenterString();
		return;
	}
	cg.scoreFadeTime = cg.time;
	cg.scoreBoardShowing = CG_DrawScoreboard();
}

/*
=================
CG_DrawFollow
=================
*/
static qboolean CG_DrawFollow(void)
{
//Makro - char size
#define DF_WIDTH 8
#define DF_HEIGHT 12
	static unsigned int df_effect_time = 0;
	int time;
	static qboolean df_showmark = qfalse;
	float x;
	vec4_t color;
	const char *name;
	int team;
	char combinedName[512];

	if (!(cg.snap->ps.pm_flags & PMF_FOLLOW)) {
		return qfalse;
	}
	color[0] = 0.75f;
	color[1] = 0.75f;
	color[2] = 0.75f;
	color[3] = 1.0f;

// JBravo: if gametype >= team, append teamname to his name.
	if (cgs.gametype >= GT_TEAM) {
		team = cgs.clientinfo[cg.snap->ps.clientNum].team;
		//Makro - different look
		/*
		if (team == TEAM_RED) {
			Com_sprintf(combinedName, sizeof(combinedName), "%sFollowing%s %s%s/%s%s", S_COLOR_RED,
				    S_COLOR_WHITE, cgs.clientinfo[cg.snap->ps.clientNum].name, S_COLOR_RED,
				    S_COLOR_MAGENTA, cg_RQ3_team1name.string);
		} else {
			Com_sprintf(combinedName, sizeof(combinedName), "%sFollowing%s %s%s/%s%s", S_COLOR_RED,
				    S_COLOR_WHITE, cgs.clientinfo[cg.snap->ps.clientNum].name, S_COLOR_RED,
				    S_COLOR_MAGENTA, cg_RQ3_team2name.string);
		}
		x = 0.5 * (640 - BIGCHAR_WIDTH * CG_DrawStrlen(combinedName));
		CG_DrawStringExt(x, 372, combinedName, color, qfalse, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0);
		*/
		time = trap_Milliseconds();
		if (time > df_effect_time)
		{
			df_effect_time = time + 500;
			df_showmark ^= qtrue;
		}
		if (df_showmark)
		{
			if (team == TEAM_RED) {
				Com_sprintf(combinedName, sizeof(combinedName), S_COLOR_YELLOW">"S_COLOR_RESET" Following ^7%s^* (^7%s^*) "S_COLOR_YELLOW"<",
					cgs.clientinfo[cg.snap->ps.clientNum].name,	cg_RQ3_team1name.string);
			} else {
				Com_sprintf(combinedName, sizeof(combinedName), S_COLOR_YELLOW">"S_COLOR_RESET" Following ^7%s^* (^7%s^*) "S_COLOR_YELLOW"<",
					cgs.clientinfo[cg.snap->ps.clientNum].name, cg_RQ3_team2name.string);
			}
		} else {
			if (team == TEAM_RED) {
				Com_sprintf(combinedName, sizeof(combinedName), "Following ^7%s ^*(^7%s^*)",
					cgs.clientinfo[cg.snap->ps.clientNum].name,	cg_RQ3_team1name.string);
			} else {
				Com_sprintf(combinedName, sizeof(combinedName), "Following ^7%s ^*(^7%s^*)",
					cgs.clientinfo[cg.snap->ps.clientNum].name, cg_RQ3_team2name.string);
			}
		}
		x = 0.5 * (640 - DF_WIDTH * CG_DrawStrlen(combinedName));
		CG_DrawStringExt(x, 80, combinedName, color, qfalse, qfalse, DF_WIDTH, DF_HEIGHT, 0);
	} else {
		CG_DrawBigString(320 - 9 * 8, 24, "following", 1.0F);
		name = cgs.clientinfo[cg.snap->ps.clientNum].name;
		x = 0.5 * (640 - GIANT_WIDTH * CG_DrawStrlen(combinedName));
		CG_DrawStringExt(x, 40, name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0);
	}

	return qtrue;
}

/*
=================
CG_DrawAmmoWarning
=================
*/
/* JBravo: Not used
static void CG_DrawAmmoWarning(void)
{
	const char *s;
//	int w;

	if (cg_drawAmmoWarning.integer == 0) {
		return;
	}

	if (!cg.lowAmmoWarning) {
		return;
	}

	if (cg.lowAmmoWarning == 2) {
		s = "OUT OF AMMO";
	} else {
		s = "LOW AMMO WARNING";
	}
	CG_DrawStrlen(s) * BIGCHAR_WIDTH;
	//Elder: commented out for now
	//CG_DrawBigString(320 - w / 2, 64, s, 1.0F);
}
*/

/*
=================
CG_DrawWarmup
=================
*/
static void CG_DrawWarmup(void)
{
	int w;
	int sec;
	int i;
//	float scale;
	clientInfo_t *ci1, *ci2;
	int cw;
	const char *s;

	sec = cg.warmup;
	if (!sec) {
		return;
	}

	if (sec < 0) {
		s = "Waiting for players";
		w = CG_DrawStrlen(s) * BIGCHAR_WIDTH;
		CG_DrawBigString(320 - w / 2, 24, s, 1.0F);
		cg.warmupCount = 0;
		return;
	}

	if (cgs.gametype == GT_TOURNAMENT) {
		// find the two active players
		ci1 = NULL;
		ci2 = NULL;
		for (i = 0; i < cgs.maxclients; i++) {
			if (cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_FREE) {
				if (!ci1) {
					ci1 = &cgs.clientinfo[i];
				} else {
					ci2 = &cgs.clientinfo[i];
				}
			}
		}

		if (ci1 && ci2) {
			s = va("%s vs %s", ci1->name, ci2->name);
			w = CG_DrawStrlen(s);
			if (w > 640 / GIANT_WIDTH) {
				cw = 640 / w;
			} else {
				cw = GIANT_WIDTH;
			}
			CG_DrawStringExt(320 - w * cw / 2, 20, s, colorWhite, qfalse, qtrue, cw, (int) (cw * 1.5f), 0);
		}
	} else {
		if (cgs.gametype == GT_FFA) {
			s = "Free For All";
		} else if (cgs.gametype == GT_TEAM) {
			s = "Team Deathmatch";
// JBravo adding teamplay
		} else if (cgs.gametype == GT_TEAMPLAY) {
			s = "RQ3 Teamplay";
// JBravo: Now known as CTB
		} else if (cgs.gametype == GT_CTF) {
			s = "Capture the Briefcase";
		} else {
			s = "";
		}
		w = CG_DrawStrlen(s);
		if (w > 640 / GIANT_WIDTH) {
			cw = 640 / w;
		} else {
			cw = GIANT_WIDTH;
		}
		CG_DrawStringExt(320 - w * cw / 2, 25, s, colorWhite, qfalse, qtrue, cw, (int) (cw * 1.1f), 0);
	}

	sec = (sec - cg.time) / 1000;
	if (sec < 0) {
		cg.warmup = 0;
		sec = 0;
	}
	s = va("Starts in: %i", sec + 1);
	if (sec != cg.warmupCount) {
		cg.warmupCount = sec;
		switch (sec) {
		case 0:
			if (cg_RQ3_anouncer.integer == 1)
				trap_S_StartLocalSound(cgs.media.count1Sound, CHAN_ANNOUNCER);
			break;
		case 1:
			if (cg_RQ3_anouncer.integer == 1)
				trap_S_StartLocalSound(cgs.media.count2Sound, CHAN_ANNOUNCER);
			break;
		case 2:
			if (cg_RQ3_anouncer.integer == 1)
				trap_S_StartLocalSound(cgs.media.count3Sound, CHAN_ANNOUNCER);
			break;
		default:
			break;
		}
	}
//	scale = 0.45f;
	switch (cg.warmupCount) {
	case 0:
		cw = 28;
//		scale = 0.54f;
		break;
	case 1:
		cw = 24;
//		scale = 0.51f;
		break;
	case 2:
		cw = 20;
//		scale = 0.48f;
		break;
	default:
		cw = 16;
//		scale = 0.45f;
		break;
	}

	w = CG_DrawStrlen(s);
	CG_DrawStringExt(320 - w * cw / 2, 70, s, colorWhite, qfalse, qtrue, cw, (int) (cw * 1.5), 0);
}

//==================================================================================
/*
=================
CG_Draw2D
=================
*/
static void CG_Draw2D(void)
{
	// if we are taking a levelshot for the menu, don't draw anything
	if (cg.levelShot) {
		return;
	}

	if (cg_draw2D.integer == 0) {
		return;
	}

	if (cg.snap->ps.pm_type == PM_INTERMISSION) {
		float y;

		CG_DrawIntermission();

		// Makro - make sure chat messages still get drawn during intermission

		y = 480 - ICON_SIZE;
		y = CG_DrawMessageQueue(y);
		
		return;
	}

/*
	if (cg.cameraMode) {
		return;
	}
*/
	//Slicer: Adding HUD for follow spectating
	if ((cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR && !(cg.snap->ps.pm_flags & PMF_FOLLOW)) ||
	    cg.predictedPlayerState.pm_type == PM_SPECTATOR) {
		//if (cg.snap->ps.persistant[PERS_SAVEDTEAM] != TEAM_RED && cg.snap->ps.persistant[PERS_SAVEDTEAM] != TEAM_BLUE)
		// cg.predictedPlayerState.pm_type == PM_SPECTATOR
// JBravo: no spectator bar for zcam modes.
		if (!(cg.snap->ps.stats[STAT_RQ3] & RQ3_ZCAM)) {
			CG_DrawSpectator();
		}
		CG_DrawCrosshair();
		CG_DrawCrosshairNames();
	} else {
		// don't draw any status if dead or the scoreboard is being explicitly shown
		// if ( !cg.showScores && cg.snap->ps.stats[STAT_HEALTH] > 0 ) {
		if (cg.snap->ps.stats[STAT_HEALTH] > 0) {
			CG_DrawCrosshair();
			CG_DrawStatusBar();
//			CG_DrawAmmoWarning();
			CG_DrawCrosshairNames();
			CG_DrawWeaponSelect();
			CG_DrawHoldableItem();
			CG_DrawReward();
		}
	}

	CG_DrawVote();
	CG_DrawTeamVote();
	CG_DrawLagometer();
	CG_DrawUpperRight();
	CG_DrawLowerRight();
	CG_DrawLowerLeft();

	if (!CG_DrawFollow()) {
		CG_DrawWarmup();
	}
	// don't draw center string if scoreboard is up
	cg.scoreBoardShowing = CG_DrawScoreboard();
	if (!cg.scoreBoardShowing) {
		CG_DrawCenterString();
	}
	if (cg.showWStats)
		CG_DrawWeaponStats();
}

static void CG_DrawTourneyScoreboard( void )
{
	CG_DrawOldTourneyScoreboard();
}

/*
=====================
CG_DrawDamageBlend

Elder: Does a fullscreen alpha blend like Quake 2 when hurt
Makro - changed to 0..1 instead of 0/1
=====================
*/
#define MAX_DAMAGE_ALPHA	0.75
#define MAX_BLEND_TIME		1500

static float CG_GetDamageBlendAlpha( void )
{
	float dmg, blend = Com_Clamp(0, 1, cg_RQ3_painblend.value);

	if (cg.rq3_blendTime <= 0.f)
		return 0.f;
	
	//Clamp blend time
	if (cg.rq3_blendTime > MAX_BLEND_TIME)
		cg.rq3_blendTime = MAX_BLEND_TIME;

	dmg = cg.rq3_trueDamage;
	dmg = blend * MAX_DAMAGE_ALPHA * (dmg / 100.0) * (1.0 - (cg.time - cg.damageTime) / cg.rq3_blendTime);
	dmg = Com_Clamp(0.f, MAX_DAMAGE_ALPHA, dmg);

	return dmg;
}

static void CG_DrawDamageBlend( void )
{
	float dmg, blend = Com_Clamp(0, 1, cg_RQ3_painblend.value);
	vec4_t damageColor;

	//CG_Printf("CG_DrawDamageBlend: trueDamage (%i)\n", cg.rq3_trueDamage);

	//Leave if no true damage, disabled, or ragepro
	if (!blend)
		return;

	if (!cg.rq3_trueDamage || cgs.glconfig.hardwareType == GLHW_RAGEPRO)
		return;

	//Clamp blend time
	if (cg.rq3_blendTime > MAX_BLEND_TIME)
		cg.rq3_blendTime = MAX_BLEND_TIME;

	//Reset if we've gone past our blendTime
	if (cg.time - cg.damageTime > cg.rq3_blendTime) {
		//cg.rq3_trueDamage = 0;
		cg.rq3_blendTime = 0;
		return;
	}

	VectorCopy(colorRed, damageColor);
	dmg = cg.rq3_trueDamage;

	//clamp at 100 health
	if (dmg > 100)
		dmg = 100;

	damageColor[3] = blend * MAX_DAMAGE_ALPHA * (dmg / 100.0) * (1.0 - (cg.time - cg.damageTime) / cg.rq3_blendTime);

	if (damageColor[3] > MAX_DAMAGE_ALPHA)
		damageColor[3] = MAX_DAMAGE_ALPHA;
	else if (damageColor[3] < 0)
		damageColor[3] = 0;

	CG_FillRect(cgs.screenXMin, 0, cgs.screenWidth, SCREEN_HEIGHT, damageColor);
}

/*
=====================
CG_DrawDeathBlend

Makro: fade to black after death
=====================
*/

static void CG_DrawDeathBlend( void )
{
	const float MAX_ALPHA = 0.875f;
	float delta;
	vec4_t color;

	if (!cg.snap || cg.snap->ps.stats[STAT_HEALTH] > 0)
		return;

	delta = (cg.time - cg.timeOfDeath) / 1000.f;
	VectorCopy(colorBlack, color);
	color[3] = MAX_ALPHA * (1.f - 1.f / (delta + 1.f));

	trap_R_SetColor(color);
	trap_R_DrawStretchPic(0.f, 0.f, cg.refdef.width, cg.refdef.height, 0.f, 0.f, 1.f, 1.f, cgs.media.whiteShader);
	trap_R_SetColor(NULL);

}


/*
=====================
CG_DrawIRBlend

Elder: Small red tint
Note: This sucks - causes 10fps drop on my system so don't use it
=====================
// JBravo: apparently not used.
*/
/*
static void CG_DrawIRBlend()
{
	vec4_t irColor;

	if (bg_itemlist[cg.snap->ps.stats[STAT_HOLDABLE_ITEM]].giTag == HI_BANDOLIER
		&& cg.rq3_irvision)
	{
		irColor[0] = 0;
		irColor[1] = 1.0f;
		irColor[2] = 0;
		irColor[3] = 0.1f;
		CG_FillRect(0,0,SCREEN_WIDTH, SCREEN_HEIGHT, irColor);
	}
}
*/

// If zbuffer is disabled, this should make it impossible to see?
// By NiceAss (which means it probably doesn't work)
void CG_DrawBigPolygon(void) {
	vec3_t		end, forward, right, up, fogColor = {0, 0, 0};
	polyVert_t	Corners[4];
	float	Dist = 2048;//12288;
	//Makro - added
	const char *info;
	int		i;

	AngleVectors(cg.refdefViewAngles, forward, right, up);
	VectorMA(cg.refdef.vieworg, Dist, forward, end);

	VectorMA(end, -Dist, right, Corners[0].xyz);
	VectorMA(Corners[0].xyz, -Dist, up, Corners[0].xyz);
	Corners[0].st[0] = 0;
	Corners[0].st[1] = 0;

	VectorMA(end, Dist, right, Corners[1].xyz);
	VectorMA(Corners[1].xyz, -Dist, up, Corners[1].xyz);
	Corners[1].st[0] = 0;
	Corners[1].st[1] = 1;

	VectorMA(end, Dist, right, Corners[2].xyz);
	VectorMA(Corners[2].xyz, Dist, up, Corners[2].xyz);
	Corners[2].st[0] = 1;
	Corners[2].st[1] = 1;

	VectorMA(end, -Dist, right, Corners[3].xyz);
	VectorMA(Corners[3].xyz, Dist, up, Corners[3].xyz);
	Corners[3].st[0] = 1;
	Corners[3].st[1] = 0;

	//Makro
	info = CG_ConfigString(CS_FOGHULL);
	if (info) {
		if (info[0]) {
			fogColor[0] = atof(Info_ValueForKey(info, "r"));
			fogColor[1] = atof(Info_ValueForKey(info, "g"));
			fogColor[2] = atof(Info_ValueForKey(info, "b"));
			//CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, fogcolor);
			//Com_Printf("Fog color: %f %f %f\n", fogcolor[0], fogcolor[1], fogcolor[2]);
		}
	}

	for (i = 0; i < 4; i++) {
		Corners[i].modulate[0] = 255 * fogColor[0];
		Corners[i].modulate[1] = 255 * fogColor[1];
		Corners[i].modulate[2] = 255 * fogColor[2];
		Corners[i].modulate[3] = 255;
	}

	//  CS_FOGHULL
	
	trap_R_AddPolyToScene(cgs.media.blackHackShader, 4, Corners);
}

// Makro - this needs to be called after cg.waterTransitionTime and cg.inWaterLastFrame have been updated
static float CG_GetUnderWaterFraction( void )
{
	const int WATER_ANIM_TIME = 500; // msec
	const int delta = cg.time - cg.waterTransitionTime;
	float frac = Com_Clamp(0.f, 1.f, delta / (float)WATER_ANIM_TIME);
	if (!cg.inWaterLastFrame)
		frac = 1.f - frac;
	return frac;
}

static qboolean CG_IsDead( void )
{
	return cg.snap && cg.snap->ps.stats[STAT_HEALTH] <= 0;
}

static qboolean CG_UsesIRVision( void )
{
	return cg.rq3_irvision && cg.snap && cg.snap->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_BANDOLIER);
}

static void CG_DrawIRVisionBlend( void )
{
	float xoffset, yoffset, xscale, yscale;
	if (!CG_UsesIRVision() || !cgs.media.irvision_overlay)
		return;

	xscale = cg.refdef.width / 256.f;
	yscale = cg.refdef.height / 256.f;
	xoffset = random() * xscale;
	yoffset = random() * yscale;

	CG_DrawVignetting(0.8125f);
	trap_R_SetColor(NULL);
	trap_R_DrawStretchPic(0.f, 0.f, cg.refdef.width, cg.refdef.height, xoffset, yoffset, xscale+xoffset, yscale+yoffset, cgs.media.irvision_overlay);
}

static void CG_SetupPostProcess( void )
{
	cg.refdefex.blurFactor = CG_GetDamageBlendAlpha();
	cg.refdefex.blurFactor += CG_GetUnderWaterFraction();
	
	if (CG_IsDead())
		cg.refdefex.blurFactor += 1.f;
	
	if (CG_UsesIRVision())
		cg.refdefex.blurFactor += 0.75f;

	if (trap_Key_GetCatcher() & KEYCATCH_UI)
		cg.refdefex.blurFactor += 1.f;

	cg.refdefex.blurFactor = Com_Clamp(0.f, 1.f, cg.refdefex.blurFactor);

	if (cg.refdefex.blurFactor > 0.f)
	{
		cg.refdef.rdflags |= RDF_EXTRA;
	}
}

/*
=====================
CG_DrawActive

Perform all drawing needed to completely fill the screen
=====================
*/
void CG_DrawActive(stereoFrame_t stereoView)
{
	// optionally draw the info screen instead
	if (!cg.snap) {
		CG_DrawInformation();
		return;
	}

	// optionally draw the tournement scoreboard instead
	if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR && (cg.snap->ps.pm_flags & PMF_SCOREBOARD)) {
		CG_DrawTourneyScoreboard();
		return;
	}

	// clear around the rendered view if sized down
	CG_TileClear();

	// NiceAss: Wallhack protection
	//if (cg.snap->ps.pm_type == PM_NORMAL)
	//	CG_DrawBigPolygon();

	//Makro - sun flare
	CG_AddLensFlare(qtrue);

	CG_SetupPostProcess();

	// draw 3D view
	trap_R_RenderScene(&cg.refdef);

	// restore original viewpoint if running stereo
//	if (separation != 0) {
//		VectorCopy(baseOrg, cg.refdef.vieworg);
//	}
	// Elder: draw damage blend
	CG_DrawDamageBlend();
	//CG_DrawIRBlend();
	CG_DrawDeathBlend();
	CG_DrawIRVisionBlend();

	// draw status bar and other floating elements
	CG_Draw2D();

	//Makro - reflection particles
	CG_AddLensFlare(qfalse);
}
