From 425decdf7e9284d15aa726e3ae96b9942fb0e3ea Mon Sep 17 00:00:00 2001 From: IronClawTrem Date: Sun, 16 Feb 2020 03:40:06 +0000 Subject: create tremded branch --- src/cgame/cg_draw.c | 2981 ++++++++++++++++++++++++++++----------------------- 1 file changed, 1638 insertions(+), 1343 deletions(-) (limited to 'src/cgame/cg_draw.c') diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c index 1a3ecae..38c9578 100644 --- a/src/cgame/cg_draw.c +++ b/src/cgame/cg_draw.c @@ -1,13 +1,14 @@ /* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2006 Tim Angus +Copyright (C) 2000-2013 Darklegion Development +Copyright (C) 2015-2019 GrangerHub 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, +published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Tremulous is distributed in the hope that it will be @@ -16,284 +17,84 @@ 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 +along with Tremulous; if not, see + =========================================================================== */ // cg_draw.c -- draw all of the graphical elements during // active (after loading) gameplay - #include "cg_local.h" -#include "../ui/ui_shared.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 ) +static void CG_AlignText( rectDef_t *rect, const char *text, float scale, + float w, float h, + int align, int valign, + float *x, float *y ) { - 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; + float tx, ty; - 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 ) + if( scale > 0.0f ) { - 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++; - } - } + w = UI_Text_Width( text, scale ); + h = UI_Text_Height( text, scale ); } - 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 ) + switch( align ) { - 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; -} + default: + case ALIGN_LEFT: + tx = 0.0f; + break; -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 ); -} + case ALIGN_RIGHT: + tx = rect->w - w; + break; -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; + case ALIGN_CENTER: + tx = ( rect->w - w ) / 2.0f; + break; - if( scale <= cg_smallFont.value ) - font = &cgDC.Assets.smallFont; - else if( scale > cg_bigFont.value ) - font = &cgDC.Assets.bigFont; + case ALIGN_NONE: + tx = 0; + break; + } - useScale = scale * font->glyphScale; - if( text ) + switch( valign ) { -// 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 + default: + case VALIGN_BOTTOM: + ty = rect->h; + break; - 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 ); - } + case VALIGN_TOP: + ty = h; + break; + case VALIGN_CENTER: + ty = h + ( ( rect->h - h ) / 2.0f ); + break; - CG_Text_PaintChar( x, y - yadj, - glyph->imageWidth, - glyph->imageHeight, - useScale, - glyph->s, - glyph->t, - glyph->s2, - glyph->t2, - glyph->glyph ); + case VALIGN_NONE: + ty = 0; + break; + } - x += ( glyph->xSkip * useScale ) + adjust; - s++; - count++; - } - } + if( x ) + *x = rect->x + tx; - trap_R_SetColor( NULL ); - } + if( y ) + *y = rect->y + ty; } /* ============== CG_DrawFieldPadded -Draws large numbers for status bar and powerups +Draws large numbers for status bar ============== */ static void CG_DrawFieldPadded( int x, int y, int width, int cw, int ch, int value ) @@ -307,7 +108,7 @@ static void CG_DrawFieldPadded( int x, int y, int width, int cw, int ch, int val charWidth = CHAR_WIDTH; if( !( charHeight = ch ) ) - charWidth = CHAR_HEIGHT; + charHeight = CHAR_HEIGHT; if( width < 1 ) return; @@ -344,7 +145,7 @@ static void CG_DrawFieldPadded( int x, int y, int width, int cw, int ch, int val orgL = l; - x += 2; + x += ( 2.0f * cgDC.aspectScale ); ptr = num; while( *ptr && l ) @@ -373,7 +174,7 @@ static void CG_DrawFieldPadded( int x, int y, int width, int cw, int ch, int val ============== CG_DrawField -Draws large numbers for status bar and powerups +Draws large numbers for status bar ============== */ void CG_DrawField( float x, float y, int width, float cw, float ch, int value ) @@ -387,7 +188,7 @@ void CG_DrawField( float x, float y, int width, float cw, float ch, int value ) charWidth = CHAR_WIDTH; if( !( charHeight = ch ) ) - charWidth = CHAR_HEIGHT; + charHeight = CHAR_HEIGHT; if( width < 1 ) return; @@ -422,7 +223,7 @@ void CG_DrawField( float x, float y, int width, float cw, float ch, int value ) if( l > width ) l = width; - x += 2 + charWidth * ( width - l ); + x += ( 2.0f * cgDC.aspectScale ) + charWidth * ( width - l ); ptr = num; while( *ptr && l ) @@ -440,18 +241,22 @@ void CG_DrawField( float x, float y, int width, float cw, float ch, int value ) } static void CG_DrawProgressBar( rectDef_t *rect, vec4_t color, float scale, - int align, int textStyle, int special, float progress ) + int align, int textalign, int textStyle, + float borderSize, float progress ) { - float rimWidth = rect->h / 20.0f; + float rimWidth; float doneWidth, leftWidth; - float tx, ty, tw, th; + float tx, ty; char textBuffer[ 8 ]; - if( rimWidth < 0.6f ) - rimWidth = 0.6f; - - if( special >= 0.0f ) - rimWidth = special; + if( borderSize >= 0.0f ) + rimWidth = borderSize; + else + { + rimWidth = rect->h / 20.0f; + if( rimWidth < 0.6f ) + rimWidth = 0.6f; + } if( progress < 0.0f ) progress = 0.0f; @@ -464,7 +269,7 @@ static void CG_DrawProgressBar( rectDef_t *rect, vec4_t color, float scale, trap_R_SetColor( color ); //draw rim and bar - if( align == ITEM_ALIGN_RIGHT ) + if( align == ALIGN_RIGHT ) { CG_DrawPic( rect->x, rect->y, rimWidth, rect->h, cgs.media.whiteShader ); CG_DrawPic( rect->x + rimWidth, rect->y, @@ -490,31 +295,9 @@ static void CG_DrawProgressBar( rectDef_t *rect, vec4_t color, float scale, if( scale > 0.0 ) { Com_sprintf( textBuffer, sizeof( textBuffer ), "%d%%", (int)( progress * 100 ) ); - tw = CG_Text_Width( textBuffer, scale, 0 ); - th = scale * 40.0f; + CG_AlignText( rect, textBuffer, scale, 0.0f, 0.0f, textalign, VALIGN_CENTER, &tx, &ty ); - 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 ); + UI_Text_Paint( tx, ty, scale, color, textBuffer, 0, 0, textStyle ); } } @@ -539,37 +322,19 @@ static void CG_DrawPlayerCreditsValue( rectDef_t *rect, vec4_t color, qboolean p value = ps->persistant[ PERS_CREDIT ]; if( value > -1 ) { - if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS && - !CG_AtHighestClass( ) ) + if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS ) { - if( cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME ) + if( !BG_AlienCanEvolve( cg.predictedPlayerState.stats[ STAT_CLASS ], + value, cgs.alienStage ) && + cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME && + ( ( cg.time - cg.lastEvolveAttempt ) / 300 ) & 1 ) { - if( ( ( cg.time - cg.lastEvolveAttempt ) / 300 ) % 2 ) - color[ 3 ] = 0.0f; + 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 /= ALIEN_CREDITS_PER_KILL; + } - value = ps->persistant[ PERS_BANK ]; - if( value > -1 ) - { trap_R_SetColor( color ); if( padding ) @@ -581,114 +346,66 @@ static void CG_DrawPlayerBankValue( rectDef_t *rect, vec4_t color, qboolean padd } } -#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 ) +static void CG_DrawPlayerCreditsFraction( 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; + float fraction; + float height; - stamina -= ( 2 * (int)maxStaminaBy3 ); - progress = stamina / maxStaminaBy3; + if( cg.predictedPlayerState.stats[ STAT_TEAM ] != TEAM_ALIENS ) + return; - if( progress > 1.0f ) - progress = 1.0f; - else if( progress < 0.0f ) - progress = 0.0f; + fraction = ((float)(cg.predictedPlayerState.persistant[ PERS_CREDIT ] % + ALIEN_CREDITS_PER_KILL)) / ALIEN_CREDITS_PER_KILL; - color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF ); + CG_AdjustFrom640( &rect->x, &rect->y, &rect->w, &rect->h ); + height = rect->h * fraction; trap_R_SetColor( color ); - CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); + trap_R_DrawStretchPic( rect->x, rect->y - height + rect->h, rect->w, + height, 0.0f, 1.0f - fraction, 1.0f, 1.0f, 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 +CG_DrawPlayerStamina ============== */ -static void CG_DrawPlayerStamina3( rectDef_t *rect, vec4_t color, qhandle_t shader ) +static void CG_DrawPlayerStamina( int ownerDraw, rectDef_t *rect, + vec4_t backColor, vec4_t foreColor, + qhandle_t shader ) { playerState_t *ps = &cg.snap->ps; float stamina = ps->stats[ STAT_STAMINA ]; - float maxStaminaBy3 = (float)MAX_STAMINA / 3.0f; + float maxStaminaBy3 = (float)STAMINA_MAX / 3.0f; float progress; + vec4_t color; + switch( ownerDraw ) + { + case CG_PLAYER_STAMINA_1: + progress = ( stamina - 2 * (int)maxStaminaBy3 ) / maxStaminaBy3; + break; + case CG_PLAYER_STAMINA_2: + progress = ( stamina - (int)maxStaminaBy3 ) / maxStaminaBy3; + break; + case CG_PLAYER_STAMINA_3: progress = stamina / maxStaminaBy3; + break; + case CG_PLAYER_STAMINA_4: + progress = ( stamina + STAMINA_MAX ) / STAMINA_MAX; + break; + default: + return; + } 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 ); + Vector4Lerp( progress, backColor, foreColor, color ); trap_R_SetColor( color ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); @@ -700,15 +417,28 @@ static void CG_DrawPlayerStamina4( rectDef_t *rect, vec4_t color, qhandle_t shad CG_DrawPlayerStaminaBolt ============== */ -static void CG_DrawPlayerStaminaBolt( rectDef_t *rect, vec4_t color, qhandle_t shader ) +static void CG_DrawPlayerStaminaBolt( rectDef_t *rect, vec4_t backColor, + vec4_t foreColor, qhandle_t shader ) { - playerState_t *ps = &cg.snap->ps; - float stamina = ps->stats[ STAT_STAMINA ]; + float stamina = cg.snap->ps.stats[ STAT_STAMINA ]; + vec4_t color; - if( stamina < 0 ) - color[ 3 ] = HH_MIN_ALPHA; + if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_SPEEDBOOST ) + { + if( stamina >= 0 ) + Vector4Lerp( ( sin( cg.time / 150.0f ) + 1 ) / 2, + backColor, foreColor, color ); + else + Vector4Lerp( ( sin( cg.time / 2000.0f ) + 1 ) / 2, + backColor, foreColor, color ); + } else - color[ 3 ] = HH_MAX_ALPHA; + { + if( stamina < 0 ) + Vector4Copy( backColor, color ); + else + Vector4Copy( foreColor, color ); + } trap_R_SetColor( color ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); @@ -720,40 +450,42 @@ static void CG_DrawPlayerStaminaBolt( rectDef_t *rect, vec4_t color, qhandle_t s CG_DrawPlayerClipsRing ============== */ -static void CG_DrawPlayerClipsRing( rectDef_t *rect, vec4_t color, qhandle_t shader ) +static void CG_DrawPlayerClipsRing( rectDef_t *rect, vec4_t backColor, + vec4_t foreColor, qhandle_t shader ) { playerState_t *ps = &cg.snap->ps; centity_t *cent; float buildTime = ps->stats[ STAT_MISC ]; float progress; float maxDelay; + weapon_t weapon; + vec4_t color; cent = &cg_entities[ cg.snap->ps.clientNum ]; + weapon = BG_GetPlayerWeapon( ps ); - switch( cent->currentState.weapon ) + switch( 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; + if( buildTime > MAXIMUM_BUILD_TIME ) + buildTime = MAXIMUM_BUILD_TIME; + progress = ( MAXIMUM_BUILD_TIME - buildTime ) / MAXIMUM_BUILD_TIME; - progress = ( maxDelay - buildTime ) / maxDelay; - - color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF ); + Vector4Lerp( progress, backColor, foreColor, color ); break; default: if( ps->weaponstate == WEAPON_RELOADING ) { - maxDelay = (float)BG_FindReloadTimeForWeapon( cent->currentState.weapon ); + maxDelay = (float)BG_Weapon( cent->currentState.weapon )->reloadTime; progress = ( maxDelay - (float)ps->weaponTime ) / maxDelay; - color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF ); + Vector4Lerp( progress, backColor, foreColor, color ); } + else + Com_Memcpy( color, foreColor, sizeof( color ) ); break; } @@ -767,24 +499,20 @@ static void CG_DrawPlayerClipsRing( rectDef_t *rect, vec4_t color, qhandle_t sha CG_DrawPlayerBuildTimerRing ============== */ -static void CG_DrawPlayerBuildTimerRing( rectDef_t *rect, vec4_t color, qhandle_t shader ) +static void CG_DrawPlayerBuildTimerRing( rectDef_t *rect, vec4_t backColor, + vec4_t foreColor, qhandle_t shader ) { playerState_t *ps = &cg.snap->ps; - centity_t *cent; float buildTime = ps->stats[ STAT_MISC ]; float progress; - float maxDelay; + vec4_t color; - cent = &cg_entities[ cg.snap->ps.clientNum ]; - - maxDelay = (float)BG_FindBuildDelayForWeapon( cent->currentState.weapon ); + if( buildTime > MAXIMUM_BUILD_TIME ) + buildTime = MAXIMUM_BUILD_TIME; - if( buildTime > maxDelay ) - buildTime = maxDelay; + progress = ( MAXIMUM_BUILD_TIME - buildTime ) / MAXIMUM_BUILD_TIME; - progress = ( maxDelay - buildTime ) / maxDelay; - - color[ 3 ] = AH_MIN_ALPHA + ( progress * AH_ALPHA_DIFF ); + Vector4Lerp( progress, backColor, foreColor, color ); trap_R_SetColor( color ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); @@ -796,17 +524,14 @@ static void CG_DrawPlayerBuildTimerRing( rectDef_t *rect, vec4_t color, qhandle_ CG_DrawPlayerBoosted ============== */ -static void CG_DrawPlayerBoosted( rectDef_t *rect, vec4_t color, qhandle_t shader ) +static void CG_DrawPlayerBoosted( rectDef_t *rect, vec4_t backColor, + vec4_t foreColor, qhandle_t shader ) { - playerState_t *ps = &cg.snap->ps; - qboolean boosted = ps->stats[ STAT_STATE ] & SS_BOOSTED; - - if( boosted ) - color[ 3 ] = AH_MAX_ALPHA; + if( cg.snap->ps.stats[ STAT_STATE ] & SS_BOOSTED ) + trap_R_SetColor( foreColor ); else - color[ 3 ] = AH_MIN_ALPHA; + trap_R_SetColor( backColor ); - trap_R_SetColor( color ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); trap_R_SetColor( NULL ); } @@ -816,26 +541,20 @@ static void CG_DrawPlayerBoosted( rectDef_t *rect, vec4_t color, qhandle_t shade CG_DrawPlayerBoosterBolt ============== */ -static void CG_DrawPlayerBoosterBolt( rectDef_t *rect, vec4_t color, qhandle_t shader ) +static void CG_DrawPlayerBoosterBolt( rectDef_t *rect, vec4_t backColor, + vec4_t foreColor, qhandle_t shader ) { - playerState_t *ps = &cg.snap->ps; - qboolean boosted = ps->stats[ STAT_STATE ] & SS_BOOSTED; - vec4_t localColor; + vec4_t color; - 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; - } - } + // Flash bolts when the boost is almost out + if( ( cg.snap->ps.stats[ STAT_STATE ] & SS_BOOSTED ) && + ( cg.snap->ps.stats[ STAT_STATE ] & SS_BOOSTEDWARNING ) ) + Vector4Lerp( ( sin( cg.time / 100.0f ) + 1 ) / 2, + backColor, foreColor, color ); + else + Vector4Copy( foreColor, color ); - trap_R_SetColor( localColor ); + trap_R_SetColor( color ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); trap_R_SetColor( NULL ); } @@ -847,38 +566,49 @@ 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; + qboolean vertical; + float x = rect->x, y = rect->y; + float width = rect->w, height = rect->h; + float diff; + int iconsize, numBarbs, maxBarbs; + + maxBarbs = BG_Weapon( cg.snap->ps.weapon )->maxAmmo; + numBarbs = cg.snap->ps.ammo; + if( maxBarbs <= 0 || numBarbs <= 0 ) + return; - numBarbs = ps->ammo; + // adjust these first to ensure the aspect ratio of the barb image is + // preserved + CG_AdjustFrom640( &x, &y, &width, &height ); if( height > width ) { vertical = qtrue; iconsize = width; + if( maxBarbs != 1 ) // avoid division by zero + diff = ( height - iconsize ) / (float)( maxBarbs - 1 ); + else + diff = 0; // doesn't matter, won't be used } - else if( height <= width ) + else { vertical = qfalse; iconsize = height; + if( maxBarbs != 1 ) + diff = ( width - iconsize ) / (float)( maxBarbs - 1 ); + else + diff = 0; } - if( color[ 3 ] != 0.0 ) - trap_R_SetColor( color ); + trap_R_SetColor( color ); - for( i = 0; i < numBarbs; i ++ ) + for( ; numBarbs > 0; numBarbs-- ) { + trap_R_DrawStretchPic( x, y, iconsize, iconsize, 0, 0, 1, 1, shader ); if( vertical ) - y += iconsize; + y += diff; else - x += iconsize; - - CG_DrawPic( x, y, iconsize, iconsize, shader ); + x += diff; } trap_R_SetColor( NULL ); @@ -889,70 +619,83 @@ static void CG_DrawPlayerPoisonBarbs( rectDef_t *rect, vec4_t color, qhandle_t s CG_DrawPlayerWallclimbing ============== */ -static void CG_DrawPlayerWallclimbing( rectDef_t *rect, vec4_t color, qhandle_t shader ) +static void CG_DrawPlayerWallclimbing( rectDef_t *rect, vec4_t backColor, vec4_t foreColor, qhandle_t shader ) { - playerState_t *ps = &cg.snap->ps; - qboolean ww = ps->stats[ STAT_STATE ] & SS_WALLCLIMBING; - - if( ww ) - color[ 3 ] = AH_MAX_ALPHA; + if( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) + trap_R_SetColor( foreColor ); else - color[ 3 ] = AH_MIN_ALPHA; + trap_R_SetColor( backColor ); - 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; + int value; + int valueMarked = -1; + qboolean bp = qfalse; - if( cent->currentState.weapon ) + switch( cg.snap->ps.stats[ STAT_WEAPON ] ) { - switch( cent->currentState.weapon ) - { - case WP_ABUILD: - case WP_ABUILD2: - //percentage of BP remaining - value = cgs.alienBuildPoints; - break; + case WP_NONE: + case WP_BLASTER: + return; - case WP_HBUILD: - case WP_HBUILD2: - //percentage of BP remaining - value = cgs.humanBuildPoints; - break; + case WP_ABUILD: + case WP_ABUILD2: + case WP_HBUILD: + value = cg.snap->ps.persistant[ PERS_BP ]; + valueMarked = cg.snap->ps.persistant[ PERS_MARKEDBP ]; + bp = qtrue; + break; - default: - value = ps->ammo; - break; - } + default: + value = cg.snap->ps.ammo; + break; + } - if( value > 999 ) - value = 999; + if( value > 999 ) + value = 999; + if( valueMarked > 999 ) + valueMarked = 999; - if( value > -1 ) + if( value > -1 ) + { + float tx, ty; + const char *text; + float scale; + int len; + + trap_R_SetColor( color ); + if( !bp ) { - trap_R_SetColor( color ); - CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value ); + CG_DrawField( rect->x - 5, rect->y, 4, rect->w / 4, rect->h, value ); trap_R_SetColor( NULL ); + return; } + + if( valueMarked > 0 ) + text = va( "%d+(%d)", value, valueMarked ); + else + text = va( "%d", value ); + + len = strlen( text ); + + if( len <= 4 ) + scale = 0.50; + else if( len <= 6 ) + scale = 0.43; + else if( len == 7 ) + scale = 0.36; + else if( len == 8 ) + scale = 0.33; + else + scale = 0.31; + + CG_AlignText( rect, text, scale, 0.0f, 0.0f, ALIGN_RIGHT, VALIGN_CENTER, &tx, &ty ); + UI_Text_Paint( tx + 1, ty, scale, color, text, 0, 0, ITEM_TEXTSTYLE_NORMAL ); + trap_R_SetColor( NULL ); } } @@ -964,7 +707,7 @@ CG_DrawAlienSense */ static void CG_DrawAlienSense( rectDef_t *rect ) { - if( BG_ClassHasAbility( cg.snap->ps.stats[ STAT_PCLASS ], SCA_ALIENSENSE ) ) + if( BG_ClassHasAbility( cg.snap->ps.stats[ STAT_CLASS ], SCA_ALIENSENSE ) ) CG_AlienSense( rect ); } @@ -999,19 +742,25 @@ static void CG_DrawUsableBuildable( rectDef_t *rect, qhandle_t shader, vec4_t co es = &cg_entities[ trace.entityNum ].currentState; - if( es->eType == ET_BUILDABLE && BG_FindUsableForBuildable( es->modelindex ) && - cg.predictedPlayerState.stats[ STAT_PTEAM ] == BG_FindTeamForBuildable( es->modelindex ) ) + if( es->eType == ET_BUILDABLE && BG_Buildable( es->modelindex )->usable && + cg.predictedPlayerState.stats[ STAT_TEAM ] == BG_Buildable( es->modelindex )->team ) { //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 ) ) ) + ( !BG_Weapon( cg.snap->ps.weapon )->usesEnergy || + BG_Weapon( cg.snap->ps.weapon )->infiniteAmmo ) ) + { + cg.nearUsableBuildable = BA_NONE; return; + } trap_R_SetColor( color ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); trap_R_SetColor( NULL ); + cg.nearUsableBuildable = es->modelindex; } + else + cg.nearUsableBuildable = BA_NONE; } @@ -1019,221 +768,381 @@ static void CG_DrawUsableBuildable( rectDef_t *rect, qhandle_t shader, vec4_t co 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; + if( ps->stats[ STAT_MISC ] <= 0 ) + return; - default: - return; - break; - } + switch( ps->stats[ STAT_WEAPON ] ) + { + case WP_ABUILD: + case WP_ABUILD2: + case WP_HBUILD: + break; - if( !ps->stats[ STAT_MISC ] ) + default: return; + } - index = (int)( progress * 8.0f ); - - if( index > 7 ) - index = 7; - else if( index < 0 ) - index = 0; + index = 8 * ( ps->stats[ STAT_MISC ] - 1 ) / MAXIMUM_BUILD_TIME; + 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 ); + if( cg.time - cg.lastBuildAttempt <= BUILD_DELAY_TIME && + ( ( 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; + playerState_t *ps = &cg.snap->ps; - if( cent->currentState.weapon ) + switch( ps->stats[ STAT_WEAPON ] ) { - switch( cent->currentState.weapon ) - { - case WP_ABUILD: - case WP_ABUILD2: - case WP_HBUILD: - case WP_HBUILD2: - break; + case WP_NONE: + case WP_BLASTER: + case WP_ABUILD: + case WP_ABUILD2: + case WP_HBUILD: + return; - default: - value = ps->clips; + default: + value = ps->clips; - 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; - } + 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 ); + CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, + cg.snap->ps.stats[ STAT_HEALTH ] ); 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 ) +static void CG_DrawPlayerHealthCross( rectDef_t *rect, vec4_t ref_color ) { - playerState_t *ps = &cg.snap->ps; - int health = ps->stats[ STAT_HEALTH ]; + qhandle_t shader; + vec4_t color; + float ref_alpha; + + // Pick the current icon + shader = cgs.media.healthCross; + if( cg.snap->ps.stats[ STAT_STATE ] & SS_HEALING_3X ) + shader = cgs.media.healthCross3X; + else if( cg.snap->ps.stats[ STAT_STATE ] & SS_HEALING_2X ) + { + if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) + shader = cgs.media.healthCross2X; + else + shader = cgs.media.healthCrossMedkit; + } + else if( cg.snap->ps.stats[ STAT_STATE ] & SS_POISONED ) + shader = cgs.media.healthCrossPoisoned; - if( health < 10 ) + // Pick the alpha value + Vector4Copy( ref_color, color ); + if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_HUMANS && + cg.snap->ps.stats[ STAT_HEALTH ] < 10 ) { color[ 0 ] = 1.0f; color[ 1 ] = color[ 2 ] = 0.0f; } + ref_alpha = ref_color[ 3 ]; + if( cg.snap->ps.stats[ STAT_STATE ] & SS_HEALING_ACTIVE ) + ref_alpha = 1.0f; + + // Don't fade from nothing + if( !cg.lastHealthCross ) + cg.lastHealthCross = shader; + // Fade the icon during transition + if( cg.lastHealthCross != shader ) + { + cg.healthCrossFade += cg.frametime / 500.0f; + if( cg.healthCrossFade > 1.0f ) + { + cg.healthCrossFade = 0.0f; + cg.lastHealthCross = shader; + } + else + { + // Fading between two icons + color[ 3 ] = ref_alpha * cg.healthCrossFade; + trap_R_SetColor( color ); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); + color[ 3 ] = ref_alpha * ( 1.0f - cg.healthCrossFade ); + trap_R_SetColor( color ); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg.lastHealthCross ); + trap_R_SetColor( NULL ); + return; + } + } + + // Not fading, draw a single icon + color[ 3 ] = ref_alpha; 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 ) +static float CG_ChargeProgress( void ) { - vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f }; - float tx, tw = CG_Text_Width( s, scale, 0 ); + float progress; + int min = 0, max = 0; - switch( align ) + if( cg.snap->ps.weapon == WP_ALEVEL3 ) { - case ITEM_ALIGN_LEFT: - tx = 0.0f; - break; + min = LEVEL3_POUNCE_TIME_MIN; + max = LEVEL3_POUNCE_TIME; + } + else if( cg.snap->ps.weapon == WP_ALEVEL3_UPG ) + { + min = LEVEL3_POUNCE_TIME_MIN; + max = LEVEL3_POUNCE_TIME_UPG; + } + else if( cg.snap->ps.weapon == WP_ALEVEL4 ) + { + if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_CHARGING ) + { + min = 0; + max = LEVEL4_TRAMPLE_DURATION; + } + else + { + min = LEVEL4_TRAMPLE_CHARGE_MIN; + max = LEVEL4_TRAMPLE_CHARGE_MAX; + } + } + else if( cg.snap->ps.weapon == WP_LUCIFER_CANNON ) + { + min = LCANNON_CHARGE_TIME_MIN; + max = LCANNON_CHARGE_TIME_MAX; + } - case ITEM_ALIGN_RIGHT: - tx = rect->w - tw; - break; + if( max - min <= 0.0f ) + return 0.0f; - case ITEM_ALIGN_CENTER: - tx = ( rect->w / 2.0f ) - ( tw / 2.0f ); - break; + progress = ( (float)cg.predictedPlayerState.stats[ STAT_MISC ] - min ) / + ( max - min ); - default: - tx = 0.0f; + if( progress > 1.0f ) + return 1.0f; + + if( progress < 0.0f ) + return 0.0f; + + return progress; +} + +#define CHARGE_BAR_FADE_RATE 0.002f + +static void CG_DrawPlayerChargeBarBG( rectDef_t *rect, vec4_t ref_color, + qhandle_t shader ) +{ + vec4_t color; + + if( !cg_drawChargeBar.integer || cg.chargeMeterAlpha <= 0.0f ) + return; + + color[ 0 ] = ref_color[ 0 ]; + color[ 1 ] = ref_color[ 1 ]; + color[ 2 ] = ref_color[ 2 ]; + color[ 3 ] = ref_color[ 3 ] * cg.chargeMeterAlpha; + + // Draw meter background + trap_R_SetColor( color ); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); + trap_R_SetColor( NULL ); +} + +// FIXME: This should come from the element info +#define CHARGE_BAR_CAP_SIZE 3 + +static void CG_DrawPlayerChargeBar( rectDef_t *rect, vec4_t ref_color, + qhandle_t shader ) +{ + vec4_t color; + float x, y, width, height, cap_size, progress; + + if( !cg_drawChargeBar.integer ) + return; + + // Get progress proportion and pump fade + progress = CG_ChargeProgress(); + if( progress <= 0.0f ) + { + cg.chargeMeterAlpha -= CHARGE_BAR_FADE_RATE * cg.frametime; + if( cg.chargeMeterAlpha <= 0.0f ) + { + cg.chargeMeterAlpha = 0.0f; + return; + } + } + else + { + cg.chargeMeterValue = progress; + cg.chargeMeterAlpha += CHARGE_BAR_FADE_RATE * cg.frametime; + if( cg.chargeMeterAlpha > 1.0f ) + cg.chargeMeterAlpha = 1.0f; + } + + color[ 0 ] = ref_color[ 0 ]; + color[ 1 ] = ref_color[ 1 ]; + color[ 2 ] = ref_color[ 2 ]; + color[ 3 ] = ref_color[ 3 ] * cg.chargeMeterAlpha; + + // Flash red for Lucifer Cannon warning + if( cg.snap->ps.weapon == WP_LUCIFER_CANNON && + cg.snap->ps.stats[ STAT_MISC ] >= LCANNON_CHARGE_TIME_WARN && + ( cg.time & 128 ) ) + { + color[ 0 ] = 1.0f; + color[ 1 ] = 0.0f; + color[ 2 ] = 0.0f; + } + + x = rect->x; + y = rect->y; + + // Horizontal charge bar + if( rect->w >= rect->h ) + { + width = ( rect->w - CHARGE_BAR_CAP_SIZE * 2 ) * cg.chargeMeterValue; + height = rect->h; + CG_AdjustFrom640( &x, &y, &width, &height ); + cap_size = CHARGE_BAR_CAP_SIZE * cgs.screenXScale; + + // Draw the meter + trap_R_SetColor( color ); + trap_R_DrawStretchPic( x, y, cap_size, height, 0, 0, 1, 1, shader ); + trap_R_DrawStretchPic( x + width + cap_size, y, cap_size, height, + 1, 0, 0, 1, shader ); + trap_R_DrawStretchPic( x + cap_size, y, width, height, 1, 0, 1, 1, shader ); + trap_R_SetColor( NULL ); } + // Vertical charge bar + else + { + y += rect->h; + width = rect->w; + height = ( rect->h - CHARGE_BAR_CAP_SIZE * 2 ) * cg.chargeMeterValue; + CG_AdjustFrom640( &x, &y, &width, &height ); + cap_size = CHARGE_BAR_CAP_SIZE * cgs.screenYScale; + + // Draw the meter + trap_R_SetColor( color ); + trap_R_DrawStretchPic( x, y - cap_size, width, cap_size, + 0, 1, 1, 0, shader ); + trap_R_DrawStretchPic( x, y - height - cap_size * 2, width, + cap_size, 0, 0, 1, 1, shader ); + trap_R_DrawStretchPic( x, y - height - cap_size, width, height, + 0, 1, 1, 1, shader ); + trap_R_SetColor( NULL ); + } +} + +static void CG_DrawProgressLabel( rectDef_t *rect, float text_x, float text_y, vec4_t color, + float scale, int textalign, int textvalign, + const char *s, float fraction ) +{ + vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f }; + float tx, ty; + + CG_AlignText( rect, s, scale, 0.0f, 0.0f, textalign, textvalign, &tx, &ty ); + if( fraction < 1.0f ) - CG_Text_Paint( rect->x + text_x + tx, rect->y + text_y, scale, white, + UI_Text_Paint( text_x + tx, text_y + ty, scale, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL ); else - CG_Text_Paint( rect->x + text_x + tx, rect->y + text_y, scale, color, + UI_Text_Paint( text_x + tx, text_y + ty, 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 ) + int align, int textalign, int textStyle, + float borderSize ) { - CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.mediaFraction ); + CG_DrawProgressBar( rect, color, scale, align, textalign, textStyle, + borderSize, cg.mediaFraction ); } static void CG_DrawMediaProgressLabel( rectDef_t *rect, float text_x, float text_y, - vec4_t color, float scale, int align ) + vec4_t color, float scale, int textalign, int textvalign ) { - CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Map and Textures", cg.mediaFraction ); + CG_DrawProgressLabel( rect, text_x, text_y, color, scale, textalign, textvalign, + "Map and Textures", cg.mediaFraction ); } -static void CG_DrawBuildablesProgress( rectDef_t *rect, vec4_t color, float scale, - int align, int textStyle, int special ) +static void CG_DrawBuildablesProgress( rectDef_t *rect, vec4_t color, + float scale, int align, int textalign, + int textStyle, float borderSize ) { - CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.buildablesFraction ); + CG_DrawProgressBar( rect, color, scale, align, textalign, textStyle, + borderSize, cg.buildablesFraction ); } static void CG_DrawBuildablesProgressLabel( rectDef_t *rect, float text_x, float text_y, - vec4_t color, float scale, int align ) + vec4_t color, float scale, int textalign, int textvalign ) { - CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Buildable Models", cg.buildablesFraction ); + CG_DrawProgressLabel( rect, text_x, text_y, color, scale, textalign, textvalign, + "Buildable Models", cg.buildablesFraction ); } -static void CG_DrawCharModelProgress( rectDef_t *rect, vec4_t color, float scale, - int align, int textStyle, int special ) +static void CG_DrawCharModelProgress( rectDef_t *rect, vec4_t color, + float scale, int align, int textalign, + int textStyle, float borderSize ) { - CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.charModelFraction ); + CG_DrawProgressBar( rect, color, scale, align, textalign, textStyle, + borderSize, cg.charModelFraction ); } static void CG_DrawCharModelProgressLabel( rectDef_t *rect, float text_x, float text_y, - vec4_t color, float scale, int align ) + vec4_t color, float scale, int textalign, int textvalign ) { - CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Character Models", cg.charModelFraction ); + CG_DrawProgressLabel( rect, text_x, text_y, color, scale, textalign, textvalign, + "Character Models", cg.charModelFraction ); } static void CG_DrawOverallProgress( rectDef_t *rect, vec4_t color, float scale, - int align, int textStyle, int special ) + int align, int textalign, int textStyle, + float borderSize ) { float total; - total = ( cg.charModelFraction + cg.buildablesFraction + cg.mediaFraction ) / 3.0f; - CG_DrawProgressBar( rect, color, scale, align, textStyle, special, total ); + total = cg.charModelFraction + cg.buildablesFraction + cg.mediaFraction; + total /= 3.0f; + + CG_DrawProgressBar( rect, color, scale, align, textalign, textStyle, + borderSize, total ); } static void CG_DrawLevelShot( rectDef_t *rect ) @@ -1258,97 +1167,44 @@ static void CG_DrawLevelShot( rectDef_t *rect ) 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 ) + vec4_t color, float scale, + int textalign, int textvalign, int textStyle ) { const char *s; s = CG_ConfigString( CS_MESSAGE ); - CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, s ); + UI_DrawTextBlock( rect, text_x, text_y, color, scale, textalign, textvalign, textStyle, s ); } static void CG_DrawMOTD( rectDef_t *rect, float text_x, float text_y, - vec4_t color, float scale, int align, int textStyle ) + vec4_t color, float scale, + int textalign, int textvalign, int textStyle ) { const char *s; + char parsed[ MAX_STRING_CHARS ]; s = CG_ConfigString( CS_MOTD ); - CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, s ); + Q_ParseNewlines( parsed, s, sizeof( parsed ) ); + + UI_DrawTextBlock( rect, text_x, text_y, color, scale, textalign, textvalign, textStyle, parsed ); } static void CG_DrawHostname( rectDef_t *rect, float text_x, float text_y, - vec4_t color, float scale, int align, int textStyle ) + vec4_t color, float scale, + int textalign, int textvalign, int textStyle ) { char buffer[ 1024 ]; const char *info; info = CG_ConfigString( CS_SERVERINFO ); - Q_strncpyz( buffer, Info_ValueForKey( info, "sv_hostname" ), 1024 ); + UI_EscapeEmoticons( buffer, Info_ValueForKey( info, "sv_hostname" ), sizeof( buffer ) ); Q_CleanStr( buffer ); - CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, buffer ); + UI_DrawTextBlock( rect, text_x, text_y, color, scale, textalign, textvalign, textStyle, buffer ); } /* @@ -1409,30 +1265,29 @@ Draw all the status / pacifier stuff during level loading */ void CG_DrawLoadingScreen( void ) { - Menu_Paint( Menus_FindByName( "Loading" ), qtrue ); + menuDef_t *menu = Menus_FindByName( "Loading" ); + + Menu_Update( menu ); + Menu_Paint( menu, qtrue ); } float CG_GetValue( int ownerDraw ) { - centity_t *cent; playerState_t *ps; + weapon_t weapon; - cent = &cg_entities[ cg.snap->ps.clientNum ]; ps = &cg.snap->ps; + weapon = BG_GetPlayerWeapon( ps ); switch( ownerDraw ) { case CG_PLAYER_AMMO_VALUE: - if( cent->currentState.weapon ) - { + if( weapon ) return ps->ammo; - } break; case CG_PLAYER_CLIPS_VALUE: - if( cent->currentState.weapon ) - { + if( weapon ) return ps->clips; - } break; case CG_PLAYER_HEALTH: return ps->stats[ STAT_HEALTH ]; @@ -1461,165 +1316,131 @@ static void CG_DrawKiller( rectDef_t *rect, float scale, vec4_t color, if( cg.killerName[ 0 ] ) { int x = rect->x + rect->w / 2; - CG_Text_Paint( x - CG_Text_Width( CG_GetKillerText( ), scale, 0 ) / 2, + UI_Text_Paint( x - UI_Text_Width( CG_GetKillerText( ), scale ) / 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; +#define SPECTATORS_PIXELS_PER_SECOND 30.0f - 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; +/* +================== +CG_DrawTeamSpectators +================== +*/ +static void CG_DrawTeamSpectators( rectDef_t *rect, float scale, int textvalign, vec4_t color, qhandle_t shader ) +{ + float y; + char *text = cg.spectatorList; + float textWidth = UI_Text_Width( text, scale ); - useScale = scale * font->glyphScale; - trap_R_SetColor( color ); - len = strlen( text ); + CG_AlignText( rect, text, scale, 0.0f, 0.0f, ALIGN_LEFT, textvalign, NULL, &y ); - if( limit > 0 && len > limit ) - len = limit; + if( textWidth > rect->w ) + { + // The text is too wide to fit, so scroll it + int now = trap_Milliseconds( ); + int delta = now - cg.spectatorTime; - count = 0; + CG_SetClipRegion( rect->x, rect->y, rect->w, rect->h ); - 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 + UI_Text_Paint( rect->x - cg.spectatorOffset, y, scale, color, text, 0, 0, 0 ); + UI_Text_Paint( rect->x + textWidth - cg.spectatorOffset, y, scale, color, text, 0, 0, 0 ); - 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; + CG_ClearClipRegion( ); - if( CG_Text_Width( s, useScale, 1 ) + x > max ) - { - *maxX = 0; - break; - } + cg.spectatorOffset += ( delta / 1000.0f ) * SPECTATORS_PIXELS_PER_SECOND; - 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++; - } - } + while( cg.spectatorOffset > textWidth ) + cg.spectatorOffset -= textWidth; - trap_R_SetColor( NULL ); + cg.spectatorTime = now; + } + else + { + UI_Text_Paint( rect->x, y, scale, color, text, 0, 0, 0 ); } } -static void CG_DrawTeamSpectators( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader ) +#define FOLLOWING_STRING "following " +#define CHASING_STRING "chasing " + +/* +================== +CG_DrawFollow +================== +*/ +static void CG_DrawFollow( rectDef_t *rect, float text_x, float text_y, + vec4_t color, float scale, int textalign, int textvalign, int textStyle ) { - if( cg.spectatorLen ) - { - float maxX; + float tx, ty; - if( cg.spectatorWidth == -1 ) - { - cg.spectatorWidth = 0; - cg.spectatorPaintX = rect->x + 1; - cg.spectatorPaintX2 = -1; - } + if( cg.snap && cg.snap->ps.pm_flags & PMF_FOLLOW ) + { + char buffer[ MAX_STRING_CHARS ]; - if( cg.spectatorOffset > cg.spectatorLen ) - { - cg.spectatorOffset = 0; - cg.spectatorPaintX = rect->x + 1; - cg.spectatorPaintX2 = -1; - } + if( !cg.chaseFollow ) + strcpy( buffer, FOLLOWING_STRING ); + else + strcpy( buffer, CHASING_STRING ); - if( cg.time > cg.spectatorTime ) - { - cg.spectatorTime = cg.time + 10; + strcat( buffer, cgs.clientinfo[ cg.snap->ps.clientNum ].name ); - 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; + CG_AlignText( rect, buffer, scale, 0, 0, textalign, textvalign, &tx, &ty ); + UI_Text_Paint( text_x + tx, text_y + ty, scale, color, buffer, 0, 0, + textStyle ); + } +} - if( cg.spectatorPaintX2 >= 0 ) - cg.spectatorPaintX = cg.spectatorPaintX2; - else - cg.spectatorPaintX = rect->x + rect->w - 2; +/* +================== +CG_DrawTeamLabel +================== +*/ +static void CG_DrawTeamLabel( rectDef_t *rect, team_t team, float text_x, float text_y, + vec4_t color, float scale, int textalign, int textvalign, int textStyle ) +{ + char *t; + char stage[ MAX_TOKEN_CHARS ]; + const char *s; + float tx, ty; - cg.spectatorPaintX2 = -1; - } - } - else - { - cg.spectatorPaintX--; + stage[ 0 ] = '\0'; - if( cg.spectatorPaintX2 >= 0 ) - cg.spectatorPaintX2--; - } - } + switch( team ) + { + case TEAM_ALIENS: + t = "Aliens"; + if( cg.intermissionStarted ) + Com_sprintf( stage, MAX_TOKEN_CHARS, "(Stage %d)", cgs.alienStage + 1 ); + break; - maxX = rect->x + rect->w - 2; + case TEAM_HUMANS: + t = "Humans"; + if( cg.intermissionStarted ) + Com_sprintf( stage, MAX_TOKEN_CHARS, "(Stage %d)", cgs.humanStage + 1 ); + break; - CG_Text_Paint_Limit( &maxX, cg.spectatorPaintX, rect->y + rect->h - 3, scale, color, - &cg.spectatorList[ cg.spectatorOffset ], 0, 0 ); + default: + t = ""; + break; + } - 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 ); - } + switch( textalign ) + { + default: + case ALIGN_LEFT: + s = va( "%s %s", t, stage ); + break; - 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; + case ALIGN_RIGHT: + s = va( "%s %s", stage, t ); + break; } + + CG_AlignText( rect, s, scale, 0.0f, 0.0f, textalign, textvalign, &tx, &ty ); + UI_Text_Paint( text_x + tx, text_y + ty, scale, color, s, 0, 0, textStyle ); } /* @@ -1628,70 +1449,52 @@ CG_DrawStageReport ================== */ static void CG_DrawStageReport( rectDef_t *rect, float text_x, float text_y, - vec4_t color, float scale, int align, int textStyle ) + vec4_t color, float scale, int textalign, int textvalign, int textStyle ) { char s[ MAX_TOKEN_CHARS ]; - int tx, w, kills; + float tx, ty; - if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_NONE && !cg.intermissionStarted ) + if( 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 ) + if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_NONE ) + return; + + if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { - kills = cgs.alienNextStageThreshold - cgs.alienKills; + int kills = ceil( (float)(cgs.alienNextStageThreshold - cgs.alienCredits) / ALIEN_CREDITS_PER_KILL ); + if( kills < 0 ) + kills = 0; 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 ); + Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, 1 frag for next stage", + cgs.alienStage + 1 ); else - Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kills for next stage", + Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d frags for next stage", cgs.alienStage + 1, kills ); } - else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) + else if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { - kills = cgs.humanNextStageThreshold - cgs.humanKills; + int credits = cgs.humanNextStageThreshold - cgs.humanCredits; + + if( credits < 0 ) + credits = 0; 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 if( credits == 1 ) + Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, 1 credit for next stage", + cgs.humanStage + 1 ); else - Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kills for next stage", - cgs.humanStage + 1, kills ); + Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d credits for next stage", + cgs.humanStage + 1, credits ); } - 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_AlignText( rect, s, scale, 0.0f, 0.0f, textalign, textvalign, &tx, &ty ); - CG_Text_Paint( text_x + tx, rect->y + text_y, scale, color, s, 0, 0, textStyle ); + UI_Text_Paint( text_x + tx, text_y + ty, scale, color, s, 0, 0, textStyle ); } /* @@ -1699,15 +1502,17 @@ static void CG_DrawStageReport( rectDef_t *rect, float text_x, float text_y, 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, + float scale, vec4_t color, + int textalign, int textvalign, int textStyle, qboolean scalableText ) { - char *s; - int tx, w, totalWidth, strLength; + const char *s; + float tx, ty; + float w, h, totalWidth; + int strLength; static int previousTimes[ FPS_FRAMES ]; static int index; int i, total; @@ -1741,27 +1546,12 @@ static void CG_DrawFPS( rectDef_t *rect, float text_x, float text_y, fps = 1000 * FPS_FRAMES / total; s = va( "%d", fps ); - w = CG_Text_Width( "0", scale, 0 ); + w = UI_Text_Width( "0", scale ); + h = UI_Text_Height( "0", scale ); 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; + totalWidth = UI_Text_Width( FPS_STRING, scale ) + w * strLength; - default: - tx = 0.0f; - } + CG_AlignText( rect, s, 0.0f, totalWidth, h, textalign, textvalign, &tx, &ty ); if( scalableText ) { @@ -1772,8 +1562,10 @@ static void CG_DrawFPS( rectDef_t *rect, float text_x, float text_y, 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 ); + UI_Text_Paint( text_x + tx + i * w, text_y + ty, scale, color, c, 0, 0, textStyle ); } + + UI_Text_Paint( text_x + tx + i * w, text_y + ty, scale, color, FPS_STRING, 0, 0, textStyle ); } else { @@ -1781,9 +1573,6 @@ static void CG_DrawFPS( rectDef_t *rect, float text_x, float text_y, 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 ); } } @@ -1844,10 +1633,13 @@ CG_DrawTimer ================= */ static void CG_DrawTimer( rectDef_t *rect, float text_x, float text_y, - float scale, vec4_t color, int align, int textStyle ) + float scale, vec4_t color, + int textalign, int textvalign, int textStyle ) { - char *s; - int i, tx, w, totalWidth, strLength; + const char *s; + float tx, ty; + int i, strLength; + float w, h, totalWidth; int mins, seconds, tens; int msec; @@ -1863,36 +1655,255 @@ static void CG_DrawTimer( rectDef_t *rect, float text_x, float text_y, seconds -= tens * 10; s = va( "%d:%d%d", mins, tens, seconds ); - w = CG_Text_Width( "0", scale, 0 ); + w = UI_Text_Width( "0", scale ); + h = UI_Text_Height( "0", scale ); strLength = CG_DrawStrlen( s ); totalWidth = w * strLength; - switch( align ) + CG_AlignText( rect, s, 0.0f, totalWidth, h, textalign, textvalign, &tx, &ty ); + + for( i = 0; i < strLength; i++ ) { - case ITEM_ALIGN_LEFT: - tx = rect->x; - break; + char c[ 2 ]; - case ITEM_ALIGN_RIGHT: - tx = rect->x + rect->w - totalWidth; - break; + c[ 0 ] = s[ i ]; + c[ 1 ] = '\0'; - case ITEM_ALIGN_CENTER: - tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f ); - break; + UI_Text_Paint( text_x + tx + i * w, text_y + ty, scale, color, c, 0, 0, textStyle ); + } +} - default: - tx = 0.0f; +/* +================= +CG_DrawTeamOverlay +================= +*/ + +typedef enum +{ + TEAMOVERLAY_OFF, + TEAMOVERLAY_ALL, + TEAMOVERLAY_SUPPORT, + TEAMOVERLAY_NEARBY, +} teamOverlayMode_t; + +typedef enum +{ + TEAMOVERLAY_SORT_NONE, + TEAMOVERLAY_SORT_SCORE, + TEAMOVERLAY_SORT_WEAPONCLASS, +} teamOverlaySort_t; + +static int QDECL SortScore( const void *a, const void *b ) +{ + int na = *(int *)a; + int nb = *(int *)b; + + return( cgs.clientinfo[ nb ].score - cgs.clientinfo[ na ].score ); +} + +static int QDECL SortWeaponClass( const void *a, const void *b ) +{ + int out; + clientInfo_t *ca = cgs.clientinfo + *(int *)a; + clientInfo_t *cb = cgs.clientinfo + *(int *)b; + + out = cb->curWeaponClass - ca->curWeaponClass; + + // We want grangers on top. ckits are already on top without the special case. + if( ca->team == TEAM_ALIENS ) + { + if( ca->curWeaponClass == PCL_ALIEN_BUILDER0_UPG || + cb->curWeaponClass == PCL_ALIEN_BUILDER0_UPG || + ca->curWeaponClass == PCL_ALIEN_BUILDER0 || + cb->curWeaponClass == PCL_ALIEN_BUILDER0 ) + { + out = -out; + } } - for( i = 0; i < strLength; i++ ) + return( out ); +} + +static void CG_DrawTeamOverlay( rectDef_t *rect, float scale, vec4_t color ) +{ + const char *s; + int i; + float x = rect->x; + float y; + clientInfo_t *ci, *pci; + vec4_t tcolor; + float iconSize = rect->h / 8.0f; + float leftMargin = 4.0f; + float iconTopMargin = 2.0f; + float midSep = 2.0f; + float backgroundWidth = rect->w; + float fontScale = 0.30f; + float vPad = 0.0f; + float nameWidth = 0.5f * rect->w; + char name[ MAX_NAME_LENGTH + 2 ]; + int maxDisplayCount = 0; + int displayCount = 0; + float nameMaxX, nameMaxXCp; + float maxX = rect->x + rect->w; + float maxXCp = maxX; + weapon_t curWeapon = WP_NONE; + teamOverlayMode_t mode = cg_drawTeamOverlay.integer; + teamOverlaySort_t sort = cg_teamOverlaySortMode.integer; + int displayClients[ MAX_CLIENTS ]; + + if( cg.predictedPlayerState.pm_type == PM_SPECTATOR ) + return; + + if( mode == TEAMOVERLAY_OFF || !cg_teamOverlayMaxPlayers.integer ) + return; + + if( !cgs.teaminfoReceievedTime ) + return; + + if( cg.showScores || + cg.predictedPlayerState.pm_type == PM_INTERMISSION ) + return; + + pci = cgs.clientinfo + cg.snap->ps.clientNum; + + if( mode == TEAMOVERLAY_ALL || mode == TEAMOVERLAY_SUPPORT ) { - char c[ 2 ]; + for( i = 0; i < MAX_CLIENTS; i++ ) + { + ci = cgs.clientinfo + i; + if( ci->infoValid && pci != ci && ci->team == pci->team ) + { + if( mode == TEAMOVERLAY_ALL ) + displayClients[ maxDisplayCount++ ] = i; + else + { + if( ci->curWeaponClass == PCL_ALIEN_BUILDER0 || + ci->curWeaponClass == PCL_ALIEN_BUILDER0_UPG || + ci->curWeaponClass == PCL_ALIEN_LEVEL1 || + ci->curWeaponClass == PCL_ALIEN_LEVEL1_UPG || + ci->curWeaponClass == WP_HBUILD ) + { + displayClients[ maxDisplayCount++ ] = i; + } + } + } + } + } + else // find nearby + { + for( i = 0; i < cg.snap->numEntities; i++ ) + { + centity_t *cent = &cg_entities[ cg.snap->entities[ i ].number ]; + vec3_t relOrigin = { 0.0f, 0.0f, 0.0f }; + int team = cent->currentState.misc & 0x00FF; - c[ 0 ] = s[ i ]; - c[ 1 ] = '\0'; + if( cent->currentState.eType != ET_PLAYER || + team != pci->team || + cent->currentState.eFlags & EF_DEAD ) + { + continue; + } + + VectorSubtract( cent->lerpOrigin, cg.predictedPlayerState.origin, relOrigin ); + + if( VectorLength( relOrigin ) < HELMET_RANGE ) + displayClients[ maxDisplayCount++ ] = cg.snap->entities[ i ].number; + } + } + + // Sort + if( sort == TEAMOVERLAY_SORT_SCORE ) + { + qsort( displayClients, maxDisplayCount, + sizeof( displayClients[ 0 ] ), SortScore ); + } + else if( sort == TEAMOVERLAY_SORT_WEAPONCLASS ) + { + qsort( displayClients, maxDisplayCount, + sizeof( displayClients[ 0 ] ), SortWeaponClass ); + } + + if( maxDisplayCount > cg_teamOverlayMaxPlayers.integer ) + maxDisplayCount = cg_teamOverlayMaxPlayers.integer; + + iconSize *= scale; + leftMargin *= scale; + iconTopMargin *= scale; + midSep *= scale; + backgroundWidth *= scale; + fontScale *= scale; + nameWidth *= scale; + + vPad = ( rect->h - ( (float) maxDisplayCount * iconSize ) ) / 2.0f; + y = rect->y + vPad; + + tcolor[ 0 ] = 1.0f; + tcolor[ 1 ] = 1.0f; + tcolor[ 2 ] = 1.0f; + tcolor[ 3 ] = color[ 3 ]; - CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle ); + for( i = 0; i < MAX_CLIENTS && displayCount < maxDisplayCount; i++ ) + { + ci = cgs.clientinfo + displayClients[ i ]; + + if( !ci->infoValid || pci == ci || ci->team != pci->team ) + continue; + + Com_sprintf( name, sizeof( name ), "%s^7", ci->name ); + + trap_R_SetColor( color ); + CG_DrawPic( x, y, backgroundWidth, + iconSize, cgs.media.teamOverlayShader ); + trap_R_SetColor( tcolor ); + if( ci->health <= 0 || !ci->curWeaponClass ) + s = ""; + else + { + if( ci->team == TEAM_HUMANS ) + curWeapon = ci->curWeaponClass; + else if( ci->team == TEAM_ALIENS ) + curWeapon = BG_Class( ci->curWeaponClass )->startWeapon; + + CG_DrawPic( x + leftMargin, y, iconSize, iconSize, + cg_weapons[ curWeapon ].weaponIcon ); + if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS ) + { + if( ci->upgrade != UP_NONE ) + { + CG_DrawPic( x + iconSize + leftMargin, y, iconSize, + iconSize, cg_upgrades[ ci->upgrade ].upgradeIcon ); + } + } + else + { + if( curWeapon == WP_ABUILD2 || curWeapon == WP_ALEVEL1_UPG || + curWeapon == WP_ALEVEL2_UPG || curWeapon == WP_ALEVEL3_UPG ) + { + CG_DrawPic( x + iconSize + leftMargin, y, iconSize, + iconSize, cgs.media.upgradeClassIconShader ); + } + } + + s = va( " [^%c%3d^7] ^7%s", + CG_GetColorCharForHealth( displayClients[ i ] ), + ci->health, + CG_ConfigString( CS_LOCATIONS + ci->location ) ); + } + + trap_R_SetColor( NULL ); + nameMaxX = nameMaxXCp = x + 2.0f * iconSize + + leftMargin + midSep + nameWidth; + UI_Text_Paint_Limit( &nameMaxXCp, x + 2.0f * iconSize + leftMargin + midSep, + y + iconSize - iconTopMargin, fontScale, tcolor, name, + 0, 0 ); + + maxXCp = maxX; + + UI_Text_Paint_Limit( &maxXCp, nameMaxX, y + iconSize - iconTopMargin, + fontScale, tcolor, s, 0, 0 ); + y += iconSize; + displayCount++; } } @@ -1902,17 +1913,19 @@ CG_DrawClock ================= */ static void CG_DrawClock( rectDef_t *rect, float text_x, float text_y, - float scale, vec4_t color, int align, int textStyle ) + float scale, vec4_t color, + int textalign, int textvalign, int textStyle ) { - char *s; - int i, tx, w, totalWidth, strLength; + const char *s; + float tx, ty; + int i, strLength; + float w, h, totalWidth; qtime_t qt; - int t; if( !cg_drawClock.integer ) return; - t = trap_RealTime( &qt ); + trap_RealTime( &qt ); if( cg_drawClock.integer == 2 ) { @@ -1936,27 +1949,12 @@ static void CG_DrawClock( rectDef_t *rect, float text_x, float text_y, s = va( "%d%s%02d%s", h, ( qt.tm_sec % 2 ) ? ":" : " ", qt.tm_min, pm ); } - w = CG_Text_Width( "0", scale, 0 ); + w = UI_Text_Width( "0", scale ); + h = UI_Text_Height( "0", scale ); 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; - } + CG_AlignText( rect, s, 0.0f, totalWidth, h, textalign, textvalign, &tx, &ty ); for( i = 0; i < strLength; i++ ) { @@ -1965,7 +1963,7 @@ static void CG_DrawClock( rectDef_t *rect, float text_x, float text_y, 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 ); + UI_Text_Paint( text_x + tx + i * w, text_y + ty, scale, color, c, 0, 0, textStyle ); } } @@ -1975,39 +1973,90 @@ CG_DrawSnapshot ================== */ static void CG_DrawSnapshot( rectDef_t *rect, float text_x, float text_y, - float scale, vec4_t color, int align, int textStyle ) + float scale, vec4_t color, + int textalign, int textvalign, int textStyle ) { - char *s; - int w, tx; + const char *s; + float tx, ty; 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; + CG_AlignText( rect, s, scale, 0.0f, 0.0f, textalign, textvalign, &tx, &ty ); - case ITEM_ALIGN_RIGHT: - tx = rect->x + rect->w - w; - break; + UI_Text_Paint( text_x + tx, text_y + ty, scale, color, s, 0, 0, textStyle ); +} - case ITEM_ALIGN_CENTER: - tx = rect->x + ( rect->w / 2.0f ) - ( w / 2.0f ); - break; +/* +=============================================================================== - default: - tx = 0.0f; - } +KILLL MESSAGE + +=============================================================================== +*/ - CG_Text_Paint( text_x + tx, rect->y + text_y, scale, color, s, 0, 0, textStyle ); +/* +================== +CG_DrawKillMsg +================== +*/ +static void CG_DrawKillMsg( rectDef_t *rect, float text_x, float text_y, + float scale, vec4_t color, + int textalign, int textvalign, int textStyle ) +{ + int i; + vec4_t hcolor; + int chatHeight; + + if (cg_killMsgHeight.integer < TEAMCHAT_HEIGHT) + chatHeight = cg_killMsgHeight.integer; + else + chatHeight = TEAMCHAT_HEIGHT; + + if (chatHeight <= 0) + return; // disabled + + if (cgs.killMsgLastPos != cgs.killMsgPos) + { + if (cg.time - cgs.killMsgMsgTimes[cgs.killMsgLastPos % chatHeight] > cg_killMsgTime.integer) + cgs.killMsgLastPos++; + + hcolor[0] = hcolor[1] = hcolor[2] = hcolor[3] = 1.0f; + + for ( i = cgs.killMsgPos - 1; i >= cgs.killMsgLastPos; i-- ) + { + int x = 0, w; + int j = i % chatHeight; + + w = UI_Text_Width( cgs.killMsgKillers[j], scale ); + UI_Text_Paint( rect->x + TINYCHAR_WIDTH, + rect->y - (cgs.killMsgPos - i)*20, + scale, color, cgs.killMsgKillers[j], + 0, 0, textStyle ); + x += w + 3; + + if ( cg_weapons[cgs.killMsgWeapons[j]].weaponIcon != WP_NONE ) + { + CG_DrawPic( rect->x + TINYCHAR_WIDTH + x, + rect->y - (cgs.killMsgPos - i)*20-15, + 16, 16, + cg_weapons[cgs.killMsgWeapons[j]].weaponIcon ); + x += 16 + 2; + + w = UI_Text_Width( cgs.killMsgVictims[j], scale ); + UI_Text_Paint( rect->x + TINYCHAR_WIDTH + x, + rect->y - (cgs.killMsgPos - i)*20, + scale, color, cgs.killMsgVictims[j], + 0, 0, textStyle ); + } + } + } } + /* =============================================================================== @@ -2080,7 +2129,7 @@ void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) { previousPings[ index++ ] = cg.snap->ping; index = index % PING_FRAMES; - + for( i = 0; i < PING_FRAMES; i++ ) { cg.ping += previousPings[ i ]; @@ -2116,8 +2165,8 @@ static void CG_DrawDisconnect( void ) // 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 ); + w = UI_Text_Width( s, 0.7f ); + UI_Text_Paint( 320 - w / 2, 100, 0.7f, color, s, 0, 0, ITEM_TEXTSTYLE_SHADOWED ); // blink the icon if( ( cg.time >> 9 ) & 1 ) @@ -2147,7 +2196,7 @@ static void CG_DrawLagometer( rectDef_t *rect, float text_x, float text_y, int color; vec4_t adjustedColor; float vscale; - vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f }; + const char *ping; if( cg.snap->ps.pm_type == PM_INTERMISSION ) return; @@ -2248,103 +2297,216 @@ static void CG_DrawLagometer( rectDef_t *rect, float text_x, float text_y, } } - v = v * vscale; + 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 ) + ping = "snc"; + else + ping = va( "%d", cg.ping ); + ax = rect->x + ( rect->w / 2.0f ) - + ( UI_Text_Width( ping, scale ) / 2.0f ) + text_x; + ay = rect->y + ( rect->h / 2.0f ) + + ( UI_Text_Height( ping, scale ) / 2.0f ) + text_y; + + Vector4Copy( textColor, adjustedColor ); + adjustedColor[ 3 ] = 0.5f; + UI_Text_Paint( ax, ay, scale, adjustedColor, ping, 0, 0, + ITEM_TEXTSTYLE_NORMAL ); + + CG_DrawDisconnect( ); +} + +#define SPEEDOMETER_NUM_SAMPLES 4096 +#define SPEEDOMETER_NUM_DISPLAYED_SAMPLES 160 +#define SPEEDOMETER_DRAW_TEXT 0x1 +#define SPEEDOMETER_DRAW_GRAPH 0x2 +#define SPEEDOMETER_IGNORE_Z 0x4 +float speedSamples[ SPEEDOMETER_NUM_SAMPLES ]; +int speedSampleTimes[ SPEEDOMETER_NUM_SAMPLES ]; +// array indices +int oldestSpeedSample = 0; +int maxSpeedSample = 0; +int maxSpeedSampleInWindow = 0; + +/* +=================== +CG_AddSpeed + +append a speed to the sample history +=================== +*/ +void CG_AddSpeed( void ) +{ + float speed; + vec3_t vel; + int windowTime; + qboolean newSpeedGteMaxSpeed, newSpeedGteMaxSpeedInWindow; + + VectorCopy( cg.snap->ps.velocity, vel ); + + if( cg_drawSpeed.integer & SPEEDOMETER_IGNORE_Z ) + vel[ 2 ] = 0; + + speed = VectorLength( vel ); + + windowTime = cg_maxSpeedTimeWindow.integer; + if( windowTime < 0 ) + windowTime = 0; + else if( windowTime > SPEEDOMETER_NUM_SAMPLES * 1000 ) + windowTime = SPEEDOMETER_NUM_SAMPLES * 1000; + + if( ( newSpeedGteMaxSpeed = ( speed >= speedSamples[ maxSpeedSample ] ) ) ) + maxSpeedSample = oldestSpeedSample; - if( v > range ) - v = range; + if( ( newSpeedGteMaxSpeedInWindow = ( speed >= speedSamples[ maxSpeedSampleInWindow ] ) ) ) + maxSpeedSampleInWindow = oldestSpeedSample; - 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 ) ] ); - } + speedSamples[ oldestSpeedSample ] = speed; + speedSampleTimes[ oldestSpeedSample ] = cg.time; - trap_R_DrawStretchPic( ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader ); + if( !newSpeedGteMaxSpeed && maxSpeedSample == oldestSpeedSample ) + { + // if old max was overwritten find a new one + int i; + for( maxSpeedSample = 0, i = 1; i < SPEEDOMETER_NUM_SAMPLES; i++ ) + { + if( speedSamples[ i ] > speedSamples[ maxSpeedSample ] ) + maxSpeedSample = i; } } - 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 + if( !newSpeedGteMaxSpeedInWindow && ( maxSpeedSampleInWindow == oldestSpeedSample || + cg.time - speedSampleTimes[ maxSpeedSampleInWindow ] > windowTime ) ) { - 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 ); + int i; + do { + maxSpeedSampleInWindow = ( maxSpeedSampleInWindow + 1 ) % SPEEDOMETER_NUM_SAMPLES; + } while( cg.time - speedSampleTimes[ maxSpeedSampleInWindow ] > windowTime ); + for( i = maxSpeedSampleInWindow; ; i = ( i + 1 ) % SPEEDOMETER_NUM_SAMPLES ) + { + if( speedSamples[ i ] > speedSamples[ maxSpeedSampleInWindow ] ) + maxSpeedSampleInWindow = i; + if( i == oldestSpeedSample ) + break; + } } - CG_DrawDisconnect( ); + oldestSpeedSample = ( oldestSpeedSample + 1 ) % SPEEDOMETER_NUM_SAMPLES; } +#define SPEEDOMETER_MIN_RANGE 900 +#define SPEED_MED 1000.f +#define SPEED_FAST 1600.f + /* -============== -CG_DrawTextBlock -============== +=================== +CG_DrawSpeedGraph +=================== */ -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 ) +static void CG_DrawSpeedGraph( rectDef_t *rect, vec4_t foreColor, + vec4_t backColor ) { - 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; + int i; + float val, max, top; + // colour of graph is interpolated between these values + const vec3_t slow = { 0.0, 0.0, 1.0 }; + const vec3_t medium = { 0.0, 1.0, 0.0 }; + const vec3_t fast = { 1.0, 0.0, 0.0 }; + vec4_t color; + + max = speedSamples[ maxSpeedSample ]; + if( max < SPEEDOMETER_MIN_RANGE ) + max = SPEEDOMETER_MIN_RANGE; + + trap_R_SetColor( backColor ); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.whiteShader ); - textItem->parent = parent; - memcpy( textItem->window.foreColor, color, sizeof( vec4_t ) ); - textItem->window.flags = 0; + Vector4Copy( foreColor, color ); - switch( align ) + for( i = 1; i < SPEEDOMETER_NUM_DISPLAYED_SAMPLES; i++ ) { - case ITEM_ALIGN_LEFT: - textItem->window.rect.x = x; - break; - - case ITEM_ALIGN_RIGHT: - textItem->window.rect.x = x + w; - break; + val = speedSamples[ ( oldestSpeedSample + i + SPEEDOMETER_NUM_SAMPLES - + SPEEDOMETER_NUM_DISPLAYED_SAMPLES ) % SPEEDOMETER_NUM_SAMPLES ]; + if( val < SPEED_MED ) + VectorLerp2( val / SPEED_MED, slow, medium, color ); + else if( val < SPEED_FAST ) + VectorLerp2( ( val - SPEED_MED ) / ( SPEED_FAST - SPEED_MED ), + medium, fast, color ); + else + VectorCopy( fast, color ); + trap_R_SetColor( color ); + top = rect->y + ( 1 - val / max ) * rect->h; + CG_DrawPic( rect->x + ( i / (float)SPEEDOMETER_NUM_DISPLAYED_SAMPLES ) * rect->w, top, + rect->w / (float)SPEEDOMETER_NUM_DISPLAYED_SAMPLES, val * rect->h / max, + cgs.media.whiteShader ); + } + trap_R_SetColor( NULL ); +} - case ITEM_ALIGN_CENTER: - textItem->window.rect.x = x + ( w / 2 ); - break; +/* +=================== +CG_DrawSpeedText +=================== +*/ +static void CG_DrawSpeedText( rectDef_t *rect, float text_x, float text_y, + float scale, vec4_t foreColor ) +{ + char speedstr[ 16 ]; + float val; + vec4_t color; - default: - textItem->window.rect.x = x; - break; + VectorCopy( foreColor, color ); + color[ 3 ] = 1; + if( cg.predictedPlayerState.clientNum == cg.clientNum ) + { + vec3_t vel; + VectorCopy( cg.predictedPlayerState.velocity, vel ); + if( cg_drawSpeed.integer & SPEEDOMETER_IGNORE_Z ) + vel[ 2 ] = 0; + val = VectorLength( vel ); } + else + val = speedSamples[ ( oldestSpeedSample - 1 + SPEEDOMETER_NUM_SAMPLES ) % SPEEDOMETER_NUM_SAMPLES ]; - 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; + Com_sprintf( speedstr, sizeof( speedstr ), "%d / %d", (int)val, (int)speedSamples[ maxSpeedSampleInWindow ] ); - //hack to utilise existing autowrap code - Item_Text_AutoWrapped_Paint( textItem ); + UI_Text_Paint( + rect->x + ( rect->w - UI_Text_Width( speedstr, scale ) ) / 2.0f, + rect->y + ( rect->h + UI_Text_Height( speedstr, scale ) ) / 2.0f, + scale, color, speedstr, 0, 0, ITEM_TEXTSTYLE_NORMAL ); +} + +/* +=================== +CG_DrawSpeed +=================== +*/ +static void CG_DrawSpeed( rectDef_t *rect, float text_x, float text_y, + float scale, vec4_t foreColor, vec4_t backColor ) +{ + if( cg_drawSpeed.integer & SPEEDOMETER_DRAW_GRAPH ) + CG_DrawSpeedGraph( rect, foreColor, backColor ); + if( cg_drawSpeed.integer & SPEEDOMETER_DRAW_TEXT ) + CG_DrawSpeedText( rect, text_x, text_y, scale, foreColor ); } /* @@ -2353,13 +2515,9 @@ CG_DrawConsole =================== */ static void CG_DrawConsole( rectDef_t *rect, float text_x, float text_y, vec4_t color, - float scale, int align, int textStyle ) + float scale, int textalign, int textvalign, 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 ); + UI_DrawTextBlock( rect, text_x, text_y, color, scale, textalign, textvalign, textStyle, cg.consoleText ); } /* @@ -2368,16 +2526,12 @@ CG_DrawTutorial =================== */ static void CG_DrawTutorial( rectDef_t *rect, float text_x, float text_y, vec4_t color, - float scale, int align, int textStyle ) + float scale, int textalign, int textvalign, 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 ); + UI_DrawTextBlock( rect, text_x, text_y, color, scale, textalign, textvalign, textStyle, CG_TutorialText( ) ); } /* @@ -2387,29 +2541,34 @@ CG_DrawWeaponIcon */ void CG_DrawWeaponIcon( rectDef_t *rect, vec4_t color ) { - int ammo, clips, maxAmmo; - centity_t *cent; + int maxAmmo; playerState_t *ps; + weapon_t weapon; - cent = &cg_entities[ cg.snap->ps.clientNum ]; ps = &cg.snap->ps; + weapon = BG_GetPlayerWeapon( ps ); - ammo = ps->ammo; - clips = ps->clips; - BG_FindAmmoForWeapon( cent->currentState.weapon, &maxAmmo, NULL ); + maxAmmo = BG_Weapon( weapon )->maxAmmo; // don't display if dead if( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 ) return; - if( cent->currentState.weapon == 0 ) + if( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) + { return; + } - CG_RegisterWeapon( cent->currentState.weapon ); + if( !cg_weapons[ weapon ].registered ) + { + Com_Printf( S_COLOR_YELLOW "WARNING: CG_DrawWeaponIcon: weapon %d (%s) " + "is not registered\n", weapon, BG_Weapon( weapon )->name ); + return; + } - if( clips == 0 && !BG_FindInfinteAmmoForWeapon( cent->currentState.weapon ) ) + if( ps->clips == 0 && !BG_Weapon( weapon )->infiniteAmmo ) { - float ammoPercent = (float)ammo / (float)maxAmmo; + float ammoPercent = (float)ps->ammo / (float)maxAmmo; if( ammoPercent < 0.33f ) { @@ -2418,7 +2577,9 @@ void CG_DrawWeaponIcon( rectDef_t *rect, vec4_t color ) } } - if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS && CG_AtHighestClass( ) ) + if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS && + !BG_AlienCanEvolve( cg.predictedPlayerState.stats[ STAT_CLASS ], + ps->persistant[ PERS_CREDIT ], cgs.alienStage ) ) { if( cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME ) { @@ -2428,7 +2589,8 @@ void CG_DrawWeaponIcon( rectDef_t *rect, vec4_t color ) } trap_R_SetColor( color ); - CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_weapons[ cent->currentState.weapon ].weaponIcon ); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, + cg_weapons[ weapon ].weaponIcon ); trap_R_SetColor( NULL ); } @@ -2443,50 +2605,65 @@ CROSSHAIR */ + /* ================= CG_DrawCrosshair ================= */ -static void CG_DrawCrosshair( void ) +static void CG_DrawCrosshair( rectDef_t *rect, vec4_t color ) { float w, h; qhandle_t hShader; float x, y; weaponInfo_t *wi; + weapon_t weapon; + + weapon = BG_GetPlayerWeapon( &cg.snap->ps ); if( cg_drawCrosshair.integer == CROSSHAIR_ALWAYSOFF ) return; if( cg_drawCrosshair.integer == CROSSHAIR_RANGEDONLY && - !BG_FindLongRangedForWeapon( cg.snap->ps.weapon ) ) - { + !BG_Weapon( weapon )->longRanged ) 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 ) ) + if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ) return; if( cg.renderingThirdPerson ) return; - wi = &cg_weapons[ cg.snap->ps.weapon ]; + if( cg.snap->ps.pm_type == PM_INTERMISSION ) + return; + + wi = &cg_weapons[ weapon ]; - w = h = wi->crossHairSize; + w = h = wi->crossHairSize * cg_crosshairSize.value; + w *= cgDC.aspectScale; - x = cg_crosshairX.integer; - y = cg_crosshairY.integer; - CG_AdjustFrom640( &x, &y, &w, &h ); + //FIXME: this still ignores the width/height of the rect, but at least it's + //neater than cg_crosshairX/cg_crosshairY + x = rect->x + ( rect->w / 2 ) - ( w / 2 ); + y = rect->y + ( rect->h / 2 ) - ( h / 2 ); hShader = wi->crossHair; + //aiming at a friendly player/buildable, dim the crosshair + if( cg.time == cg.crosshairClientTime || cg.crosshairBuildable >= 0 ) + { + int i; + for( i = 0; i < 3; i++ ) + color[i] *= .5f; + + } + 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 ); + + trap_R_SetColor( color ); + CG_DrawPic( x, y, w, h, hShader ); + trap_R_SetColor( NULL ); } } @@ -2502,7 +2679,7 @@ static void CG_ScanForCrosshairEntity( void ) trace_t trace; vec3_t start, end; int content; - pTeam_t team; + team_t team; VectorCopy( cg.refdef.vieworg, start ); VectorMA( start, 131072, cg.refdef.viewaxis[ 0 ], end ); @@ -2510,20 +2687,29 @@ static void CG_ScanForCrosshairEntity( void ) 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; + if( trace.entityNum >= MAX_CLIENTS ) + { + entityState_t *s = &cg_entities[ trace.entityNum ].currentState; + if( s->eType == ET_BUILDABLE && BG_Buildable( s->modelindex )->team == + cg.snap->ps.stats[ STAT_TEAM ] ) + cg.crosshairBuildable = trace.entityNum; + else + cg.crosshairBuildable = -1; + + return; + } + team = cgs.clientinfo[ trace.entityNum ].team; - if( cg.snap->ps.persistant[ PERS_TEAM ] != TEAM_SPECTATOR ) + if( cg.snap->ps.stats[ STAT_TEAM ] != TEAM_NONE ) { //only display team names of those on the same team as this player - if( team != cg.snap->ps.stats[ STAT_PTEAM ] ) + if( team != cg.snap->ps.stats[ STAT_TEAM ] ) return; } @@ -2533,6 +2719,44 @@ static void CG_ScanForCrosshairEntity( void ) } +/* +===================== +CG_DrawLocation +===================== +*/ +static void CG_DrawLocation( rectDef_t *rect, float scale, int textalign, vec4_t color ) +{ + const char *location; + centity_t *locent; + float maxX; + float tx = rect->x, ty = rect->y; + + if( cg.intermissionStarted ) + return; + + maxX = rect->x + rect->w; + + locent = CG_GetPlayerLocation( ); + if( locent ) + location = CG_ConfigString( CS_LOCATIONS + locent->currentState.generic1 ); + else + location = CG_ConfigString( CS_LOCATIONS ); + + // need to skip horiz. align if it's too long, but valign must be run either way + if( UI_Text_Width( location, scale ) < rect->w ) + { + CG_AlignText( rect, location, scale, 0.0f, 0.0f, textalign, VALIGN_CENTER, &tx, &ty ); + UI_Text_Paint( tx, ty, scale, color, location, 0, 0, ITEM_TEXTSTYLE_NORMAL ); + } + else + { + CG_AlignText( rect, location, scale, 0.0f, 0.0f, ALIGN_NONE, VALIGN_CENTER, &tx, &ty ); + UI_Text_Paint_Limit( &maxX, tx, ty, scale, color, location, 0, 0 ); + } + + trap_R_SetColor( NULL ); +} + /* ===================== CG_DrawCrosshairNames @@ -2541,7 +2765,7 @@ CG_DrawCrosshairNames static void CG_DrawCrosshairNames( rectDef_t *rect, float scale, int textStyle ) { float *color; - char *name; + const char *name; float w, x; if( !cg_drawCrosshairNames.integer ) @@ -2554,21 +2778,31 @@ static void CG_DrawCrosshairNames( rectDef_t *rect, float scale, int textStyle ) CG_ScanForCrosshairEntity( ); // draw the name of the player being looked at - color = CG_FadeColor( cg.crosshairClientTime, 1000 ); + color = CG_FadeColor( cg.crosshairClientTime, CROSSHAIR_CLIENT_TIMEOUT ); if( !color ) { trap_R_SetColor( NULL ); return; } + // add health from overlay info to the crosshair client name 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 ); + if( cg_teamOverlayUserinfo.integer && + cg.snap->ps.stats[ STAT_TEAM ] != TEAM_NONE && + cgs.teaminfoReceievedTime && + cgs.clientinfo[ cg.crosshairClientNum ].health > 0 ) + { + name = va( "%s ^7[^%c%d^7]", name, + CG_GetColorCharForHealth( cg.crosshairClientNum ), + cgs.clientinfo[ cg.crosshairClientNum ].health ); + } + + w = UI_Text_Width( name, scale ); + x = rect->x + rect->w / 2.0f; + UI_Text_Paint( x - w / 2.0f, rect->y + rect->h, scale, color, name, 0, 0, textStyle ); trap_R_SetColor( NULL ); } - /* =============== CG_OwnerDraw @@ -2578,14 +2812,12 @@ 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, + int align, int textalign, int textvalign, float borderSize, + float scale, vec4_t foreColor, vec4_t backColor, qhandle_t shader, int textStyle ) { rectDef_t rect; - if( cg_drawStatus.integer == 0 ) - return; - rect.x = x; rect.y = y; rect.w = w; @@ -2594,100 +2826,107 @@ void CG_OwnerDraw( float x, float y, float w, float h, float text_x, switch( ownerDraw ) { case CG_PLAYER_CREDITS_VALUE: - CG_DrawPlayerCreditsValue( &rect, color, qtrue ); + CG_DrawPlayerCreditsValue( &rect, foreColor, qtrue ); break; - case CG_PLAYER_BANK_VALUE: - CG_DrawPlayerBankValue( &rect, color, qtrue ); + case CG_PLAYER_CREDITS_FRACTION: + CG_DrawPlayerCreditsFraction( &rect, foreColor, shader ); 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 ); + CG_DrawPlayerCreditsValue( &rect, foreColor, qfalse ); 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 ); + CG_DrawPlayerStamina( ownerDraw, &rect, backColor, foreColor, shader ); break; case CG_PLAYER_STAMINA_BOLT: - CG_DrawPlayerStaminaBolt( &rect, color, shader ); + CG_DrawPlayerStaminaBolt( &rect, backColor, foreColor, shader ); break; case CG_PLAYER_AMMO_VALUE: - CG_DrawPlayerAmmoValue( &rect, color ); + CG_DrawPlayerAmmoValue( &rect, foreColor ); break; case CG_PLAYER_CLIPS_VALUE: - CG_DrawPlayerClipsValue( &rect, color ); + CG_DrawPlayerClipsValue( &rect, foreColor ); break; case CG_PLAYER_BUILD_TIMER: - CG_DrawPlayerBuildTimer( &rect, color ); + CG_DrawPlayerBuildTimer( &rect, foreColor ); break; case CG_PLAYER_HEALTH: - CG_DrawPlayerHealthValue( &rect, color ); - break; - case CG_PLAYER_HEALTH_BAR: - CG_DrawPlayerHealthBar( &rect, color, scale, align, textStyle, special ); + CG_DrawPlayerHealthValue( &rect, foreColor ); break; case CG_PLAYER_HEALTH_CROSS: - CG_DrawPlayerHealthCross( &rect, color, shader ); + CG_DrawPlayerHealthCross( &rect, foreColor ); + break; + case CG_PLAYER_CHARGE_BAR_BG: + CG_DrawPlayerChargeBarBG( &rect, foreColor, shader ); + break; + case CG_PLAYER_CHARGE_BAR: + CG_DrawPlayerChargeBar( &rect, foreColor, shader ); break; case CG_PLAYER_CLIPS_RING: - CG_DrawPlayerClipsRing( &rect, color, shader ); + CG_DrawPlayerClipsRing( &rect, backColor, foreColor, shader ); break; case CG_PLAYER_BUILD_TIMER_RING: - CG_DrawPlayerBuildTimerRing( &rect, color, shader ); + CG_DrawPlayerBuildTimerRing( &rect, backColor, foreColor, shader ); break; case CG_PLAYER_WALLCLIMBING: - CG_DrawPlayerWallclimbing( &rect, color, shader ); + CG_DrawPlayerWallclimbing( &rect, backColor, foreColor, shader ); break; case CG_PLAYER_BOOSTED: - CG_DrawPlayerBoosted( &rect, color, shader ); + CG_DrawPlayerBoosted( &rect, backColor, foreColor, shader ); break; case CG_PLAYER_BOOST_BOLT: - CG_DrawPlayerBoosterBolt( &rect, color, shader ); + CG_DrawPlayerBoosterBolt( &rect, backColor, foreColor, shader ); break; case CG_PLAYER_POISON_BARBS: - CG_DrawPlayerPoisonBarbs( &rect, color, shader ); + CG_DrawPlayerPoisonBarbs( &rect, foreColor, shader ); break; case CG_PLAYER_ALIEN_SENSE: CG_DrawAlienSense( &rect ); break; case CG_PLAYER_HUMAN_SCANNER: - CG_DrawHumanScanner( &rect, shader, color ); + CG_DrawHumanScanner( &rect, shader, foreColor ); break; case CG_PLAYER_USABLE_BUILDABLE: - CG_DrawUsableBuildable( &rect, shader, color ); + CG_DrawUsableBuildable( &rect, shader, foreColor ); break; case CG_KILLER: - CG_DrawKiller( &rect, scale, color, shader, textStyle ); + CG_DrawKiller( &rect, scale, foreColor, shader, textStyle ); break; case CG_PLAYER_SELECT: - CG_DrawItemSelect( &rect, color ); + CG_DrawItemSelect( &rect, foreColor ); break; case CG_PLAYER_WEAPONICON: - CG_DrawWeaponIcon( &rect, color ); + CG_DrawWeaponIcon( &rect, foreColor ); break; case CG_PLAYER_SELECTTEXT: CG_DrawItemSelectText( &rect, scale, textStyle ); break; case CG_SPECTATORS: - CG_DrawTeamSpectators( &rect, scale, color, shader ); + CG_DrawTeamSpectators( &rect, scale, textvalign, foreColor, shader ); + break; + case CG_PLAYER_LOCATION: + CG_DrawLocation( &rect, scale, textalign, foreColor ); + break; + case CG_FOLLOW: + CG_DrawFollow( &rect, text_x, text_y, foreColor, scale, + textalign, textvalign, textStyle ); break; case CG_PLAYER_CROSSHAIRNAMES: CG_DrawCrosshairNames( &rect, scale, textStyle ); break; + case CG_PLAYER_CROSSHAIR: + CG_DrawCrosshair( &rect, foreColor ); + break; case CG_STAGE_REPORT_TEXT: - CG_DrawStageReport( &rect, text_x, text_y, color, scale, align, textStyle ); + CG_DrawStageReport( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle ); + break; + case CG_ALIENS_SCORE_LABEL: + CG_DrawTeamLabel( &rect, TEAM_ALIENS, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle ); + break; + case CG_HUMANS_SCORE_LABEL: + CG_DrawTeamLabel( &rect, TEAM_HUMANS, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle ); break; //loading screen @@ -2695,74 +2934,92 @@ void CG_OwnerDraw( float x, float y, float w, float h, float text_x, CG_DrawLevelShot( &rect ); break; case CG_LOAD_MEDIA: - CG_DrawMediaProgress( &rect, color, scale, align, textStyle, special ); + CG_DrawMediaProgress( &rect, foreColor, scale, align, textalign, textStyle, + borderSize ); break; case CG_LOAD_MEDIA_LABEL: - CG_DrawMediaProgressLabel( &rect, text_x, text_y, color, scale, align ); + CG_DrawMediaProgressLabel( &rect, text_x, text_y, foreColor, scale, textalign, textvalign ); break; case CG_LOAD_BUILDABLES: - CG_DrawBuildablesProgress( &rect, color, scale, align, textStyle, special ); + CG_DrawBuildablesProgress( &rect, foreColor, scale, align, textalign, + textStyle, borderSize ); break; case CG_LOAD_BUILDABLES_LABEL: - CG_DrawBuildablesProgressLabel( &rect, text_x, text_y, color, scale, align ); + CG_DrawBuildablesProgressLabel( &rect, text_x, text_y, foreColor, scale, textalign, textvalign ); break; case CG_LOAD_CHARMODEL: - CG_DrawCharModelProgress( &rect, color, scale, align, textStyle, special ); + CG_DrawCharModelProgress( &rect, foreColor, scale, align, textalign, + textStyle, borderSize ); break; case CG_LOAD_CHARMODEL_LABEL: - CG_DrawCharModelProgressLabel( &rect, text_x, text_y, color, scale, align ); + CG_DrawCharModelProgressLabel( &rect, text_x, text_y, foreColor, scale, textalign, textvalign ); break; case CG_LOAD_OVERALL: - CG_DrawOverallProgress( &rect, color, scale, align, textStyle, special ); + CG_DrawOverallProgress( &rect, foreColor, scale, align, textalign, textStyle, + borderSize ); break; case CG_LOAD_LEVELNAME: - CG_DrawLevelName( &rect, text_x, text_y, color, scale, align, textStyle ); + CG_DrawLevelName( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle ); break; case CG_LOAD_MOTD: - CG_DrawMOTD( &rect, text_x, text_y, color, scale, align, textStyle ); + CG_DrawMOTD( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle ); break; case CG_LOAD_HOSTNAME: - CG_DrawHostname( &rect, text_x, text_y, color, scale, align, textStyle ); + CG_DrawHostname( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle ); break; case CG_FPS: - CG_DrawFPS( &rect, text_x, text_y, scale, color, align, textStyle, qtrue ); + CG_DrawFPS( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle, qtrue ); break; case CG_FPS_FIXED: - CG_DrawFPS( &rect, text_x, text_y, scale, color, align, textStyle, qfalse ); + CG_DrawFPS( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle, qfalse ); break; case CG_TIMER: - CG_DrawTimer( &rect, text_x, text_y, scale, color, align, textStyle ); + CG_DrawTimer( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle ); break; case CG_CLOCK: - CG_DrawClock( &rect, text_x, text_y, scale, color, align, textStyle ); + CG_DrawClock( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle ); break; case CG_TIMER_MINS: - CG_DrawTimerMins( &rect, color ); + CG_DrawTimerMins( &rect, foreColor ); break; case CG_TIMER_SECS: - CG_DrawTimerSecs( &rect, color ); + CG_DrawTimerSecs( &rect, foreColor ); break; case CG_SNAPSHOT: - CG_DrawSnapshot( &rect, text_x, text_y, scale, color, align, textStyle ); + CG_DrawSnapshot( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle ); break; case CG_LAGOMETER: - CG_DrawLagometer( &rect, text_x, text_y, scale, color ); + CG_DrawLagometer( &rect, text_x, text_y, scale, foreColor ); + break; + case CG_TEAMOVERLAY: + CG_DrawTeamOverlay( &rect, scale, foreColor ); + break; + case CG_SPEEDOMETER: + CG_DrawSpeed( &rect, text_x, text_y, scale, foreColor, backColor ); break; case CG_DEMO_PLAYBACK: - CG_DrawDemoPlayback( &rect, color, shader ); + CG_DrawDemoPlayback( &rect, foreColor, shader ); break; case CG_DEMO_RECORDING: - CG_DrawDemoRecording( &rect, color, shader ); + CG_DrawDemoRecording( &rect, foreColor, shader ); break; case CG_CONSOLE: - CG_DrawConsole( &rect, text_x, text_y, color, scale, align, textStyle ); + CG_DrawConsole( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle ); break; case CG_TUTORIAL: - CG_DrawTutorial( &rect, text_x, text_y, color, scale, align, textStyle ); + CG_DrawTutorial( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle ); + break; + + case CG_KILLFEED: + CG_DrawKillMsg( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle ); + break; + + case CG_PLAYER_THZ_SCANNER: + THZ_DrawScanner( &rect ); break; default: @@ -2827,17 +3084,17 @@ CG_ShowTeamMenus */ void CG_ShowTeamMenu( void ) { - Menus_OpenByName( "teamMenu" ); + Menus_ActivateByName( "teamMenu" ); } /* ================== CG_EventHandling -================== - type 0 - no event handling - 1 - team menu - 2 - hud editor +type 0 - no event handling + 1 - team menu + 2 - hud editor +================== */ void CG_EventHandling( int type ) { @@ -2891,14 +3148,6 @@ int CG_ClientNumFromName( const char *p ) 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 @@ -2910,13 +3159,9 @@ 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 ) ) + if( ( cg.snap->ps.stats[ STAT_STAMINA ] < STAMINA_BLACKOUT_LEVEL ) && + ( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) ) { vec4_t black = { 0, 0, 0, 0 }; black[ 3 ] = 1.0 - ( (float)( cg.snap->ps.stats[ STAT_STAMINA ] + 1000 ) / 200.0f ); @@ -2946,8 +3191,15 @@ for a few moments void CG_CenterPrint( const char *str, int y, int charWidth ) { char *s; + char newlineParsed[ MAX_STRING_CHARS ]; + const char *wrapped; + static int maxWidth = (int)( ( 2.0f / 3.0f ) * (float)SCREEN_WIDTH ); + + Q_ParseNewlines( newlineParsed, str, sizeof( newlineParsed ) ); - Q_strncpyz( cg.centerPrint, str, sizeof( cg.centerPrint ) ); + wrapped = Item_Text_Wrap( newlineParsed, 0.5f, maxWidth ); + + Q_strncpyz( cg.centerPrint, wrapped, sizeof( cg.centerPrint ) ); cg.centerPrintTime = cg.time; cg.centerPrintY = y; @@ -2994,9 +3246,9 @@ static void CG_DrawCenterString( void ) while( 1 ) { - char linebuffer[ 1024 ]; + char linebuffer[ MAX_STRING_CHARS ]; - for( l = 0; l < 50; l++ ) + for( l = 0; l < sizeof(linebuffer) - 1; l++ ) { if( !start[ l ] || start[ l ] == '\n' ) break; @@ -3006,10 +3258,10 @@ static void CG_DrawCenterString( void ) linebuffer[ l ] = 0; - w = CG_Text_Width( linebuffer, 0.5, 0 ); - h = CG_Text_Height( linebuffer, 0.5, 0 ); + w = UI_Text_Width( linebuffer, 0.5 ); + h = UI_Text_Height( linebuffer, 0.5 ); x = ( SCREEN_WIDTH - w ) / 2; - CG_Text_Paint( x, y + h, 0.5, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE ); + UI_Text_Paint( x, y + h, 0.5, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE ); y += h + 6; while( *start && ( *start != '\n' ) ) @@ -3037,83 +3289,62 @@ static void CG_DrawCenterString( void ) CG_DrawVote ================= */ -static void CG_DrawVote( void ) +static void CG_DrawVote( team_t team ) { - char *s; + const char *s; int sec; + int offset = 0; vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f }; - char yeskey[ 32 ], nokey[ 32 ]; + char yeskey[ 32 ] = "", nokey[ 32 ] = ""; - if( !cgs.voteTime ) + if( !cgs.voteTime[ team ] ) return; // play a talk beep whenever it is modified - if( cgs.voteModified ) + if( cgs.voteModified[ team ] ) { - cgs.voteModified = qfalse; + cgs.voteModified[ team ] = qfalse; trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); } - sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000; + sec = ( VOTE_TIME - ( cg.time - cgs.voteTime[ team ] ) ) / 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_tutorial.integer ) + { + Com_sprintf( yeskey, sizeof( yeskey ), "[%s]", + CG_KeyBinding( va( "%svote yes", team == TEAM_NONE ? "" : "team" ) ) ); + Com_sprintf( nokey, sizeof( nokey ), "[%s]", + CG_KeyBinding( va( "%svote no", team == TEAM_NONE ? "" : "team" ) ) ); + } - 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( team != TEAM_NONE ) + offset = 80; - if( !cgs.teamVoteTime[ cs_offset ] ) - return; + s = va( "%sVOTE(%i): %s", + team == TEAM_NONE ? "" : "TEAM", sec, cgs.voteString[ team ] ); - // 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 ); - } + UI_Text_Paint( 8, 300 + offset, 0.3f, white, s, 0, 0, + ITEM_TEXTSTYLE_NORMAL ); - sec = ( VOTE_TIME - ( cg.time - cgs.teamVoteTime[ cs_offset ] ) ) / 1000; + s = va( " Called by: \"%s\"", cgs.voteCaller[ team ] ); - if( sec < 0 ) - sec = 0; + UI_Text_Paint( 8, 320 + offset, 0.3f, white, s, 0, 0, + ITEM_TEXTSTYLE_NORMAL ); - 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 ] ); + s = va( " %sYes:%i %sNo:%i", + yeskey, cgs.voteYes[ team ], nokey, cgs.voteNo[ team ] ); - CG_Text_Paint( 8, 360, 0.3f, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL ); + UI_Text_Paint( 8, 340 + offset, 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; @@ -3125,13 +3356,8 @@ static qboolean CG_DrawScoreboard( void ) return qfalse; } - if( cg.showScores || - cg.predictedPlayerState.pm_type == PM_INTERMISSION ) - { - fade = 1.0; - fadeColor = colorWhite; - } - else + if( !cg.showScores && + cg.predictedPlayerState.pm_type != PM_INTERMISSION ) { cg.deferredPlayerLoading = 0; cg.killerName[ 0 ] = 0; @@ -3139,6 +3365,7 @@ static qboolean CG_DrawScoreboard( void ) return qfalse; } + CG_RequestScores( ); if( menuScoreboard == NULL ) menuScoreboard = Menus_FindByName( "teamscore_menu" ); @@ -3147,10 +3374,12 @@ static qboolean CG_DrawScoreboard( void ) { if( firstTime ) { + cg.spectatorTime = trap_Milliseconds(); CG_SetScoreSelection( menuScoreboard ); firstTime = qfalse; } + Menu_Update( menuScoreboard ); Menu_Paint( menuScoreboard, qtrue ); } @@ -3164,27 +3393,28 @@ CG_DrawIntermission */ static void CG_DrawIntermission( void ) { - if( cg_drawStatus.integer ) - Menu_Paint( Menus_FindByName( "default_hud" ), qtrue ); + menuDef_t *menu = Menus_FindByName( "default_hud" ); + + Menu_Update( menu ); + Menu_Paint( menu, qtrue ); cg.scoreFadeTime = cg.time; cg.scoreBoardShowing = CG_DrawScoreboard( ); } -#define FOLLOWING_STRING "following " - /* ================= -CG_DrawFollow +CG_DrawQueue ================= */ -static qboolean CG_DrawFollow( void ) +static qboolean CG_DrawQueue( void ) { float w; vec4_t color; - char buffer[ MAX_STRING_CHARS ]; + int position; + char *ordinal, buffer[ MAX_STRING_CHARS ]; - if( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) + if( !( cg.snap->ps.pm_flags & PMF_QUEUED ) ) return qfalse; color[ 0 ] = 1; @@ -3192,66 +3422,82 @@ static qboolean CG_DrawFollow( void ) color[ 2 ] = 1; color[ 3 ] = 1; - strcpy( buffer, FOLLOWING_STRING ); - strcat( buffer, cgs.clientinfo[ cg.snap->ps.clientNum ].name ); + position = cg.snap->ps.persistant[ PERS_QUEUEPOS ] + 1; + if( position < 1 ) + return qfalse; + + switch( position % 100 ) + { + case 11: + case 12: + case 13: + ordinal = "th"; + break; + default: + switch( position % 10 ) + { + case 1: ordinal = "st"; break; + case 2: ordinal = "nd"; break; + case 3: ordinal = "rd"; break; + default: ordinal = "th"; break; + } + break; + } + + Com_sprintf( buffer, MAX_STRING_CHARS, "You are %d%s in the spawn queue", + position, ordinal ); + + w = UI_Text_Width( buffer, 0.7f ); + UI_Text_Paint( 320 - w / 2, 360, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED ); + + if( cg.snap->ps.persistant[ PERS_SPAWNS ] == 0 ) + Com_sprintf( buffer, MAX_STRING_CHARS, "There are no spawns remaining" ); + else if( cg.snap->ps.persistant[ PERS_SPAWNS ] == 1 ) + Com_sprintf( buffer, MAX_STRING_CHARS, "There is 1 spawn remaining" ); + else + Com_sprintf( buffer, MAX_STRING_CHARS, "There are %d spawns remaining", + cg.snap->ps.persistant[ PERS_SPAWNS ] ); - w = CG_Text_Width( buffer, 0.7f, 0 ); - CG_Text_Paint( 320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED ); + w = UI_Text_Width( buffer, 0.7f ); + UI_Text_Paint( 320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED ); return qtrue; } + /* ================= -CG_DrawQueue +CG_DrawWarmup ================= */ -static qboolean CG_DrawQueue( void ) +static void CG_DrawWarmup( void ) { - float w; - vec4_t color; - char buffer[ MAX_STRING_CHARS ]; - - if( !( cg.snap->ps.pm_flags & PMF_QUEUED ) ) - return qfalse; + int sec = 0; + int w; + int h; + float size = 0.5f; + char text[ MAX_STRING_CHARS ] = "Warmup Time:"; - color[ 0 ] = 1; - color[ 1 ] = 1; - color[ 2 ] = 1; - color[ 3 ] = 1; + if( !cg.warmupTime ) + return; - Com_sprintf( buffer, MAX_STRING_CHARS, "You are in position %d of the spawn queue.", - cg.snap->ps.persistant[ PERS_QUEUEPOS ] + 1 ); + sec = ( cg.warmupTime - cg.time ) / 1000; - 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( sec < 0 ) + return; - 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 = UI_Text_Width( text, size ); + h = UI_Text_Height( text, size ); + UI_Text_Paint( 320 - w / 2, 200, size, colorWhite, text, 0, 0, ITEM_TEXTSTYLE_SHADOWED ); - w = CG_Text_Width( buffer, 0.7f, 0 ); - CG_Text_Paint( 320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED ); + Com_sprintf( text, sizeof( text ), "%s", sec ? va( "%d", sec ) : "FIGHT!" ); - return qtrue; + w = UI_Text_Width( text, size ); + UI_Text_Paint( 320 - w / 2, 200 + 1.5f * h, size, colorWhite, text, 0, 0, ITEM_TEXTSTYLE_SHADOWED ); } //================================================================================== -#define SPECTATOR_STRING "SPECTATOR" /* ================= CG_Draw2D @@ -3259,15 +3505,11 @@ CG_Draw2D */ static void CG_Draw2D( void ) { - vec4_t color; - float w; - menuDef_t *menu = NULL, *defaultMenu; + menuDef_t *menu = NULL; - 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; + // fading to black if stamina runs out + // (only 2D that can't be disabled) + CG_DrawLighting( ); if( cg_draw2D.integer == 0 ) return; @@ -3278,36 +3520,30 @@ static void CG_Draw2D( void ) 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 ) + if ( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_NONE + || (cg.snap->ps.persistant[ PERS_SPECSTATE ] == SPECTATOR_NOT + && cg.snap->ps.stats[ STAT_HEALTH ] > 0 )) { - 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 ); + menu = Menus_FindByName( BG_ClassConfig( + cg.predictedPlayerState.stats[ STAT_CLASS ] )->hudName ); + + CG_DrawBuildableStatus( ); } - 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 ) ) + if( !menu ) { - CG_DrawBuildableStatus( ); - if( cg_drawStatus.integer ) - Menu_Paint( menu, qtrue ); + menu = Menus_FindByName( "default_hud" ); - CG_DrawCrosshair( ); + if( !menu ) // still couldn't find it + CG_Error( "Default HUD could not be found" ); } - else if( cg_drawStatus.integer ) - Menu_Paint( defaultMenu, qtrue ); - CG_DrawVote( ); - CG_DrawTeamVote( ); - CG_DrawFollow( ); + Menu_Update( menu ); + Menu_Paint( menu, qtrue ); + + CG_DrawVote( TEAM_NONE ); + CG_DrawVote( cg.predictedPlayerState.stats[ STAT_TEAM ] ); + CG_DrawWarmup( ); CG_DrawQueue( ); // don't draw center string if scoreboard is up @@ -3356,7 +3592,7 @@ static void CG_PainBlend( void ) float x, y, w, h; float s1, t1, s2, t2; - if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR || cg.intermissionStarted ) + if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT || cg.intermissionStarted ) return; damage = cg.lastHealth - cg.snap->ps.stats[ STAT_HEALTH ]; @@ -3383,9 +3619,9 @@ static void CG_PainBlend( void ) return; } - if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) + if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) VectorSet( color, 0.43f, 0.8f, 0.37f ); - else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) + else if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) VectorSet( color, 0.8f, 0.0f, 0.0f ); if( cg.painBlendValue > cg.painBlendTarget ) @@ -3454,6 +3690,66 @@ void CG_ResetPainBlend( void ) cg.lastHealth = cg.snap->ps.stats[ STAT_HEALTH ]; } +/* +================ +CG_DrawBinaryShadersFinalPhases +================ +*/ +static void CG_DrawBinaryShadersFinalPhases( void ) +{ + float ss, f, l, u; + polyVert_t verts[ 4 ] = { + { { 0, 0, 0 }, { 0, 0 }, { 255, 255, 255, 255 } }, + { { 0, 0, 0 }, { 1, 0 }, { 255, 255, 255, 255 } }, + { { 0, 0, 0 }, { 1, 1 }, { 255, 255, 255, 255 } }, + { { 0, 0, 0 }, { 0, 1 }, { 255, 255, 255, 255 } } + }; + int i, j, k; + + if( !cg.numBinaryShadersUsed ) + return; + + ss = cg_binaryShaderScreenScale.value; + if( ss <= 0.0f ) + { + cg.numBinaryShadersUsed = 0; + return; + } + else if( ss > 1.0f ) + ss = 1.0f; + + ss = sqrt( ss ); + + f = 1.01f; // FIXME: is this a good choice to avoid near-clipping? + l = f * tan( DEG2RAD( cg.refdef.fov_x / 2 ) ) * ss; + u = f * tan( DEG2RAD( cg.refdef.fov_y / 2 ) ) * ss; + + VectorMA( cg.refdef.vieworg, f, cg.refdef.viewaxis[ 0 ], verts[ 0 ].xyz ); + VectorMA( verts[ 0 ].xyz, l, cg.refdef.viewaxis[ 1 ], verts[ 0 ].xyz ); + VectorMA( verts[ 0 ].xyz, u, cg.refdef.viewaxis[ 2 ], verts[ 0 ].xyz ); + VectorMA( verts[ 0 ].xyz, -2*l, cg.refdef.viewaxis[ 1 ], verts[ 1 ].xyz ); + VectorMA( verts[ 1 ].xyz, -2*u, cg.refdef.viewaxis[ 2 ], verts[ 2 ].xyz ); + VectorMA( verts[ 0 ].xyz, -2*u, cg.refdef.viewaxis[ 2 ], verts[ 3 ].xyz ); + + trap_R_AddPolyToScene( cgs.media.binaryAlpha1Shader, 4, verts ); + + for( i = 0; i < cg.numBinaryShadersUsed; ++i ) + { + for( j = 0; j < 4; ++j ) + { + for( k = 0; k < 3; ++k ) + verts[ j ].modulate[ k ] = cg.binaryShaderSettings[ i ].color[ k ]; + } + + if( cg.binaryShaderSettings[ i ].drawFrontline ) + trap_R_AddPolyToScene( cgs.media.binaryShaders[ i ].f3, 4, verts ); + if( cg.binaryShaderSettings[ i ].drawIntersection ) + trap_R_AddPolyToScene( cgs.media.binaryShaders[ i ].b3, 4, verts ); + } + + cg.numBinaryShadersUsed = 0; +} + /* ===================== CG_DrawActive @@ -3496,6 +3792,8 @@ void CG_DrawActive( stereoFrame_t stereoView ) VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[ 1 ], cg.refdef.vieworg ); + CG_DrawBinaryShadersFinalPhases( ); + // draw 3D view trap_R_RenderScene( &cg.refdef ); @@ -3510,6 +3808,3 @@ void CG_DrawActive( stereoFrame_t stereoView ) // draw status bar and other floating elements CG_Draw2D( ); } - - - -- cgit