q3rally/engine/code/cgame/cg_draw.c

3237 lines
73 KiB
C
Raw Normal View History

2011-02-18 14:31:32 +00:00
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of q3rally source code.
q3rally 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.
q3rally 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 q3rally; 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];
2011-02-18 14:31:32 +00:00
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];
2011-02-18 14:31:32 +00:00
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];
2011-02-18 14:31:32 +00:00
//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
/*
2022-03-01 19:55:24 +00:00
==============================================================
CG_DrawField - Draws large numbers for status bar and powerups
==============================================================
2011-02-18 14:31:32 +00:00
*/
#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);
2011-02-18 14:31:32 +00:00
// center x, move y to bottom.
x += (1.0f-cg_statusScale.value)*l*CHAR_WIDTH*0.5f;
y += (1.0f-cg_statusScale.value)*CHAR_HEIGHT;
2011-02-18 14:31:32 +00:00
ptr = num;
while (*ptr && l)
{
if (*ptr == '-')
frame = STAT_MINUS;
else
frame = *ptr -'0';
CG_DrawPic( x,y, CHAR_WIDTH*cg_statusScale.value, CHAR_HEIGHT*cg_statusScale.value, cgs.media.numberShaders[frame] );
x += CHAR_WIDTH*cg_statusScale.value;
2011-02-18 14:31:32 +00:00
ptr++;
l--;
}
}
#endif // MISSIONPACK
/*
==============
2011-02-18 14:31:32 +00:00
CG_Draw3DModel
==============
2011-02-18 14:31:32 +00:00
*/
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_DrawHealthModel
==================
*/
void CG_DrawHealthModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, qhandle_t model2, vec3_t origin, vec3_t angles, float yaw2 ) {
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 );
if ( model2 ) {
ent.hModel = model2;
angles[YAW] = yaw2;
AnglesToAxis( angles, ent.axis );
trap_R_AddRefEntityToScene( &ent );
}
trap_R_RenderScene( &refdef );
}
2011-02-18 14:31:32 +00:00
/*
2022-03-01 19:55:24 +00:00
==================================================================
CG_DrawFlagModel - Used for both the status bar and the scoreboard
==================================================================
2011-02-18 14:31:32 +00:00
*/
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 );
}
}
}
/*
====================
2011-02-18 14:31:32 +00:00
CG_DrawStatusBarHead
====================
2011-02-18 14:31:32 +00:00
*/
#ifndef MISSIONPACK
#if 0 // ZTM: Not used by Q3Rally
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;
}
#endif
#endif // MISSIONPACK
/*
====================
2011-02-18 14:31:32 +00:00
CG_DrawStatusBarFlag
====================
2011-02-18 14:31:32 +00:00
*/
#ifndef MISSIONPACK
static void CG_DrawStatusBarFlag( float x, int team ) {
int iconSize = ICON_SIZE*cg_statusScale.value;
CG_DrawFlagModel( x+(1.0f-cg_statusScale.value)*ICON_SIZE*0.5f, 480 - iconSize, iconSize, iconSize, team, qfalse );
2011-02-18 14:31:32 +00:00
}
#endif // MISSIONPACK
/*
=====================
2011-02-18 14:31:32 +00:00
CG_DrawTeamBackground
=====================
2011-02-18 14:31:32 +00:00
*/
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;
}
// Q3Rally Code Start
else if ( team == TEAM_GREEN ) {
hcolor[0] = 0;
hcolor[1] = 1;
hcolor[2] = 0;
}
else if ( team == TEAM_YELLOW ) {
hcolor[0] = 1;
hcolor[1] = 1;
hcolor[2] = 0;
}
// END
else {
return;
}
trap_R_SetColor( hcolor );
CG_SetScreenPlacement(PLACE_STRETCH, CG_GetScreenVerticalPlacement());
2011-02-18 14:31:32 +00:00
CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
CG_PopScreenPlacement();
2011-02-18 14:31:32 +00:00
trap_R_SetColor( NULL );
}
/*
2022-03-01 19:55:24 +00:00
===============
CG_DrawSigilHUD
2022-03-01 19:55:24 +00:00
===============
*/
void CG_DrawSigilHUD( void ) {
int i, x=440, y=0;
for (i=0; i<MAX_SIGILS; i++) {
switch ( cgs.sigil[i] )
{
case SIGIL_ISRED:
CG_DrawPic( x, y, 18, 18, cgs.media.redsigilShader );
break;
case SIGIL_ISBLUE:
CG_DrawPic( x, y, 18, 18, cgs.media.bluesigilShader );
break;
case SIGIL_ISGREEN:
CG_DrawPic( x, y, 18, 18, cgs.media.greensigilShader );
break;
case SIGIL_ISYELLOW:
CG_DrawPic( x, y, 18, 18, cgs.media.yellowsigilShader );
break;
case SIGIL_ISWHITE:
CG_DrawPic( x, y, 18, 18, cgs.media.sigilShader );
break;
case SIGIL_NONE:
break;
}
x+= 19;
}
}
2011-02-18 14:31:32 +00:00
/*
================
CG_DrawStatusBar
================
*/
#ifndef MISSIONPACK
#if 0
static void CG_DrawStatusBar( void ) {
int color;
centity_t *cent;
playerState_t *ps;
int value;
vec4_t hcolor;
vec3_t angles;
vec3_t origin;
float scale, iconSize;
2011-02-18 14:31:32 +00:00
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
2011-02-18 14:31:32 +00:00
if ( cg_drawStatus.integer == 0 ) {
return;
}
CG_SetScreenPlacement(PLACE_CENTER, PLACE_BOTTOM);
scale = Com_Clamp( 0.1f, 2, cg_statusScale.value);
iconSize = scale * ICON_SIZE;
2011-02-18 14:31:32 +00:00
// draw the team background
CG_DrawTeamBackground( 0, 480 - 60*scale, 640, 60*scale, 0.33f, cg.snap->ps.persistant[PERS_TEAM] );
2011-02-18 14:31:32 +00:00
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, 480-iconSize, iconSize, iconSize,
2011-02-18 14:31:32 +00:00
cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles );
}
if ( cg_drawStatusHead.integer == 2 ) {
origin[0] = 60;
origin[1] = 0;
origin[2] = -5;
angles[YAW] = ( cg.time & 2047 ) * 360 / 4096.0;
CG_DrawHealthModel( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 480-iconSize, iconSize, iconSize,
cg_items[ 6 /*item_health_large*/].models[0], 0, cg_items[ 6 /*item_health_large*/].models[1], origin, angles, 0 );
// if we didn't draw a 3D icon, draw a 2D icon for health
if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) {
CG_DrawPic( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 480 - iconSize, iconSize, iconSize, cg_items[6/*item_health_large*/].icon );
}
}
else if ( cg_drawStatusHead.integer == 1 )
CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE );
2011-02-18 14:31:32 +00:00
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, 480 - iconSize, iconSize, iconSize,
2011-02-18 14:31:32 +00:00
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
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
&& (cg.predictedPlayerState.weaponTime & NORMAL_WEAPON_TIME_MASK) > 100 ) {
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
// 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);
2011-02-18 14:31:32 +00:00
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, 480 - iconSize, iconSize, iconSize, icon );
2011-02-18 14:31:32 +00:00
}
}
}
}
//
// 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);
2011-02-18 14:31:32 +00:00
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, 480 - iconSize, iconSize, iconSize, cgs.media.armorIcon );
2011-02-18 14:31:32 +00:00
}
}
}
#endif
/*
=====================
2011-02-18 14:31:32 +00:00
CG_DrawRallyStatusBar
=====================
2011-02-18 14:31:32 +00:00
*/
static void CG_DrawRallyStatusBar( void ) {
int color;
centity_t *cent;
playerState_t *ps;
int value;
vec4_t hcolor;
vec3_t angles;
vec3_t origin;
qhandle_t healthModel;
vec4_t bg_color;
int weapon, i;
if ( !cg_drawStatus.integer ) {
return;
}
// draw the dtf sigils
if ( cgs.gametype == GT_DOMINATION )
CG_DrawSigilHUD();
2011-02-18 14:31:32 +00:00
cent = &cg_entities[cg.snap->ps.clientNum];
ps = &cg.snap->ps;
VectorClear( angles );
switch (cgs.clientinfo[cg.snap->ps.clientNum].team){
case TEAM_RED:
Vector4Copy(colorRed, bg_color);
bg_color[3] = 0.5;
break;
case TEAM_BLUE:
Vector4Copy(colorBlue, bg_color);
bg_color[3] = 0.5;
break;
case TEAM_GREEN:
Vector4Copy(colorGreen, bg_color);
bg_color[3] = 0.5;
break;
case TEAM_YELLOW:
Vector4Copy(colorYellow, bg_color);
bg_color[3] = 0.5;
break;
default:
Vector4Copy(bgColor, bg_color);
}
// draw ammo background
value = ps->ammo[cent->currentState.weapon];
if ( value > -1 )
CG_FillRect( 20, 476 - 30, 90, 24, bg_color );
2022-03-07 22:15:00 +00:00
// health background
CG_FillRect( 210, 476 - 30, 90, 24, bg_color );
2011-02-18 14:31:32 +00:00
// armor background
if ( ps->stats[ STAT_ARMOR ] )
CG_FillRect( 305, 476 - 30, 90, 24, bg_color );
2011-02-18 14:31:32 +00:00
// rearammo background
weapon = 0;
for (i = RWP_SMOKE; i < WP_NUM_WEAPONS; i++){
if (ps->stats[STAT_WEAPONS] & ( 1 << i )){
if (ps->ammo[ i ]){
weapon = i;
break;
}
}
}
if ( weapon )
CG_FillRect( 115, 476 - 30, 90, 24, bg_color );
2011-02-18 14:31:32 +00:00
// draw any 3D icons now, 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] = 270 * sin( cg.time / 1000.0 );
CG_Draw3DModel( 26, 476 - 27, 19, 19,
2011-02-18 14:31:32 +00:00
cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles );
}
// Replace head with '+' sign.
VectorClear( angles );
if (ps->stats[STAT_HEALTH] < 25)
healthModel = trap_R_RegisterModel( "models/powerups/health/large_cross.md3" );
else if (ps->stats[STAT_HEALTH] < 50)
healthModel = trap_R_RegisterModel( "models/powerups/health/medium_cross.md3" );
else
healthModel = trap_R_RegisterModel( "models/powerups/health/small_cross.md3" );
if ( healthModel ) {
origin[0] = 68;
2011-02-18 14:31:32 +00:00
origin[1] = 0;
origin[2] = 0;
angles[YAW] = 270 * sin( cg.time / 1000.0 );
CG_Draw3DModel( 212, 476 - 26, 19, 19,
2011-02-18 14:31:32 +00:00
healthModel, 0, origin, angles );
}
if (cg.predictedPlayerState.powerups[PW_REDFLAG])
CG_DrawStatusBarFlag( 495, TEAM_RED);
2011-02-18 14:31:32 +00:00
else if (cg.predictedPlayerState.powerups[PW_BLUEFLAG])
CG_DrawStatusBarFlag( 495, TEAM_BLUE);
2011-02-18 14:31:32 +00:00
else if (cg_entities[cg.snap->ps.clientNum].finishRaceTime &&
cg_entities[cg.snap->ps.clientNum].currentPosition == 1){
CG_DrawStatusBarFlag( 495, 15);
2011-02-18 14:31:32 +00:00
}
if ( ps->stats[ STAT_ARMOR ] ) {
origin[0] = 45;
origin[1] = 0;
origin[2] = -10;
angles[YAW] = 270 * sin( cg.time / 1000.0 );
// CG_Draw3DModel( 196, 476 - 64, 26, 26,
CG_Draw3DModel( 311, 476 - 27, 19, 19,
2011-02-18 14:31:32 +00:00
cgs.media.armorModel, 0, origin, angles );
}
//
// Draw numbers and 2D icons now
//
//
// ammo
//
if ( cent->currentState.weapon ) {
value = ps->ammo[cent->currentState.weapon];
if ( value > -1 ) {
if ( cg.predictedPlayerState.weaponstate == WEAPON_FIRING
&& (cg.predictedPlayerState.weaponTime & NORMAL_WEAPON_TIME_MASK) > 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 (42, 476 - 28, 3, value);
2011-02-18 14:31:32 +00:00
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( 10, 476 - 28, 26, 26, icon );
2011-02-18 14:31:32 +00:00
}
}
}
}
//
// rear ammo
//
if ( weapon && cg_weapons[ weapon ].weaponModel ) {
origin[0] = 70;
origin[1] = 0;
origin[2] = 0;
angles[YAW] = 270 * sin( cg.time / 1000.0 );
CG_Draw3DModel( 120, 476 - 28, 19, 19,
2011-02-18 14:31:32 +00:00
cg_weapons[ weapon ].weaponModel, 0, origin, angles );
}
if ( weapon ) {
value = ps->ammo[weapon];
if ( value > -1 ) {
if ( cg.predictedPlayerState.weaponstate == WEAPON_REARFIRING
&& ((cg.predictedPlayerState.weaponTime & REAR_WEAPON_TIME_MASK) >> 16) > 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 (158, 476 - 28, 2, value);
2011-02-18 14:31:32 +00:00
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[ weapon ].weaponIcon;
if ( icon ) {
CG_DrawPic( 6, 480 - 67, 26, 26, icon );
2011-02-18 14:31:32 +00:00
}
}
}
}
//
// 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 ( 232, 476 - 28, 3, value);
2011-02-18 14:31:32 +00:00
CG_ColorForHealth( hcolor );
trap_R_SetColor( hcolor );
//
// armor
//
value = ps->stats[STAT_ARMOR];
if (value > 0 ) {
trap_R_SetColor( colors[0] );
// CG_DrawField ( 242, 476 - 64, 3, value);
CG_DrawField ( 327, 476 - 28, 3, value);
2011-02-18 14:31:32 +00:00
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( 196, 476 - 64, 26, 26, cgs.media.armorIcon );
2011-02-18 14:31:32 +00:00
}
}
}
#endif
/*
================
CG_DrawPowerups
================
*/
#ifndef MISSIONPACK
#if 0 // ZTM: Not used by Q3Rally
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
// Q3Rally Code Start
/*
====================
CG_DrawRallyPowerups
====================
*/
static float CG_DrawRallyPowerups( 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 color;
float size;
float f;
vec4_t bg_color;
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;
}
switch (cgs.clientinfo[cg.snap->ps.clientNum].team){
case TEAM_RED:
Vector4Copy(colorRed, bg_color);
bg_color[3] = 0.5f;
break;
case TEAM_BLUE:
Vector4Copy(colorBlue, bg_color);
bg_color[3] = 0.5f;
break;
case TEAM_GREEN:
Vector4Copy(colorGreen, bg_color);
bg_color[3] = 0.5f;
break;
case TEAM_YELLOW:
Vector4Copy(colorYellow, bg_color);
bg_color[3] = 0.5f;
break;
default:
Vector4Copy(bgColor, bg_color);
}
// sort the list by time remaining
active = 0;
for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
if ( !ps->powerups[ i ] ) {
continue;
}
t = ps->powerups[ i ] - cg.time;
if (i == PW_TURBO && ps->powerups[ i ] < 0)
t = -ps->powerups[ i ];
// ZOID--don't draw if the power up has unlimited time (999 seconds)
// This is true of the CTF flags
if ( t < 0 || t > 999000 ) {
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
for ( i = 0 ; i < active ; i++ ) {
item = BG_FindItemForPowerup( sorted[i] );
color = 1;
2011-02-18 14:31:32 +00:00
CG_FillRect( 402, 476 - 28, 90, 24, bg_color );
trap_R_SetColor( colors[color] );
CG_DrawField( 424 + CHAR_WIDTH, 476 - 26, 2, sortedTime[ i ] / 1000 );
t = ps->powerups[ sorted[i] ];
if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
trap_R_SetColor( NULL );
}
// dont blink turbo when not on
else if (item->giTag == PW_TURBO && ps->powerups[ item->giTag ] < 0){
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 = 19 * ( 1.0 + ( PULSE_SCALE - 1.0 ) * f );
} else {
size = 19;
}
CG_DrawPic( 408, 476 - 25, size, size, trap_R_RegisterShader( item->icon ) );
// y -= 36;
}
trap_R_SetColor( NULL );
return y;
}
#endif // MISSIONPACK
// Q3Rally Code END
2011-02-18 14:31:32 +00:00
/*
===========================================================================================
2022-03-01 19:55:24 +00:00
UPPER RIGHT CORNER
2011-02-18 14:31:32 +00:00
===========================================================================================
*/
/*
================
CG_DrawAttacker
================
*/
static float CG_DrawAttacker( float y ) {
int t;
float size;
2012-09-15 03:56:52 +00:00
// Q3Rally Code
//vec3_t angles;
// Q3Rally Code END
2011-02-18 14:31:32 +00:00
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;
}
2011-02-18 14:31:32 +00:00
t = cg.time - cg.attackerTime;
if ( t > ATTACKER_HEAD_TIME ) {
cg.attackerTime = 0;
return y;
}
size = ICON_SIZE * 1.25;
// Q3Rally Code ( removed function )
2012-09-15 03:56:52 +00:00
// angles[PITCH] = 0;
// angles[YAW] = 180;
// angles[ROLL] = 0;
2011-02-18 14:31:32 +00:00
// CG_DrawHead( 640 - size, y, size, size, clientNum, angles );
// Q3Rally Code END
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;
}
/*
===============
2011-02-18 14:31:32 +00:00
CG_DrawSnapshot
===============
2011-02-18 14:31:32 +00:00
*/
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;
}
/*
==========
2011-02-18 14:31:32 +00:00
CG_DrawFPS
==========
2011-02-18 14:31:32 +00:00
*/
#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;
}
/*
2022-03-01 19:55:24 +00:00
============
2011-02-18 14:31:32 +00:00
CG_DrawTimer
2022-03-01 19:55:24 +00:00
============
2011-02-18 14:31:32 +00:00
*/
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;
}
/*
2022-03-01 19:55:24 +00:00
==================
2011-02-18 14:31:32 +00:00
CG_DrawTeamOverlay
2022-03-01 19:55:24 +00:00
==================
2011-02-18 14:31:32 +00:00
*/
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;
2011-02-18 14:31:32 +00:00
// 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
}
/*
2022-03-01 19:55:24 +00:00
=================
2011-02-18 14:31:32 +00:00
CG_DrawUpperRight
2022-03-01 19:55:24 +00:00
=================
2011-02-18 14:31:32 +00:00
*/
static void CG_DrawUpperRight(stereoFrame_t stereoFrame)
{
float y;
y = 0;
CG_SetScreenPlacement(PLACE_RIGHT, PLACE_TOP);
2011-02-18 14:31:32 +00:00
y = CG_DrawUpperRightHUD( y );
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 );
2011-02-18 14:31:32 +00:00
}
}
/*
===========================================================================================
LOWER RIGHT CORNER
===========================================================================================
*/
/*
2022-03-01 19:55:24 +00:00
================================================
CG_DrawScores - Draw the small two score display
================================================
2011-02-18 14:31:32 +00:00
*/
#ifndef MISSIONPACK
2011-02-18 14:31:32 +00:00
float CG_DrawScores( float x, float y ) {
2011-02-18 14:31:32 +00:00
const char *s;
int s1, s2, score;
int s3, s4, w;
int v;
vec4_t color;
float y1;
gitem_t *item;
if ( !cg_drawScores.integer ) {
return y;
}
2011-02-18 14:31:32 +00:00
s1 = cgs.scores1;
s2 = cgs.scores2;
s3 = cgs.scores3;
s4 = cgs.scores4;
y1 = y + 18;
2011-02-18 14:31:32 +00:00
// draw from the right side to left
if ( cgs.gametype >= GT_TEAM ) {
2021-11-30 22:44:46 +00:00
CG_FillRect( x - 80, y, 96, 18, bgColor );
2011-02-18 14:31:32 +00:00
if (cgs.gametype >= GT_TEAM){
2011-02-18 14:31:32 +00:00
// draw yellow
color[0] = 1.0f;
color[1] = 1.0f;
color[2] = 0.0f;
color[3] = 0.33f;
s = va( "%2i", s4 );
w = CG_DrawStrlen( s ) * TINYCHAR_WIDTH + 8;
x -= w;
CG_FillRect( x+1, y+1, w-2, 16, color );
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_YELLOW ) {
CG_DrawPic( x+1, y+1, w-2, 16, cgs.media.selectShader );
}
CG_DrawTinyDigitalString( x + 4, y+4, s, 1.0F);
// draw green
color[0] = 0.0f;
color[1] = 1.0f;
color[2] = 0.0f;
color[3] = 0.33f;
s = va( "%2i", s3 );
w = CG_DrawStrlen( s ) * TINYCHAR_WIDTH + 8;
x -= w;
CG_FillRect( x+1, y+1, w-2, 16, color );
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_GREEN ) {
CG_DrawPic( x+1, y+1, w-2, 16, cgs.media.selectShader );
}
CG_DrawTinyDigitalString( x + 4, y+4, s, 1.0F);
}
color[0] = 0.0f;
color[1] = 0.0f;
color[2] = 1.0f;
color[3] = 0.33f;
s = va( "%2i", s2 );
w = CG_DrawStrlen( s ) * TINYCHAR_WIDTH + 8;
x -= w;
2011-02-18 14:31:32 +00:00
CG_FillRect( x+1, y+1, w-2, 16, color );
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
CG_DrawPic( x+1, y+1, w-2, 16, cgs.media.selectShader );
2011-02-18 14:31:32 +00:00
}
2011-02-18 14:31:32 +00:00
CG_DrawTinyDigitalString( x + 4, y+4, s, 1.0F);
if ( cgs.gametype == GT_CTF ) {
// Display flag status
item = BG_FindItemForPowerup( PW_BLUEFLAG );
if (item) {
y1 = y + TINYCHAR_HEIGHT + 8;
2011-02-18 14:31:32 +00:00
if( cgs.blueflag >= 0 && cgs.blueflag <= 2 ) {
2011-02-18 14:31:32 +00:00
CG_DrawPic( x+1, y1+1, w-2, 16, cgs.media.blueFlagShader[cgs.blueflag] );
2011-02-18 14:31:32 +00:00
}
}
}
color[0] = 1.0f;
color[1] = 0.0f;
color[2] = 0.0f;
color[3] = 0.33f;
s = va( "%2i", s1 );
w = CG_DrawStrlen( s ) * TINYCHAR_WIDTH + 8;
x -= w;
2011-02-18 14:31:32 +00:00
CG_FillRect( x+1, y+1, w-2, 16, color );
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) {
2011-02-18 14:31:32 +00:00
CG_DrawPic( x+1, y+1, w-2, 16, cgs.media.selectShader );
2011-02-18 14:31:32 +00:00
}
2011-02-18 14:31:32 +00:00
CG_DrawTinyDigitalString( x + 4, y+4, s, 1.0F);
if ( cgs.gametype == GT_CTF ) {
// Display flag status
item = BG_FindItemForPowerup( PW_REDFLAG );
if (item) {
2011-02-18 14:31:32 +00:00
y1 = y + TINYCHAR_HEIGHT + 8;
2011-02-18 14:31:32 +00:00
if( cgs.redflag >= 0 && cgs.redflag <= 2 ) {
2011-02-18 14:31:32 +00:00
}
}
}
#ifdef MISSIONPACK
if ( cgs.gametype == GT_1FCTF ) {
// Display flag status
item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
if (item) {
y1 = y - BIGCHAR_HEIGHT - 8;
if( cgs.flagStatus >= 0 && cgs.flagStatus <= 3 ) {
CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.flagShader[cgs.flagStatus] );
}
}
}
#endif
if ( cgs.gametype >= GT_CTF ) {
v = cgs.capturelimit;
} else {
v = cgs.fraglimit;
}
2011-02-18 14:31:32 +00:00
if ( v && !isRallyRace()) {
2011-02-18 14:31:32 +00:00
s = va( "%2i", v );
w = CG_DrawStrlen( s ) * TINYCHAR_WIDTH + 8;
x -= w;
2011-02-18 14:31:32 +00:00
CG_DrawTinyDigitalString( x + 4, y+4, s, 1.0F);
2011-02-18 14:31:32 +00:00
}
} else {
qboolean spectator;
2021-11-30 22:44:46 +00:00
CG_FillRect( x - 80, y, 96, 18, bgColor );
2011-02-18 14:31:32 +00:00
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 ) * TINYCHAR_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;
2011-02-18 14:31:32 +00:00
CG_FillRect( x+1, y+1, w-2, 16, color );
CG_DrawPic( x+1, y+1, w-2, 16, cgs.media.selectShader );
2011-02-18 14:31:32 +00:00
} else {
color[0] = 0.5f;
color[1] = 0.5f;
color[2] = 0.5f;
color[3] = 0.33f;
2011-02-18 14:31:32 +00:00
CG_FillRect( x+1, y+1, w-2, 16, color );
2011-02-18 14:31:32 +00:00
}
2011-02-18 14:31:32 +00:00
CG_DrawTinyDigitalString( x + 4, y+4, s, 1.0F);
2011-02-18 14:31:32 +00:00
}
// first place
if ( s1 != SCORE_NOT_PRESENT ) {
s = va( "%2i", s1 );
w = CG_DrawStrlen( s ) * TINYCHAR_WIDTH + 8;
x -= w;
if ( !spectator && score == s1 ) {
color[0] = 0.0f;
color[1] = 0.0f;
color[2] = 1.0f;
color[3] = 0.33f;
2011-02-18 14:31:32 +00:00
CG_FillRect( x+1, y+1, w-2, 16, color );
CG_DrawPic( x+1, y+1, w-2, 16, cgs.media.selectShader );
2011-02-18 14:31:32 +00:00
} else {
color[0] = 0.5f;
color[1] = 0.5f;
color[2] = 0.5f;
color[3] = 0.33f;
2011-02-18 14:31:32 +00:00
CG_FillRect( x+1, y+1, w-2, 16, color );
2011-02-18 14:31:32 +00:00
}
2011-02-18 14:31:32 +00:00
CG_DrawTinyDigitalString( x + 4, y+4, s, 1.0F);
2011-02-18 14:31:32 +00:00
}
if ( cgs.fraglimit ) {
s = va( "%2i", cgs.fraglimit );
w = CG_DrawStrlen( s ) * TINYCHAR_WIDTH + 8;
x -= w;
2011-02-18 14:31:32 +00:00
CG_DrawTinyDigitalString( x + 4, y+4, s, 1.0F);
2011-02-18 14:31:32 +00:00
}
}
return y1;
2011-02-18 14:31:32 +00:00
}
#endif // MISSIONPACK
/*
2022-03-01 19:55:24 +00:00
=================
2011-02-18 14:31:32 +00:00
CG_DrawLowerRight
2022-03-01 19:55:24 +00:00
=================
2011-02-18 14:31:32 +00:00
*/
#ifndef MISSIONPACK
static void CG_DrawLowerRight( void ) {
float y;
y = 470;
CG_SetScreenPlacement(PLACE_RIGHT, PLACE_BOTTOM);
2011-02-18 14:31:32 +00:00
if ( isRaceObserver( cg.snap->ps.clientNum ) )
return;
CG_DrawRallyPowerups( 476 );
y = CG_DrawLowerRightHUD( y );
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2 ) {
y = CG_DrawTeamOverlay( y, qtrue, qfalse );
}
}
#endif // MISSIONPACK
/*
2022-03-01 19:55:24 +00:00
=================
2011-02-18 14:31:32 +00:00
CG_DrawPickupItem
2022-03-01 19:55:24 +00:00
=================
2011-02-18 14:31:32 +00:00
*/
#ifndef MISSIONPACK
static int CG_DrawPickupItem( int y ) {
int value;
float *fadeColor;
float iconSize, charWidth, charHeight;
if ( cg_drawPickups.value <= 0 ) {
return y;
}
2011-02-18 14:31:32 +00:00
if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
return y;
}
iconSize = ICON_SIZE*cg_drawPickups.value;
charWidth = BIGCHAR_WIDTH*cg_drawPickups.value;
charHeight = BIGCHAR_HEIGHT*cg_drawPickups.value;
y -= iconSize;
2011-02-18 14:31:32 +00:00
value = cg.itemPickup;
if ( value ) {
fadeColor = CG_FadeColor( cg.itemPickupTime, 3000 );
if ( fadeColor ) {
CG_RegisterItemVisuals( value );
trap_R_SetColor( fadeColor );
CG_DrawPic( 8, y, iconSize, iconSize, cg_items[ value ].icon );
CG_DrawStringExt( iconSize + 16, y + (iconSize/2 - charHeight/2), bg_itemlist[ value ].pickup_name, fadeColor, qfalse, qtrue, charWidth, charHeight, 0 );
2011-02-18 14:31:32 +00:00
trap_R_SetColor( NULL );
}
}
return y;
}
#endif // MISSIONPACK
/*
2022-03-01 19:55:24 +00:00
================
2011-02-18 14:31:32 +00:00
CG_DrawLowerLeft
2022-03-01 19:55:24 +00:00
================
2011-02-18 14:31:32 +00:00
*/
#ifndef MISSIONPACK
static void CG_DrawLowerLeft( void ) {
float y;
y = 480;
CG_SetScreenPlacement(PLACE_LEFT, PLACE_BOTTOM);
2011-02-18 14:31:32 +00:00
if (y > 404)
y = 404;
y = CG_DrawLowerLeftHUD( y );
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 3 ) {
y = CG_DrawTeamOverlay( y, qfalse, qfalse );
}
CG_DrawPickupItem( y );
2011-02-18 14:31:32 +00:00
}
#endif // MISSIONPACK
//===========================================================================================
/*
=================
CG_DrawTeamInfo
=================
*/
#ifndef MISSIONPACK
static void CG_DrawTeamInfo( void ) {
int h;
int i;
2011-02-18 14:31:32 +00:00
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
CG_SetScreenPlacement( PLACE_LEFT, PLACE_BOTTOM );
2011-02-18 14:31:32 +00:00
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;
ioquake3 resync to revision 3385 from 3347. Fix going to previous browser source in q3_ui Limit ui_smallFont/ui_bigFont/cg_noTaunt cvars to missionpack Fix team chat box for spectators Don't draw crosshair 0 in Team Arena setup menu Make client for Windows x86_64 use OpenAL64.dll by default Fix loading renderer DLLs on Windows x86 Add Windows application manifest Disable DPI scaling on Windows ignore window resize event on fullscreen Don't reload arenas.txt/*.arena files in Team Arena UI Fix crash when out of memory in Team Arena's String_Alloc Fix in_nograb not releasing the mouse cursor Update UI player animation handling to match CGame Fix specifying minimum mac os version in make-macosx.sh Fix listen server sending snapshots each client frame Statically link libgcc on Windows Fix hit accuracy stats for lightning gun and shotgun kills Don't link to libGL at compile time Add common OpenGL version parsing + OpenGL 3 fixes Support parsing OpenGL ES version strings Fix setting cflags/libs from sdl2-config Load OpenGL ES 1.1 function procs [qcommon] Use unsigned types where wrapping arithmetic is intended OpenGL2: Fix brightness when r_autoExposure is disabled OpenGL2: Fix MD3 surface with zero shaders dividing by zero [botlib/be_aas_def.h] Change array size from MAX_PATH to MAX_QPATH Don't redefine MAX_PATH in bot code Fix memory leak in (unused) AAS_FloodAreas() Fix compiling GLSL shaders under Windows. Only draw cm_patch/bot debug polygons in world scenes Fix reading crash log when log wraps around buffer Don't send team overlay info to bots Fix a race condition in the makedirs target Fix shader corruption on OpenBSD
2017-11-04 11:30:30 +00:00
if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) {
2011-02-18 14:31:32 +00:00
hcolor[0] = 1.0f;
hcolor[1] = 0.0f;
hcolor[2] = 0.0f;
hcolor[3] = 0.33f;
ioquake3 resync to revision 3385 from 3347. Fix going to previous browser source in q3_ui Limit ui_smallFont/ui_bigFont/cg_noTaunt cvars to missionpack Fix team chat box for spectators Don't draw crosshair 0 in Team Arena setup menu Make client for Windows x86_64 use OpenAL64.dll by default Fix loading renderer DLLs on Windows x86 Add Windows application manifest Disable DPI scaling on Windows ignore window resize event on fullscreen Don't reload arenas.txt/*.arena files in Team Arena UI Fix crash when out of memory in Team Arena's String_Alloc Fix in_nograb not releasing the mouse cursor Update UI player animation handling to match CGame Fix specifying minimum mac os version in make-macosx.sh Fix listen server sending snapshots each client frame Statically link libgcc on Windows Fix hit accuracy stats for lightning gun and shotgun kills Don't link to libGL at compile time Add common OpenGL version parsing + OpenGL 3 fixes Support parsing OpenGL ES version strings Fix setting cflags/libs from sdl2-config Load OpenGL ES 1.1 function procs [qcommon] Use unsigned types where wrapping arithmetic is intended OpenGL2: Fix brightness when r_autoExposure is disabled OpenGL2: Fix MD3 surface with zero shaders dividing by zero [botlib/be_aas_def.h] Change array size from MAX_PATH to MAX_QPATH Don't redefine MAX_PATH in bot code Fix memory leak in (unused) AAS_FloodAreas() Fix compiling GLSL shaders under Windows. Only draw cm_patch/bot debug polygons in world scenes Fix reading crash log when log wraps around buffer Don't send team overlay info to bots Fix a race condition in the makedirs target Fix shader corruption on OpenBSD
2017-11-04 11:30:30 +00:00
} else if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) {
2011-02-18 14:31:32 +00:00
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;
CG_SetScreenPlacement(PLACE_RIGHT, PLACE_CENTER);
2011-02-18 14:31:32 +00:00
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
/*
2022-03-01 19:55:24 +00:00
========================
2011-02-18 14:31:32 +00:00
CG_DrawPersistantPowerup
2022-03-01 19:55:24 +00:00
========================
2011-02-18 14:31:32 +00:00
*/
#if 0 // sos001208 - DEAD
static void CG_DrawPersistantPowerup( void ) {
int value;
CG_SetScreenPlacement(PLACE_RIGHT, PLACE_CENTER);
2011-02-18 14:31:32 +00:00
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
/*
2022-03-01 19:55:24 +00:00
=============
2011-02-18 14:31:32 +00:00
CG_DrawReward
2022-03-01 19:55:24 +00:00
=============
2011-02-18 14:31:32 +00:00
*/
static void CG_DrawReward( void ) {
float *color;
int i, count;
float x, y;
char buf[32];
if ( !cg_drawRewards.integer ) {
return;
}
CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
2011-02-18 14:31:32 +00:00
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 = 112;
2011-02-18 14:31:32 +00:00
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 = 112;
2011-02-18 14:31:32 +00:00
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 );
}
/*
2022-03-01 19:55:24 +00:00
=========
2011-02-18 14:31:32 +00:00
LAGOMETER
2022-03-01 19:55:24 +00:00
=========
2011-02-18 14:31:32 +00:00
*/
#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;
/*
2022-03-01 19:55:24 +00:00
========================================================================================
CG_AddLagometerFrameInfo - Adds the current interpolate / extrapolate bar for this frame
========================================================================================
2011-02-18 14:31:32 +00:00
*/
void CG_AddLagometerFrameInfo( void ) {
int offset;
offset = cg.time - cg.latestSnapshotTime;
lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1) ] = offset;
lagometer.frameCount++;
}
/*
2022-03-01 19:55:24 +00:00
=======================================================
2011-02-18 14:31:32 +00:00
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.
2022-03-01 19:55:24 +00:00
=======================================================
2011-02-18 14:31:32 +00:00
*/
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++;
}
/*
2022-03-01 19:55:24 +00:00
==================================================================================
CG_DrawDisconnect - Should we draw something differnet for long lag vs no packets?
==================================================================================
2011-02-18 14:31:32 +00:00
*/
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;
}
CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
2011-02-18 14:31:32 +00:00
// 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;
}
CG_SetScreenPlacement(PLACE_RIGHT, PLACE_BOTTOM);
#ifdef MISSIONPACK
x = 640 - 48;
y = 480 - 144;
#else
2011-02-18 14:31:32 +00:00
x = 640 - 48;
y = 480 - 48;
#endif
2011-02-18 14:31:32 +00:00
CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga" ) );
}
#define MAX_LAGOMETER_PING 900
#define MAX_LAGOMETER_RANGE 300
/*
2022-03-01 19:55:24 +00:00
================
2011-02-18 14:31:32 +00:00
CG_DrawLagometer
2022-03-01 19:55:24 +00:00
================
2011-02-18 14:31:32 +00:00
*/
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;
}
CG_SetScreenPlacement(PLACE_RIGHT, PLACE_BOTTOM);
2011-02-18 14:31:32 +00:00
//
// 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 );
2011-02-18 14:31:32 +00:00
}
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;
}
CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
2011-02-18 14:31:32 +00:00
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
=================
2011-02-18 14:31:32 +00:00
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;
}
CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
2011-02-18 14:31:32 +00:00
// 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;
ca = cg_drawCrosshair.integer;
if (ca < 0) {
ca = 0;
}
hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ];
CG_DrawPic( ((SCREEN_WIDTH-w)*0.5f)+x, ((SCREEN_HEIGHT-h)*0.5f)+y, w, h, hShader );
trap_R_SetColor( NULL );
2011-02-18 14:31:32 +00:00
}
*/
2011-02-18 14:31:32 +00:00
/*
2022-03-01 19:55:24 +00:00
==================
2011-02-18 14:31:32 +00:00
CG_DrawCrosshair3D
2022-03-01 19:55:24 +00:00
==================
2011-02-18 14:31:32 +00:00
static void CG_DrawCrosshair3D(void)
{
float w;
2011-02-18 14:31:32 +00:00
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;
2011-02-18 14:31:32 +00:00
// 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);
}
*/
2011-02-18 14:31:32 +00:00
/*
2022-03-01 19:55:24 +00:00
=========================
2011-02-18 14:31:32 +00:00
CG_ScanForCrosshairEntity
2022-03-01 19:55:24 +00:00
=========================
2011-02-18 14:31:32 +00:00
*/
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 );
2011-02-18 14:31:32 +00:00
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;
}
CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
2011-02-18 14:31:32 +00:00
// 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_SetScreenPlacement(PLACE_CENTER, PLACE_BOTTOM);
2011-02-18 14:31:32 +00:00
CG_DrawBigString(320 - 9 * 8, 440, "SPECTATOR", 1.0F);
// Q3Rally Code Start - removed gametype
/*
if ( cgs.gametype == GT_TOURNAMENT ) {
CG_DrawBigString(320 - 15 * 8, 460, "waiting to play", 1.0F);
}
else
*/
// Q3Rally Code END
if ( cgs.gametype >= GT_TEAM ) {
CG_DrawBigString(320 - 39 * 8, 460, "press ESC and use the START menu to play", 1.0F);
2011-02-18 14:31:32 +00:00
}
}
/*
2022-03-01 19:55:24 +00:00
===========
2011-02-18 14:31:32 +00:00
CG_DrawVote
2022-03-01 19:55:24 +00:00
===========
2011-02-18 14:31:32 +00:00
*/
static void CG_DrawVote(void) {
char *s;
int sec;
if ( !cgs.voteTime ) {
return;
}
CG_SetScreenPlacement(PLACE_LEFT, PLACE_TOP);
2011-02-18 14:31:32 +00:00
// 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
}
/*
2022-03-01 19:55:24 +00:00
===============
2011-02-18 14:31:32 +00:00
CG_DrawTeamVote
2022-03-01 19:55:24 +00:00
===============
2011-02-18 14:31:32 +00:00
*/
static void CG_DrawTeamVote(void) {
char *s;
int sec, cs_offset;
if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED )
2011-02-18 14:31:32 +00:00
cs_offset = 0;
else if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE )
2011-02-18 14:31:32 +00:00
cs_offset = 1;
else
return;
if ( !cgs.teamVoteTime[cs_offset] ) {
return;
}
CG_SetScreenPlacement(PLACE_LEFT, PLACE_TOP);
2011-02-18 14:31:32 +00:00
// 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;
CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
2011-02-18 14:31:32 +00:00
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 ) ) {
2011-02-18 14:31:32 +00:00
// next time scoreboard comes up, don't print killer
cg.deferredPlayerLoading = 0;
cg.killerName[0] = 0;
firstTime = qtrue;
return qfalse;
}
}
2011-02-18 14:31:32 +00:00
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
}
/*
2022-03-01 19:55:24 +00:00
===================
2011-02-18 14:31:32 +00:00
CG_DrawIntermission
2022-03-01 19:55:24 +00:00
===================
2011-02-18 14:31:32 +00:00
*/
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;
// Q3Rally Code Start
cg.scoreBoardShowing = CG_DrawHUD();
if (!cg.scoreBoardShowing)
// Q3Rally Code END
cg.scoreBoardShowing = CG_DrawScoreboard();
}
/*
2022-03-01 19:55:24 +00:00
=============
2011-02-18 14:31:32 +00:00
CG_DrawFollow
2022-03-01 19:55:24 +00:00
=============
2011-02-18 14:31:32 +00:00
*/
static qboolean CG_DrawFollow( void ) {
float x;
vec4_t color;
const char *name;
if ( !(cg.snap->ps.pm_flags & PMF_FOLLOW) ) {
return qfalse;
}
CG_SetScreenPlacement(PLACE_CENTER, PLACE_TOP);
2011-02-18 14:31:32 +00:00
color[0] = 1;
color[1] = 1;
color[2] = 1;
color[3] = 1;
2023-12-10 04:50:44 +00:00
CG_DrawBigString( 320 - 9 * BIGCHAR_WIDTH/2, 24, "following", 1.0F );
2011-02-18 14:31:32 +00:00
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;
}
/*
2022-03-01 19:55:24 +00:00
==================
2011-02-18 14:31:32 +00:00
CG_DrawAmmoWarning
2022-03-01 19:55:24 +00:00
==================
2011-02-18 14:31:32 +00:00
*/
static void CG_DrawAmmoWarning( void ) {
const char *s;
int w;
// Q3Rally Code Start
if (isRallyNonDMRace()){
return;
}
// Q3Rally Code END
if ( cg_drawAmmoWarning.integer == 0 ) {
return;
}
if ( !cg.lowAmmoWarning ) {
return;
}
CG_SetScreenPlacement(PLACE_CENTER, PLACE_TOP);
2011-02-18 14:31:32 +00:00
if ( cg.lowAmmoWarning == 2 ) {
s = "OUT OF AMMO";
} else {
s = "LOW AMMO WARNING";
}
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
CG_DrawBigString(320 - w / 2, 98, s, 1.0F);
2011-02-18 14:31:32 +00:00
}
#ifdef MISSIONPACK
/*
2022-03-01 19:55:24 +00:00
==================
2011-02-18 14:31:32 +00:00
CG_DrawProxWarning
2022-03-01 19:55:24 +00:00
==================
2011-02-18 14:31:32 +00:00
*/
static void CG_DrawProxWarning( void ) {
char s [32];
int w;
static int proxTime;
int proxTick;
2011-02-18 14:31:32 +00:00
if( !(cg.snap->ps.eFlags & EF_TICKING ) ) {
proxTime = 0;
return;
}
CG_SetScreenPlacement(PLACE_CENTER, PLACE_TOP);
2011-02-18 14:31:32 +00:00
if (proxTime == 0) {
proxTime = cg.time;
2011-02-18 14:31:32 +00:00
}
proxTick = 10 - ((cg.time - proxTime) / 1000);
2011-02-18 14:31:32 +00:00
if (proxTick > 0 && proxTick <= 5) {
2011-02-18 14:31:32 +00:00
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
2022-03-01 19:55:24 +00:00
//========================
//CG_DrawSigilLocationInfo
2022-03-01 19:55:24 +00:00
//========================
void CG_DrawSigilLocationInfo( vec3_t origin, vec3_t target, qhandle_t shader, vec4_t color ) {
int x = 320, y = 240;
int w = 320, h = 240;
float angle, distance;
vec3_t temp, angles;
VectorSubtract(origin, target, temp);
distance=VectorLength(temp);
VectorNormalize(temp);
vectoangles(temp,angles);
angles[YAW]=AngleSubtract(cg.refdefViewAngles[YAW],angles[YAW]);
angle=(angles[YAW] + 180.0f)/360.0f;
angle -=0.25;
angle *= (2*M_PI);
w=sqrt((w*w)+(h*h));
x +=cos(angle)*w;
y +=sin(angle)*w;
if (x<15)
x=15;
else {
if (x>605)
x=605;
}
if (y<20)
y=20;
else
{
if (y>440)
y=440;
}
CG_DrawPic( x, y, 20, 20, shader );
CG_DrawStringExt( x-50, y+20, va("%10.2f",distance/100.0), color, qtrue, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 );
}
2022-03-01 19:55:24 +00:00
//=====================
//CG_DrawSigilLocations
2022-03-01 19:55:24 +00:00
//=====================
static void CG_DrawSigilLocations( void ) {
snapshot_t *snap;
int i;
vec3_t origin, end;
int redSigil, blueSigil, greenSigil, yellowSigil, whiteSigil;
if ( cgs.gametype != GT_DOMINATION)
return;
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR )
return;
if ( cg.nextSnap && (!cg.nextFrameTeleport && !cg.thisFrameTeleport))
snap = cg.nextSnap;
else
snap = cg.snap;
VectorCopy(cg.snap->ps.origin,origin);
redSigil = ITEM_INDEX( BG_FindItemForPowerup( PW_SIGILRED ) );
blueSigil = ITEM_INDEX( BG_FindItemForPowerup( PW_SIGILBLUE ) );
greenSigil = ITEM_INDEX( BG_FindItemForPowerup( PW_SIGILGREEN ) );
yellowSigil = ITEM_INDEX( BG_FindItemForPowerup( PW_SIGILYELLOW ) );
whiteSigil = ITEM_INDEX( BG_FindItemForPowerup( PW_SIGILWHITE ) );
for ( i = 0; i < snap->numEntities; i++ )
{
centity_t *target = &cg_entities[snap->entities[i].number];
if (target->currentState.eType != ET_ITEM)
continue;
if ( target->currentState.modelindex != redSigil && target->currentState.modelindex != blueSigil && target->currentState.modelindex != greenSigil && target->currentState.modelindex != yellowSigil && target->currentState.modelindex != whiteSigil )
continue;
VectorCopy(target->lerpOrigin,end);
if (target->currentState.modelindex == redSigil)
CG_DrawSigilLocationInfo(origin, end, cgs.media.redsigilShader, colorRed);
else if (target->currentState.modelindex == blueSigil)
CG_DrawSigilLocationInfo(origin, end, cgs.media.bluesigilShader, colorBlue);
else if (target->currentState.modelindex == greenSigil)
CG_DrawSigilLocationInfo(origin, end, cgs.media.greensigilShader, colorGreen);
else if (target->currentState.modelindex == yellowSigil)
CG_DrawSigilLocationInfo(origin, end, cgs.media.yellowsigilShader, colorYellow);
else if (target->currentState.modelindex == whiteSigil)
CG_DrawSigilLocationInfo(origin, end, cgs.media.sigilShader, colorWhite);
}
}
2011-02-18 14:31:32 +00:00
//==================================================================================
#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
/*
2022-03-01 19:55:24 +00:00
=========
2011-02-18 14:31:32 +00:00
CG_Draw2D
2022-03-01 19:55:24 +00:00
=========
2011-02-18 14:31:32 +00:00
*/
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.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR
|| isRaceObserver( cg.snap->ps.clientNum ) ) {
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
CG_DrawSpectator();
if(stereoFrame == STEREO_CENTER)
CG_DrawCrosshairNames();
} else {
// don't draw any status if dead or the scoreboard is being explicitly shown
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
if ( !cg.showScores && cg.snap->ps.stats[STAT_HEALTH] > 0 ) {
#ifdef MISSIONPACK
if ( cg_drawStatus.integer ) {
CG_SetScreenPlacement(PLACE_CENTER, PLACE_BOTTOM);
2011-02-18 14:31:32 +00:00
Menu_PaintAll();
CG_DrawTimedMenus();
}
#else
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
CG_DrawRallyStatusBar();
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
#endif
CG_DrawAmmoWarning();
#ifdef MISSIONPACK
CG_DrawProxWarning();
#endif
if(stereoFrame == STEREO_CENTER)
CG_DrawCrosshairNames();
CG_DrawWeaponSelect();
#ifndef MISSIONPACK
CG_DrawHoldableItem();
#else
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
#endif
CG_DrawReward();
}
ioquake3 resync to revision 3385 from 3347. Fix going to previous browser source in q3_ui Limit ui_smallFont/ui_bigFont/cg_noTaunt cvars to missionpack Fix team chat box for spectators Don't draw crosshair 0 in Team Arena setup menu Make client for Windows x86_64 use OpenAL64.dll by default Fix loading renderer DLLs on Windows x86 Add Windows application manifest Disable DPI scaling on Windows ignore window resize event on fullscreen Don't reload arenas.txt/*.arena files in Team Arena UI Fix crash when out of memory in Team Arena's String_Alloc Fix in_nograb not releasing the mouse cursor Update UI player animation handling to match CGame Fix specifying minimum mac os version in make-macosx.sh Fix listen server sending snapshots each client frame Statically link libgcc on Windows Fix hit accuracy stats for lightning gun and shotgun kills Don't link to libGL at compile time Add common OpenGL version parsing + OpenGL 3 fixes Support parsing OpenGL ES version strings Fix setting cflags/libs from sdl2-config Load OpenGL ES 1.1 function procs [qcommon] Use unsigned types where wrapping arithmetic is intended OpenGL2: Fix brightness when r_autoExposure is disabled OpenGL2: Fix MD3 surface with zero shaders dividing by zero [botlib/be_aas_def.h] Change array size from MAX_PATH to MAX_QPATH Don't redefine MAX_PATH in bot code Fix memory leak in (unused) AAS_FloodAreas() Fix compiling GLSL shaders under Windows. Only draw cm_patch/bot debug polygons in world scenes Fix reading crash log when log wraps around buffer Don't send team overlay info to bots Fix a race condition in the makedirs target Fix shader corruption on OpenBSD
2017-11-04 11:30:30 +00:00
}
if ( cgs.gametype >= GT_TEAM ) {
2011-02-18 14:31:32 +00:00
#ifndef MISSIONPACK
ioquake3 resync to revision 3385 from 3347. Fix going to previous browser source in q3_ui Limit ui_smallFont/ui_bigFont/cg_noTaunt cvars to missionpack Fix team chat box for spectators Don't draw crosshair 0 in Team Arena setup menu Make client for Windows x86_64 use OpenAL64.dll by default Fix loading renderer DLLs on Windows x86 Add Windows application manifest Disable DPI scaling on Windows ignore window resize event on fullscreen Don't reload arenas.txt/*.arena files in Team Arena UI Fix crash when out of memory in Team Arena's String_Alloc Fix in_nograb not releasing the mouse cursor Update UI player animation handling to match CGame Fix specifying minimum mac os version in make-macosx.sh Fix listen server sending snapshots each client frame Statically link libgcc on Windows Fix hit accuracy stats for lightning gun and shotgun kills Don't link to libGL at compile time Add common OpenGL version parsing + OpenGL 3 fixes Support parsing OpenGL ES version strings Fix setting cflags/libs from sdl2-config Load OpenGL ES 1.1 function procs [qcommon] Use unsigned types where wrapping arithmetic is intended OpenGL2: Fix brightness when r_autoExposure is disabled OpenGL2: Fix MD3 surface with zero shaders dividing by zero [botlib/be_aas_def.h] Change array size from MAX_PATH to MAX_QPATH Don't redefine MAX_PATH in bot code Fix memory leak in (unused) AAS_FloodAreas() Fix compiling GLSL shaders under Windows. Only draw cm_patch/bot debug polygons in world scenes Fix reading crash log when log wraps around buffer Don't send team overlay info to bots Fix a race condition in the makedirs target Fix shader corruption on OpenBSD
2017-11-04 11:30:30 +00:00
CG_DrawTeamInfo();
2011-02-18 14:31:32 +00:00
#endif
}
CG_DrawVote();
CG_DrawTeamVote();
CG_DrawLagometer();
if (cg_sigilLocator.integer == 1)
CG_DrawSigilLocations();
2011-02-18 14:31:32 +00:00
#ifdef MISSIONPACK
if (!cg_paused.integer) {
CG_DrawUpperRight(stereoFrame);
}
#else
CG_DrawUpperRight(stereoFrame);
#endif
#ifndef MISSIONPACK
CG_DrawLowerRight();
CG_DrawLowerLeft();
#endif
CG_DrawFollow();
cg.scoreBoardShowing = CG_DrawHUD();
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
// don't draw center string if scoreboard is up
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
if (!cg.scoreBoardShowing)
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
cg.scoreBoardShowing = CG_DrawScoreboard();
if ( !cg.scoreBoardShowing) {
CG_DrawCenterString();
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
CG_DrawRaceCountDown();
2022-03-01 19:55:24 +00:00
2011-02-18 14:31:32 +00:00
}
}
/*
2022-03-01 19:55:24 +00:00
========================================================================
CG_DrawActive - Perform all drawing needed to completely fill the screen
========================================================================
2011-02-18 14:31:32 +00:00
*/
void CG_DrawActive( stereoFrame_t stereoView ) {
// optionally draw the info screen instead
if ( !cg.snap ) {
CG_DrawInformation();
return;
}
// clear around the rendered view if sized down
CG_TileClear();
if(stereoView != STEREO_CENTER)
// CG_DrawCrosshair3D();
// apply earthquake effect
CG_Earthquake();
2011-02-18 14:31:32 +00:00
// draw 3D view
trap_R_RenderScene( &cg.refdef );
// Q3Rally Code Start
if ( !cg.scoreBoardShowing )
CG_DrawRearviewMirror( 170, 10, 300, 75);
CG_DrawMMap( 0, 25, 160, 120); //TBB draw minimap function
2011-02-18 14:31:32 +00:00
// Q3Rally Code END
// draw status bar and other floating elements
CG_Draw2D(stereoView);
}