// Copyright (C) 1999-2000 Id Software, Inc. // // cg_draw.c -- draw all of the graphical elements during // active (after loading) gameplay /* * Portions Copyright (C) 2000-2001 Tim Angus * * This program is free software; you can redistribute it and/or modify it * under the terms of the OSML - Open Source Modification License v1.0 as * described in the file COPYING which is distributed with this source * code. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include "cg_local.h" // used for scoreboard extern displayContextDef_t cgDC; menuDef_t *menuScoreboard = NULL; int drawTeamOverlayModificationCount = -1; int sortedTeamPlayers[TEAM_MAXOVERLAY]; int numSortedTeamPlayers; char systemChat[256]; char teamChat1[256]; char teamChat2[256]; //TA UI int CG_Text_Width( const char *text, float scale, int limit ) { int count,len; float out; glyphInfo_t *glyph; float useScale; // FIXME: see ui_main.c, same problem // const unsigned char *s = text; const char *s = text; fontInfo_t *font = &cgDC.Assets.textFont; if( scale <= cg_smallFont.value ) font = &cgDC.Assets.smallFont; else if( scale > cg_bigFont.value ) font = &cgDC.Assets.bigFont; useScale = scale * font->glyphScale; out = 0; if( text ) { len = strlen( text ); if( limit > 0 && len > limit ) len = limit; count = 0; while( s && *s && count < len ) { if( Q_IsColorString( s ) ) { s += 2; continue; } else { glyph = &font->glyphs[ (int)*s ]; //TTimo: FIXME: getting nasty warnings without the cast, //hopefully this doesn't break the VM build out += glyph->xSkip; s++; count++; } } } return out * useScale; } int CG_Text_Height( const char *text, float scale, int limit ) { int len, count; float max; glyphInfo_t *glyph; float useScale; // TTimo: FIXME // const unsigned char *s = text; const char *s = text; fontInfo_t *font = &cgDC.Assets.textFont; if( scale <= cg_smallFont.value ) font = &cgDC.Assets.smallFont; else if( scale > cg_bigFont.value ) font = &cgDC.Assets.bigFont; useScale = scale * font->glyphScale; max = 0; if( text ) { len = strlen( text ); if( limit > 0 && len > limit ) len = limit; count = 0; while( s && *s && count < len ) { if( Q_IsColorString( s ) ) { s += 2; continue; } else { glyph = &font->glyphs[ (int)*s ]; //TTimo: FIXME: getting nasty warnings without the cast, //hopefully this doesn't break the VM build if( max < glyph->height ) max = glyph->height; s++; count++; } } } return max * useScale; } void CG_Text_PaintChar( float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader ) { float w, h; w = width * scale; h = height * scale; CG_AdjustFrom640( &x, &y, &w, &h ); trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader ); } void CG_Text_Paint( float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style ) { int len, count; vec4_t newColor; glyphInfo_t *glyph; float useScale; fontInfo_t *font = &cgDC.Assets.textFont; if( scale <= cg_smallFont.value ) font = &cgDC.Assets.smallFont; else if( scale > cg_bigFont.value ) font = &cgDC.Assets.bigFont; useScale = scale * font->glyphScale; if( text ) { // TTimo: FIXME // const unsigned char *s = text; const char *s = text; trap_R_SetColor( color ); memcpy( &newColor[ 0 ], &color[ 0 ], sizeof( vec4_t ) ); len = strlen( text ); if( limit > 0 && len > limit ) len = limit; count = 0; while( s && *s && count < len ) { glyph = &font->glyphs[ (int)*s ]; //TTimo: FIXME: getting nasty warnings without the cast, //hopefully this doesn't break the VM build if( Q_IsColorString( s ) ) { memcpy( newColor, g_color_table[ ColorIndex( *( s + 1 ) ) ], sizeof( newColor ) ); newColor[ 3 ] = color[ 3 ]; trap_R_SetColor( newColor ); s += 2; continue; } else { float yadj = useScale * glyph->top; if( style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE ) { int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2; colorBlack[ 3 ] = newColor[ 3 ]; trap_R_SetColor( colorBlack ); CG_Text_PaintChar( x + ofs, y - yadj + ofs, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph ); colorBlack[ 3 ] = 1.0; trap_R_SetColor( newColor ); } CG_Text_PaintChar( x, y - yadj, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph ); x += ( glyph->xSkip * useScale ) + adjust; s++; count++; } } trap_R_SetColor( NULL ); } } /* ============== CG_DrawField Draws large numbers for status bar and powerups ============== */ static void CG_DrawField( int x, int y, int width, int cw, int ch, int value ) { char num[ 16 ], *ptr; int l; int frame; int charWidth, charHeight; if( !( charWidth = cw ) ) charWidth = CHAR_WIDTH; if( !( charHeight = ch ) ) charWidth = CHAR_HEIGHT; if( width < 1 ) return; // draw number string if( width > 4 ) width = 4; switch( width ) { case 1: value = value > 9 ? 9 : value; value = value < 0 ? 0 : value; break; case 2: value = value > 99 ? 99 : value; value = value < -9 ? -9 : value; break; case 3: value = value > 999 ? 999 : value; value = value < -99 ? -99 : value; break; case 4: value = value > 9999 ? 9999 : value; value = value < -999 ? -999 : value; break; } Com_sprintf( num, sizeof( num ), "%d", value ); l = strlen( num ); if( l > width ) l = width; x += 2 + charWidth * ( width - l ); ptr = num; while( *ptr && l ) { if( *ptr == '-' ) frame = STAT_MINUS; else frame = *ptr -'0'; CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[ frame ] ); x += charWidth; ptr++; l--; } } //=============== TA: was cg_newdraw.c void CG_InitTeamChat( ) { memset( teamChat1, 0, sizeof( teamChat1 ) ); memset( teamChat2, 0, sizeof( teamChat2 ) ); memset( systemChat, 0, sizeof( systemChat ) ); } void CG_SetPrintString( int type, const char *p ) { if( type == SYSTEM_PRINT ) { strcpy( systemChat, p ); } else { strcpy( teamChat2, teamChat1 ); strcpy( teamChat1, p ); } } static void CG_DrawPlayerCreditsValue( rectDef_t *rect, vec4_t color ) { int value; playerState_t *ps; ps = &cg.snap->ps; value = ps->persistant[ PERS_CREDIT ]; if( value > -1 ) { trap_R_SetColor( color ); CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value ); trap_R_SetColor( NULL ); } } static void CG_DrawPlayerBankValue( rectDef_t *rect, vec4_t color ) { int value; playerState_t *ps; ps = &cg.snap->ps; value = ps->persistant[ PERS_BANK ]; if( value > -1 ) { trap_R_SetColor( color ); CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value ); trap_R_SetColor( NULL ); } } static void CG_DrawPlayerStamina( rectDef_t *rect ) { playerState_t *ps = &cg.snap->ps; int stamina = ps->stats[ STAT_STAMINA ]; int height = (int)( (float)stamina / ( MAX_STAMINA / ( rect->h / 2 ) ) ); vec4_t bcolor = { 0.5f, 0.5f, 0.5f, 0.5f }; vec4_t pos = { 0.0f, 0.5f, 0.0f, 0.5f }; vec4_t neg = { 0.5f, 0.0f, 0.0f, 0.5f }; trap_R_SetColor( bcolor ); // white CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.whiteShader ); if( stamina > 0 ) { trap_R_SetColor( pos ); // green CG_DrawPic( rect->x, rect->y + ( rect->h / 2 - height ), rect->w, height, cgs.media.whiteShader ); } if( stamina < 0 ) { trap_R_SetColor( neg ); // red CG_DrawPic( rect->x, rect->y + rect->h / 2, rect->w, -height, cgs.media.whiteShader ); } trap_R_SetColor( NULL ); } static void CG_DrawPlayerAmmoValue( rectDef_t *rect, vec4_t color ) { int value; centity_t *cent; playerState_t *ps; cent = &cg_entities[ cg.snap->ps.clientNum ]; ps = &cg.snap->ps; if( cent->currentState.weapon ) { BG_unpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups, &value, NULL, NULL ); if( value > -1 ) { trap_R_SetColor( color ); CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value ); trap_R_SetColor( NULL ); } } } static void CG_DrawPlayerClipsValue( rectDef_t *rect, vec4_t color ) { int value; centity_t *cent; playerState_t *ps; cent = &cg_entities[ cg.snap->ps.clientNum ]; ps = &cg.snap->ps; if( cent->currentState.weapon ) { BG_unpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups, NULL, &value, NULL ); if( value > -1 ) { trap_R_SetColor( color ); CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value ); trap_R_SetColor( NULL ); } } } static void CG_DrawPlayerHealth( rectDef_t *rect, vec4_t color ) { playerState_t *ps; int value; ps = &cg.snap->ps; value = ps->stats[ STAT_HEALTH ]; trap_R_SetColor( color ); CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value ); trap_R_SetColor( NULL ); } float CG_GetValue( int ownerDraw ) { centity_t *cent; clientInfo_t *ci; playerState_t *ps; cent = &cg_entities[ cg.snap->ps.clientNum ]; ps = &cg.snap->ps; switch( ownerDraw ) { case CG_PLAYER_AMMO_VALUE: if( cent->currentState.weapon ) { int value; BG_unpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups, &value, NULL, NULL ); return value; } break; case CG_PLAYER_CLIPS_VALUE: if( cent->currentState.weapon ) { int value; BG_unpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups, NULL, &value, NULL ); return value; } break; case CG_PLAYER_HEALTH: return ps->stats[ STAT_HEALTH ]; break; default: break; } return -1; } static void CG_DrawAreaSystemChat( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader ) { CG_Text_Paint( rect->x, rect->y + rect->h, scale, color, systemChat, 0, 0, 0 ); } static void CG_DrawAreaTeamChat( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader ) { CG_Text_Paint( rect->x, rect->y + rect->h, scale, color,teamChat1, 0, 0, 0 ); } static void CG_DrawAreaChat(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader) { CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, teamChat2, 0, 0, 0); } const char *CG_GetKillerText( ) { const char *s = ""; if( cg.killerName[ 0 ] ) s = va( "Fragged by %s", cg.killerName ); return s; } static void CG_DrawKiller( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) { // fragged by ... line if( cg.killerName[ 0 ] ) { int x = rect->x + rect->w / 2; CG_Text_Paint( x - CG_Text_Width( CG_GetKillerText( ), scale, 0 ) / 2, rect->y + rect->h, scale, color, CG_GetKillerText( ), 0, 0, textStyle ); } } static void CG_Text_Paint_Limit( float *maxX, float x, float y, float scale, vec4_t color, const char* text, float adjust, int limit ) { int len, count; vec4_t newColor; glyphInfo_t *glyph; if( text ) { // TTimo: FIXME // const unsigned char *s = text; // bk001206 - unsigned const char *s = text; float max = *maxX; float useScale; fontInfo_t *font = &cgDC.Assets.textFont; if( scale <= cg_smallFont.value ) font = &cgDC.Assets.smallFont; else if( scale > cg_bigFont.value ) font = &cgDC.Assets.bigFont; useScale = scale * font->glyphScale; trap_R_SetColor( color ); len = strlen( text ); if( limit > 0 && len > limit ) len = limit; count = 0; while( s && *s && count < len ) { glyph = &font->glyphs[ (int)*s ]; //TTimo: FIXME: getting nasty warnings without the cast, //hopefully this doesn't break the VM build if( Q_IsColorString( s ) ) { memcpy( newColor, g_color_table[ ColorIndex( *(s+1) ) ], sizeof( newColor ) ); newColor[ 3 ] = color[ 3 ]; trap_R_SetColor( newColor ); s += 2; continue; } else { float yadj = useScale * glyph->top; if( CG_Text_Width( s, useScale, 1 ) + x > max ) { *maxX = 0; break; } CG_Text_PaintChar( x, y - yadj, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph ); x += ( glyph->xSkip * useScale ) + adjust; *maxX = x; count++; s++; } } trap_R_SetColor( NULL ); } } void CG_OwnerDraw( float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle ) { rectDef_t rect; if( cg_drawStatus.integer == 0 ) return; rect.x = x; rect.y = y; rect.w = w; rect.h = h; switch( ownerDraw ) { case CG_PLAYER_CREDITS_VALUE: CG_DrawPlayerCreditsValue( &rect, color ); break; case CG_PLAYER_BANK_VALUE: CG_DrawPlayerBankValue( &rect, color ); break; case CG_PLAYER_STAMINA: CG_DrawPlayerStamina( &rect ); break; case CG_PLAYER_AMMO_VALUE: CG_DrawPlayerAmmoValue( &rect, color ); break; case CG_PLAYER_CLIPS_VALUE: CG_DrawPlayerClipsValue( &rect, color ); break; case CG_PLAYER_HEALTH: CG_DrawPlayerHealth( &rect, color ); break; case CG_AREA_SYSTEMCHAT: CG_DrawAreaSystemChat( &rect, scale, color, shader ); break; case CG_AREA_TEAMCHAT: CG_DrawAreaTeamChat( &rect, scale, color, shader ); break; case CG_AREA_CHAT: CG_DrawAreaChat( &rect, scale, color, shader ); break; case CG_KILLER: CG_DrawKiller( &rect, scale, color, shader, textStyle ); break; case CG_PLAYER_SELECT: CG_DrawWeaponSelect( &rect ); break; case CG_PLAYER_SELECTTEXT: CG_DrawWeaponSelectText( &rect, scale, textStyle ); break; default: break; } } void CG_MouseEvent( int x, int y ) { int n; if( ( cg.predictedPlayerState.pm_type == PM_NORMAL || cg.predictedPlayerState.pm_type == PM_SPECTATOR ) && cg.showScores == qfalse ) { trap_Key_SetCatcher( 0 ); return; } cgs.cursorX += x; if( cgs.cursorX < 0 ) cgs.cursorX = 0; else if( cgs.cursorX > 640 ) cgs.cursorX = 640; cgs.cursorY += y; if( cgs.cursorY < 0 ) cgs.cursorY = 0; else if( cgs.cursorY > 480 ) cgs.cursorY = 480; n = Display_CursorType( cgs.cursorX, cgs.cursorY ); cgs.activeCursor = 0; if( n == CURSOR_ARROW ) cgs.activeCursor = cgs.media.selectCursor; else if( n == CURSOR_SIZER ) cgs.activeCursor = cgs.media.sizeCursor; if( cgs.capturedItem ) Display_MouseMove( cgs.capturedItem, x, y ); else Display_MouseMove( NULL, cgs.cursorX, cgs.cursorY ); } /* ================== CG_HideTeamMenus ================== */ void CG_HideTeamMenu( ) { Menus_CloseByName( "teamMenu" ); Menus_CloseByName( "getMenu" ); } /* ================== CG_ShowTeamMenus ================== */ void CG_ShowTeamMenu( ) { Menus_OpenByName("teamMenu"); } /* ================== CG_EventHandling ================== type 0 - no event handling 1 - team menu 2 - hud editor */ void CG_EventHandling( int type ) { cgs.eventHandling = type; if( type == CGAME_EVENT_NONE ) { CG_HideTeamMenu( ); } else if( type == CGAME_EVENT_TEAMMENU ) { //CG_ShowTeamMenu(); } else if( type == CGAME_EVENT_SCOREBOARD ) { } } void CG_KeyEvent( int key, qboolean down ) { if( !down) return; if( cg.predictedPlayerState.pm_type == PM_NORMAL || ( cg.predictedPlayerState.pm_type == PM_SPECTATOR && cg.showScores == qfalse ) ) { CG_EventHandling( CGAME_EVENT_NONE ); trap_Key_SetCatcher( 0 ); return; } Display_HandleKey( key, down, cgs.cursorX, cgs.cursorY ); if( cgs.capturedItem ) { cgs.capturedItem = NULL; } else { if( key == K_MOUSE2 && down ) cgs.capturedItem = Display_CaptureItem( cgs.cursorX, cgs.cursorY ); } } int CG_ClientNumFromName( const char *p ) { int i; for( i = 0; i < cgs.maxclients; i++ ) { if( cgs.clientinfo[ i ].infoValid && Q_stricmp( cgs.clientinfo[ i ].name, p ) == 0 ) return i; } return -1; } void CG_ShowResponseHead( ) { Menus_OpenByName( "voiceMenu" ); trap_Cvar_Set( "cl_conXOffset", "72" ); cg.voiceTime = cg.time; } void CG_RunMenuScript( char **args ) { } void CG_GetTeamColor(vec4_t *color) { (*color)[0] = (*color)[2] = 0.0f; (*color)[1] = 0.17f; (*color)[3] = 0.25f; } //END TA UI /* ================ CG_DrawLighting ================ */ static void CG_DrawLighting( void ) { centity_t *cent; cent = &cg_entities[cg.snap->ps.clientNum]; if( cg.snap->ps.stats[ STAT_PCLASS ] == PCL_H_BASE ) { if( BG_activated( UP_NVG, cg.snap->ps.stats ) ) CG_DrawPic( 0, 0, 640, 480, cgs.media.humanNV ); } //fade to black if stamina is low if( ( cg.snap->ps.stats[ STAT_STAMINA ] < -800 ) && ( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { vec4_t black = { 0, 0, 0, 0 }; black[ 3 ] = 1.0 - ( (float)( cg.snap->ps.stats[ STAT_STAMINA ] + 1000 ) / 200.0f ); trap_R_SetColor( black ); CG_DrawPic( 0, 0, 640, 480, cgs.media.whiteShader ); trap_R_SetColor( NULL ); } } /* ================ CG_DrawStatusBar ================ */ /*static void CG_DrawStatusBar( void ) { int color; centity_t *cent; playerState_t *ps; int value; int ammo, clips, maxclips; vec4_t hcolor; vec3_t angles; vec3_t origin; static float colors[4][4] = { // { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} }; { 0.3f, 0.4f, 0.3f, 1.0f } , // normal { 1.0f, 0.2f, 0.2f, 1.0f }, // low health {0.2f, 0.3f, 0.2f, 1.0f}, // weapon firing { 1.0f, 1.0f, 1.0f, 1.0f } }; // health > 100 if ( cg_drawStatus.integer == 0 ) { return; } // draw the team background CG_DrawTeamBackground( 0, 420, 640, 60, 0.33f, cg.snap->ps.persistant[PERS_TEAM] ); cent = &cg_entities[cg.snap->ps.clientNum]; ps = &cg.snap->ps; VectorClear( angles );*/ //TA: stop drawing all these silly 3d models on the hud. Saves space and is more realistic. // draw any 3D icons first, so the changes back to 2D are minimized /*if ( cent->currentState.weapon && cg_weapons[ cent->currentState.weapon ].ammoModel ) { origin[0] = 70; origin[1] = 0; origin[2] = 0; angles[YAW] = 90 + 20 * sin( cg.time / 1000.0 ); CG_Draw3DModel( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles ); }*/ //CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE ); /*if (cg.predictedPlayerState.powerups[PW_REDFLAG]) CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_HUMANS); else if (cg.predictedPlayerState.powerups[PW_BLUEFLAG]) CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_ALIENS);*/ /*if ( ps->stats[ STAT_ARMOR ] ) { origin[0] = 90; origin[1] = 0; origin[2] = -10; angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0; CG_Draw3DModel( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cgs.media.armorModel, 0, origin, angles ); }*/ // // ammo // /* if ( cent->currentState.weapon ) { //TA: must mask off clips and maxClips if( !BG_FindInfinteAmmoForWeapon( cent->currentState.weapon ) ) BG_unpackAmmoArray( cent->currentState.weapon, ps->ammo, ps->powerups, &ammo, &clips, &maxclips ); else ammo = -1; if ( ammo > -1 ) { if ( cg.predictedPlayerState.weaponstate == WEAPON_FIRING && cg.predictedPlayerState.weaponTime > 100 ) { // draw as dark grey when reloading color = 2; // dark grey } else { if ( ammo >= 0 ) { color = 0; // green } else { color = 1; // red } } trap_R_SetColor( colors[color] ); CG_DrawField( 85, 432, 3, ammo); if( maxclips ) CG_DrawField( 20, 432, 1, clips ); trap_R_SetColor( NULL );*/ // if we didn't draw a 3D icon, draw a 2D icon for ammo /*if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) { qhandle_t icon; icon = cg_weapons[ cg.predictedPlayerState.weapon ].ammoIcon; if ( icon ) { CG_DrawPic( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, icon ); } }*/ /* } } // // stamina // #define STAM_HEIGHT 20 #define STAM_WIDTH 10 #define STAM_X 5 #define STAM_Y 435 if( ps->stats[ STAT_PTEAM ] == PTE_HUMANS ) { int stamina = ps->stats[ STAT_STAMINA ]; int height = (int)( (float)stamina / ( 1000 / STAM_HEIGHT ) ); vec4_t bcolor = { 0.5, 0.5, 0.5, 0.5 }; trap_R_SetColor( bcolor ); // white CG_DrawPic( STAM_X, STAM_Y, STAM_WIDTH, STAM_HEIGHT * 2, cgs.media.whiteShader ); if( stamina > 0 ) { trap_R_SetColor( colors[0] ); // green CG_DrawPic( STAM_X, STAM_Y + ( STAM_HEIGHT - height ), STAM_WIDTH, height, cgs.media.whiteShader ); } if( stamina < 0 ) { trap_R_SetColor( colors[1] ); // red CG_DrawPic( STAM_X, STAM_Y + STAM_HEIGHT , STAM_WIDTH, -height, cgs.media.whiteShader ); } } // // power // #define PWR_HEIGHT 10 #define PWR_WIDTH 80 #define PWR_X 555 #define PWR_Y 20 if( ps->stats[ STAT_PTEAM ] == PTE_HUMANS ) { float total = cgs.humanBuildPointsTotal; float allocated = total - cgs.humanBuildPoints; float powered = total - cgs.humanBuildPointsPowered; int awidth = (int)( ( allocated / total ) * PWR_WIDTH ); int pwidth = (int)( ( powered / total ) * PWR_WIDTH ); vec4_t bcolor = { 0.5, 0.5, 0.5, 0.5 }; char *s; int w; trap_R_SetColor( bcolor ); // white CG_DrawPic( PWR_X, PWR_Y, PWR_WIDTH, PWR_HEIGHT, cgs.media.whiteShader ); trap_R_SetColor( colors[0] ); // green CG_DrawPic( PWR_X, PWR_Y, awidth, PWR_HEIGHT, cgs.media.whiteShader ); if( allocated > powered ) { trap_R_SetColor( colors[1] ); // red CG_DrawPic( PWR_X + pwidth, PWR_Y, awidth - pwidth, PWR_HEIGHT, cgs.media.whiteShader ); } //display amount of credit s = va( "%dg", ps->stats[ PERS_CREDIT ] ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 635 - w, 35, s, 1.0F); } // // hive // #define HV_HEIGHT 10 #define HV_WIDTH 80 #define HV_X 555 #define HV_Y 20 if( ps->stats[ STAT_PTEAM ] == PTE_ALIENS ) { float total = cgs.alienBuildPointsTotal; float allocated = total - cgs.alienBuildPoints; int awidth = (int)( ( allocated / total ) * HV_WIDTH ); vec4_t bcolor = { 0.5, 0.5, 0.5, 0.5 }; trap_R_SetColor( bcolor ); // white CG_DrawPic( HV_X, HV_Y, HV_WIDTH, HV_HEIGHT, cgs.media.whiteShader ); trap_R_SetColor( colors[0] ); // green CG_DrawPic( HV_X, HV_Y, awidth, HV_HEIGHT, cgs.media.whiteShader ); } // // health+armor // if( ps->stats[ STAT_PTEAM ] == PTE_ALIENS ) { vec4_t fcolor = { 1, 0, 0, 0.5 }; //red half alpha vec4_t tcolor = { 0.3, 0.8, 1, 1 }; //cyan no alpha value = (int)( (float)( (float)ps->stats[STAT_HEALTH] / ps->stats[STAT_MAX_HEALTH] ) * 100 ); CG_DrawFadePic( 20, 0, 30, 440, fcolor, tcolor, value, cgs.media.alienHealth );*/ /* value = (int)( (float)( (float)ps->stats[STAT_ARMOR] / ps->stats[STAT_MAX_HEALTH] ) * 100 ); if( value > 0 ) CG_DrawFadePic( 580, 0, 30, 440, fcolor, tcolor, value, cgs.media.alienHealth );*/ /* } else { value = ps->stats[STAT_HEALTH]; if ( value > 100 ) { trap_R_SetColor( colors[0] ); // white } else if (value > 25) { trap_R_SetColor( colors[0] ); // green } else if (value > 0) { color = (cg.time >> 8) & 1; // flash trap_R_SetColor( colors[color] ); } else { trap_R_SetColor( colors[1] ); // red } // stretch the health up when taking damage CG_DrawField ( 300, 432, 3, value); CG_ColorForHealth( hcolor ); trap_R_SetColor( hcolor ); */ /* value = ps->stats[STAT_ARMOR]; if (value > 0 ) { trap_R_SetColor( colors[0] ); CG_DrawField (541, 432, 3, value); trap_R_SetColor( NULL ); // if we didn't draw a 3D icon, draw a 2D icon for armor if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) { CG_DrawPic( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cgs.media.armorIcon ); } }*/ /* } }*/ /* =========================================================================================== UPPER RIGHT CORNER =========================================================================================== */ /* ================== CG_DrawSnapshot ================== */ static float CG_DrawSnapshot( float y ) { char *s; int w; s = va( "time:%d snap:%d cmd:%d", cg.snap->serverTime, cg.latestSnapshotNum, cgs.serverCommandSequence ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 635 - w, y + 2, s, 1.0F); return y + BIGCHAR_HEIGHT + 4; } /* ================== CG_DrawFPS ================== */ //TA: personally i think this should be longer - it should really be a cvar #define FPS_FRAMES 40 static float CG_DrawFPS( float y ) { char *s; int w; static int previousTimes[ FPS_FRAMES ]; static int index; int i, total; int fps; static int previous; int t, frameTime; // don't use serverTime, because that will be drifting to // correct for internet lag changes, timescales, timedemos, etc t = trap_Milliseconds( ); frameTime = t - previous; previous = t; previousTimes[ index % FPS_FRAMES ] = frameTime; index++; if( index > FPS_FRAMES ) { // average multiple frames together to smooth changes out a bit total = 0; for( i = 0 ; i < FPS_FRAMES ; i++ ) total += previousTimes[ i ]; if( !total ) total = 1; fps = 1000 * FPS_FRAMES / total; s = va( "%dfps", fps ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 635 - w, y + 2, s, 1.0F ); } return y + BIGCHAR_HEIGHT + 4; } /* ================= CG_DrawTimer ================= */ static float CG_DrawTimer( float y ) { char *s; int w; int mins, seconds, tens; int msec; msec = cg.time - cgs.levelStartTime; seconds = msec / 1000; mins = seconds / 60; seconds -= mins * 60; tens = seconds / 10; seconds -= tens * 10; s = va( "%d:%d%d", mins, tens, seconds ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 635 - w, y + 2, s, 1.0F); return y + BIGCHAR_HEIGHT + 4; } /* ===================== CG_DrawUpperRight ===================== */ static void CG_DrawUpperRight( void ) { float y; y = 0; if( cg_drawSnapshot.integer ) y = CG_DrawSnapshot( y ); if( cg_drawFPS.integer ) y = CG_DrawFPS( y ); if( cg_drawTimer.integer ) y = CG_DrawTimer( y ); } /* =========================================================================================== LOWER RIGHT CORNER =========================================================================================== */ /* ================= CG_DrawBuildPoints Draw the small two score display ================= */ /*static float CG_DrawBuildPoints( float y ) { const char *s; int points, totalpoints, buildpoints; int team; int x, w; float y1; qboolean spectator; y -= BIGCHAR_HEIGHT + 8; y1 = y; x = 640; points = cg.snap->ps.persistant[PERS_POINTS]; totalpoints = cg.snap->ps.persistant[PERS_TOTALPOINTS]; team = cg.snap->ps.stats[ STAT_PTEAM ]; if( team == PTE_ALIENS ) buildpoints = cgs.alienBuildPoints; else if( team == PTE_HUMANS ) buildpoints = cgs.humanBuildPoints; spectator = ( team == PTE_NONE ); if( !spectator ) { s = va( "%2i", points ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; CG_DrawBigString( x + 2, y, s, 1.0F ); s = va( "%2i", totalpoints ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; CG_DrawBigString( x + 2, y, s, 1.0F ); s = va( "%2i", buildpoints ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; CG_DrawBigString( x + 2, y, s, 1.0F ); } return y1 - 8; }*/ //=========================================================================================== /* =============================================================================== LAGOMETER =============================================================================== */ #define LAG_SAMPLES 128 typedef struct { int frameSamples[ LAG_SAMPLES ]; int frameCount; int snapshotFlags[ LAG_SAMPLES ]; int snapshotSamples[ LAG_SAMPLES ]; int snapshotCount; } lagometer_t; lagometer_t lagometer; /* ============== CG_AddLagometerFrameInfo Adds the current interpolate / extrapolate bar for this frame ============== */ void CG_AddLagometerFrameInfo( void ) { int offset; offset = cg.time - cg.latestSnapshotTime; lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1 ) ] = offset; lagometer.frameCount++; } /* ============== CG_AddLagometerSnapshotInfo Each time a snapshot is received, log its ping time and the number of snapshots that were dropped before it. Pass NULL for a dropped packet. ============== */ void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) { // dropped packet if( !snap ) { lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1 ) ] = -1; lagometer.snapshotCount++; return; } // add this snapshot's info lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1 ) ] = snap->ping; lagometer.snapshotFlags[ lagometer.snapshotCount & ( LAG_SAMPLES - 1 ) ] = snap->snapFlags; lagometer.snapshotCount++; } /* ============== CG_DrawDisconnect Should we draw something differnet for long lag vs no packets? ============== */ static void CG_DrawDisconnect( void ) { float x, y; int cmdNum; usercmd_t cmd; const char *s; int w; // draw the phone jack if we are completely past our buffers cmdNum = trap_GetCurrentCmdNumber( ) - CMD_BACKUP + 1; trap_GetUserCmd( cmdNum, &cmd ); // special check for map_restart if( cmd.serverTime <= cg.snap->ps.commandTime || cmd.serverTime > cg.time ) return; // also add text in center of screen s = "Connection Interrupted"; w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 320 - w / 2, 100, s, 1.0F); // blink the icon if( ( cg.time >> 9 ) & 1 ) return; x = 640 - 48; y = 480 - 48; CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader( "gfx/2d/net.tga" ) ); } #define MAX_LAGOMETER_PING 900 #define MAX_LAGOMETER_RANGE 300 /* ============== CG_DrawLagometer ============== */ static void CG_DrawLagometer( void ) { int a, x, y, i; float v; float ax, ay, aw, ah, mid, range; int color; float vscale; if( !cg_lagometer.integer || cgs.localServer ) { CG_DrawDisconnect( ); return; } // // draw the graph // x = 640 - 48; y = 480 - 144; trap_R_SetColor( NULL ); CG_DrawPic( x, y, 48, 48, cgs.media.lagometerShader ); ax = x; ay = y; aw = 48; ah = 48; CG_AdjustFrom640( &ax, &ay, &aw, &ah ); color = -1; range = ah / 3; mid = ay + range; vscale = range / MAX_LAGOMETER_RANGE; // draw the frame interpoalte / extrapolate graph for( a = 0 ; a < aw ; a++ ) { i = ( lagometer.frameCount - 1 - a ) & ( LAG_SAMPLES - 1 ); v = lagometer.frameSamples[ i ]; v *= vscale; if( v > 0 ) { if( color != 1 ) { color = 1; trap_R_SetColor( g_color_table[ ColorIndex( COLOR_YELLOW ) ] ); } if( v > range ) v = range; trap_R_DrawStretchPic( ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); } else if( v < 0 ) { if( color != 2 ) { color = 2; trap_R_SetColor( g_color_table[ ColorIndex( COLOR_BLUE ) ] ); } v = -v; if( v > range ) v = range; trap_R_DrawStretchPic( ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); } } // draw the snapshot latency / drop graph range = ah / 2; vscale = range / MAX_LAGOMETER_PING; for( a = 0 ; a < aw ; a++ ) { i = ( lagometer.snapshotCount - 1 - a ) & ( LAG_SAMPLES - 1 ); v = lagometer.snapshotSamples[ i ]; if( v > 0 ) { if( lagometer.snapshotFlags[ i ] & SNAPFLAG_RATE_DELAYED ) { if( color != 5 ) { color = 5; // YELLOW for rate delay trap_R_SetColor( g_color_table[ ColorIndex( COLOR_YELLOW ) ] ); } } else { if( color != 3 ) { color = 3; trap_R_SetColor( g_color_table[ ColorIndex( COLOR_GREEN ) ] ); } } v = v * vscale; if( v > range ) v = range; trap_R_DrawStretchPic( ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); } else if( v < 0 ) { if( color != 4 ) { color = 4; // RED for dropped snapshots trap_R_SetColor( g_color_table[ ColorIndex( COLOR_RED ) ] ); } trap_R_DrawStretchPic( ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader ); } } trap_R_SetColor( NULL ); if( cg_nopredict.integer || cg_synchronousClients.integer ) CG_DrawBigString( ax, ay, "snc", 1.0 ); CG_DrawDisconnect(); } /* =============================================================================== CENTER PRINTING =============================================================================== */ /* ============== CG_CenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ void CG_CenterPrint( const char *str, int y, int charWidth ) { char *s; Q_strncpyz( cg.centerPrint, str, sizeof( cg.centerPrint ) ); cg.centerPrintTime = cg.time; cg.centerPrintY = y; cg.centerPrintCharWidth = charWidth; // count the number of lines for centering cg.centerPrintLines = 1; s = cg.centerPrint; while( *s ) { if( *s == '\n' ) cg.centerPrintLines++; s++; } } /* =================== CG_DrawCenterString =================== */ static void CG_DrawCenterString( void ) { char *start; int l; int x, y, w; int h; float *color; if( !cg.centerPrintTime ) return; color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value ); if( !color ) return; trap_R_SetColor( color ); start = cg.centerPrint; y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2; while( 1 ) { char linebuffer[ 1024 ]; for( l = 0; l < 50; l++ ) { if( !start[ l ] || start[ l ] == '\n' ) break; linebuffer[ l ] = start[ l ]; } linebuffer[ l ] = 0; w = CG_Text_Width( linebuffer, 0.5, 0 ); h = CG_Text_Height( linebuffer, 0.5, 0 ); x = ( SCREEN_WIDTH - w ) / 2; CG_Text_Paint( x, y + h, 0.5, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE ); y += h + 6; while( *start && ( *start != '\n' ) ) start++; if( !*start ) break; start++; } trap_R_SetColor( NULL ); } /* ================================================================================ CROSSHAIR ================================================================================ */ /* ================= CG_DrawCrosshair ================= */ static void CG_DrawCrosshair( void ) { float w, h; qhandle_t hShader; float f; float x, y; int ca; if( !cg_drawCrosshair.integer ) return; if( ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) || ( cg.snap->ps.stats[ STAT_STATE ] & SS_INFESTING ) || ( cg.snap->ps.stats[ STAT_STATE ] & SS_HOVELING ) ) return; if( cg.renderingThirdPerson ) return; // set color based on health if( cg_crosshairHealth.integer ) { vec4_t hcolor; CG_ColorForHealth( hcolor ); trap_R_SetColor( hcolor ); } else trap_R_SetColor( NULL ); w = h = cg_crosshairSize.value; // pulse the size of the crosshair when picking up items f = cg.time - cg.itemPickupBlendTime; if( f > 0 && f < ITEM_BLOB_TIME ) { f /= ITEM_BLOB_TIME; w *= ( 1 + f ); h *= ( 1 + f ); } x = cg_crosshairX.integer; y = cg_crosshairY.integer; CG_AdjustFrom640( &x, &y, &w, &h ); ca = cg_drawCrosshair.integer; if( ca < 0 ) ca = 0; hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ]; trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * ( cg.refdef.width - w ), y + cg.refdef.y + 0.5 * ( cg.refdef.height - h ), w, h, 0, 0, 1, 1, hShader ); } /* ================= CG_ScanForCrosshairEntity ================= */ static void CG_ScanForCrosshairEntity( void ) { trace_t trace; vec3_t start, end; int content; VectorCopy( cg.refdef.vieworg, start ); VectorMA( start, 131072, cg.refdef.viewaxis[ 0 ], end ); CG_Trace( &trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, CONTENTS_SOLID|CONTENTS_BODY ); if( trace.entityNum >= MAX_CLIENTS ) return; // if the player is in fog, don't show it content = trap_CM_PointContents( trace.endpos, 0 ); if( content & CONTENTS_FOG ) return; // update the fade timer cg.crosshairClientNum = trace.entityNum; cg.crosshairClientTime = cg.time; } /* ===================== CG_DrawCrosshairNames ===================== */ static void CG_DrawCrosshairNames( void ) { float *color; char *name; float w; if( !cg_drawCrosshair.integer ) return; if( !cg_drawCrosshairNames.integer ) return; if( cg.renderingThirdPerson ) return; // scan the known entities to see if the crosshair is sighted on one CG_ScanForCrosshairEntity( ); // draw the name of the player being looked at color = CG_FadeColor( cg.crosshairClientTime, 1000 ); if( !color ) { trap_R_SetColor( NULL ); return; } name = cgs.clientinfo[ cg.crosshairClientNum ].name; color[ 3 ] *= 0.5f; w = CG_Text_Width( name, 0.3f, 0 ); CG_Text_Paint( 320 - w / 2, 190, 0.3f, color, name, 0, 0, ITEM_TEXTSTYLE_SHADOWED ); trap_R_SetColor( NULL ); } //============================================================================== /* ================= CG_DrawVote ================= */ static void CG_DrawVote( void ) { char *s; int sec; if( !cgs.voteTime ) return; // play a talk beep whenever it is modified if( cgs.voteModified ) { cgs.voteModified = qfalse; trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); } sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000; if( sec < 0 ) sec = 0; s = va( "VOTE(%i):%s yes:%i no:%i", sec, cgs.voteString, cgs.voteYes, cgs.voteNo ); CG_DrawSmallString( 0, 58, s, 1.0F ); s = "or press ESC then click Vote"; CG_DrawSmallString( 0, 58 + SMALLCHAR_HEIGHT + 2, s, 1.0F ); } /* ================= CG_DrawTeamVote ================= */ static void CG_DrawTeamVote( void ) { char *s; int sec, cs_offset; if( cgs.clientinfo->team == TEAM_HUMANS ) cs_offset = 0; else if( cgs.clientinfo->team == TEAM_ALIENS ) cs_offset = 1; else return; if( !cgs.teamVoteTime[ cs_offset ] ) return; // play a talk beep whenever it is modified if ( cgs.teamVoteModified[ cs_offset ] ) { cgs.teamVoteModified[ cs_offset ] = qfalse; trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); } sec = ( VOTE_TIME - ( cg.time - cgs.teamVoteTime[ cs_offset ] ) ) / 1000; if( sec < 0 ) sec = 0; s = va( "TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[ cs_offset ], cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[ cs_offset ] ); CG_DrawSmallString( 0, 90, s, 1.0F ); } static qboolean CG_DrawScoreboard( ) { return CG_DrawOldScoreboard( ); } /* ================= CG_DrawIntermission ================= */ static void CG_DrawIntermission( void ) { if( cgs.gametype == GT_SINGLE_PLAYER ) { CG_DrawCenterString( ); return; } cg.scoreFadeTime = cg.time; cg.scoreBoardShowing = CG_DrawScoreboard( ); } /* ================= CG_DrawFollow ================= */ static qboolean CG_DrawFollow( void ) { float x; vec4_t color; const char *name; if( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) return qfalse; color[ 0 ] = 1; color[ 1 ] = 1; color[ 2 ] = 1; color[ 3 ] = 1; CG_DrawBigString( 320 - 9 * 8, 24, "following", 1.0F ); name = cgs.clientinfo[ cg.snap->ps.clientNum ].name; x = 0.5 * ( 640 - GIANT_WIDTH * CG_DrawStrlen( name ) ); CG_DrawStringExt( x, 40, name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); return qtrue; } /* ================= CG_DrawAmmoWarning ================= */ static void CG_DrawAmmoWarning( void ) { const char *s; int w; if( cg.snap->ps.weapon == WP_NONE ) return; if ( cg_drawAmmoWarning.integer == 0 ) return; if ( !cg.lowAmmoWarning ) return; if ( cg.lowAmmoWarning == 2 ) s = "OUT OF AMMO"; else s = "LOW AMMO WARNING"; w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 320 - w / 2, 64, s, 1.0F ); } /* ================= CG_DrawWarmup ================= */ static void CG_DrawWarmup( void ) { int w; int sec; int i; float scale; clientInfo_t *ci1, *ci2; int cw; const char *s; sec = cg.warmup; if( !sec ) return; if( sec < 0 ) { s = "Waiting for players"; w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 320 - w / 2, 24, s, 1.0F ); cg.warmupCount = 0; return; } if( cgs.gametype == GT_TOURNAMENT ) { // find the two active players ci1 = NULL; ci2 = NULL; for( i = 0 ; i < cgs.maxclients ; i++ ) { if( cgs.clientinfo[ i ].infoValid && cgs.clientinfo[ i ].team == TEAM_FREE ) { if( !ci1 ) ci1 = &cgs.clientinfo[ i ]; else ci2 = &cgs.clientinfo[ i ]; } } if( ci1 && ci2 ) { s = va( "%s vs %s", ci1->name, ci2->name ); w = CG_DrawStrlen( s ); if( w > 640 / GIANT_WIDTH ) cw = 640 / w; else cw = GIANT_WIDTH; CG_DrawStringExt( 320 - w * cw / 2, 20,s, colorWhite, qfalse, qtrue, cw, (int)( cw * 1.5f ), 0 ); } } else { if( cgs.gametype == GT_FFA ) s = "Free For All"; else if ( cgs.gametype == GT_TEAM ) s = "Team Deathmatch"; else if ( cgs.gametype == GT_CTF ) s = "Capture the Flag"; else s = ""; w = CG_DrawStrlen( s ); if( w > 640 / GIANT_WIDTH ) cw = 640 / w; else cw = GIANT_WIDTH; CG_DrawStringExt( 320 - w * cw / 2, 25,s, colorWhite, qfalse, qtrue, cw, (int)( cw * 1.1f ), 0 ); } sec = ( sec - cg.time ) / 1000; if( sec < 0 ) { cg.warmup = 0; sec = 0; } s = va( "Starts in: %i", sec + 1 ); if( sec != cg.warmupCount ) { cg.warmupCount = sec; switch( sec ) { case 0: trap_S_StartLocalSound( cgs.media.count1Sound, CHAN_ANNOUNCER ); break; case 1: trap_S_StartLocalSound( cgs.media.count2Sound, CHAN_ANNOUNCER ); break; case 2: trap_S_StartLocalSound( cgs.media.count3Sound, CHAN_ANNOUNCER ); break; default: break; } } scale = 0.45f; switch( cg.warmupCount ) { case 0: cw = 28; scale = 0.54f; break; case 1: cw = 24; scale = 0.51f; break; case 2: cw = 20; scale = 0.48f; break; default: cw = 16; scale = 0.45f; break; } w = CG_DrawStrlen( s ); CG_DrawStringExt( 320 - w * cw / 2, 70, s, colorWhite, qfalse, qtrue, cw, (int)( cw * 1.5 ), 0 ); } //================================================================================== /* ================= CG_Draw2D ================= */ static void CG_Draw2D( void ) { // if we are taking a levelshot for the menu, don't draw anything if( cg.levelShot ) return; if( cg_draw2D.integer == 0 ) return; if( cg.snap->ps.pm_type == PM_INTERMISSION ) { CG_DrawIntermission( ); return; } //TA: draw the lighting effects e.g. nvg CG_DrawLighting( ); if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR ) CG_DrawBigString(320 - 9 * 8, 440, "SPECTATOR", 1.0F); else if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_INFESTING ) && !( cg.snap->ps.stats[ STAT_STATE ] & SS_HOVELING ) ) { // don't draw any status if dead or the scoreboard is being explicitly shown if( !cg.showScores && cg.snap->ps.stats[ STAT_HEALTH ] > 0 ) { if( cg_drawStatus.integer ) { Menu_Paint( Menus_FindByName( BG_FindHudNameForClass( cg.predictedPlayerState.stats[ STAT_PCLASS ] ) ), qtrue ); } CG_DrawAmmoWarning(); CG_DrawCrosshair(); if( BG_gotItem( UP_HELMET, cg.snap->ps.stats ) ) CG_Scanner( ); if( BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_PCLASS ], SCA_ALIENSENSE ) ) CG_AlienSense( ); } } CG_DrawVote(); CG_DrawTeamVote(); CG_DrawLagometer(); CG_DrawUpperRight(); if( !CG_DrawFollow( ) ) CG_DrawWarmup(); // don't draw center string if scoreboard is up cg.scoreBoardShowing = CG_DrawScoreboard( ); if( !cg.scoreBoardShowing ) CG_DrawCenterString( ); } static void CG_DrawTourneyScoreboard( ) { CG_DrawOldTourneyScoreboard(); } /* ===================== CG_DrawActive Perform all drawing needed to completely fill the screen ===================== */ void CG_DrawActive( stereoFrame_t stereoView ) { float separation; vec3_t baseOrg; // optionally draw the info screen instead if( !cg.snap ) { CG_DrawInformation( ); return; } // optionally draw the tournement scoreboard instead if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR && ( cg.snap->ps.pm_flags & PMF_SCOREBOARD ) ) { CG_DrawTourneyScoreboard( ); return; } switch ( stereoView ) { case STEREO_CENTER: separation = 0; break; case STEREO_LEFT: separation = -cg_stereoSeparation.value / 2; break; case STEREO_RIGHT: separation = cg_stereoSeparation.value / 2; break; default: separation = 0; CG_Error( "CG_DrawActive: Undefined stereoView" ); } // clear around the rendered view if sized down CG_TileClear( ); // offset vieworg appropriately if we're doing stereo separation VectorCopy( cg.refdef.vieworg, baseOrg ); if( separation != 0 ) VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[ 1 ], cg.refdef.vieworg ); // draw 3D view trap_R_RenderScene( &cg.refdef ); // restore original viewpoint if running stereo if( separation != 0 ) VectorCopy( baseOrg, cg.refdef.vieworg ); // draw status bar and other floating elements CG_Draw2D( ); }