/* =========================================================================== Wolfenstein: Enemy Territory GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Wolfenstein: Enemy Territory GPL Source Code (“Wolf ET Source Code”). Wolf ET Source Code 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 3 of the License, or (at your option) any later version. Wolf ET Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Wolf ET Source Code. If not, see . In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ // cg_draw.c -- draw all of the graphical elements during // active (after loading) gameplay #include "cg_local.h" #define STATUSBARHEIGHT 452 char* BindingFromName( const char *cvar ); void Controls_GetConfig( void ); void SetHeadOrigin( clientInfo_t *ci, playerInfo_t *pi ); void CG_DrawOverlays(); int activeFont; #define TEAM_OVERLAY_TIME 1000 //////////////////////// //////////////////////// ////// new hud stuff /////////////////////// /////////////////////// void CG_Text_SetActiveFont( int font ) { activeFont = font; } int CG_Text_Width_Ext( const char *text, float scale, int limit, fontInfo_t* font ) { int count, len; glyphInfo_t *glyph; const char *s = text; float out, 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[(unsigned char)*s]; out += glyph->xSkip; s++; count++; } } } return out * useScale; } int CG_Text_Width( const char *text, float scale, int limit ) { fontInfo_t *font = &cgDC.Assets.fonts[activeFont]; return CG_Text_Width_Ext( text, scale, limit, font ); } int CG_Text_Height_Ext( const char *text, float scale, int limit, fontInfo_t* font ) { int len, count; float max; glyphInfo_t *glyph; float useScale; const char *s = text; 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[(unsigned char)*s]; if ( max < glyph->height ) { max = glyph->height; } s++; count++; } } } return max * useScale; } int CG_Text_Height( const char *text, float scale, int limit ) { fontInfo_t *font = &cgDC.Assets.fonts[activeFont]; return CG_Text_Height_Ext( text, scale, limit, font ); } void CG_Text_PaintChar_Ext( float x, float y, float w, float h, float scalex, float scaley, float s, float t, float s2, float t2, qhandle_t hShader ) { w *= scalex; h *= scaley; CG_AdjustFrom640( &x, &y, &w, &h ); trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader ); } 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_Centred_Ext( float x, float y, float scalex, float scaley, vec4_t color, const char *text, float adjust, int limit, int style, fontInfo_t* font ) { x -= CG_Text_Width_Ext( text, scalex, limit, font ) * 0.5f; CG_Text_Paint_Ext( x, y, scalex, scaley, color, text, adjust, limit, style, font ); } void CG_Text_Paint_Ext( float x, float y, float scalex, float scaley, vec4_t color, const char *text, float adjust, int limit, int style, fontInfo_t* font ) { int len, count; vec4_t newColor; glyphInfo_t *glyph; scalex *= font->glyphScale; scaley *= font->glyphScale; if ( 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[(unsigned char)*s]; if ( Q_IsColorString( s ) ) { if ( *( s + 1 ) == COLOR_NULL ) { memcpy( newColor, color, sizeof( newColor ) ); } else { memcpy( newColor, g_color_table[ColorIndex( *( s + 1 ) )], sizeof( newColor ) ); newColor[3] = color[3]; } trap_R_SetColor( newColor ); s += 2; continue; } else { float yadj = scaley * 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_Ext( x + ( glyph->pitch * scalex ) + ofs, y - yadj + ofs, glyph->imageWidth, glyph->imageHeight, scalex, scaley, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph ); colorBlack[3] = 1.0; trap_R_SetColor( newColor ); } CG_Text_PaintChar_Ext( x + ( glyph->pitch * scalex ), y - yadj, glyph->imageWidth, glyph->imageHeight, scalex, scaley, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph ); x += ( glyph->xSkip * scalex ) + adjust; s++; count++; } } trap_R_SetColor( NULL ); } } void CG_Text_Paint( float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style ) { fontInfo_t *font = &cgDC.Assets.fonts[activeFont]; CG_Text_Paint_Ext( x, y, scale, scale, color, text, adjust, limit, style, font ); } // NERVE - SMF - added back in int CG_DrawFieldWidth( int x, int y, int width, int value, int charWidth, int charHeight ) { char num[16], *ptr; int l; int frame; int totalwidth = 0; if ( width < 1 ) { return 0; } // draw number string if ( width > 5 ) { width = 5; } switch ( width ) { case 1: value = value > 9 ? 9 : value; value = value < 0 ? 0 : value; break; case 2: value = value > 99 ? 99 : value; value = value < -9 ? -9 : value; break; case 3: value = value > 999 ? 999 : value; value = value < -99 ? -99 : value; break; case 4: value = value > 9999 ? 9999 : value; value = value < -999 ? -999 : value; break; } Com_sprintf( num, sizeof( num ), "%i", value ); l = strlen( num ); if ( l > width ) { l = width; } ptr = num; while ( *ptr && l ) { if ( *ptr == '-' ) { frame = STAT_MINUS; } else { frame = *ptr - '0'; } totalwidth += charWidth; ptr++; l--; } return totalwidth; } int CG_DrawField( int x, int y, int width, int value, int charWidth, int charHeight, qboolean dodrawpic, qboolean leftAlign ) { char num[16], *ptr; int l; int frame; int startx; if ( width < 1 ) { return 0; } // draw number string if ( width > 5 ) { width = 5; } switch ( width ) { case 1: value = value > 9 ? 9 : value; value = value < 0 ? 0 : value; break; case 2: value = value > 99 ? 99 : value; value = value < -9 ? -9 : value; break; case 3: value = value > 999 ? 999 : value; value = value < -99 ? -99 : value; break; case 4: value = value > 9999 ? 9999 : value; value = value < -999 ? -999 : value; break; } Com_sprintf( num, sizeof( num ), "%i", value ); l = strlen( num ); if ( l > width ) { l = width; } // NERVE - SMF if ( !leftAlign ) { x -= 2 + charWidth * ( l ); } startx = x; ptr = num; while ( *ptr && l ) { if ( *ptr == '-' ) { frame = STAT_MINUS; } else { frame = *ptr - '0'; } if ( dodrawpic ) { CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[frame] ); } x += charWidth; ptr++; l--; } return startx; } // -NERVE - SMF /* ================ CG_Draw3DModel ================ */ void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles ) { refdef_t refdef; refEntity_t ent; CG_AdjustFrom640( &x, &y, &w, &h ); memset( &refdef, 0, sizeof( refdef ) ); memset( &ent, 0, sizeof( ent ) ); AnglesToAxis( angles, ent.axis ); VectorCopy( origin, ent.origin ); ent.hModel = model; ent.customSkin = skin; ent.renderfx = RF_NOSHADOW; // no stencil shadows refdef.rdflags = RDF_NOWORLDMODEL; AxisClear( refdef.viewaxis ); refdef.fov_x = 30; refdef.fov_y = 30; refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; refdef.time = cg.time; trap_R_ClearScene(); trap_R_AddRefEntityToScene( &ent ); trap_R_RenderScene( &refdef ); } /* ============== CG_DrawKeyModel ============== */ void CG_DrawKeyModel( int keynum, float x, float y, float w, float h, int fadetime ) { qhandle_t cm; float len; vec3_t origin, angles; vec3_t mins, maxs; VectorClear( angles ); cm = cg_items[keynum].models[0]; // offset the origin y and z to center the model trap_R_ModelBounds( cm, mins, maxs ); origin[2] = -0.5 * ( mins[2] + maxs[2] ); origin[1] = 0.5 * ( mins[1] + maxs[1] ); // len = 0.5 * ( maxs[2] - mins[2] ); len = 0.75 * ( maxs[2] - mins[2] ); origin[0] = len / 0.268; // len / tan( fov/2 ) angles[YAW] = 30 * sin( cg.time / 2000.0 );; CG_Draw3DModel( x, y, w, h, cg_items[keynum].models[0], 0, origin, angles ); } /* ================ CG_DrawTeamBackground ================ */ void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team ) { vec4_t hcolor; hcolor[3] = alpha; if ( team == TEAM_AXIS ) { hcolor[0] = 1; hcolor[1] = 0; hcolor[2] = 0; } else if ( team == TEAM_ALLIES ) { hcolor[0] = 0; hcolor[1] = 0; hcolor[2] = 1; } else { return; } trap_R_SetColor( hcolor ); CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar ); trap_R_SetColor( NULL ); } /* =========================================================================================== UPPER RIGHT CORNER =========================================================================================== */ #define UPPERRIGHT_X 634 /* ================== CG_DrawSnapshot ================== */ static float CG_DrawSnapshot( float y ) { char *s; int w; s = va( "time:%i snap:%i cmd:%i", cg.snap->serverTime, cg.latestSnapshotNum, cgs.serverCommandSequence ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( UPPERRIGHT_X - w, y + 2, s, 1.0F ); return y + BIGCHAR_HEIGHT + 4; } /* ================== CG_DrawFPS ================== */ #define FPS_FRAMES 4 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; vec4_t timerBackground = { 0.16f, 0.2f, 0.17f, 0.8f }; vec4_t timerBorder = { 0.5f, 0.5f, 0.5f, 0.5f }; vec4_t tclr = { 0.625f, 0.625f, 0.6f, 1.0f }; // 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( "%i FPS", fps ); w = CG_Text_Width_Ext( s, 0.19f, 0, &cgs.media.limboFont1 ); CG_FillRect( UPPERRIGHT_X - w - 2, y, w + 5, 12 + 2, timerBackground ); CG_DrawRect_FixedBorder( UPPERRIGHT_X - w - 2, y, w + 5, 12 + 2, 1, timerBorder ); CG_Text_Paint_Ext( UPPERRIGHT_X - w, y + 11, 0.19f, 0.19f, tclr, s, 0, 0, 0, &cgs.media.limboFont1 ); } return y + 12 + 4; } /* ================= CG_DrawTimer ================= */ static float CG_DrawTimer( float y ) { char *s; int w; int mins, seconds, tens; int msec; char *rt; vec4_t color = { 0.625f, 0.625f, 0.6f, 1.0f }; vec4_t timerBackground = { 0.16f, 0.2f, 0.17f, 0.8f }; vec4_t timerBorder = { 0.5f, 0.5f, 0.5f, 0.5f }; rt = ( cgs.gametype != GT_WOLF_LMS && cg_drawReinforcementTime.integer > 0 ) ? va( "^F%d%s", CG_CalculateReinfTime( qfalse ), ( ( cgs.timelimit <= 0.0f ) ? "" : " " ) ) : ""; msec = ( cgs.timelimit * 60.f * 1000.f ) - ( cg.time - cgs.levelStartTime ); seconds = msec / 1000; mins = seconds / 60; seconds -= mins * 60; tens = seconds / 10; seconds -= tens * 10; if ( cgs.gamestate != GS_PLAYING ) { //% s = va( "%s^*WARMUP", rt ); s = "^*WARMUP"; // ydnar: don't draw reinforcement time in warmup mode color[3] = fabs( sin( cg.time * 0.002 ) ); } else if ( msec < 0 && cgs.timelimit > 0.0f ) { s = va( "^N0:00" ); color[3] = fabs( sin( cg.time * 0.002 ) ); } else { if ( cgs.timelimit <= 0.0f ) { s = va( "%s", rt ); } else { s = va( "%s^*%i:%i%i", rt, mins, tens, seconds ); } color[3] = 1.f; } w = CG_Text_Width_Ext( s, 0.19f, 0, &cgs.media.limboFont1 ); CG_FillRect( UPPERRIGHT_X - w - 2, y, w + 5, 12 + 2, timerBackground ); CG_DrawRect_FixedBorder( UPPERRIGHT_X - w - 2, y, w + 5, 12 + 2, 1, timerBorder ); CG_Text_Paint_Ext( UPPERRIGHT_X - w, y + 11, 0.19f, 0.19f, color, s, 0, 0, 0, &cgs.media.limboFont1 ); return y + 12 + 4; } // START xkan, 8/29/2002 int CG_BotIsSelected( int clientNum ) { int i; for ( i = 0; i < MAX_NUM_BUDDY; i++ ) { if ( cg.selectedBotClientNumber[i] == 0 ) { return 0; } else if ( cg.selectedBotClientNumber[i] == clientNum ) { return 1; } } return 0; } // END xkan, 8/29/2002 /* ================= CG_DrawTeamOverlay ================= */ int maxCharsBeforeOverlay; #define TEAM_OVERLAY_MAXNAME_WIDTH 16 #define TEAM_OVERLAY_MAXLOCATION_WIDTH 20 /* ===================== CG_DrawUpperRight ===================== */ static void CG_DrawUpperRight( void ) { float y; if ( !cg_drawFireteamOverlay.integer ) { return; } y = 20 + 100 + 32; if ( CG_IsOnFireteam( cg.clientNum ) ) { rectDef_t rect = { 10, 10, 100, 100 }; CG_DrawFireTeamOverlay( &rect ); } else { // CG_DrawTeamOverlay( 0 ); } if ( !( cg.snap->ps.pm_flags & PMF_LIMBO ) && ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) && ( cgs.autoMapExpanded || ( !cgs.autoMapExpanded && ( cg.time - cgs.autoMapExpandTime < 250.f ) ) ) ) { return; } if ( cg_drawRoundTimer.integer ) { y = CG_DrawTimer( y ); } if ( cg_drawFPS.integer ) { y = CG_DrawFPS( y ); } if ( cg_drawSnapshot.integer ) { y = CG_DrawSnapshot( y ); } } /* =========================================================================================== LOWER RIGHT CORNER =========================================================================================== */ #define CHATLOC_X 160 #define CHATLOC_Y 478 #define CHATLOC_TEXT_X ( CHATLOC_X + 0.25f * TINYCHAR_WIDTH ) /* ================= CG_DrawTeamInfo ================= */ static void CG_DrawTeamInfo( void ) { int w, h; int i, len; vec4_t hcolor; int chatHeight; float alphapercent; float lineHeight = 9.f; int chatWidth = 640 - CHATLOC_X - 100; if ( cg_teamChatHeight.integer < TEAMCHAT_HEIGHT ) { chatHeight = cg_teamChatHeight.integer; } else { chatHeight = TEAMCHAT_HEIGHT; } if ( chatHeight <= 0 ) { return; // disabled } if ( cgs.teamLastChatPos != cgs.teamChatPos ) { if ( cg.time - cgs.teamChatMsgTimes[cgs.teamLastChatPos % chatHeight] > cg_teamChatTime.integer ) { cgs.teamLastChatPos++; } h = ( cgs.teamChatPos - cgs.teamLastChatPos ) * lineHeight; w = 0; for ( i = cgs.teamLastChatPos; i < cgs.teamChatPos; i++ ) { len = CG_Text_Width_Ext( cgs.teamChatMsgs[i % chatHeight], 0.2f, 0, &cgs.media.limboFont2 ); if ( len > w ) { w = len; } } w *= TINYCHAR_WIDTH; w += TINYCHAR_WIDTH * 2; for ( i = cgs.teamChatPos - 1; i >= cgs.teamLastChatPos; i-- ) { alphapercent = 1.0f - ( cg.time - cgs.teamChatMsgTimes[i % chatHeight] ) / (float)( cg_teamChatTime.integer ); if ( alphapercent > 1.0f ) { alphapercent = 1.0f; } else if ( alphapercent < 0.f ) { alphapercent = 0.f; } if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_AXIS ) { hcolor[0] = 1; hcolor[1] = 0; hcolor[2] = 0; } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_ALLIES ) { hcolor[0] = 0; hcolor[1] = 0; hcolor[2] = 1; } else { hcolor[0] = 0; hcolor[1] = 1; hcolor[2] = 0; } hcolor[3] = 0.33f * alphapercent; trap_R_SetColor( hcolor ); CG_DrawPic( CHATLOC_X, CHATLOC_Y - ( cgs.teamChatPos - i ) * lineHeight, chatWidth, lineHeight, cgs.media.teamStatusBar ); hcolor[0] = hcolor[1] = hcolor[2] = 1.0; hcolor[3] = alphapercent; trap_R_SetColor( hcolor ); CG_Text_Paint_Ext( CHATLOC_TEXT_X, CHATLOC_Y - ( cgs.teamChatPos - i - 1 ) * lineHeight - 1, 0.2f, 0.2f, hcolor, cgs.teamChatMsgs[i % chatHeight], 0, 0, 0, &cgs.media.limboFont2 ); } } } const char* CG_PickupItemText( int item ) { if ( bg_itemlist[ item ].giType == IT_HEALTH ) { if ( bg_itemlist[ item ].world_model[2] ) { // this is a multi-stage item // FIXME: print the correct amount for multi-stage return va( "a %s", bg_itemlist[ item ].pickup_name ); } else { return va( "%i %s", bg_itemlist[ item ].quantity, bg_itemlist[ item ].pickup_name ); } } else if ( bg_itemlist[ item ].giType == IT_TEAM ) { return "an Objective"; } else { if ( bg_itemlist[ item ].pickup_name[0] == 'a' || bg_itemlist[ item ].pickup_name[0] == 'A' ) { return va( "an %s", bg_itemlist[ item ].pickup_name ); } else { return va( "a %s", bg_itemlist[ item ].pickup_name ); } } } /* ================= CG_DrawNotify ================= */ #define NOTIFYLOC_Y 42 // bottom end #define NOTIFYLOC_X 0 #define NOTIFYLOC_Y_SP 128 static void CG_DrawNotify( void ) { int w, h; int i, len; vec4_t hcolor; int chatHeight; float alphapercent; char var[MAX_TOKEN_CHARS]; float notifytime = 1.0f; int yLoc; return; yLoc = NOTIFYLOC_Y; trap_Cvar_VariableStringBuffer( "con_notifytime", var, sizeof( var ) ); notifytime = atof( var ) * 1000; if ( notifytime <= 100.f ) { notifytime = 100.0f; } chatHeight = NOTIFY_HEIGHT; if ( cgs.notifyLastPos != cgs.notifyPos ) { if ( cg.time - cgs.notifyMsgTimes[cgs.notifyLastPos % chatHeight] > notifytime ) { cgs.notifyLastPos++; } h = ( cgs.notifyPos - cgs.notifyLastPos ) * TINYCHAR_HEIGHT; w = 0; for ( i = cgs.notifyLastPos; i < cgs.notifyPos; i++ ) { len = CG_DrawStrlen( cgs.notifyMsgs[i % chatHeight] ); if ( len > w ) { w = len; } } w *= TINYCHAR_WIDTH; w += TINYCHAR_WIDTH * 2; if ( maxCharsBeforeOverlay <= 0 ) { maxCharsBeforeOverlay = 80; } for ( i = cgs.notifyPos - 1; i >= cgs.notifyLastPos; i-- ) { alphapercent = 1.0f - ( ( cg.time - cgs.notifyMsgTimes[i % chatHeight] ) / notifytime ); if ( alphapercent > 0.5f ) { alphapercent = 1.0f; } else { alphapercent *= 2; } if ( alphapercent < 0.f ) { alphapercent = 0.f; } hcolor[0] = hcolor[1] = hcolor[2] = 1.0; hcolor[3] = alphapercent; trap_R_SetColor( hcolor ); CG_DrawStringExt( NOTIFYLOC_X + TINYCHAR_WIDTH, yLoc - ( cgs.notifyPos - i ) * TINYCHAR_HEIGHT, cgs.notifyMsgs[i % chatHeight], hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, maxCharsBeforeOverlay ); } } } /* =============================================================================== 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; // bk010215 - FIXME char message[1024]; // OSP - dont draw if a demo and we're running at a different timescale if ( cg.demoPlayback && cg_timescale.value != 1.0f ) { return; } // ydnar: don't draw if the server is respawning if ( cg.serverRespawning ) { return; } // draw the phone jack if we are completely past our buffers cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1; trap_GetUserCmd( cmdNum, &cmd ); if ( cmd.serverTime <= cg.snap->ps.commandTime || cmd.serverTime > cg.time ) { // special check for map_restart // bk 0102165 - FIXME return; } // also add text in center of screen s = CG_TranslateString( "Connection Interrupted" ); // bk 010215 - FIXME w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 320 - w / 2, 100, s, 1.0F ); // blink the icon if ( ( cg.time >> 9 ) & 1 ) { return; } x = 640 - 48; y = 480 - 200; CG_DrawPic( x, y, 48, 48, cgs.media.disconnectIcon ); } #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 ) { // if(0) { CG_DrawDisconnect(); return; } // // draw the graph // x = 640 - 48; y = 480 - 200; 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( colorYellow ); } 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( colorBlue ); } 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( colorYellow ); } } else { if ( color != 3 ) { color = 3; trap_R_SetColor( colorGreen ); } } 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( colorRed ); } 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 #ifdef ALLOW_GSYNC || cg_synchronousClients.integer #endif // ALLOW_GSYNC ) { CG_DrawBigString( ax, ay, "snc", 1.0 ); } CG_DrawDisconnect(); } void CG_DrawLivesLeft( void ) { if ( cg_gameType.integer == GT_WOLF_LMS ) { return; } if ( cg.snap->ps.persistant[PERS_RESPAWNS_LEFT] < 0 ) { return; } CG_DrawPic( 4, 360, 48, 24, cg.snap->ps.persistant[PERS_TEAM] == TEAM_ALLIES ? cgs.media.hudAlliedHelmet : cgs.media.hudAxisHelmet ); CG_DrawField( 44, 360, 3, cg.snap->ps.persistant[PERS_RESPAWNS_LEFT], 14, 20, qtrue, qtrue ); } /* =============================================================================== CENTER PRINTING =============================================================================== */ /* ============== CG_CenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ #define CP_LINEWIDTH 56 // NERVE - SMF void CG_CenterPrint( const char *str, int y, int charWidth ) { char *s; int i, len; // NERVE - SMF qboolean neednewline = qfalse; // NERVE - SMF int priority = 0; // NERVE - SMF - don't draw if this print message is less important if ( cg.centerPrintTime && priority < cg.centerPrintPriority ) { return; } Q_strncpyz( cg.centerPrint, str, sizeof( cg.centerPrint ) ); cg.centerPrintPriority = priority; // NERVE - SMF // NERVE - SMF - turn spaces into newlines, if we've run over the linewidth len = strlen( cg.centerPrint ); for ( i = 0; i < len; i++ ) { // NOTE: subtract a few chars here so long words still get displayed properly if ( i % ( CP_LINEWIDTH - 20 ) == 0 && i > 0 ) { neednewline = qtrue; } if ( cg.centerPrint[i] == ' ' && neednewline ) { cg.centerPrint[i] = '\n'; neednewline = qfalse; } } // -NERVE - SMF 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++; } } // NERVE - SMF /* ============== CG_PriorityCenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ void CG_PriorityCenterPrint( const char *str, int y, int charWidth, int priority ) { char *s; int i, len; // NERVE - SMF qboolean neednewline = qfalse; // NERVE - SMF // NERVE - SMF - don't draw if this print message is less important if ( cg.centerPrintTime && priority < cg.centerPrintPriority ) { return; } Q_strncpyz( cg.centerPrint, str, sizeof( cg.centerPrint ) ); cg.centerPrintPriority = priority; // NERVE - SMF // NERVE - SMF - turn spaces into newlines, if we've run over the linewidth len = strlen( cg.centerPrint ); for ( i = 0; i < len; i++ ) { // NOTE: subtract a few chars here so long words still get displayed properly if ( i % ( CP_LINEWIDTH - 20 ) == 0 && i > 0 ) { neednewline = qtrue; } if ( cg.centerPrint[i] == ' ' && neednewline ) { cg.centerPrint[i] = '\n'; neednewline = qfalse; } } // -NERVE - SMF cg.centerPrintTime = cg.time + 2000; 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++; } } // -NERVE - SMF /* =================== CG_DrawCenterString =================== */ static void CG_DrawCenterString( void ) { char *start; int l; int x, y, w; float *color; if ( !cg.centerPrintTime ) { return; } color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value ); if ( !color ) { cg.centerPrintTime = 0; cg.centerPrintPriority = 0; 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 < CP_LINEWIDTH; l++ ) { // NERVE - SMF - added CP_LINEWIDTH if ( !start[l] || start[l] == '\n' ) { break; } linebuffer[l] = start[l]; } linebuffer[l] = 0; w = cg.centerPrintCharWidth * CG_DrawStrlen( linebuffer ); x = ( SCREEN_WIDTH - w ) / 2; CG_DrawStringExt( x, y, linebuffer, color, qfalse, qtrue, cg.centerPrintCharWidth, (int)( cg.centerPrintCharWidth * 1.5 ), 0 ); y += cg.centerPrintCharWidth * 1.5; while ( *start && ( *start != '\n' ) ) { start++; } if ( !*start ) { break; } start++; } trap_R_SetColor( NULL ); } /* ================================================================================ CROSSHAIRS ================================================================================ */ /* ============== CG_DrawWeapReticle ============== */ static void CG_DrawWeapReticle( void ) { vec4_t color = {0, 0, 0, 1}; qboolean fg, garand, k43; // DHM - Nerve :: So that we will draw reticle if ( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) || cg.demoPlayback ) { garand = (qboolean)( cg.snap->ps.weapon == WP_GARAND_SCOPE ); k43 = (qboolean)( cg.snap->ps.weapon == WP_K43_SCOPE ); fg = (qboolean)( cg.snap->ps.weapon == WP_FG42SCOPE ); } else { fg = (qboolean)( cg.weaponSelect == WP_FG42SCOPE ); garand = (qboolean)( cg.weaponSelect == WP_GARAND_SCOPE ); k43 = (qboolean)( cg.weaponSelect == WP_K43_SCOPE ); } if ( fg ) { // sides CG_FillRect( 0, 0, 80, 480, color ); CG_FillRect( 560, 0, 80, 480, color ); // center if ( cgs.media.reticleShaderSimple ) { CG_DrawPic( 80, 0, 480, 480, cgs.media.reticleShaderSimple ); } /* if(cgs.media.reticleShaderSimpleQ) { trap_R_DrawStretchPic( x, 0, w, h, 0, 0, 1, 1, cgs.media.reticleShaderSimpleQ ); // tl trap_R_DrawStretchPic( x+w, 0, w, h, 1, 0, 0, 1, cgs.media.reticleShaderSimpleQ ); // tr trap_R_DrawStretchPic( x, h, w, h, 0, 1, 1, 0, cgs.media.reticleShaderSimpleQ ); // bl trap_R_DrawStretchPic( x+w, h, w, h, 1, 1, 0, 0, cgs.media.reticleShaderSimpleQ ); // br }*/ // hairs CG_FillRect( 84, 239, 150, 3, color ); // left CG_FillRect( 234, 240, 173, 1, color ); // horiz center CG_FillRect( 407, 239, 150, 3, color ); // right CG_FillRect( 319, 2, 3, 151, color ); // top center top CG_FillRect( 320, 153, 1, 114, color ); // top center bot CG_FillRect( 320, 241, 1, 87, color ); // bot center top CG_FillRect( 319, 327, 3, 151, color ); // bot center bot } else if ( garand ) { // sides CG_FillRect( 0, 0, 80, 480, color ); CG_FillRect( 560, 0, 80, 480, color ); // center if ( cgs.media.reticleShaderSimple ) { CG_DrawPic( 80, 0, 480, 480, cgs.media.reticleShaderSimple ); } // hairs CG_FillRect( 84, 239, 177, 2, color ); // left CG_FillRect( 320, 242, 1, 58, color ); // center top CG_FillRect( 319, 300, 2, 178, color ); // center bot CG_FillRect( 380, 239, 177, 2, color ); // right } else if ( k43 ) { // sides CG_FillRect( 0, 0, 80, 480, color ); CG_FillRect( 560, 0, 80, 480, color ); // center if ( cgs.media.reticleShaderSimple ) { CG_DrawPic( 80, 0, 480, 480, cgs.media.reticleShaderSimple ); } // hairs CG_FillRect( 84, 239, 177, 2, color ); // left CG_FillRect( 320, 242, 1, 58, color ); // center top CG_FillRect( 319, 300, 2, 178, color ); // center bot CG_FillRect( 380, 239, 177, 2, color ); // right } } /* ============== CG_DrawMortarReticle ============== */ static void CG_DrawMortarReticle( void ) { vec4_t color = { 1.f, 1.f, 1.f, .5f }; vec4_t color_back = { 0.f, 0.f, 0.f, .25f }; vec4_t color_extends = { .77f, .73f, .1f, 1.f }; vec4_t color_lastfire = { .77f, .1f, .1f, 1.f }; //vec4_t color_firerequest = { .23f, 1.f, .23f, 1.f }; vec4_t color_firerequest = { 1.f, 1.f, 1.f, 1.f }; float offset, localOffset; int i, min, majorOffset, val, printval, fadeTime; char *s; float angle, angleMin, angleMax; qboolean hasRightTarget, hasLeftTarget; // Background CG_FillRect( 136, 236, 154, 38, color_back ); CG_FillRect( 290, 160, 60, 208, color_back ); CG_FillRect( 350, 236, 154, 38, color_back ); // Horizontal bar // bottom CG_FillRect( 140, 264, 150, 1, color ); // left CG_FillRect( 350, 264, 150, 1, color ); // right // 10 units - 5 degrees // total of 360 units // nothing displayed between 150 and 210 units // 360 / 10 = 36 bits, means 36 * 5 = 180 degrees // that means left is cg.predictedPlayerState.viewangles[YAW] - .5f * 180 angle = 360 - AngleNormalize360( cg.predictedPlayerState.viewangles[YAW] - 90.f ); offset = ( 5.f / 65536 ) * ( (int)( angle * ( 65536 / 5.f ) ) & 65535 ); min = (int)( AngleNormalize360( angle - .5f * 180 ) / 15.f ) * 15; majorOffset = (int)( floor( (int)floor( AngleNormalize360( angle - .5f * 180 ) ) % 15 ) / 5.f ); for ( val = i = 0; i < 36; i++ ) { localOffset = i * 10.f + ( offset * 2.f ); if ( localOffset >= 150 && localOffset <= 210 ) { if ( i % 3 == majorOffset ) { val++; } continue; } if ( i % 3 == majorOffset ) { printval = min - val * 15 + 180; // rain - old tertiary abuse was nasty and had undefined result if ( printval < 0 ) { printval += 360; } else if ( printval >= 360 ) { printval -= 360; } s = va( "%i", printval ); //CG_Text_Paint_Ext( 140 + localOffset - .5f * CG_Text_Width_Ext( s, .15f, 0, &cgs.media.limboFont1 ), 244, .15f, .15f, color, s, 0, 0, 0, &cgs.media.limboFont1 ); //CG_FillRect( 140 + localOffset, 248, 1, 16, color); CG_Text_Paint_Ext( 500 - localOffset - .5f * CG_Text_Width_Ext( s, .15f, 0, &cgs.media.limboFont1 ), 244, .15f, .15f, color, s, 0, 0, 0, &cgs.media.limboFont1 ); CG_FillRect( 500 - localOffset, 248, 1, 16, color ); val++; } else { //CG_FillRect( 140 + localOffset, 256, 1, 8, color); CG_FillRect( 500 - localOffset, 256, 1, 8, color ); } } // the extremes // 30 degrees plus a 15 degree border angleMin = AngleNormalize360( 360 - ( cg.pmext.mountedWeaponAngles[YAW] - 90.f ) - ( 30.f + 15.f ) ); angleMax = AngleNormalize360( 360 - ( cg.pmext.mountedWeaponAngles[YAW] - 90.f ) + ( 30.f + 15.f ) ); // right localOffset = ( AngleNormalize360( angle - angleMin ) / 5.f ) * 10.f; //CG_FillRect( 320 + localOffset, 252, 2, 18, color_extends); CG_FillRect( 320 - localOffset, 252, 2, 18, color_extends ); // left localOffset = ( AngleNormalize360( angleMax - angle ) / 5.f ) * 10.f; //CG_FillRect( 320 - localOffset, 252, 2, 18, color_extends); CG_FillRect( 320 + localOffset, 252, 2, 18, color_extends ); // last fire pos fadeTime = 0; if ( cg.lastFiredWeapon == WP_MORTAR_SET && cg.mortarImpactTime >= -1 ) { fadeTime = cg.time - ( cg.predictedPlayerEntity.muzzleFlashTime + 5000 ); if ( fadeTime < 3000 ) { float lastfireAngle; if ( fadeTime > 0 ) { color_lastfire[3] = 1.f - ( fadeTime / 3000.f ); } lastfireAngle = AngleNormalize360( 360 - ( cg.mortarFireAngles[YAW] - 90.f ) ); localOffset = ( ( AngleSubtract( angle, lastfireAngle ) ) / 5.f ) * 10.f; //CG_FillRect( 320 + localOffset, 252, 2, 18, color_lastfire); CG_FillRect( 320 - localOffset, 252, 2, 18, color_lastfire ); } } // mortar attack requests hasRightTarget = hasLeftTarget = qfalse; for ( i = 0; i < MAX_CLIENTS; i++ ) { int requestFadeTime = cg.time - ( cg.artilleryRequestTime[i] + 25000 ); if ( requestFadeTime < 5000 ) { vec3_t dir; float yaw; float attackRequestAngle; VectorSubtract( cg.artilleryRequestPos[i], cg.predictedPlayerEntity.lerpOrigin, dir ); // ripped this out of vectoangles if ( dir[1] == 0 && dir[0] == 0 ) { yaw = 0; } else { if ( dir[0] ) { yaw = ( atan2( dir[1], dir[0] ) * 180 / M_PI ); } else if ( dir[1] > 0 ) { yaw = 90; } else { yaw = 270; } if ( yaw < 0 ) { yaw += 360; } } if ( requestFadeTime > 0 ) { color_firerequest[3] = 1.f - ( requestFadeTime / 5000.f ); } attackRequestAngle = AngleNormalize360( 360 - ( yaw - 90.f ) ); yaw = AngleSubtract( attackRequestAngle, angleMin ); if ( yaw < 0 ) { if ( !hasLeftTarget ) { //CG_FillRect( 136 + 2, 236 + 38 - 6, 4, 4, color_firerequest ); trap_R_SetColor( color_firerequest ); CG_DrawPic( 136 + 2, 236 + 38 - 10 + 1, 8, 8, cgs.media.ccMortarTargetArrow ); trap_R_SetColor( NULL ); hasLeftTarget = qtrue; } } else if ( yaw > 90 ) { if ( !hasRightTarget ) { //CG_FillRect( 350 + 154 - 6, 236 + 38 - 6, 4, 4, color_firerequest ); trap_R_SetColor( color_firerequest ); CG_DrawPic( 350 + 154 - 10, 236 + 38 - 10 + 1, -8, 8, cgs.media.ccMortarTargetArrow ); trap_R_SetColor( NULL ); hasRightTarget = qtrue; } } else { localOffset = ( ( AngleSubtract( angle, attackRequestAngle ) ) / 5.f ) * 10.f; //CG_FillRect( 320 + localOffset - 3, 264 - 3, 6, 6, color_firerequest ); trap_R_SetColor( color_firerequest ); //CG_DrawPic( 320 + localOffset - 8, 264 - 8, 16, 16, cgs.media.ccMortarTarget ); CG_DrawPic( 320 - localOffset - 8, 264 - 8, 16, 16, cgs.media.ccMortarTarget ); trap_R_SetColor( NULL ); } } } /*s = va( "%.2f (%i / %i)",AngleNormalize360(angle - .5f * 180), majorOffset, min ); CG_Text_Paint( 140, 224, .25f, color, s, 0, 0, 0 ); s = va( "%.2f",AngleNormalize360(angle) ); CG_Text_Paint( 320 - .5f * CG_Text_Width( s, .25f, 0), 224, .25f, color, s, 0, 0, 0 ); s = va( "%.2f", AngleNormalize360(angle + .5f * 180) ); CG_Text_Paint( 500 - CG_Text_Width( s, .25f, 0 ), 224, .25f, color, s, 0, 0, 0 );*/ // Vertical bar // sides CG_FillRect( 295, 164, 1, 200, color ); // left CG_FillRect( 345, 164, 1, 200, color ); // right // 10 units - 2.5 degrees // total of 200 units // 200 / 10 = 20 bits, means 20 * 2.5 = 50 degrees // that means left is cg.predictedPlayerState.viewangles[PITCH] - .5f * 50 angle = AngleNormalize180( 360 - ( cg.predictedPlayerState.viewangles[PITCH] - 60 ) ); offset = ( 2.5f / 65536 ) * ( (int)( angle * ( 65536 / 2.5f ) ) & 65535 ); min = floor( ( angle + .5f * 50 ) / 10.f ) * 10; majorOffset = (int)( floor( (int)( ( angle + .5f * 50 ) * 10.f ) % 100 ) / 25.f ); for ( val = i = 0; i < 20; i++ ) { localOffset = i * 10.f + ( offset * 4.f ); /*if( localOffset >= 150 && localOffset <= 210 ) { if( i % 3 == majorOffset) val++; continue; }*/ if ( i % 4 == majorOffset ) { printval = min - val * 10; // rain - old tertiary abuse was nasty and had undefined result if ( printval <= -180 ) { printval += 360; } else if ( printval >= 180 ) { printval -= 180; } s = va( "%i", printval ); CG_Text_Paint_Ext( 320 - .5f * CG_Text_Width_Ext( s, .15f, 0, &cgs.media.limboFont1 ), 164 + localOffset + .5f * CG_Text_Height_Ext( s, .15f, 0, &cgs.media.limboFont1 ), .15f, .15f, color, s, 0, 0, 0, &cgs.media.limboFont1 ); CG_FillRect( 295 + 1, 164 + localOffset, 12, 1, color ); CG_FillRect( 345 - 12, 164 + localOffset, 12, 1, color ); val++; } else { CG_FillRect( 295 + 1, 164 + localOffset, 8, 1, color ); CG_FillRect( 345 - 8, 164 + localOffset, 8, 1, color ); } } // the extremes // 30 degrees up // 20 degrees down angleMin = AngleNormalize180( 360 - ( cg.pmext.mountedWeaponAngles[PITCH] - 60 ) ) - 20.f; angleMax = AngleNormalize180( 360 - ( cg.pmext.mountedWeaponAngles[PITCH] - 60 ) ) + 30.f; // top localOffset = angleMax - angle; if ( localOffset < 0 ) { localOffset = 0; } localOffset = ( AngleNormalize360( localOffset ) / 2.5f ) * 10.f; if ( localOffset < 100 ) { CG_FillRect( 295 - 2, 264 - localOffset, 6, 2, color_extends ); CG_FillRect( 345 - 4 + 1, 264 - localOffset, 6, 2, color_extends ); } // bottom localOffset = angle - angleMin; if ( localOffset < 0 ) { localOffset = 0; } localOffset = ( AngleNormalize360( localOffset ) / 2.5f ) * 10.f; if ( localOffset < 100 ) { CG_FillRect( 295 - 2, 264 + localOffset, 6, 2, color_extends ); CG_FillRect( 345 - 4 + 1, 264 + localOffset, 6, 2, color_extends ); } // last fire pos if ( cg.lastFiredWeapon == WP_MORTAR_SET && cg.mortarImpactTime >= -1 ) { if ( fadeTime < 3000 ) { float lastfireAngle; lastfireAngle = AngleNormalize180( 360 - ( cg.mortarFireAngles[PITCH] - 60 ) ); if ( lastfireAngle > angle ) { localOffset = lastfireAngle - angle; if ( localOffset < 0 ) { localOffset = 0; } localOffset = ( AngleNormalize360( localOffset ) / 2.5f ) * 10.f; if ( localOffset < 100 ) { CG_FillRect( 295 - 2, 264 - localOffset, 6, 2, color_lastfire ); CG_FillRect( 345 - 4 + 1, 264 - localOffset, 6, 2, color_lastfire ); } } else { localOffset = angle - lastfireAngle; if ( localOffset < 0 ) { localOffset = 0; } localOffset = ( AngleNormalize360( localOffset ) / 2.5f ) * 10.f; if ( localOffset < 100 ) { CG_FillRect( 295 - 2, 264 + localOffset, 6, 2, color_lastfire ); CG_FillRect( 345 - 4 + 1, 264 + localOffset, 6, 2, color_lastfire ); } } } } /*s = va( "%.2f (%i / %i)", angle + .5f * 50, majorOffset, min ); CG_Text_Paint( 348, 164, .25f, color, s, 0, 0, 0 ); s = va( "%.2f",angle ); CG_Text_Paint( 348, 264, .25f, color, s, 0, 0, 0 ); s = va( "%.2f", angle - .5f * 50 ); CG_Text_Paint( 348, 364, .25f, color, s, 0, 0, 0 );*/ } /* ============== CG_DrawBinocReticle ============== */ static void CG_DrawBinocReticle( void ) { // an alternative. This gives nice sharp lines at the expense of a few extra polys vec4_t color; color[0] = color[1] = color[2] = 0; color[3] = 1; if ( cgs.media.binocShaderSimple ) { CG_DrawPic( 0, 0, 640, 480, cgs.media.binocShaderSimple ); } CG_FillRect( 146, 239, 348, 1, color ); CG_FillRect( 188, 234, 1, 13, color ); // ll CG_FillRect( 234, 226, 1, 29, color ); // l CG_FillRect( 274, 234, 1, 13, color ); // lr CG_FillRect( 320, 213, 1, 55, color ); // center CG_FillRect( 360, 234, 1, 13, color ); // rl CG_FillRect( 406, 226, 1, 29, color ); // r CG_FillRect( 452, 234, 1, 13, color ); // rr } void CG_FinishWeaponChange( int lastweap, int newweap ); // JPW NERVE /* ================= CG_DrawCrosshair ================= */ static void CG_DrawCrosshair( void ) { float w, h; qhandle_t hShader; float f; float x, y; int weapnum; // DHM - Nerve if ( cg.renderingThirdPerson ) { return; } // using binoculars if ( cg.zoomedBinoc ) { CG_DrawBinocReticle(); return; } // DHM - Nerve :: show reticle in limbo and spectator if ( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) || cg.demoPlayback ) { weapnum = cg.snap->ps.weapon; } else { weapnum = cg.weaponSelect; } switch ( weapnum ) { // weapons that get no reticle case WP_NONE: // no weapon, no crosshair if ( cg.zoomedBinoc ) { CG_DrawBinocReticle(); } if ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) { return; } break; // special reticle for weapon case WP_FG42SCOPE: case WP_GARAND_SCOPE: case WP_K43_SCOPE: if ( !BG_PlayerMounted( cg.snap->ps.eFlags ) ) { // JPW NERVE -- don't let players run with rifles -- speed 80 == crouch, 128 == walk, 256 == run if ( VectorLengthSquared( cg.snap->ps.velocity ) > SQR( 127 ) ) { if ( cg.snap->ps.weapon == WP_FG42SCOPE ) { CG_FinishWeaponChange( WP_FG42SCOPE, WP_FG42 ); } if ( cg.snap->ps.weapon == WP_GARAND_SCOPE ) { CG_FinishWeaponChange( WP_GARAND_SCOPE, WP_GARAND ); } if ( cg.snap->ps.weapon == WP_K43_SCOPE ) { CG_FinishWeaponChange( WP_K43_SCOPE, WP_K43 ); } } // OSP if ( cg.mvTotalClients < 1 || cg.snap->ps.stats[STAT_HEALTH] > 0 ) { CG_DrawWeapReticle(); } return; } break; default: break; } if ( cg.predictedPlayerState.eFlags & EF_PRONE_MOVING ) { return; } // FIXME: spectators/chasing? if ( cg.predictedPlayerState.weapon == WP_MORTAR_SET && cg.predictedPlayerState.weaponstate != WEAPON_RAISING ) { CG_DrawMortarReticle(); return; } if ( cg_drawCrosshair.integer < 0 ) { //----(SA) moved down so it doesn't keep the scoped weaps from drawing reticles return; } // no crosshair while leaning if ( cg.snap->ps.leanf ) { return; } // TAT 1/10/2003 - Don't draw crosshair if have exit hintcursor if ( cg.snap->ps.serverCursorHint >= HINT_EXIT && cg.snap->ps.serverCursorHint <= HINT_NOEXIT ) { return; } // set color based on health if ( cg_crosshairHealth.integer ) { vec4_t hcolor; CG_ColorForHealth( hcolor ); trap_R_SetColor( hcolor ); } else { trap_R_SetColor( cg.xhairColor ); } w = h = cg_crosshairSize.value; // RF, crosshair size represents aim spread f = (float)( ( cg_crosshairPulse.integer == 0 ) ? 0 : cg.snap->ps.aimSpreadScale / 255.0 ); w *= ( 1 + f * 2.0 ); h *= ( 1 + f * 2.0 ); x = cg_crosshairX.integer; y = cg_crosshairY.integer; CG_AdjustFrom640( &x, &y, &w, &h ); hShader = cgs.media.crosshairShader[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ]; trap_R_DrawStretchPic( x + 0.5 * ( cg.refdef_current->width - w ), y + 0.5 * ( cg.refdef_current->height - h ), w, h, 0, 0, 1, 1, hShader ); if ( cg.crosshairShaderAlt[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ] ) { w = h = cg_crosshairSize.value; x = cg_crosshairX.integer; y = cg_crosshairY.integer; CG_AdjustFrom640( &x, &y, &w, &h ); if ( cg_crosshairHealth.integer == 0 ) { trap_R_SetColor( cg.xhairColorAlt ); } trap_R_DrawStretchPic( x + 0.5 * ( cg.refdef_current->width - w ), y + 0.5 * ( cg.refdef_current->height - h ), w, h, 0, 0, 1, 1, cg.crosshairShaderAlt[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ] ); } } static void CG_DrawNoShootIcon( void ) { float x, y, w, h; float *color; if ( cg.predictedPlayerState.eFlags & EF_PRONE && cg.snap->ps.weapon == WP_PANZERFAUST ) { trap_R_SetColor( colorRed ); } else if ( cg.crosshairClientNoShoot // xkan, 1/6/2003 - don't shoot friend or civilian || cg.snap->ps.serverCursorHint == HINT_PLYR_NEUTRAL || cg.snap->ps.serverCursorHint == HINT_PLYR_FRIEND ) { color = CG_FadeColor( cg.crosshairClientTime, 1000 ); if ( !color ) { trap_R_SetColor( NULL ); return; } else { trap_R_SetColor( color ); } } else { return; } w = h = 48.f; x = cg_crosshairX.integer + 1; y = cg_crosshairY.integer + 1; CG_AdjustFrom640( &x, &y, &w, &h ); // FIXME precache trap_R_DrawStretchPic( x + 0.5 * ( cg.refdef_current->width - w ), y + 0.5 * ( cg.refdef_current->height - h ), w, h, 0, 0, 1, 1, cgs.media.friendShader ); } /* ================= CG_ScanForCrosshairEntity ================= Returns the distance to the entity */ static float CG_ScanForCrosshairEntity( float * zChange, qboolean * hitClient ) { trace_t trace; // gentity_t *traceEnt; vec3_t start, end; float dist; centity_t* cent; // We haven't hit a client yet *hitClient = qfalse; VectorCopy( cg.refdef.vieworg, start ); VectorMA( start, 8192, cg.refdef.viewaxis[0], end ); //----(SA) changed from 8192 cg.crosshairClientNoShoot = qfalse; CG_Trace( &trace, start, NULL, NULL, end, cg.snap->ps.clientNum, CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_ITEM ); // How far from start to end of trace? dist = VectorDistance( start, trace.endpos ); // How far up or down are we looking? *zChange = trace.endpos[2] - start[2]; if ( trace.entityNum >= MAX_CLIENTS ) { if ( cg_entities[trace.entityNum].currentState.eFlags & EF_TAGCONNECT ) { trace.entityNum = cg_entities[trace.entityNum].tagParent; } // is a tank with a healthbar // this might have some side-effects, but none right now as the script_mover is the only one that sets effect1Time if ( ( cg_entities[trace.entityNum].currentState.eType == ET_MOVER && cg_entities[trace.entityNum].currentState.effect1Time ) || cg_entities[trace.entityNum].currentState.eType == ET_CONSTRUCTIBLE_MARKER ) { // update the fade timer cg.crosshairClientNum = trace.entityNum; cg.crosshairClientTime = cg.time; cg.identifyClientRequest = cg.crosshairClientNum; } // Default: We're not looking at a client cg.crosshairNotLookingAtClient = qtrue; return dist; } // traceEnt = &g_entities[trace.entityNum]; // Reset the draw time for the SP crosshair cg.crosshairSPClientTime = cg.time; // Default: We're not looking at a client cg.crosshairNotLookingAtClient = qfalse; // We hit a client *hitClient = qtrue; // update the fade timer cg.crosshairClientNum = trace.entityNum; cg.crosshairClientTime = cg.time; if ( cg.crosshairClientNum != cg.snap->ps.identifyClient && cg.crosshairClientNum != ENTITYNUM_WORLD ) { cg.identifyClientRequest = cg.crosshairClientNum; } cent = &cg_entities[cg.crosshairClientNum]; if ( cent && cent->currentState.powerups & ( 1 << PW_OPS_DISGUISED ) ) { if ( cgs.clientinfo[cg.crosshairClientNum].team == cgs.clientinfo[cg.clientNum].team ) { cg.crosshairClientNoShoot = qtrue; } } return dist; } #define CH_KNIFE_DIST 48 // from g_weapon.c #define CH_LADDER_DIST 100 #define CH_WATER_DIST 100 #define CH_BREAKABLE_DIST 64 #define CH_DOOR_DIST 96 #define CH_DIST 100 //128 // use the largest value from above /* ============== CG_CheckForCursorHints concept in progress... ============== */ void CG_CheckForCursorHints( void ) { trace_t trace; vec3_t start, end; centity_t *tracent; vec3_t pforward, eforward; float dist; if ( cg.renderingThirdPerson ) { return; } if ( cg.snap->ps.serverCursorHint ) { // server is dictating a cursor hint, use it. cg.cursorHintTime = cg.time; cg.cursorHintFade = 500; // fade out time cg.cursorHintIcon = cg.snap->ps.serverCursorHint; cg.cursorHintValue = cg.snap->ps.serverCursorHintVal; return; } // From here on it's client-side cursor hints. So if the server isn't sending that info (as an option) // then it falls into here and you can get basic cursorhint info if you want, but not the detailed info // the server sends. // the trace VectorCopy( cg.refdef_current->vieworg, start ); VectorMA( start, CH_DIST, cg.refdef_current->viewaxis[0], end ); // CG_Trace( &trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, MASK_ALL &~CONTENTS_MONSTERCLIP); CG_Trace( &trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, MASK_PLAYERSOLID ); if ( trace.fraction == 1 ) { return; } dist = trace.fraction * CH_DIST; tracent = &cg_entities[ trace.entityNum ]; // Arnout: invisible entities don't show hints if ( trace.entityNum >= MAX_CLIENTS && ( tracent->currentState.powerups == STATE_INVISIBLE || tracent->currentState.powerups == STATE_UNDERCONSTRUCTION ) ) { return; } // // world // if ( trace.entityNum == ENTITYNUM_WORLD ) { if ( ( trace.surfaceFlags & SURF_LADDER ) && !( cg.snap->ps.pm_flags & PMF_LADDER ) ) { if ( dist <= CH_LADDER_DIST ) { cg.cursorHintIcon = HINT_LADDER; cg.cursorHintTime = cg.time; cg.cursorHintFade = 500; cg.cursorHintValue = 0; } } } else if ( trace.entityNum < MAX_CLIENTS ) { // people // knife if ( trace.entityNum < MAX_CLIENTS && ( cg.snap->ps.weapon == WP_KNIFE ) ) { if ( dist <= CH_KNIFE_DIST ) { AngleVectors( cg.snap->ps.viewangles, pforward, NULL, NULL ); AngleVectors( tracent->lerpAngles, eforward, NULL, NULL ); if ( DotProduct( eforward, pforward ) > 0.6f ) { // from behind(-ish) cg.cursorHintIcon = HINT_KNIFE; cg.cursorHintTime = cg.time; cg.cursorHintFade = 100; cg.cursorHintValue = 0; } } } } } /* ===================== CG_DrawCrosshairNames ===================== */ static void CG_DrawCrosshairNames( void ) { float *color; char *name; float w; const char *s, *playerClass; int playerHealth = 0; vec4_t c; float barFrac; qboolean drawStuff = qfalse; const char *playerRank; qboolean isTank = qfalse; int maxHealth = 1; int i; // Distance to the entity under the crosshair float dist; float zChange; qboolean hitClient = qfalse; if ( cg_drawCrosshair.integer < 0 ) { return; } // scan the known entities to see if the crosshair is sighted on one dist = CG_ScanForCrosshairEntity( &zChange, &hitClient ); if ( cg.renderingThirdPerson ) { return; } // draw the name of the player being looked at color = CG_FadeColor( cg.crosshairClientTime, 1000 ); if ( !color ) { trap_R_SetColor( NULL ); return; } // NERVE - SMF if ( cg.crosshairClientNum > MAX_CLIENTS ) { if ( !cg_drawCrosshairNames.integer ) { return; } if ( cgs.clientinfo[cg.snap->ps.clientNum].team != TEAM_SPECTATOR ) { if ( cg_entities[cg.crosshairClientNum].currentState.eType == ET_MOVER && cg_entities[cg.crosshairClientNum].currentState.effect1Time ) { isTank = qtrue; playerHealth = cg_entities[cg.crosshairClientNum].currentState.dl_intensity; maxHealth = 255; s = Info_ValueForKey( CG_ConfigString( CS_SCRIPT_MOVER_NAMES ), va( "%i", cg.crosshairClientNum ) ); if ( !*s ) { return; } w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor( 320 - w / 2, 170, s, color ); } else if ( cg_entities[cg.crosshairClientNum].currentState.eType == ET_CONSTRUCTIBLE_MARKER ) { s = Info_ValueForKey( CG_ConfigString( CS_CONSTRUCTION_NAMES ), va( "%i", cg.crosshairClientNum ) ); if ( *s ) { w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor( 320 - w / 2, 170, s, color ); } return; } } if ( !isTank ) { return; } } else if ( cgs.clientinfo[cg.crosshairClientNum].team != cgs.clientinfo[cg.snap->ps.clientNum].team ) { if ( ( cg_entities[cg.crosshairClientNum].currentState.powerups & ( 1 << PW_OPS_DISGUISED ) ) && cgs.clientinfo[cg.snap->ps.clientNum].team != TEAM_SPECTATOR ) { if ( cgs.clientinfo[cg.snap->ps.clientNum].team != TEAM_SPECTATOR && cgs.clientinfo[cg.snap->ps.clientNum].skill[SK_SIGNALS] >= 4 && cgs.clientinfo[cg.snap->ps.clientNum].cls == PC_FIELDOPS ) { s = CG_TranslateString( "Disguised Enemy!" ); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; CG_DrawSmallStringColor( 320 - w / 2, 170, s, color ); return; } else if ( dist > 512 ) { if ( !cg_drawCrosshairNames.integer ) { return; } drawStuff = qtrue; // determine player class playerClass = BG_ClassLetterForNumber( ( cg_entities[ cg.crosshairClientNum ].currentState.powerups >> PW_OPS_CLASS_1 ) & 6 ); name = cgs.clientinfo[ cg.crosshairClientNum ].disguiseName; playerRank = cgs.clientinfo[ cg.crosshairClientNum ].team != TEAM_AXIS ? rankNames_Axis[cgs.clientinfo[cg.crosshairClientNum].disguiseRank] : rankNames_Allies[cgs.clientinfo[cg.crosshairClientNum].disguiseRank]; s = va( "[%s] %s %s", CG_TranslateString( playerClass ), playerRank, name ); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; // draw the name and class CG_DrawSmallStringColor( 320 - w / 2, 170, s, color ); // set the health // rain - #480 - make sure it's the health for the right entity; // if it's not, use the clientinfo health (which is updated // by tinfo) if ( cg.crosshairClientNum == cg.snap->ps.identifyClient ) { playerHealth = cg.snap->ps.identifyClientHealth; } else { playerHealth = cgs.clientinfo[ cg.crosshairClientNum ].health; } maxHealth = 100; } else { // rain - #480 - don't show the name after you look away, should this be // a disguised covert cg.crosshairClientTime = 0; return; } } else { return; } } if ( !cg_drawCrosshairNames.integer ) { return; } // Mad Doc - TDF // changed this from early-exiting if true, to only executing most stuff if false. We want to // show debug info regardless // we only want to see players on our team if ( !isTank && !( cgs.clientinfo[cg.snap->ps.clientNum].team != TEAM_SPECTATOR && cgs.clientinfo[ cg.crosshairClientNum ].team != cgs.clientinfo[cg.snap->ps.clientNum].team ) ) { drawStuff = qtrue; // determine player class playerClass = BG_ClassLetterForNumber( cg_entities[ cg.crosshairClientNum ].currentState.teamNum ); name = cgs.clientinfo[ cg.crosshairClientNum ].name; playerRank = cgs.clientinfo[cg.crosshairClientNum].team == TEAM_AXIS ? rankNames_Axis[cgs.clientinfo[cg.crosshairClientNum].rank] : rankNames_Allies[cgs.clientinfo[cg.crosshairClientNum].rank]; s = va( "[%s] %s %s", CG_TranslateString( playerClass ), playerRank, name ); w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; // draw the name and class CG_DrawSmallStringColor( 320 - w / 2, 170, s, color ); // set the health if ( cg.crosshairClientNum == cg.snap->ps.identifyClient ) { playerHealth = cg.snap->ps.identifyClientHealth; } else { playerHealth = cgs.clientinfo[ cg.crosshairClientNum ].health; } maxHealth = 100; for ( i = 0; i < MAX_CLIENTS; i++ ) { if ( !cgs.clientinfo[i].infoValid ) { continue; } if ( cgs.clientinfo[i].team != cgs.clientinfo[cg.snap->ps.clientNum].team ) { continue; } if ( cgs.clientinfo[i].cls != PC_MEDIC ) { continue; } maxHealth += 10; if ( maxHealth >= 125 ) { maxHealth = 125; break; } } if ( cgs.clientinfo[ cg.crosshairClientNum ].skill[SK_BATTLE_SENSE] >= 3 ) { maxHealth += 15; } if ( cgs.clientinfo[ cg.crosshairClientNum ].cls == PC_MEDIC ) { maxHealth *= 1.12f; } } // draw the health bar // if ( isTank || (cg.crosshairClientNum == cg.snap->ps.identifyClient && drawStuff && cgs.clientinfo[cg.snap->ps.clientNum].team != TEAM_SPECTATOR ) ) { vec4_t bgcolor; barFrac = (float)playerHealth / maxHealth; if ( barFrac > 1.0 ) { barFrac = 1.0; } else if ( barFrac < 0 ) { barFrac = 0; } c[0] = 1.0f; c[1] = c[2] = barFrac; c[3] = ( 0.25 + barFrac * 0.5 ) * color[3]; Vector4Set( bgcolor, 1.f, 1.f, 1.f, .25f * color[3] ); CG_FilledBar( 320 - 110 /*w*/ / 2, 190, 110, 10, c, NULL, bgcolor, barFrac, 16 ); } // -NERVE - SMF if ( isTank ) { return; } if ( drawStuff ) { trap_R_SetColor( NULL ); } } //============================================================================== /* ================= CG_DrawSpectator ================= */ static void CG_DrawSpectator( void ) { CG_DrawBigString( 320 - 9 * 8, 440, CG_TranslateString( "SPECTATOR" ), 1.f ); } /* ================= CG_DrawVote ================= */ static void CG_DrawVote( void ) { char *s; char str1[32], str2[32]; float color[4] = { 1, 1, 0, 1 }; int sec; if ( cgs.complaintEndTime > cg.time && !cg.demoPlayback && cg_complaintPopUp.integer > 0 && cgs.complaintClient >= 0 ) { Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); s = va( CG_TranslateString( "File complaint against %s for team-killing?" ), cgs.clientinfo[cgs.complaintClient].name ); CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.applicationEndTime > cg.time && cgs.applicationClient >= 0 ) { Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); s = va( CG_TranslateString( "Accept %s's application to join your fireteam?" ), cgs.clientinfo[cgs.applicationClient].name ); CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.propositionEndTime > cg.time && cgs.propositionClient >= 0 ) { Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); s = va( CG_TranslateString( "Accept %s's proposition to invite %s to join your fireteam?" ), cgs.clientinfo[cgs.propositionClient2].name, cgs.clientinfo[cgs.propositionClient].name ); CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.invitationEndTime > cg.time && cgs.invitationClient >= 0 ) { Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); s = va( CG_TranslateString( "Accept %s's invitation to join their fireteam?" ), cgs.clientinfo[cgs.invitationClient].name ); CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.autoFireteamEndTime > cg.time && cgs.autoFireteamNum == -1 ) { Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); s = "Make Fireteam private?"; CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.autoFireteamCreateEndTime > cg.time && cgs.autoFireteamCreateNum == -1 ) { Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); s = "Create a Fireteam?"; CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.autoFireteamJoinEndTime > cg.time && cgs.autoFireteamJoinNum == -1 ) { Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); s = "Join a Fireteam?"; CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.voteTime ) { Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); // play a talk beep whenever it is modified if ( cgs.voteModified ) { cgs.voteModified = qfalse; } sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000; if ( sec < 0 ) { sec = 0; } if ( !Q_stricmpn( cgs.voteString, "kick", 4 ) ) { if ( strlen( cgs.voteString ) > 5 ) { int nameindex; char buffer[ 128 ]; Q_strncpyz( buffer, cgs.voteString + 5, sizeof( buffer ) ); Q_CleanStr( buffer ); for ( nameindex = 0; nameindex < MAX_CLIENTS; nameindex++ ) { if ( !cgs.clientinfo[ nameindex ].infoValid ) { continue; } if ( !Q_stricmp( cgs.clientinfo[ nameindex ].cleanname, buffer ) ) { if ( cgs.clientinfo[ nameindex ].team != TEAM_SPECTATOR && cgs.clientinfo[ nameindex ].team != cgs.clientinfo[ cg.clientNum ].team ) { return; } } } } } if ( !( cg.snap->ps.eFlags & EF_VOTED ) ) { s = va( CG_TranslateString( "VOTE(%i): %s" ), sec, cgs.voteString ); CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); if ( cgs.clientinfo[cg.clientNum].team != TEAM_AXIS && cgs.clientinfo[cg.clientNum].team != TEAM_ALLIES ) { s = CG_TranslateString( "Cannot vote as Spectator" ); } else { s = va( CG_TranslateString( "YES(%s):%i, NO(%s):%i" ), str1, cgs.voteYes, str2, cgs.voteNo ); } CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 60 ); return; } else { s = va( CG_TranslateString( "YOU VOTED ON: %s" ), cgs.voteString ); CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); s = va( CG_TranslateString( "Y:%i, N:%i" ), cgs.voteYes, cgs.voteNo ); CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 20 ); return; } } if ( cgs.complaintEndTime > cg.time && !cg.demoPlayback && cg_complaintPopUp.integer > 0 && cgs.complaintClient < 0 ) { if ( cgs.complaintClient == -1 ) { s = "Your complaint has been filed"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.complaintClient == -2 ) { s = "Complaint dismissed"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.complaintClient == -3 ) { s = "Server Host cannot be complained against"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.complaintClient == -4 ) { s = "You were team-killed by the Server Host"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } } if ( cgs.applicationEndTime > cg.time && cgs.applicationClient < 0 ) { if ( cgs.applicationClient == -1 ) { s = "Your application has been submitted"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.applicationClient == -2 ) { s = "Your application failed"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.applicationClient == -3 ) { s = "Your application has been approved"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.applicationClient == -4 ) { s = "Your application reply has been sent"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } } if ( cgs.propositionEndTime > cg.time && cgs.propositionClient < 0 ) { if ( cgs.propositionClient == -1 ) { s = "Your proposition has been submitted"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.propositionClient == -2 ) { s = "Your proposition was rejected"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.propositionClient == -3 ) { s = "Your proposition was accepted"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.propositionClient == -4 ) { s = "Your proposition reply has been sent"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } } if ( cgs.invitationEndTime > cg.time && cgs.invitationClient < 0 ) { if ( cgs.invitationClient == -1 ) { s = "Your invitation has been submitted"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.invitationClient == -2 ) { s = "Your invitation was rejected"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.invitationClient == -3 ) { s = "Your invitation was accepted"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.invitationClient == -4 ) { s = "Your invitation reply has been sent"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } if ( cgs.invitationClient < 0 ) { return; } } if ( ( cgs.autoFireteamEndTime > cg.time && cgs.autoFireteamNum == -2 ) || ( cgs.autoFireteamCreateEndTime > cg.time && cgs.autoFireteamCreateNum == -2 ) || ( cgs.autoFireteamJoinEndTime > cg.time && cgs.autoFireteamJoinNum == -2 ) ) { s = "Response Sent"; CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); return; } } /* ================= CG_DrawIntermission ================= */ static void CG_DrawIntermission( void ) { // End-of-level autoactions if ( !cg.demoPlayback ) { static int doScreenshot = 0, doDemostop = 0; if ( !cg.latchAutoActions ) { cg.latchAutoActions = qtrue; if ( cg_autoAction.integer & AA_SCREENSHOT ) { doScreenshot = cg.time + 1000; } if ( cg_autoAction.integer & AA_STATSDUMP ) { CG_dumpStats_f(); } if ( ( cg_autoAction.integer & AA_DEMORECORD ) && ( ( cgs.gametype == GT_WOLF_STOPWATCH && cgs.currentRound == 0 ) || cgs.gametype != GT_WOLF_STOPWATCH ) ) { doDemostop = cg.time + 5000; // stats should show up within 5 seconds } } if ( doScreenshot > 0 && doScreenshot < cg.time ) { CG_autoScreenShot_f(); doScreenshot = 0; } if ( doDemostop > 0 && doDemostop < cg.time ) { trap_SendConsoleCommand( "stoprecord\n" ); doDemostop = 0; } } // Intermission view CG_Debriefing_Draw(); /* cg.scoreFadeTime = cg.time; CG_DrawScoreboard(); */ } /* ================= CG_ActivateLimboMenu NERVE - SMF ================= */ static void CG_ActivateLimboMenu( void ) { /* static qboolean latch = qfalse; qboolean test; // should we open the limbo menu (make allowances for MV clients) test = ((cg.snap->ps.pm_flags & PMF_LIMBO) || ( (cg.mvTotalClients < 1 && ( (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) || (cg.warmup)) ) && cg.snap->ps.pm_type != PM_INTERMISSION ) ); // auto open/close limbo mode if(cg_popupLimboMenu.integer && !cg.demoPlayback) { if(test && !latch) { CG_LimboMenu_f(); latch = qtrue; } else if(!test && latch && cg.showGameView) { CG_EventHandling(CGAME_EVENT_NONE, qfalse); latch = qfalse; } }*/ } /* ================= CG_DrawSpectatorMessage ================= */ static void CG_DrawSpectatorMessage( void ) { const char *str, *str2; float x, y; static int lastconfigGet = 0; if ( !cg_descriptiveText.integer ) { return; } if ( !( cg.snap->ps.pm_flags & PMF_LIMBO || cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) ) { return; } if ( cg.time - lastconfigGet > 1000 ) { Controls_GetConfig(); lastconfigGet = cg.time; } x = ( cg.snap->ps.pm_flags & PMF_LIMBO ) ? 170 : 80; y = 408; y -= 2 * TINYCHAR_HEIGHT; str2 = BindingFromName( "openlimbomenu" ); if ( !Q_stricmp( str2, "(openlimbomenu)" ) ) { str2 = "ESCAPE"; } str = va( CG_TranslateString( "Press %s to open Limbo Menu" ), str2 ); CG_DrawStringExt( 8, 154, str, colorWhite, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); str2 = BindingFromName( "+attack" ); str = va( CG_TranslateString( "Press %s to follow next player" ), str2 ); CG_DrawStringExt( 8, 172, str, colorWhite, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); #ifdef MV_SUPPORT str2 = BindingFromName( "mvactivate" ); str = va( CG_TranslateString( "- Press %s to %s multiview mode" ), str2, ( ( cg.mvTotalClients > 0 ) ? "disable" : "activate" ) ); CG_DrawStringExt( x, y, str, colorWhite, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); y += TINYCHAR_HEIGHT; #endif } float CG_CalculateReinfTime_Float( qboolean menu ) { team_t team; int dwDeployTime; if ( menu ) { if ( cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR ) { team = cgs.ccSelectedTeam == 0 ? TEAM_AXIS : TEAM_ALLIES; } else { team = cgs.clientinfo[cg.clientNum].team; } } else { team = cgs.clientinfo[cg.snap->ps.clientNum].team; } dwDeployTime = ( team == TEAM_AXIS ) ? cg_redlimbotime.integer : cg_bluelimbotime.integer; return ( 1 + ( dwDeployTime - ( ( cgs.aReinfOffset[team] + cg.time - cgs.levelStartTime ) % dwDeployTime ) ) * 0.001f ); } int CG_CalculateReinfTime( qboolean menu ) { return( (int)CG_CalculateReinfTime_Float( menu ) ); } /* ================= CG_DrawLimboMessage ================= */ #define INFOTEXT_STARTX 8 static void CG_DrawLimboMessage( void ) { float color[4] = { 1, 1, 1, 1 }; const char *str; playerState_t *ps; int y = 118; ps = &cg.snap->ps; if ( ps->stats[STAT_HEALTH] > 0 ) { return; } if ( cg.snap->ps.pm_flags & PMF_LIMBO || cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR ) { return; } if ( cg_descriptiveText.integer ) { str = CG_TranslateString( "You are wounded and waiting for a medic." ); CG_DrawSmallStringColor( INFOTEXT_STARTX, y, str, color ); y += 18; if ( cgs.gametype == GT_WOLF_LMS ) { trap_R_SetColor( NULL ); return; } str = CG_TranslateString( "Press JUMP to go into reinforcement queue." ); CG_DrawSmallStringColor( INFOTEXT_STARTX, 134, str, color ); y += 18; } else if ( cgs.gametype == GT_WOLF_LMS ) { trap_R_SetColor( NULL ); return; } // JPW NERVE str = ( ps->persistant[PERS_RESPAWNS_LEFT] == 0 ) ? CG_TranslateString( "No more reinforcements this round." ) : va( CG_TranslateString( "Reinforcements deploy in %d seconds." ), CG_CalculateReinfTime( qfalse ) ); CG_DrawSmallStringColor( INFOTEXT_STARTX, y, str, color ); y += 18; // jpw trap_R_SetColor( NULL ); } // -NERVE - SMF /* ================= CG_DrawFollow ================= */ static qboolean CG_DrawFollow( void ) { char deploytime[128]; // MV following info for mainview if ( CG_ViewingDraw() ) { return( qtrue ); } if ( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) { return( qfalse ); } // if in limbo, show different follow message if ( cg.snap->ps.pm_flags & PMF_LIMBO ) { if ( cgs.gametype != GT_WOLF_LMS ) { if ( cg.snap->ps.persistant[PERS_RESPAWNS_LEFT] == 0 ) { if ( cg.snap->ps.persistant[PERS_RESPAWNS_PENALTY] >= 0 ) { int deployTime = ( cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_AXIS ) ? cg_redlimbotime.integer : cg_bluelimbotime.integer; deployTime *= 0.001f; sprintf( deploytime, CG_TranslateString( "Bonus Life! Deploying in %d seconds" ), CG_CalculateReinfTime( qfalse ) + cg.snap->ps.persistant[PERS_RESPAWNS_PENALTY] * deployTime ); } else { sprintf( deploytime, CG_TranslateString( "No more deployments this round" ) ); } } else { sprintf( deploytime, CG_TranslateString( "Deploying in %d seconds" ), CG_CalculateReinfTime( qfalse ) ); } CG_DrawStringExt( INFOTEXT_STARTX, 118, deploytime, colorWhite, qtrue, qtrue, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 80 ); } // Don't display if you're following yourself if ( cg.snap->ps.clientNum != cg.clientNum ) { sprintf( deploytime, "(%s %s %s [%s])", CG_TranslateString( "Following" ), cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_ALLIES ? rankNames_Allies[cgs.clientinfo[cg.snap->ps.clientNum].rank] : rankNames_Axis[cgs.clientinfo[cg.snap->ps.clientNum].rank], cgs.clientinfo[cg.snap->ps.clientNum].name, BG_ClassnameForNumber( cgs.clientinfo[cg.snap->ps.clientNum].cls ) ); CG_DrawStringExt( INFOTEXT_STARTX, 136, deploytime, colorWhite, qtrue, qtrue, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 80 ); } } else { CG_DrawStringExt( INFOTEXT_STARTX, 118, CG_TranslateString( "Following" ), colorWhite, qtrue, qtrue, BIGCHAR_WIDTH / 2, BIGCHAR_HEIGHT, 0 ); CG_DrawStringExt( 84, 118, va( "%s %s [%s]", cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_ALLIES ? rankNames_Allies[cgs.clientinfo[cg.snap->ps.clientNum].rank] : rankNames_Axis[cgs.clientinfo[cg.snap->ps.clientNum].rank], cgs.clientinfo[cg.snap->ps.clientNum].name, BG_ClassnameForNumber( cgs.clientinfo[cg.snap->ps.clientNum].cls ) ), colorWhite, qtrue, qtrue, BIGCHAR_WIDTH / 2, BIGCHAR_HEIGHT, 0 ); } return( qtrue ); } /* ================= CG_DrawWarmup ================= */ static void CG_DrawWarmup( void ) { int w; int sec; int cw; const char *s, *s1, *s2; sec = cg.warmup; if ( !sec ) { if ( ( cgs.gamestate == GS_WARMUP && !cg.warmup ) || cgs.gamestate == GS_WAITING_FOR_PLAYERS ) { cw = 10; s1 = va( CG_TranslateString( "^3WARMUP:^7 Waiting on ^2%i^7 %s" ), cgs.minclients, cgs.minclients == 1 ? "player" : "players" ); w = CG_DrawStrlen( s1 ); CG_DrawStringExt( 320 - w * 12 / 2, 188, s1, colorWhite, qfalse, qtrue, 12, 18, 0 ); if ( !cg.demoPlayback && cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR && ( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) || ( cg.snap->ps.pm_flags & PMF_LIMBO ) ) ) { char str1[32]; Q_strncpyz( str1, BindingFromName( "ready" ), 32 ); if ( !Q_stricmp( str1, "(?" "?" "?)" ) ) { s2 = CG_TranslateString( "Type ^3\\ready^* in the console to start" ); } else { s2 = va( "Press ^3%s^* to start", str1 ); s2 = CG_TranslateString( s2 ); } w = CG_DrawStrlen( s2 ); CG_DrawStringExt( 320 - w * cw / 2, 208, s2, colorWhite, qfalse, qtrue, cw, (int)( cw * 1.5 ), 0 ); } /* if ( !sec ) { if ( cgs.gamestate == GS_WAITING_FOR_PLAYERS ) { cw = 10; s = CG_TranslateString( "Game Stopped - Waiting for more players" ); w = CG_DrawStrlen( s ); CG_DrawStringExt( 320 - w * 6, 120, s, colorWhite, qfalse, qtrue, 12, 18, 0 ); if( cg_gameType.integer != GT_WOLF_LMS ) { s1 = va( CG_TranslateString( "Waiting for %i players" ), cgs.minclients ); s2 = CG_TranslateString( "or call a vote to start the match" ); w = CG_DrawStrlen( s1 ); CG_DrawStringExt( 320 - w * cw/2, 160, s1, colorWhite, qfalse, qtrue, cw, (int)(cw * 1.5), 0 ); w = CG_DrawStrlen( s2 ); CG_DrawStringExt( 320 - w * cw/2, 180, s2, colorWhite, qfalse, qtrue, cw, (int)(cw * 1.5), 0 ); } */ return; } return; } sec = ( sec - cg.time ) / 1000; if ( sec < 0 ) { sec = 0; } s = va( "%s %i", CG_TranslateString( "(WARMUP) Match begins in:" ), sec + 1 ); w = CG_DrawStrlen( s ); CG_DrawStringExt( 320 - w * 6, 120, s, colorYellow, qfalse, qtrue, 12, 18, 0 ); // NERVE - SMF - stopwatch stuff s1 = ""; s2 = ""; if ( cgs.gametype == GT_WOLF_STOPWATCH ) { const char *cs; int defender; s = va( "%s %i", CG_TranslateString( "Stopwatch Round" ), cgs.currentRound + 1 ); cs = CG_ConfigString( CS_MULTI_INFO ); defender = atoi( Info_ValueForKey( cs, "defender" ) ); if ( !defender ) { if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_AXIS ) { if ( cgs.currentRound == 1 ) { s1 = "You have been switched to the Axis team"; s2 = "Keep the Allies from beating the clock!"; } else { s1 = "You are on the Axis team"; } } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_ALLIES ) { if ( cgs.currentRound == 1 ) { s1 = "You have been switched to the Allied team"; s2 = "Try to beat the clock!"; } else { s1 = "You are on the Allied team"; } } } else { if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_AXIS ) { if ( cgs.currentRound == 1 ) { s1 = "You have been switched to the Axis team"; s2 = "Try to beat the clock!"; } else { s1 = "You are on the Axis team"; } } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_ALLIES ) { if ( cgs.currentRound == 1 ) { s1 = "You have been switched to the Allied team"; s2 = "Keep the Axis from beating the clock!"; } else { s1 = "You are on the Allied team"; } } } if ( strlen( s1 ) ) { s1 = CG_TranslateString( s1 ); } if ( strlen( s2 ) ) { s2 = CG_TranslateString( s2 ); } cw = 10; w = CG_DrawStrlen( s ); CG_DrawStringExt( 320 - w * cw / 2, 140, s, colorWhite, qfalse, qtrue, cw, (int)( cw * 1.5 ), 0 ); w = CG_DrawStrlen( s1 ); CG_DrawStringExt( 320 - w * cw / 2, 160, s1, colorWhite, qfalse, qtrue, cw, (int)( cw * 1.5 ), 0 ); w = CG_DrawStrlen( s2 ); CG_DrawStringExt( 320 - w * cw / 2, 180, s2, colorWhite, qfalse, qtrue, cw, (int)( cw * 1.5 ), 0 ); } } //================================================================================== /* ================= CG_DrawFlashFade ================= */ static void CG_DrawFlashFade( void ) { static int lastTime; int elapsed, time; vec4_t col; qboolean fBlackout = ( !CG_IsSinglePlayer() && int_ui_blackout.integer > 0 ); if ( cgs.fadeStartTime + cgs.fadeDuration < cg.time ) { cgs.fadeAlphaCurrent = cgs.fadeAlpha; } else if ( cgs.fadeAlphaCurrent != cgs.fadeAlpha ) { elapsed = ( time = trap_Milliseconds() ) - lastTime; // we need to use trap_Milliseconds() here since the cg.time gets modified upon reloading lastTime = time; if ( elapsed < 500 && elapsed > 0 ) { if ( cgs.fadeAlphaCurrent > cgs.fadeAlpha ) { cgs.fadeAlphaCurrent -= ( (float)elapsed / (float)cgs.fadeDuration ); if ( cgs.fadeAlphaCurrent < cgs.fadeAlpha ) { cgs.fadeAlphaCurrent = cgs.fadeAlpha; } } else { cgs.fadeAlphaCurrent += ( (float)elapsed / (float)cgs.fadeDuration ); if ( cgs.fadeAlphaCurrent > cgs.fadeAlpha ) { cgs.fadeAlphaCurrent = cgs.fadeAlpha; } } } } // OSP - ugh, have to inform the ui that we need to remain blacked out (or not) if ( int_ui_blackout.integer == 0 ) { if ( cg.mvTotalClients < 1 && cg.snap->ps.powerups[PW_BLACKOUT] > 0 ) { trap_Cvar_Set( "ui_blackout", va( "%d", cg.snap->ps.powerups[PW_BLACKOUT] ) ); } } else if ( cg.snap->ps.powerups[PW_BLACKOUT] == 0 || cg.mvTotalClients > 0 ) { trap_Cvar_Set( "ui_blackout", "0" ); } // now draw the fade if ( cgs.fadeAlphaCurrent > 0.0 || fBlackout ) { VectorClear( col ); col[3] = ( fBlackout ) ? 1.0f : cgs.fadeAlphaCurrent; // CG_FillRect( -10, -10, 650, 490, col ); CG_FillRect( 0, 0, 640, 480, col ); // why do a bunch of these extend outside 640x480? //bani - #127 - bail out if we're a speclocked spectator with cg_draw2d = 0 if ( cgs.clientinfo[ cg.clientNum ].team == TEAM_SPECTATOR && !cg_draw2D.integer ) { return; } // OSP - Show who is speclocked if ( fBlackout ) { int i, nOffset = 90; char *str, *format = "The %s team is speclocked!"; char *teams[TEAM_NUM_TEAMS] = { "??", "AXIS", "ALLIES", "???" }; float color[4] = { 1, 1, 0, 1 }; for ( i = TEAM_AXIS; i <= TEAM_ALLIES; i++ ) { if ( cg.snap->ps.powerups[PW_BLACKOUT] & i ) { str = va( format, teams[i] ); CG_DrawStringExt( INFOTEXT_STARTX, nOffset, str, color, qtrue, qfalse, 10, 10, 0 ); nOffset += 12; } } } } } /* ============== CG_DrawFlashZoomTransition hide the snap transition from regular view to/from zoomed FIXME: TODO: use cg_fade? ============== */ static void CG_DrawFlashZoomTransition( void ) { vec4_t color; float frac; int fadeTime; if ( !cg.snap ) { return; } if ( BG_PlayerMounted( cg.snap->ps.eFlags ) ) { // don't draw when on mg_42 // keep the timer fresh so when you remove yourself from the mg42, it'll fade cg.zoomTime = cg.time; return; } if ( cg.renderingThirdPerson ) { return; } fadeTime = 400; frac = cg.time - cg.zoomTime; if ( frac < fadeTime ) { frac = frac / (float)fadeTime; Vector4Set( color, 0, 0, 0, 1.0f - frac ); CG_FillRect( -10, -10, 650, 490, color ); } } /* ================= CG_DrawFlashDamage ================= */ static void CG_DrawFlashDamage( void ) { vec4_t col; float redFlash; if ( !cg.snap ) { return; } if ( cg.v_dmg_time > cg.time ) { redFlash = fabs( cg.v_dmg_pitch * ( ( cg.v_dmg_time - cg.time ) / DAMAGE_TIME ) ); // blend the entire screen red if ( redFlash > 5 ) { redFlash = 5; } VectorSet( col, 0.2, 0, 0 ); col[3] = 0.7 * ( redFlash / 5.0 ) * ( ( cg_bloodFlash.value > 1.0 ) ? 1.0 : ( cg_bloodFlash.value < 0.0 ) ? 0.0 : cg_bloodFlash.value ); CG_FillRect( -10, -10, 650, 490, col ); } } /* ================= CG_DrawFlashFire ================= */ static void CG_DrawFlashFire( void ) { vec4_t col = {1,1,1,1}; float alpha, max, f; if ( !cg.snap ) { return; } if ( cg.renderingThirdPerson ) { return; } if ( !cg.snap->ps.onFireStart ) { cg.v_noFireTime = cg.time; return; } alpha = (float)( ( FIRE_FLASH_TIME - 1000 ) - ( cg.time - cg.snap->ps.onFireStart ) ) / ( FIRE_FLASH_TIME - 1000 ); if ( alpha > 0 ) { if ( alpha >= 1.0 ) { alpha = 1.0; } // fade in? f = (float)( cg.time - cg.v_noFireTime ) / FIRE_FLASH_FADEIN_TIME; if ( f >= 0.0 && f < 1.0 ) { alpha = f; } max = 0.5 + 0.5 * sin( (float)( ( cg.time / 10 ) % 1000 ) / 1000.0 ); if ( alpha > max ) { alpha = max; } col[0] = alpha; col[1] = alpha; col[2] = alpha; col[3] = alpha; trap_R_SetColor( col ); CG_DrawPic( -10, -10, 650, 490, cgs.media.viewFlashFire[( cg.time / 50 ) % 16] ); trap_R_SetColor( NULL ); trap_S_AddLoopingSound( cg.snap->ps.origin, vec3_origin, cgs.media.flameSound, (int)( 255.0 * alpha ), 0 ); trap_S_AddLoopingSound( cg.snap->ps.origin, vec3_origin, cgs.media.flameCrackSound, (int)( 255.0 * alpha ), 0 ); } else { cg.v_noFireTime = cg.time; } } /* ============== CG_DrawFlashBlendBehindHUD screen flash stuff drawn first (on top of world, behind HUD) ============== */ static void CG_DrawFlashBlendBehindHUD( void ) { CG_DrawFlashZoomTransition(); CG_DrawFlashFade(); } /* ================= CG_DrawFlashBlend screen flash stuff drawn last (on top of everything) ================= */ static void CG_DrawFlashBlend( void ) { // Gordon: no flash blends if in limbo or spectator, and in the limbo menu if ( ( cg.snap->ps.pm_flags & PMF_LIMBO || cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR ) && cg.showGameView ) { return; } CG_DrawFlashFire(); CG_DrawFlashDamage(); } // NERVE - SMF /* ================= CG_DrawObjectiveInfo ================= */ #define OID_LEFT 10 #define OID_TOP 360 void CG_ObjectivePrint( const char *str, int charWidth ) { char *s; int i, len; // NERVE - SMF qboolean neednewline = qfalse; // NERVE - SMF if ( cg.centerPrintTime ) { return; } s = CG_TranslateString( str ); Q_strncpyz( cg.oidPrint, s, sizeof( cg.oidPrint ) ); // NERVE - SMF - turn spaces into newlines, if we've run over the linewidth len = strlen( cg.oidPrint ); for ( i = 0; i < len; i++ ) { // NOTE: subtract a few chars here so long words still get displayed properly if ( i % ( CP_LINEWIDTH - 20 ) == 0 && i > 0 ) { neednewline = qtrue; } if ( cg.oidPrint[i] == ' ' && neednewline ) { cg.oidPrint[i] = '\n'; neednewline = qfalse; } } // -NERVE - SMF cg.oidPrintTime = cg.time; cg.oidPrintY = OID_TOP; cg.oidPrintCharWidth = charWidth; // count the number of lines for oiding cg.oidPrintLines = 1; s = cg.oidPrint; while ( *s ) { if ( *s == '\n' ) { cg.oidPrintLines++; } s++; } } static void CG_DrawObjectiveInfo( void ) { char *start; int l; int x, y, w,h; int x1, y1, x2, y2; float *color; vec4_t backColor; backColor[0] = 0.2f; backColor[1] = 0.2f; backColor[2] = 0.2f; backColor[2] = 1.f; if ( !cg.oidPrintTime ) { return; } color = CG_FadeColor( cg.oidPrintTime, 250 ); if ( !color ) { cg.oidPrintTime = 0; return; } trap_R_SetColor( color ); start = cg.oidPrint; // JPW NERVE // y = cg.oidPrintY - cg.oidPrintLines * BIGCHAR_HEIGHT / 2; y = 400 - cg.oidPrintLines * BIGCHAR_HEIGHT / 2; x1 = 319; y1 = y - 2; x2 = 321; // jpw // first just find the bounding rect while ( 1 ) { char linebuffer[1024]; for ( l = 0; l < CP_LINEWIDTH; l++ ) { if ( !start[l] || start[l] == '\n' ) { break; } linebuffer[l] = start[l]; } linebuffer[l] = 0; w = cg.oidPrintCharWidth * CG_DrawStrlen( linebuffer ) + 10; // JPW NERVE if ( 320 - w / 2 < x1 ) { x1 = 320 - w / 2; x2 = 320 + w / 2; } /* if ( x1 + w > x2 ) x2 = x1 + w; */ x = 320 - w / 2; // jpw y += cg.oidPrintCharWidth * 1.5; while ( *start && ( *start != '\n' ) ) { start++; } if ( !*start ) { break; } start++; } x2 = x2 + 4; y2 = y - cg.oidPrintCharWidth * 1.5 + 4; h = y2 - y1; // JPW NERVE VectorCopy( color, backColor ); backColor[3] = 0.5 * color[3]; trap_R_SetColor( backColor ); CG_DrawPic( x1, y1, x2 - x1, y2 - y1, cgs.media.teamStatusBar ); VectorSet( backColor, 0, 0, 0 ); CG_DrawRect( x1, y1, x2 - x1, y2 - y1, 1, backColor ); trap_R_SetColor( color ); // do the actual drawing start = cg.oidPrint; // y = cg.oidPrintY - cg.oidPrintLines * BIGCHAR_HEIGHT / 2; y = 400 - cg.oidPrintLines * BIGCHAR_HEIGHT / 2; // JPW NERVE while ( 1 ) { char linebuffer[1024]; for ( l = 0; l < CP_LINEWIDTH; l++ ) { if ( !start[l] || start[l] == '\n' ) { break; } linebuffer[l] = start[l]; } linebuffer[l] = 0; w = cg.oidPrintCharWidth * CG_DrawStrlen( linebuffer ); if ( x1 + w > x2 ) { x2 = x1 + w; } x = 320 - w / 2; // JPW NERVE CG_DrawStringExt( x, y, linebuffer, color, qfalse, qtrue, cg.oidPrintCharWidth, (int)( cg.oidPrintCharWidth * 1.5 ), 0 ); y += cg.oidPrintCharWidth * 1.5; while ( *start && ( *start != '\n' ) ) { start++; } if ( !*start ) { break; } start++; } trap_R_SetColor( NULL ); } //================================================================================== void CG_DrawTimedMenus() { if ( cg.voiceTime ) { int t = cg.time - cg.voiceTime; if ( t > 2500 ) { Menus_CloseByName( "voiceMenu" ); trap_Cvar_Set( "cl_conXOffset", "0" ); cg.voiceTime = 0; } } } /* ================= CG_Fade ================= */ void CG_Fade( int r, int g, int b, int a, int time, int duration ) { // incorporate this into the current fade scheme cgs.fadeAlpha = (float)a / 255.0f; cgs.fadeStartTime = time; cgs.fadeDuration = duration; if ( cgs.fadeStartTime + cgs.fadeDuration <= cg.time ) { cgs.fadeAlphaCurrent = cgs.fadeAlpha; } return; if ( time <= 0 ) { // do instantly cg.fadeRate = 1; cg.fadeTime = cg.time - 1; // set cg.fadeTime behind cg.time so it will start out 'done' } else { cg.fadeRate = 1.0f / time; cg.fadeTime = cg.time + time; } cg.fadeColor2[ 0 ] = ( float )r / 255.0f; cg.fadeColor2[ 1 ] = ( float )g / 255.0f; cg.fadeColor2[ 2 ] = ( float )b / 255.0f; cg.fadeColor2[ 3 ] = ( float )a / 255.0f; } /* ================= CG_ScreenFade ================= */ static void CG_ScreenFade( void ) { int msec; int i; float t, invt; vec4_t color; if ( !cg.fadeRate ) { return; } msec = cg.fadeTime - cg.time; if ( msec <= 0 ) { cg.fadeColor1[ 0 ] = cg.fadeColor2[ 0 ]; cg.fadeColor1[ 1 ] = cg.fadeColor2[ 1 ]; cg.fadeColor1[ 2 ] = cg.fadeColor2[ 2 ]; cg.fadeColor1[ 3 ] = cg.fadeColor2[ 3 ]; if ( !cg.fadeColor1[ 3 ] ) { cg.fadeRate = 0; return; } CG_FillRect( 0, 0, 640, 480, cg.fadeColor1 ); } else { t = ( float )msec * cg.fadeRate; invt = 1.0f - t; for ( i = 0; i < 4; i++ ) { color[ i ] = cg.fadeColor1[ i ] * t + cg.fadeColor2[ i ] * invt; } if ( color[ 3 ] ) { CG_FillRect( 0, 0, 640, 480, color ); } } } #if 0 // rain - unused // JPW NERVE void CG_Draw2D2( void ) { qhandle_t weapon; trap_R_SetColor( NULL ); CG_DrawPic( 0,480, 640, -70, cgs.media.hud1Shader ); if ( !BG_PlayerMounted( cg.snap->ps.eFlags ) ) { switch ( cg.snap->ps.weapon ) { case WP_COLT: case WP_LUGER: weapon = cgs.media.hud2Shader; break; case WP_KNIFE: weapon = cgs.media.hud5Shader; break; default: weapon = cgs.media.hud3Shader; } CG_DrawPic( 220,410, 200,-200,weapon ); } } #endif /* ================= CG_DrawCompassIcon NERVE - SMF ================= */ void CG_DrawCompassIcon( float x, float y, float w, float h, vec3_t origin, vec3_t dest, qhandle_t shader ) { float angle, pi2 = M_PI * 2; vec3_t v1, angles; float len; VectorCopy( dest, v1 ); VectorSubtract( origin, v1, v1 ); len = VectorLength( v1 ); VectorNormalize( v1 ); vectoangles( v1, angles ); if ( v1[0] == 0 && v1[1] == 0 && v1[2] == 0 ) { return; } // if( cg_drawCompass.integer == 2 ) // angles[YAW] = AngleSubtract( 90, angles[YAW] ); // else angles[YAW] = AngleSubtract( cg.predictedPlayerState.viewangles[YAW], angles[YAW] ); angle = ( ( angles[YAW] + 180.f ) / 360.f - ( 0.50 / 2.f ) ) * pi2; // if (!CG_IsSinglePlayer()) { w /= 2; h /= 2; x += w; y += h; // if (CG_IsSinglePlayer()) /* if (0) { w = 80; // hardcoded, because it has to fit the art } else*/ { w = sqrt( ( w * w ) + ( h * h ) ) / 3.f * 2.f * 0.9f; } x = x + ( cos( angle ) * w ); y = y + ( sin( angle ) * w ); len = 1 - min( 1.f, len / 2000.f ); CG_DrawPic( x - ( 14 * len + 4 ) / 2, y - ( 14 * len + 4 ) / 2, 14 * len + 8, 14 * len + 8, shader ); #ifdef SQUARE_COMPASS } else { int iconWidth, iconHeight; // START Mad Doc - TDF // talk about fitting a square peg into a round hole... // we're now putting the compass icons around the square automap instead of the round compass while ( angle < 0 ) angle += pi2; while ( angle >= pi2 ) angle -= pi2; x = x + w / 2; y = y + h / 2; w /= 2; // = sqrt( ( w * w ) + ( h * h ) ) / 3.f * 2.f * 0.9f; if ( ( angle >= 0 ) && ( angle < M_PI / 4.0 ) ) { x += w; y += w * tan( angle ); } else if ( ( angle >= M_PI / 4.0 ) && ( angle < 3.0 * M_PI / 4.0 ) ) { x += w / tan( angle ); y += w; } else if ( ( angle >= 3.0 * M_PI / 4.0 ) && ( angle < 5.0 * M_PI / 4.0 ) ) { x -= w; y -= w * tan( angle ); } else if ( ( angle >= 5.0 * M_PI / 4.0 ) && ( angle < 7.0 * M_PI / 4.0 ) ) { x -= w / tan( angle ); y -= w; } else { x += w; y += w * tan( angle ); } len = 1 - min( 1.f, len / 2000.f ); iconWidth = 14 * len + 4; // where did this calc. come from? iconHeight = 14 * len + 4; // adjust so that icon is always outside of the map if ( ( angle > 5.0 * M_PI / 4.0 ) && ( angle < 2 * M_PI ) ) { y -= iconHeight; } if ( ( angle >= 3.0 * M_PI / 4.0 ) && ( angle <= 5.0 * M_PI / 4.0 ) ) { x -= iconWidth; } CG_DrawPic( x, y, iconWidth, iconHeight, shader ); // END Mad Doc - TDF } #endif } /* ================= CG_DrawNewCompass ================= */ static void CG_DrawNewCompass( void ) { float basex, basey; float basew, baseh; snapshot_t *snap; float angle; int i; static float lastangle = 0; static float anglespeed = 0; float diff; if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { snap = cg.nextSnap; } else { snap = cg.snap; } if ( snap->ps.pm_flags & PMF_LIMBO || snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR || cg.mvTotalClients > 0 ) { return; } // Arnout: bit larger basex = 520 - 16; basey = 20 - 16; basew = 100 + 32; baseh = 100 + 32; CG_DrawAutoMap(); if ( cgs.autoMapExpanded ) { if ( cg.time - cgs.autoMapExpandTime < 100.f ) { basey -= ( ( cg.time - cgs.autoMapExpandTime ) / 100.f ) * 128.f; } else { //basey -= 128.f; return; } } else { if ( cg.time - cgs.autoMapExpandTime <= 150.f ) { //basey -= 128.f; return; } else if ( ( cg.time - cgs.autoMapExpandTime > 150.f ) && ( cg.time - cgs.autoMapExpandTime < 250.f ) ) { basey = ( basey - 128.f ) + ( ( cg.time - cgs.autoMapExpandTime - 150.f ) / 100.f ) * 128.f; } else { rectDef_t compassHintRect = { 640 - 22, 128, 20, 20 }; CG_DrawKeyHint( &compassHintRect, "+mapexpand" ); } } CG_DrawPic( basex + 4, basey + 4, basew - 8, baseh - 8, cgs.media.compassShader ); angle = ( cg.predictedPlayerState.viewangles[YAW] + 180.f ) / 360.f - ( 0.125f ); diff = AngleSubtract( angle * 360, lastangle * 360 ) / 360.f; anglespeed /= 1.08f; anglespeed += diff * 0.01f; if ( Q_fabs( anglespeed ) < 0.00001f ) { anglespeed = 0; } lastangle += anglespeed; CG_DrawRotatedPic( basex + 4, basey + 4, basew - 8, baseh - 8, cgs.media.compass2Shader, lastangle ); // if( !(cgs.ccFilter & CC_FILTER_REQUESTS) ) { // draw voice chats for ( i = 0; i < MAX_CLIENTS; i++ ) { centity_t *cent = &cg_entities[i]; if ( cg.predictedPlayerState.clientNum == i || !cgs.clientinfo[i].infoValid || cg.predictedPlayerState.persistant[PERS_TEAM] != cgs.clientinfo[i].team ) { continue; } // also draw revive icons if cent is dead and player is a medic if ( cent->voiceChatSpriteTime < cg.time ) { continue; } if ( cgs.clientinfo[i].health <= 0 ) { // reset cent->voiceChatSpriteTime = cg.time; continue; } CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->lerpOrigin, cent->voiceChatSprite ); } // } /*if( !(cgs.ccFilter & CC_FILTER_DESTRUCTIONS) ) { // draw explosives if an engineer if ( cg.predictedPlayerState.stats[ STAT_PLAYER_CLASS ] == PC_ENGINEER ) { for ( i = 0; i < snap->numEntities; i++ ) { centity_t *cent = &cg_entities[ snap->entities[ i ].number ]; if ( cent->currentState.eType != ET_EXPLOSIVE_INDICATOR ) { continue; } if ( cent->currentState.teamNum == 1 && cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_AXIS ) continue; else if ( cent->currentState.teamNum == 2 && cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_ALLIES ) continue; CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->lerpOrigin, cgs.media.compassDestroyShader ); } } }*/ // if( !(cgs.ccFilter & CC_FILTER_REQUESTS) ) { // draw revive medic icons if ( cg.predictedPlayerState.stats[ STAT_PLAYER_CLASS ] == PC_MEDIC ) { for ( i = 0; i < snap->numEntities; i++ ) { entityState_t *ent = &snap->entities[i]; if ( ent->eType != ET_PLAYER ) { continue; } if ( ( ent->eFlags & EF_DEAD ) && ent->number == ent->clientNum ) { if ( !cgs.clientinfo[ent->clientNum].infoValid || cg.predictedPlayerState.persistant[PERS_TEAM] != cgs.clientinfo[ent->clientNum].team ) { continue; } CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, ent->pos.trBase, cgs.media.medicReviveShader ); } } } // } /* if( !(cgs.ccFilter & CC_FILTER_DESTRUCTIONS) ) { // draw constructibles if an engineer if ( cg.predictedPlayerState.stats[ STAT_PLAYER_CLASS ] == PC_ENGINEER ) { for ( i = 0; i < snap->numEntities; i++ ) { centity_t *cent = &cg_entities[ snap->entities[ i ].number ]; if ( cent->currentState.eType != ET_CONSTRUCTIBLE_INDICATOR ) { continue; } if ( cent->currentState.teamNum != cg.predictedPlayerState.persistant[PERS_TEAM] && cent->currentState.teamNum != 3 ) continue; CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->lerpOrigin, cgs.media.compassConstructShader ); } } }*/ /* if( !(cgs.ccFilter & CC_FILTER_WAYPOINTS) ) { // draw waypoint icons for ( i = 0; i < snap->numEntities; i++ ) { centity_t *cent = &cg_entities[ snap->entities[ i ].number ]; if( cent->currentState.eType != ET_WAYPOINT ) { continue; } // see if the waypoint owner is someone that you accept waypoints from if( !CG_IsOnSameFireteam( cg.clientNum, cent->currentState.clientNum )) { // TODO: change to fireteam continue; } switch( cent->currentState.frame ) { case WAYP_ATTACK: CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->currentState.pos.trBase, cgs.media.waypointCompassAttackShader ); break; case WAYP_DEFEND: CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->currentState.pos.trBase, cgs.media.waypointCompassDefendShader ); break; case WAYP_REGROUP: CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->currentState.pos.trBase, cgs.media.waypointCompassRegroupShader ); break; } } }*/ // if( !(cgs.ccFilter & CC_FILTER_BUDDIES) ) { for ( i = 0; i < snap->numEntities; i++ ) { entityState_t *ent = &snap->entities[i]; if ( ent->eType != ET_PLAYER ) { continue; } if ( ent->eFlags & EF_DEAD ) { continue; } if ( !cgs.clientinfo[ent->clientNum].infoValid || cg.predictedPlayerState.persistant[PERS_TEAM] != cgs.clientinfo[ent->clientNum].team ) { continue; } if ( !CG_IsOnSameFireteam( cg.clientNum, ent->clientNum ) ) { continue; } CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, ent->pos.trBase, cgs.media.buddyShader ); } // } } static int CG_PlayerAmmoValue( int *ammo, int *clips, int *akimboammo ) { centity_t *cent; playerState_t *ps; int weap; qboolean skipammo = qfalse; *ammo = *clips = *akimboammo = -1; if ( cg.snap->ps.clientNum == cg.clientNum ) { cent = &cg.predictedPlayerEntity; } else { cent = &cg_entities[cg.snap->ps.clientNum]; } ps = &cg.snap->ps; weap = cent->currentState.weapon; if ( !weap ) { return weap; } switch ( weap ) { // some weapons don't draw ammo count text case WP_AMMO: case WP_MEDKIT: case WP_KNIFE: case WP_PLIERS: case WP_SMOKE_MARKER: case WP_DYNAMITE: case WP_SATCHEL: case WP_SATCHEL_DET: case WP_SMOKE_BOMB: case WP_BINOCULARS: return weap; case WP_LANDMINE: case WP_MEDIC_SYRINGE: case WP_MEDIC_ADRENALINE: case WP_GRENADE_LAUNCHER: case WP_GRENADE_PINEAPPLE: case WP_FLAMETHROWER: case WP_MORTAR: case WP_MORTAR_SET: case WP_PANZERFAUST: skipammo = qtrue; break; default: break; } if ( cg.snap->ps.eFlags & EF_MG42_ACTIVE || cg.snap->ps.eFlags & EF_MOUNTEDTANK ) { return WP_MOBILE_MG42; } // total ammo in clips *clips = cg.snap->ps.ammo[BG_FindAmmoForWeapon( weap )]; // current clip *ammo = ps->ammoclip[BG_FindClipForWeapon( weap )]; if ( BG_IsAkimboWeapon( weap ) ) { *akimboammo = ps->ammoclip[BG_FindClipForWeapon( BG_AkimboSidearm( weap ) )]; } else { *akimboammo = -1; } if ( weap == WP_LANDMINE ) { if ( !cgs.gameManager ) { *ammo = 0; } else { if ( cgs.clientinfo[ps->clientNum].team == TEAM_AXIS ) { *ammo = cgs.gameManager->currentState.otherEntityNum; } else { *ammo = cgs.gameManager->currentState.otherEntityNum2; } } } else if ( weap == WP_MORTAR || weap == WP_MORTAR_SET || weap == WP_PANZERFAUST ) { *ammo += *clips; } if ( skipammo ) { *clips = -1; } return weap; } #define HEAD_TURNTIME 10000 #define HEAD_TURNANGLE 20 #define HEAD_PITCHANGLE 2.5 static void CG_DrawPlayerStatusHead( void ) { hudHeadAnimNumber_t anim; rectDef_t headRect = { 44, 480 - 92, 62, 80 }; // rectDef_t headHintRect = { 40, 480 - 22, 20, 20 }; bg_character_t* character = CG_CharacterForPlayerstate( &cg.snap->ps ); bg_character_t* headcharacter = BG_GetCharacter( cgs.clientinfo[ cg.snap->ps.clientNum ].team, cgs.clientinfo[ cg.snap->ps.clientNum ].cls ); qhandle_t painshader = 0; anim = cg.idleAnim; if ( cg.weaponFireTime > 500 ) { anim = HD_ATTACK; } else if ( cg.time - cg.lastFiredWeaponTime < 500 ) { anim = HD_ATTACK_END; } else if ( cg.time - cg.painTime < ( character->hudheadanimations[ HD_PAIN ].numFrames * character->hudheadanimations[ HD_PAIN ].frameLerp ) ) { anim = HD_PAIN; } else if ( cg.time > cg.nextIdleTime ) { cg.nextIdleTime = cg.time + 7000 + rand() % 1000; if ( cg.snap->ps.stats[ STAT_HEALTH ] < 40 ) { cg.idleAnim = ( rand() % ( HD_DAMAGED_IDLE3 - HD_DAMAGED_IDLE2 + 1 ) ) + HD_DAMAGED_IDLE2; } else { cg.idleAnim = ( rand() % ( HD_IDLE8 - HD_IDLE2 + 1 ) ) + HD_IDLE2; } cg.lastIdleTimeEnd = cg.time + character->hudheadanimations[ cg.idleAnim ].numFrames * character->hudheadanimations[ cg.idleAnim ].frameLerp; } if ( cg.snap->ps.stats[ STAT_HEALTH ] < 5 ) { painshader = cgs.media.hudDamagedStates[3]; } else if ( cg.snap->ps.stats[ STAT_HEALTH ] < 20 ) { painshader = cgs.media.hudDamagedStates[2]; } else if ( cg.snap->ps.stats[ STAT_HEALTH ] < 40 ) { painshader = cgs.media.hudDamagedStates[1]; } else if ( cg.snap->ps.stats[ STAT_HEALTH ] < 60 ) { painshader = cgs.media.hudDamagedStates[0]; } if ( cg.time > cg.lastIdleTimeEnd ) { if ( cg.snap->ps.stats[ STAT_HEALTH ] < 40 ) { cg.idleAnim = HD_DAMAGED_IDLE1; } else { cg.idleAnim = HD_IDLE1; } } CG_DrawPlayerHead( &headRect, character, headcharacter, 180, 0, cg.snap->ps.eFlags & EF_HEADSHOT ? qfalse : qtrue, anim, painshader, cgs.clientinfo[ cg.snap->ps.clientNum ].rank, qfalse ); // CG_DrawKeyHint( &headHintRect, "openlimbomenu" ); } static void CG_DrawPlayerHealthBar( rectDef_t *rect ) { vec4_t bgcolour = { 1.f, 1.f, 1.f, 0.3f }; vec4_t colour; int flags = 1 | 4 | 16 | 64; float frac; CG_ColorForHealth( colour ); colour[3] = 0.5f; if ( cgs.clientinfo[ cg.snap->ps.clientNum ].cls == PC_MEDIC ) { frac = cg.snap->ps.stats[STAT_HEALTH] / ( (float) cg.snap->ps.stats[STAT_MAX_HEALTH] * 1.12f ); } else { frac = cg.snap->ps.stats[STAT_HEALTH] / (float) cg.snap->ps.stats[STAT_MAX_HEALTH]; } CG_FilledBar( rect->x, rect->y + ( rect->h * 0.1f ), rect->w, rect->h * 0.84f, colour, NULL, bgcolour, frac, flags ); trap_R_SetColor( NULL ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.hudSprintBar ); CG_DrawPic( rect->x, rect->y + rect->h + 4, rect->w, rect->w, cgs.media.hudHealthIcon ); } static void CG_DrawStaminaBar( rectDef_t *rect ) { vec4_t bgcolour = { 1.f, 1.f, 1.f, 0.3f }; vec4_t colour = { 0.1f, 1.0f, 0.1f, 0.5f }; vec4_t colourlow = { 1.0f, 0.1f, 0.1f, 0.5f }; vec_t* color = colour; int flags = 1 | 4 | 16 | 64; float frac = cg.pmext.sprintTime / (float)SPRINTTIME; if ( cg.snap->ps.powerups[PW_ADRENALINE] ) { if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { Vector4Average( colour, colorWhite, sin( cg.time * .005f ), colour ); } else { float msec = cg.snap->ps.powerups[PW_ADRENALINE] - cg.time; if ( msec < 0 ) { msec = 0; } else { Vector4Average( colour, colorWhite, .5f + sin( .2f * sqrt( msec ) * 2 * M_PI ) * .5f, colour ); } } } else { if ( frac < 0.25 ) { color = colourlow; } } CG_FilledBar( rect->x, rect->y + ( rect->h * 0.1f ), rect->w, rect->h * 0.84f, color, NULL, bgcolour, frac, flags ); trap_R_SetColor( NULL ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.hudSprintBar ); CG_DrawPic( rect->x, rect->y + rect->h + 4, rect->w, rect->w, cgs.media.hudSprintIcon ); } static void CG_DrawWeapRecharge( rectDef_t *rect ) { float barFrac, chargeTime; int weap, flags; qboolean fade = qfalse; vec4_t bgcolor = { 1.0f, 1.0f, 1.0f, 0.25f }; vec4_t color; flags = 1 | 4 | 16; weap = cg.snap->ps.weapon; // if( !(cg.snap->ps.eFlags & EF_ZOOMING) ) { // if ( weap != WP_PANZERFAUST && weap != WP_DYNAMITE && weap != WP_MEDKIT && weap != WP_SMOKE_GRENADE && weap != WP_PLIERS && weap != WP_AMMO ) { // fade = qtrue; // } // } // Draw power bar if ( cg.snap->ps.stats[ STAT_PLAYER_CLASS ] == PC_ENGINEER ) { chargeTime = cg.engineerChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; } else if ( cg.snap->ps.stats[ STAT_PLAYER_CLASS ] == PC_MEDIC ) { chargeTime = cg.medicChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; } else if ( cg.snap->ps.stats[ STAT_PLAYER_CLASS ] == PC_FIELDOPS ) { chargeTime = cg.ltChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; } else if ( cg.snap->ps.stats[ STAT_PLAYER_CLASS ] == PC_COVERTOPS ) { chargeTime = cg.covertopsChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; } else { chargeTime = cg.soldierChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; } barFrac = (float)( cg.time - cg.snap->ps.classWeaponTime ) / chargeTime; if ( barFrac > 1.0 ) { barFrac = 1.0; } color[0] = 1.0f; color[1] = color[2] = barFrac; color[3] = 0.25 + barFrac * 0.5; if ( fade ) { bgcolor[3] *= 0.4f; color[3] *= 0.4; } CG_FilledBar( rect->x, rect->y + ( rect->h * 0.1f ), rect->w, rect->h * 0.84f, color, NULL, bgcolor, barFrac, flags ); trap_R_SetColor( NULL ); CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.hudSprintBar ); CG_DrawPic( rect->x + ( rect->w * 0.25f ) - 1, rect->y + rect->h + 4, ( rect->w * 0.5f ) + 2, rect->w + 2, cgs.media.hudPowerIcon ); } static void CG_DrawPlayerStatus( void ) { int value, value2, value3; char buffer[32]; int weap; playerState_t *ps; rectDef_t rect; // vec4_t colorFaded = { 1.f, 1.f, 1.f, 0.3f }; ps = &cg.snap->ps; // Draw weapon icon and overheat bar rect.x = 640 - 82; rect.y = 480 - 56; rect.w = 60; rect.h = 32; CG_DrawWeapHeat( &rect, HUD_HORIZONTAL ); if ( cg.mvTotalClients < 1 && cg_drawWeaponIconFlash.integer == 0 ) { CG_DrawPlayerWeaponIcon( &rect, qtrue, ITEM_ALIGN_RIGHT, &colorWhite ); } else { int ws = ( cg.mvTotalClients > 0 ) ? cgs.clientinfo[cg.snap->ps.clientNum].weaponState : BG_simpleWeaponState( cg.snap->ps.weaponstate ); CG_DrawPlayerWeaponIcon( &rect, ( ws != WSTATE_IDLE ), ITEM_ALIGN_RIGHT, ( ( ws == WSTATE_SWITCH ) ? &colorWhite : ( ws == WSTATE_FIRE ) ? &colorRed : &colorYellow ) ); } // Draw ammo weap = CG_PlayerAmmoValue( &value, &value2, &value3 ); if ( value3 >= 0 ) { Com_sprintf( buffer, sizeof( buffer ), "%i|%i/%i", value3, value, value2 ); CG_Text_Paint_Ext( 640 - 22 - CG_Text_Width_Ext( buffer, .25f, 0, &cgs.media.limboFont1 ), 480 - 1 * ( 16 + 2 ) + 12 - 4, .25f, .25f, colorWhite, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); // CG_DrawPic( 640 - 2 * ( 12 + 2 ) - 16 - 4, 480 - 1 * ( 16 + 2 ) - 4, 16, 16, cgs.media.SPPlayerInfoAmmoIcon ); } else if ( value2 >= 0 ) { Com_sprintf( buffer, sizeof( buffer ), "%i/%i", value, value2 ); CG_Text_Paint_Ext( 640 - 22 - CG_Text_Width_Ext( buffer, .25f, 0, &cgs.media.limboFont1 ), 480 - 1 * ( 16 + 2 ) + 12 - 4, .25f, .25f, colorWhite, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); // CG_DrawPic( 640 - 2 * ( 12 + 2 ) - 16 - 4, 480 - 1 * ( 16 + 2 ) - 4, 16, 16, cgs.media.SPPlayerInfoAmmoIcon ); } else if ( value >= 0 ) { Com_sprintf( buffer, sizeof( buffer ), "%i", value ); CG_Text_Paint_Ext( 640 - 22 - CG_Text_Width_Ext( buffer, .25f, 0, &cgs.media.limboFont1 ), 480 - 1 * ( 16 + 2 ) + 12 - 4, .25f, .25f, colorWhite, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); // CG_DrawPic( 640 - 2 * ( 12 + 2 ) - 16 - 4, 480 - 1 * ( 16 + 2 ) - 4, 16, 16, cgs.media.SPPlayerInfoAmmoIcon ); } // == rect.x = 24; rect.y = 480 - 92; rect.w = 12; rect.h = 72; CG_DrawPlayerHealthBar( &rect ); // == // == rect.x = 4; rect.y = 480 - 92; rect.w = 12; rect.h = 72; CG_DrawStaminaBar( &rect ); // == // == rect.x = 640 - 16; rect.y = 480 - 92; rect.w = 12; rect.h = 72; CG_DrawWeapRecharge( &rect ); // == } static void CG_DrawSkillBar( float x, float y, float w, float h, int skill ) { int i; float blockheight = ( h - 4 ) / (float)( NUM_SKILL_LEVELS - 1 ); float draw_y; vec4_t colour; float x1, y1, w1, h1; draw_y = y + h - blockheight; for ( i = 0; i < NUM_SKILL_LEVELS - 1; i++ ) { if ( i >= skill ) { Vector4Set( colour, 1.f, 1.f, 1.f, .15f ); } else { Vector4Set( colour, 0.f, 0.f, 0.f, .4f ); } CG_FillRect( x, draw_y, w, blockheight, colour ); if ( i < skill ) { x1 = x; y1 = draw_y; w1 = w; h1 = blockheight; CG_AdjustFrom640( &x1, &y1, &w1, &h1 ); trap_R_DrawStretchPic( x1, y1, w1, h1, 0, 0, 1.f, 0.5f, cgs.media.limboStar_roll ); } CG_DrawRect_FixedBorder( x, draw_y, w, blockheight, 1, colorBlack ); // CG_DrawPic( x, draw_y, w, blockheight, cgs.media.hudBorderVert2 ); draw_y -= ( blockheight + 1 ); } } #define SKILL_ICON_SIZE 14 #define SKILLS_X 112 #define SKILLS_Y 20 #define SKILL_BAR_OFFSET ( 2 * SKILL_BAR_X_INDENT ) #define SKILL_BAR_X_INDENT 0 #define SKILL_BAR_Y_INDENT 6 #define SKILL_BAR_WIDTH ( SKILL_ICON_SIZE - SKILL_BAR_OFFSET ) #define SKILL_BAR_X ( SKILL_BAR_OFFSET + SKILL_BAR_X_INDENT + SKILLS_X ) #define SKILL_BAR_X_SCALE ( SKILL_ICON_SIZE + 2 ) #define SKILL_ICON_X ( SKILL_BAR_OFFSET + SKILLS_X ) #define SKILL_ICON_X_SCALE ( SKILL_ICON_SIZE + 2 ) #define SKILL_BAR_Y ( SKILL_BAR_Y_INDENT - SKILL_BAR_OFFSET - SKILLS_Y ) #define SKILL_BAR_Y_SCALE ( SKILL_ICON_SIZE + 2 ) #define SKILL_ICON_Y ( -( SKILL_ICON_SIZE + 2 ) - SKILL_BAR_OFFSET - SKILLS_Y ) skillType_t CG_ClassSkillForPosition( clientInfo_t* ci, int pos ) { switch ( pos ) { case 0: return BG_ClassSkillForClass( ci->cls ); case 1: return SK_BATTLE_SENSE; case 2: return SK_LIGHT_WEAPONS; } return SK_BATTLE_SENSE; } static void CG_DrawPlayerStats( void ) { int value = 0; playerState_t *ps; clientInfo_t *ci; skillType_t skill; int i; const char* str; float w; vec_t* clr; str = va( "%i", cg.snap->ps.stats[STAT_HEALTH] ); w = CG_Text_Width_Ext( str, 0.25f, 0, &cgs.media.limboFont1 ); CG_Text_Paint_Ext( SKILLS_X - 28 - w, 480 - 4, 0.25f, 0.25f, colorWhite, str, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); CG_Text_Paint_Ext( SKILLS_X - 28 + 2, 480 - 4, 0.2f, 0.2f, colorWhite, "HP", 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); if ( cgs.gametype == GT_WOLF_LMS ) { return; } ps = &cg.snap->ps; ci = &cgs.clientinfo[ ps->clientNum ]; for ( i = 0; i < 3; i++ ) { skill = CG_ClassSkillForPosition( ci, i ); CG_DrawSkillBar( i * SKILL_BAR_X_SCALE + SKILL_BAR_X, 480 - ( 5 * SKILL_BAR_Y_SCALE ) + SKILL_BAR_Y, SKILL_BAR_WIDTH, 4 * SKILL_ICON_SIZE, ci->skill[skill] ); CG_DrawPic( i * SKILL_ICON_X_SCALE + SKILL_ICON_X, 480 + SKILL_ICON_Y, SKILL_ICON_SIZE, SKILL_ICON_SIZE, cgs.media.skillPics[skill] ); } if ( cg.time - cg.xpChangeTime < 1000 ) { clr = colorYellow; } else { clr = colorWhite; } str = va( "%i", cg.snap->ps.stats[STAT_XP] ); w = CG_Text_Width_Ext( str, 0.25f, 0, &cgs.media.limboFont1 ); CG_Text_Paint_Ext( SKILLS_X + 28 - w, 480 - 4, 0.25f, 0.25f, clr, str, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); CG_Text_Paint_Ext( SKILLS_X + 28 + 2, 480 - 4, 0.2f, 0.2f, clr, "XP", 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); // draw treasure icon if we have the flag // rain - #274 - use the playerstate instead of the clientinfo if ( ps->powerups[PW_REDFLAG] || ps->powerups[PW_BLUEFLAG] ) { trap_R_SetColor( NULL ); CG_DrawPic( 640 - 40, 480 - 140 - value, 36, 36, cgs.media.objectiveShader ); } else if ( ps->powerups[PW_OPS_DISGUISED] ) { // Disguised? CG_DrawPic( 640 - 40, 480 - 140 - value, 36, 36, ps->persistant[PERS_TEAM] == TEAM_AXIS ? cgs.media.alliedUniformShader : cgs.media.axisUniformShader ); } } static char statsDebugStrings[6][512]; static int statsDebugTime[6]; static int statsDebugTextWidth[6]; static int statsDebugPos; void CG_InitStatsDebug( void ) { memset( &statsDebugStrings, 0, sizeof( statsDebugStrings ) ); memset( &statsDebugTime, 0, sizeof( statsDebugTime ) ); statsDebugPos = -1; } void CG_StatsDebugAddText( const char *text ) { if ( cg_debugSkills.integer ) { statsDebugPos++; if ( statsDebugPos >= 6 ) { statsDebugPos = 0; } Q_strncpyz( statsDebugStrings[statsDebugPos], text, 512 ); statsDebugTime[statsDebugPos] = cg.time; statsDebugTextWidth[statsDebugPos] = CG_Text_Width_Ext( text, .15f, 0, &cgs.media.limboFont2 ); CG_Printf( "%s\n", text ); } } static void CG_DrawStatsDebug( void ) { int textWidth = 0; int i, x, y, w, h; if ( !cg_debugSkills.integer ) { return; } for ( i = 0; i < 6; i++ ) { if ( statsDebugTime[i] + 9000 > cg.time ) { if ( statsDebugTextWidth[i] > textWidth ) { textWidth = statsDebugTextWidth[i]; } } } w = textWidth + 6; h = 9; x = 640 - w; y = ( 480 - 5 * ( 12 + 2 ) + 6 - 4 ) - 6 - h; // don't ask i = statsDebugPos; do { vec4_t colour; if ( statsDebugTime[i] + 9000 <= cg.time ) { break; } colour[0] = colour[1] = colour[2] = .5f; if ( cg.time - statsDebugTime[i] > 5000 ) { colour[3] = .5f - .5f * ( ( cg.time - statsDebugTime[i] - 5000 ) / 4000.f ); } else { colour[3] = .5f ; } CG_FillRect( x, y, w, h, colour ); colour[0] = colour[1] = colour[2] = 1.f; if ( cg.time - statsDebugTime[i] > 5000 ) { colour[3] = 1.f - ( ( cg.time - statsDebugTime[i] - 5000 ) / 4000.f ); } else { colour[3] = 1.f ; } CG_Text_Paint_Ext( 640.f - 3 - statsDebugTextWidth[i], y + h - 2, .15f, .15f, colour, statsDebugStrings[i], 0, 0, ITEM_TEXTSTYLE_NORMAL, &cgs.media.limboFont2 ); y -= h; i--; if ( i < 0 ) { i = 6 - 1; } } while ( i != statsDebugPos ); } //bani void CG_DrawDemoRecording( void ) { char status[1024]; char demostatus[128]; char wavestatus[128]; if ( !cl_demorecording.integer && !cl_waverecording.integer ) { return; } if ( !cg_recording_statusline.integer ) { return; } if ( cl_demorecording.integer ) { Com_sprintf( demostatus, sizeof( demostatus ), " demo %s: %ik ", cl_demofilename.string, cl_demooffset.integer / 1024 ); } else { strncpy( demostatus, "", sizeof( demostatus ) ); } if ( cl_waverecording.integer ) { Com_sprintf( wavestatus, sizeof( demostatus ), " audio %s: %ik ", cl_wavefilename.string, cl_waveoffset.integer / 1024 ); } else { strncpy( wavestatus, "", sizeof( wavestatus ) ); } Com_sprintf( status, sizeof( status ), "RECORDING%s%s", demostatus, wavestatus ); CG_Text_Paint_Ext( 5, cg_recording_statusline.integer, 0.2f, 0.2f, colorWhite, status, 0, 0, 0, &cgs.media.limboFont2 ); } /* ================= CG_Draw2D ================= */ static void CG_Draw2D( void ) { CG_ScreenFade(); // Arnout: no 2d when in esc menu // FIXME: do allow for quickchat (bleh) // Gordon: Removing for now /* if( trap_Key_GetCatcher() & KEYCATCH_UI ) { return; }*/ if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { CG_DrawIntermission(); return; } else { if ( cgs.dbShowing ) { CG_Debriefing_Shutdown(); } } if ( cg.editingSpeakers ) { CG_SpeakerEditorDraw(); return; } //bani - #127 - no longer cheat protected, we draw crosshair/reticle in non demoplayback if ( cg_draw2D.integer == 0 ) { if ( cg.demoPlayback ) { return; } CG_DrawCrosshair(); CG_DrawFlashFade(); return; } if ( !cg.cameraMode ) { CG_DrawFlashBlendBehindHUD(); if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) { CG_DrawSpectator(); CG_DrawCrosshair(); CG_DrawCrosshairNames(); // NERVE - SMF - we need to do this for spectators as well CG_DrawTeamInfo(); } else { // don't draw any status if dead if ( cg.snap->ps.stats[STAT_HEALTH] > 0 || ( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) { CG_DrawCrosshair(); CG_DrawCrosshairNames(); CG_DrawNoShootIcon(); // CG_DrawPickupItem(); } CG_DrawTeamInfo(); if ( cg_drawStatus.integer ) { Menu_PaintAll(); CG_DrawTimedMenus(); } } CG_DrawVote(); CG_DrawLagometer(); } // don't draw center string if scoreboard is up if ( !CG_DrawScoreboard() ) { if ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) { rectDef_t rect; if ( cg.snap->ps.stats[STAT_HEALTH] > 0 ) { CG_DrawPlayerStatusHead(); CG_DrawPlayerStatus(); CG_DrawPlayerStats(); } CG_DrawLivesLeft(); // Cursor hint rect.w = rect.h = 48; rect.x = .5f * SCREEN_WIDTH - .5f * rect.w; rect.y = 260; CG_DrawCursorhint( &rect ); // Stability bar rect.x = 50; rect.y = 208; rect.w = 10; rect.h = 64; CG_DrawWeapStability( &rect ); // Stats Debugging CG_DrawStatsDebug(); } if ( !cg_paused.integer ) { CG_DrawUpperRight(); } CG_DrawCenterString(); CG_DrawPMItems(); CG_DrawPMItemsBig(); CG_DrawFollow(); CG_DrawWarmup(); CG_DrawNotify(); if ( cg_drawCompass.integer ) { CG_DrawNewCompass(); } CG_DrawObjectiveInfo(); CG_DrawSpectatorMessage(); CG_DrawLimboMessage(); } else { if ( cgs.eventHandling != CGAME_EVENT_NONE ) { // qboolean old = cg.showGameView; // cg.showGameView = qfalse; // draw cursor trap_R_SetColor( NULL ); CG_DrawPic( cgDC.cursorx - 14, cgDC.cursory - 14, 32, 32, cgs.media.cursorIcon ); // cg.showGameView = old; } } if ( cg.showFireteamMenu ) { CG_Fireteams_Draw(); } // Info overlays CG_DrawOverlays(); // OSP - window updates CG_windowDraw(); // Ridah, draw flash blends now CG_DrawFlashBlend(); CG_DrawDemoRecording(); } // NERVE - SMF void CG_StartShakeCamera( float p ) { cg.cameraShakeScale = p; cg.cameraShakeLength = 1000 * ( p * p ); cg.cameraShakeTime = cg.time + cg.cameraShakeLength; cg.cameraShakePhase = crandom() * M_PI; // start chain in random dir } void CG_ShakeCamera() { float x, val; if ( cg.time > cg.cameraShakeTime ) { cg.cameraShakeScale = 0; // JPW NERVE all pending explosions resolved, so reset shakescale return; } // JPW NERVE starts at 1, approaches 0 over time x = ( cg.cameraShakeTime - cg.time ) / cg.cameraShakeLength; // ydnar: move the camera #if 0 // up/down val = sin( M_PI * 8 * x + cg.cameraShakePhase ) * x * 18.0f * cg.cameraShakeScale; cg.refdefViewAngles[0] += val; // left/right val = sin( M_PI * 15 * x + cg.cameraShakePhase ) * x * 16.0f * cg.cameraShakeScale; cg.refdefViewAngles[1] += val; #else // move val = sin( M_PI * 7 * x + cg.cameraShakePhase ) * x * 4.0f * cg.cameraShakeScale; cg.refdef.vieworg[ 2 ] += val; val = sin( M_PI * 13 * x + cg.cameraShakePhase ) * x * 4.0f * cg.cameraShakeScale; cg.refdef.vieworg[ 1 ] += val; val = cos( M_PI * 17 * x + cg.cameraShakePhase ) * x * 4.0f * cg.cameraShakeScale; cg.refdef.vieworg[ 0 ] += val; #endif AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); } // -NERVE - SMF void CG_DrawMiscGamemodels( void ) { int i, j; refEntity_t ent; int drawn = 0; memset( &ent, 0, sizeof( ent ) ); ent.reType = RT_MODEL; ent.nonNormalizedAxes = qtrue; // ydnar: static gamemodels don't project shadows ent.renderfx = RF_NOSHADOW; for ( i = 0; i < cg.numMiscGameModels; i++ ) { if ( cgs.miscGameModels[i].radius ) { if ( CG_CullPointAndRadius( cgs.miscGameModels[i].org, cgs.miscGameModels[i].radius ) ) { continue; } } if ( !trap_R_inPVS( cg.refdef_current->vieworg, cgs.miscGameModels[i].org ) ) { continue; } VectorCopy( cgs.miscGameModels[i].org, ent.origin ); VectorCopy( cgs.miscGameModels[i].org, ent.oldorigin ); VectorCopy( cgs.miscGameModels[i].org, ent.lightingOrigin ); /* { vec3_t v; vec3_t vu = { 0.f, 0.f, 1.f }; vec3_t vl = { 0.f, 1.f, 0.f }; vec3_t vf = { 1.f, 0.f, 0.f }; VectorCopy( cgs.miscGameModels[i].org, v ); VectorMA( v, cgs.miscGameModels[i].radius, vu, v ); CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); VectorCopy( cgs.miscGameModels[i].org, v ); VectorMA( v, cgs.miscGameModels[i].radius, vf, v ); CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); VectorCopy( cgs.miscGameModels[i].org, v ); VectorMA( v, cgs.miscGameModels[i].radius, vl, v ); CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); VectorCopy( cgs.miscGameModels[i].org, v ); VectorMA( v, -cgs.miscGameModels[i].radius, vu, v ); CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); VectorCopy( cgs.miscGameModels[i].org, v ); VectorMA( v, -cgs.miscGameModels[i].radius, vf, v ); CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); VectorCopy( cgs.miscGameModels[i].org, v ); VectorMA( v, -cgs.miscGameModels[i].radius, vl, v ); CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); }*/ for ( j = 0; j < 3; j++ ) { VectorCopy( cgs.miscGameModels[i].axes[j], ent.axis[j] ); } ent.hModel = cgs.miscGameModels[i].model; trap_R_AddRefEntityToScene( &ent ); drawn++; } } /* ===================== 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( qfalse ); 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_current->vieworg, baseOrg ); if ( separation != 0 ) { VectorMA( cg.refdef_current->vieworg, -separation, cg.refdef_current->viewaxis[1], cg.refdef_current->vieworg ); } cg.refdef_current->glfog.registered = 0; // make sure it doesn't use fog from another scene CG_ActivateLimboMenu(); // if( cgs.ccCurrentCamObjective == -1 ) { // if( cg.showGameView ) { // CG_FillRect( 0, 0, 640, 480, colorBlack ); // CG_LimboPanel_Draw(); // return; // } // } if ( cg.showGameView ) { float x, y, w, h; x = LIMBO_3D_X; y = LIMBO_3D_Y; w = LIMBO_3D_W; h = LIMBO_3D_H; CG_AdjustFrom640( &x, &y, &w, &h ); cg.refdef_current->x = x; cg.refdef_current->y = y; cg.refdef_current->width = w; cg.refdef_current->height = h; CG_Letterbox( ( LIMBO_3D_W / 640.f ) * 100, ( LIMBO_3D_H / 480.f ) * 100, qfalse ); } CG_ShakeCamera(); // NERVE - SMF // Gordon CG_PB_RenderPolyBuffers(); // Gordon CG_DrawMiscGamemodels(); if ( !( cg.limboEndCinematicTime > cg.time && cg.showGameView ) ) { trap_R_RenderScene( cg.refdef_current ); } // restore original viewpoint if running stereo if ( separation != 0 ) { VectorCopy( baseOrg, cg.refdef_current->vieworg ); } if ( !cg.showGameView ) { // draw status bar and other floating elements CG_Draw2D(); } else { CG_LimboPanel_Draw(); } }