summaryrefslogtreecommitdiff
path: root/src/cgame/cg_draw.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cgame/cg_draw.c')
-rw-r--r--src/cgame/cg_draw.c3524
1 files changed, 3524 insertions, 0 deletions
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c
new file mode 100644
index 0000000..82df413
--- /dev/null
+++ b/src/cgame/cg_draw.c
@@ -0,0 +1,3524 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2006 Tim Angus
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Tremulous is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Tremulous; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// cg_draw.c -- draw all of the graphical elements during
+// active (after loading) gameplay
+
+
+#include "cg_local.h"
+#include "../ui/ui_shared.h"
+
+// used for scoreboard
+extern displayContextDef_t cgDC;
+menuDef_t *menuScoreboard = NULL;
+
+int drawTeamOverlayModificationCount = -1;
+
+int sortedTeamPlayers[ TEAM_MAXOVERLAY ];
+int numSortedTeamPlayers;
+
+//TA UI
+int CG_Text_Width( const char *text, float scale, int limit )
+{
+ int count,len;
+ float out;
+ glyphInfo_t *glyph;
+ float useScale;
+// FIXME: see ui_main.c, same problem
+// const unsigned char *s = text;
+ const char *s = text;
+ fontInfo_t *font = &cgDC.Assets.textFont;
+
+ if( scale <= cg_smallFont.value )
+ font = &cgDC.Assets.smallFont;
+ else if( scale > cg_bigFont.value )
+ font = &cgDC.Assets.bigFont;
+
+ useScale = scale * font->glyphScale;
+ out = 0;
+
+ if( text )
+ {
+ len = strlen( text );
+ if( limit > 0 && len > limit )
+ len = limit;
+
+ count = 0;
+ while( s && *s && count < len )
+ {
+ if( Q_IsColorString( s ) )
+ {
+ s += 2;
+ continue;
+ }
+ else
+ {
+ glyph = &font->glyphs[ (int)*s ];
+ //TTimo: FIXME: getting nasty warnings without the cast,
+ //hopefully this doesn't break the VM build
+ out += glyph->xSkip;
+ s++;
+ count++;
+ }
+ }
+ }
+
+ return out * useScale;
+}
+
+int CG_Text_Height( const char *text, float scale, int limit )
+{
+ int len, count;
+ float max;
+ glyphInfo_t *glyph;
+ float useScale;
+// TTimo: FIXME
+// const unsigned char *s = text;
+ const char *s = text;
+ fontInfo_t *font = &cgDC.Assets.textFont;
+
+ if( scale <= cg_smallFont.value )
+ font = &cgDC.Assets.smallFont;
+ else if( scale > cg_bigFont.value )
+ font = &cgDC.Assets.bigFont;
+
+ useScale = scale * font->glyphScale;
+ max = 0;
+
+ if( text )
+ {
+ len = strlen( text );
+ if( limit > 0 && len > limit )
+ len = limit;
+
+ count = 0;
+ while( s && *s && count < len )
+ {
+ if( Q_IsColorString( s ) )
+ {
+ s += 2;
+ continue;
+ }
+ else
+ {
+ glyph = &font->glyphs[ (int)*s ];
+ //TTimo: FIXME: getting nasty warnings without the cast,
+ //hopefully this doesn't break the VM build
+ if( max < glyph->height )
+ max = glyph->height;
+
+ s++;
+ count++;
+ }
+ }
+ }
+
+ return max * useScale;
+}
+
+void CG_Text_PaintChar( float x, float y, float width, float height, float scale,
+ float s, float t, float s2, float t2, qhandle_t hShader )
+{
+ float w, h;
+ w = width * scale;
+ h = height * scale;
+ CG_AdjustFrom640( &x, &y, &w, &h );
+ trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader );
+}
+
+void CG_Text_Paint( float x, float y, float scale, vec4_t color, const char *text,
+ float adjust, int limit, int style )
+{
+ int len, count;
+ vec4_t newColor;
+ glyphInfo_t *glyph;
+ float useScale;
+ fontInfo_t *font = &cgDC.Assets.textFont;
+
+ if( scale <= cg_smallFont.value )
+ font = &cgDC.Assets.smallFont;
+ else if( scale > cg_bigFont.value )
+ font = &cgDC.Assets.bigFont;
+
+ useScale = scale * font->glyphScale;
+ if( text )
+ {
+// TTimo: FIXME
+// const unsigned char *s = text;
+ const char *s = text;
+
+ trap_R_SetColor( color );
+ memcpy( &newColor[ 0 ], &color[ 0 ], sizeof( vec4_t ) );
+ len = strlen( text );
+
+ if( limit > 0 && len > limit )
+ len = limit;
+
+ count = 0;
+ while( s && *s && count < len )
+ {
+ glyph = &font->glyphs[ (int)*s ];
+ //TTimo: FIXME: getting nasty warnings without the cast,
+ //hopefully this doesn't break the VM build
+
+ if( Q_IsColorString( s ) )
+ {
+ memcpy( newColor, g_color_table[ ColorIndex( *( s + 1 ) ) ], sizeof( newColor ) );
+ newColor[ 3 ] = color[ 3 ];
+ trap_R_SetColor( newColor );
+ s += 2;
+ continue;
+ }
+ else
+ {
+ float yadj = useScale * glyph->top;
+ if( style == ITEM_TEXTSTYLE_SHADOWED ||
+ style == ITEM_TEXTSTYLE_SHADOWEDMORE )
+ {
+ int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;
+ colorBlack[ 3 ] = newColor[ 3 ];
+ trap_R_SetColor( colorBlack );
+ CG_Text_PaintChar( x + ofs, y - yadj + ofs,
+ glyph->imageWidth,
+ glyph->imageHeight,
+ useScale,
+ glyph->s,
+ glyph->t,
+ glyph->s2,
+ glyph->t2,
+ glyph->glyph );
+
+ colorBlack[ 3 ] = 1.0;
+ trap_R_SetColor( newColor );
+ }
+ else if( style == ITEM_TEXTSTYLE_NEON )
+ {
+ vec4_t glow, outer, inner, white;
+
+ glow[ 0 ] = newColor[ 0 ] * 0.5;
+ glow[ 1 ] = newColor[ 1 ] * 0.5;
+ glow[ 2 ] = newColor[ 2 ] * 0.5;
+ glow[ 3 ] = newColor[ 3 ] * 0.2;
+
+ outer[ 0 ] = newColor[ 0 ];
+ outer[ 1 ] = newColor[ 1 ];
+ outer[ 2 ] = newColor[ 2 ];
+ outer[ 3 ] = newColor[ 3 ];
+
+ inner[ 0 ] = newColor[ 0 ] * 1.5 > 1.0f ? 1.0f : newColor[ 0 ] * 1.5;
+ inner[ 1 ] = newColor[ 1 ] * 1.5 > 1.0f ? 1.0f : newColor[ 1 ] * 1.5;
+ inner[ 2 ] = newColor[ 2 ] * 1.5 > 1.0f ? 1.0f : newColor[ 2 ] * 1.5;
+ inner[ 3 ] = newColor[ 3 ];
+
+ white[ 0 ] = white[ 1 ] = white[ 2 ] = white[ 3 ] = 1.0f;
+
+ trap_R_SetColor( glow );
+ CG_Text_PaintChar( x - 3, y - yadj - 3,
+ glyph->imageWidth + 6,
+ glyph->imageHeight + 6,
+ useScale,
+ glyph->s,
+ glyph->t,
+ glyph->s2,
+ glyph->t2,
+ glyph->glyph );
+
+ trap_R_SetColor( outer );
+ CG_Text_PaintChar( x - 1, y - yadj - 1,
+ glyph->imageWidth + 2,
+ glyph->imageHeight + 2,
+ useScale,
+ glyph->s,
+ glyph->t,
+ glyph->s2,
+ glyph->t2,
+ glyph->glyph );
+
+ trap_R_SetColor( inner );
+ CG_Text_PaintChar( x - 0.5, y - yadj - 0.5,
+ glyph->imageWidth + 1,
+ glyph->imageHeight + 1,
+ useScale,
+ glyph->s,
+ glyph->t,
+ glyph->s2,
+ glyph->t2,
+ glyph->glyph );
+
+ trap_R_SetColor( white );
+ }
+
+
+ CG_Text_PaintChar( x, y - yadj,
+ glyph->imageWidth,
+ glyph->imageHeight,
+ useScale,
+ glyph->s,
+ glyph->t,
+ glyph->s2,
+ glyph->t2,
+ glyph->glyph );
+
+ x += ( glyph->xSkip * useScale ) + adjust;
+ s++;
+ count++;
+ }
+ }
+
+ trap_R_SetColor( NULL );
+ }
+}
+
+/*
+==============
+CG_DrawFieldPadded
+
+Draws large numbers for status bar and powerups
+==============
+*/
+static void CG_DrawFieldPadded( int x, int y, int width, int cw, int ch, int value )
+{
+ char num[ 16 ], *ptr;
+ int l, orgL;
+ int frame;
+ int charWidth, charHeight;
+
+ if( !( charWidth = cw ) )
+ charWidth = CHAR_WIDTH;
+
+ if( !( charHeight = ch ) )
+ charWidth = CHAR_HEIGHT;
+
+ if( width < 1 )
+ return;
+
+ // draw number string
+ if( width > 4 )
+ width = 4;
+
+ 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 ), "%d", value );
+ l = strlen( num );
+
+ if( l > width )
+ l = width;
+
+ orgL = l;
+
+ x += 2;
+
+ ptr = num;
+ while( *ptr && l )
+ {
+ if( width > orgL )
+ {
+ CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[ 0 ] );
+ width--;
+ x += charWidth;
+ continue;
+ }
+
+ if( *ptr == '-' )
+ frame = STAT_MINUS;
+ else
+ frame = *ptr - '0';
+
+ CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[ frame ] );
+ x += charWidth;
+ ptr++;
+ l--;
+ }
+}
+
+/*
+==============
+CG_DrawField
+
+Draws large numbers for status bar and powerups
+==============
+*/
+void CG_DrawField( float x, float y, int width, float cw, float ch, int value )
+{
+ char num[ 16 ], *ptr;
+ int l;
+ int frame;
+ float charWidth, charHeight;
+
+ if( !( charWidth = cw ) )
+ charWidth = CHAR_WIDTH;
+
+ if( !( charHeight = ch ) )
+ charWidth = CHAR_HEIGHT;
+
+ if( width < 1 )
+ return;
+
+ // draw number string
+ if( width > 4 )
+ width = 4;
+
+ 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 ), "%d", value );
+ l = strlen( num );
+
+ if( l > width )
+ l = width;
+
+ x += 2 + charWidth * ( width - l );
+
+ ptr = num;
+ while( *ptr && l )
+ {
+ if( *ptr == '-' )
+ frame = STAT_MINUS;
+ else
+ frame = *ptr -'0';
+
+ CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[ frame ] );
+ x += charWidth;
+ ptr++;
+ l--;
+ }
+}
+
+static void CG_DrawProgressBar( rectDef_t *rect, vec4_t color, float scale,
+ int align, int textStyle, int special, float progress )
+{
+ float rimWidth = rect->h / 20.0f;
+ float doneWidth, leftWidth;
+ float tx, ty, tw, th;
+ char textBuffer[ 8 ];
+
+ if( rimWidth < 0.6f )
+ rimWidth = 0.6f;
+
+ if( special >= 0.0f )
+ rimWidth = special;
+
+ if( progress < 0.0f )
+ progress = 0.0f;
+ else if( progress > 1.0f )
+ progress = 1.0f;
+
+ doneWidth = ( rect->w - 2 * rimWidth ) * progress;
+ leftWidth = ( rect->w - 2 * rimWidth ) - doneWidth;
+
+ trap_R_SetColor( color );
+
+ //draw rim and bar
+ if( align == ITEM_ALIGN_RIGHT )
+ {
+ CG_DrawPic( rect->x, rect->y, rimWidth, rect->h, cgs.media.whiteShader );
+ CG_DrawPic( rect->x + rimWidth, rect->y,
+ leftWidth, rimWidth, cgs.media.whiteShader );
+ CG_DrawPic( rect->x + rimWidth, rect->y + rect->h - rimWidth,
+ leftWidth, rimWidth, cgs.media.whiteShader );
+ CG_DrawPic( rect->x + rimWidth + leftWidth, rect->y,
+ rimWidth + doneWidth, rect->h, cgs.media.whiteShader );
+ }
+ else
+ {
+ CG_DrawPic( rect->x, rect->y, rimWidth + doneWidth, rect->h, cgs.media.whiteShader );
+ CG_DrawPic( rimWidth + rect->x + doneWidth, rect->y,
+ leftWidth, rimWidth, cgs.media.whiteShader );
+ CG_DrawPic( rimWidth + rect->x + doneWidth, rect->y + rect->h - rimWidth,
+ leftWidth, rimWidth, cgs.media.whiteShader );
+ CG_DrawPic( rect->x + rect->w - rimWidth, rect->y, rimWidth, rect->h, cgs.media.whiteShader );
+ }
+
+ trap_R_SetColor( NULL );
+
+ //draw text
+ if( scale > 0.0 )
+ {
+ Com_sprintf( textBuffer, sizeof( textBuffer ), "%d%%", (int)( progress * 100 ) );
+ tw = CG_Text_Width( textBuffer, scale, 0 );
+ th = scale * 40.0f;
+
+ switch( align )
+ {
+ case ITEM_ALIGN_LEFT:
+ tx = rect->x + ( rect->w / 10.0f );
+ ty = rect->y + ( rect->h / 2.0f ) + ( th / 2.0f );
+ break;
+
+ case ITEM_ALIGN_RIGHT:
+ tx = rect->x + rect->w - ( rect->w / 10.0f ) - tw;
+ ty = rect->y + ( rect->h / 2.0f ) + ( th / 2.0f );
+ break;
+
+ case ITEM_ALIGN_CENTER:
+ tx = rect->x + ( rect->w / 2.0f ) - ( tw / 2.0f );
+ ty = rect->y + ( rect->h / 2.0f ) + ( th / 2.0f );
+ break;
+
+ default:
+ tx = ty = 0.0f;
+ }
+
+ CG_Text_Paint( tx, ty, scale, color, textBuffer, 0, 0, textStyle );
+ }
+}
+
+//=============== TA: was cg_newdraw.c
+
+#define NO_CREDITS_TIME 2000
+
+static void CG_DrawPlayerCreditsValue( rectDef_t *rect, vec4_t color, qboolean padding )
+{
+ int value;
+ playerState_t *ps;
+ centity_t *cent;
+
+ cent = &cg_entities[ cg.snap->ps.clientNum ];
+ ps = &cg.snap->ps;
+
+ //if the build timer pie is showing don't show this
+ if( ( cent->currentState.weapon == WP_ABUILD ||
+ cent->currentState.weapon == WP_ABUILD2 ) && ps->stats[ STAT_MISC ] )
+ return;
+
+ value = ps->persistant[ PERS_CREDIT ];
+ if( value > -1 )
+ {
+ if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS &&
+ !CG_AtHighestClass( ) )
+ {
+ if( cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME )
+ {
+ if( ( ( cg.time - cg.lastEvolveAttempt ) / 300 ) % 2 )
+ color[ 3 ] = 0.0f;
+ }
+ }
+
+ trap_R_SetColor( color );
+
+ if( padding )
+ CG_DrawFieldPadded( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
+ else
+ CG_DrawField( rect->x, rect->y, 1, rect->w, rect->h, value );
+
+ trap_R_SetColor( NULL );
+ }
+}
+
+static void CG_DrawPlayerBankValue( rectDef_t *rect, vec4_t color, qboolean padding )
+{
+ int value;
+ playerState_t *ps;
+
+ ps = &cg.snap->ps;
+
+ value = ps->persistant[ PERS_BANK ];
+ if( value > -1 )
+ {
+ trap_R_SetColor( color );
+
+ if( padding )
+ CG_DrawFieldPadded( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
+ else
+ CG_DrawField( rect->x, rect->y, 1, rect->w, rect->h, value );
+
+ trap_R_SetColor( NULL );
+ }
+}
+
+#define HH_MIN_ALPHA 0.2f
+#define HH_MAX_ALPHA 0.8f
+#define HH_ALPHA_DIFF (HH_MAX_ALPHA-HH_MIN_ALPHA)
+
+#define AH_MIN_ALPHA 0.2f
+#define AH_MAX_ALPHA 0.8f
+#define AH_ALPHA_DIFF (AH_MAX_ALPHA-AH_MIN_ALPHA)
+
+/*
+==============
+CG_DrawPlayerStamina1
+==============
+*/
+static void CG_DrawPlayerStamina1( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ float stamina = ps->stats[ STAT_STAMINA ];
+ float maxStaminaBy3 = (float)MAX_STAMINA / 3.0f;
+ float progress;
+
+ stamina -= ( 2 * (int)maxStaminaBy3 );
+ progress = stamina / maxStaminaBy3;
+
+ if( progress > 1.0f )
+ progress = 1.0f;
+ else if( progress < 0.0f )
+ progress = 0.0f;
+
+ color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+/*
+==============
+CG_DrawPlayerStamina2
+==============
+*/
+static void CG_DrawPlayerStamina2( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ float stamina = ps->stats[ STAT_STAMINA ];
+ float maxStaminaBy3 = (float)MAX_STAMINA / 3.0f;
+ float progress;
+
+ stamina -= (int)maxStaminaBy3;
+ progress = stamina / maxStaminaBy3;
+
+ if( progress > 1.0f )
+ progress = 1.0f;
+ else if( progress < 0.0f )
+ progress = 0.0f;
+
+ color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+/*
+==============
+CG_DrawPlayerStamina3
+==============
+*/
+static void CG_DrawPlayerStamina3( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ float stamina = ps->stats[ STAT_STAMINA ];
+ float maxStaminaBy3 = (float)MAX_STAMINA / 3.0f;
+ float progress;
+
+ progress = stamina / maxStaminaBy3;
+
+ if( progress > 1.0f )
+ progress = 1.0f;
+ else if( progress < 0.0f )
+ progress = 0.0f;
+
+ color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+/*
+==============
+CG_DrawPlayerStamina4
+==============
+*/
+static void CG_DrawPlayerStamina4( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ float stamina = ps->stats[ STAT_STAMINA ];
+ float progress;
+
+ stamina += (float)MAX_STAMINA;
+ progress = stamina / (float)MAX_STAMINA;
+
+ if( progress > 1.0f )
+ progress = 1.0f;
+ else if( progress < 0.0f )
+ progress = 0.0f;
+
+ color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+/*
+==============
+CG_DrawPlayerStaminaBolt
+==============
+*/
+static void CG_DrawPlayerStaminaBolt( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ float stamina = ps->stats[ STAT_STAMINA ];
+
+ if( stamina < 0 )
+ color[ 3 ] = HH_MIN_ALPHA;
+ else
+ color[ 3 ] = HH_MAX_ALPHA;
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+/*
+==============
+CG_DrawPlayerClipsRing
+==============
+*/
+static void CG_DrawPlayerClipsRing( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ centity_t *cent;
+ float buildTime = ps->stats[ STAT_MISC ];
+ float progress;
+ float maxDelay;
+
+ cent = &cg_entities[ cg.snap->ps.clientNum ];
+
+ switch( cent->currentState.weapon )
+ {
+ case WP_ABUILD:
+ case WP_ABUILD2:
+ case WP_HBUILD:
+ case WP_HBUILD2:
+ maxDelay = (float)BG_FindBuildDelayForWeapon( cent->currentState.weapon );
+
+ if( buildTime > maxDelay )
+ buildTime = maxDelay;
+
+ progress = ( maxDelay - buildTime ) / maxDelay;
+
+ color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
+ break;
+
+ default:
+ if( ps->weaponstate == WEAPON_RELOADING )
+ {
+ maxDelay = (float)BG_FindReloadTimeForWeapon( cent->currentState.weapon );
+ progress = ( maxDelay - (float)ps->weaponTime ) / maxDelay;
+
+ color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
+ }
+ break;
+ }
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+/*
+==============
+CG_DrawPlayerBuildTimerRing
+==============
+*/
+static void CG_DrawPlayerBuildTimerRing( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ centity_t *cent;
+ float buildTime = ps->stats[ STAT_MISC ];
+ float progress;
+ float maxDelay;
+
+ cent = &cg_entities[ cg.snap->ps.clientNum ];
+
+ maxDelay = (float)BG_FindBuildDelayForWeapon( cent->currentState.weapon );
+
+ if( buildTime > maxDelay )
+ buildTime = maxDelay;
+
+ progress = ( maxDelay - buildTime ) / maxDelay;
+
+ color[ 3 ] = AH_MIN_ALPHA + ( progress * AH_ALPHA_DIFF );
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+/*
+==============
+CG_DrawPlayerBoosted
+==============
+*/
+static void CG_DrawPlayerBoosted( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ qboolean boosted = ps->stats[ STAT_STATE ] & SS_BOOSTED;
+
+ if( boosted )
+ color[ 3 ] = AH_MAX_ALPHA;
+ else
+ color[ 3 ] = AH_MIN_ALPHA;
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+/*
+==============
+CG_DrawPlayerBoosterBolt
+==============
+*/
+static void CG_DrawPlayerBoosterBolt( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ qboolean boosted = ps->stats[ STAT_STATE ] & SS_BOOSTED;
+ vec4_t localColor;
+
+ Vector4Copy( color, localColor );
+
+ if( boosted )
+ {
+ if( ps->stats[ STAT_BOOSTTIME ] > BOOST_TIME - 3000 )
+ {
+ qboolean flash = ( ps->stats[ STAT_BOOSTTIME ] / 500 ) % 2;
+
+ if( flash )
+ localColor[ 3 ] = 1.0f;
+ }
+ }
+
+ trap_R_SetColor( localColor );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+/*
+==============
+CG_DrawPlayerPoisonBarbs
+==============
+*/
+static void CG_DrawPlayerPoisonBarbs( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ int x = rect->x;
+ int y = rect->y;
+ int width = rect->w;
+ int height = rect->h;
+ qboolean vertical;
+ int iconsize, numBarbs, i;
+
+ BG_UnpackAmmoArray( ps->weapon, ps->ammo, ps->powerups, &numBarbs, NULL );
+
+ if( height > width )
+ {
+ vertical = qtrue;
+ iconsize = width;
+ }
+ else if( height <= width )
+ {
+ vertical = qfalse;
+ iconsize = height;
+ }
+
+ if( color[ 3 ] != 0.0 )
+ trap_R_SetColor( color );
+
+ for( i = 0; i < numBarbs; i ++ )
+ {
+ if( vertical )
+ y += iconsize;
+ else
+ x += iconsize;
+
+ CG_DrawPic( x, y, iconsize, iconsize, shader );
+ }
+
+ trap_R_SetColor( NULL );
+}
+
+/*
+==============
+CG_DrawPlayerWallclimbing
+==============
+*/
+static void CG_DrawPlayerWallclimbing( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ qboolean ww = ps->stats[ STAT_STATE ] & SS_WALLCLIMBING;
+
+ if( ww )
+ color[ 3 ] = AH_MAX_ALPHA;
+ else
+ color[ 3 ] = AH_MIN_ALPHA;
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+static void CG_DrawPlayerStamina( rectDef_t *rect, vec4_t color, float scale,
+ int align, int textStyle, int special )
+{
+ playerState_t *ps = &cg.snap->ps;
+ int stamina = ps->stats[ STAT_STAMINA ];
+ float progress = ( (float)stamina + (float)MAX_STAMINA ) / ( (float)MAX_STAMINA * 2.0f );
+
+ CG_DrawProgressBar( rect, color, scale, align, textStyle, special, progress );
+}
+
+static void CG_DrawPlayerAmmoValue( rectDef_t *rect, vec4_t color )
+{
+ int value;
+ centity_t *cent;
+ playerState_t *ps;
+
+ cent = &cg_entities[ cg.snap->ps.clientNum ];
+ ps = &cg.snap->ps;
+
+ if( cent->currentState.weapon )
+ {
+ switch( cent->currentState.weapon )
+ {
+ case WP_ABUILD:
+ case WP_ABUILD2:
+ //percentage of BP remaining
+ value = cgs.alienBuildPoints;
+ break;
+
+ case WP_HBUILD:
+ case WP_HBUILD2:
+ //percentage of BP remaining
+ value = cgs.humanBuildPoints;
+ break;
+
+ default:
+ BG_UnpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups, &value, NULL );
+ break;
+ }
+
+ if( value > 999 )
+ value = 999;
+
+ if( value > -1 )
+ {
+ trap_R_SetColor( color );
+ CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
+ trap_R_SetColor( NULL );
+ }
+ }
+}
+
+
+/*
+==============
+CG_DrawAlienSense
+==============
+*/
+static void CG_DrawAlienSense( rectDef_t *rect )
+{
+ if( BG_ClassHasAbility( cg.snap->ps.stats[ STAT_PCLASS ], SCA_ALIENSENSE ) )
+ CG_AlienSense( rect );
+}
+
+
+/*
+==============
+CG_DrawHumanScanner
+==============
+*/
+static void CG_DrawHumanScanner( rectDef_t *rect, qhandle_t shader, vec4_t color )
+{
+ if( BG_InventoryContainsUpgrade( UP_HELMET, cg.snap->ps.stats ) )
+ CG_Scanner( rect, shader, color );
+}
+
+
+/*
+==============
+CG_DrawUsableBuildable
+==============
+*/
+static void CG_DrawUsableBuildable( rectDef_t *rect, qhandle_t shader, vec4_t color )
+{
+ vec3_t view, point;
+ trace_t trace;
+ entityState_t *es;
+
+ AngleVectors( cg.refdefViewAngles, view, NULL, NULL );
+ VectorMA( cg.refdef.vieworg, 64, view, point );
+ CG_Trace( &trace, cg.refdef.vieworg, NULL, NULL,
+ point, cg.predictedPlayerState.clientNum, MASK_SHOT );
+
+ es = &cg_entities[ trace.entityNum ].currentState;
+
+ if( es->eType == ET_BUILDABLE && BG_FindUsableForBuildable( es->modelindex ) &&
+ cg.predictedPlayerState.stats[ STAT_PTEAM ] == BG_FindTeamForBuildable( es->modelindex ) )
+ {
+ //hack to prevent showing the usable buildable when you aren't carrying an energy weapon
+ if( ( es->modelindex == BA_H_REACTOR || es->modelindex == BA_H_REPEATER ) &&
+ ( !BG_FindUsesEnergyForWeapon( cg.snap->ps.weapon ) ||
+ BG_FindInfinteAmmoForWeapon( cg.snap->ps.weapon ) ) )
+ return;
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+ }
+}
+
+
+#define BUILD_DELAY_TIME 2000
+
+static void CG_DrawPlayerBuildTimer( rectDef_t *rect, vec4_t color )
+{
+ float progress;
+ int index;
+ centity_t *cent;
+ playerState_t *ps;
+
+ cent = &cg_entities[ cg.snap->ps.clientNum ];
+ ps = &cg.snap->ps;
+
+ if( cent->currentState.weapon )
+ {
+ switch( cent->currentState.weapon )
+ {
+ case WP_ABUILD:
+ progress = (float)ps->stats[ STAT_MISC ] / (float)ABUILDER_BASE_DELAY;
+ break;
+
+ case WP_ABUILD2:
+ progress = (float)ps->stats[ STAT_MISC ] / (float)ABUILDER_ADV_DELAY;
+ break;
+
+ case WP_HBUILD:
+ progress = (float)ps->stats[ STAT_MISC ] / (float)HBUILD_DELAY;
+ break;
+
+ case WP_HBUILD2:
+ progress = (float)ps->stats[ STAT_MISC ] / (float)HBUILD2_DELAY;
+ break;
+
+ default:
+ return;
+ break;
+ }
+
+ if( !ps->stats[ STAT_MISC ] )
+ return;
+
+ index = (int)( progress * 8.0f );
+
+ if( index > 7 )
+ index = 7;
+ else if( index < 0 )
+ index = 0;
+
+ if( cg.time - cg.lastBuildAttempt <= BUILD_DELAY_TIME )
+ {
+ if( ( ( cg.time - cg.lastBuildAttempt ) / 300 ) % 2 )
+ {
+ color[ 0 ] = 1.0f;
+ color[ 1 ] = color[ 2 ] = 0.0f;
+ color[ 3 ] = 1.0f;
+ }
+ }
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h,
+ cgs.media.buildWeaponTimerPie[ index ] );
+ trap_R_SetColor( NULL );
+ }
+}
+
+static void CG_DrawPlayerClipsValue( rectDef_t *rect, vec4_t color )
+{
+ int value;
+ centity_t *cent;
+ playerState_t *ps;
+
+ cent = &cg_entities[ cg.snap->ps.clientNum ];
+ ps = &cg.snap->ps;
+
+ if( cent->currentState.weapon )
+ {
+ switch( cent->currentState.weapon )
+ {
+ case WP_ABUILD:
+ case WP_ABUILD2:
+ case WP_HBUILD:
+ case WP_HBUILD2:
+ break;
+
+ default:
+ BG_UnpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups, NULL, &value );
+
+ if( value > -1 )
+ {
+ trap_R_SetColor( color );
+ CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
+ trap_R_SetColor( NULL );
+ }
+ break;
+ }
+ }
+}
+
+static void CG_DrawPlayerHealthValue( rectDef_t *rect, vec4_t color )
+{
+ playerState_t *ps;
+ int value;
+
+ ps = &cg.snap->ps;
+
+ value = ps->stats[ STAT_HEALTH ];
+
+ trap_R_SetColor( color );
+ CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
+ trap_R_SetColor( NULL );
+}
+
+static void CG_DrawPlayerHealthBar( rectDef_t *rect, vec4_t color, float scale,
+ int align, int textStyle, int special )
+{
+ playerState_t *ps;
+ float total;
+
+ ps = &cg.snap->ps;
+
+ total = ( (float)ps->stats[ STAT_HEALTH ] / (float)ps->stats[ STAT_MAX_HEALTH ] );
+ CG_DrawProgressBar( rect, color, scale, align, textStyle, special, total );
+}
+
+/*
+==============
+CG_DrawPlayerHealthCross
+==============
+*/
+static void CG_DrawPlayerHealthCross( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ playerState_t *ps = &cg.snap->ps;
+ int health = ps->stats[ STAT_HEALTH ];
+
+ if( health < 10 )
+ {
+ color[ 0 ] = 1.0f;
+ color[ 1 ] = color[ 2 ] = 0.0f;
+ }
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+static void CG_DrawProgressLabel( rectDef_t *rect, float text_x, float text_y, vec4_t color,
+ float scale, int align, const char *s, float fraction )
+{
+ vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
+ float tx, tw = CG_Text_Width( s, scale, 0 );
+
+ switch( align )
+ {
+ case ITEM_ALIGN_LEFT:
+ tx = 0.0f;
+ break;
+
+ case ITEM_ALIGN_RIGHT:
+ tx = rect->w - tw;
+ break;
+
+ case ITEM_ALIGN_CENTER:
+ tx = ( rect->w / 2.0f ) - ( tw / 2.0f );
+ break;
+
+ default:
+ tx = 0.0f;
+ }
+
+ if( fraction < 1.0f )
+ CG_Text_Paint( rect->x + text_x + tx, rect->y + text_y, scale, white,
+ s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
+ else
+ CG_Text_Paint( rect->x + text_x + tx, rect->y + text_y, scale, color,
+ s, 0, 0, ITEM_TEXTSTYLE_NEON );
+}
+
+static void CG_DrawMediaProgress( rectDef_t *rect, vec4_t color, float scale,
+ int align, int textStyle, int special )
+{
+ CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.mediaFraction );
+}
+
+static void CG_DrawMediaProgressLabel( rectDef_t *rect, float text_x, float text_y,
+ vec4_t color, float scale, int align )
+{
+ CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Map and Textures", cg.mediaFraction );
+}
+
+static void CG_DrawBuildablesProgress( rectDef_t *rect, vec4_t color, float scale,
+ int align, int textStyle, int special )
+{
+ CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.buildablesFraction );
+}
+
+static void CG_DrawBuildablesProgressLabel( rectDef_t *rect, float text_x, float text_y,
+ vec4_t color, float scale, int align )
+{
+ CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Buildable Models", cg.buildablesFraction );
+}
+
+static void CG_DrawCharModelProgress( rectDef_t *rect, vec4_t color, float scale,
+ int align, int textStyle, int special )
+{
+ CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.charModelFraction );
+}
+
+static void CG_DrawCharModelProgressLabel( rectDef_t *rect, float text_x, float text_y,
+ vec4_t color, float scale, int align )
+{
+ CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Character Models", cg.charModelFraction );
+}
+
+static void CG_DrawOverallProgress( rectDef_t *rect, vec4_t color, float scale,
+ int align, int textStyle, int special )
+{
+ float total;
+
+ total = ( cg.charModelFraction + cg.buildablesFraction + cg.mediaFraction ) / 3.0f;
+ CG_DrawProgressBar( rect, color, scale, align, textStyle, special, total );
+}
+
+static void CG_DrawLevelShot( rectDef_t *rect )
+{
+ const char *s;
+ const char *info;
+ qhandle_t levelshot;
+ qhandle_t detail;
+
+ info = CG_ConfigString( CS_SERVERINFO );
+ s = Info_ValueForKey( info, "mapname" );
+ levelshot = trap_R_RegisterShaderNoMip( va( "levelshots/%s.tga", s ) );
+
+ if( !levelshot )
+ levelshot = trap_R_RegisterShaderNoMip( "gfx/2d/load_screen" );
+
+ trap_R_SetColor( NULL );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, levelshot );
+
+ // blend a detail texture over it
+ detail = trap_R_RegisterShader( "gfx/misc/detail" );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, detail );
+}
+
+static void CG_DrawLoadingString( rectDef_t *rect, float text_x, float text_y, vec4_t color,
+ float scale, int align, int textStyle, const char *s )
+{
+ float tw, th, tx;
+ int pos, i;
+ char buffer[ 1024 ];
+ char *end;
+
+ if( !s[ 0 ] )
+ return;
+
+ strcpy( buffer, s );
+ tw = CG_Text_Width( s, scale, 0 );
+ th = scale * 40.0f;
+
+ pos = i = 0;
+
+ while( pos < strlen( s ) )
+ {
+ strcpy( buffer, &s[ pos ] );
+ tw = CG_Text_Width( buffer, scale, 0 );
+
+ while( tw > rect->w )
+ {
+ end = strrchr( buffer, ' ' );
+
+ if( end == NULL )
+ break;
+
+ *end = '\0';
+ tw = CG_Text_Width( buffer, scale, 0 );
+ }
+
+ switch( align )
+ {
+ case ITEM_ALIGN_LEFT:
+ tx = rect->x;
+ break;
+
+ case ITEM_ALIGN_RIGHT:
+ tx = rect->x + rect->w - tw;
+ break;
+
+ case ITEM_ALIGN_CENTER:
+ tx = rect->x + ( rect->w / 2.0f ) - ( tw / 2.0f );
+ break;
+
+ default:
+ tx = 0.0f;
+ }
+
+ CG_Text_Paint( tx + text_x, rect->y + text_y + i * ( th + 3 ), scale, color,
+ buffer, 0, 0, textStyle );
+
+ pos += strlen( buffer ) + 1;
+ i++;
+ }
+}
+
+static void CG_DrawLevelName( rectDef_t *rect, float text_x, float text_y,
+ vec4_t color, float scale, int align, int textStyle )
+{
+ const char *s;
+
+ s = CG_ConfigString( CS_MESSAGE );
+
+ CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, s );
+}
+
+static void CG_DrawMOTD( rectDef_t *rect, float text_x, float text_y,
+ vec4_t color, float scale, int align, int textStyle )
+{
+ const char *s;
+
+ s = CG_ConfigString( CS_MOTD );
+
+ CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, s );
+}
+
+static void CG_DrawHostname( rectDef_t *rect, float text_x, float text_y,
+ vec4_t color, float scale, int align, int textStyle )
+{
+ char buffer[ 1024 ];
+ const char *info;
+
+ info = CG_ConfigString( CS_SERVERINFO );
+
+ Q_strncpyz( buffer, Info_ValueForKey( info, "sv_hostname" ), 1024 );
+ Q_CleanStr( buffer );
+
+ CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, buffer );
+}
+
+/*
+==============
+CG_DrawDemoPlayback
+==============
+*/
+static void CG_DrawDemoPlayback( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ if( !cg_drawDemoState.integer )
+ return;
+
+ if( trap_GetDemoState( ) != DS_PLAYBACK )
+ return;
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+/*
+==============
+CG_DrawDemoRecording
+==============
+*/
+static void CG_DrawDemoRecording( rectDef_t *rect, vec4_t color, qhandle_t shader )
+{
+ if( !cg_drawDemoState.integer )
+ return;
+
+ if( trap_GetDemoState( ) != DS_RECORDING )
+ return;
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+/*
+======================
+CG_UpdateMediaFraction
+
+======================
+*/
+void CG_UpdateMediaFraction( float newFract )
+{
+ cg.mediaFraction = newFract;
+
+ trap_UpdateScreen( );
+}
+
+/*
+====================
+CG_DrawLoadingScreen
+
+Draw all the status / pacifier stuff during level loading
+====================
+*/
+void CG_DrawLoadingScreen( void )
+{
+ Menu_Paint( Menus_FindByName( "Loading" ), qtrue );
+}
+
+float CG_GetValue( int ownerDraw )
+{
+ centity_t *cent;
+ playerState_t *ps;
+
+ cent = &cg_entities[ cg.snap->ps.clientNum ];
+ ps = &cg.snap->ps;
+
+ switch( ownerDraw )
+ {
+ case CG_PLAYER_AMMO_VALUE:
+ if( cent->currentState.weapon )
+ {
+ int value;
+
+ BG_UnpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups,
+ &value, NULL );
+
+ return value;
+ }
+ break;
+ case CG_PLAYER_CLIPS_VALUE:
+ if( cent->currentState.weapon )
+ {
+ int value;
+
+ BG_UnpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups,
+ NULL, &value );
+
+ return value;
+ }
+ break;
+ case CG_PLAYER_HEALTH:
+ return ps->stats[ STAT_HEALTH ];
+ break;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+const char *CG_GetKillerText( )
+{
+ const char *s = "";
+ if( cg.killerName[ 0 ] )
+ s = va( "Fragged by %s", cg.killerName );
+
+ return s;
+}
+
+
+static void CG_DrawKiller( rectDef_t *rect, float scale, vec4_t color,
+ qhandle_t shader, int textStyle )
+{
+ // fragged by ... line
+ if( cg.killerName[ 0 ] )
+ {
+ int x = rect->x + rect->w / 2;
+ CG_Text_Paint( x - CG_Text_Width( CG_GetKillerText( ), scale, 0 ) / 2,
+ rect->y + rect->h, scale, color, CG_GetKillerText( ), 0, 0, textStyle );
+ }
+}
+
+
+static void CG_Text_Paint_Limit( float *maxX, float x, float y, float scale,
+ vec4_t color, const char* text, float adjust, int limit )
+{
+ int len, count;
+ vec4_t newColor;
+ glyphInfo_t *glyph;
+
+ if( text )
+ {
+// TTimo: FIXME
+// const unsigned char *s = text; // bk001206 - unsigned
+ const char *s = text;
+ float max = *maxX;
+ float useScale;
+ fontInfo_t *font = &cgDC.Assets.textFont;
+
+ if( scale <= cg_smallFont.value )
+ font = &cgDC.Assets.smallFont;
+ else if( scale > cg_bigFont.value )
+ font = &cgDC.Assets.bigFont;
+
+ useScale = scale * font->glyphScale;
+ trap_R_SetColor( color );
+ len = strlen( text );
+
+ if( limit > 0 && len > limit )
+ len = limit;
+
+ count = 0;
+
+ while( s && *s && count < len )
+ {
+ glyph = &font->glyphs[ (int)*s ];
+ //TTimo: FIXME: getting nasty warnings without the cast,
+ //hopefully this doesn't break the VM build
+
+ if( Q_IsColorString( s ) )
+ {
+ memcpy( newColor, g_color_table[ ColorIndex( *(s+1) ) ], sizeof( newColor ) );
+ newColor[ 3 ] = color[ 3 ];
+ trap_R_SetColor( newColor );
+ s += 2;
+ continue;
+ }
+ else
+ {
+ float yadj = useScale * glyph->top;
+
+ if( CG_Text_Width( s, useScale, 1 ) + x > max )
+ {
+ *maxX = 0;
+ break;
+ }
+
+ CG_Text_PaintChar( x, y - yadj,
+ glyph->imageWidth,
+ glyph->imageHeight,
+ useScale,
+ glyph->s,
+ glyph->t,
+ glyph->s2,
+ glyph->t2,
+ glyph->glyph );
+ x += ( glyph->xSkip * useScale ) + adjust;
+ *maxX = x;
+ count++;
+ s++;
+ }
+ }
+
+ trap_R_SetColor( NULL );
+ }
+}
+
+static void CG_DrawTeamSpectators( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader )
+{
+ if( cg.spectatorLen )
+ {
+ float maxX;
+
+ if( cg.spectatorWidth == -1 )
+ {
+ cg.spectatorWidth = 0;
+ cg.spectatorPaintX = rect->x + 1;
+ cg.spectatorPaintX2 = -1;
+ }
+
+ if( cg.spectatorOffset > cg.spectatorLen )
+ {
+ cg.spectatorOffset = 0;
+ cg.spectatorPaintX = rect->x + 1;
+ cg.spectatorPaintX2 = -1;
+ }
+
+ if( cg.time > cg.spectatorTime )
+ {
+ cg.spectatorTime = cg.time + 10;
+
+ if( cg.spectatorPaintX <= rect->x + 2 )
+ {
+ if( cg.spectatorOffset < cg.spectatorLen )
+ {
+ //TA: skip colour directives
+ if( Q_IsColorString( &cg.spectatorList[ cg.spectatorOffset ] ) )
+ cg.spectatorOffset += 2;
+ else
+ {
+ cg.spectatorPaintX += CG_Text_Width( &cg.spectatorList[ cg.spectatorOffset ], scale, 1 ) - 1;
+ cg.spectatorOffset++;
+ }
+ }
+ else
+ {
+ cg.spectatorOffset = 0;
+
+ if( cg.spectatorPaintX2 >= 0 )
+ cg.spectatorPaintX = cg.spectatorPaintX2;
+ else
+ cg.spectatorPaintX = rect->x + rect->w - 2;
+
+ cg.spectatorPaintX2 = -1;
+ }
+ }
+ else
+ {
+ cg.spectatorPaintX--;
+
+ if( cg.spectatorPaintX2 >= 0 )
+ cg.spectatorPaintX2--;
+ }
+ }
+
+ maxX = rect->x + rect->w - 2;
+
+ CG_Text_Paint_Limit( &maxX, cg.spectatorPaintX, rect->y + rect->h - 3, scale, color,
+ &cg.spectatorList[ cg.spectatorOffset ], 0, 0 );
+
+ if( cg.spectatorPaintX2 >= 0 )
+ {
+ float maxX2 = rect->x + rect->w - 2;
+ CG_Text_Paint_Limit( &maxX2, cg.spectatorPaintX2, rect->y + rect->h - 3, scale,
+ color, cg.spectatorList, 0, cg.spectatorOffset );
+ }
+
+ if( cg.spectatorOffset && maxX > 0 )
+ {
+ // if we have an offset ( we are skipping the first part of the string ) and we fit the string
+ if( cg.spectatorPaintX2 == -1 )
+ cg.spectatorPaintX2 = rect->x + rect->w - 2;
+ }
+ else
+ cg.spectatorPaintX2 = -1;
+ }
+}
+
+/*
+==================
+CG_DrawStageReport
+==================
+*/
+static void CG_DrawStageReport( rectDef_t *rect, float text_x, float text_y,
+ vec4_t color, float scale, int align, int textStyle )
+{
+ char s[ MAX_TOKEN_CHARS ];
+ int tx, w, kills;
+
+ if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_NONE && !cg.intermissionStarted )
+ return;
+
+ if( cg.intermissionStarted )
+ {
+ Com_sprintf( s, MAX_TOKEN_CHARS,
+ "Stage %d" //PH34R MY MAD-LEET CODING SKILLZ
+ " "
+ "Stage %d",
+ cgs.alienStage + 1, cgs.humanStage + 1 );
+ }
+ else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ {
+ kills = cgs.alienNextStageThreshold - cgs.alienKills;
+
+ if( cgs.alienNextStageThreshold < 0 )
+ Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d", cgs.alienStage + 1 );
+ else if( kills == 1 )
+ Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kill for next stage",
+ cgs.alienStage + 1, kills );
+ else
+ Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kills for next stage",
+ cgs.alienStage + 1, kills );
+ }
+ else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ {
+ kills = cgs.humanNextStageThreshold - cgs.humanKills;
+
+ if( cgs.humanNextStageThreshold < 0 )
+ Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d", cgs.humanStage + 1 );
+ else if( kills == 1 )
+ Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kill for next stage",
+ cgs.humanStage + 1, kills );
+ else
+ Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kills for next stage",
+ cgs.humanStage + 1, kills );
+ }
+
+ w = CG_Text_Width( s, scale, 0 );
+
+ switch( align )
+ {
+ case ITEM_ALIGN_LEFT:
+ tx = rect->x;
+ break;
+
+ case ITEM_ALIGN_RIGHT:
+ tx = rect->x + rect->w - w;
+ break;
+
+ case ITEM_ALIGN_CENTER:
+ tx = rect->x + ( rect->w / 2.0f ) - ( w / 2.0f );
+ break;
+
+ default:
+ tx = 0.0f;
+ }
+
+ CG_Text_Paint( text_x + tx, rect->y + text_y, scale, color, s, 0, 0, textStyle );
+}
+
+/*
+==================
+CG_DrawFPS
+==================
+*/
+//TA: personally i think this should be longer - it should really be a cvar
+#define FPS_FRAMES 20
+#define FPS_STRING "fps"
+static void CG_DrawFPS( rectDef_t *rect, float text_x, float text_y,
+ float scale, vec4_t color, int align, int textStyle,
+ qboolean scalableText )
+{
+ char *s;
+ int tx, w, totalWidth, strLength;
+ static int previousTimes[ FPS_FRAMES ];
+ static int index;
+ int i, total;
+ int fps;
+ static int previous;
+ int t, frameTime;
+
+ if( !cg_drawFPS.integer )
+ return;
+
+ // 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;
+
+ previousTimes[ index % FPS_FRAMES ] = frameTime;
+ 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;
+
+ s = va( "%d", fps );
+ w = CG_Text_Width( "0", scale, 0 );
+ strLength = CG_DrawStrlen( s );
+ totalWidth = CG_Text_Width( FPS_STRING, scale, 0 ) + w * strLength;
+
+ switch( align )
+ {
+ case ITEM_ALIGN_LEFT:
+ tx = rect->x;
+ break;
+
+ case ITEM_ALIGN_RIGHT:
+ tx = rect->x + rect->w - totalWidth;
+ break;
+
+ case ITEM_ALIGN_CENTER:
+ tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f );
+ break;
+
+ default:
+ tx = 0.0f;
+ }
+
+ if( scalableText )
+ {
+ for( i = 0; i < strLength; i++ )
+ {
+ char c[ 2 ];
+
+ c[ 0 ] = s[ i ];
+ c[ 1 ] = '\0';
+
+ CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle );
+ }
+ }
+ else
+ {
+ trap_R_SetColor( color );
+ CG_DrawField( rect->x, rect->y, 3, rect->w / 3, rect->h, fps );
+ trap_R_SetColor( NULL );
+ }
+
+ if( scalableText )
+ CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, FPS_STRING, 0, 0, textStyle );
+ }
+}
+
+
+/*
+=================
+CG_DrawTimerMins
+=================
+*/
+static void CG_DrawTimerMins( rectDef_t *rect, vec4_t color )
+{
+ int mins, seconds;
+ int msec;
+
+ if( !cg_drawTimer.integer )
+ return;
+
+ msec = cg.time - cgs.levelStartTime;
+
+ seconds = msec / 1000;
+ mins = seconds / 60;
+ seconds -= mins * 60;
+
+ trap_R_SetColor( color );
+ CG_DrawField( rect->x, rect->y, 3, rect->w / 3, rect->h, mins );
+ trap_R_SetColor( NULL );
+}
+
+
+/*
+=================
+CG_DrawTimerSecs
+=================
+*/
+static void CG_DrawTimerSecs( rectDef_t *rect, vec4_t color )
+{
+ int mins, seconds;
+ int msec;
+
+ if( !cg_drawTimer.integer )
+ return;
+
+ msec = cg.time - cgs.levelStartTime;
+
+ seconds = msec / 1000;
+ mins = seconds / 60;
+ seconds -= mins * 60;
+
+ trap_R_SetColor( color );
+ CG_DrawFieldPadded( rect->x, rect->y, 2, rect->w / 2, rect->h, seconds );
+ trap_R_SetColor( NULL );
+}
+
+
+/*
+=================
+CG_DrawTimer
+=================
+*/
+static void CG_DrawTimer( rectDef_t *rect, float text_x, float text_y,
+ float scale, vec4_t color, int align, int textStyle )
+{
+ char *s;
+ int i, tx, w, totalWidth, strLength;
+ int mins, seconds, tens;
+ int msec;
+
+ if( !cg_drawTimer.integer )
+ return;
+
+ msec = cg.time - cgs.levelStartTime;
+
+ seconds = msec / 1000;
+ mins = seconds / 60;
+ seconds -= mins * 60;
+ tens = seconds / 10;
+ seconds -= tens * 10;
+
+ s = va( "%d:%d%d", mins, tens, seconds );
+ w = CG_Text_Width( "0", scale, 0 );
+ strLength = CG_DrawStrlen( s );
+ totalWidth = w * strLength;
+
+ switch( align )
+ {
+ case ITEM_ALIGN_LEFT:
+ tx = rect->x;
+ break;
+
+ case ITEM_ALIGN_RIGHT:
+ tx = rect->x + rect->w - totalWidth;
+ break;
+
+ case ITEM_ALIGN_CENTER:
+ tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f );
+ break;
+
+ default:
+ tx = 0.0f;
+ }
+
+ for( i = 0; i < strLength; i++ )
+ {
+ char c[ 2 ];
+
+ c[ 0 ] = s[ i ];
+ c[ 1 ] = '\0';
+
+ CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle );
+ }
+}
+
+/*
+=================
+CG_DrawClock
+=================
+*/
+static void CG_DrawClock( rectDef_t *rect, float text_x, float text_y,
+ float scale, vec4_t color, int align, int textStyle )
+{
+ char *s;
+ int i, tx, w, totalWidth, strLength;
+ qtime_t qt;
+ int t;
+
+ if( !cg_drawClock.integer )
+ return;
+
+ t = trap_RealTime( &qt );
+
+ if( cg_drawClock.integer == 2 )
+ {
+ s = va( "%02d%s%02d", qt.tm_hour, ( qt.tm_sec % 2 ) ? ":" : " ",
+ qt.tm_min );
+ }
+ else
+ {
+ char *pm = "am";
+ int h = qt.tm_hour;
+
+ if( h == 0 )
+ h = 12;
+ else if( h == 12 )
+ pm = "pm";
+ else if( h > 12 )
+ {
+ h -= 12;
+ pm = "pm";
+ }
+
+ s = va( "%d%s%02d%s", h, ( qt.tm_sec % 2 ) ? ":" : " ", qt.tm_min, pm );
+ }
+ w = CG_Text_Width( "0", scale, 0 );
+ strLength = CG_DrawStrlen( s );
+ totalWidth = w * strLength;
+
+ switch( align )
+ {
+ case ITEM_ALIGN_LEFT:
+ tx = rect->x;
+ break;
+
+ case ITEM_ALIGN_RIGHT:
+ tx = rect->x + rect->w - totalWidth;
+ break;
+
+ case ITEM_ALIGN_CENTER:
+ tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f );
+ break;
+
+ default:
+ tx = 0.0f;
+ }
+
+ for( i = 0; i < strLength; i++ )
+ {
+ char c[ 2 ];
+
+ c[ 0 ] = s[ i ];
+ c[ 1 ] = '\0';
+
+ CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle );
+ }
+}
+
+/*
+==================
+CG_DrawSnapshot
+==================
+*/
+static void CG_DrawSnapshot( rectDef_t *rect, float text_x, float text_y,
+ float scale, vec4_t color, int align, int textStyle )
+{
+ char *s;
+ int w, tx;
+
+ if( !cg_drawSnapshot.integer )
+ return;
+
+ s = va( "time:%d snap:%d cmd:%d", cg.snap->serverTime,
+ cg.latestSnapshotNum, cgs.serverCommandSequence );
+ w = CG_Text_Width( s, scale, 0 );
+
+ switch( align )
+ {
+ case ITEM_ALIGN_LEFT:
+ tx = rect->x;
+ break;
+
+ case ITEM_ALIGN_RIGHT:
+ tx = rect->x + rect->w - w;
+ break;
+
+ case ITEM_ALIGN_CENTER:
+ tx = rect->x + ( rect->w / 2.0f ) - ( w / 2.0f );
+ break;
+
+ default:
+ tx = 0.0f;
+ }
+
+ CG_Text_Paint( text_x + tx, rect->y + text_y, scale, color, s, 0, 0, textStyle );
+}
+
+/*
+===============================================================================
+
+LAGOMETER
+
+===============================================================================
+*/
+
+#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_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.
+==============
+*/
+#define PING_FRAMES 40
+void CG_AddLagometerSnapshotInfo( snapshot_t *snap )
+{
+ static int previousPings[ PING_FRAMES ];
+ static int index;
+ int i;
+
+ // 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.ping = 0;
+ if( cg.snap )
+ {
+ previousPings[ index++ ] = cg.snap->ping;
+ index = index % PING_FRAMES;
+
+ for( i = 0; i < PING_FRAMES; i++ )
+ {
+ cg.ping += previousPings[ i ];
+ }
+
+ cg.ping /= PING_FRAMES;
+ }
+}
+
+/*
+==============
+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;
+ vec4_t color = { 1.0f, 1.0f, 1.0f, 1.0f };
+
+ // draw the phone jack if we are completely past our buffers
+ cmdNum = trap_GetCurrentCmdNumber( ) - CMD_BACKUP + 1;
+ trap_GetUserCmd( cmdNum, &cmd );
+
+ // special check for map_restart
+ if( cmd.serverTime <= cg.snap->ps.commandTime || cmd.serverTime > cg.time )
+ return;
+
+ // also add text in center of screen
+ s = "Connection Interrupted";
+ w = CG_Text_Width( s, 0.7f, 0 );
+ CG_Text_Paint( 320 - w / 2, 100, 0.7f, color, s, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
+
+ // blink the icon
+ if( ( cg.time >> 9 ) & 1 )
+ return;
+
+ x = 640 - 48;
+ y = 480 - 48;
+
+ CG_DrawPic( 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( rectDef_t *rect, float text_x, float text_y,
+ float scale, vec4_t textColor )
+{
+ int a, x, y, i;
+ float v;
+ float ax, ay, aw, ah, mid, range;
+ int color;
+ vec4_t adjustedColor;
+ float vscale;
+ vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
+
+ if( cg.snap->ps.pm_type == PM_INTERMISSION )
+ return;
+
+ if( !cg_lagometer.integer )
+ return;
+
+ if( cg.demoPlayback )
+ return;
+
+ Vector4Copy( textColor, adjustedColor );
+ adjustedColor[ 3 ] = 0.25f;
+
+ trap_R_SetColor( adjustedColor );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.whiteShader );
+ trap_R_SetColor( NULL );
+
+ //
+ // draw the graph
+ //
+ ax = x = rect->x;
+ ay = y = rect->y;
+ aw = rect->w;
+ ah = rect->h;
+
+ trap_R_SetColor( NULL );
+
+ 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_Text_Paint( ax, ay, 0.5, white, "snc", 0, 0, ITEM_TEXTSTYLE_NORMAL );
+ else
+ {
+ char *s;
+
+ s = va( "%d", cg.ping );
+ ax = rect->x + ( rect->w / 2.0f ) - ( CG_Text_Width( s, scale, 0 ) / 2.0f ) + text_x;
+ ay = rect->y + ( rect->h / 2.0f ) + ( CG_Text_Height( s, scale, 0 ) / 2.0f ) + text_y;
+
+ Vector4Copy( textColor, adjustedColor );
+ adjustedColor[ 3 ] = 0.5f;
+ CG_Text_Paint( ax, ay, scale, adjustedColor, s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
+ }
+
+ CG_DrawDisconnect( );
+}
+
+/*
+==============
+CG_DrawTextBlock
+==============
+*/
+static void CG_DrawTextBlock( rectDef_t *rect, float text_x, float text_y, vec4_t color,
+ float scale, int align, int textStyle, const char *text,
+ menuDef_t *parent, itemDef_t *textItem )
+{
+ float x, y, w, h;
+
+ //offset the text
+ x = rect->x;
+ y = rect->y;
+ w = rect->w - ( 16 + ( 2 * text_x ) ); //16 to ensure text within frame
+ h = rect->h;
+
+ textItem->text = text;
+
+ textItem->parent = parent;
+ memcpy( textItem->window.foreColor, color, sizeof( vec4_t ) );
+ textItem->window.flags = 0;
+
+ switch( align )
+ {
+ case ITEM_ALIGN_LEFT:
+ textItem->window.rect.x = x;
+ break;
+
+ case ITEM_ALIGN_RIGHT:
+ textItem->window.rect.x = x + w;
+ break;
+
+ case ITEM_ALIGN_CENTER:
+ textItem->window.rect.x = x + ( w / 2 );
+ break;
+
+ default:
+ textItem->window.rect.x = x;
+ break;
+ }
+
+ textItem->window.rect.y = y;
+ textItem->window.rect.w = w;
+ textItem->window.rect.h = h;
+ textItem->window.borderSize = 0;
+ textItem->textRect.x = 0;
+ textItem->textRect.y = 0;
+ textItem->textRect.w = 0;
+ textItem->textRect.h = 0;
+ textItem->textalignment = align;
+ textItem->textalignx = text_x;
+ textItem->textaligny = text_y;
+ textItem->textscale = scale;
+ textItem->textStyle = textStyle;
+
+ //hack to utilise existing autowrap code
+ Item_Text_AutoWrapped_Paint( textItem );
+}
+
+/*
+===================
+CG_DrawConsole
+===================
+*/
+static void CG_DrawConsole( rectDef_t *rect, float text_x, float text_y, vec4_t color,
+ float scale, int align, int textStyle )
+{
+ static menuDef_t dummyParent;
+ static itemDef_t textItem;
+
+ CG_DrawTextBlock( rect, text_x, text_y, color, scale, align, textStyle,
+ cg.consoleText, &dummyParent, &textItem );
+}
+
+/*
+===================
+CG_DrawTutorial
+===================
+*/
+static void CG_DrawTutorial( rectDef_t *rect, float text_x, float text_y, vec4_t color,
+ float scale, int align, int textStyle )
+{
+ static menuDef_t dummyParent;
+ static itemDef_t textItem;
+
+ if( !cg_tutorial.integer )
+ return;
+
+ CG_DrawTextBlock( rect, text_x, text_y, color, scale, align, textStyle,
+ CG_TutorialText( ), &dummyParent, &textItem );
+}
+
+/*
+===================
+CG_DrawWeaponIcon
+===================
+*/
+void CG_DrawWeaponIcon( rectDef_t *rect, vec4_t color )
+{
+ int ammo, clips, maxAmmo;
+ centity_t *cent;
+ playerState_t *ps;
+
+ cent = &cg_entities[ cg.snap->ps.clientNum ];
+ ps = &cg.snap->ps;
+
+ BG_UnpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups, &ammo, &clips );
+ BG_FindAmmoForWeapon( cent->currentState.weapon, &maxAmmo, NULL );
+
+ // don't display if dead
+ if( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 )
+ return;
+
+ if( cent->currentState.weapon == 0 )
+ return;
+
+ CG_RegisterWeapon( cent->currentState.weapon );
+
+ if( clips == 0 && !BG_FindInfinteAmmoForWeapon( cent->currentState.weapon ) )
+ {
+ float ammoPercent = (float)ammo / (float)maxAmmo;
+
+ if( ammoPercent < 0.33f )
+ {
+ color[ 0 ] = 1.0f;
+ color[ 1 ] = color[ 2 ] = 0.0f;
+ }
+ }
+
+ if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS && CG_AtHighestClass( ) )
+ {
+ if( cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME )
+ {
+ if( ( ( cg.time - cg.lastEvolveAttempt ) / 300 ) % 2 )
+ color[ 3 ] = 0.0f;
+ }
+ }
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_weapons[ cent->currentState.weapon ].weaponIcon );
+ trap_R_SetColor( NULL );
+}
+
+
+
+/*
+================================================================================
+
+CROSSHAIR
+
+================================================================================
+*/
+
+
+/*
+=================
+CG_DrawCrosshair
+=================
+*/
+static void CG_DrawCrosshair( void )
+{
+ float w, h;
+ qhandle_t hShader;
+ float x, y;
+ weaponInfo_t *wi;
+
+ if( cg_drawCrosshair.integer == CROSSHAIR_ALWAYSOFF )
+ return;
+
+ if( cg_drawCrosshair.integer == CROSSHAIR_RANGEDONLY &&
+ !BG_FindLongRangedForWeapon( cg.snap->ps.weapon ) )
+ {
+ return;
+ }
+
+ if( ( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR ) ||
+ ( cg.snap->ps.stats[ STAT_STATE ] & SS_INFESTING ) ||
+ ( cg.snap->ps.stats[ STAT_STATE ] & SS_HOVELING ) )
+ return;
+
+ if( cg.renderingThirdPerson )
+ return;
+
+ wi = &cg_weapons[ cg.snap->ps.weapon ];
+
+ w = h = wi->crossHairSize;
+
+ x = cg_crosshairX.integer;
+ y = cg_crosshairY.integer;
+ CG_AdjustFrom640( &x, &y, &w, &h );
+
+ hShader = wi->crossHair;
+
+ if( hShader != 0 )
+ {
+ trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * ( cg.refdef.width - w ),
+ y + cg.refdef.y + 0.5 * ( cg.refdef.height - h ),
+ w, h, 0, 0, 1, 1, hShader );
+ }
+}
+
+
+
+/*
+=================
+CG_ScanForCrosshairEntity
+=================
+*/
+static void CG_ScanForCrosshairEntity( void )
+{
+ trace_t trace;
+ vec3_t start, end;
+ int content;
+ pTeam_t team;
+
+ 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 );
+ if( content & CONTENTS_FOG )
+ return;
+
+ team = cgs.clientinfo[ trace.entityNum ].team;
+
+ if( cg.snap->ps.persistant[ PERS_TEAM ] != TEAM_SPECTATOR )
+ {
+ //only display team names of those on the same team as this player
+ if( team != cg.snap->ps.stats[ STAT_PTEAM ] )
+ return;
+ }
+
+ // update the fade timer
+ cg.crosshairClientNum = trace.entityNum;
+ cg.crosshairClientTime = cg.time;
+}
+
+
+/*
+=====================
+CG_DrawCrosshairNames
+=====================
+*/
+static void CG_DrawCrosshairNames( rectDef_t *rect, float scale, int textStyle )
+{
+ float *color;
+ char *name;
+ float w, x;
+
+ if( !cg_drawCrosshairNames.integer )
+ return;
+
+ if( cg.renderingThirdPerson )
+ 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;
+ }
+
+ name = cgs.clientinfo[ cg.crosshairClientNum ].name;
+ w = CG_Text_Width( name, scale, 0 );
+ x = rect->x + rect->w / 2;
+ CG_Text_Paint( x - w / 2, rect->y + rect->h, scale, color, name, 0, 0, textStyle );
+ trap_R_SetColor( NULL );
+}
+
+
+/*
+===============
+CG_OwnerDraw
+
+Draw an owner drawn item
+===============
+*/
+void CG_OwnerDraw( float x, float y, float w, float h, float text_x,
+ float text_y, int ownerDraw, int ownerDrawFlags,
+ int align, float special, float scale, vec4_t color,
+ qhandle_t shader, int textStyle )
+{
+ rectDef_t rect;
+
+ if( cg_drawStatus.integer == 0 )
+ return;
+
+ rect.x = x;
+ rect.y = y;
+ rect.w = w;
+ rect.h = h;
+
+ switch( ownerDraw )
+ {
+ case CG_PLAYER_CREDITS_VALUE:
+ CG_DrawPlayerCreditsValue( &rect, color, qtrue );
+ break;
+ case CG_PLAYER_BANK_VALUE:
+ CG_DrawPlayerBankValue( &rect, color, qtrue );
+ break;
+ case CG_PLAYER_CREDITS_VALUE_NOPAD:
+ CG_DrawPlayerCreditsValue( &rect, color, qfalse );
+ break;
+ case CG_PLAYER_BANK_VALUE_NOPAD:
+ CG_DrawPlayerBankValue( &rect, color, qfalse );
+ break;
+ case CG_PLAYER_STAMINA:
+ CG_DrawPlayerStamina( &rect, color, scale, align, textStyle, special );
+ break;
+ case CG_PLAYER_STAMINA_1:
+ CG_DrawPlayerStamina1( &rect, color, shader );
+ break;
+ case CG_PLAYER_STAMINA_2:
+ CG_DrawPlayerStamina2( &rect, color, shader );
+ break;
+ case CG_PLAYER_STAMINA_3:
+ CG_DrawPlayerStamina3( &rect, color, shader );
+ break;
+ case CG_PLAYER_STAMINA_4:
+ CG_DrawPlayerStamina4( &rect, color, shader );
+ break;
+ case CG_PLAYER_STAMINA_BOLT:
+ CG_DrawPlayerStaminaBolt( &rect, color, shader );
+ break;
+ case CG_PLAYER_AMMO_VALUE:
+ CG_DrawPlayerAmmoValue( &rect, color );
+ break;
+ case CG_PLAYER_CLIPS_VALUE:
+ CG_DrawPlayerClipsValue( &rect, color );
+ break;
+ case CG_PLAYER_BUILD_TIMER:
+ CG_DrawPlayerBuildTimer( &rect, color );
+ break;
+ case CG_PLAYER_HEALTH:
+ CG_DrawPlayerHealthValue( &rect, color );
+ break;
+ case CG_PLAYER_HEALTH_BAR:
+ CG_DrawPlayerHealthBar( &rect, color, scale, align, textStyle, special );
+ break;
+ case CG_PLAYER_HEALTH_CROSS:
+ CG_DrawPlayerHealthCross( &rect, color, shader );
+ break;
+ case CG_PLAYER_CLIPS_RING:
+ CG_DrawPlayerClipsRing( &rect, color, shader );
+ break;
+ case CG_PLAYER_BUILD_TIMER_RING:
+ CG_DrawPlayerBuildTimerRing( &rect, color, shader );
+ break;
+ case CG_PLAYER_WALLCLIMBING:
+ CG_DrawPlayerWallclimbing( &rect, color, shader );
+ break;
+ case CG_PLAYER_BOOSTED:
+ CG_DrawPlayerBoosted( &rect, color, shader );
+ break;
+ case CG_PLAYER_BOOST_BOLT:
+ CG_DrawPlayerBoosterBolt( &rect, color, shader );
+ break;
+ case CG_PLAYER_POISON_BARBS:
+ CG_DrawPlayerPoisonBarbs( &rect, color, shader );
+ break;
+ case CG_PLAYER_ALIEN_SENSE:
+ CG_DrawAlienSense( &rect );
+ break;
+ case CG_PLAYER_HUMAN_SCANNER:
+ CG_DrawHumanScanner( &rect, shader, color );
+ break;
+ case CG_PLAYER_USABLE_BUILDABLE:
+ CG_DrawUsableBuildable( &rect, shader, color );
+ break;
+ case CG_KILLER:
+ CG_DrawKiller( &rect, scale, color, shader, textStyle );
+ break;
+ case CG_PLAYER_SELECT:
+ CG_DrawItemSelect( &rect, color );
+ break;
+ case CG_PLAYER_WEAPONICON:
+ CG_DrawWeaponIcon( &rect, color );
+ break;
+ case CG_PLAYER_SELECTTEXT:
+ CG_DrawItemSelectText( &rect, scale, textStyle );
+ break;
+ case CG_SPECTATORS:
+ CG_DrawTeamSpectators( &rect, scale, color, shader );
+ break;
+ case CG_PLAYER_CROSSHAIRNAMES:
+ CG_DrawCrosshairNames( &rect, scale, textStyle );
+ break;
+ case CG_STAGE_REPORT_TEXT:
+ CG_DrawStageReport( &rect, text_x, text_y, color, scale, align, textStyle );
+ break;
+
+ //loading screen
+ case CG_LOAD_LEVELSHOT:
+ CG_DrawLevelShot( &rect );
+ break;
+ case CG_LOAD_MEDIA:
+ CG_DrawMediaProgress( &rect, color, scale, align, textStyle, special );
+ break;
+ case CG_LOAD_MEDIA_LABEL:
+ CG_DrawMediaProgressLabel( &rect, text_x, text_y, color, scale, align );
+ break;
+ case CG_LOAD_BUILDABLES:
+ CG_DrawBuildablesProgress( &rect, color, scale, align, textStyle, special );
+ break;
+ case CG_LOAD_BUILDABLES_LABEL:
+ CG_DrawBuildablesProgressLabel( &rect, text_x, text_y, color, scale, align );
+ break;
+ case CG_LOAD_CHARMODEL:
+ CG_DrawCharModelProgress( &rect, color, scale, align, textStyle, special );
+ break;
+ case CG_LOAD_CHARMODEL_LABEL:
+ CG_DrawCharModelProgressLabel( &rect, text_x, text_y, color, scale, align );
+ break;
+ case CG_LOAD_OVERALL:
+ CG_DrawOverallProgress( &rect, color, scale, align, textStyle, special );
+ break;
+ case CG_LOAD_LEVELNAME:
+ CG_DrawLevelName( &rect, text_x, text_y, color, scale, align, textStyle );
+ break;
+ case CG_LOAD_MOTD:
+ CG_DrawMOTD( &rect, text_x, text_y, color, scale, align, textStyle );
+ break;
+ case CG_LOAD_HOSTNAME:
+ CG_DrawHostname( &rect, text_x, text_y, color, scale, align, textStyle );
+ break;
+
+ case CG_FPS:
+ CG_DrawFPS( &rect, text_x, text_y, scale, color, align, textStyle, qtrue );
+ break;
+ case CG_FPS_FIXED:
+ CG_DrawFPS( &rect, text_x, text_y, scale, color, align, textStyle, qfalse );
+ break;
+ case CG_TIMER:
+ CG_DrawTimer( &rect, text_x, text_y, scale, color, align, textStyle );
+ break;
+ case CG_CLOCK:
+ CG_DrawClock( &rect, text_x, text_y, scale, color, align, textStyle );
+ break;
+ case CG_TIMER_MINS:
+ CG_DrawTimerMins( &rect, color );
+ break;
+ case CG_TIMER_SECS:
+ CG_DrawTimerSecs( &rect, color );
+ break;
+ case CG_SNAPSHOT:
+ CG_DrawSnapshot( &rect, text_x, text_y, scale, color, align, textStyle );
+ break;
+ case CG_LAGOMETER:
+ CG_DrawLagometer( &rect, text_x, text_y, scale, color );
+ break;
+
+ case CG_DEMO_PLAYBACK:
+ CG_DrawDemoPlayback( &rect, color, shader );
+ break;
+ case CG_DEMO_RECORDING:
+ CG_DrawDemoRecording( &rect, color, shader );
+ break;
+
+ case CG_CONSOLE:
+ CG_DrawConsole( &rect, text_x, text_y, color, scale, align, textStyle );
+ break;
+
+ case CG_TUTORIAL:
+ CG_DrawTutorial( &rect, text_x, text_y, color, scale, align, textStyle );
+ break;
+
+ default:
+ break;
+ }
+}
+
+void CG_MouseEvent( int x, int y )
+{
+ int n;
+
+ if( ( cg.predictedPlayerState.pm_type == PM_NORMAL ||
+ cg.predictedPlayerState.pm_type == PM_SPECTATOR ) &&
+ cg.showScores == qfalse )
+ {
+ trap_Key_SetCatcher( 0 );
+ return;
+ }
+
+ cgs.cursorX += x;
+ if( cgs.cursorX < 0 )
+ cgs.cursorX = 0;
+ else if( cgs.cursorX > 640 )
+ cgs.cursorX = 640;
+
+ cgs.cursorY += y;
+ if( cgs.cursorY < 0 )
+ cgs.cursorY = 0;
+ else if( cgs.cursorY > 480 )
+ cgs.cursorY = 480;
+
+ n = Display_CursorType( cgs.cursorX, cgs.cursorY );
+ cgs.activeCursor = 0;
+ if( n == CURSOR_ARROW )
+ cgs.activeCursor = cgs.media.selectCursor;
+ else if( n == CURSOR_SIZER )
+ cgs.activeCursor = cgs.media.sizeCursor;
+
+ if( cgs.capturedItem )
+ Display_MouseMove( cgs.capturedItem, x, y );
+ else
+ Display_MouseMove( NULL, cgs.cursorX, cgs.cursorY );
+}
+
+/*
+==================
+CG_HideTeamMenus
+==================
+
+*/
+void CG_HideTeamMenu( void )
+{
+ Menus_CloseByName( "teamMenu" );
+ Menus_CloseByName( "getMenu" );
+}
+
+/*
+==================
+CG_ShowTeamMenus
+==================
+
+*/
+void CG_ShowTeamMenu( void )
+{
+ Menus_OpenByName( "teamMenu" );
+}
+
+/*
+==================
+CG_EventHandling
+==================
+ type 0 - no event handling
+ 1 - team menu
+ 2 - hud editor
+
+*/
+void CG_EventHandling( int type )
+{
+ cgs.eventHandling = type;
+
+ if( type == CGAME_EVENT_NONE )
+ CG_HideTeamMenu( );
+}
+
+
+
+void CG_KeyEvent( int key, qboolean down )
+{
+ if( !down )
+ return;
+
+ if( cg.predictedPlayerState.pm_type == PM_NORMAL ||
+ ( cg.predictedPlayerState.pm_type == PM_SPECTATOR &&
+ cg.showScores == qfalse ) )
+ {
+ CG_EventHandling( CGAME_EVENT_NONE );
+ trap_Key_SetCatcher( 0 );
+ return;
+ }
+
+ Display_HandleKey( key, down, cgs.cursorX, cgs.cursorY );
+
+ if( cgs.capturedItem )
+ cgs.capturedItem = NULL;
+ else
+ {
+ if( key == K_MOUSE2 && down )
+ cgs.capturedItem = Display_CaptureItem( cgs.cursorX, cgs.cursorY );
+ }
+}
+
+int CG_ClientNumFromName( const char *p )
+{
+ int i;
+
+ for( i = 0; i < cgs.maxclients; i++ )
+ {
+ if( cgs.clientinfo[ i ].infoValid &&
+ Q_stricmp( cgs.clientinfo[ i ].name, p ) == 0 )
+ return i;
+ }
+
+ return -1;
+}
+
+void CG_RunMenuScript( char **args )
+{
+}
+
+
+void CG_GetTeamColor( vec4_t *color )
+{
+ (*color)[ 0 ] = (*color)[ 2 ] = 0.0f;
+ (*color)[ 1 ] = 0.17f;
+ (*color)[ 3 ] = 0.25f;
+}
+//END TA UI
+
+
+/*
+================
+CG_DrawLighting
+
+================
+*/
+static void CG_DrawLighting( void )
+{
+ centity_t *cent;
+
+ cent = &cg_entities[ cg.snap->ps.clientNum ];
+
+ //fade to black if stamina is low
+ if( ( cg.snap->ps.stats[ STAT_STAMINA ] < -800 ) &&
+ ( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) )
+ {
+ vec4_t black = { 0, 0, 0, 0 };
+ black[ 3 ] = 1.0 - ( (float)( cg.snap->ps.stats[ STAT_STAMINA ] + 1000 ) / 200.0f );
+ trap_R_SetColor( black );
+ CG_DrawPic( 0, 0, 640, 480, cgs.media.whiteShader );
+ trap_R_SetColor( NULL );
+ }
+}
+
+/*
+===============================================================================
+
+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;
+
+ Q_strncpyz( cg.centerPrint, str, sizeof( cg.centerPrint ) );
+
+ cg.centerPrintTime = cg.time;
+ cg.centerPrintY = y;
+ cg.centerPrintCharWidth = charWidth;
+
+ // count the number of lines for centering
+ cg.centerPrintLines = 1;
+ s = cg.centerPrint;
+ while( *s )
+ {
+ if( *s == '\n' )
+ cg.centerPrintLines++;
+
+ s++;
+ }
+}
+
+
+/*
+===================
+CG_DrawCenterString
+===================
+*/
+static void CG_DrawCenterString( void )
+{
+ char *start;
+ int l;
+ int x, y, w;
+ int h;
+ float *color;
+
+ if( !cg.centerPrintTime )
+ return;
+
+ color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value );
+ if( !color )
+ return;
+
+ trap_R_SetColor( color );
+
+ start = cg.centerPrint;
+
+ y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2;
+
+ 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_Text_Width( linebuffer, 0.5, 0 );
+ h = CG_Text_Height( linebuffer, 0.5, 0 );
+ x = ( SCREEN_WIDTH - w ) / 2;
+ CG_Text_Paint( x, y + h, 0.5, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE );
+ y += h + 6;
+
+ while( *start && ( *start != '\n' ) )
+ start++;
+
+ if( !*start )
+ break;
+
+ start++;
+ }
+
+ trap_R_SetColor( NULL );
+}
+
+
+
+
+
+//==============================================================================
+
+//FIXME: both vote notes are hardcoded, change to ownerdrawn?
+
+/*
+=================
+CG_DrawVote
+=================
+*/
+static void CG_DrawVote( void )
+{
+ char *s;
+ int sec;
+ vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
+ char yeskey[ 32 ], nokey[ 32 ];
+
+ 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 ) ) / 1000;
+
+ if( sec < 0 )
+ sec = 0;
+ Q_strncpyz( yeskey, CG_KeyBinding( "vote yes" ), sizeof( yeskey ) );
+ Q_strncpyz( nokey, CG_KeyBinding( "vote no" ), sizeof( nokey ) );
+ s = va( "VOTE(%i): \"%s\" [%s]Yes:%i [%s]No:%i", sec, cgs.voteString,
+ yeskey, cgs.voteYes, nokey, cgs.voteNo );
+ CG_Text_Paint( 8, 340, 0.3f, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
+}
+
+/*
+=================
+CG_DrawTeamVote
+=================
+*/
+static void CG_DrawTeamVote( void )
+{
+ char *s;
+ int sec, cs_offset;
+ vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
+ char yeskey[ 32 ], nokey[ 32 ];
+
+ if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ cs_offset = 0;
+ else if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ 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;
+
+ Q_strncpyz( yeskey, CG_KeyBinding( "teamvote yes" ), sizeof( yeskey ) );
+ Q_strncpyz( nokey, CG_KeyBinding( "teamvote no" ), sizeof( nokey ) );
+ s = va( "TEAMVOTE(%i): \"%s\" [%s]Yes:%i [%s]No:%i", sec,
+ cgs.teamVoteString[ cs_offset ],
+ yeskey, cgs.teamVoteYes[cs_offset],
+ nokey, cgs.teamVoteNo[ cs_offset ] );
+
+ CG_Text_Paint( 8, 360, 0.3f, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
+}
+
+
+static qboolean CG_DrawScoreboard( void )
+{
+ static qboolean firstTime = qtrue;
+ float fade, *fadeColor;
+
+ if( menuScoreboard )
+ menuScoreboard->window.flags &= ~WINDOW_FORCED;
+
+ if( cg_paused.integer )
+ {
+ cg.deferredPlayerLoading = 0;
+ firstTime = qtrue;
+ return qfalse;
+ }
+
+ if( cg.showScores ||
+ cg.predictedPlayerState.pm_type == PM_INTERMISSION )
+ {
+ fade = 1.0;
+ fadeColor = colorWhite;
+ }
+ else
+ {
+ cg.deferredPlayerLoading = 0;
+ cg.killerName[ 0 ] = 0;
+ firstTime = qtrue;
+ return qfalse;
+ }
+
+
+ if( menuScoreboard == NULL )
+ menuScoreboard = Menus_FindByName( "teamscore_menu" );
+
+ if( menuScoreboard )
+ {
+ if( firstTime )
+ {
+ CG_SetScoreSelection( menuScoreboard );
+ firstTime = qfalse;
+ }
+
+ Menu_Paint( menuScoreboard, qtrue );
+ }
+
+ return qtrue;
+}
+
+/*
+=================
+CG_DrawIntermission
+=================
+*/
+static void CG_DrawIntermission( void )
+{
+ if( cg_drawStatus.integer )
+ Menu_Paint( Menus_FindByName( "default_hud" ), qtrue );
+
+ cg.scoreFadeTime = cg.time;
+ cg.scoreBoardShowing = CG_DrawScoreboard( );
+}
+
+#define FOLLOWING_STRING "following "
+
+/*
+=================
+CG_DrawFollow
+=================
+*/
+static qboolean CG_DrawFollow( void )
+{
+ float w;
+ vec4_t color;
+ char buffer[ MAX_STRING_CHARS ];
+
+ if( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
+ return qfalse;
+
+ color[ 0 ] = 1;
+ color[ 1 ] = 1;
+ color[ 2 ] = 1;
+ color[ 3 ] = 1;
+
+ strcpy( buffer, FOLLOWING_STRING );
+ strcat( buffer, cgs.clientinfo[ cg.snap->ps.clientNum ].name );
+
+ w = CG_Text_Width( buffer, 0.7f, 0 );
+ CG_Text_Paint( 320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
+
+ return qtrue;
+}
+
+/*
+=================
+CG_DrawQueue
+=================
+*/
+static qboolean CG_DrawQueue( void )
+{
+ float w;
+ vec4_t color;
+ char buffer[ MAX_STRING_CHARS ];
+
+ if( !( cg.snap->ps.pm_flags & PMF_QUEUED ) )
+ return qfalse;
+
+ color[ 0 ] = 1;
+ color[ 1 ] = 1;
+ color[ 2 ] = 1;
+ color[ 3 ] = 1;
+
+ Com_sprintf( buffer, MAX_STRING_CHARS, "You are in position %d of the spawn queue.",
+ cg.snap->ps.persistant[ PERS_QUEUEPOS ] + 1 );
+
+ w = CG_Text_Width( buffer, 0.7f, 0 );
+ CG_Text_Paint( 320 - w / 2, 360, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
+
+ if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ {
+ if( cgs.numAlienSpawns == 1 )
+ Com_sprintf( buffer, MAX_STRING_CHARS, "There is 1 spawn remaining." );
+ else
+ Com_sprintf( buffer, MAX_STRING_CHARS, "There are %d spawns remaining.",
+ cgs.numAlienSpawns );
+ }
+ else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ {
+ if( cgs.numHumanSpawns == 1 )
+ Com_sprintf( buffer, MAX_STRING_CHARS, "There is 1 spawn remaining." );
+ else
+ Com_sprintf( buffer, MAX_STRING_CHARS, "There are %d spawns remaining.",
+ cgs.numHumanSpawns );
+ }
+
+ w = CG_Text_Width( buffer, 0.7f, 0 );
+ CG_Text_Paint( 320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
+
+ return qtrue;
+}
+
+//==================================================================================
+
+#define SPECTATOR_STRING "SPECTATOR"
+/*
+=================
+CG_Draw2D
+=================
+*/
+static void CG_Draw2D( void )
+{
+ vec4_t color;
+ float w;
+ menuDef_t *menu = NULL, *defaultMenu;
+
+ color[ 0 ] = color[ 1 ] = color[ 2 ] = color[ 3 ] = 1.0f;
+
+ // 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 )
+ {
+ CG_DrawIntermission( );
+ return;
+ }
+
+ //TA: draw the lighting effects e.g. nvg
+ CG_DrawLighting( );
+
+
+ defaultMenu = Menus_FindByName( "default_hud" );
+
+ if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR )
+ {
+ w = CG_Text_Width( SPECTATOR_STRING, 0.7f, 0 );
+ CG_Text_Paint( 320 - w / 2, 440, 0.7f, color, SPECTATOR_STRING, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
+ }
+ else
+ menu = Menus_FindByName( BG_FindHudNameForClass( cg.predictedPlayerState.stats[ STAT_PCLASS ] ) );
+
+ if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_INFESTING ) &&
+ !( cg.snap->ps.stats[ STAT_STATE ] & SS_HOVELING ) && menu &&
+ ( cg.snap->ps.stats[ STAT_HEALTH ] > 0 ) )
+ {
+ CG_DrawBuildableStatus( );
+ if( cg_drawStatus.integer )
+ Menu_Paint( menu, qtrue );
+
+ CG_DrawCrosshair( );
+ }
+ else if( cg_drawStatus.integer )
+ Menu_Paint( defaultMenu, qtrue );
+
+ CG_DrawVote( );
+ CG_DrawTeamVote( );
+ CG_DrawFollow( );
+ CG_DrawQueue( );
+
+ // don't draw center string if scoreboard is up
+ cg.scoreBoardShowing = CG_DrawScoreboard( );
+
+ if( !cg.scoreBoardShowing )
+ CG_DrawCenterString( );
+}
+
+/*
+===============
+CG_ScalePainBlendTCs
+===============
+*/
+static void CG_ScalePainBlendTCs( float* s1, float *t1, float *s2, float *t2 )
+{
+ *s1 -= 0.5f;
+ *t1 -= 0.5f;
+ *s2 -= 0.5f;
+ *t2 -= 0.5f;
+
+ *s1 *= cg_painBlendZoom.value;
+ *t1 *= cg_painBlendZoom.value;
+ *s2 *= cg_painBlendZoom.value;
+ *t2 *= cg_painBlendZoom.value;
+
+ *s1 += 0.5f;
+ *t1 += 0.5f;
+ *s2 += 0.5f;
+ *t2 += 0.5f;
+}
+
+#define PAINBLEND_BORDER 0.15f
+
+/*
+===============
+CG_PainBlend
+===============
+*/
+static void CG_PainBlend( void )
+{
+ vec4_t color;
+ int damage;
+ float damageAsFracOfMax;
+ qhandle_t shader = cgs.media.viewBloodShader;
+ float x, y, w, h;
+ float s1, t1, s2, t2;
+
+ if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR || cg.intermissionStarted )
+ return;
+
+ damage = cg.lastHealth - cg.snap->ps.stats[ STAT_HEALTH ];
+
+ if( damage < 0 )
+ damage = 0;
+
+ damageAsFracOfMax = (float)damage / cg.snap->ps.stats[ STAT_MAX_HEALTH ];
+ cg.lastHealth = cg.snap->ps.stats[ STAT_HEALTH ];
+
+ cg.painBlendValue += damageAsFracOfMax * cg_painBlendScale.value;
+
+ if( cg.painBlendValue > 0.0f )
+ {
+ cg.painBlendValue -= ( cg.frametime / 1000.0f ) *
+ cg_painBlendDownRate.value;
+ }
+
+ if( cg.painBlendValue > 1.0f )
+ cg.painBlendValue = 1.0f;
+ else if( cg.painBlendValue <= 0.0f )
+ {
+ cg.painBlendValue = 0.0f;
+ return;
+ }
+
+ if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ VectorSet( color, 0.43f, 0.8f, 0.37f );
+ else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ VectorSet( color, 0.8f, 0.0f, 0.0f );
+
+ if( cg.painBlendValue > cg.painBlendTarget )
+ {
+ cg.painBlendTarget += ( cg.frametime / 1000.0f ) *
+ cg_painBlendUpRate.value;
+ }
+ else if( cg.painBlendValue < cg.painBlendTarget )
+ cg.painBlendTarget = cg.painBlendValue;
+
+ if( cg.painBlendTarget > cg_painBlendMax.value )
+ cg.painBlendTarget = cg_painBlendMax.value;
+
+ color[ 3 ] = cg.painBlendTarget;
+
+ trap_R_SetColor( color );
+
+ //left
+ x = 0.0f; y = 0.0f;
+ w = PAINBLEND_BORDER * 640.0f; h = 480.0f;
+ CG_AdjustFrom640( &x, &y, &w, &h );
+ s1 = 0.0f; t1 = 0.0f;
+ s2 = PAINBLEND_BORDER; t2 = 1.0f;
+ CG_ScalePainBlendTCs( &s1, &t1, &s2, &t2 );
+ trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, shader );
+
+ //right
+ x = 640.0f - ( PAINBLEND_BORDER * 640.0f ); y = 0.0f;
+ w = PAINBLEND_BORDER * 640.0f; h = 480.0f;
+ CG_AdjustFrom640( &x, &y, &w, &h );
+ s1 = 1.0f - PAINBLEND_BORDER; t1 = 0.0f;
+ s2 = 1.0f; t2 =1.0f;
+ CG_ScalePainBlendTCs( &s1, &t1, &s2, &t2 );
+ trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, shader );
+
+ //top
+ x = PAINBLEND_BORDER * 640.0f; y = 0.0f;
+ w = 640.0f - ( 2 * PAINBLEND_BORDER * 640.0f ); h = PAINBLEND_BORDER * 480.0f;
+ CG_AdjustFrom640( &x, &y, &w, &h );
+ s1 = PAINBLEND_BORDER; t1 = 0.0f;
+ s2 = 1.0f - PAINBLEND_BORDER; t2 = PAINBLEND_BORDER;
+ CG_ScalePainBlendTCs( &s1, &t1, &s2, &t2 );
+ trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, shader );
+
+ //bottom
+ x = PAINBLEND_BORDER * 640.0f; y = 480.0f - ( PAINBLEND_BORDER * 480.0f );
+ w = 640.0f - ( 2 * PAINBLEND_BORDER * 640.0f ); h = PAINBLEND_BORDER * 480.0f;
+ CG_AdjustFrom640( &x, &y, &w, &h );
+ s1 = PAINBLEND_BORDER; t1 = 1.0f - PAINBLEND_BORDER;
+ s2 = 1.0f - PAINBLEND_BORDER; t2 = 1.0f;
+ CG_ScalePainBlendTCs( &s1, &t1, &s2, &t2 );
+ trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, shader );
+
+ trap_R_SetColor( NULL );
+}
+
+/*
+=====================
+CG_ResetPainBlend
+=====================
+*/
+void CG_ResetPainBlend( void )
+{
+ cg.painBlendValue = 0.0f;
+ cg.painBlendTarget = 0.0f;
+ cg.lastHealth = cg.snap->ps.stats[ STAT_HEALTH ];
+}
+
+/*
+=====================
+CG_DrawActive
+
+Perform all drawing needed to completely fill the screen
+=====================
+*/
+void CG_DrawActive( stereoFrame_t stereoView )
+{
+ float separation;
+ vec3_t baseOrg;
+
+ // optionally draw the info screen instead
+ if( !cg.snap )
+ return;
+
+ switch ( stereoView )
+ {
+ case STEREO_CENTER:
+ separation = 0;
+ break;
+ case STEREO_LEFT:
+ separation = -cg_stereoSeparation.value / 2;
+ break;
+ case STEREO_RIGHT:
+ separation = cg_stereoSeparation.value / 2;
+ break;
+ default:
+ separation = 0;
+ CG_Error( "CG_DrawActive: Undefined stereoView" );
+ }
+
+ // clear around the rendered view if sized down
+ CG_TileClear( );
+
+ // offset vieworg appropriately if we're doing stereo separation
+ VectorCopy( cg.refdef.vieworg, baseOrg );
+
+ if( separation != 0 )
+ VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[ 1 ],
+ cg.refdef.vieworg );
+
+ // draw 3D view
+ trap_R_RenderScene( &cg.refdef );
+
+ // restore original viewpoint if running stereo
+ if( separation != 0 )
+ VectorCopy( baseOrg, cg.refdef.vieworg );
+
+ // first person blend blobs, done after AnglesToAxis
+ if( !cg.renderingThirdPerson )
+ CG_PainBlend( );
+
+ // draw status bar and other floating elements
+ CG_Draw2D( );
+}
+
+
+