aboutsummaryrefslogtreecommitdiffstats
path: root/code/cgame/cg_draw.c
diff options
context:
space:
mode:
Diffstat (limited to 'code/cgame/cg_draw.c')
-rwxr-xr-xcode/cgame/cg_draw.c5318
1 files changed, 2659 insertions, 2659 deletions
diff --git a/code/cgame/cg_draw.c b/code/cgame/cg_draw.c
index e29c10f..5575aaf 100755
--- a/code/cgame/cg_draw.c
+++ b/code/cgame/cg_draw.c
@@ -1,2659 +1,2659 @@
-/*
-===========================================================================
-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 Foobar; 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;
-// FIXME: see ui_main.c, same problem
-// const unsigned char *s = text;
- const char *s = text;
- fontInfo_t *font = &cgDC.Assets.textFont;
- if (scale <= cg_smallFont.value) {
- font = &cgDC.Assets.smallFont;
- } else if (scale > cg_bigFont.value) {
- font = &cgDC.Assets.bigFont;
- }
- useScale = scale * font->glyphScale;
- out = 0;
- if (text) {
- len = strlen(text);
- if (limit > 0 && len > limit) {
- len = limit;
- }
- count = 0;
- while (s && *s && count < len) {
- if ( Q_IsColorString(s) ) {
- s += 2;
- continue;
- } else {
- glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
- out += glyph->xSkip;
- s++;
- count++;
- }
- }
- }
- return out * useScale;
-}
-
-int CG_Text_Height(const char *text, float scale, int limit) {
- int len, count;
- float max;
- glyphInfo_t *glyph;
- float useScale;
-// TTimo: FIXME
-// const unsigned char *s = text;
- const char *s = text;
- fontInfo_t *font = &cgDC.Assets.textFont;
- if (scale <= cg_smallFont.value) {
- font = &cgDC.Assets.smallFont;
- } else if (scale > cg_bigFont.value) {
- font = &cgDC.Assets.bigFont;
- }
- useScale = scale * font->glyphScale;
- max = 0;
- if (text) {
- len = strlen(text);
- if (limit > 0 && len > limit) {
- len = limit;
- }
- count = 0;
- while (s && *s && count < len) {
- if ( Q_IsColorString(s) ) {
- s += 2;
- continue;
- } else {
- glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
- if (max < glyph->height) {
- max = glyph->height;
- }
- s++;
- count++;
- }
- }
- }
- return max * useScale;
-}
-
-void CG_Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader) {
- float w, h;
- w = width * scale;
- h = height * scale;
- CG_AdjustFrom640( &x, &y, &w, &h );
- trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader );
-}
-
-void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style) {
- int len, count;
- vec4_t newColor;
- glyphInfo_t *glyph;
- float useScale;
- fontInfo_t *font = &cgDC.Assets.textFont;
- if (scale <= cg_smallFont.value) {
- font = &cgDC.Assets.smallFont;
- } else if (scale > cg_bigFont.value) {
- font = &cgDC.Assets.bigFont;
- }
- useScale = scale * font->glyphScale;
- if (text) {
-// TTimo: FIXME
-// const unsigned char *s = text;
- const char *s = text;
- trap_R_SetColor( color );
- memcpy(&newColor[0], &color[0], sizeof(vec4_t));
- len = strlen(text);
- if (limit > 0 && len > limit) {
- len = limit;
- }
- count = 0;
- while (s && *s && count < len) {
- glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
- //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;
-#ifdef MISSIONPACK
- qhandle_t handle;
-#endif
- 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 );
- }
-#ifdef MISSIONPACK
- if( cgs.gametype == GT_HARVESTER ) {
- origin[0] = 90;
- origin[1] = 0;
- origin[2] = -10;
- angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0;
- if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
- handle = cgs.media.redCubeModel;
- } else {
- handle = cgs.media.blueCubeModel;
- }
- CG_Draw3DModel( 640 - (TEXT_ICON_SPACE + ICON_SIZE), 416, ICON_SIZE, ICON_SIZE, handle, 0, origin, angles );
- }
-#endif
- //
- // 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 );
- }
-
- }
-#ifdef MISSIONPACK
- //
- // cubes
- //
- if( cgs.gametype == GT_HARVESTER ) {
- value = ps->generic1;
- if( value > 99 ) {
- value = 99;
- }
- trap_R_SetColor( colors[0] );
- CG_DrawField (640 - (CHAR_WIDTH*2 + TEXT_ICON_SPACE + ICON_SIZE), 432, 2, 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 ) {
- if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
- handle = cgs.media.redCubeIcon;
- } else {
- handle = cgs.media.blueCubeIcon;
- }
- CG_DrawPic( 640 - (TEXT_ICON_SPACE + ICON_SIZE), 432, ICON_SIZE, ICON_SIZE, handle );
- }
- }
-#endif
-}
-#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;
- }
-
- 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( void ) {
- 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 ) {
- y = CG_DrawFPS( y );
- }
- if ( cg_drawTimer.integer ) {
- y = CG_DrawTimer( y );
- }
- if ( cg_drawAttacker.integer ) {
- y = 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] );
- }
- }
- }
-
-#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;
- }
- 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;
- }
- t = ps->powerups[ i ] - cg.time;
- // 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
- 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 );
- 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 );
- }
-
-
- y = CG_DrawPickupItem( y );
-}
-#endif // MISSIONPACK
-
-
-//===========================================================================================
-
-/*
-=================
-CG_DrawTeamInfo
-=================
-*/
-#ifndef MISSIONPACK
-static void CG_DrawTeamInfo( void ) {
- int w, h;
- int i, len;
- 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;
-
- w = 0;
-
- for (i = cgs.teamLastChatPos; i < cgs.teamChatPos; i++) {
- len = CG_DrawStrlen(cgs.teamChatMsgs[i % chatHeight]);
- if (len > w)
- w = len;
- }
- w *= TINYCHAR_WIDTH;
- w += TINYCHAR_WIDTH * 2;
-
- 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;
- } 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; // bk010215 - FIXME char message[1024];
-
- // draw the phone jack if we are completely past our buffers
- cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1;
- trap_GetUserCmd( cmdNum, &cmd );
- if ( cmd.serverTime <= cg.snap->ps.commandTime
- || cmd.serverTime > cg.time ) { // special check for map_restart // bk 0102165 - FIXME
- return;
- }
-
- // also add text in center of screen
- s = "Connection Interrupted"; // bk 010215 - FIXME
- w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
- CG_DrawBigString( 320 - w/2, 100, s, 1.0F);
-
- // blink the icon
- if ( ( cg.time >> 9 ) & 1 ) {
- return;
- }
-
- x = 640 - 48;
- y = 480 - 48;
-
- CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga" ) );
-}
-
-
-#define MAX_LAGOMETER_PING 900
-#define MAX_LAGOMETER_RANGE 300
-
-/*
-==============
-CG_DrawLagometer
-==============
-*/
-static void CG_DrawLagometer( void ) {
- int a, x, y, i;
- float v;
- float ax, ay, aw, ah, mid, range;
- int color;
- float vscale;
-
- if ( !cg_lagometer.integer || cgs.localServer ) {
- CG_DrawDisconnect();
- return;
- }
-
- //
- // draw the graph
- //
-#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( ax, ay, "snc", 1.0 );
- }
-
- CG_DrawDisconnect();
-}
-
-
-
-/*
-===============================================================================
-
-CENTER PRINTING
-
-===============================================================================
-*/
-
-
-/*
-==============
-CG_CenterPrint
-
-Called for important messages that should stay in the center of the screen
-for a few moments
-==============
-*/
-void CG_CenterPrint( const char *str, int y, int charWidth ) {
- char *s;
-
- Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
-
- cg.centerPrintTime = cg.time;
- cg.centerPrintY = y;
- cg.centerPrintCharWidth = charWidth;
-
- // count the number of lines for centering
- cg.centerPrintLines = 1;
- s = cg.centerPrint;
- while( *s ) {
- if (*s == '\n')
- cg.centerPrintLines++;
- s++;
- }
-}
-
-
-/*
-===================
-CG_DrawCenterString
-===================
-*/
-static void CG_DrawCenterString( void ) {
- char *start;
- int l;
- int x, y, w;
-#ifdef MISSIONPACK // bk010221 - unused else
- 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 );
-}
-
-
-
-/*
-=================
-CG_ScanForCrosshairEntity
-=================
-*/
-static void CG_ScanForCrosshairEntity( void ) {
- trace_t trace;
- vec3_t start, end;
- int content;
-
- VectorCopy( cg.refdef.vieworg, start );
- VectorMA( start, 131072, cg.refdef.viewaxis[0], end );
-
- CG_Trace( &trace, start, vec3_origin, vec3_origin, end,
- cg.snap->ps.clientNum, CONTENTS_SOLID|CONTENTS_BODY );
- if ( trace.entityNum >= MAX_CLIENTS ) {
- return;
- }
-
- // if the player is in fog, don't show it
- content = trap_CM_PointContents( trace.endpos, 0 );
- if ( content & CONTENTS_FOG ) {
- return;
- }
-
- // 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->team == TEAM_RED )
- cs_offset = 0;
- else if ( cgs.clientinfo->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() {
-#ifdef MISSIONPACK
- static qboolean firstTime = qtrue;
- float fade, *fadeColor;
-
- 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 ) {
- fade = 1.0;
- fadeColor = colorWhite;
- } else {
- fadeColor = CG_FadeColor( cg.scoreFadeTime, FADE_TIME );
- if ( !fadeColor ) {
- // next time scoreboard comes up, don't print killer
- cg.deferredPlayerLoading = 0;
- cg.killerName[0] = 0;
- firstTime = qtrue;
- return qfalse;
- }
- fade = *fadeColor;
- }
-
-
- 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;
- static int proxCounter;
- static int proxTick;
-
- if( !(cg.snap->ps.eFlags & EF_TICKING ) ) {
- proxTime = 0;
- return;
- }
-
- if (proxTime == 0) {
- proxTime = cg.time + 5000;
- proxCounter = 5;
- proxTick = 0;
- }
-
- if (cg.time > proxTime) {
- proxTick = proxCounter--;
- proxTime = cg.time + 1000;
- }
-
- if (proxTick != 0) {
- 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;
- float scale;
- clientInfo_t *ci1, *ci2;
- int cw;
- const char *s;
-
- sec = cg.warmup;
- if ( !sec ) {
- return;
- }
-
- if ( sec < 0 ) {
- s = "Waiting for players";
- w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
- CG_DrawBigString(320 - w / 2, 24, s, 1.0F);
- cg.warmupCount = 0;
- return;
- }
-
- if (cgs.gametype == GT_TOURNAMENT) {
- // find the two active players
- ci1 = NULL;
- ci2 = NULL;
- for ( i = 0 ; i < cgs.maxclients ; i++ ) {
- if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_FREE ) {
- if ( !ci1 ) {
- ci1 = &cgs.clientinfo[i];
- } else {
- ci2 = &cgs.clientinfo[i];
- }
- }
- }
-
- if ( ci1 && ci2 ) {
- s = va( "%s vs %s", ci1->name, ci2->name );
-#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;
- }
- }
- scale = 0.45f;
- switch ( cg.warmupCount ) {
- case 0:
- cw = 28;
- scale = 0.54f;
- break;
- case 1:
- cw = 24;
- scale = 0.51f;
- break;
- case 2:
- cw = 20;
- scale = 0.48f;
- break;
- default:
- cw = 16;
- scale = 0.45f;
- break;
- }
-
-#ifdef MISSIONPACK
- w = CG_Text_Width(s, scale, 0);
- CG_Text_Paint(320 - w / 2, 125, scale, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE);
-#else
- 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() {
- 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( void ) {
-#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();
- 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
- 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();
- }
-#else
- CG_DrawUpperRight();
-#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();
- }
-}
-
-
-static void CG_DrawTourneyScoreboard() {
-#ifdef MISSIONPACK
-#else
- CG_DrawOldTourneyScoreboard();
-#endif
-}
-
-/*
-=====================
-CG_DrawActive
-
-Perform all drawing needed to completely fill the screen
-=====================
-*/
-void CG_DrawActive( stereoFrame_t stereoView ) {
- float separation;
- vec3_t baseOrg;
-
- // optionally draw the info screen instead
- if ( !cg.snap ) {
- CG_DrawInformation();
- return;
- }
-
- // optionally draw the tournement scoreboard instead
- if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR &&
- ( cg.snap->ps.pm_flags & PMF_SCOREBOARD ) ) {
- CG_DrawTourneyScoreboard();
- return;
- }
-
- switch ( stereoView ) {
- case STEREO_CENTER:
- separation = 0;
- break;
- case STEREO_LEFT:
- separation = -cg_stereoSeparation.value / 2;
- break;
- case STEREO_RIGHT:
- separation = cg_stereoSeparation.value / 2;
- break;
- default:
- separation = 0;
- CG_Error( "CG_DrawActive: Undefined stereoView" );
- }
-
-
- // clear around the rendered view if sized down
- CG_TileClear();
-
- // offset vieworg appropriately if we're doing stereo separation
- VectorCopy( cg.refdef.vieworg, baseOrg );
- if ( separation != 0 ) {
- VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[1], cg.refdef.vieworg );
- }
-
- // draw 3D view
- trap_R_RenderScene( &cg.refdef );
-
- // restore original viewpoint if running stereo
- if ( separation != 0 ) {
- VectorCopy( baseOrg, cg.refdef.vieworg );
- }
-
- // draw status bar and other floating elements
- CG_Draw2D();
-}
-
-
-
+/*
+===========================================================================
+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 Foobar; 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;
+// FIXME: see ui_main.c, same problem
+// const unsigned char *s = text;
+ const char *s = text;
+ fontInfo_t *font = &cgDC.Assets.textFont;
+ if (scale <= cg_smallFont.value) {
+ font = &cgDC.Assets.smallFont;
+ } else if (scale > cg_bigFont.value) {
+ font = &cgDC.Assets.bigFont;
+ }
+ useScale = scale * font->glyphScale;
+ out = 0;
+ if (text) {
+ len = strlen(text);
+ if (limit > 0 && len > limit) {
+ len = limit;
+ }
+ count = 0;
+ while (s && *s && count < len) {
+ if ( Q_IsColorString(s) ) {
+ s += 2;
+ continue;
+ } else {
+ glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
+ out += glyph->xSkip;
+ s++;
+ count++;
+ }
+ }
+ }
+ return out * useScale;
+}
+
+int CG_Text_Height(const char *text, float scale, int limit) {
+ int len, count;
+ float max;
+ glyphInfo_t *glyph;
+ float useScale;
+// TTimo: FIXME
+// const unsigned char *s = text;
+ const char *s = text;
+ fontInfo_t *font = &cgDC.Assets.textFont;
+ if (scale <= cg_smallFont.value) {
+ font = &cgDC.Assets.smallFont;
+ } else if (scale > cg_bigFont.value) {
+ font = &cgDC.Assets.bigFont;
+ }
+ useScale = scale * font->glyphScale;
+ max = 0;
+ if (text) {
+ len = strlen(text);
+ if (limit > 0 && len > limit) {
+ len = limit;
+ }
+ count = 0;
+ while (s && *s && count < len) {
+ if ( Q_IsColorString(s) ) {
+ s += 2;
+ continue;
+ } else {
+ glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
+ if (max < glyph->height) {
+ max = glyph->height;
+ }
+ s++;
+ count++;
+ }
+ }
+ }
+ return max * useScale;
+}
+
+void CG_Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader) {
+ float w, h;
+ w = width * scale;
+ h = height * scale;
+ CG_AdjustFrom640( &x, &y, &w, &h );
+ trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader );
+}
+
+void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style) {
+ int len, count;
+ vec4_t newColor;
+ glyphInfo_t *glyph;
+ float useScale;
+ fontInfo_t *font = &cgDC.Assets.textFont;
+ if (scale <= cg_smallFont.value) {
+ font = &cgDC.Assets.smallFont;
+ } else if (scale > cg_bigFont.value) {
+ font = &cgDC.Assets.bigFont;
+ }
+ useScale = scale * font->glyphScale;
+ if (text) {
+// TTimo: FIXME
+// const unsigned char *s = text;
+ const char *s = text;
+ trap_R_SetColor( color );
+ memcpy(&newColor[0], &color[0], sizeof(vec4_t));
+ len = strlen(text);
+ if (limit > 0 && len > limit) {
+ len = limit;
+ }
+ count = 0;
+ while (s && *s && count < len) {
+ glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
+ //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;
+#ifdef MISSIONPACK
+ qhandle_t handle;
+#endif
+ 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 );
+ }
+#ifdef MISSIONPACK
+ if( cgs.gametype == GT_HARVESTER ) {
+ origin[0] = 90;
+ origin[1] = 0;
+ origin[2] = -10;
+ angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0;
+ if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
+ handle = cgs.media.redCubeModel;
+ } else {
+ handle = cgs.media.blueCubeModel;
+ }
+ CG_Draw3DModel( 640 - (TEXT_ICON_SPACE + ICON_SIZE), 416, ICON_SIZE, ICON_SIZE, handle, 0, origin, angles );
+ }
+#endif
+ //
+ // 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 );
+ }
+
+ }
+#ifdef MISSIONPACK
+ //
+ // cubes
+ //
+ if( cgs.gametype == GT_HARVESTER ) {
+ value = ps->generic1;
+ if( value > 99 ) {
+ value = 99;
+ }
+ trap_R_SetColor( colors[0] );
+ CG_DrawField (640 - (CHAR_WIDTH*2 + TEXT_ICON_SPACE + ICON_SIZE), 432, 2, 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 ) {
+ if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
+ handle = cgs.media.redCubeIcon;
+ } else {
+ handle = cgs.media.blueCubeIcon;
+ }
+ CG_DrawPic( 640 - (TEXT_ICON_SPACE + ICON_SIZE), 432, ICON_SIZE, ICON_SIZE, handle );
+ }
+ }
+#endif
+}
+#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;
+ }
+
+ 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( void ) {
+ 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 ) {
+ y = CG_DrawFPS( y );
+ }
+ if ( cg_drawTimer.integer ) {
+ y = CG_DrawTimer( y );
+ }
+ if ( cg_drawAttacker.integer ) {
+ y = 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] );
+ }
+ }
+ }
+
+#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;
+ }
+ 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;
+ }
+ t = ps->powerups[ i ] - cg.time;
+ // 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
+ 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 );
+ 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 );
+ }
+
+
+ y = CG_DrawPickupItem( y );
+}
+#endif // MISSIONPACK
+
+
+//===========================================================================================
+
+/*
+=================
+CG_DrawTeamInfo
+=================
+*/
+#ifndef MISSIONPACK
+static void CG_DrawTeamInfo( void ) {
+ int w, h;
+ int i, len;
+ 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;
+
+ w = 0;
+
+ for (i = cgs.teamLastChatPos; i < cgs.teamChatPos; i++) {
+ len = CG_DrawStrlen(cgs.teamChatMsgs[i % chatHeight]);
+ if (len > w)
+ w = len;
+ }
+ w *= TINYCHAR_WIDTH;
+ w += TINYCHAR_WIDTH * 2;
+
+ 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;
+ } 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; // bk010215 - FIXME char message[1024];
+
+ // draw the phone jack if we are completely past our buffers
+ cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1;
+ trap_GetUserCmd( cmdNum, &cmd );
+ if ( cmd.serverTime <= cg.snap->ps.commandTime
+ || cmd.serverTime > cg.time ) { // special check for map_restart // bk 0102165 - FIXME
+ return;
+ }
+
+ // also add text in center of screen
+ s = "Connection Interrupted"; // bk 010215 - FIXME
+ w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
+ CG_DrawBigString( 320 - w/2, 100, s, 1.0F);
+
+ // blink the icon
+ if ( ( cg.time >> 9 ) & 1 ) {
+ return;
+ }
+
+ x = 640 - 48;
+ y = 480 - 48;
+
+ CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga" ) );
+}
+
+
+#define MAX_LAGOMETER_PING 900
+#define MAX_LAGOMETER_RANGE 300
+
+/*
+==============
+CG_DrawLagometer
+==============
+*/
+static void CG_DrawLagometer( void ) {
+ int a, x, y, i;
+ float v;
+ float ax, ay, aw, ah, mid, range;
+ int color;
+ float vscale;
+
+ if ( !cg_lagometer.integer || cgs.localServer ) {
+ CG_DrawDisconnect();
+ return;
+ }
+
+ //
+ // draw the graph
+ //
+#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( ax, ay, "snc", 1.0 );
+ }
+
+ CG_DrawDisconnect();
+}
+
+
+
+/*
+===============================================================================
+
+CENTER PRINTING
+
+===============================================================================
+*/
+
+
+/*
+==============
+CG_CenterPrint
+
+Called for important messages that should stay in the center of the screen
+for a few moments
+==============
+*/
+void CG_CenterPrint( const char *str, int y, int charWidth ) {
+ char *s;
+
+ Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
+
+ cg.centerPrintTime = cg.time;
+ cg.centerPrintY = y;
+ cg.centerPrintCharWidth = charWidth;
+
+ // count the number of lines for centering
+ cg.centerPrintLines = 1;
+ s = cg.centerPrint;
+ while( *s ) {
+ if (*s == '\n')
+ cg.centerPrintLines++;
+ s++;
+ }
+}
+
+
+/*
+===================
+CG_DrawCenterString
+===================
+*/
+static void CG_DrawCenterString( void ) {
+ char *start;
+ int l;
+ int x, y, w;
+#ifdef MISSIONPACK // bk010221 - unused else
+ 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 );
+}
+
+
+
+/*
+=================
+CG_ScanForCrosshairEntity
+=================
+*/
+static void CG_ScanForCrosshairEntity( void ) {
+ trace_t trace;
+ vec3_t start, end;
+ int content;
+
+ VectorCopy( cg.refdef.vieworg, start );
+ VectorMA( start, 131072, cg.refdef.viewaxis[0], end );
+
+ CG_Trace( &trace, start, vec3_origin, vec3_origin, end,
+ cg.snap->ps.clientNum, CONTENTS_SOLID|CONTENTS_BODY );
+ if ( trace.entityNum >= MAX_CLIENTS ) {
+ return;
+ }
+
+ // if the player is in fog, don't show it
+ content = trap_CM_PointContents( trace.endpos, 0 );
+ if ( content & CONTENTS_FOG ) {
+ return;
+ }
+
+ // 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->team == TEAM_RED )
+ cs_offset = 0;
+ else if ( cgs.clientinfo->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() {
+#ifdef MISSIONPACK
+ static qboolean firstTime = qtrue;
+ float fade, *fadeColor;
+
+ 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 ) {
+ fade = 1.0;
+ fadeColor = colorWhite;
+ } else {
+ fadeColor = CG_FadeColor( cg.scoreFadeTime, FADE_TIME );
+ if ( !fadeColor ) {
+ // next time scoreboard comes up, don't print killer
+ cg.deferredPlayerLoading = 0;
+ cg.killerName[0] = 0;
+ firstTime = qtrue;
+ return qfalse;
+ }
+ fade = *fadeColor;
+ }
+
+
+ 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;
+ static int proxCounter;
+ static int proxTick;
+
+ if( !(cg.snap->ps.eFlags & EF_TICKING ) ) {
+ proxTime = 0;
+ return;
+ }
+
+ if (proxTime == 0) {
+ proxTime = cg.time + 5000;
+ proxCounter = 5;
+ proxTick = 0;
+ }
+
+ if (cg.time > proxTime) {
+ proxTick = proxCounter--;
+ proxTime = cg.time + 1000;
+ }
+
+ if (proxTick != 0) {
+ 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;
+ float scale;
+ clientInfo_t *ci1, *ci2;
+ int cw;
+ const char *s;
+
+ sec = cg.warmup;
+ if ( !sec ) {
+ return;
+ }
+
+ if ( sec < 0 ) {
+ s = "Waiting for players";
+ w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
+ CG_DrawBigString(320 - w / 2, 24, s, 1.0F);
+ cg.warmupCount = 0;
+ return;
+ }
+
+ if (cgs.gametype == GT_TOURNAMENT) {
+ // find the two active players
+ ci1 = NULL;
+ ci2 = NULL;
+ for ( i = 0 ; i < cgs.maxclients ; i++ ) {
+ if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_FREE ) {
+ if ( !ci1 ) {
+ ci1 = &cgs.clientinfo[i];
+ } else {
+ ci2 = &cgs.clientinfo[i];
+ }
+ }
+ }
+
+ if ( ci1 && ci2 ) {
+ s = va( "%s vs %s", ci1->name, ci2->name );
+#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;
+ }
+ }
+ scale = 0.45f;
+ switch ( cg.warmupCount ) {
+ case 0:
+ cw = 28;
+ scale = 0.54f;
+ break;
+ case 1:
+ cw = 24;
+ scale = 0.51f;
+ break;
+ case 2:
+ cw = 20;
+ scale = 0.48f;
+ break;
+ default:
+ cw = 16;
+ scale = 0.45f;
+ break;
+ }
+
+#ifdef MISSIONPACK
+ w = CG_Text_Width(s, scale, 0);
+ CG_Text_Paint(320 - w / 2, 125, scale, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE);
+#else
+ 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() {
+ 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( void ) {
+#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();
+ 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
+ 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();
+ }
+#else
+ CG_DrawUpperRight();
+#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();
+ }
+}
+
+
+static void CG_DrawTourneyScoreboard() {
+#ifdef MISSIONPACK
+#else
+ CG_DrawOldTourneyScoreboard();
+#endif
+}
+
+/*
+=====================
+CG_DrawActive
+
+Perform all drawing needed to completely fill the screen
+=====================
+*/
+void CG_DrawActive( stereoFrame_t stereoView ) {
+ float separation;
+ vec3_t baseOrg;
+
+ // optionally draw the info screen instead
+ if ( !cg.snap ) {
+ CG_DrawInformation();
+ return;
+ }
+
+ // optionally draw the tournement scoreboard instead
+ if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR &&
+ ( cg.snap->ps.pm_flags & PMF_SCOREBOARD ) ) {
+ CG_DrawTourneyScoreboard();
+ return;
+ }
+
+ switch ( stereoView ) {
+ case STEREO_CENTER:
+ separation = 0;
+ break;
+ case STEREO_LEFT:
+ separation = -cg_stereoSeparation.value / 2;
+ break;
+ case STEREO_RIGHT:
+ separation = cg_stereoSeparation.value / 2;
+ break;
+ default:
+ separation = 0;
+ CG_Error( "CG_DrawActive: Undefined stereoView" );
+ }
+
+
+ // clear around the rendered view if sized down
+ CG_TileClear();
+
+ // offset vieworg appropriately if we're doing stereo separation
+ VectorCopy( cg.refdef.vieworg, baseOrg );
+ if ( separation != 0 ) {
+ VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[1], cg.refdef.vieworg );
+ }
+
+ // draw 3D view
+ trap_R_RenderScene( &cg.refdef );
+
+ // restore original viewpoint if running stereo
+ if ( separation != 0 ) {
+ VectorCopy( baseOrg, cg.refdef.vieworg );
+ }
+
+ // draw status bar and other floating elements
+ CG_Draw2D();
+}
+
+
+