stvoy-mp-sdk/Code-DM/cgame/cg_draw.c
2000-12-11 00:00:00 +00:00

2630 lines
63 KiB
C

// Copyright (C) 1999-2000 Id Software, Inc.
//
// cg_draw.c -- draw all of the graphical elements during
// active (after loading) gameplay
#include "cg_local.h"
#include "cg_text.h"
#include "cg_screenfx.h"
// set in CG_ParseTeamInfo
int sortedTeamPlayers[TEAM_MAXOVERLAY];
int numSortedTeamPlayers;
int drawTeamOverlayModificationCount = -1;
extern void InitPostGameMenuStruct();
static void CG_InterfaceStartup();
char *ingame_text[IGT_MAX]; // Holds pointers to ingame text
interfacegraphics_s interface_graphics[IG_MAX] =
{
// type timer x y width height file/text graphic, min max color style ptr
SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0, // IG_GROW
SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0, // IG_HEALTH_START
SG_GRAPHIC, 0.0, 5, 429, 32, 64, "gfx/interface/healthcap1", 0, 0, 0, CT_DKBROWN1, 0, // IG_HEALTH_BEGINCAP
SG_GRAPHIC, 0.0, 64, 429, 6, 25, "gfx/interface/ammobar", 0, 0, 0, CT_DKBROWN1, 0, // IG_HEALTH_BOX1
SG_GRAPHIC, 0.0, 72, 429, 0, 25, "gfx/interface/ammobar", 0, 0, 0, CT_LTBROWN1, 0, // IG_HEALTH_SLIDERFULL
SG_GRAPHIC, 0.0, 0, 429, 0, 25, "gfx/interface/ammobar", 0, 0, 0, CT_DKBROWN1, 0, // IG_HEALTH_SLIDEREMPTY
SG_GRAPHIC, 0.0, 72, 429, 16, 32, "gfx/interface/healthcap2", 0, 0, 147, CT_DKBROWN1, 0, // IG_HEALTH_ENDCAP
SG_NUMBER, 0.0, 23, 425, 16, 32, NULL, 0, 0, 0, CT_LTBROWN1, NUM_FONT_BIG, // IG_HEALTH_COUNT
SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0, // IG_HEALTH_END
SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0, // IG_ARMOR_START
SG_GRAPHIC, 0.0, 20, 458, 32, 16, "gfx/interface/armorcap1", 0, 0, 0, CT_DKPURPLE1, 0, // IG_ARMOR_BEGINCAP
SG_GRAPHIC, 0.0, 64, 458, 6, 12, "gfx/interface/ammobar", 0, 0, 0, CT_DKPURPLE1, 0, // IG_ARMOR_BOX1
SG_GRAPHIC, 0.0, 72, 458, 0, 12, "gfx/interface/ammobar", 0, 0, 0, CT_LTPURPLE1, 0, // IG_ARMOR_SLIDERFULL
SG_GRAPHIC, 0.0, 0, 458, 0, 12, "gfx/interface/ammobar", 0, 0, 0, CT_DKPURPLE1, 0, // IG_ARMOR_SLIDEREMPTY
SG_GRAPHIC, 0.0, 72, 458, 16, 16, "gfx/interface/armorcap2", 0, 0, 147, CT_DKPURPLE1, 0, // IG_ARMOR_ENDCAP
SG_NUMBER, 0.0, 44, 458, 16, 16, NULL, 0, 0, 0, CT_LTPURPLE1, NUM_FONT_SMALL, // IG_ARMOR_COUNT
SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0, // IG_ARMOR_END
SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0, // IG_AMMO_START
SG_GRAPHIC, 0.0, 613, 429, 32, 64, "gfx/interface/ammouppercap1", 0, 0, 0, CT_LTPURPLE2, 0, // IG_AMMO_UPPER_BEGINCAP
SG_GRAPHIC, 0.0, 607, 429, 16, 32, "gfx/interface/ammouppercap2", 0, 0, 572, CT_LTPURPLE2, 0, // IG_AMMO_UPPER_ENDCAP
SG_GRAPHIC, 0.0, 613, 458, 16, 16, "gfx/interface/ammolowercap1", 0, 0, 0, CT_LTPURPLE2, 0, // IG_AMMO_LOWER_BEGINCAP
SG_GRAPHIC, 0.0, 578, 458, 0, 12, "gfx/interface/ammobar", 0, 0, 0, CT_LTPURPLE1, 0, // IG_AMMO_SLIDERFULL
SG_GRAPHIC, 0.0, 0, 458, 0, 12, "gfx/interface/ammobar", 0, 0, 0, CT_DKPURPLE1, 0, // IG_AMMO_SLIDEREMPTY
SG_GRAPHIC, 0.0, 607, 458, 16, 16, "gfx/interface/ammolowercap2", 0, 0, 572, CT_LTPURPLE2, 0, // IG_AMMO_LOWER_ENDCAP
SG_NUMBER, 0.0, 573, 425, 16, 32, NULL, 0, 0, 0, CT_LTPURPLE1, NUM_FONT_BIG, // IG_AMMO_COUNT
SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0, // IG_AMMO_END
};
#define LOWEROVERLAY_Y (SCREEN_HEIGHT - ICON_SIZE - 15)
/*
==============
CG_DrawField
Draws large numbers for status bar and powerups
==============
*/
/*
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--;
}
}
*/
/*
================
CG_Draw3DModel
================
*/
static void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, qhandle_t shader, 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.customShader = shader;
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, 0, 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 ) {
qhandle_t cm;
float len;
vec3_t origin, angles;
vec3_t mins, maxs;
if ( 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 );;
CG_Draw3DModel( x, y, w, h,
team == TEAM_RED ? cgs.media.redFlagModel : cgs.media.blueFlagModel, 0,
team == TEAM_RED ? cgs.media.redFlagShader[3] : cgs.media.blueFlagShader[3], origin, angles );
} else if ( cg_drawIcons.integer ) {
gitem_t *item = BG_FindItemForPowerup( team == TEAM_RED ? PW_REDFLAG : PW_BLUEFLAG );
if (item)
{
CG_DrawPic( x, y, w, h, cg_items[ ITEM_INDEX(item) ].icon );
}
}
}
/*
================
CG_DrawStatusBarHead
================
*/
/*
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 );
}
*/
/*
================
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; // no team
}
trap_R_SetColor( hcolor );
CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
trap_R_SetColor( NULL );
}
/*
================
CG_DrawAmmo
================
*/
static void CG_DrawAmmo(centity_t *cent)
{
float value;
float xLength;
playerState_t *ps;
int max,brightColor_i,darkColor_i,numColor_i;
ps = &cg.snap->ps;
value = ps->ammo[cent->currentState.weapon];
if (value < 0)
{
return;
}
interface_graphics[IG_AMMO_COUNT].max = value;
brightColor_i = CT_LTGOLD1;
darkColor_i = CT_DKGOLD1;
numColor_i = CT_LTGOLD1;
// Calc bar length
max = ps->ammo[cent->currentState.weapon];
if (max > 0)
{
xLength = 33 * (value/max);
}
else
{
max = 0;
xLength = 0;
}
// Armor empty section
interface_graphics[IG_AMMO_SLIDEREMPTY].x = 578 + xLength;
interface_graphics[IG_AMMO_SLIDEREMPTY].width = 33 - xLength;
// Armor full section
interface_graphics[IG_AMMO_SLIDERFULL].width = xLength;
interface_graphics[IG_AMMO_UPPER_BEGINCAP].color = darkColor_i;
interface_graphics[IG_AMMO_UPPER_ENDCAP].color = darkColor_i;
interface_graphics[IG_AMMO_LOWER_BEGINCAP].color = darkColor_i;
interface_graphics[IG_AMMO_LOWER_ENDCAP].color = darkColor_i;
interface_graphics[IG_AMMO_SLIDERFULL].color = brightColor_i;
interface_graphics[IG_AMMO_SLIDEREMPTY].color = darkColor_i;
interface_graphics[IG_AMMO_COUNT].color = numColor_i;
// Print it
CG_PrintInterfaceGraphics(IG_AMMO_START + 1,IG_AMMO_END);
}
/*
================
CG_DrawArmor
================
*/
static void CG_DrawArmor(centity_t *cent)
{
int max;
float value,xLength;
playerState_t *ps;
int lengthMax;
ps = &cg.snap->ps;
value = ps->stats[STAT_ARMOR];
interface_graphics[IG_ARMOR_COUNT].max = value;
if (interface_graphics[IG_ARMOR_COUNT].max <= ps->stats[STAT_MAX_HEALTH])
{
interface_graphics[IG_ARMOR_COUNT].color = CT_LTPURPLE1; //
interface_graphics[IG_ARMOR_SLIDERFULL].color = CT_LTPURPLE1; //
interface_graphics[IG_ARMOR_COUNT].style &= ~UI_PULSE; // Numbers
}
else
{
interface_graphics[IG_ARMOR_COUNT].color = CT_LTGREY; // Numbers
interface_graphics[IG_ARMOR_SLIDERFULL].color = CT_LTGREY; //
interface_graphics[IG_ARMOR_COUNT].style |= UI_PULSE; // Numbers
}
/*
if (cg.oldarmor < value)
{
cg.oldArmorTime = cg.time + 100;
}
cg.oldarmor = value;
if (cg.oldArmorTime < cg.time)
{ */
// interface_graphics[IG_ARMOR_COUNT].color = CT_LTPURPLE1; // Numbers
/* }
else
{
interface_graphics[IG_ARMOR_COUNT].color = CT_YELLOW; // Numbers
}
*/
max = ps->stats[STAT_MAX_HEALTH];
lengthMax = 73;
if (max > 0)
{
if (value > max)
{
xLength = lengthMax;
}
else
{
xLength = lengthMax * (value/max);
}
}
else
{
max = 0;
xLength = 0;
}
// Armor empty section
interface_graphics[IG_ARMOR_SLIDEREMPTY].x = 72 + xLength;
interface_graphics[IG_ARMOR_SLIDEREMPTY].width = lengthMax - xLength;
// Armor full section
interface_graphics[IG_ARMOR_SLIDERFULL].width = xLength;
CG_PrintInterfaceGraphics(IG_ARMOR_START + 1,IG_ARMOR_END);
}
/*
================
CG_DrawHealth
================
*/
static void CG_DrawHealth(centity_t *cent)
{
int max;
float value,xLength;
playerState_t *ps;
int lengthMax;
ps = &cg.snap->ps;
value = ps->stats[STAT_HEALTH];
// Changing colors on numbers
// if (cg.oldhealth < value)
// {
// cg.oldHealthTime = cg.time + 100;
// }
// cg.oldhealth = value;
// Is health changing?
// if (cg.oldHealthTime < cg.time)
// {
// interface_graphics[IG_HEALTH_COUNT].color = CT_LTBROWN1; // Numbers
// }
// else
// {
// }
interface_graphics[IG_HEALTH_COUNT].max = value;
if (interface_graphics[IG_HEALTH_COUNT].max <= ps->stats[STAT_MAX_HEALTH])
{
interface_graphics[IG_HEALTH_COUNT].color = CT_LTBROWN1; //
interface_graphics[IG_HEALTH_SLIDERFULL].color = CT_LTBROWN1; //
interface_graphics[IG_HEALTH_SLIDEREMPTY].color = CT_DKBROWN1; //
interface_graphics[IG_HEALTH_COUNT].style &= ~UI_PULSE; // Numbers
}
else
{
interface_graphics[IG_HEALTH_COUNT].color = CT_LTGREY; // Numbers
interface_graphics[IG_HEALTH_SLIDERFULL].color = CT_LTGREY; //
interface_graphics[IG_HEALTH_COUNT].style |= UI_PULSE; // Numbers
}
// Calculating size of health bar
max = ps->stats[STAT_MAX_HEALTH];
lengthMax = 73;
if (max > 0)
{
if (value < max)
{
xLength = lengthMax * (value/max);
}
else // So the graphic doesn't extend past the cap
{
xLength = lengthMax;
}
}
else
{
max = 0;
xLength = 0;
}
// Health empty section
interface_graphics[IG_HEALTH_SLIDEREMPTY].x = 72 + xLength;
interface_graphics[IG_HEALTH_SLIDEREMPTY].width = lengthMax - xLength;
// Health full section
interface_graphics[IG_HEALTH_SLIDERFULL].width = xLength;
// Print it
CG_PrintInterfaceGraphics(IG_HEALTH_START + 1,IG_HEALTH_END);
}
/*
================
CG_DrawStatusBar
================
*/
static void CG_DrawStatusBar( void )
{
centity_t *cent;
playerState_t *ps;
vec3_t angles;
int y=0;
vec4_t whiteA;
static float colors[4][4] =
{
{ 1, 0.69, 0, 1.0 } , // normal
{ 1.0, 0.2, 0.2, 1.0 }, // low health
{0.5, 0.5, 0.5, 1}, // weapon firing
{ 1, 1, 1, 1 } }; // health > 100
whiteA[0] = whiteA[1] = whiteA[2] = 1.0f; whiteA[3] = 0.3f;
if ( cg_drawStatus.integer == 0 ) {
return;
}
// draw the team background
CG_DrawTeamBackground( 0, 420, 640, 60, 0.33, 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
y = (SCREEN_HEIGHT - (4*ICON_SIZE) - 20);
if (cg.predictedPlayerState.powerups[PW_REDFLAG])
{ //fixme: move to powerup renderer? make it pulse?
// CG_FillRect( 5, y, ICON_SIZE*2, ICON_SIZE*2, whiteA);
CG_DrawFlagModel( 5, y, ICON_SIZE*2, ICON_SIZE*2, TEAM_RED );
}
else if (cg.predictedPlayerState.powerups[PW_BLUEFLAG])
{
// CG_FillRect( 5, y, ICON_SIZE*2, ICON_SIZE*2, whiteA);
CG_DrawFlagModel( 5, y, ICON_SIZE*2, ICON_SIZE*2, TEAM_BLUE );
}
// Do start
if (!cg.interfaceStartupDone)
{
CG_InterfaceStartup();
}
//
// ammo
//
if ( cent->currentState.weapon )
{
CG_DrawAmmo(cent);
}
//
// health
//
CG_DrawHealth(cent);
//
// armor
//
CG_DrawArmor(cent);
}
/*
================
CG_InterfaceStartup
================
*/
static void CG_InterfaceStartup()
{
// Turn on Health Graphics
if ((interface_graphics[IG_HEALTH_START].timer < cg.time) && (interface_graphics[IG_HEALTH_BEGINCAP].type == SG_OFF))
{
trap_S_StartLocalSound( cgs.media.interfaceSnd1, CHAN_LOCAL_SOUND );
interface_graphics[IG_HEALTH_BEGINCAP].type = SG_GRAPHIC;
interface_graphics[IG_HEALTH_BOX1].type = SG_GRAPHIC;
interface_graphics[IG_HEALTH_ENDCAP].type = SG_GRAPHIC;
}
// Turn on Armor Graphics
if ((interface_graphics[IG_ARMOR_START].timer < cg.time) && (interface_graphics[IG_ARMOR_BEGINCAP].type == SG_OFF))
{
if (interface_graphics[IG_ARMOR_BEGINCAP].type == SG_OFF)
{
trap_S_StartLocalSound( cgs.media.interfaceSnd1, CHAN_LOCAL_SOUND );
}
interface_graphics[IG_ARMOR_BEGINCAP].type = SG_GRAPHIC;
interface_graphics[IG_ARMOR_BOX1].type = SG_GRAPHIC;
interface_graphics[IG_ARMOR_ENDCAP].type = SG_GRAPHIC;
}
// Turn on Ammo Graphics
if (interface_graphics[IG_AMMO_START].timer < cg.time)
{
if (interface_graphics[IG_AMMO_UPPER_BEGINCAP].type == SG_OFF)
{
trap_S_StartLocalSound( cgs.media.interfaceSnd1, CHAN_LOCAL_SOUND );
interface_graphics[IG_GROW].type = SG_VAR;
interface_graphics[IG_GROW].timer = cg.time;
}
interface_graphics[IG_AMMO_UPPER_BEGINCAP].type = SG_GRAPHIC;
interface_graphics[IG_AMMO_UPPER_ENDCAP].type = SG_GRAPHIC;
interface_graphics[IG_AMMO_LOWER_BEGINCAP].type = SG_GRAPHIC;
interface_graphics[IG_AMMO_LOWER_ENDCAP].type = SG_GRAPHIC;
}
if (interface_graphics[IG_GROW].type == SG_VAR)
{
interface_graphics[IG_HEALTH_ENDCAP].x += 2;
interface_graphics[IG_ARMOR_ENDCAP].x += 2;
interface_graphics[IG_AMMO_UPPER_ENDCAP].x -= 1;
interface_graphics[IG_AMMO_LOWER_ENDCAP].x -= 1;
if (interface_graphics[IG_HEALTH_ENDCAP].x >= interface_graphics[IG_HEALTH_ENDCAP].max)
{
interface_graphics[IG_HEALTH_ENDCAP].x = interface_graphics[IG_HEALTH_ENDCAP].max;
interface_graphics[IG_ARMOR_ENDCAP].x = interface_graphics[IG_ARMOR_ENDCAP].max;
interface_graphics[IG_AMMO_UPPER_ENDCAP].x = interface_graphics[IG_AMMO_UPPER_ENDCAP].max;
interface_graphics[IG_AMMO_LOWER_ENDCAP].x = interface_graphics[IG_AMMO_LOWER_ENDCAP].max;
interface_graphics[IG_GROW].type = SG_OFF;
interface_graphics[IG_HEALTH_SLIDERFULL].type = SG_GRAPHIC;
interface_graphics[IG_HEALTH_SLIDEREMPTY].type = SG_GRAPHIC;
interface_graphics[IG_HEALTH_COUNT].type = SG_NUMBER;
interface_graphics[IG_ARMOR_SLIDERFULL].type = SG_GRAPHIC;
interface_graphics[IG_ARMOR_SLIDEREMPTY].type = SG_GRAPHIC;
interface_graphics[IG_ARMOR_COUNT].type = SG_NUMBER;
interface_graphics[IG_AMMO_SLIDERFULL].type = SG_GRAPHIC;
interface_graphics[IG_AMMO_SLIDEREMPTY].type = SG_GRAPHIC;
interface_graphics[IG_AMMO_COUNT].type = SG_NUMBER;
trap_S_StartLocalSound( cgs.media.interfaceSnd1, CHAN_LOCAL_SOUND );
cg.interfaceStartupDone = 1; // All done
}
interface_graphics[IG_GROW].timer = cg.time + 10;
}
cg.interfaceStartupTime = cg.time;
// kef -- init struct for post game awards
InitPostGameMenuStruct();
}
/*
===========================================================================================
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 );
UI_DrawProportionalString( 635, y, name, UI_RIGHT | UI_SMALLFONT, colorTable[CT_LTGOLD1] );
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 = UI_ProportionalStringWidth(s,UI_BIGFONT);
UI_DrawProportionalString(635 - w, y + 2, s, UI_BIGFONT, colorTable[CT_LTGOLD1]);
return y + BIGCHAR_HEIGHT + 10;
}
/*
==================
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 = UI_ProportionalStringWidth(s,UI_BIGFONT);
UI_DrawProportionalString(635 - w, y + 2, s, UI_BIGFONT, colorTable[CT_LTGOLD1]);
}
return y + BIGCHAR_HEIGHT + 10;
}
/*
=================
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 = UI_ProportionalStringWidth(s,UI_BIGFONT);
UI_DrawProportionalString(635 - w, y + 2, s, UI_BIGFONT, colorTable[CT_LTGOLD1]);
return y + BIGCHAR_HEIGHT + 10;
}
#define TINYPAD 1.25
/*
=================
CG_DrawTeamOverlay
=================
*/
#define TEAM_OVERLAY_MAXNAME_WIDTH 12
#define TEAM_OVERLAY_MAXLOCATION_WIDTH 16
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;
int ret_y;
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;
for (i = 0; i < numSortedTeamPlayers; 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 * TINYPAD);
if ( right )
x = 640 - w;
else
x = 0;
h = plyrs * (TINYCHAR_HEIGHT * TINYPAD);
if ( upper ) {
ret_y = y + h;
} else {
y -= h;
ret_y = y;
}
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) {
hcolor[0] = 1;
hcolor[1] = 0;
hcolor[2] = 0;
hcolor[3] = 0.33;
} else { // if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE )
hcolor[0] = 0;
hcolor[1] = 0;
hcolor[2] = 1;
hcolor[3] = 0.33;
}
trap_R_SetColor( hcolor );
CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
trap_R_SetColor( NULL );
for (i = 0; i < numSortedTeamPlayers; 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);
UI_DrawProportionalString( xx, y, ci->name, UI_TINYFONT, hcolor);
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);
UI_DrawProportionalString( xx, y, p, UI_TINYFONT, hcolor);
}
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 );
UI_DrawProportionalString( xx, y, st, UI_TINYFONT, hcolor);
// draw weapon icon
xx += (TINYCHAR_WIDTH * TINYPAD) * 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)) {
gitem_t *item = BG_FindItemForPowerup( j );
if (item)
{
CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT,
trap_R_RegisterShader( item->icon ) );
}
if (right) {
xx -= (TINYCHAR_WIDTH * TINYPAD);
} else {
xx += (TINYCHAR_WIDTH * TINYPAD);
}
}
}
y += (TINYCHAR_HEIGHT * TINYPAD);
}
}
return ret_y;
}
/*
=====================
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
=================
*/
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;
color[1] = 0;
color[2] = 1;
color[3] = 0.33;
s = va( "%2i", s2 );
// w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
w = UI_ProportionalStringWidth(s,UI_SMALLFONT) + 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);
UI_DrawProportionalString( x + 4, y, s, UI_SMALLFONT, colorTable[CT_LTGOLD1] );
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] );
}
#if 0
CG_RegisterItemVisuals( ITEM_INDEX(item) );
switch (cgs.blueflag) {
case 0 : // at base
// Draw the icon
CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, trap_R_RegisterShader( item->icon ) );
case 1 : // taken
CG_FillRect( x, y1-4, w, BIGCHAR_HEIGHT+8, color );
break;
case 2 : // droppped, grey background
color[0] = color[1] = color[2] = 1;
CG_FillRect( x, y1-4, w, BIGCHAR_HEIGHT+8, color );
break;
// Other values won't draw (-1 is disabled)
}
#endif
}
}
color[0] = 1;
color[1] = 0;
color[2] = 0;
color[3] = 0.33;
s = va( "%2i", s1 );
// w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
w = UI_ProportionalStringWidth(s,UI_SMALLFONT) + 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);
UI_DrawProportionalString( x + 4, y, s, UI_SMALLFONT, colorTable[CT_LTGOLD1] );
if ( cgs.gametype == GT_CTF ) {
// Display flag status
item = BG_FindItemForPowerup( PW_REDFLAG );
if (item)
{
y1 = y - BIGCHAR_HEIGHT - 8;
if( cgs.redflag >= 0 && cgs.redflag <= 2 )
{
CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.redFlagShader[cgs.redflag] );
}
#if 0
CG_RegisterItemVisuals( ITEM_INDEX(item) );
switch (cgs.redflag)
{
case 0 : // at base
// Draw the icon
CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, trap_R_RegisterShader( item->icon ) );
break;
case 1 : // taken
CG_FillRect( x, y1-4, w, BIGCHAR_HEIGHT+8, color );
break;
case 2 : // droppped, grey background
color[0] = color[1] = color[2] = 1;
CG_FillRect( x, y1-4, w, BIGCHAR_HEIGHT+8, color );
break;
// Other values won't draw (-1 is disabled)
}
#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;
w = UI_ProportionalStringWidth(s,UI_SMALLFONT) + 8;
x -= w;
// CG_DrawBigString( x + 4, y, s, 1.0F);
UI_DrawProportionalString( x + 4, y, s, UI_SMALLFONT, colorTable[CT_LTGOLD1] );
}
}
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;
w = UI_ProportionalStringWidth(s,UI_SMALLFONT) + 8;
x -= w;
if ( !spectator && score == s2 && score != s1 )
{
color[0] = 1;
color[1] = 0;
color[2] = 0;
color[3] = 0.33;
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.5;
color[1] = 0.5;
color[2] = 0.5;
color[3] = 0.33;
CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
}
// CG_DrawBigString( x + 4, y, s, 1.0F);
UI_DrawProportionalString( x + 4, y, s, UI_SMALLFONT, colorTable[CT_LTGOLD1] );
}
// first place
if ( s1 != SCORE_NOT_PRESENT )
{
s = va( "%2i", s1 );
// w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
w = UI_ProportionalStringWidth(s,UI_SMALLFONT) + 8;
x -= w;
if ( !spectator && score == s1 )
{
color[0] = 0;
color[1] = 0;
color[2] = 1;
color[3] = 0.33;
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.5;
color[1] = 0.5;
color[2] = 0.5;
color[3] = 0.33;
CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color );
}
// CG_DrawBigString( x + 4, y, s, 1.0F);
UI_DrawProportionalString( x + 4, y, s, UI_SMALLFONT, colorTable[CT_LTGOLD1] );
}
if ( cgs.fraglimit )
{
s = va( "%2i", cgs.fraglimit );
// w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
w = UI_ProportionalStringWidth(s,UI_SMALLFONT) + 8;
x -= w;
// CG_DrawBigString( x + 4, y, s, 1.0F);
UI_DrawProportionalString( x + 4, y, s, UI_SMALLFONT, colorTable[CT_LTGOLD1] );
}
}
return y1 - 8;
}
/*
================
CG_DrawPowerups
================
*/
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.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 } };
int hasHoldable;
hasHoldable = cg.snap->ps.stats[STAT_HOLDABLE_ITEM];
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++ ) {
// Don't draw almost timed out powerups if we have more than 3 and a holdable item
if (!(hasHoldable && i<active-2))
{
item = BG_FindItemForPowerup( sorted[i] );
if (NULL == item)
{
continue;
}
color = 1;
y -= ICON_SIZE;
trap_R_SetColor( colors[color] );
// CG_DrawField( x, y, 2, sortedTime[ i ] / 1000 );
CG_DrawNumField (x,y,2,sortedTime[ i ] / 1000,16,32,NUM_FONT_BIG);
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;
}
/*
=====================
CG_DrawLowerRight
=====================
*/
static void CG_DrawLowerRight( void ) {
float y;
y = LOWEROVERLAY_Y;
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2 ) {
y = CG_DrawTeamOverlay( y, qtrue, qfalse );
}
y = CG_DrawScores( y );
y = CG_DrawPowerups( y );
}
/*
===================
CG_DrawPickupItem
===================
*/
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 );
UI_DrawProportionalString( ICON_SIZE + 16, y + (ICON_SIZE/2 - BIGCHAR_HEIGHT/2), bg_itemlist[ value ].pickup_name, UI_SMALLFONT, fadeColor);
// 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;
}
/*
=====================
CG_DrawLowerLeft
=====================
*/
static void CG_DrawLowerLeft( void ) {
float y;
y = LOWEROVERLAY_Y;
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 3 ) {
y = CG_DrawTeamOverlay( y, qfalse, qfalse );
}
y = CG_DrawPickupItem( y );
}
//===========================================================================================
/*
=================
CG_DrawTeamInfo
=================
*/
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;
hcolor[1] = 0;
hcolor[2] = 0;
hcolor[3] = 0.33;
} else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
hcolor[0] = 0;
hcolor[1] = 0;
hcolor[2] = 1;
hcolor[3] = 0.33;
} else {
hcolor[0] = 0;
hcolor[1] = 1;
hcolor[2] = 0;
hcolor[3] = 0.33;
}
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.0;
hcolor[3] = 1.0;
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 );
UI_DrawProportionalString( CHATLOC_X + TINYCHAR_WIDTH,
CHATLOC_Y - (cgs.teamChatPos - i)*TINYCHAR_HEIGHT,
cgs.teamChatMsgs[i % chatHeight], UI_TINYFONT, hcolor);
}
}
}
/*
===================
CG_DrawHoldableItem
===================
*/
static void CG_DrawHoldableItem( void ) {
int value;
value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM];
if ( value )
{
CG_RegisterItemVisuals( value );
if (cg.snap->ps.stats[STAT_USEABLE_PLACED])
{
CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2, ICON_SIZE, ICON_SIZE, cgs.media.detpackPlacedIcon );
}
else
{
CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
}
}
}
/*
===================
CG_DrawReward
===================
*/
static void CG_DrawReward( void ) {
float *color;
int i;
float x, y;
if ( !cg_drawRewards.integer ) {
return;
}
color = CG_FadeColor( cg.rewardTime, REWARD_TIME );
if ( !color ) {
return;
}
trap_R_SetColor( color );
y = 56;
x = 320 - cg.rewardCount * ICON_SIZE/2;
for ( i = 0 ; i < cg.rewardCount ; i++ ) {
CG_DrawPic( x, y, ICON_SIZE-4, ICON_SIZE-4, cg.rewardShader );
x += ICON_SIZE;
}
trap_R_SetColor( NULL );
}
/*
===============================================================================
LAGOMETER
===============================================================================
*/
#define LAG_SAMPLES 128
typedef struct {
int frameSamples[LAG_SAMPLES];
int frameCount;
int snapshotFlags[LAG_SAMPLES];
int snapshotSamples[LAG_SAMPLES];
int snapshotCount;
} lagometer_t;
lagometer_t lagometer;
/*
==============
CG_AddLagometerFrameInfo
Adds the current interpolate / extrapolate bar for this frame
==============
*/
void CG_AddLagometerFrameInfo( void ) {
int offset;
offset = cg.time - cg.latestSnapshotTime;
lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1) ] = offset;
lagometer.frameCount++;
}
/*
==============
CG_AddLagometerSnapshotInfo
Each time a snapshot is received, log its ping time and
the number of snapshots that were dropped before it.
Pass NULL for a dropped packet.
==============
*/
void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) {
// dropped packet
if ( !snap ) {
lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = -1;
lagometer.snapshotCount++;
return;
}
// add this snapshot's info
lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->ping;
lagometer.snapshotFlags[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->snapFlags;
lagometer.snapshotCount++;
}
/*
==============
CG_DrawDisconnect
Should we draw something differnet for long lag vs no packets?
==============
*/
static void CG_DrawDisconnect( void ) {
float x, y;
int cmdNum;
usercmd_t cmd;
const char *s;
int w;
// draw the phone jack if we are completely past our buffers
cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1;
trap_GetUserCmd( cmdNum, &cmd );
if ( cmd.serverTime <= cg.snap->ps.commandTime ||
cmd.serverTime > cg.time || // special check for map_restart
cmd.serverTime < cg.snap->ps.introTime) // special check for holointro
{
return;
}
// also add text in center of screen
s = ingame_text[IGT_CONNECTIONINTERRUPTED];
// w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
// CG_DrawBigString( 320 - w/2, 100, s, 1.0F);
UI_DrawProportionalString(320 - w/2, 100, s, UI_BIGFONT, colorTable[CT_LTGOLD1]);
// blink the icon
if ( ( cg.time >> 9 ) & 1 ) {
return;
}
x = 640 - 48;
y = 480 - 48;
CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga" ) );
}
#define MAX_LAGOMETER_PING 900
#define MAX_LAGOMETER_RANGE 300
/*
==============
CG_DrawLagometer
==============
*/
static void CG_DrawLagometer( void ) {
int a, x, y, i;
float v;
float ax, ay, aw, ah, mid, range;
int color;
float vscale;
if ( !cg_lagometer.integer || cgs.localServer ) {
CG_DrawDisconnect();
return;
}
//
// draw the graph
//
x = 640 - 48 - 75; //move it left of the ammo numbers
y = 480 - 48;
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_syncronousClients.integer ) {
// CG_DrawBigString( ax, ay, "snc", 1.0 );
UI_DrawProportionalString(ax, ay, "snc", UI_BIGFONT, colorTable[CT_LTGOLD1]);
}
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;
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 < 40; l++ ) {
if ( !start[l] || start[l] == '\n' ) {
break;
}
linebuffer[l] = start[l];
}
linebuffer[l] = 0;
// w = cg.centerPrintCharWidth * CG_DrawStrlen( linebuffer );
w = UI_ProportionalStringWidth(linebuffer,UI_BIGFONT);
x = ( SCREEN_WIDTH - w ) / 2;
// CG_DrawStringExt( x, y, linebuffer, color, qfalse, qtrue,
// cg.centerPrintCharWidth, (int)(cg.centerPrintCharWidth * 1.5), 0 );
UI_DrawProportionalString( x, y, linebuffer, UI_BIGFONT|UI_DROPSHADOW, color);
y += cg.centerPrintCharWidth * 1.5;
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;
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 );
hShader = cgs.media.crosshairShader[ cg_drawCrosshair.integer % 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, 8192, 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[MAX_QPATH];
int team;
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;
}
color[3] *= 0.9;
Q_strncpyz (name, cgs.clientinfo[ cg.crosshairClientNum ].name, sizeof (name) );
// Draw in red if red team, blue if blue team
if (cgs.gametype >= GT_TEAM)
{
Q_CleanStr(name);
team = cgs.clientinfo[ cg.crosshairClientNum ].team;
if (team==TEAM_RED)
{
color[0] = colorRed[0];
color[1] = colorRed[1];
color[2] = colorRed[2];
}
else
{
color[0] = colorBlue[0];
color[1] = colorBlue[1];
color[2] = colorBlue[2];
}
}
else
{
color[0] = colorTable[CT_YELLOW][0];
color[1] = colorTable[CT_YELLOW][1];
color[2] = colorTable[CT_YELLOW][2];
}
UI_DrawProportionalString(320,170,name,UI_CENTER|UI_SMALLFONT,color);
}
//==============================================================================
/*
=================
CG_DrawSpectator
=================
*/
static void CG_DrawSpectator(void) {
// CG_DrawBigString(320 - 9 * 8, 440, ingame_text[IGT_SPECTATOR], 1.0F);
UI_DrawProportionalString(SCREEN_WIDTH/2, SCREEN_HEIGHT - ((BIGCHAR_HEIGHT * 1.50) * 2) , ingame_text[IGT_SPECTATOR], UI_BIGFONT|UI_CENTER, colorTable[CT_LTGOLD1]);
if ( cgs.gametype == GT_TOURNAMENT ) {
// CG_DrawBigString(320 - 15 * 8, 460, ingame_text[IGT_WAITINGTOPLAY], 1.0F);
UI_DrawProportionalString(SCREEN_WIDTH/2, SCREEN_HEIGHT - (BIGCHAR_HEIGHT * 1.5), ingame_text[IGT_WAITINGTOPLAY], UI_BIGFONT|UI_CENTER, colorTable[CT_LTGOLD1]);
}
if ( cgs.gametype == GT_TEAM || cgs.gametype == GT_CTF ) {
// CG_DrawBigString(320 - 25 * 8, 460, ingame_text[IGT_USEDTEAMMENU], 1.0F);
UI_DrawProportionalString(SCREEN_WIDTH/2, SCREEN_HEIGHT - (BIGCHAR_HEIGHT * 1.5), ingame_text[IGT_USEDTEAMMENU], UI_BIGFONT|UI_CENTER, colorTable[CT_LTGOLD1]);
}
}
/*
=================
CG_DrawVote
=================
*/
static void CG_DrawVote(void) {
char *s;
int sec;
if ( !cgs.voteTime ) {
return;
}
// play a talk beep whenever it is modified
if ( cgs.voteModified ) {
cgs.voteModified = qfalse;
trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
}
sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000;
if ( sec < 0 ) {
sec = 0;
}
s = va("%s(%i):%s %s(F1):%i %s(F2):%i", ingame_text[IGT_VOTE],sec, cgs.voteString,ingame_text[IGT_YES], cgs.voteYes,ingame_text[IGT_NO] ,cgs.voteNo);
// CG_DrawSmallStringColor( 0, 58, s, colorTable[CT_YELLOW] );
UI_DrawProportionalString( 0, 58, s, UI_SMALLFONT, colorTable[CT_YELLOW]);
}
/*
=================
CG_DrawIntermission
=================
*/
static void CG_DrawIntermission( void ) {
if (0)// cgs.gametype == GT_SINGLE_PLAYER )
{
CG_DrawCenterString();
return;
}
cg.scoreFadeTime = cg.time;
CG_DrawScoreboard();
}
/*
=================
CG_DrawFollow
=================
*/
static qboolean CG_DrawFollow( void ) {
float y;
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;
y = 16;
UI_DrawProportionalString((SCREEN_WIDTH/2), y, ingame_text[IGT_FOLLOWING], UI_BIGFONT|UI_CENTER, colorTable[CT_LTGOLD1]);
name = cgs.clientinfo[ cg.snap->ps.clientNum ].name;
y += (BIGCHAR_HEIGHT * 1.25);
UI_DrawProportionalString( (SCREEN_WIDTH/2), 40, name, UI_BIGFONT|UI_CENTER, color);
return qtrue;
}
/*
=================
CG_DrawAmmoWarning
=================
*/
static void CG_DrawAmmoWarning( void )
{
const char *s;
if ( cg_drawAmmoWarning.integer == 0 )
{
return;
}
if ( !cg.lowAmmoWarning )
{
return;
}
if ( cg.lowAmmoWarning >= 2 )
{
s = ingame_text[IGT_OUTOFAMMO];
} else
{
s = ingame_text[IGT_LOWAMMO];
}
UI_DrawProportionalString(320, 64, s, UI_SMALLFONT | UI_CENTER, colorTable[CT_LTGOLD1]);
}
/*
=================
CG_DrawWarmup
=================
*/
static void CG_DrawWarmup( void ) {
int w;
int sec;
int i;
clientInfo_t *ci1, *ci2;
int cw;
const char *s;
sec = cg.warmup;
if ( !sec ) {
return;
}
if ( sec < 0 ) {
s = ingame_text[IGT_WAITINGFORPLAYERS];
// w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
// CG_DrawBigString(320 - w / 2, 40, s, 1.0F);
UI_DrawProportionalString(320 - w / 2, 40, s, UI_BIGFONT, colorTable[CT_LTGOLD1]);
cg.warmupCount = 0;
return;
}
if (cgs.gametype == GT_TOURNAMENT) {
// find the two active players
ci1 = NULL;
ci2 = NULL;
for ( i = 0 ; i < cgs.maxclients ; i++ ) {
if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_FREE ) {
if ( !ci1 ) {
ci1 = &cgs.clientinfo[i];
} else {
ci2 = &cgs.clientinfo[i];
}
}
}
if ( ci1 && ci2 ) {
s = va( "%s vs %s", ci1->name, ci2->name );
// w = CG_DrawStrlen( s );
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
if ( w > 640 / BIGCHAR_WIDTH ) {
cw = 640 / w;
} else {
cw = BIGCHAR_WIDTH;
}
// CG_DrawStringExt( 320 - w * cw/2, 20,s, colorWhite,
// qfalse, qtrue, cw, (int)(cw * 1.5), 0 );
UI_DrawProportionalString( (SCREEN_WIDTH/2), 20,s, UI_BIGFONT|UI_CENTER, colorTable[CT_LTGOLD1]);
}
} else {
if ( cgs.gametype == GT_FFA ) {
s = ingame_text[IGT_GAME_FREEFORALL];
} else if ( cgs.gametype == GT_TEAM ) {
s =ingame_text[IGT_GAME_TEAMHOLOMATCH];
} else if ( cgs.gametype == GT_CTF ) {
s = ingame_text[IGT_GAME_CAPTUREFLAG];
} else {
s = "";
}
// w = CG_DrawStrlen( s );
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
if ( w > 640 / BIGCHAR_WIDTH ) {
cw = 640 / w;
} else {
cw = BIGCHAR_WIDTH;
}
// CG_DrawStringExt( 320 - w * cw/2, 20,s, colorWhite,
// qfalse, qtrue, cw, (int)(cw * 1.5), 0 );
UI_DrawProportionalString((SCREEN_WIDTH/2) , 20,s, UI_BIGFONT|UI_CENTER, colorTable[CT_LTGOLD1]);
}
sec = ( sec - cg.time ) / 1000;
if ( sec < 0 ) {
sec = 0;
}
s = va( "%s: %i",ingame_text[IGT_STARTSIN], 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;
}
}
switch ( cg.warmupCount ) {
case 0:
cw = 28;
break;
case 1:
cw = 24;
break;
case 2:
cw = 20;
break;
default:
cw = 16;
break;
}
// w = CG_DrawStrlen( s );
// CG_DrawStringExt( 320 - w * cw/2, 70, s, colorWhite,
// qfalse, qtrue, cw, (int)(cw * 1.5), 0 );
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
UI_DrawProportionalString( (SCREEN_WIDTH/2), 70, s, UI_BIGFONT|UI_CENTER, colorTable[CT_LTGOLD1]);
}
/*
================
CG_DrawZoomMask
================
*/
static void CG_DrawZoomMask( void )
{
float amt = 1, size, val, start_x, start_y;
int width, height, i;
vec4_t color1;
// Calc where to place the zoom mask...all calcs are based off of a virtual 640x480 screen
size = cg_viewsize.integer;
width = 640 * size * 0.01;
width &= ~1;
height = 480 * size * 0.01;
height &= ~1;
start_x = ( 640 - width ) * 0.5;
start_y = ( 480 - height ) * 0.5;
if ( cg.zoomed )
{
// Smoothly fade in..Turn this off for now since the zoom is set to snap to 30% or so...fade looks a bit weird when it does that
if ( cg.time - cg.zoomTime <= ZOOM_OUT_TIME ) {
amt = ( cg.time - cg.zoomTime ) / ZOOM_OUT_TIME;
}
// Fade mask in
for ( i = 0; i < 4; i++ ) {
color1[i] = amt;
}
// Set fade color
trap_R_SetColor( color1 );
CG_DrawPic( start_x, start_y, width, height, cgs.media.zoomMaskShader );
start_x = 210;
start_y = 80;
CG_DrawPic( 320 + start_x, 241, 35, -170, cgs.media.zoomBarShader);
CG_DrawPic( 320 - start_x, 241, -35, -170, cgs.media.zoomBarShader);
CG_DrawPic( 320 + start_x, 239, 35, 170, cgs.media.zoomBarShader);
CG_DrawPic( 320 - start_x, 239, -35, 170, cgs.media.zoomBarShader);
// Calculate a percent and clamp it
val = 26 - ( cg_fov.value - cg_zoomFov.value ) / ( cg_fov.value - MAX_ZOOM_FOV ) * 26;
if ( val > 17.0f )
val = 17.0f;
else if ( val < 0.0f )
val = 0.0f;
// pink
color1[0] = 0.85f;
color1[1] = 0.55f;
color1[2] = 0.75f;
color1[3] = 1.0f;
CG_DrawPic( 320 + start_x + 12, 245, 10, 108, cgs.media.zoomInsertShader );
CG_DrawPic( 320 + start_x + 12, 235, 10, -108, cgs.media.zoomInsertShader );
CG_DrawPic( 320 - start_x - 12, 245, -10, 108, cgs.media.zoomInsertShader );
CG_DrawPic( 320 - start_x - 12, 235, -10, -108, cgs.media.zoomInsertShader );
trap_R_SetColor( color1 );
i = ((int)val) * 6;
CG_DrawPic( 320 + start_x + 10, 230 - i, 12, 5, cgs.media.ammoslider );
CG_DrawPic( 320 + start_x + 10, 251 + i, 12, -5, cgs.media.ammoslider );
CG_DrawPic( 320 - start_x - 10, 230 - i, -12, 5, cgs.media.ammoslider );
CG_DrawPic( 320 - start_x - 10, 251 + i, -12, -5, cgs.media.ammoslider );
// Convert zoom and view axis into some numbers to throw onto the screen
CG_DrawNumField( 468, 100, 5, cg_zoomFov.value * 1000 + 9999, 18, 10 ,NUM_FONT_BIG );
CG_DrawNumField( 468, 120, 5, cg.refdef.viewaxis[0][0] * 9999 + 20000, 18, 10,NUM_FONT_BIG );
CG_DrawNumField( 468, 140, 5, cg.refdef.viewaxis[0][1] * 9999 + 20000, 18, 10,NUM_FONT_BIG );
CG_DrawNumField( 468, 160, 5, cg.refdef.viewaxis[0][2] * 9999 + 20000, 18, 10,NUM_FONT_BIG );
// Is it time to draw the little max zoom arrows?
if ( val < 0.2f )
{
amt = sin( cg.time * 0.03 ) * 0.5 + 0.5;
color1[0] = 0.592156f * amt;
color1[1] = 0.592156f * amt;
color1[2] = 0.850980f * amt;
color1[3] = 1.0f * amt;
trap_R_SetColor( color1 );
CG_DrawPic( 320 + start_x, 240 - 6, 16, 12, cgs.media.zoomArrowShader );
CG_DrawPic( 320 - start_x, 240 - 6, -16, 12, cgs.media.zoomArrowShader );
}
}
else
{
if ( cg.time - cg.zoomTime <= ZOOM_OUT_TIME )
{
amt = 1.0f - ( cg.time - cg.zoomTime ) / ZOOM_OUT_TIME;
// Fade mask away
for ( i = 0; i < 4; i++ ) {
color1[i] = amt;
}
trap_R_SetColor( color1 );
CG_DrawPic( start_x, start_y, width, height, cgs.media.zoomMaskShader );
}
}
}
//==================================================================================
/*
=================
CG_Draw2D
=================
*/
static void CG_Draw2D( void ) {
// if we are taking a levelshot for the menu, don't draw anything
if ( cg.levelShot ) {
return;
}
if ( cg_draw2D.integer == 0 ) {
return;
}
if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
#ifndef FINAL_BUILD
CG_DrawUpperRight();
#endif
CG_DrawIntermission();
return;
}
if ( !cg.renderingThirdPerson )
{
CG_DrawZoomMask();
}
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
// CG_DrawSpectator();
CG_DrawCrosshair();
CG_DrawCrosshairNames();
} else {
// don't draw any status if dead
if ( cg.snap->ps.stats[STAT_HEALTH] > 0 ) {
CG_DrawStatusBar();
CG_DrawAmmoWarning();
CG_DrawCrosshair();
CG_DrawCrosshairNames();
CG_DrawWeaponSelect();
CG_DrawHoldableItem();
CG_DrawReward();
}
if ( cgs.gametype >= GT_TEAM ) {
CG_DrawTeamInfo();
}
}
CG_DrawVote();
CG_DrawLagometer();
CG_DrawUpperRight();
CG_DrawLowerRight();
CG_DrawLowerLeft();
if ( !CG_DrawFollow() ) {
CG_DrawWarmup();
}
// don't draw center string if scoreboard is up
if ( !CG_DrawScoreboard() ) {
CG_DrawCenterString();
}
// kef -- need the "use TEAM menu to play" message to draw on top of the bottom bar of scoreboard
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR )
{
CG_DrawSpectator();
}
}
/*
=====================
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();
}