ioq3/code/cgame/cg_draw.c
Zack Middleton e152761c28 Fix team chat box for spectators
Make spectators use green background for team chat box when following
players. The team chat messages are from spectators not the team of
the followed player.

Make spectators draw team chat box even when not following a player.
2017-09-02 17:57:26 -05:00

2658 lines
58 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena 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 2 of the License,
or (at your option) any later version.
Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
//
// cg_draw.c -- draw all of the graphical elements during
// active (after loading) gameplay
#include "cg_local.h"
#ifdef MISSIONPACK
#include "../ui/ui_shared.h"
// used for scoreboard
extern displayContextDef_t cgDC;
menuDef_t *menuScoreboard = NULL;
#else
int drawTeamOverlayModificationCount = -1;
#endif
int sortedTeamPlayers[TEAM_MAXOVERLAY];
int numSortedTeamPlayers;
char systemChat[256];
char teamChat1[256];
char teamChat2[256];
#ifdef MISSIONPACK
int CG_Text_Width(const char *text, float scale, int limit) {
int count,len;
float out;
glyphInfo_t *glyph;
float useScale;
const char *s = text;
fontInfo_t *font = &cgDC.Assets.textFont;
if (scale <= cg_smallFont.value) {
font = &cgDC.Assets.smallFont;
} else if (scale > cg_bigFont.value) {
font = &cgDC.Assets.bigFont;
}
useScale = scale * font->glyphScale;
out = 0;
if (text) {
len = strlen(text);
if (limit > 0 && len > limit) {
len = limit;
}
count = 0;
while (s && *s && count < len) {
if ( Q_IsColorString(s) ) {
s += 2;
continue;
} else {
glyph = &font->glyphs[*s & 255];
out += glyph->xSkip;
s++;
count++;
}
}
}
return out * useScale;
}
int CG_Text_Height(const char *text, float scale, int limit) {
int len, count;
float max;
glyphInfo_t *glyph;
float useScale;
const char *s = text;
fontInfo_t *font = &cgDC.Assets.textFont;
if (scale <= cg_smallFont.value) {
font = &cgDC.Assets.smallFont;
} else if (scale > cg_bigFont.value) {
font = &cgDC.Assets.bigFont;
}
useScale = scale * font->glyphScale;
max = 0;
if (text) {
len = strlen(text);
if (limit > 0 && len > limit) {
len = limit;
}
count = 0;
while (s && *s && count < len) {
if ( Q_IsColorString(s) ) {
s += 2;
continue;
} else {
glyph = &font->glyphs[*s & 255];
if (max < glyph->height) {
max = glyph->height;
}
s++;
count++;
}
}
}
return max * useScale;
}
void CG_Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader) {
float w, h;
w = width * scale;
h = height * scale;
CG_AdjustFrom640( &x, &y, &w, &h );
trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader );
}
void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style) {
int len, count;
vec4_t newColor;
glyphInfo_t *glyph;
float useScale;
fontInfo_t *font = &cgDC.Assets.textFont;
if (scale <= cg_smallFont.value) {
font = &cgDC.Assets.smallFont;
} else if (scale > cg_bigFont.value) {
font = &cgDC.Assets.bigFont;
}
useScale = scale * font->glyphScale;
if (text) {
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[*s & 255];
//int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top;
//float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height);
if ( Q_IsColorString( s ) ) {
memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) );
newColor[3] = color[3];
trap_R_SetColor( newColor );
s += 2;
continue;
} else {
float yadj = useScale * glyph->top;
if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) {
int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;
colorBlack[3] = newColor[3];
trap_R_SetColor( colorBlack );
CG_Text_PaintChar(x + ofs, y - yadj + ofs,
glyph->imageWidth,
glyph->imageHeight,
useScale,
glyph->s,
glyph->t,
glyph->s2,
glyph->t2,
glyph->glyph);
colorBlack[3] = 1.0;
trap_R_SetColor( newColor );
}
CG_Text_PaintChar(x, y - yadj,
glyph->imageWidth,
glyph->imageHeight,
useScale,
glyph->s,
glyph->t,
glyph->s2,
glyph->t2,
glyph->glyph);
// CG_DrawPic(x, y - yadj, scale * cgDC.Assets.textFont.glyphs[text[i]].imageWidth, scale * cgDC.Assets.textFont.glyphs[text[i]].imageHeight, cgDC.Assets.textFont.glyphs[text[i]].glyph);
x += (glyph->xSkip * useScale) + adjust;
s++;
count++;
}
}
trap_R_SetColor( NULL );
}
}
#endif
/*
==============
CG_DrawField
Draws large numbers for status bar and powerups
==============
*/
#ifndef MISSIONPACK
static void CG_DrawField (int x, int y, int width, int value) {
char num[16], *ptr;
int l;
int frame;
if ( width < 1 ) {
return;
}
// draw number string
if ( width > 5 ) {
width = 5;
}
switch ( width ) {
case 1:
value = value > 9 ? 9 : value;
value = value < 0 ? 0 : value;
break;
case 2:
value = value > 99 ? 99 : value;
value = value < -9 ? -9 : value;
break;
case 3:
value = value > 999 ? 999 : value;
value = value < -99 ? -99 : value;
break;
case 4:
value = value > 9999 ? 9999 : value;
value = value < -999 ? -999 : value;
break;
}
Com_sprintf (num, sizeof(num), "%i", value);
l = strlen(num);
if (l > width)
l = width;
x += 2 + CHAR_WIDTH*(width - l);
ptr = num;
while (*ptr && l)
{
if (*ptr == '-')
frame = STAT_MINUS;
else
frame = *ptr -'0';
CG_DrawPic( x,y, CHAR_WIDTH, CHAR_HEIGHT, cgs.media.numberShaders[frame] );
x += CHAR_WIDTH;
ptr++;
l--;
}
}
#endif // MISSIONPACK
/*
================
CG_Draw3DModel
================
*/
void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles ) {
refdef_t refdef;
refEntity_t ent;
if ( !cg_draw3dIcons.integer || !cg_drawIcons.integer ) {
return;
}
CG_AdjustFrom640( &x, &y, &w, &h );
memset( &refdef, 0, sizeof( refdef ) );
memset( &ent, 0, sizeof( ent ) );
AnglesToAxis( angles, ent.axis );
VectorCopy( origin, ent.origin );
ent.hModel = model;
ent.customSkin = skin;
ent.renderfx = RF_NOSHADOW; // no stencil shadows
refdef.rdflags = RDF_NOWORLDMODEL;
AxisClear( refdef.viewaxis );
refdef.fov_x = 30;
refdef.fov_y = 30;
refdef.x = x;
refdef.y = y;
refdef.width = w;
refdef.height = h;
refdef.time = cg.time;
trap_R_ClearScene();
trap_R_AddRefEntityToScene( &ent );
trap_R_RenderScene( &refdef );
}
/*
================
CG_DrawHead
Used for both the status bar and the scoreboard
================
*/
void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ) {
clipHandle_t cm;
clientInfo_t *ci;
float len;
vec3_t origin;
vec3_t mins, maxs;
ci = &cgs.clientinfo[ clientNum ];
if ( cg_draw3dIcons.integer ) {
cm = ci->headModel;
if ( !cm ) {
return;
}
// offset the origin y and z to center the head
trap_R_ModelBounds( cm, mins, maxs );
origin[2] = -0.5 * ( mins[2] + maxs[2] );
origin[1] = 0.5 * ( mins[1] + maxs[1] );
// calculate distance so the head nearly fills the box
// assume heads are taller than wide
len = 0.7 * ( maxs[2] - mins[2] );
origin[0] = len / 0.268; // len / tan( fov/2 )
// allow per-model tweaking
VectorAdd( origin, ci->headOffset, origin );
CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, origin, headAngles );
} else if ( cg_drawIcons.integer ) {
CG_DrawPic( x, y, w, h, ci->modelIcon );
}
// if they are deferred, draw a cross out
if ( ci->deferred ) {
CG_DrawPic( x, y, w, h, cgs.media.deferShader );
}
}
/*
================
CG_DrawFlagModel
Used for both the status bar and the scoreboard
================
*/
void CG_DrawFlagModel( float x, float y, float w, float h, int team, qboolean force2D ) {
qhandle_t cm;
float len;
vec3_t origin, angles;
vec3_t mins, maxs;
qhandle_t handle;
if ( !force2D && cg_draw3dIcons.integer ) {
VectorClear( angles );
cm = cgs.media.redFlagModel;
// offset the origin y and z to center the flag
trap_R_ModelBounds( cm, mins, maxs );
origin[2] = -0.5 * ( mins[2] + maxs[2] );
origin[1] = 0.5 * ( mins[1] + maxs[1] );
// calculate distance so the flag nearly fills the box
// assume heads are taller than wide
len = 0.5 * ( maxs[2] - mins[2] );
origin[0] = len / 0.268; // len / tan( fov/2 )
angles[YAW] = 60 * sin( cg.time / 2000.0 );;
if( team == TEAM_RED ) {
handle = cgs.media.redFlagModel;
} else if( team == TEAM_BLUE ) {
handle = cgs.media.blueFlagModel;
} else if( team == TEAM_FREE ) {
handle = cgs.media.neutralFlagModel;
} else {
return;
}
CG_Draw3DModel( x, y, w, h, handle, 0, origin, angles );
} else if ( cg_drawIcons.integer ) {
gitem_t *item;
if( team == TEAM_RED ) {
item = BG_FindItemForPowerup( PW_REDFLAG );
} else if( team == TEAM_BLUE ) {
item = BG_FindItemForPowerup( PW_BLUEFLAG );
} else if( team == TEAM_FREE ) {
item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
} else {
return;
}
if (item) {
CG_DrawPic( x, y, w, h, cg_items[ ITEM_INDEX(item) ].icon );
}
}
}
/*
================
CG_DrawStatusBarHead
================
*/
#ifndef MISSIONPACK
static void CG_DrawStatusBarHead( float x ) {
vec3_t angles;
float size, stretch;
float frac;
VectorClear( angles );
if ( cg.damageTime && cg.time - cg.damageTime < DAMAGE_TIME ) {
frac = (float)(cg.time - cg.damageTime ) / DAMAGE_TIME;
size = ICON_SIZE * 1.25 * ( 1.5 - frac * 0.5 );
stretch = size - ICON_SIZE * 1.25;
// kick in the direction of damage
x -= stretch * 0.5 + cg.damageX * stretch * 0.5;
cg.headStartYaw = 180 + cg.damageX * 45;
cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI );
cg.headEndPitch = 5 * cos( crandom()*M_PI );
cg.headStartTime = cg.time;
cg.headEndTime = cg.time + 100 + random() * 2000;
} else {
if ( cg.time >= cg.headEndTime ) {
// select a new head angle
cg.headStartYaw = cg.headEndYaw;
cg.headStartPitch = cg.headEndPitch;
cg.headStartTime = cg.headEndTime;
cg.headEndTime = cg.time + 100 + random() * 2000;
cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI );
cg.headEndPitch = 5 * cos( crandom()*M_PI );
}
size = ICON_SIZE * 1.25;
}
// if the server was frozen for a while we may have a bad head start time
if ( cg.headStartTime > cg.time ) {
cg.headStartTime = cg.time;
}
frac = ( cg.time - cg.headStartTime ) / (float)( cg.headEndTime - cg.headStartTime );
frac = frac * frac * ( 3 - 2 * frac );
angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac;
angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac;
CG_DrawHead( x, 480 - size, size, size,
cg.snap->ps.clientNum, angles );
}
#endif // MISSIONPACK
/*
================
CG_DrawStatusBarFlag
================
*/
#ifndef MISSIONPACK
static void CG_DrawStatusBarFlag( float x, int team ) {
CG_DrawFlagModel( x, 480 - ICON_SIZE, ICON_SIZE, ICON_SIZE, team, qfalse );
}
#endif // MISSIONPACK
/*
================
CG_DrawTeamBackground
================
*/
void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team )
{
vec4_t hcolor;
hcolor[3] = alpha;
if ( team == TEAM_RED ) {
hcolor[0] = 1;
hcolor[1] = 0;
hcolor[2] = 0;
} else if ( team == TEAM_BLUE ) {
hcolor[0] = 0;
hcolor[1] = 0;
hcolor[2] = 1;
} else {
return;
}
trap_R_SetColor( hcolor );
CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
trap_R_SetColor( NULL );
}
/*
================
CG_DrawStatusBar
================
*/
#ifndef MISSIONPACK
static void CG_DrawStatusBar( void ) {
int color;
centity_t *cent;
playerState_t *ps;
int value;
vec4_t hcolor;
vec3_t angles;
vec3_t origin;
static float colors[4][4] = {
// { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} };
{ 1.0f, 0.69f, 0.0f, 1.0f }, // normal
{ 1.0f, 0.2f, 0.2f, 1.0f }, // low health
{ 0.5f, 0.5f, 0.5f, 1.0f }, // weapon firing
{ 1.0f, 1.0f, 1.0f, 1.0f } }; // health > 100
if ( cg_drawStatus.integer == 0 ) {
return;
}
// draw the team background
CG_DrawTeamBackground( 0, 420, 640, 60, 0.33f, cg.snap->ps.persistant[PERS_TEAM] );
cent = &cg_entities[cg.snap->ps.clientNum];
ps = &cg.snap->ps;
VectorClear( angles );
// draw any 3D icons first, so the changes back to 2D are minimized
if ( cent->currentState.weapon && cg_weapons[ cent->currentState.weapon ].ammoModel ) {
origin[0] = 70;
origin[1] = 0;
origin[2] = 0;
angles[YAW] = 90 + 20 * sin( cg.time / 1000.0 );
CG_Draw3DModel( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE,
cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles );
}
CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE );
if( cg.predictedPlayerState.powerups[PW_REDFLAG] ) {
CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_RED );
} else if( cg.predictedPlayerState.powerups[PW_BLUEFLAG] ) {
CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_BLUE );
} else if( cg.predictedPlayerState.powerups[PW_NEUTRALFLAG] ) {
CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_FREE );
}
if ( ps->stats[ STAT_ARMOR ] ) {
origin[0] = 90;
origin[1] = 0;
origin[2] = -10;
angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0;
CG_Draw3DModel( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE,
cgs.media.armorModel, 0, origin, angles );
}
//
// ammo
//
if ( cent->currentState.weapon ) {
value = ps->ammo[cent->currentState.weapon];
if ( value > -1 ) {
if ( cg.predictedPlayerState.weaponstate == WEAPON_FIRING
&& cg.predictedPlayerState.weaponTime > 100 ) {
// draw as dark grey when reloading
color = 2; // dark grey
} else {
if ( value >= 0 ) {
color = 0; // green
} else {
color = 1; // red
}
}
trap_R_SetColor( colors[color] );
CG_DrawField (0, 432, 3, value);
trap_R_SetColor( NULL );
// if we didn't draw a 3D icon, draw a 2D icon for ammo
if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) {
qhandle_t icon;
icon = cg_weapons[ cg.predictedPlayerState.weapon ].ammoIcon;
if ( icon ) {
CG_DrawPic( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, icon );
}
}
}
}
//
// health
//
value = ps->stats[STAT_HEALTH];
if ( value > 100 ) {
trap_R_SetColor( colors[3] ); // white
} else if (value > 25) {
trap_R_SetColor( colors[0] ); // green
} else if (value > 0) {
color = (cg.time >> 8) & 1; // flash
trap_R_SetColor( colors[color] );
} else {
trap_R_SetColor( colors[1] ); // red
}
// stretch the health up when taking damage
CG_DrawField ( 185, 432, 3, value);
CG_ColorForHealth( hcolor );
trap_R_SetColor( hcolor );
//
// armor
//
value = ps->stats[STAT_ARMOR];
if (value > 0 ) {
trap_R_SetColor( colors[0] );
CG_DrawField (370, 432, 3, value);
trap_R_SetColor( NULL );
// if we didn't draw a 3D icon, draw a 2D icon for armor
if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) {
CG_DrawPic( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cgs.media.armorIcon );
}
}
}
#endif
/*
===========================================================================================
UPPER RIGHT CORNER
===========================================================================================
*/
/*
================
CG_DrawAttacker
================
*/
static float CG_DrawAttacker( float y ) {
int t;
float size;
vec3_t angles;
const char *info;
const char *name;
int clientNum;
if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
return y;
}
if ( !cg.attackerTime ) {
return y;
}
clientNum = cg.predictedPlayerState.persistant[PERS_ATTACKER];
if ( clientNum < 0 || clientNum >= MAX_CLIENTS || clientNum == cg.snap->ps.clientNum ) {
return y;
}
if ( !cgs.clientinfo[clientNum].infoValid ) {
cg.attackerTime = 0;
return y;
}
t = cg.time - cg.attackerTime;
if ( t > ATTACKER_HEAD_TIME ) {
cg.attackerTime = 0;
return y;
}
size = ICON_SIZE * 1.25;
angles[PITCH] = 0;
angles[YAW] = 180;
angles[ROLL] = 0;
CG_DrawHead( 640 - size, y, size, size, clientNum, angles );
info = CG_ConfigString( CS_PLAYERS + clientNum );
name = Info_ValueForKey( info, "n" );
y += size;
CG_DrawBigString( 640 - ( Q_PrintStrlen( name ) * BIGCHAR_WIDTH), y, name, 0.5 );
return y + BIGCHAR_HEIGHT + 2;
}
/*
==================
CG_DrawSnapshot
==================
*/
static float CG_DrawSnapshot( float y ) {
char *s;
int w;
s = va( "time:%i snap:%i cmd:%i", cg.snap->serverTime,
cg.latestSnapshotNum, cgs.serverCommandSequence );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
CG_DrawBigString( 635 - w, y + 2, s, 1.0F);
return y + BIGCHAR_HEIGHT + 4;
}
/*
==================
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;
// 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( "%ifps", fps );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
CG_DrawBigString( 635 - w, y + 2, s, 1.0F);
}
return y + BIGCHAR_HEIGHT + 4;
}
/*
=================
CG_DrawTimer
=================
*/
static float CG_DrawTimer( float y ) {
char *s;
int w;
int mins, seconds, tens;
int msec;
msec = cg.time - cgs.levelStartTime;
seconds = msec / 1000;
mins = seconds / 60;
seconds -= mins * 60;
tens = seconds / 10;
seconds -= tens * 10;
s = va( "%i:%i%i", mins, tens, seconds );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
CG_DrawBigString( 635 - w, y + 2, s, 1.0F);
return y + BIGCHAR_HEIGHT + 4;
}
/*
=================
CG_DrawTeamOverlay
=================
*/
static float CG_DrawTeamOverlay( float y, qboolean right, qboolean upper ) {
int x, w, h, xx;
int i, j, len;
const char *p;
vec4_t hcolor;
int pwidth, lwidth;
int plyrs;
char st[16];
clientInfo_t *ci;
gitem_t *item;
int ret_y, count;
if ( !cg_drawTeamOverlay.integer ) {
return y;
}
if ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_RED && cg.snap->ps.persistant[PERS_TEAM] != TEAM_BLUE ) {
return y; // Not on any team
}
plyrs = 0;
// max player name width
pwidth = 0;
count = (numSortedTeamPlayers > 8) ? 8 : numSortedTeamPlayers;
for (i = 0; i < count; i++) {
ci = cgs.clientinfo + sortedTeamPlayers[i];
if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) {
plyrs++;
len = CG_DrawStrlen(ci->name);
if (len > pwidth)
pwidth = len;
}
}
if (!plyrs)
return y;
if (pwidth > TEAM_OVERLAY_MAXNAME_WIDTH)
pwidth = TEAM_OVERLAY_MAXNAME_WIDTH;
// max location name width
lwidth = 0;
for (i = 1; i < MAX_LOCATIONS; i++) {
p = CG_ConfigString(CS_LOCATIONS + i);
if (p && *p) {
len = CG_DrawStrlen(p);
if (len > lwidth)
lwidth = len;
}
}
if (lwidth > TEAM_OVERLAY_MAXLOCATION_WIDTH)
lwidth = TEAM_OVERLAY_MAXLOCATION_WIDTH;
w = (pwidth + lwidth + 4 + 7) * TINYCHAR_WIDTH;
if ( right )
x = 640 - w;
else
x = 0;
h = plyrs * TINYCHAR_HEIGHT;
if ( upper ) {
ret_y = y + h;
} else {
y -= h;
ret_y = y;
}
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) {
hcolor[0] = 1.0f;
hcolor[1] = 0.0f;
hcolor[2] = 0.0f;
hcolor[3] = 0.33f;
} else { // if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE )
hcolor[0] = 0.0f;
hcolor[1] = 0.0f;
hcolor[2] = 1.0f;
hcolor[3] = 0.33f;
}
trap_R_SetColor( hcolor );
CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
trap_R_SetColor( NULL );
for (i = 0; i < count; i++) {
ci = cgs.clientinfo + sortedTeamPlayers[i];
if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) {
hcolor[0] = hcolor[1] = hcolor[2] = hcolor[3] = 1.0;
xx = x + TINYCHAR_WIDTH;
CG_DrawStringExt( xx, y,
ci->name, hcolor, qfalse, qfalse,
TINYCHAR_WIDTH, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXNAME_WIDTH);
if (lwidth) {
p = CG_ConfigString(CS_LOCATIONS + ci->location);
if (!p || !*p)
p = "unknown";
// len = CG_DrawStrlen(p);
// if (len > lwidth)
// len = lwidth;
// xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth +
// ((lwidth/2 - len/2) * TINYCHAR_WIDTH);
xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth;
CG_DrawStringExt( xx, y,
p, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT,
TEAM_OVERLAY_MAXLOCATION_WIDTH);
}
CG_GetColorForHealth( ci->health, ci->armor, hcolor );
Com_sprintf (st, sizeof(st), "%3i %3i", ci->health, ci->armor);
xx = x + TINYCHAR_WIDTH * 3 +
TINYCHAR_WIDTH * pwidth + TINYCHAR_WIDTH * lwidth;
CG_DrawStringExt( xx, y,
st, hcolor, qfalse, qfalse,
TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 );
// draw weapon icon
xx += TINYCHAR_WIDTH * 3;
if ( cg_weapons[ci->curWeapon].weaponIcon ) {
CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT,
cg_weapons[ci->curWeapon].weaponIcon );
} else {
CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT,
cgs.media.deferShader );
}
// Draw powerup icons
if (right) {
xx = x;
} else {
xx = x + w - TINYCHAR_WIDTH;
}
for (j = 0; j <= PW_NUM_POWERUPS; j++) {
if (ci->powerups & (1 << j)) {
item = BG_FindItemForPowerup( j );
if (item) {
CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT,
trap_R_RegisterShader( item->icon ) );
if (right) {
xx -= TINYCHAR_WIDTH;
} else {
xx += TINYCHAR_WIDTH;
}
}
}
}
y += TINYCHAR_HEIGHT;
}
}
return ret_y;
//#endif
}
/*
=====================
CG_DrawUpperRight
=====================
*/
static void CG_DrawUpperRight(stereoFrame_t stereoFrame)
{
float y;
y = 0;
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 1 ) {
y = CG_DrawTeamOverlay( y, qtrue, qtrue );
}
if ( cg_drawSnapshot.integer ) {
y = CG_DrawSnapshot( y );
}
if (cg_drawFPS.integer && (stereoFrame == STEREO_CENTER || stereoFrame == STEREO_RIGHT)) {
y = CG_DrawFPS( y );
}
if ( cg_drawTimer.integer ) {
y = CG_DrawTimer( y );
}
if ( cg_drawAttacker.integer ) {
CG_DrawAttacker( y );
}
}
/*
===========================================================================================
LOWER RIGHT CORNER
===========================================================================================
*/
/*
=================
CG_DrawScores
Draw the small two score display
=================
*/
#ifndef MISSIONPACK
static float CG_DrawScores( float y ) {
const char *s;
int s1, s2, score;
int x, w;
int v;
vec4_t color;
float y1;
gitem_t *item;
s1 = cgs.scores1;
s2 = cgs.scores2;
y -= BIGCHAR_HEIGHT + 8;
y1 = y;
// draw from the right side to left
if ( cgs.gametype >= GT_TEAM ) {
x = 640;
color[0] = 0.0f;
color[1] = 0.0f;
color[2] = 1.0f;
color[3] = 0.33f;
s = va( "%2i", s2 );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
x -= w;
CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
}
CG_DrawBigString( x + 4, y, s, 1.0F);
if ( cgs.gametype == GT_CTF ) {
// Display flag status
item = BG_FindItemForPowerup( PW_BLUEFLAG );
if (item) {
y1 = y - BIGCHAR_HEIGHT - 8;
if( cgs.blueflag >= 0 && cgs.blueflag <= 2 ) {
CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.blueFlagShader[cgs.blueflag] );
}
}
}
color[0] = 1.0f;
color[1] = 0.0f;
color[2] = 0.0f;
color[3] = 0.33f;
s = va( "%2i", s1 );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
x -= w;
CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) {
CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
}
CG_DrawBigString( x + 4, y, s, 1.0F);
if ( cgs.gametype == GT_CTF ) {
// Display flag status
item = BG_FindItemForPowerup( PW_REDFLAG );
if (item) {
y1 = y - BIGCHAR_HEIGHT - 8;
if( cgs.redflag >= 0 && cgs.redflag <= 2 ) {
CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.redFlagShader[cgs.redflag] );
}
}
}
if ( cgs.gametype >= GT_CTF ) {
v = cgs.capturelimit;
} else {
v = cgs.fraglimit;
}
if ( v ) {
s = va( "%2i", v );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
x -= w;
CG_DrawBigString( x + 4, y, s, 1.0F);
}
} else {
qboolean spectator;
x = 640;
score = cg.snap->ps.persistant[PERS_SCORE];
spectator = ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR );
// always show your score in the second box if not in first place
if ( s1 != score ) {
s2 = score;
}
if ( s2 != SCORE_NOT_PRESENT ) {
s = va( "%2i", s2 );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
x -= w;
if ( !spectator && score == s2 && score != s1 ) {
color[0] = 1.0f;
color[1] = 0.0f;
color[2] = 0.0f;
color[3] = 0.33f;
CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
} else {
color[0] = 0.5f;
color[1] = 0.5f;
color[2] = 0.5f;
color[3] = 0.33f;
CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
}
CG_DrawBigString( x + 4, y, s, 1.0F);
}
// first place
if ( s1 != SCORE_NOT_PRESENT ) {
s = va( "%2i", s1 );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
x -= w;
if ( !spectator && score == s1 ) {
color[0] = 0.0f;
color[1] = 0.0f;
color[2] = 1.0f;
color[3] = 0.33f;
CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
} else {
color[0] = 0.5f;
color[1] = 0.5f;
color[2] = 0.5f;
color[3] = 0.33f;
CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
}
CG_DrawBigString( x + 4, y, s, 1.0F);
}
if ( cgs.fraglimit ) {
s = va( "%2i", cgs.fraglimit );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
x -= w;
CG_DrawBigString( x + 4, y, s, 1.0F);
}
}
return y1 - 8;
}
#endif // MISSIONPACK
/*
================
CG_DrawPowerups
================
*/
#ifndef MISSIONPACK
static float CG_DrawPowerups( float y ) {
int sorted[MAX_POWERUPS];
int sortedTime[MAX_POWERUPS];
int i, j, k;
int active;
playerState_t *ps;
int t;
gitem_t *item;
int x;
int color;
float size;
float f;
static float colors[2][4] = {
{ 0.2f, 1.0f, 0.2f, 1.0f } ,
{ 1.0f, 0.2f, 0.2f, 1.0f }
};
ps = &cg.snap->ps;
if ( ps->stats[STAT_HEALTH] <= 0 ) {
return y;
}
// sort the list by time remaining
active = 0;
for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
if ( !ps->powerups[ i ] ) {
continue;
}
// ZOID--don't draw if the power up has unlimited time
// This is true of the CTF flags
if ( ps->powerups[ i ] == INT_MAX ) {
continue;
}
t = ps->powerups[ i ] - cg.time;
if ( t <= 0 ) {
continue;
}
// insert into the list
for ( j = 0 ; j < active ; j++ ) {
if ( sortedTime[j] >= t ) {
for ( k = active - 1 ; k >= j ; k-- ) {
sorted[k+1] = sorted[k];
sortedTime[k+1] = sortedTime[k];
}
break;
}
}
sorted[j] = i;
sortedTime[j] = t;
active++;
}
// draw the icons and timers
x = 640 - ICON_SIZE - CHAR_WIDTH * 2;
for ( i = 0 ; i < active ; i++ ) {
item = BG_FindItemForPowerup( sorted[i] );
if (item) {
color = 1;
y -= ICON_SIZE;
trap_R_SetColor( colors[color] );
CG_DrawField( x, y, 2, sortedTime[ i ] / 1000 );
t = ps->powerups[ sorted[i] ];
if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
trap_R_SetColor( NULL );
} else {
vec4_t modulate;
f = (float)( t - cg.time ) / POWERUP_BLINK_TIME;
f -= (int)f;
modulate[0] = modulate[1] = modulate[2] = modulate[3] = f;
trap_R_SetColor( modulate );
}
if ( cg.powerupActive == sorted[i] &&
cg.time - cg.powerupTime < PULSE_TIME ) {
f = 1.0 - ( ( (float)cg.time - cg.powerupTime ) / PULSE_TIME );
size = ICON_SIZE * ( 1.0 + ( PULSE_SCALE - 1.0 ) * f );
} else {
size = ICON_SIZE;
}
CG_DrawPic( 640 - size, y + ICON_SIZE / 2 - size / 2,
size, size, trap_R_RegisterShader( item->icon ) );
}
}
trap_R_SetColor( NULL );
return y;
}
#endif // MISSIONPACK
/*
=====================
CG_DrawLowerRight
=====================
*/
#ifndef MISSIONPACK
static void CG_DrawLowerRight( void ) {
float y;
y = 480 - ICON_SIZE;
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2 ) {
y = CG_DrawTeamOverlay( y, qtrue, qfalse );
}
y = CG_DrawScores( y );
CG_DrawPowerups( y );
}
#endif // MISSIONPACK
/*
===================
CG_DrawPickupItem
===================
*/
#ifndef MISSIONPACK
static int CG_DrawPickupItem( int y ) {
int value;
float *fadeColor;
if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
return y;
}
y -= ICON_SIZE;
value = cg.itemPickup;
if ( value ) {
fadeColor = CG_FadeColor( cg.itemPickupTime, 3000 );
if ( fadeColor ) {
CG_RegisterItemVisuals( value );
trap_R_SetColor( fadeColor );
CG_DrawPic( 8, y, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
CG_DrawBigString( ICON_SIZE + 16, y + (ICON_SIZE/2 - BIGCHAR_HEIGHT/2), bg_itemlist[ value ].pickup_name, fadeColor[0] );
trap_R_SetColor( NULL );
}
}
return y;
}
#endif // MISSIONPACK
/*
=====================
CG_DrawLowerLeft
=====================
*/
#ifndef MISSIONPACK
static void CG_DrawLowerLeft( void ) {
float y;
y = 480 - ICON_SIZE;
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 3 ) {
y = CG_DrawTeamOverlay( y, qfalse, qfalse );
}
CG_DrawPickupItem( y );
}
#endif // MISSIONPACK
//===========================================================================================
/*
=================
CG_DrawTeamInfo
=================
*/
#ifndef MISSIONPACK
static void CG_DrawTeamInfo( void ) {
int h;
int i;
vec4_t hcolor;
int chatHeight;
#define CHATLOC_Y 420 // bottom end
#define CHATLOC_X 0
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) * TINYCHAR_HEIGHT;
if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) {
hcolor[0] = 1.0f;
hcolor[1] = 0.0f;
hcolor[2] = 0.0f;
hcolor[3] = 0.33f;
} else if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) {
hcolor[0] = 0.0f;
hcolor[1] = 0.0f;
hcolor[2] = 1.0f;
hcolor[3] = 0.33f;
} else {
hcolor[0] = 0.0f;
hcolor[1] = 1.0f;
hcolor[2] = 0.0f;
hcolor[3] = 0.33f;
}
trap_R_SetColor( hcolor );
CG_DrawPic( CHATLOC_X, CHATLOC_Y - h, 640, h, cgs.media.teamStatusBar );
trap_R_SetColor( NULL );
hcolor[0] = hcolor[1] = hcolor[2] = 1.0f;
hcolor[3] = 1.0f;
for (i = cgs.teamChatPos - 1; i >= cgs.teamLastChatPos; i--) {
CG_DrawStringExt( CHATLOC_X + TINYCHAR_WIDTH,
CHATLOC_Y - (cgs.teamChatPos - i)*TINYCHAR_HEIGHT,
cgs.teamChatMsgs[i % chatHeight], hcolor, qfalse, qfalse,
TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 );
}
}
}
#endif // MISSIONPACK
/*
===================
CG_DrawHoldableItem
===================
*/
#ifndef MISSIONPACK
static void CG_DrawHoldableItem( void ) {
int value;
value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM];
if ( value ) {
CG_RegisterItemVisuals( value );
CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
}
}
#endif // MISSIONPACK
#ifdef MISSIONPACK
/*
===================
CG_DrawPersistantPowerup
===================
*/
#if 0 // sos001208 - DEAD
static void CG_DrawPersistantPowerup( void ) {
int value;
value = cg.snap->ps.stats[STAT_PERSISTANT_POWERUP];
if ( value ) {
CG_RegisterItemVisuals( value );
CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2 - ICON_SIZE, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
}
}
#endif
#endif // MISSIONPACK
/*
===================
CG_DrawReward
===================
*/
static void CG_DrawReward( void ) {
float *color;
int i, count;
float x, y;
char buf[32];
if ( !cg_drawRewards.integer ) {
return;
}
color = CG_FadeColor( cg.rewardTime, REWARD_TIME );
if ( !color ) {
if (cg.rewardStack > 0) {
for(i = 0; i < cg.rewardStack; i++) {
cg.rewardSound[i] = cg.rewardSound[i+1];
cg.rewardShader[i] = cg.rewardShader[i+1];
cg.rewardCount[i] = cg.rewardCount[i+1];
}
cg.rewardTime = cg.time;
cg.rewardStack--;
color = CG_FadeColor( cg.rewardTime, REWARD_TIME );
trap_S_StartLocalSound(cg.rewardSound[0], CHAN_ANNOUNCER);
} else {
return;
}
}
trap_R_SetColor( color );
/*
count = cg.rewardCount[0]/10; // number of big rewards to draw
if (count) {
y = 4;
x = 320 - count * ICON_SIZE;
for ( i = 0 ; i < count ; i++ ) {
CG_DrawPic( x, y, (ICON_SIZE*2)-4, (ICON_SIZE*2)-4, cg.rewardShader[0] );
x += (ICON_SIZE*2);
}
}
count = cg.rewardCount[0] - count*10; // number of small rewards to draw
*/
if ( cg.rewardCount[0] >= 10 ) {
y = 56;
x = 320 - ICON_SIZE/2;
CG_DrawPic( x, y, ICON_SIZE-4, ICON_SIZE-4, cg.rewardShader[0] );
Com_sprintf(buf, sizeof(buf), "%d", cg.rewardCount[0]);
x = ( SCREEN_WIDTH - SMALLCHAR_WIDTH * CG_DrawStrlen( buf ) ) / 2;
CG_DrawStringExt( x, y+ICON_SIZE, buf, color, qfalse, qtrue,
SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 );
}
else {
count = cg.rewardCount[0];
y = 56;
x = 320 - count * ICON_SIZE/2;
for ( i = 0 ; i < count ; i++ ) {
CG_DrawPic( x, y, ICON_SIZE-4, ICON_SIZE-4, cg.rewardShader[0] );
x += ICON_SIZE;
}
}
trap_R_SetColor( NULL );
}
/*
===============================================================================
LAGOMETER
===============================================================================
*/
#define LAG_SAMPLES 128
typedef struct {
int frameSamples[LAG_SAMPLES];
int frameCount;
int snapshotFlags[LAG_SAMPLES];
int snapshotSamples[LAG_SAMPLES];
int snapshotCount;
} lagometer_t;
lagometer_t lagometer;
/*
==============
CG_AddLagometerFrameInfo
Adds the current interpolate / extrapolate bar for this frame
==============
*/
void CG_AddLagometerFrameInfo( void ) {
int offset;
offset = cg.time - cg.latestSnapshotTime;
lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1) ] = offset;
lagometer.frameCount++;
}
/*
==============
CG_AddLagometerSnapshotInfo
Each time a snapshot is received, log its ping time and
the number of snapshots that were dropped before it.
Pass NULL for a dropped packet.
==============
*/
void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) {
// dropped packet
if ( !snap ) {
lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = -1;
lagometer.snapshotCount++;
return;
}
// add this snapshot's info
lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->ping;
lagometer.snapshotFlags[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->snapFlags;
lagometer.snapshotCount++;
}
/*
==============
CG_DrawDisconnect
Should we draw something differnet for long lag vs no packets?
==============
*/
static void CG_DrawDisconnect( void ) {
float x, y;
int cmdNum;
usercmd_t cmd;
const char *s;
int w;
// draw the phone jack if we are completely past our buffers
cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1;
trap_GetUserCmd( cmdNum, &cmd );
if ( cmd.serverTime <= cg.snap->ps.commandTime
|| cmd.serverTime > cg.time ) { // special check for map_restart
return;
}
// also add text in center of screen
s = "Connection Interrupted";
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
CG_DrawBigString( 320 - w/2, 100, s, 1.0F);
// blink the icon
if ( ( cg.time >> 9 ) & 1 ) {
return;
}
#ifdef MISSIONPACK
x = 640 - 48;
y = 480 - 144;
#else
x = 640 - 48;
y = 480 - 48;
#endif
CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga" ) );
}
#define MAX_LAGOMETER_PING 900
#define MAX_LAGOMETER_RANGE 300
/*
==============
CG_DrawLagometer
==============
*/
static void CG_DrawLagometer( void ) {
int a, x, y, i;
float v;
float ax, ay, aw, ah, mid, range;
int color;
float vscale;
if ( !cg_lagometer.integer || cgs.localServer ) {
CG_DrawDisconnect();
return;
}
//
// draw the graph
//
#ifdef MISSIONPACK
x = 640 - 48;
y = 480 - 144;
#else
x = 640 - 48;
y = 480 - 48;
#endif
trap_R_SetColor( NULL );
CG_DrawPic( x, y, 48, 48, cgs.media.lagometerShader );
ax = x;
ay = y;
aw = 48;
ah = 48;
CG_AdjustFrom640( &ax, &ay, &aw, &ah );
color = -1;
range = ah / 3;
mid = ay + range;
vscale = range / MAX_LAGOMETER_RANGE;
// draw the frame interpoalte / extrapolate graph
for ( a = 0 ; a < aw ; a++ ) {
i = ( lagometer.frameCount - 1 - a ) & (LAG_SAMPLES - 1);
v = lagometer.frameSamples[i];
v *= vscale;
if ( v > 0 ) {
if ( color != 1 ) {
color = 1;
trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] );
}
if ( v > range ) {
v = range;
}
trap_R_DrawStretchPic ( ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
} else if ( v < 0 ) {
if ( color != 2 ) {
color = 2;
trap_R_SetColor( g_color_table[ColorIndex(COLOR_BLUE)] );
}
v = -v;
if ( v > range ) {
v = range;
}
trap_R_DrawStretchPic( ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
}
}
// draw the snapshot latency / drop graph
range = ah / 2;
vscale = range / MAX_LAGOMETER_PING;
for ( a = 0 ; a < aw ; a++ ) {
i = ( lagometer.snapshotCount - 1 - a ) & (LAG_SAMPLES - 1);
v = lagometer.snapshotSamples[i];
if ( v > 0 ) {
if ( lagometer.snapshotFlags[i] & SNAPFLAG_RATE_DELAYED ) {
if ( color != 5 ) {
color = 5; // YELLOW for rate delay
trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] );
}
} else {
if ( color != 3 ) {
color = 3;
trap_R_SetColor( g_color_table[ColorIndex(COLOR_GREEN)] );
}
}
v = v * vscale;
if ( v > range ) {
v = range;
}
trap_R_DrawStretchPic( ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
} else if ( v < 0 ) {
if ( color != 4 ) {
color = 4; // RED for dropped snapshots
trap_R_SetColor( g_color_table[ColorIndex(COLOR_RED)] );
}
trap_R_DrawStretchPic( ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader );
}
}
trap_R_SetColor( NULL );
if ( cg_nopredict.integer || cg_synchronousClients.integer ) {
CG_DrawBigString( x, y, "snc", 1.0 );
}
CG_DrawDisconnect();
}
/*
===============================================================================
CENTER PRINTING
===============================================================================
*/
/*
==============
CG_CenterPrint
Called for important messages that should stay in the center of the screen
for a few moments
==============
*/
void CG_CenterPrint( const char *str, int y, int charWidth ) {
char *s;
Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
cg.centerPrintTime = cg.time;
cg.centerPrintY = y;
cg.centerPrintCharWidth = charWidth;
// count the number of lines for centering
cg.centerPrintLines = 1;
s = cg.centerPrint;
while( *s ) {
if (*s == '\n')
cg.centerPrintLines++;
s++;
}
}
/*
===================
CG_DrawCenterString
===================
*/
static void CG_DrawCenterString( void ) {
char *start;
int l;
int x, y, w;
#ifdef MISSIONPACK
int h;
#endif
float *color;
if ( !cg.centerPrintTime ) {
return;
}
color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value );
if ( !color ) {
return;
}
trap_R_SetColor( color );
start = cg.centerPrint;
y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2;
while ( 1 ) {
char linebuffer[1024];
for ( l = 0; l < 50; l++ ) {
if ( !start[l] || start[l] == '\n' ) {
break;
}
linebuffer[l] = start[l];
}
linebuffer[l] = 0;
#ifdef MISSIONPACK
w = CG_Text_Width(linebuffer, 0.5, 0);
h = CG_Text_Height(linebuffer, 0.5, 0);
x = (SCREEN_WIDTH - w) / 2;
CG_Text_Paint(x, y + h, 0.5, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE);
y += h + 6;
#else
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;
#endif
while ( *start && ( *start != '\n' ) ) {
start++;
}
if ( !*start ) {
break;
}
start++;
}
trap_R_SetColor( NULL );
}
/*
================================================================================
CROSSHAIR
================================================================================
*/
/*
=================
CG_DrawCrosshair
=================
*/
static void CG_DrawCrosshair(void)
{
float w, h;
qhandle_t hShader;
float f;
float x, y;
int ca;
if ( !cg_drawCrosshair.integer ) {
return;
}
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) {
return;
}
if ( cg.renderingThirdPerson ) {
return;
}
// set color based on health
if ( cg_crosshairHealth.integer ) {
vec4_t hcolor;
CG_ColorForHealth( hcolor );
trap_R_SetColor( hcolor );
} else {
trap_R_SetColor( NULL );
}
w = h = cg_crosshairSize.value;
// pulse the size of the crosshair when picking up items
f = cg.time - cg.itemPickupBlendTime;
if ( f > 0 && f < ITEM_BLOB_TIME ) {
f /= ITEM_BLOB_TIME;
w *= ( 1 + f );
h *= ( 1 + f );
}
x = cg_crosshairX.integer;
y = cg_crosshairY.integer;
CG_AdjustFrom640( &x, &y, &w, &h );
ca = cg_drawCrosshair.integer;
if (ca < 0) {
ca = 0;
}
hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ];
trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * (cg.refdef.width - w),
y + cg.refdef.y + 0.5 * (cg.refdef.height - h),
w, h, 0, 0, 1, 1, hShader );
trap_R_SetColor( NULL );
}
/*
=================
CG_DrawCrosshair3D
=================
*/
static void CG_DrawCrosshair3D(void)
{
float w;
qhandle_t hShader;
float f;
int ca;
trace_t trace;
vec3_t endpos;
float stereoSep, zProj, maxdist, xmax;
char rendererinfos[128];
refEntity_t ent;
if ( !cg_drawCrosshair.integer ) {
return;
}
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) {
return;
}
if ( cg.renderingThirdPerson ) {
return;
}
w = cg_crosshairSize.value;
// pulse the size of the crosshair when picking up items
f = cg.time - cg.itemPickupBlendTime;
if ( f > 0 && f < ITEM_BLOB_TIME ) {
f /= ITEM_BLOB_TIME;
w *= ( 1 + f );
}
ca = cg_drawCrosshair.integer;
if (ca < 0) {
ca = 0;
}
hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ];
// Use a different method rendering the crosshair so players don't see two of them when
// focusing their eyes at distant objects with high stereo separation
// We are going to trace to the next shootable object and place the crosshair in front of it.
// first get all the important renderer information
trap_Cvar_VariableStringBuffer("r_zProj", rendererinfos, sizeof(rendererinfos));
zProj = atof(rendererinfos);
trap_Cvar_VariableStringBuffer("r_stereoSeparation", rendererinfos, sizeof(rendererinfos));
stereoSep = zProj / atof(rendererinfos);
xmax = zProj * tan(cg.refdef.fov_x * M_PI / 360.0f);
// let the trace run through until a change in stereo separation of the crosshair becomes less than one pixel.
maxdist = cgs.glconfig.vidWidth * stereoSep * zProj / (2 * xmax);
VectorMA(cg.refdef.vieworg, maxdist, cg.refdef.viewaxis[0], endpos);
CG_Trace(&trace, cg.refdef.vieworg, NULL, NULL, endpos, 0, MASK_SHOT);
memset(&ent, 0, sizeof(ent));
ent.reType = RT_SPRITE;
ent.renderfx = RF_DEPTHHACK | RF_CROSSHAIR;
VectorCopy(trace.endpos, ent.origin);
// scale the crosshair so it appears the same size for all distances
ent.radius = w / 640 * xmax * trace.fraction * maxdist / zProj;
ent.customShader = hShader;
trap_R_AddRefEntityToScene(&ent);
}
/*
=================
CG_ScanForCrosshairEntity
=================
*/
static void CG_ScanForCrosshairEntity( void ) {
trace_t trace;
vec3_t start, end;
int content;
VectorCopy( cg.refdef.vieworg, start );
VectorMA( start, 131072, cg.refdef.viewaxis[0], end );
CG_Trace( &trace, start, vec3_origin, vec3_origin, end,
cg.snap->ps.clientNum, CONTENTS_SOLID|CONTENTS_BODY );
if ( trace.entityNum >= MAX_CLIENTS ) {
return;
}
// if the player is in fog, don't show it
content = CG_PointContents( trace.endpos, 0 );
if ( content & CONTENTS_FOG ) {
return;
}
// if the player is invisible, don't show it
if ( cg_entities[ trace.entityNum ].currentState.powerups & ( 1 << PW_INVIS ) ) {
return;
}
// update the fade timer
cg.crosshairClientNum = trace.entityNum;
cg.crosshairClientTime = cg.time;
}
/*
=====================
CG_DrawCrosshairNames
=====================
*/
static void CG_DrawCrosshairNames( void ) {
float *color;
char *name;
float w;
if ( !cg_drawCrosshair.integer ) {
return;
}
if ( !cg_drawCrosshairNames.integer ) {
return;
}
if ( cg.renderingThirdPerson ) {
return;
}
// scan the known entities to see if the crosshair is sighted on one
CG_ScanForCrosshairEntity();
// draw the name of the player being looked at
color = CG_FadeColor( cg.crosshairClientTime, 1000 );
if ( !color ) {
trap_R_SetColor( NULL );
return;
}
name = cgs.clientinfo[ cg.crosshairClientNum ].name;
#ifdef MISSIONPACK
color[3] *= 0.5f;
w = CG_Text_Width(name, 0.3f, 0);
CG_Text_Paint( 320 - w / 2, 190, 0.3f, color, name, 0, 0, ITEM_TEXTSTYLE_SHADOWED);
#else
w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH;
CG_DrawBigString( 320 - w / 2, 170, name, color[3] * 0.5f );
#endif
trap_R_SetColor( NULL );
}
//==============================================================================
/*
=================
CG_DrawSpectator
=================
*/
static void CG_DrawSpectator(void) {
CG_DrawBigString(320 - 9 * 8, 440, "SPECTATOR", 1.0F);
if ( cgs.gametype == GT_TOURNAMENT ) {
CG_DrawBigString(320 - 15 * 8, 460, "waiting to play", 1.0F);
}
else if ( cgs.gametype >= GT_TEAM ) {
CG_DrawBigString(320 - 39 * 8, 460, "press ESC and use the JOIN menu to play", 1.0F);
}
}
/*
=================
CG_DrawVote
=================
*/
static void CG_DrawVote(void) {
char *s;
int sec;
if ( !cgs.voteTime ) {
return;
}
// play a talk beep whenever it is modified
if ( cgs.voteModified ) {
cgs.voteModified = qfalse;
trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
}
sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000;
if ( sec < 0 ) {
sec = 0;
}
#ifdef MISSIONPACK
s = va("VOTE(%i):%s yes:%i no:%i", sec, cgs.voteString, cgs.voteYes, cgs.voteNo);
CG_DrawSmallString( 0, 58, s, 1.0F );
s = "or press ESC then click Vote";
CG_DrawSmallString( 0, 58 + SMALLCHAR_HEIGHT + 2, s, 1.0F );
#else
s = va("VOTE(%i):%s yes:%i no:%i", sec, cgs.voteString, cgs.voteYes, cgs.voteNo );
CG_DrawSmallString( 0, 58, s, 1.0F );
#endif
}
/*
=================
CG_DrawTeamVote
=================
*/
static void CG_DrawTeamVote(void) {
char *s;
int sec, cs_offset;
if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED )
cs_offset = 0;
else if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE )
cs_offset = 1;
else
return;
if ( !cgs.teamVoteTime[cs_offset] ) {
return;
}
// play a talk beep whenever it is modified
if ( cgs.teamVoteModified[cs_offset] ) {
cgs.teamVoteModified[cs_offset] = qfalse;
trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
}
sec = ( VOTE_TIME - ( cg.time - cgs.teamVoteTime[cs_offset] ) ) / 1000;
if ( sec < 0 ) {
sec = 0;
}
s = va("TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[cs_offset],
cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset] );
CG_DrawSmallString( 0, 90, s, 1.0F );
}
static qboolean CG_DrawScoreboard( void ) {
#ifdef MISSIONPACK
static qboolean firstTime = qtrue;
if (menuScoreboard) {
menuScoreboard->window.flags &= ~WINDOW_FORCED;
}
if (cg_paused.integer) {
cg.deferredPlayerLoading = 0;
firstTime = qtrue;
return qfalse;
}
// should never happen in Team Arena
if (cgs.gametype == GT_SINGLE_PLAYER && cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
cg.deferredPlayerLoading = 0;
firstTime = qtrue;
return qfalse;
}
// don't draw scoreboard during death while warmup up
if ( cg.warmup && !cg.showScores ) {
return qfalse;
}
if ( cg.showScores || cg.predictedPlayerState.pm_type == PM_DEAD || cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
} else {
if ( !CG_FadeColor( cg.scoreFadeTime, FADE_TIME ) ) {
// next time scoreboard comes up, don't print killer
cg.deferredPlayerLoading = 0;
cg.killerName[0] = 0;
firstTime = qtrue;
return qfalse;
}
}
if (menuScoreboard == NULL) {
if ( cgs.gametype >= GT_TEAM ) {
menuScoreboard = Menus_FindByName("teamscore_menu");
} else {
menuScoreboard = Menus_FindByName("score_menu");
}
}
if (menuScoreboard) {
if (firstTime) {
CG_SetScoreSelection(menuScoreboard);
firstTime = qfalse;
}
Menu_Paint(menuScoreboard, qtrue);
}
// load any models that have been deferred
if ( ++cg.deferredPlayerLoading > 10 ) {
CG_LoadDeferredPlayers();
}
return qtrue;
#else
return CG_DrawOldScoreboard();
#endif
}
/*
=================
CG_DrawIntermission
=================
*/
static void CG_DrawIntermission( void ) {
// int key;
#ifdef MISSIONPACK
//if (cg_singlePlayer.integer) {
// CG_DrawCenterString();
// return;
//}
#else
if ( cgs.gametype == GT_SINGLE_PLAYER ) {
CG_DrawCenterString();
return;
}
#endif
cg.scoreFadeTime = cg.time;
cg.scoreBoardShowing = CG_DrawScoreboard();
}
/*
=================
CG_DrawFollow
=================
*/
static qboolean CG_DrawFollow( void ) {
float x;
vec4_t color;
const char *name;
if ( !(cg.snap->ps.pm_flags & PMF_FOLLOW) ) {
return qfalse;
}
color[0] = 1;
color[1] = 1;
color[2] = 1;
color[3] = 1;
CG_DrawBigString( 320 - 9 * 8, 24, "following", 1.0F );
name = cgs.clientinfo[ cg.snap->ps.clientNum ].name;
x = 0.5 * ( 640 - GIANT_WIDTH * CG_DrawStrlen( name ) );
CG_DrawStringExt( x, 40, name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
return qtrue;
}
/*
=================
CG_DrawAmmoWarning
=================
*/
static void CG_DrawAmmoWarning( void ) {
const char *s;
int w;
if ( cg_drawAmmoWarning.integer == 0 ) {
return;
}
if ( !cg.lowAmmoWarning ) {
return;
}
if ( cg.lowAmmoWarning == 2 ) {
s = "OUT OF AMMO";
} else {
s = "LOW AMMO WARNING";
}
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
CG_DrawBigString(320 - w / 2, 64, s, 1.0F);
}
#ifdef MISSIONPACK
/*
=================
CG_DrawProxWarning
=================
*/
static void CG_DrawProxWarning( void ) {
char s [32];
int w;
static int proxTime;
int proxTick;
if( !(cg.snap->ps.eFlags & EF_TICKING ) ) {
proxTime = 0;
return;
}
if (proxTime == 0) {
proxTime = cg.time;
}
proxTick = 10 - ((cg.time - proxTime) / 1000);
if (proxTick > 0 && proxTick <= 5) {
Com_sprintf(s, sizeof(s), "INTERNAL COMBUSTION IN: %i", proxTick);
} else {
Com_sprintf(s, sizeof(s), "YOU HAVE BEEN MINED");
}
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
CG_DrawBigStringColor( 320 - w / 2, 64 + BIGCHAR_HEIGHT, s, g_color_table[ColorIndex(COLOR_RED)] );
}
#endif
/*
=================
CG_DrawWarmup
=================
*/
static void CG_DrawWarmup( void ) {
int w;
int sec;
int i;
#ifdef MISSIONPACK
float scale;
#else
int cw;
#endif
clientInfo_t *ci1, *ci2;
const char *s;
sec = cg.warmup;
if ( !sec ) {
return;
}
if ( sec < 0 ) {
s = "Waiting for players";
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
CG_DrawBigString(320 - w / 2, 24, s, 1.0F);
cg.warmupCount = 0;
return;
}
if (cgs.gametype == GT_TOURNAMENT) {
// find the two active players
ci1 = NULL;
ci2 = NULL;
for ( i = 0 ; i < cgs.maxclients ; i++ ) {
if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_FREE ) {
if ( !ci1 ) {
ci1 = &cgs.clientinfo[i];
} else {
ci2 = &cgs.clientinfo[i];
}
}
}
if ( ci1 && ci2 ) {
s = va( "%s vs %s", ci1->name, ci2->name );
#ifdef MISSIONPACK
w = CG_Text_Width(s, 0.6f, 0);
CG_Text_Paint(320 - w / 2, 60, 0.6f, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE);
#else
w = CG_DrawStrlen( s );
if ( w > 640 / GIANT_WIDTH ) {
cw = 640 / w;
} else {
cw = GIANT_WIDTH;
}
CG_DrawStringExt( 320 - w * cw/2, 20,s, colorWhite,
qfalse, qtrue, cw, (int)(cw * 1.5f), 0 );
#endif
}
} else {
if ( cgs.gametype == GT_FFA ) {
s = "Free For All";
} else if ( cgs.gametype == GT_TEAM ) {
s = "Team Deathmatch";
} else if ( cgs.gametype == GT_CTF ) {
s = "Capture the Flag";
#ifdef MISSIONPACK
} else if ( cgs.gametype == GT_1FCTF ) {
s = "One Flag CTF";
} else if ( cgs.gametype == GT_OBELISK ) {
s = "Overload";
} else if ( cgs.gametype == GT_HARVESTER ) {
s = "Harvester";
#endif
} else {
s = "";
}
#ifdef MISSIONPACK
w = CG_Text_Width(s, 0.6f, 0);
CG_Text_Paint(320 - w / 2, 90, 0.6f, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE);
#else
w = CG_DrawStrlen( s );
if ( w > 640 / GIANT_WIDTH ) {
cw = 640 / w;
} else {
cw = GIANT_WIDTH;
}
CG_DrawStringExt( 320 - w * cw/2, 25,s, colorWhite,
qfalse, qtrue, cw, (int)(cw * 1.1f), 0 );
#endif
}
sec = ( sec - cg.time ) / 1000;
if ( sec < 0 ) {
cg.warmup = 0;
sec = 0;
}
s = va( "Starts in: %i", sec + 1 );
if ( sec != cg.warmupCount ) {
cg.warmupCount = sec;
switch ( sec ) {
case 0:
trap_S_StartLocalSound( cgs.media.count1Sound, CHAN_ANNOUNCER );
break;
case 1:
trap_S_StartLocalSound( cgs.media.count2Sound, CHAN_ANNOUNCER );
break;
case 2:
trap_S_StartLocalSound( cgs.media.count3Sound, CHAN_ANNOUNCER );
break;
default:
break;
}
}
#ifdef MISSIONPACK
switch ( cg.warmupCount ) {
case 0:
scale = 0.54f;
break;
case 1:
scale = 0.51f;
break;
case 2:
scale = 0.48f;
break;
default:
scale = 0.45f;
break;
}
w = CG_Text_Width(s, scale, 0);
CG_Text_Paint(320 - w / 2, 125, scale, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE);
#else
switch ( cg.warmupCount ) {
case 0:
cw = 28;
break;
case 1:
cw = 24;
break;
case 2:
cw = 20;
break;
default:
cw = 16;
break;
}
w = CG_DrawStrlen( s );
CG_DrawStringExt( 320 - w * cw/2, 70, s, colorWhite,
qfalse, qtrue, cw, (int)(cw * 1.5), 0 );
#endif
}
//==================================================================================
#ifdef MISSIONPACK
/*
=================
CG_DrawTimedMenus
=================
*/
void CG_DrawTimedMenus( void ) {
if (cg.voiceTime) {
int t = cg.time - cg.voiceTime;
if ( t > 2500 ) {
Menus_CloseByName("voiceMenu");
trap_Cvar_Set("cl_conXOffset", "0");
cg.voiceTime = 0;
}
}
}
#endif
/*
=================
CG_Draw2D
=================
*/
static void CG_Draw2D(stereoFrame_t stereoFrame)
{
#ifdef MISSIONPACK
if (cgs.orderPending && cg.time > cgs.orderTime) {
CG_CheckOrderPending();
}
#endif
// if we are taking a levelshot for the menu, don't draw anything
if ( cg.levelShot ) {
return;
}
if ( cg_draw2D.integer == 0 ) {
return;
}
if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
CG_DrawIntermission();
return;
}
/*
if (cg.cameraMode) {
return;
}
*/
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
CG_DrawSpectator();
if(stereoFrame == STEREO_CENTER)
CG_DrawCrosshair();
CG_DrawCrosshairNames();
} else {
// don't draw any status if dead or the scoreboard is being explicitly shown
if ( !cg.showScores && cg.snap->ps.stats[STAT_HEALTH] > 0 ) {
#ifdef MISSIONPACK
if ( cg_drawStatus.integer ) {
Menu_PaintAll();
CG_DrawTimedMenus();
}
#else
CG_DrawStatusBar();
#endif
CG_DrawAmmoWarning();
#ifdef MISSIONPACK
CG_DrawProxWarning();
#endif
if(stereoFrame == STEREO_CENTER)
CG_DrawCrosshair();
CG_DrawCrosshairNames();
CG_DrawWeaponSelect();
#ifndef MISSIONPACK
CG_DrawHoldableItem();
#else
//CG_DrawPersistantPowerup();
#endif
CG_DrawReward();
}
}
if ( cgs.gametype >= GT_TEAM ) {
#ifndef MISSIONPACK
CG_DrawTeamInfo();
#endif
}
CG_DrawVote();
CG_DrawTeamVote();
CG_DrawLagometer();
#ifdef MISSIONPACK
if (!cg_paused.integer) {
CG_DrawUpperRight(stereoFrame);
}
#else
CG_DrawUpperRight(stereoFrame);
#endif
#ifndef MISSIONPACK
CG_DrawLowerRight();
CG_DrawLowerLeft();
#endif
if ( !CG_DrawFollow() ) {
CG_DrawWarmup();
}
// don't draw center string if scoreboard is up
cg.scoreBoardShowing = CG_DrawScoreboard();
if ( !cg.scoreBoardShowing) {
CG_DrawCenterString();
}
}
/*
=====================
CG_DrawActive
Perform all drawing needed to completely fill the screen
=====================
*/
void CG_DrawActive( stereoFrame_t stereoView ) {
// optionally draw the info screen instead
if ( !cg.snap ) {
CG_DrawInformation();
return;
}
// optionally draw the tournement scoreboard instead
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR &&
( cg.snap->ps.pm_flags & PMF_SCOREBOARD ) ) {
CG_DrawTourneyScoreboard();
return;
}
// clear around the rendered view if sized down
CG_TileClear();
if(stereoView != STEREO_CENTER)
CG_DrawCrosshair3D();
// draw 3D view
trap_R_RenderScene( &cg.refdef );
// draw status bar and other floating elements
CG_Draw2D(stereoView);
}