jedioutcast/CODE-mp/cgame/cg_draw.c
2013-04-04 13:01:17 -05:00

4078 lines
90 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 "../ui/ui_shared.h"
qboolean CG_WorldCoordToScreenCoordFloat(vec3_t worldCoord, float *x, float *y);
qboolean CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle );
// used for scoreboard
extern displayContextDef_t cgDC;
menuDef_t *menuScoreboard = NULL;
vec4_t bluehudtint = {0.5, 0.5, 1.0, 1.0};
vec4_t redhudtint = {1.0, 0.5, 0.5, 1.0};
float *hudTintColor;
int sortedTeamPlayers[TEAM_MAXOVERLAY];
int numSortedTeamPlayers;
int lastvalidlockdif;
extern float zoomFov; //this has to be global client-side
char systemChat[256];
char teamChat1[256];
char teamChat2[256];
char *showPowersName[] =
{
"Heal",//FP_HEAL
"Jump",//FP_LEVITATION
"Speed",//FP_SPEED
"Push",//FP_PUSH
"Pull",//FP_PULL
"Mind Trick",//FP_TELEPTAHY
"Grip",//FP_GRIP
"Lightning",//FP_LIGHTNING
"Dark Rage",//FP_RAGE
"Protect",//FP_PROTECT
"Absorb",//FP_ABSORB
"Team Heal",//FP_TEAM_HEAL
"Team Replenish",//FP_TEAM_FORCE
"Drain",//FP_DRAIN
"Sight",//FP_SEE
"Saber Attack",//FP_SABERATTACK
"Saber Defend",//FP_SABERDEFEND
"Saber Throw",//FP_SABERTHROW
NULL
};
int MenuFontToHandle(int iMenuFont)
{
switch (iMenuFont)
{
case FONT_SMALL: return cgDC.Assets.qhSmallFont;
case FONT_MEDIUM: return cgDC.Assets.qhMediumFont;
case FONT_LARGE: return cgDC.Assets.qhBigFont;
}
return cgDC.Assets.qhMediumFont;
}
int CG_Text_Width(const char *text, float scale, int iMenuFont)
{
int iFontIndex = MenuFontToHandle(iMenuFont);
return trap_R_Font_StrLenPixels(text, iFontIndex, scale);
}
int CG_Text_Height(const char *text, float scale, int iMenuFont)
{
int iFontIndex = MenuFontToHandle(iMenuFont);
return trap_R_Font_HeightPixels(iFontIndex, scale);
}
#include "../qcommon/qfiles.h" // for STYLE_BLINK etc
void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style, int iMenuFont)
{
int iStyleOR = 0;
int iFontIndex = MenuFontToHandle(iMenuFont);
switch (style)
{
case ITEM_TEXTSTYLE_NORMAL: iStyleOR = 0;break; // JK2 normal text
case ITEM_TEXTSTYLE_BLINK: iStyleOR = STYLE_BLINK;break; // JK2 fast blinking
case ITEM_TEXTSTYLE_PULSE: iStyleOR = STYLE_BLINK;break; // JK2 slow pulsing
case ITEM_TEXTSTYLE_SHADOWED: iStyleOR = (int)STYLE_DROPSHADOW;break; // JK2 drop shadow ( need a color for this )
case ITEM_TEXTSTYLE_OUTLINED: iStyleOR = (int)STYLE_DROPSHADOW;break; // JK2 drop shadow ( need a color for this )
case ITEM_TEXTSTYLE_OUTLINESHADOWED: iStyleOR = (int)STYLE_DROPSHADOW;break; // JK2 drop shadow ( need a color for this )
case ITEM_TEXTSTYLE_SHADOWEDMORE: iStyleOR = (int)STYLE_DROPSHADOW;break; // JK2 drop shadow ( need a color for this )
}
trap_R_Font_DrawString( x, // int ox
y, // int oy
text, // const char *text
color, // paletteRGBA_c c
iStyleOR | iFontIndex, // const int iFontHandle
!limit?-1:limit, // iCharLimit (-1 = none)
scale // const float scale = 1.0f
);
}
/*
qboolean CG_WorldCoordToScreenCoord(vec3_t worldCoord, int *x, int *y)
Take any world coord and convert it to a 2D virtual 640x480 screen coord
*/
/*
qboolean CG_WorldCoordToScreenCoordFloat(vec3_t worldCoord, float *x, float *y)
{
int xcenter, ycenter;
vec3_t local, transformed;
// xcenter = cg.refdef.width / 2;//gives screen coords adjusted for resolution
// ycenter = cg.refdef.height / 2;//gives screen coords adjusted for resolution
//NOTE: did it this way because most draw functions expect virtual 640x480 coords
// and adjust them for current resolution
xcenter = 640 / 2;//gives screen coords in virtual 640x480, to be adjusted when drawn
ycenter = 480 / 2;//gives screen coords in virtual 640x480, to be adjusted when drawn
VectorSubtract (worldCoord, cg.refdef.vieworg, local);
transformed[0] = DotProduct(local,vright);
transformed[1] = DotProduct(local,vup);
transformed[2] = DotProduct(local,vfwd);
// Make sure Z is not negative.
if(transformed[2] < 0.01)
{
return qfalse;
}
// Simple convert to screen coords.
float xzi = xcenter / transformed[2] * (90.0/cg.refdef.fov_x);
float yzi = ycenter / transformed[2] * (90.0/cg.refdef.fov_y);
*x = xcenter + xzi * transformed[0];
*y = ycenter - yzi * transformed[1];
return qtrue;
}
qboolean CG_WorldCoordToScreenCoord( vec3_t worldCoord, int *x, int *y )
{
float xF, yF;
qboolean retVal = CG_WorldCoordToScreenCoordFloat( worldCoord, &xF, &yF );
*x = (int)xF;
*y = (int)yF;
return retVal;
}
*/
/*
================
CG_DrawZoomMask
================
*/
static void CG_DrawZoomMask( void )
{
vec4_t color1;
float level;
static qboolean flip = qtrue;
// int ammo = cg_entities[0].gent->client->ps.ammo[weaponData[cent->currentState.weapon].ammoIndex];
float cx, cy;
// int val[5];
float max, fi;
// Check for Binocular specific zooming since we'll want to render different bits in each case
if ( cg.predictedPlayerState.zoomMode == 2 )
{
int val, i;
float off;
// zoom level
level = (float)(80.0f - cg.predictedPlayerState.zoomFov) / 80.0f;
// ...so we'll clamp it
if ( level < 0.0f )
{
level = 0.0f;
}
else if ( level > 1.0f )
{
level = 1.0f;
}
// Using a magic number to convert the zoom level to scale amount
level *= 162.0f;
// draw blue tinted distortion mask, trying to make it as small as is necessary to fill in the viewable area
trap_R_SetColor( colorTable[CT_WHITE] );
CG_DrawPic( 34, 48, 570, 362, cgs.media.binocularStatic );
// Black out the area behind the numbers
trap_R_SetColor( colorTable[CT_BLACK]);
CG_DrawPic( 212, 367, 200, 40, cgs.media.whiteShader );
// Numbers should be kind of greenish
color1[0] = 0.2f;
color1[1] = 0.4f;
color1[2] = 0.2f;
color1[3] = 0.3f;
trap_R_SetColor( color1 );
// Draw scrolling numbers, use intervals 10 units apart--sorry, this section of code is just kind of hacked
// up with a bunch of magic numbers.....
val = ((int)((cg.refdefViewAngles[YAW] + 180) / 10)) * 10;
off = (cg.refdefViewAngles[YAW] + 180) - val;
for ( i = -10; i < 30; i += 10 )
{
val -= 10;
if ( val < 0 )
{
val += 360;
}
// we only want to draw the very far left one some of the time, if it's too far to the left it will
// poke outside the mask.
if (( off > 3.0f && i == -10 ) || i > -10 )
{
// draw the value, but add 200 just to bump the range up...arbitrary, so change it if you like
CG_DrawNumField( 155 + i * 10 + off * 10, 374, 3, val + 200, 24, 14, NUM_FONT_CHUNKY, qtrue );
CG_DrawPic( 245 + (i-1) * 10 + off * 10, 376, 6, 6, cgs.media.whiteShader );
}
}
CG_DrawPic( 212, 367, 200, 28, cgs.media.binocularOverlay );
color1[0] = sin( cg.time * 0.01f ) * 0.5f + 0.5f;
color1[0] = color1[0] * color1[0];
color1[1] = color1[0];
color1[2] = color1[0];
color1[3] = 1.0f;
trap_R_SetColor( color1 );
CG_DrawPic( 82, 94, 16, 16, cgs.media.binocularCircle );
// Flickery color
color1[0] = 0.7f + crandom() * 0.1f;
color1[1] = 0.8f + crandom() * 0.1f;
color1[2] = 0.7f + crandom() * 0.1f;
color1[3] = 1.0f;
trap_R_SetColor( color1 );
CG_DrawPic( 0, 0, 640, 480, cgs.media.binocularMask );
CG_DrawPic( 4, 282 - level, 16, 16, cgs.media.binocularArrow );
// The top triangle bit randomly flips
if ( flip )
{
CG_DrawPic( 330, 60, -26, -30, cgs.media.binocularTri );
}
else
{
CG_DrawPic( 307, 40, 26, 30, cgs.media.binocularTri );
}
if ( random() > 0.98f && ( cg.time & 1024 ))
{
flip = !flip;
}
}
else if ( cg.predictedPlayerState.zoomMode)
{
// disruptor zoom mode
level = (float)(50.0f - zoomFov) / 50.0f;//(float)(80.0f - zoomFov) / 80.0f;
// ...so we'll clamp it
if ( level < 0.0f )
{
level = 0.0f;
}
else if ( level > 1.0f )
{
level = 1.0f;
}
// Using a magic number to convert the zoom level to a rotation amount that correlates more or less with the zoom artwork.
level *= 103.0f;
// Draw target mask
trap_R_SetColor( colorTable[CT_WHITE] );
CG_DrawPic( 0, 0, 640, 480, cgs.media.disruptorMask );
// apparently 99.0f is the full zoom level
if ( level >= 99 )
{
// Fully zoomed, so make the rotating insert pulse
color1[0] = 1.0f;
color1[1] = 1.0f;
color1[2] = 1.0f;
color1[3] = 0.7f + sin( cg.time * 0.01f ) * 0.3f;
trap_R_SetColor( color1 );
}
// Draw rotating insert
CG_DrawRotatePic2( 320, 240, 640, 480, -level, cgs.media.disruptorInsert );
// Increase the light levels under the center of the target
// CG_DrawPic( 198, 118, 246, 246, cgs.media.disruptorLight );
// weirdness.....converting ammo to a base five number scale just to be geeky.
/* val[0] = ammo % 5;
val[1] = (ammo / 5) % 5;
val[2] = (ammo / 25) % 5;
val[3] = (ammo / 125) % 5;
val[4] = (ammo / 625) % 5;
color1[0] = 0.2f;
color1[1] = 0.55f + crandom() * 0.1f;
color1[2] = 0.5f + crandom() * 0.1f;
color1[3] = 1.0f;
trap_R_SetColor( color1 );
for ( int t = 0; t < 5; t++ )
{
cx = 320 + sin( (t*10+45)/57.296f ) * 192;
cy = 240 + cos( (t*10+45)/57.296f ) * 192;
CG_DrawRotatePic2( cx, cy, 24, 38, 45 - t * 10, trap_R_RegisterShader( va("gfx/2d/char%d",val[4-t] )));
}
*/
//max = ( cg_entities[0].gent->health / 100.0f );
max = cg.snap->ps.ammo[weaponData[WP_DISRUPTOR].ammoIndex] / (float)ammoData[weaponData[WP_DISRUPTOR].ammoIndex].max;
if ( max > 1.0f )
{
max = 1.0f;
}
color1[0] = (1.0f - max) * 2.0f;
color1[1] = max * 1.5f;
color1[2] = 0.0f;
color1[3] = 1.0f;
// If we are low on health, make us flash
if ( max < 0.15f && ( cg.time & 512 ))
{
VectorClear( color1 );
}
if ( color1[0] > 1.0f )
{
color1[0] = 1.0f;
}
if ( color1[1] > 1.0f )
{
color1[1] = 1.0f;
}
trap_R_SetColor( color1 );
max *= 58.0f;
for (fi = 18.5f; fi <= 18.5f + max; fi+= 3 ) // going from 15 to 45 degrees, with 5 degree increments
{
cx = 320 + sin( (fi+90.0f)/57.296f ) * 190;
cy = 240 + cos( (fi+90.0f)/57.296f ) * 190;
CG_DrawRotatePic2( cx, cy, 12, 24, 90 - fi, cgs.media.disruptorInsertTick );
}
if ( cg.predictedPlayerState.weaponstate == WEAPON_CHARGING_ALT )
{
trap_R_SetColor( colorTable[CT_WHITE] );
// draw the charge level
max = ( cg.time - cg.predictedPlayerState.weaponChargeTime ) / ( 50.0f * 30.0f ); // bad hardcodedness 50 is disruptor charge unit and 30 is max charge units allowed.
if ( max > 1.0f )
{
max = 1.0f;
}
trap_R_DrawStretchPic(257, 435, 134*max, 34, 0, 0, max, 1, cgs.media.disruptorChargeShader);
}
// trap_R_SetColor( colorTable[CT_WHITE] );
// CG_DrawPic( 0, 0, 640, 480, cgs.media.disruptorMask );
}
}
/*
================
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;
}
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 );
}
}
}
/*
================
DrawAmmo
================
*/
void DrawAmmo()
{
int x, y;
x = SCREEN_WIDTH-80;
y = SCREEN_HEIGHT-80;
}
/*
================
CG_DrawHUDLeftFrame1
================
*/
void CG_DrawHUDLeftFrame1(int x,int y)
{
// Inner gray wire frame
trap_R_SetColor( hudTintColor );
CG_DrawPic( x, y, 80, 80, cgs.media.HUDInnerLeft );
}
/*
================
CG_DrawHUDLeftFrame2
================
*/
void CG_DrawHUDLeftFrame2(int x,int y)
{
// Inner gray wire frame
trap_R_SetColor( hudTintColor );
CG_DrawPic( x, y, 80, 80, cgs.media.HUDLeftFrame ); // Metal frame
}
/*
================
DrawHealthArmor
================
*/
void DrawHealthArmor(int x,int y)
{
vec4_t calcColor;
float armorPercent,hold,healthPercent;
playerState_t *ps;
int healthAmt;
int armorAmt;
ps = &cg.snap->ps;
healthAmt = ps->stats[STAT_HEALTH];
armorAmt = ps->stats[STAT_ARMOR];
if (healthAmt > ps->stats[STAT_MAX_HEALTH])
{
healthAmt = ps->stats[STAT_MAX_HEALTH];
}
if (armorAmt > 100)
{
armorAmt = 100;
}
trap_R_SetColor( colorTable[CT_WHITE] );
CG_DrawPic( x, y, 80, 80, cgs.media.HUDLeftFrame ); // Circular black background
// Outer Armor circular
memcpy(calcColor, colorTable[CT_GREEN], sizeof(vec4_t));
hold = armorAmt-(ps->stats[STAT_MAX_HEALTH]/2);
armorPercent = (float) hold/(ps->stats[STAT_MAX_HEALTH]/2);
if (armorPercent <0)
{
armorPercent = 0;
}
calcColor[0] *= armorPercent;
calcColor[1] *= armorPercent;
calcColor[2] *= armorPercent;
trap_R_SetColor( calcColor);
CG_DrawPic( x, y, 80, 80, cgs.media.HUDArmor1 );
// Inner Armor circular
if (armorPercent>0)
{
armorPercent = 1;
}
else
{
armorPercent = (float) armorAmt/(ps->stats[STAT_MAX_HEALTH]/2);
}
memcpy(calcColor, colorTable[CT_GREEN], sizeof(vec4_t));
calcColor[0] *= armorPercent;
calcColor[1] *= armorPercent;
calcColor[2] *= armorPercent;
trap_R_SetColor( calcColor);
CG_DrawPic( x, y, 80, 80, cgs.media.HUDArmor2 ); // Inner Armor circular
if (ps->stats[STAT_ARMOR]) // Is there armor? Draw the HUD Armor TIC
{
// Make tic flash if inner armor is at 50% (25% of full armor)
if (armorPercent<.5) // Do whatever the flash timer says
{
if (cg.HUDTickFlashTime < cg.time) // Flip at the same time
{
cg.HUDTickFlashTime = cg.time + 100;
if (cg.HUDArmorFlag)
{
cg.HUDArmorFlag = qfalse;
}
else
{
cg.HUDArmorFlag = qtrue;
}
}
}
else
{
cg.HUDArmorFlag=qtrue;
}
}
else // No armor? Don't show it.
{
cg.HUDArmorFlag=qfalse;
}
memcpy(calcColor, colorTable[CT_RED], sizeof(vec4_t));
healthPercent = (float) healthAmt/ps->stats[STAT_MAX_HEALTH];
calcColor[0] *= healthPercent;
calcColor[1] *= healthPercent;
calcColor[2] *= healthPercent;
trap_R_SetColor( calcColor);
CG_DrawPic( x, y, 80, 80, cgs.media.HUDHealth );
// Make tic flash if health is at 20% of full
if (healthPercent>.20)
{
cg.HUDHealthFlag=qtrue;
}
else
{
if (cg.HUDTickFlashTime < cg.time) // Flip at the same time
{
cg.HUDTickFlashTime = cg.time + 100;
if ((armorPercent>0) && (armorPercent<.5)) // Keep the tics in sync if flashing
{
cg.HUDHealthFlag=cg.HUDArmorFlag;
}
else
{
if (cg.HUDHealthFlag)
{
cg.HUDHealthFlag = qfalse;
}
else
{
cg.HUDHealthFlag = qtrue;
}
}
}
}
// Draw the ticks
if (cg.HUDHealthFlag)
{
trap_R_SetColor( colorTable[CT_RED] );
CG_DrawPic( x, y, 80, 80, cgs.media.HUDHealthTic );
}
if (cg.HUDArmorFlag)
{
trap_R_SetColor( colorTable[CT_GREEN] );
CG_DrawPic( x, y, 80, 80, cgs.media.HUDArmorTic ); //
}
trap_R_SetColor(hudTintColor);
CG_DrawPic( x, y, 80, 80, cgs.media.HUDLeftStatic ); //
trap_R_SetColor( colorTable[CT_RED] );
CG_DrawNumField (x + 16, y + 40, 3, ps->stats[STAT_HEALTH], 14, 18,
NUM_FONT_SMALL,qfalse);
trap_R_SetColor( colorTable[CT_GREEN] );
CG_DrawNumField (x + 18 + 14, y + 40 + 14, 3, ps->stats[STAT_ARMOR], 14, 18,
NUM_FONT_SMALL,qfalse);
trap_R_SetColor(hudTintColor );
CG_DrawPic( x, y, 80, 80, cgs.media.HUDLeft ); // Metal frame
}
/*
================
CG_DrawHealth
================
*/
void CG_DrawHealth(int x,int y)
{
vec4_t calcColor;
float healthPercent;
playerState_t *ps;
int healthAmt;
ps = &cg.snap->ps;
healthAmt = ps->stats[STAT_HEALTH];
if (healthAmt > ps->stats[STAT_MAX_HEALTH])
{
healthAmt = ps->stats[STAT_MAX_HEALTH];
}
memcpy(calcColor, colorTable[CT_HUD_RED], sizeof(vec4_t));
healthPercent = (float) healthAmt/ps->stats[STAT_MAX_HEALTH];
calcColor[0] *= healthPercent;
calcColor[1] *= healthPercent;
calcColor[2] *= healthPercent;
trap_R_SetColor( calcColor);
CG_DrawPic( x, y, 80, 80, cgs.media.HUDHealth );
// Draw the ticks
if (cg.HUDHealthFlag)
{
trap_R_SetColor( colorTable[CT_HUD_RED] );
CG_DrawPic( x, y, 80, 80, cgs.media.HUDHealthTic );
}
trap_R_SetColor( colorTable[CT_HUD_RED] );
CG_DrawNumField (x + 16, y + 40, 3, ps->stats[STAT_HEALTH], 6, 12,
NUM_FONT_SMALL,qfalse);
}
/*
================
CG_DrawArmor
================
*/
void CG_DrawArmor(int x,int y)
{
vec4_t calcColor;
float armorPercent,hold;
playerState_t *ps;
int armor;
ps = &cg.snap->ps;
// Outer Armor circular
memcpy(calcColor, colorTable[CT_HUD_GREEN], sizeof(vec4_t));
armor =ps->stats[STAT_ARMOR];
if (armor> ps->stats[STAT_MAX_HEALTH])
{
armor = ps->stats[STAT_MAX_HEALTH];
}
hold = armor-(ps->stats[STAT_MAX_HEALTH]/2);
armorPercent = (float) hold/(ps->stats[STAT_MAX_HEALTH]/2);
if (armorPercent <0)
{
armorPercent = 0;
}
calcColor[0] *= armorPercent;
calcColor[1] *= armorPercent;
calcColor[2] *= armorPercent;
trap_R_SetColor( calcColor);
CG_DrawPic( x, y, 80, 80, cgs.media.HUDArmor1 );
// Inner Armor circular
if (armorPercent>0)
{
armorPercent = 1;
}
else
{
armorPercent = (float) ps->stats[STAT_ARMOR]/(ps->stats[STAT_MAX_HEALTH]/2);
}
memcpy(calcColor, colorTable[CT_HUD_GREEN], sizeof(vec4_t));
calcColor[0] *= armorPercent;
calcColor[1] *= armorPercent;
calcColor[2] *= armorPercent;
trap_R_SetColor( calcColor);
CG_DrawPic( x, y, 80, 80, cgs.media.HUDArmor2 ); // Inner Armor circular
if (ps->stats[STAT_ARMOR]) // Is there armor? Draw the HUD Armor TIC
{
// Make tic flash if inner armor is at 50% (25% of full armor)
if (armorPercent<.5) // Do whatever the flash timer says
{
if (cg.HUDTickFlashTime < cg.time) // Flip at the same time
{
cg.HUDTickFlashTime = cg.time + 100;
if (cg.HUDArmorFlag)
{
cg.HUDArmorFlag = qfalse;
}
else
{
cg.HUDArmorFlag = qtrue;
}
}
}
else
{
cg.HUDArmorFlag=qtrue;
}
}
else // No armor? Don't show it.
{
cg.HUDArmorFlag=qfalse;
}
if (cg.HUDArmorFlag)
{
trap_R_SetColor( colorTable[CT_HUD_GREEN] );
CG_DrawPic( x, y, 80, 80, cgs.media.HUDArmorTic );
}
trap_R_SetColor( colorTable[CT_HUD_GREEN] );
CG_DrawNumField (x + 18 + 14, y + 40 + 14, 3, ps->stats[STAT_ARMOR], 6, 12,
NUM_FONT_SMALL,qfalse);
}
/*
================
CG_DrawHUDRightFrame1
================
*/
void CG_DrawHUDRightFrame1(int x,int y)
{
trap_R_SetColor( hudTintColor );
// Inner gray wire frame
CG_DrawPic( x, y, 80, 80, cgs.media.HUDInnerRight ); //
}
/*
================
CG_DrawHUDRightFrame2
================
*/
void CG_DrawHUDRightFrame2(int x,int y)
{
trap_R_SetColor( hudTintColor );
CG_DrawPic( x, y, 80, 80, cgs.media.HUDRightFrame ); // Metal frame
}
/*
================
CG_DrawAmmo
================
*/
static void CG_DrawAmmo(centity_t *cent,int x,int y)
{
playerState_t *ps;
int numColor_i;
int i;
vec4_t calcColor;
float value,inc,percent;
ps = &cg.snap->ps;
if (!cent->currentState.weapon ) // We don't have a weapon right now
{
return;
}
if ( cent->currentState.weapon == WP_SABER )
{
// value = cent->gent->client->ps.forcePower;
return;
}
else
{
value = ps->ammo[weaponData[cent->currentState.weapon].ammoIndex];
}
if (value < 0) // No ammo
{
return;
}
//
// ammo
//
/* if (cg.oldammo < value)
{
cg.oldAmmoTime = cg.time + 200;
}
cg.oldammo = value;
*/
// Firing or reloading?
/* if (( pm->ps->weaponstate == WEAPON_FIRING
&& cg.predictedPlayerState.weaponTime > 100 ))
{
numColor_i = CT_LTGREY;
} */
// Overcharged?
// else if ( cent->gent->s.powerups & ( 1 << PW_WEAPON_OVERCHARGE ) )
// {
// numColor_i = CT_WHITE;
// }
// else
// {
// if ( value > 0 )
// {
// if (cg.oldAmmoTime > cg.time)
// {
// numColor_i = CT_YELLOW;
// }
// else
// {
// numColor_i = CT_HUD_ORANGE;
// }
// }
// else
// {
// numColor_i = CT_RED;
// }
// }
numColor_i = CT_HUD_ORANGE;
trap_R_SetColor( colorTable[numColor_i] );
CG_DrawNumField (x + 30, y + 26, 3, value, 6, 12, NUM_FONT_SMALL,qfalse);
//cg.snap->ps.ammo[weaponData[cg.snap->ps.weapon].ammoIndex]
inc = (float) ammoData[weaponData[cent->currentState.weapon].ammoIndex].max / MAX_TICS;
value =ps->ammo[weaponData[cent->currentState.weapon].ammoIndex];
for (i=MAX_TICS-1;i>=0;i--)
{
if (value <= 0) // partial tic
{
memcpy(calcColor, colorTable[CT_BLACK], sizeof(vec4_t));
}
else if (value < inc) // partial tic
{
memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
percent = value / inc;
calcColor[0] *= percent;
calcColor[1] *= percent;
calcColor[2] *= percent;
}
else
{
memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
}
trap_R_SetColor( calcColor);
CG_DrawPic( x + ammoTicPos[i].x,
y + ammoTicPos[i].y,
ammoTicPos[i].width,
ammoTicPos[i].height,
ammoTicPos[i].tic );
value -= inc;
}
}
/*
================
CG_DrawForcePower
================
*/
void CG_DrawForcePower(int x,int y)
{
int i;
vec4_t calcColor;
float value,inc,percent;
inc = (float) 100 / MAX_TICS;
value = cg.snap->ps.fd.forcePower;
for (i=MAX_TICS-1;i>=0;i--)
{
if (value <= 0) // partial tic
{
memcpy(calcColor, colorTable[CT_BLACK], sizeof(vec4_t));
}
else if (value < inc) // partial tic
{
memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
percent = value / inc;
calcColor[0] *= percent;
calcColor[1] *= percent;
calcColor[2] *= percent;
}
else
{
memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
}
trap_R_SetColor( calcColor);
CG_DrawPic( x + forceTicPos[i].x,
y + forceTicPos[i].y,
forceTicPos[i].width,
forceTicPos[i].height,
forceTicPos[i].tic );
value -= inc;
}
}
/*
================
CG_DrawHUD
================
*/
void CG_DrawHUD(centity_t *cent)
{
menuDef_t *menuHUD = NULL;
if (cgs.gametype >= GT_TEAM)
{ // tint the hud items based on team
if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED )
hudTintColor = redhudtint;
else if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE )
hudTintColor = bluehudtint;
else // If we're not on a team for whatever reason, leave things as they are.
hudTintColor = colorTable[CT_WHITE];
}
else
{ // tint the hud items white (dont' tint)
hudTintColor = colorTable[CT_WHITE];
}
menuHUD = Menus_FindByName("lefthud");
if (menuHUD)
{
CG_DrawHUDLeftFrame1(menuHUD->window.rect.x,menuHUD->window.rect.y);
CG_DrawArmor(menuHUD->window.rect.x,menuHUD->window.rect.y);
CG_DrawHealth(menuHUD->window.rect.x,menuHUD->window.rect.y);
CG_DrawHUDLeftFrame2(menuHUD->window.rect.x,menuHUD->window.rect.y);
}
else
{ //Apparently we failed to get proper coordinates from the menu, so resort to manually inputting them.
CG_DrawHUDLeftFrame1(0,SCREEN_HEIGHT-80);
CG_DrawArmor(0,SCREEN_HEIGHT-80);
CG_DrawHealth(0,SCREEN_HEIGHT-80);
CG_DrawHUDLeftFrame2(0,SCREEN_HEIGHT-80);
}
menuHUD = Menus_FindByName("righthud");
if (menuHUD)
{
CG_DrawHUDRightFrame1(menuHUD->window.rect.x,menuHUD->window.rect.y);
CG_DrawForcePower(menuHUD->window.rect.x,menuHUD->window.rect.y);
CG_DrawAmmo(cent,menuHUD->window.rect.x,menuHUD->window.rect.y);
CG_DrawHUDRightFrame2(menuHUD->window.rect.x,menuHUD->window.rect.y);
}
else
{ //Apparently we failed to get proper coordinates from the menu, so resort to manually inputting them.
CG_DrawHUDRightFrame1(SCREEN_WIDTH-80,SCREEN_HEIGHT-80);
CG_DrawForcePower(SCREEN_WIDTH-80,SCREEN_HEIGHT-80);
CG_DrawAmmo(cent,SCREEN_WIDTH-80,SCREEN_HEIGHT-80);
CG_DrawHUDRightFrame2(SCREEN_WIDTH-80,SCREEN_HEIGHT-80);
}
}
#define MAX_SHOWPOWERS NUM_FORCE_POWERS
qboolean ForcePower_Valid(int i)
{
if (i == FP_LEVITATION ||
i == FP_SABERATTACK ||
i == FP_SABERDEFEND ||
i == FP_SABERTHROW)
{
return qfalse;
}
if (cg.snap->ps.fd.forcePowersKnown & (1 << i))
{
return qtrue;
}
return qfalse;
}
/*
===================
CG_DrawForceSelect
===================
*/
void CG_DrawForceSelect( void )
{
int i;
int count;
int smallIconSize,bigIconSize;
int holdX,x,y,x2,y2,pad,length;
int sideLeftIconCnt,sideRightIconCnt;
int sideMax,holdCount,iconCnt;
x2 = 0;
y2 = 0;
// don't display if dead
if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 )
{
return;
}
if ((cg.forceSelectTime+WEAPON_SELECT_TIME)<cg.time) // Time is up for the HUD to display
{
cg.forceSelect = cg.snap->ps.fd.forcePowerSelected;
return;
}
if (!cg.snap->ps.fd.forcePowersKnown)
{
return;
}
// count the number of powers owned
count = 0;
for (i=0;i < NUM_FORCE_POWERS;++i)
{
if (ForcePower_Valid(i))
{
count++;
}
}
if (count == 0) // If no force powers, don't display
{
return;
}
sideMax = 3; // Max number of icons on the side
// Calculate how many icons will appear to either side of the center one
holdCount = count - 1; // -1 for the center icon
if (holdCount == 0) // No icons to either side
{
sideLeftIconCnt = 0;
sideRightIconCnt = 0;
}
else if (count > (2*sideMax)) // Go to the max on each side
{
sideLeftIconCnt = sideMax;
sideRightIconCnt = sideMax;
}
else // Less than max, so do the calc
{
sideLeftIconCnt = holdCount/2;
sideRightIconCnt = holdCount - sideLeftIconCnt;
}
smallIconSize = 30;
bigIconSize = 60;
pad = 12;
x = 320;
y = 425;
// Background
length = (sideLeftIconCnt * smallIconSize) + (sideLeftIconCnt*pad) +
bigIconSize + (sideRightIconCnt * smallIconSize) + (sideRightIconCnt*pad) + 12;
i = BG_ProperForceIndex(cg.forceSelect) - 1;
if (i < 0)
{
i = MAX_SHOWPOWERS;
}
trap_R_SetColor(NULL);
// Work backwards from current icon
holdX = x - ((bigIconSize/2) + pad + smallIconSize);
for (iconCnt=1;iconCnt<(sideLeftIconCnt+1);i--)
{
if (i < 0)
{
i = MAX_SHOWPOWERS;
}
if (!ForcePower_Valid(forcePowerSorted[i])) // Does he have this power?
{
continue;
}
++iconCnt; // Good icon
if (cgs.media.forcePowerIcons[forcePowerSorted[i]])
{
CG_DrawPic( holdX, y, smallIconSize, smallIconSize, cgs.media.forcePowerIcons[forcePowerSorted[i]] );
holdX -= (smallIconSize+pad);
}
}
if (ForcePower_Valid(cg.forceSelect))
{
// Current Center Icon
if (cgs.media.forcePowerIcons[cg.forceSelect])
{
CG_DrawPic( x-(bigIconSize/2), (y-((bigIconSize-smallIconSize)/2)), bigIconSize, bigIconSize, cgs.media.forcePowerIcons[cg.forceSelect] ); //only cache the icon for display
}
}
i = BG_ProperForceIndex(cg.forceSelect) + 1;
if (i>MAX_SHOWPOWERS)
{
i = 0;
}
// Work forwards from current icon
holdX = x + (bigIconSize/2) + pad;
for (iconCnt=1;iconCnt<(sideRightIconCnt+1);i++)
{
if (i>MAX_SHOWPOWERS)
{
i = 0;
}
if (!ForcePower_Valid(forcePowerSorted[i])) // Does he have this power?
{
continue;
}
++iconCnt; // Good icon
if (cgs.media.forcePowerIcons[forcePowerSorted[i]])
{
CG_DrawPic( holdX, y, smallIconSize, smallIconSize, cgs.media.forcePowerIcons[forcePowerSorted[i]] ); //only cache the icon for display
holdX += (smallIconSize+pad);
}
}
if ( showPowersName[cg.forceSelect] )
{
UI_DrawProportionalString(320, y + 33, showPowersName[cg.forceSelect], UI_CENTER | UI_SMALLFONT, colorTable[CT_ICON_BLUE]);
}
}
/*
===================
CG_DrawInventorySelect
===================
*/
void CG_DrawInvenSelect( void )
{
int i;
int sideMax,holdCount,iconCnt;
int smallIconSize,bigIconSize;
int sideLeftIconCnt,sideRightIconCnt;
int count;
int holdX,x,y,y2,pad;
int height;
float addX;
// don't display if dead
if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 )
{
return;
}
if ((cg.invenSelectTime+WEAPON_SELECT_TIME)<cg.time) // Time is up for the HUD to display
{
return;
}
if (!cg.snap->ps.stats[STAT_HOLDABLE_ITEM] || !cg.snap->ps.stats[STAT_HOLDABLE_ITEMS])
{
return;
}
if (cg.itemSelect == -1)
{
cg.itemSelect = bg_itemlist[cg.snap->ps.stats[STAT_HOLDABLE_ITEM]].giTag;
}
//const int bits = cg.snap->ps.stats[ STAT_ITEMS ];
// count the number of items owned
count = 0;
for ( i = 0 ; i < HI_NUM_HOLDABLE ; i++ )
{
if (/*CG_InventorySelectable(i) && inv_icons[i]*/
(cg.snap->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << i)) )
{
count++;
}
}
if (!count)
{
y2 = 0; //err?
UI_DrawProportionalString(320, y2 + 22, "EMPTY INVENTORY", UI_CENTER | UI_SMALLFONT, colorTable[CT_ICON_BLUE]);
return;
}
sideMax = 3; // Max number of icons on the side
// Calculate how many icons will appear to either side of the center one
holdCount = count - 1; // -1 for the center icon
if (holdCount == 0) // No icons to either side
{
sideLeftIconCnt = 0;
sideRightIconCnt = 0;
}
else if (count > (2*sideMax)) // Go to the max on each side
{
sideLeftIconCnt = sideMax;
sideRightIconCnt = sideMax;
}
else // Less than max, so do the calc
{
sideLeftIconCnt = holdCount/2;
sideRightIconCnt = holdCount - sideLeftIconCnt;
}
i = cg.itemSelect - 1;
if (i<0)
{
i = HI_NUM_HOLDABLE-1;
}
smallIconSize = 40;
bigIconSize = 80;
pad = 16;
x = 320;
y = 410;
// Left side ICONS
// Work backwards from current icon
holdX = x - ((bigIconSize/2) + pad + smallIconSize);
height = smallIconSize * cg.iconHUDPercent;
addX = (float) smallIconSize * .75;
for (iconCnt=0;iconCnt<sideLeftIconCnt;i--)
{
if (i<0)
{
i = HI_NUM_HOLDABLE-1;
}
if ( !(cg.snap->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << i)) || i == cg.itemSelect )
{
continue;
}
++iconCnt; // Good icon
if (cgs.media.invenIcons[i])
{
trap_R_SetColor(NULL);
CG_DrawPic( holdX, y+10, smallIconSize, smallIconSize, cgs.media.invenIcons[i] );
trap_R_SetColor(colorTable[CT_ICON_BLUE]);
/*CG_DrawNumField (holdX + addX, y + smallIconSize, 2, cg.snap->ps.inventory[i], 6, 12,
NUM_FONT_SMALL,qfalse);
*/
holdX -= (smallIconSize+pad);
}
}
// Current Center Icon
height = bigIconSize * cg.iconHUDPercent;
if (cgs.media.invenIcons[cg.itemSelect])
{
trap_R_SetColor(NULL);
CG_DrawPic( x-(bigIconSize/2), (y-((bigIconSize-smallIconSize)/2))+10, bigIconSize, bigIconSize, cgs.media.invenIcons[cg.itemSelect] );
addX = (float) bigIconSize * .75;
trap_R_SetColor(colorTable[CT_ICON_BLUE]);
/*CG_DrawNumField ((x-(bigIconSize/2)) + addX, y, 2, cg.snap->ps.inventory[cg.inventorySelect], 6, 12,
NUM_FONT_SMALL,qfalse);*/
if (bg_itemlist[BG_GetItemIndexByTag(cg.itemSelect, IT_HOLDABLE)].pickup_name)
{
vec4_t textColor = { .312f, .75f, .621f, 1.0f };
UI_DrawProportionalString(320, y+48, bg_itemlist[BG_GetItemIndexByTag(cg.itemSelect, IT_HOLDABLE)].pickup_name, UI_CENTER | UI_SMALLFONT, textColor);
}
}
i = cg.itemSelect + 1;
if (i> HI_NUM_HOLDABLE-1)
{
i = 0;
}
// Right side ICONS
// Work forwards from current icon
holdX = x + (bigIconSize/2) + pad;
height = smallIconSize * cg.iconHUDPercent;
addX = (float) smallIconSize * .75;
for (iconCnt=0;iconCnt<sideRightIconCnt;i++)
{
if (i> HI_NUM_HOLDABLE-1)
{
i = 0;
}
if ( !(cg.snap->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << i)) || i == cg.itemSelect )
{
continue;
}
++iconCnt; // Good icon
if (cgs.media.invenIcons[i])
{
trap_R_SetColor(NULL);
CG_DrawPic( holdX, y+10, smallIconSize, smallIconSize, cgs.media.invenIcons[i] );
trap_R_SetColor(colorTable[CT_ICON_BLUE]);
/*CG_DrawNumField (holdX + addX, y + smallIconSize, 2, cg.snap->ps.inventory[i], 6, 12,
NUM_FONT_SMALL,qfalse);*/
holdX += (smallIconSize+pad);
}
}
}
/*
================
CG_DrawStats
================
*/
static void CG_DrawStats( void )
{
centity_t *cent;
/* playerState_t *ps;
vec3_t angles;
// vec3_t origin;
if ( cg_drawStatus.integer == 0 ) {
return;
}
*/
cent = &cg_entities[cg.snap->ps.clientNum];
/* ps = &cg.snap->ps;
VectorClear( angles );
// Do start
if (!cg.interfaceStartupDone)
{
CG_InterfaceStartup();
}
cgi_UI_MenuPaintAll();*/
CG_DrawHUD(cent);
/*CG_DrawArmor(cent);
CG_DrawHealth(cent);
CG_DrawAmmo(cent);
CG_DrawTalk(cent);*/
}
/*
================
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 );
}
/*
===========================================================================================
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 );
}
}
/*
===================
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
//
x = 640 - 48;
y = 480 - 144;
trap_R_SetColor( NULL );
CG_DrawPic( x, y, 48, 48, cgs.media.lagometerShader );
ax = x;
ay = y;
aw = 48;
ah = 48;
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;
int h;
float *color;
const float scale = 1.0; //0.5
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;
w = CG_Text_Width(linebuffer, scale, FONT_MEDIUM);
h = CG_Text_Height(linebuffer, scale, FONT_MEDIUM);
x = (SCREEN_WIDTH - w) / 2;
CG_Text_Paint(x, y + h, scale, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE, FONT_MEDIUM);
y += h + 6;
while ( *start && ( *start != '\n' ) ) {
start++;
}
if ( !*start ) {
break;
}
start++;
}
trap_R_SetColor( NULL );
}
/*
================================================================================
CROSSHAIR
================================================================================
*/
/*
=================
CG_DrawCrosshair
=================
*/
static void CG_DrawCrosshair( vec3_t worldPoint, int chEntValid ) {
float w, h;
qhandle_t hShader;
float f;
float x, y;
if ( !cg_drawCrosshair.integer )
{
return;
}
if ( cg.predictedPlayerState.zoomMode != 0 )
{//not while scoped
return;
}
if ( cg_crosshairHealth.integer )
{
vec4_t hcolor;
CG_ColorForHealth( hcolor );
trap_R_SetColor( hcolor );
}
else
{
//set color based on what kind of ent is under crosshair
if ( cg.crosshairClientNum >= ENTITYNUM_WORLD )
{
trap_R_SetColor( NULL );
}
else if (chEntValid && (cg_entities[cg.crosshairClientNum].currentState.number < MAX_CLIENTS || cg_entities[cg.crosshairClientNum].currentState.shouldtarget))
{
vec4_t ecolor = {0,0,0,0};
centity_t *crossEnt = &cg_entities[cg.crosshairClientNum];
if ( crossEnt->currentState.number < MAX_CLIENTS )
{
if (cgs.gametype >= GT_TEAM &&
cgs.clientinfo[crossEnt->currentState.number].team == cgs.clientinfo[cg.snap->ps.clientNum].team )
{
//Allies are green
ecolor[0] = 0.0;//R
ecolor[1] = 1.0;//G
ecolor[2] = 0.0;//B
}
else
{
//Enemies are red
ecolor[0] = 1.0;//R
ecolor[1] = 0.0;//G
ecolor[2] = 0.0;//B
}
if (cg.snap->ps.duelInProgress)
{
if (crossEnt->currentState.number != cg.snap->ps.duelIndex)
{ //grey out crosshair for everyone but your foe if you're in a duel
ecolor[0] = 0.4;
ecolor[1] = 0.4;
ecolor[2] = 0.4;
}
}
else if (crossEnt->currentState.bolt1)
{ //this fellow is in a duel. We just checked if we were in a duel above, so
//this means we aren't and he is. Which of course means our crosshair greys out over him.
ecolor[0] = 0.4;
ecolor[1] = 0.4;
ecolor[2] = 0.4;
}
}
else if (crossEnt->currentState.shouldtarget)
{
//VectorCopy( crossEnt->startRGBA, ecolor );
if ( !ecolor[0] && !ecolor[1] && !ecolor[2] )
{
// We really don't want black, so set it to yellow
ecolor[0] = 1.0F;//R
ecolor[1] = 0.8F;//G
ecolor[2] = 0.3F;//B
}
if (crossEnt->currentState.owner == cg.snap->ps.clientNum ||
(cgs.gametype >= GT_TEAM && crossEnt->currentState.teamowner == cgs.clientinfo[cg.snap->ps.clientNum].team))
{
ecolor[0] = 0.0;//R
ecolor[1] = 1.0;//G
ecolor[2] = 0.0;//B
}
else if (crossEnt->currentState.teamowner == 16 ||
(cgs.gametype >= GT_TEAM && crossEnt->currentState.teamowner && crossEnt->currentState.teamowner != cgs.clientinfo[cg.snap->ps.clientNum].team))
{
ecolor[0] = 1.0;//R
ecolor[1] = 0.0;//G
ecolor[2] = 0.0;//B
}
}
ecolor[3] = 1.0;
trap_R_SetColor( ecolor );
}
}
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 );
}
if ( worldPoint && VectorLength( worldPoint ) )
{
if ( !CG_WorldCoordToScreenCoordFloat( worldPoint, &x, &y ) )
{//off screen, don't draw it
return;
}
x -= 320;
y -= 240;
}
else
{
x = cg_crosshairX.integer;
y = cg_crosshairY.integer;
}
hShader = cgs.media.crosshairShader[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ];
trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * (640 - w),
y + cg.refdef.y + 0.5 * (480 - h),
w, h, 0, 0, 1, 1, hShader );
}
qboolean CG_WorldCoordToScreenCoordFloat(vec3_t worldCoord, float *x, float *y)
{
int xcenter, ycenter;
vec3_t local, transformed;
vec3_t vfwd;
vec3_t vright;
vec3_t vup;
float xzi;
float yzi;
// xcenter = cg.refdef.width / 2;//gives screen coords adjusted for resolution
// ycenter = cg.refdef.height / 2;//gives screen coords adjusted for resolution
//NOTE: did it this way because most draw functions expect virtual 640x480 coords
// and adjust them for current resolution
xcenter = 640 / 2;//gives screen coords in virtual 640x480, to be adjusted when drawn
ycenter = 480 / 2;//gives screen coords in virtual 640x480, to be adjusted when drawn
AngleVectors (cg.refdefViewAngles, vfwd, vright, vup);
VectorSubtract (worldCoord, cg.refdef.vieworg, local);
transformed[0] = DotProduct(local,vright);
transformed[1] = DotProduct(local,vup);
transformed[2] = DotProduct(local,vfwd);
// Make sure Z is not negative.
if(transformed[2] < 0.01)
{
return qfalse;
}
xzi = xcenter / transformed[2] * (90.0/cg.refdef.fov_x);
yzi = ycenter / transformed[2] * (90.0/cg.refdef.fov_y);
*x = xcenter + xzi * transformed[0];
*y = ycenter - yzi * transformed[1];
return qtrue;
}
qboolean CG_WorldCoordToScreenCoord( vec3_t worldCoord, int *x, int *y )
{
float xF, yF;
qboolean retVal = CG_WorldCoordToScreenCoordFloat( worldCoord, &xF, &yF );
*x = (int)xF;
*y = (int)yF;
return retVal;
}
/*
====================
CG_SaberClashFlare
====================
*/
int g_saberFlashTime = 0;
vec3_t g_saberFlashPos = {0, 0, 0};
void CG_SaberClashFlare( void )
{
int t, maxTime = 150;
vec3_t dif;
vec3_t color;
int x,y;
float v, len;
trace_t tr;
t = cg.time - g_saberFlashTime;
if ( t <= 0 || t >= maxTime )
{
return;
}
// Don't do clashes for things that are behind us
VectorSubtract( g_saberFlashPos, cg.refdef.vieworg, dif );
if ( DotProduct( dif, cg.refdef.viewaxis[0] ) < 0.2 )
{
return;
}
CG_Trace( &tr, cg.refdef.vieworg, NULL, NULL, g_saberFlashPos, -1, CONTENTS_SOLID );
if ( tr.fraction < 1.0f )
{
return;
}
len = VectorNormalize( dif );
// clamp to a known range
if ( len > 800 )
{
len = 800;
}
v = ( 1.0f - ((float)t / maxTime )) * ((1.0f - ( len / 800.0f )) * 2.0f + 0.35f);
CG_WorldCoordToScreenCoord( g_saberFlashPos, &x, &y );
VectorSet( color, 0.8f, 0.8f, 0.8f );
trap_R_SetColor( color );
CG_DrawPic( x - ( v * 300 ), y - ( v * 300 ),
v * 600, v * 600,
trap_R_RegisterShader( "gfx/effects/saberFlare" ));
}
//--------------------------------------------------------------
static void CG_DrawHolocronIcons(void)
//--------------------------------------------------------------
{
int icon_size = 40;
int i = 0;
int startx = 10;
int starty = 10;//SCREEN_HEIGHT - icon_size*3;
int endx = icon_size;
int endy = icon_size;
if (cg.snap->ps.zoomMode)
{ //don't display over zoom mask
return;
}
while (i < NUM_FORCE_POWERS)
{
if (cg.snap->ps.holocronBits & (1 << forcePowerSorted[i]))
{
CG_DrawPic( startx, starty, endx, endy, cgs.media.forcePowerIcons[forcePowerSorted[i]]);
starty += (icon_size+2); //+2 for spacing
if ((starty+icon_size) >= SCREEN_HEIGHT-80)
{
starty = 10;//SCREEN_HEIGHT - icon_size*3;
startx += (icon_size+2);
}
}
i++;
}
}
static qboolean CG_IsDurationPower(int power)
{
if (power == FP_HEAL ||
power == FP_SPEED ||
power == FP_TELEPATHY ||
power == FP_RAGE ||
power == FP_PROTECT ||
power == FP_ABSORB ||
power == FP_SEE)
{
return qtrue;
}
return qfalse;
}
//--------------------------------------------------------------
static void CG_DrawActivePowers(void)
//--------------------------------------------------------------
{
int icon_size = 40;
int i = 0;
int startx = icon_size*2+16;
int starty = SCREEN_HEIGHT - icon_size*2;
int endx = icon_size;
int endy = icon_size;
if (cg.snap->ps.zoomMode)
{ //don't display over zoom mask
return;
}
while (i < NUM_FORCE_POWERS)
{
if ((cg.snap->ps.fd.forcePowersActive & (1 << forcePowerSorted[i])) &&
CG_IsDurationPower(forcePowerSorted[i]))
{
CG_DrawPic( startx, starty, endx, endy, cgs.media.forcePowerIcons[forcePowerSorted[i]]);
startx += (icon_size+2); //+2 for spacing
if ((startx+icon_size) >= SCREEN_WIDTH-80)
{
startx = icon_size*2+16;
starty += (icon_size+2);
}
}
i++;
}
//additionally, draw an icon force force rage recovery
if (cg.snap->ps.fd.forceRageRecoveryTime > cg.time)
{
CG_DrawPic( startx, starty, endx, endy, cgs.media.rageRecShader);
}
}
//--------------------------------------------------------------
static void CG_DrawRocketLocking( int lockEntNum, int lockTime )
//--------------------------------------------------------------
{
int cx, cy;
vec3_t org;
static int oldDif = 0;
centity_t *cent = &cg_entities[lockEntNum];
vec4_t color={0.0f,0.0f,0.0f,0.0f};
int dif = ( cg.time - cg.snap->ps.rocketLockTime ) / ( 1200.0f / /*8.0f*/16.0f );
int i;
if (!cg.snap->ps.rocketLockTime)
{
return;
}
if (cg.snap->ps.rocketLockTime != -1)
{
lastvalidlockdif = dif;
}
else
{
dif = lastvalidlockdif;
}
if ( !cent )
{
return;
}
VectorCopy( cent->lerpOrigin, org );
if ( CG_WorldCoordToScreenCoord( org, &cx, &cy ))
{
// we care about distance from enemy to eye, so this is good enough
float sz = Distance( cent->lerpOrigin, cg.refdef.vieworg ) / 1024.0f;
if ( sz > 1.0f )
{
sz = 1.0f;
}
else if ( sz < 0.0f )
{
sz = 0.0f;
}
sz = (1.0f - sz) * (1.0f - sz) * 32 + 6;
cy += sz * 0.5f;
if ( dif < 0 )
{
oldDif = 0;
return;
}
else if ( dif > 8 )
{
dif = 8;
}
// do sounds
if ( oldDif != dif )
{
if ( dif == 8 )
{
trap_S_StartSound( org, 0, CHAN_AUTO, trap_S_RegisterSound( "sound/weapons/rocket/lock.wav" ));
}
else
{
trap_S_StartSound( org, 0, CHAN_AUTO, trap_S_RegisterSound( "sound/weapons/rocket/tick.wav" ));
}
}
oldDif = dif;
for ( i = 0; i < dif; i++ )
{
color[0] = 1.0f;
color[1] = 0.0f;
color[2] = 0.0f;
color[3] = 0.1f * i + 0.2f;
trap_R_SetColor( color );
// our slices are offset by about 45 degrees.
CG_DrawRotatePic( cx - sz, cy - sz, sz, sz, i * 45.0f, trap_R_RegisterShaderNoMip( "gfx/2d/wedge" ));
}
// we are locked and loaded baby
if ( dif == 8 )
{
color[0] = color[1] = color[2] = sin( cg.time * 0.05f ) * 0.5f + 0.5f;
color[3] = 1.0f; // this art is additive, so the alpha value does nothing
trap_R_SetColor( color );
CG_DrawPic( cx - sz, cy - sz * 2, sz * 2, sz * 2, trap_R_RegisterShaderNoMip( "gfx/2d/lock" ));
}
}
}
/*
=================
CG_ScanForCrosshairEntity
=================
*/
static void CG_ScanForCrosshairEntity( void ) {
trace_t trace;
vec3_t start, end;
int content;
if ( cg_dynamicCrosshair.integer )
{
vec3_t d_f, d_rt, d_up;
/*
if ( cg.snap->ps.weapon == WP_NONE ||
cg.snap->ps.weapon == WP_SABER ||
cg.snap->ps.weapon == WP_STUN_BATON)
{
VectorCopy( cg.refdef.vieworg, start );
AngleVectors( cg.refdefViewAngles, d_f, d_rt, d_up );
}
else
*/
//For now we still want to draw the crosshair in relation to the player's world coordinates
//even if we have a melee weapon/no weapon.
{
if (cg.snap && cg.snap->ps.weapon == WP_EMPLACED_GUN && cg.snap->ps.emplacedIndex)
{
vec3_t pitchConstraint;
VectorCopy(cg.refdefViewAngles, pitchConstraint);
if (cg.renderingThirdPerson)
{
VectorCopy(cg.predictedPlayerState.viewangles, pitchConstraint);
}
else
{
VectorCopy(cg.refdefViewAngles, pitchConstraint);
}
if (pitchConstraint[PITCH] > 40)
{
pitchConstraint[PITCH] = 40;
}
AngleVectors( pitchConstraint, d_f, d_rt, d_up );
}
else
{
vec3_t pitchConstraint;
if (cg.renderingThirdPerson)
{
VectorCopy(cg.predictedPlayerState.viewangles, pitchConstraint);
}
else
{
VectorCopy(cg.refdefViewAngles, pitchConstraint);
}
AngleVectors( pitchConstraint, d_f, d_rt, d_up );
}
CG_CalcMuzzlePoint(cg.snap->ps.clientNum, start);
}
//FIXME: increase this? Increase when zoom in?
VectorMA( start, 4096, d_f, end );//was 8192
}
else
{
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)
{
if (CG_IsMindTricked(cg_entities[trace.entityNum].currentState.trickedentindex,
cg_entities[trace.entityNum].currentState.trickedentindex2,
cg_entities[trace.entityNum].currentState.trickedentindex3,
cg_entities[trace.entityNum].currentState.trickedentindex4,
cg.snap->ps.clientNum))
{
if (cg.crosshairClientNum == trace.entityNum)
{
cg.crosshairClientNum = ENTITYNUM_NONE;
cg.crosshairClientTime = 0;
}
CG_DrawCrosshair(trace.endpos, 0);
return; //this entity is mind-tricking the current client, so don't render it
}
}
if (cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR)
{
if (trace.entityNum < /*MAX_CLIENTS*/ENTITYNUM_WORLD)
{
CG_DrawCrosshair(trace.endpos, 1);
}
else
{
CG_DrawCrosshair(trace.endpos, 0);
}
}
// 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 ( trace.entityNum >= MAX_CLIENTS ) {
cg.crosshairClientNum = trace.entityNum;
cg.crosshairClientTime = cg.time;
return;
}
// update the fade timer
cg.crosshairClientNum = trace.entityNum;
cg.crosshairClientTime = cg.time;
}
/*
=====================
CG_DrawCrosshairNames
=====================
*/
static void CG_DrawCrosshairNames( void ) {
float *color;
vec4_t tcolor;
char *name;
int baseColor;
if ( !cg_drawCrosshair.integer ) {
return;
}
if ( !cg_drawCrosshairNames.integer ) {
return;
}
// scan the known entities to see if the crosshair is sighted on one
CG_ScanForCrosshairEntity();
if (cg.crosshairClientNum >= MAX_CLIENTS)
{
return;
}
// 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;
if (cgs.gametype >= GT_TEAM)
{
if (cgs.clientinfo[cg.crosshairClientNum].team == TEAM_RED)
{
baseColor = CT_RED;
}
else
{
baseColor = CT_BLUE;
}
/*
//For now instead of team-based we'll make it oriented based on which team we're on
if (cgs.clientinfo[cg.crosshairClientNum].team == cgs.clientinfo[cg.snap->ps.clientNum].team)
{
baseColor = CT_GREEN;
}
else
{
baseColor = CT_RED;
}
*/
}
else
{
//baseColor = CT_WHITE;
baseColor = CT_RED; //just make it red in nonteam modes since everyone is hostile and crosshair will be red on them too
}
if (cg.snap->ps.duelInProgress)
{
if (cg.crosshairClientNum != cg.snap->ps.duelIndex)
{ //grey out crosshair for everyone but your foe if you're in a duel
baseColor = CT_BLACK;
}
}
else if (cg_entities[cg.crosshairClientNum].currentState.bolt1)
{ //this fellow is in a duel. We just checked if we were in a duel above, so
//this means we aren't and he is. Which of course means our crosshair greys out over him.
baseColor = CT_BLACK;
}
tcolor[0] = colorTable[baseColor][0];
tcolor[1] = colorTable[baseColor][1];
tcolor[2] = colorTable[baseColor][2];
tcolor[3] = color[3]*0.5f;
UI_DrawProportionalString(320, 170, name, UI_CENTER, tcolor);
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;
}
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 );
}
/*
=================
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() {
return CG_DrawOldScoreboard();
#if 0
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;
#endif
}
/*
=================
CG_DrawIntermission
=================
*/
static void CG_DrawIntermission( void ) {
// int key;
//if (cg_singlePlayer.integer) {
// CG_DrawCenterString();
// return;
//}
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;
}
static void CG_DrawTemporaryStats()
{ //placeholder for testing (draws ammo and force power)
#if 0
char s[512];
if (!cg.snap)
{
return;
}
sprintf(s, "Force: %i", cg.snap->ps.fd.forcePower);
CG_DrawBigString(SCREEN_WIDTH-164, SCREEN_HEIGHT-128, s, 1.0f);
sprintf(s, "Ammo: %i", cg.snap->ps.ammo[weaponData[cg.snap->ps.weapon].ammoIndex]);
CG_DrawBigString(SCREEN_WIDTH-164, SCREEN_HEIGHT-112, s, 1.0f);
sprintf(s, "Health: %i", cg.snap->ps.stats[STAT_HEALTH]);
CG_DrawBigString(8, SCREEN_HEIGHT-128, s, 1.0f);
sprintf(s, "Armor: %i", cg.snap->ps.stats[STAT_ARMOR]);
CG_DrawBigString(8, SCREEN_HEIGHT-112, s, 1.0f);
#endif
}
/*
=================
CG_DrawAmmoWarning
=================
*/
static void CG_DrawAmmoWarning( void ) {
#if 0
const char *s;
int w;
if (!cg_drawStatus.integer)
{
return;
}
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);
#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 );
w = CG_Text_Width(s, 0.6f, FONT_MEDIUM);
CG_Text_Paint(320 - w / 2, 60, 0.6f, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE,FONT_MEDIUM);
}
} else {
if ( cgs.gametype == GT_FFA ) {
s = "Free For All";
} else if ( cgs.gametype == GT_HOLOCRON ) {
s = "Holocron FFA";
} else if ( cgs.gametype == GT_JEDIMASTER ) {
s = "Jedi Master";
} else if ( cgs.gametype == GT_TEAM ) {
s = "Team FFA";
} else if ( cgs.gametype == GT_SAGA ) {
s = "Saga";
} else if ( cgs.gametype == GT_CTF ) {
s = "Capture the Flag";
} else if ( cgs.gametype == GT_CTY ) {
s = "Capture the Ysalimari";
} else {
s = "";
}
w = CG_Text_Width(s, 0.6f, FONT_MEDIUM);
CG_Text_Paint(320 - w / 2, 90, 0.6f, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE,FONT_MEDIUM);
}
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;
}
w = CG_Text_Width(s, scale, FONT_MEDIUM);
CG_Text_Paint(320 - w / 2, 125, scale, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE, FONT_MEDIUM);
}
//==================================================================================
/*
=================
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;
}
}
}
static void CG_DrawPowerupIcons()
{
int j;
int ico_size = 64;
int y = ico_size/2;
gitem_t *item;
if (!cg.snap)
{
return;
}
for (j = 0; j <= PW_NUM_POWERUPS; j++)
{
if (cg.snap->ps.powerups[j] > cg.time)
{
int secondsleft = (cg.snap->ps.powerups[j] - cg.time)/1000;
item = BG_FindItemForPowerup( j );
if (item)
{
int icoShader = 0;
if (cgs.gametype == GT_CTY && (j == PW_REDFLAG || j == PW_BLUEFLAG))
{
if (j == PW_REDFLAG)
{
icoShader = trap_R_RegisterShader( "gfx/hud/mpi_rflag_ys" );
}
else
{
icoShader = trap_R_RegisterShader( "gfx/hud/mpi_bflag_ys" );
}
}
else
{
icoShader = trap_R_RegisterShader( item->icon );
}
CG_DrawPic( (640-(ico_size*1.5)), y, ico_size, ico_size, icoShader );
y += ico_size;
if (j != PW_REDFLAG && j != PW_BLUEFLAG && secondsleft < 999)
{
UI_DrawProportionalString((640-(ico_size*1.5))+(ico_size/2), y-8, va("%i", secondsleft), UI_CENTER | UI_SMALLFONT | UI_DROPSHADOW, colorTable[CT_WHITE]);
}
y += (ico_size/3);
}
}
}
}
void CG_DrawFlagStatus()
{
int myFlagTakenShader = 0;
int theirFlagShader = 0;
int team = 0;
int startDrawPos = 2;
int ico_size = 32;
if (!cg.snap)
{
return;
}
if (cgs.gametype != GT_CTF && cgs.gametype != GT_CTY)
{
return;
}
team = cg.snap->ps.persistant[PERS_TEAM];
if (cgs.gametype == GT_CTY)
{
if (team == TEAM_RED)
{
myFlagTakenShader = trap_R_RegisterShader( "gfx/hud/mpi_rflag_x" );
theirFlagShader = trap_R_RegisterShader( "gfx/hud/mpi_bflag_ys" );
}
else
{
myFlagTakenShader = trap_R_RegisterShader( "gfx/hud/mpi_bflag_x" );
theirFlagShader = trap_R_RegisterShader( "gfx/hud/mpi_rflag_ys" );
}
}
else
{
if (team == TEAM_RED)
{
myFlagTakenShader = trap_R_RegisterShader( "gfx/hud/mpi_rflag_x" );
theirFlagShader = trap_R_RegisterShader( "gfx/hud/mpi_bflag" );
}
else
{
myFlagTakenShader = trap_R_RegisterShader( "gfx/hud/mpi_bflag_x" );
theirFlagShader = trap_R_RegisterShader( "gfx/hud/mpi_rflag" );
}
}
if (CG_YourTeamHasFlag())
{
CG_DrawPic( startDrawPos, 365, ico_size, ico_size, theirFlagShader );
startDrawPos += ico_size+2;
}
if (CG_OtherTeamHasFlag())
{
CG_DrawPic( startDrawPos, 365, ico_size, ico_size, myFlagTakenShader );
}
}
int cgRageTime = 0;
int cgRageFadeTime = 0;
float cgRageFadeVal = 0;
int cgRageRecTime = 0;
int cgRageRecFadeTime = 0;
float cgRageRecFadeVal = 0;
int cgAbsorbTime = 0;
int cgAbsorbFadeTime = 0;
float cgAbsorbFadeVal = 0;
int cgProtectTime = 0;
int cgProtectFadeTime = 0;
float cgProtectFadeVal = 0;
int cgYsalTime = 0;
int cgYsalFadeTime = 0;
float cgYsalFadeVal = 0;
/*
=================
CG_Draw2D
=================
*/
static void CG_Draw2D( void ) {
float inTime = cg.invenSelectTime+WEAPON_SELECT_TIME;
float wpTime = cg.weaponSelectTime+WEAPON_SELECT_TIME;
float bestTime;
int drawSelect = 0;
float fallTime, rageTime, rageRecTime, absorbTime, protectTime, ysalTime;
vec4_t hcolor;
if (cgs.orderPending && cg.time > cgs.orderTime) {
CG_CheckOrderPending();
}
// if we are taking a levelshot for the menu, don't draw anything
if ( cg.levelShot ) {
return;
}
if ( cg_draw2D.integer == 0 ) {
return;
}
if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
CG_DrawIntermission();
return;
}
if (cg.snap->ps.fd.forcePowersActive & (1 << FP_RAGE))
{
if (!cgRageTime)
{
cgRageTime = cg.time;
}
rageTime = (float)(cg.time - cgRageTime);
rageTime /= 9000;
if (rageTime < 0)
{
rageTime = 0;
}
if (rageTime > 0.15)
{
rageTime = 0.15;
}
hcolor[3] = rageTime;
hcolor[0] = 0.7;
hcolor[1] = 0;
hcolor[2] = 0;
if (!cg.renderingThirdPerson)
{
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
cgRageFadeTime = 0;
cgRageFadeVal = 0;
}
else if (cgRageTime)
{
if (!cgRageFadeTime)
{
cgRageFadeTime = cg.time;
cgRageFadeVal = 0.15;
}
rageTime = cgRageFadeVal;
cgRageFadeVal -= (cg.time - cgRageFadeTime)*0.000005;
if (rageTime < 0)
{
rageTime = 0;
}
if (rageTime > 0.15)
{
rageTime = 0.15;
}
if (cg.snap->ps.fd.forceRageRecoveryTime > cg.time)
{
float checkRageRecTime = rageTime;
if (checkRageRecTime < 0.15)
{
checkRageRecTime = 0.15;
}
hcolor[3] = checkRageRecTime;
hcolor[0] = rageTime*4;
if (hcolor[0] < 0.2)
{
hcolor[0] = 0.2;
}
hcolor[1] = 0.2;
hcolor[2] = 0.2;
}
else
{
hcolor[3] = rageTime;
hcolor[0] = 0.7;
hcolor[1] = 0;
hcolor[2] = 0;
}
if (!cg.renderingThirdPerson && rageTime)
{
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
else
{
if (cg.snap->ps.fd.forceRageRecoveryTime > cg.time)
{
hcolor[3] = 0.15;
hcolor[0] = 0.2;
hcolor[1] = 0.2;
hcolor[2] = 0.2;
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
cgRageTime = 0;
}
}
else if (cg.snap->ps.fd.forceRageRecoveryTime > cg.time)
{
if (!cgRageRecTime)
{
cgRageRecTime = cg.time;
}
rageRecTime = (float)(cg.time - cgRageRecTime);
rageRecTime /= 9000;
if (rageRecTime < 0.15)//0)
{
rageRecTime = 0.15;//0;
}
if (rageRecTime > 0.15)
{
rageRecTime = 0.15;
}
hcolor[3] = rageRecTime;
hcolor[0] = 0.2;
hcolor[1] = 0.2;
hcolor[2] = 0.2;
if (!cg.renderingThirdPerson)
{
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
cgRageRecFadeTime = 0;
cgRageRecFadeVal = 0;
}
else if (cgRageRecTime)
{
if (!cgRageRecFadeTime)
{
cgRageRecFadeTime = cg.time;
cgRageRecFadeVal = 0.15;
}
rageRecTime = cgRageRecFadeVal;
cgRageRecFadeVal -= (cg.time - cgRageRecFadeTime)*0.000005;
if (rageRecTime < 0)
{
rageRecTime = 0;
}
if (rageRecTime > 0.15)
{
rageRecTime = 0.15;
}
hcolor[3] = rageRecTime;
hcolor[0] = 0.2;
hcolor[1] = 0.2;
hcolor[2] = 0.2;
if (!cg.renderingThirdPerson && rageRecTime)
{
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
else
{
cgRageRecTime = 0;
}
}
if (cg.snap->ps.fd.forcePowersActive & (1 << FP_ABSORB))
{
if (!cgAbsorbTime)
{
cgAbsorbTime = cg.time;
}
absorbTime = (float)(cg.time - cgAbsorbTime);
absorbTime /= 9000;
if (absorbTime < 0)
{
absorbTime = 0;
}
if (absorbTime > 0.15)
{
absorbTime = 0.15;
}
hcolor[3] = absorbTime/2;
hcolor[0] = 0;
hcolor[1] = 0;
hcolor[2] = 0.7;
if (!cg.renderingThirdPerson)
{
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
cgAbsorbFadeTime = 0;
cgAbsorbFadeVal = 0;
}
else if (cgAbsorbTime)
{
if (!cgAbsorbFadeTime)
{
cgAbsorbFadeTime = cg.time;
cgAbsorbFadeVal = 0.15;
}
absorbTime = cgAbsorbFadeVal;
cgAbsorbFadeVal -= (cg.time - cgAbsorbFadeTime)*0.000005;
if (absorbTime < 0)
{
absorbTime = 0;
}
if (absorbTime > 0.15)
{
absorbTime = 0.15;
}
hcolor[3] = absorbTime/2;
hcolor[0] = 0;
hcolor[1] = 0;
hcolor[2] = 0.7;
if (!cg.renderingThirdPerson && absorbTime)
{
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
else
{
cgAbsorbTime = 0;
}
}
if (cg.snap->ps.fd.forcePowersActive & (1 << FP_PROTECT))
{
if (!cgProtectTime)
{
cgProtectTime = cg.time;
}
protectTime = (float)(cg.time - cgProtectTime);
protectTime /= 9000;
if (protectTime < 0)
{
protectTime = 0;
}
if (protectTime > 0.15)
{
protectTime = 0.15;
}
hcolor[3] = protectTime/2;
hcolor[0] = 0;
hcolor[1] = 0.7;
hcolor[2] = 0;
if (!cg.renderingThirdPerson)
{
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
cgProtectFadeTime = 0;
cgProtectFadeVal = 0;
}
else if (cgProtectTime)
{
if (!cgProtectFadeTime)
{
cgProtectFadeTime = cg.time;
cgProtectFadeVal = 0.15;
}
protectTime = cgProtectFadeVal;
cgProtectFadeVal -= (cg.time - cgProtectFadeTime)*0.000005;
if (protectTime < 0)
{
protectTime = 0;
}
if (protectTime > 0.15)
{
protectTime = 0.15;
}
hcolor[3] = protectTime/2;
hcolor[0] = 0;
hcolor[1] = 0.7;
hcolor[2] = 0;
if (!cg.renderingThirdPerson && protectTime)
{
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
else
{
cgProtectTime = 0;
}
}
if (cg.snap->ps.rocketLockIndex != MAX_CLIENTS && (cg.time - cg.snap->ps.rocketLockTime) > 0)
{
CG_DrawRocketLocking( cg.snap->ps.rocketLockIndex, cg.snap->ps.rocketLockTime );
}
if (BG_HasYsalimari(cgs.gametype, &cg.snap->ps))
{
if (!cgYsalTime)
{
cgYsalTime = cg.time;
}
ysalTime = (float)(cg.time - cgYsalTime);
ysalTime /= 9000;
if (ysalTime < 0)
{
ysalTime = 0;
}
if (ysalTime > 0.15)
{
ysalTime = 0.15;
}
hcolor[3] = ysalTime/2;
hcolor[0] = 0.7;
hcolor[1] = 0.7;
hcolor[2] = 0;
if (!cg.renderingThirdPerson)
{
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
cgYsalFadeTime = 0;
cgYsalFadeVal = 0;
}
else if (cgYsalTime)
{
if (!cgYsalFadeTime)
{
cgYsalFadeTime = cg.time;
cgYsalFadeVal = 0.15;
}
ysalTime = cgYsalFadeVal;
cgYsalFadeVal -= (cg.time - cgYsalFadeTime)*0.000005;
if (ysalTime < 0)
{
ysalTime = 0;
}
if (ysalTime > 0.15)
{
ysalTime = 0.15;
}
hcolor[3] = ysalTime/2;
hcolor[0] = 0.7;
hcolor[1] = 0.7;
hcolor[2] = 0;
if (!cg.renderingThirdPerson && ysalTime)
{
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
else
{
cgYsalTime = 0;
}
}
if (cg.snap->ps.rocketLockIndex != MAX_CLIENTS && (cg.time - cg.snap->ps.rocketLockTime) > 0)
{
CG_DrawRocketLocking( cg.snap->ps.rocketLockIndex, cg.snap->ps.rocketLockTime );
}
if (cg.snap->ps.holocronBits)
{
CG_DrawHolocronIcons();
}
if (cg.snap->ps.fd.forcePowersActive || cg.snap->ps.fd.forceRageRecoveryTime > cg.time)
{
CG_DrawActivePowers();
}
// Draw this before the text so that any text won't get clipped off
CG_DrawZoomMask();
/*
if (cg.cameraMode) {
return;
}
*/
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
CG_DrawSpectator();
CG_DrawCrosshair(NULL, 0);
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 ) {
if ( /*cg_drawStatus.integer*/0 ) {
//Reenable if stats are drawn with menu system again
Menu_PaintAll();
CG_DrawTimedMenus();
}
//CG_DrawTemporaryStats();
CG_DrawAmmoWarning();
CG_DrawCrosshairNames();
if (cg_drawStatus.integer)
{
CG_DrawIconBackground();
}
if (inTime > wpTime)
{
drawSelect = 1;
bestTime = cg.invenSelectTime;
}
else //only draw the most recent since they're drawn in the same place
{
drawSelect = 2;
bestTime = cg.weaponSelectTime;
}
if (cg.forceSelectTime > bestTime)
{
drawSelect = 3;
}
switch(drawSelect)
{
case 1:
CG_DrawInvenSelect();
break;
case 2:
CG_DrawWeaponSelect();
break;
case 3:
CG_DrawForceSelect();
break;
default:
break;
}
if (cg_drawStatus.integer)
{
CG_DrawPowerupIcons();
CG_DrawFlagStatus();
}
CG_SaberClashFlare();
if (cg_drawStatus.integer)
{
CG_DrawStats();
}
//Do we want to use this system again at some point?
//CG_DrawReward();
}
}
if (cg.snap->ps.fallingToDeath)
{
fallTime = (float)(cg.time - cg.snap->ps.fallingToDeath);
fallTime /= FALL_FADE_TIME;
if (fallTime < 0)
{
fallTime = 0;
}
if (fallTime > 1)
{
fallTime = 1;
}
hcolor[3] = fallTime;
hcolor[0] = 0;
hcolor[1] = 0;
hcolor[2] = 0;
CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
}
CG_DrawVote();
CG_DrawTeamVote();
CG_DrawLagometer();
if (!cg_paused.integer) {
CG_DrawUpperRight();
}
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() {
}
/*
=====================
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();
}