stvoy-sp-sdk/cgame/cg_draw.cpp

2532 lines
67 KiB
C++

// cg_draw.c -- draw all of the graphical elements during
// active (after loading) gameplay
#include "cg_local.h"
#include "cg_media.h"
#include "cg_text.h"
#include "..\game\objectives.h"
#include "..\game\speakers.h"
qboolean G_ParseInt( char **data, int *i );
qboolean G_ParseString( char **data, char **s );
qboolean G_ParseFloat( char **data, float *f );
void CG_ColorForGivenHealth( vec4_t hcolor, int health );
void CG_DrawMissionInformation( void );
extern float g_crosshairEntDist;
extern int g_crosshairSameEntTime;
extern int g_crosshairEntNum;
extern int g_crosshairEntTime;
speakerTable_t speakerTable [SP_MAX] =
{
"NONE", "", 0, "", 0, qfalse, // SP_NONE
//Senior Officers
"JANEWAY", "janeway", 0, "default", 0, qfalse, // SP_JANEWAY
"CHAKOTAY", "chakotay", 0, "default", 0, qfalse, // SP_CHAKOTAY
"TUVOK", "tuvok", 0, "default", 0, qfalse, // SP_TUVOK
"TORRES", "torres", 0, "default", 0, qfalse, // SP_TORRES
"PARIS", "paris", 0, "default", 0, qfalse, // SP_PARIS
"NEELIX", "neelix", 0, "default", 0, qfalse, // SP_NEELIX
"SEVEN", "seven", 0, "default", 0, qfalse, // SP_SEVEN
"DOCTOR", "doctor", 0, "default", 0, qfalse, // SP_DOCTOR
"KIM", "kim", 0, "default", 0, qfalse, // SP_KIM
//HazTeam alpha
"FOSTER", "foster", 0, "default", 0, qfalse, // SP_FOSTER
"MUNRO", "munro", 0, "default", 0, qfalse, // SP_MUNRO
"BIESSMAN", "biessman", 0, "default", 0, qfalse, // SP_BIESSMAN
"CHANG", "chang", 0, "default", 0, qfalse, // SP_CHANG
"CHELL", "chell", 0, "default", 0, qfalse, // SP_CHELL
"JUROT", "telsia", 0, "jurot", 0, qfalse, // SP_JUROT
"TELSIA", "telsia", 0, "default", 0, qfalse, // SP_TELSIA
//HazTeam beta
"KENN", "munro", 0, "kenn", 0, qfalse, // SP_HOEKSTRA
"CSATLOS", "oviedo_h", 0, "jaworski", 0, qfalse, // SP_CSATLOS
"NELSON", "chell", 0, "long", 0, qfalse, // SP_NELSON
"ODELL", "chang", 0, "odell", 0, qfalse, // SP_ODELL
"OVIEDO", "oviedo_h", 0, "default", 0, qfalse, // SP_OVIEDO
"JAWORSKI", "oviedo_h", 0, "csatlos", 0, qfalse, // SP_JAWORSKI
"LAIRD", "garren", 0, "mackey", 0, qfalse, // SP_LAIRD
//Misc crew
"LANG", "chakotay", 0, "nelson", 0, qfalse, // SP_LANG
"RENNER", "munrocrew", 0, "jon", 0, qfalse, // SP_RENNER
"PELLETIER", "pelletier", 0, "default", 0, qfalse, // SP_PELLETIER
"GREEN", "green", 0, "default", 0, qfalse, // SP_GREEN
"SALMA", "garren", 0, "salma", 0, qfalse, // SP_SALMA
//Baddies
"HIROGENALPHA", "hirogen_boss", 0, "default", 0, qfalse, // SP_HIROGEN_BOSS
"DOCKREEGE", "imperial4", 0, "default", 0, qfalse, // SP_KREEGE
//Other
"COMPUTER", "", 0, "", 0, qfalse, // SP_COMPUTER
};
float interfaceColors[MC_MAX][4] =
{
{ 0.671875, 0.515625, 0., 1.0 } , // Ammo Count, Star Fleet Orange (brighter)
{0.66666F,0.309803F,0.0,1.0}, // Ammo Icon, Star Fleet Orange (darker)
{ 1.0F, 0.2F, 0.2F, 1.0F }, // Red
{0.5, 0.5, 0.5, 1}, // Grey
{0.592156F, 0.592156F, 0.850980F, 1}, // Ammo Alien Bright Color
{0.492156F, 0.492156F, 0.750980F, 1}, // Ammo Alien Dark Color
{ 1.0, 0.4F, 0.4F, 1.0 }, // Dark Red
{1.0, 0.811764F, 0.376470F, 1.0}, // Ammo Phaser Bright Color
{0.90F, 0.711764F, 0.276470F, 1.0}, // Ammo Phaser Dark Color
{1.0,1.0,1.0,1.0}, // MC_WHITE
{1.0,1.0,0.0,1.0} // MC_YELLOW
};
char *ingame_text[IGT_MAX];
/*
char *ingame_text[IGT_MAX] =
{
NULL, // IGT_NONE
"GAME PAUSED", // IGT_PAUSED,
"MISSION INFORMATION", // IGT_MISSIONINFORMATION
"TACTICAL INFORMATION", // IGT_TACTICALINFO
"OBJECTIVES:", // IGT_OBJECTIVES
"NONE", // IGT_NONE1,
"MISSION INFORMATION UPDATED", // IGT_MISSIONINFO_UPDATED,
" - FAILED", // IGT_FAILED,
" - SUCCEEDED", // IGT_SUCCEEDED,
"LCARS", // IGT_LCARS,
"MISSION ANALYSIS", // IGT_MISSIONANALYSIS,
"ENEMIES ELIMINATED :", // IGT_ENEMIES
"TEAM CASULATIES :", // IGT_CASULATIES
"SHOTS FIRED :", // IGT_SHOTSFIRED
"SHOTS EFFECTIVE :", // IGT_EFFECTIVE
"ACCURACY :", // IGT_ACCURACY
"PUZZLES SOLVED :", // IGT_PUZZLESSOLVED
"MISSION DURATION :", // IGT_DURATION
"FINAL ANALYSIS :", // IGT_ANALYSIS
"MISSION SUCCESSFUL", // IGT_MISSIONSUCCESSFUL,
"MISSION FAILED", // IGT_MISSIONFAILED,
"You performed inadequately.", // IGT_INADEQUATE,
"Your response time could be improved.", // IGT_RESPONSETIME
"Spend more time on the shooting range.", // IGT_SHOOTINRANGE
"Try again.", // IGT_TRYAGAIN
"You performed adequately.", // IGT_ADEQUATE,
"Your response time is impressive.", // IGT_RESPONSETIMEIMPRESSIVE
"You are obviously a marksman.", // IGT_MARKSMAN
"Well done.", // IGT_CONGRATULATIONS
};
*/
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, NULL, 0, 0, CT_NONE, 0, NULL, // IG_GROW
SG_VAR, 0.0, 0, 0, 0, 0, NULL, NULL, 0, 0, CT_NONE, 0, NULL, // IG_HEALTH_START
SG_GRAPHIC, 0.0, 5, 429, 32, 64, "gfx/interface/healthcap1", NULL, 0, 0, CT_DKBROWN1, 0, NULL, // IG_HEALTH_BEGINCAP
SG_GRAPHIC, 0.0, 64, 429, 6, 25, "gfx/interface/ammobar", NULL, 0, 0, CT_DKBROWN1, 0, NULL, // IG_HEALTH_BOX1
SG_GRAPHIC, 0.0, 72, 429, 0, 25, "gfx/interface/ammobar", NULL, 0, 0, CT_LTBROWN1, 0, NULL, // IG_HEALTH_SLIDERFULL
SG_GRAPHIC, 0.0, 0, 429, 0, 25, "gfx/interface/ammobar", NULL, 0, 0, CT_DKBROWN1, 0, NULL, // IG_HEALTH_SLIDEREMPTY
SG_GRAPHIC, 0.0, 72, 429, 16, 32, "gfx/interface/healthcap2", NULL, 0, 147, CT_DKBROWN1, 0, NULL, // IG_HEALTH_ENDCAP
SG_NUMBER, 0.0, 23, 425, 16, 32, NULL, NULL, 0, 0, CT_YELLOW, NUM_FONT_BIG, NULL, // IG_HEALTH_COUNT
SG_VAR, 0.0, 0, 0, 0, 0, NULL, NULL, 0, 0, CT_NONE, 0, NULL, // IG_HEALTH_END
SG_VAR, 0.0, 0, 0, 0, 0, NULL, NULL, 0, 0, CT_NONE, 0, NULL, // IG_ARMOR_START
SG_GRAPHIC, 0.0, 20, 458, 32, 16, "gfx/interface/armorcap1", NULL, 0, 0, CT_DKPURPLE1, 0, NULL, // IG_ARMOR_BEGINCAP
SG_GRAPHIC, 0.0, 64, 458, 6, 12, "gfx/interface/ammobar", NULL, 0, 0, CT_DKPURPLE1, 0, NULL, // IG_ARMOR_BOX1
SG_GRAPHIC, 0.0, 72, 458, 0, 12, "gfx/interface/ammobar", NULL, 0, 0, CT_LTPURPLE1, 0, NULL, // IG_ARMOR_SLIDERFULL
SG_GRAPHIC, 0.0, 0, 458, 0, 12, "gfx/interface/ammobar", NULL, 0, 0, CT_DKPURPLE1, 0, NULL, // IG_ARMOR_SLIDEREMPTY
SG_GRAPHIC, 0.0, 72, 458, 16, 16, "gfx/interface/armorcap2", NULL, 0, 147, CT_DKPURPLE1, 0, NULL, // IG_ARMOR_ENDCAP
SG_NUMBER, 0.0, 44, 458, 16, 16, NULL, NULL, 0, 0, CT_YELLOW, NUM_FONT_SMALL, NULL, // IG_ARMOR_COUNT
SG_VAR, 0.0, 0, 0, 0, 0, NULL, NULL, 0, 0, CT_NONE, 0, NULL, // IG_ARMOR_END
SG_VAR, 0.0, 0, 0, 0, 0, NULL, NULL, 0, 0, CT_NONE, 0, NULL, // IG_AMMO_START
SG_GRAPHIC, 0.0, 613, 429, 32, 64, "gfx/interface/ammouppercap1", NULL, 0, 0, CT_LTPURPLE2, 0, NULL, // IG_AMMO_UPPER_BEGINCAP
SG_GRAPHIC, 0.0, 607, 429, 16, 32, "gfx/interface/ammouppercap2", NULL, 0, 572, CT_LTPURPLE2, 0, NULL, // IG_AMMO_UPPER_ENDCAP
SG_GRAPHIC, 0.0, 613, 458, 16, 16, "gfx/interface/ammolowercap1", NULL, 0, 0, CT_LTPURPLE2, 0, NULL, // IG_AMMO_LOWER_BEGINCAP
SG_GRAPHIC, 0.0, 578, 458, 0, 12, "gfx/interface/ammobar", NULL, 0, 0, CT_LTPURPLE1, 0, NULL, // IG_AMMO_SLIDERFULL
SG_GRAPHIC, 0.0, 0, 458, 0, 12, "gfx/interface/ammobar", NULL, 0, 0, CT_DKPURPLE1, 0, NULL, // IG_AMMO_SLIDEREMPTY
SG_GRAPHIC, 0.0, 607, 458, 16, 16, "gfx/interface/ammolowercap2", NULL, 0, 572, CT_LTPURPLE2, 0, NULL, // IG_AMMO_LOWER_ENDCAP
SG_NUMBER, 0.0, 573, 425, 16, 32, NULL, NULL, 0, 0, CT_YELLOW, NUM_FONT_BIG, NULL, // IG_AMMO_COUNT
SG_VAR, 0.0, 0, 0, 0, 0, NULL, NULL, 0, 0, CT_NONE, 0, NULL, // IG_AMMO_END
};
vec3_t vfwd;
vec3_t vright;
vec3_t vup;
vec3_t vfwd_n;
vec3_t vright_n;
vec3_t vup_n;
int infoStringCount;
//===============================================================
/*
================
CG_Draw3DModel
================
*/
static 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;
CG_AdjustFrom640( &x, &y, &w, &h );
memset( &refdef, 0, sizeof( refdef ) );
memset( &ent, 0, sizeof( ent ) );
AnglesToAxis( angles, ent.axis );
VectorCopy( origin, ent.origin );
ent.hModel = model;
ent.customSkin = skin;
ent.renderfx = RF_NOSHADOW; // no stencil shadows
refdef.rdflags = RDF_NOWORLDMODEL;
AxisClear( refdef.viewaxis );
refdef.fov_x = 30;
refdef.fov_y = 30;
refdef.x = x;
refdef.y = y;
refdef.width = w;
refdef.height = h;
refdef.time = cg.time;
cgi_R_ClearScene();
cgi_R_AddRefEntityToScene( &ent );
cgi_R_RenderScene( &refdef );
}
/*
================
CG_DrawHead
Used for both the status bar and the scoreboard
================
*/
int USE_ENT_NUM = 2048;
void CG_DrawHead( float x, float y, float w, float h, int speaker_i, vec3_t headAngles )
{
qhandle_t hm = 0;
qhandle_t hs = 0;
float len;
vec3_t origin;
vec3_t mins, maxs;
gentity_t *ent;
qboolean extensions = qfalse;
int talking = 0;
//If the talking ent is actually on the level, use his info
if ( cg.gameTextEntNum != -1 && cg.gameTextEntNum < ENTITYNUM_WORLD )
{
ent = &g_entities[cg.gameTextEntNum];
if ( ent && ent->client )
{
hm = ent->client->clientInfo.headModel;
if ( hm )
{
hs = ent->client->clientInfo.headSkin;
extensions = ent->client->clientInfo.extensions;
talking = gi.S_Override[ent->s.number];
}
}
}
if ( !hm )
{
hm = cgs.model_draw[speakerTable[speaker_i].headModel];
if ( !hm )
{//this will happen with the computer
return;
}
//PRECACHE ME!!! But only if we're needed on this map!
hs = speakerTable[speaker_i].headSkin;//fabs(cgi_R_RegisterSkin( va("models/players/%s/head_%s.skin", speakerTable[speaker_i].headModelFile, speakerTable[speaker_i].skinName ) ) );
//fabs because a < 0 skin number just means it has extensions
extensions = speakerTable[speaker_i].extensions;
//get talking from player, we're presuming that if it isn't playing on an NPC, it's playing on the player
talking = gi.S_Override[0];
}
if ( !talking )
{//no sound playing, don't display the head any more
cg.gameNextTextTime = cg.time;
return;
}
//add talking anim
if ( extensions && talking > 0 )
{
hs = hs + talking;
}
// offset the origin y and z to center the head
cgi_R_ModelBounds( hm, 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 )
CG_Draw3DModel( x, y, w, h, hm, hs, origin, headAngles );
}
/*
================
CG_DrawTalk
================
*/
static void CG_DrawTalk(centity_t *cent)
{
float size;
vec3_t angles;
// int totalLines,y,i;
vec4_t color;
if ( cg.gameNextTextTime > cg.time)
{
color[0] = colorTable[CT_BLACK][0];
color[1] = colorTable[CT_BLACK][1];
color[2] = colorTable[CT_BLACK][2];
color[3] = 0.350F;
cgi_R_SetColor(color); // Background
CG_DrawPic( 5, 27, 50, 64, cgs.media.ammoslider );
cgi_R_SetColor(colorTable[CT_LTPURPLE1]);
CG_DrawPic( 5, 6, 128, 64, cgs.media.talkingtop );
/*
totalLines = cg.scrollTextLines - cg.gameTextCurrentLine;
y = 6;
CG_DrawPic( 55, y, 16, 16, cgs.media.bracketlu );
CG_DrawPic( 616, y, 16, 16, cgs.media.bracketru );
for (i=1;i<totalLines;++i)
{
y +=16;
CG_DrawPic( 55, y, 16, 16, cgs.media.ammoslider );
CG_DrawPic( 616,y, 16, 16, cgs.media.ammoslider );
}
y +=16;
CG_DrawPic( 55, y, 16, 16, cgs.media.bracketld );
CG_DrawPic( 616,y, 16, 16, cgs.media.bracketrd );
*/
size = ICON_SIZE * 1.5;
VectorClear( angles );
angles[YAW] = 180;
CG_DrawHead( -6, 25, size, size, cg.gameTextSpeaker, angles );
cgi_R_SetColor(colorTable[CT_LTPURPLE1]); // Bottom
CG_DrawPic( 5, 90, 64, 16, cgs.media.talkingbot );
cgi_R_SetColor(NULL);
}
}
/*
================
CG_DrawArmor
================
*/
static void CG_DrawArmor(centity_t *cent)
{
int max,i;
float value,xLength;
playerState_t *ps;
int lengthMax;
ps = &cg.snap->ps;
value = ps->stats[STAT_ARMOR];
for (i=IG_ARMOR_START + 1;i<IG_ARMOR_END;++i)
{
if (interface_graphics[i].type == SG_GRAPHIC)
{
cgi_R_SetColor(colorTable[interface_graphics[i].color]);
CG_DrawPic( interface_graphics[i].x,
interface_graphics[i].y,
interface_graphics[i].width,
interface_graphics[i].height,
interface_graphics[i].graphic);
}
}
interface_graphics[IG_ARMOR_COUNT].max = value;
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 = MC_YELLOW; // Numbers
}
max = ps->stats[STAT_MAX_HEALTH];
lengthMax = 73;
if (max > 0)
{
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;
int flashHealth;
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;
flashHealth = ps->stats[STAT_MAX_HEALTH]/4;
// Is health changing?
if (value<flashHealth)
{
interface_graphics[IG_HEALTH_COUNT].color = CT_RED; // Numbers
interface_graphics[IG_HEALTH_SLIDERFULL].color = CT_RED;
interface_graphics[IG_HEALTH_SLIDERFULL].style |= CG_BLINK;
interface_graphics[IG_HEALTH_SLIDEREMPTY].color = CT_DKRED;
interface_graphics[IG_HEALTH_SLIDEREMPTY].style |= CG_BLINK;
interface_graphics[IG_HEALTH_BEGINCAP].color = CT_DKRED;
interface_graphics[IG_HEALTH_BOX1].color = CT_DKRED;
interface_graphics[IG_HEALTH_ENDCAP].color = CT_DKRED;
}
else if (cg.oldHealthTime < cg.time)
{
interface_graphics[IG_HEALTH_COUNT].color = CT_LTBROWN1; // Numbers
interface_graphics[IG_HEALTH_SLIDERFULL].color = CT_LTBROWN1;
interface_graphics[IG_HEALTH_SLIDEREMPTY].color = CT_DKBROWN1;
interface_graphics[IG_HEALTH_BEGINCAP].color = CT_DKBROWN1;
interface_graphics[IG_HEALTH_BOX1].color = CT_DKBROWN1;
interface_graphics[IG_HEALTH_ENDCAP].color = CT_DKBROWN1;
interface_graphics[IG_HEALTH_SLIDERFULL].style &= ~ CG_BLINK;
interface_graphics[IG_HEALTH_SLIDEREMPTY].style &= ~ CG_BLINK;
}
else
{
interface_graphics[IG_HEALTH_COUNT].color = MC_YELLOW; // Numbers
interface_graphics[IG_HEALTH_SLIDERFULL].color = CT_LTBROWN1;
interface_graphics[IG_HEALTH_SLIDEREMPTY].color = CT_DKBROWN1;
interface_graphics[IG_HEALTH_BEGINCAP].color = CT_DKBROWN1;
interface_graphics[IG_HEALTH_BOX1].color = CT_DKBROWN1;
interface_graphics[IG_HEALTH_ENDCAP].color = CT_DKBROWN1;
interface_graphics[IG_HEALTH_SLIDERFULL].style &= ~ CG_BLINK;
interface_graphics[IG_HEALTH_SLIDEREMPTY].style &= ~ CG_BLINK;
}
interface_graphics[IG_HEALTH_COUNT].max = value;
// Calculating size of health bar
max = ps->stats[STAT_MAX_HEALTH];
lengthMax = 73;
if (max > 0)
{
xLength = lengthMax * (value/max);
}
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_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;
if (!cent->currentState.weapon ) // We don't have a weapon right now
{
return;
}
value = ps->ammo[weaponData[cent->currentState.weapon].ammoIndex];
if (value < 0) // No ammo
{
return;
}
interface_graphics[IG_AMMO_COUNT].max = value;
if (weaponData[ps->weapon].ammoIndex == AMMO_STARFLEET)
{
brightColor_i = CT_LTBLUE2;
darkColor_i = CT_DKBLUE2;
}
else if (weaponData[ps->weapon].ammoIndex == AMMO_ALIEN)
{
brightColor_i = CT_LTPURPLE2;
darkColor_i = CT_DKPURPLE2;
}
else
{
brightColor_i = CT_LTGOLD1;
darkColor_i = CT_DKGOLD1;
}
//
// ammo
//
if (cg.oldammo < value)
{
cg.oldAmmoTime = cg.time + 200;
}
cg.oldammo = value;
if (( cg.predicted_player_state.weaponstate == WEAPON_FIRING
&& cg.predicted_player_state.weaponTime > 100 ))
{
// draw as dark grey when reloading
numColor_i = CT_LTGREY;
}
else
{
if ( value >= 0 )
{
if (cg.oldAmmoTime > cg.time)
{
numColor_i = CT_YELLOW;
}
else
{
numColor_i = brightColor_i;
}
}
else
{
numColor_i = CT_RED;
}
}
// Calc bar length
max = ammoData[weaponData[cent->currentState.weapon].ammoIndex].max;
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_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))
{
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.interfaceSnd1);
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)
{
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.interfaceSnd1);
}
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)
{
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.interfaceSnd1);
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;
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.interfaceSnd2);
cg.interfaceStartupDone = 1; // All done
}
interface_graphics[IG_GROW].timer = cg.time + 10;
}
cg.interfaceStartupTime = cg.time;
}
/*
================
CG_DrawZoomMask
================
*/
extern float cg_zoomFov; //from cg_view.cpp
static void CG_DrawZoomMask( void )
{
float amt = 1, size, val, start_x, start_y;
int width, height, i, t;
vec4_t color1;
if ( cg.weaponSelect == WP_PROTON_GUN )
{
cg.zoomLocked = qfalse;
cg.zoomed = qfalse;
return;
}
// 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--then draw fullscreen mask
cgi_R_SetColor( color1 );
CG_DrawPic( start_x, start_y, width, height, cgs.media.zoomMaskShader );
start_x = 210;
start_y = 80;
// Main curvy zoom art
CG_DrawPic( 320 + start_x + 35, 241, -35, -170, cgs.media.zoomBar2Shader);
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 - 35, 239, 35, 170, cgs.media.zoomBar2Shader);
// Draw the array of ticks inset into the curvy art
CG_DrawPic( 320 + start_x + 12, 245, 10, 108, cgs.media.zoomInsertShader );
CG_DrawPic( 320 + start_x + 12, 127, 10, 108, cgs.media.zoomInsert2Shader );
CG_DrawPic( 320 - start_x - 12, 353, -10, -108, cgs.media.zoomInsert2Shader );
CG_DrawPic( 320 - start_x - 12, 235, -10, -108, cgs.media.zoomInsertShader );
// pink
color1[0] = 0.85f * amt;
color1[1] = 0.55f * amt;
color1[2] = 0.75f * amt;
color1[3] = amt;
cgi_R_SetColor( color1 );
// Calculate a percent and clamp it
val = 26 - ( cg_fov.value - cg_zoomFov ) / ( cg_fov.value - MAX_ZOOM_FOV ) * 26;
if ( val > 17.0f )
val = 17.0f;
else if ( val < 0.0f )
val = 0.0f;
i = ((int)val) * 6;
// Draw the tick at the current zoom position
CG_DrawPic( 320 + start_x + 10, 230 - i, 12, 5, cgs.media.ammoslider );
CG_DrawPic( 320 + start_x + 10, 245 + i, 12, 5, cgs.media.ammoslider );
CG_DrawPic( 320 - start_x - 22, 230 - i, 12, 5, cgs.media.ammoslider );
CG_DrawPic( 320 - start_x - 22, 245 + i, 12, 5, cgs.media.ammoslider );
// Convert zoom and view axis into some numbers to throw onto the screen
CG_DrawNumField( 468, 100, 5, (120 - cg_zoomFov) * 500, 18, 10 ,NUM_FONT_BIG, qtrue );
CG_DrawNumField( 468, 120, 5, ceil(g_crosshairEntDist * 24), 18, 10,NUM_FONT_BIG, qtrue );
CG_DrawNumField( 468, 140, 5, cg.refdef.viewaxis[0][0] * 9999 + 20000, 18, 10,NUM_FONT_BIG, qtrue );
CG_DrawNumField( 468, 160, 5, cg.refdef.viewaxis[0][1] * 9999 + 20000, 18, 10,NUM_FONT_BIG, qtrue );
CG_DrawNumField( 468, 180, 5, cg.refdef.viewaxis[0][2] * 9999 + 20000, 18, 10,NUM_FONT_BIG, qtrue );
// Is it time to draw the little max zoom arrows?
if ( val < 0.2f )
{
amt = sin( cg.time * 0.03 ) * 0.5 + 0.5 * amt;
for ( t = 0; t < 4; t++ )
color1[t] = interfaceColors[4][t] * amt;
cgi_R_SetColor( color1 );
// Draw the arrow centered on either side of the zoom art
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;
}
cgi_R_SetColor( color1 );
CG_DrawPic( start_x, start_y, width, height, cgs.media.zoomMaskShader );
}
}
}
/*
================
CG_DrawTeleportEffects
================
*/
static void CG_DrawTeleportEffects( void )
{
centity_t *cent;
cent = &cg_entities[0];
if ( cg.snap->ps.clientNum != 0 || cg.renderingThirdPerson || !cent->gent )
{
return;
}
if ( cent->gent->s.powerups & ( 1 << PW_INVIS ) )
{
float clr = ( cent->gent->client->ps.powerups[PW_INVIS] - 2000 - cg.time ) / 2000.0f;
vec4_t color;
if ( clr < 0 )
clr = 0;
if ( clr > 1 )
clr = 1;
color[0] =
color[1] =
color[2] = clr;
color[3] = 1.0f;
cgi_R_SetColor( color );
CG_DrawPic( 0, 0, 640, 480, cgs.media.playerTeleportShader );
if ( cent->gent->client->ps.powerups[PW_INVIS] - 2000 < cg.time )
{
cent->gent->s.powerups &= ~( 1 << PW_INVIS );
cent->gent->client->ps.powerups[PW_INVIS] = 0;
}
}
else if ( cent->gent->s.powerups & ( 1 << PW_QUAD ) )
{
float clr = ( cent->gent->client->ps.powerups[PW_QUAD] - 2000 - cg.time ) / 2000.0f;
vec4_t color;
if ( clr < 0 )
clr = 0;
if ( clr > 1 )
clr = 1;
color[0] =
color[1] =
color[2] = clr;
color[3] = 1.0f;
cgi_R_SetColor( color );
CG_DrawPic( 0, 0, 640, 480, cgs.media.playerTeleportShader );
if ( cent->gent->client->ps.powerups[PW_QUAD] - 2000 < cg.time )
{
cent->gent->s.powerups &= ~( 1 << PW_QUAD );
cent->gent->client->ps.powerups[PW_QUAD] = 0;
}
}
}
/*
================
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();
}
CG_DrawArmor(cent);
CG_DrawHealth(cent);
CG_DrawAmmo(cent);
CG_DrawTalk(cent);
}
/*
===================
CG_DrawPickupItem
===================
*/
static void CG_DrawPickupItem( void ) {
int value;
float *fadeColor;
value = cg.itemPickup;
if ( value ) {
fadeColor = CG_FadeColor( cg.itemPickupTime, 3000 );
if ( fadeColor ) {
CG_RegisterItemVisuals( value );
// cgi_R_SetColor( fadeColor );
// CG_DrawPic( 8, 380, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
// CG_DrawBigString( ICON_SIZE + 16, 398, bg_itemlist[ value ].pickup_name, fadeColor[0] );
// CG_DrawProportionalString( ICON_SIZE + 16, 398,
// bg_itemlist[ value ].pickup_name, CG_SMALLFONT,fadeColor );
// cgi_R_SetColor( NULL );
}
}
}
/*
===================
CG_DrawHoldableItem
===================
*/
/*
static void CG_DrawHoldableItem( void ) {
int value;
value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM];
if ( value ) {
CG_RegisterItemVisuals( value );
CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
}
}
*/
/*
================================================================================
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.renderingThirdPerson )
{
return;
}
// Don't bother drawing the crosshairs when we don't have a weapon
if ( cg.snap->ps.weapon == WP_NONE )
{
return;
}
//NOTE: Maybe have crosshair turn red over enemies and green over allies?
// set color based on health
if ( cg_crosshairHealth.integer ) {
vec4_t hcolor;
CG_ColorForHealth( hcolor );
cgi_R_SetColor( hcolor );
} else {
//set color based on what kind of ent is under crosshair
if ( g_crosshairEntNum >= ENTITYNUM_WORLD )
{
cgi_R_SetColor( NULL );
}
else
{
vec4_t ecolor;
gentity_t *crossEnt = &g_entities[g_crosshairEntNum];
if ( crossEnt->client )
{
if ( crossEnt->client->playerTeam == TEAM_STARFLEET )
{
//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
}
}
else
{
VectorCopy( crossEnt->startRGBA, ecolor );
if ( !ecolor[0] && !ecolor[1] && !ecolor[2] )
{
// We really don't want black, so set it to yellow
ecolor[0] = 0.9F;//R
ecolor[1] = 0.7F;//G
ecolor[2] = 0.0F;//B
}
}
ecolor[3] = 1.0;
cgi_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 );
}
x = cg_crosshairX.integer;
y = cg_crosshairY.integer;
CG_AdjustFrom640( &x, &y, &w, &h );
hShader = cgs.media.crosshairShader[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ];
cgi_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 );
}
/*
qboolean CG_WorldCoordToScreenCoord(vec3_t worldCoord, int *x, int *y)
Take any world coord and convert it to a 2D virtual 640x480 screen coord
*/
static qboolean CG_WorldCoordToScreenCoord(vec3_t worldCoord, int *x, int *y, qboolean clamp)
{
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)
{
if ( clamp )
{
transformed[2] = 0.01f;
}
else
{
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 = (int)(xcenter + xzi * transformed[0]);
*y = (int)(ycenter - yzi * transformed[1]);
return qtrue;
}
/*
=================
CG_LabelCrosshairEntity
=================
*/
static void CG_LabelViewEntity( gentity_t *crossEnt, char *name, qboolean scanAll, vec4_t color, qboolean drawHealth, char *rank, char *race, char *height, char *weight, char *weapon )
{//ID teammates, ID enemies, ID objectives, etc.
vec3_t center, maxs, mins, top, bottom, topLeft, topRight, bottomLeft, bottomRight;
vec3_t worldEast = {1.0f, 0, 0}, worldNorth = {0, 1.0f, 0}, worldUp = {0, 0, 1.0f};
vec4_t hcolor;
int x = 0, y = 0;
int topLeftx, topLefty, topRightx, topRighty, bottomLeftx, bottomLefty, bottomRightx, bottomRighty;
int corner, topSize, bottomSize, leftSize, rightSize, health;
int charIndex, rankCharIndex, raceCharIndex, htCharIndex, wtCharIndex, weapCharIndex;
float lineHorzLength = 8.0f, lineVertLength = 8.0f, lineWidth = 2.0f;
float fUpDot, fEastDot, fNorthDot, uNorthDot, uEastDot, hwidth;//, timedScale = 1.0f;
qboolean doTopLeft = qfalse;
qboolean doTopRight = qfalse;
qboolean doBottomLeft = qfalse;
qboolean doBottomRight = qfalse;
qboolean doSizes = qtrue;
float w;
char showName[1024];
char showRank[1024];
char showRace[1024];
char showHt[1024];
char showWt[1024];
char showWeap[1024];
infoStringCount += cg.frametime;
rankCharIndex = raceCharIndex = htCharIndex = wtCharIndex = weapCharIndex = charIndex = floor(infoStringCount/33);
//TODO: have box scale in from corners of screen? Or out from center?
/*
if(infoStringCount < 1000)
{
timedScale = (float)infoStringCount/100.0f;
timedScale = 10.0f - timedScale;
if(timedScale < 1.0f)
{
timedScale = 1.0f;
}
}
*/
//IDEA: We COULD actually rotate a wire-mesh version of the crossEnt until it
// matches the crossEnt's angles then flash it and pop up this info...
// but that would be way too much work for something like this.
// Alternately, could rotate a scaled-down fully-skinned version
// next to it, but that, too, might be overkill... (plus, model would
// need back faces)
//FIXME: can be optimized...
//Draw frame around ent's bbox
//FIXME: make global, do once
fUpDot = 1.0f - fabs( DotProduct( vfwd_n, worldUp ) );//1.0 if looking up or down, so use mins and maxs more
fEastDot = fabs( DotProduct( vfwd_n, worldEast ) );//1.0 if looking east or west, so use mins[1] and maxs[1] more
fNorthDot = fabs( DotProduct( vfwd_n, worldNorth ) );//1.0 if looking north or south, so use mins[0] and maxs[0] more
uEastDot = fabs( DotProduct( vup_n, worldEast ) );//1.0 if looking up or down, head towards east or west, so use mins[0] and maxs[0] more
uNorthDot = fabs( DotProduct( vup_n, worldNorth ) );//1.0 if looking up or down, head towards north or south, so use mins[1] and maxs[1] more
if ( crossEnt->s.solid == SOLID_BMODEL )
{//brush model, no origin, so use the center
VectorAdd( crossEnt->absmin, crossEnt->absmax, center );
VectorScale( center, 0.5, center );
VectorSubtract( crossEnt->absmax, center, maxs );
VectorSubtract( crossEnt->absmin, center, mins );
}
else
{
VectorCopy( crossEnt->currentOrigin, center );
VectorCopy( crossEnt->maxs, maxs );
VectorCopy( crossEnt->mins, mins );
}
//NOTE: this presumes that mins[0] and maxs[0] are symmetrical and mins[1] and maxs[1] as well
topSize = (maxs[2]*fUpDot + maxs[1]*uNorthDot + maxs[0]*uEastDot);//* timedScale
bottomSize = (mins[2]*fUpDot + mins[1]*uNorthDot + mins[0]*uEastDot);//* timedScale
leftSize = (fUpDot*(mins[0]*fNorthDot + mins[1]*fEastDot) + mins[0]*uNorthDot + mins[1]*uEastDot);//* timedScale
rightSize = (fUpDot*(maxs[0]*fNorthDot + maxs[1]*fEastDot) + maxs[0]*uNorthDot + maxs[1]*uEastDot);//* timedScale
//Find corners
//top
VectorMA( center, topSize, vup_n, top );
//bottom
VectorMA( center, bottomSize, vup_n, bottom );
//Top-left frame
VectorMA( top, leftSize, vright_n, topLeft );
//Top-right frame
VectorMA( top, rightSize, vright_n, topRight );
//bottom-left frame
VectorMA( bottom, leftSize, vright_n, bottomLeft );
//bottom-right frame
VectorMA( bottom, rightSize, vright_n, bottomRight );
if ( CG_WorldCoordToScreenCoord( topLeft, &topLeftx, &topLefty, qfalse ) )
{
doTopLeft = qtrue;
}
else
{
doSizes = qfalse;
}
if ( CG_WorldCoordToScreenCoord( topRight, &topRightx, &topRighty, qfalse ) )
{
doTopRight = qtrue;
}
else
{
doSizes = qfalse;
}
if ( CG_WorldCoordToScreenCoord( bottomLeft, &bottomLeftx, &bottomLefty, qfalse ) )
{
doBottomLeft = qtrue;
}
else
{
doSizes = qfalse;
}
if ( CG_WorldCoordToScreenCoord( bottomRight, &bottomRightx, &bottomRighty, qfalse ) )
{
doBottomRight = qtrue;
}
else
{
doSizes = qfalse;
}
//NOTE: maybe print color-coded "Primary/Secondary Objective" on top if an objective?
for ( corner = 0; corner < 11; corner++ )
{//FIXME: make sure line length of 8 isn't greater than width of object
switch ( corner )
{
case 0://top-left
if ( doTopLeft )
{
if ( doSizes )
{
//Line lengths
lineVertLength = (bottomLefty-topLefty)*0.25f;
lineHorzLength = (topRightx-topLeftx)*0.25f;
}
CG_FillRect( topLeftx + 2, topLefty, lineHorzLength, lineWidth, color );
CG_FillRect( topLeftx, topLefty, lineWidth, lineVertLength, color );
}
break;
case 1://top-right
if ( doTopRight )
{
if ( doSizes )
{
//Line lengths
lineVertLength = (bottomRighty-topRighty)*0.25f;
lineHorzLength = (topRightx-topLeftx)*0.25f;
}
CG_FillRect( topRightx-lineHorzLength, topRighty, lineHorzLength, lineWidth, color );
CG_FillRect( topRightx, topRighty, lineWidth, lineVertLength, color );
}
break;
case 2://bottom-left
if ( doBottomLeft )
{
if ( doSizes )
{
//Line lengths
lineVertLength = (bottomLefty-topLefty)*0.25f;
lineHorzLength = (bottomRightx-bottomLeftx)*0.25f;
}
CG_FillRect( bottomLeftx, bottomLefty, lineHorzLength, lineWidth, color );
CG_FillRect( bottomLeftx, bottomLefty-lineVertLength, lineWidth, lineVertLength, color );
}
break;
case 3://bottom-right
if ( doBottomRight )
{
if ( doSizes )
{
//Line lengths
lineVertLength = (bottomRighty-topRighty)*0.25f;
lineHorzLength = (bottomRightx-bottomLeftx)*0.25f;
}
CG_FillRect( bottomRightx-lineHorzLength, bottomRighty, lineHorzLength, lineWidth, color );
CG_FillRect( bottomRightx, bottomRighty-lineVertLength, lineWidth, lineVertLength + 2, color );
}
break;
case 4://healthBar
if ( charIndex > 0 )
{
/*
//tried to keep original functionality, but it would pop from top to bottom
//when you let go of the button and had no way to tell then (during the
//fade-out) whether it should be on top or bottom. So now it is always on top.
if ( !scanAll )
{
if ( !CG_WorldCoordToScreenCoord( bottom, &x, &y, qfalse ) )
{//Can't draw bottom
return;
}
}
else
*/
{//try to draw at top as to not obscure the tricorder
CG_WorldCoordToScreenCoord( top, &x, &y, qtrue );
if ( y > 0.01 )
{
y -= SMALLCHAR_HEIGHT;
if ( y > 0.01 )
{
if ( charIndex > 0 && name )
{
if ( y >= SMALLCHAR_HEIGHT )
{
y -= SMALLCHAR_HEIGHT;
}
else
{
y = 0.01;
}
}
if ( y > 0.01 )
{
if ( rankCharIndex > 0 && rank )
{
if ( y >= SMALLCHAR_HEIGHT )
{
y -= SMALLCHAR_HEIGHT;
}
}
if ( y > 0.01 )
{
if ( raceCharIndex > 0 && race )
{
if ( y >= SMALLCHAR_HEIGHT )
{
y -= SMALLCHAR_HEIGHT;
}
}
if ( y > 0.01 )
{
if ( htCharIndex > 0 && height )
{
if ( y >= SMALLCHAR_HEIGHT )
{
y -= SMALLCHAR_HEIGHT;
}
}
if ( y > 0.01 )
{
if ( wtCharIndex > 0 && weight )
{
if ( y >= SMALLCHAR_HEIGHT )
{
y -= SMALLCHAR_HEIGHT;
}
}
if ( y > 0.01 )
{
if ( weapCharIndex > 0 && weapon )
{
if ( y >= SMALLCHAR_HEIGHT )
{
y -= SMALLCHAR_HEIGHT;
}
}
}
}
}
}
}
}
}
}
if ( !drawHealth )
{
continue;
}
health = ceil( (float)crossEnt->health/(float)crossEnt->max_health*100.0f );
CG_ColorForGivenHealth( hcolor, health );
hwidth = (float)health*0.5f;
y += lineWidth + 2;
CG_FillRect( x - hwidth/2, y + lineWidth, hwidth, lineWidth*2, hcolor );
y += lineWidth*2;
}
break;
case 5://infoString (name/description)
//Bright yellow
VectorCopy( crossEnt->startRGBA, color );
if ( !color[0] && !color[1] && !color[2] )
{
// We really don't want black, so set it to yellow
color[0] = 0.9F;//R
color[1] = 0.7F;//G
color[2] = 0.0F;//B
}
color[3] = 0.75;
if ( charIndex > 0 && name )
{
int len = strlen(name);
if ( charIndex > len+1 )
{
charIndex = len+1;
}
else
{
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.tedTextSound );
}
Q_strncpyz( showName, name, charIndex );
w = CG_DrawStrlen( name ) * SMALLCHAR_WIDTH;
CG_DrawSmallStringColor( x - w / 2, y + lineWidth, showName, color );
y += SMALLCHAR_HEIGHT;
}
break;
case 6://rank
if ( rankCharIndex > 0 && rank )
{
int len = strlen(rank);
if ( rankCharIndex > len+1 )
{
rankCharIndex = len+1;
}
else
{
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.tedTextSound );
}
Q_strncpyz( showRank, rank, rankCharIndex );
w = CG_DrawStrlen( rank ) * SMALLCHAR_WIDTH;
CG_DrawSmallStringColor( x - w / 2, y + lineWidth, showRank, color );
y += SMALLCHAR_HEIGHT;
}
break;
case 7://race
if ( raceCharIndex > 0 && race )
{
int len = strlen(race);
if ( raceCharIndex > len+1 )
{
raceCharIndex = len+1;
}
else
{
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.tedTextSound );
}
Q_strncpyz( showRace, race, raceCharIndex );
w = CG_DrawStrlen( race ) * SMALLCHAR_WIDTH;
CG_DrawSmallStringColor( x - w / 2, y + lineWidth, showRace, color );
y += SMALLCHAR_HEIGHT;
}
break;
case 8://height
if ( htCharIndex > 0 && height )
{
int len = strlen(height);
if ( htCharIndex > len+1 )
{
htCharIndex = len+1;
}
else
{
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.tedTextSound );
}
Q_strncpyz( showHt, height, htCharIndex );
w = CG_DrawStrlen( height ) * SMALLCHAR_WIDTH;
CG_DrawSmallStringColor( x - w / 2, y + lineWidth, showHt, color );
y += SMALLCHAR_HEIGHT;
}
break;
case 9://weight
if ( wtCharIndex > 0 && weight )
{
int len = strlen(weight);
if ( wtCharIndex > len+1 )
{
wtCharIndex = len+1;
}
else
{
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.tedTextSound );
}
Q_strncpyz( showWt, weight, wtCharIndex );
w = CG_DrawStrlen( weight ) * SMALLCHAR_WIDTH;
CG_DrawSmallStringColor( x - w / 2, y + lineWidth, showWt, color );
y += SMALLCHAR_HEIGHT;
}
break;
case 10://weapon
if ( weapCharIndex > 0 && weapon )
{
int len = strlen(weapon);
if ( weapCharIndex > len+1 )
{
weapCharIndex = len+1;
}
else
{
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.tedTextSound );
}
Q_strncpyz( showWeap, weapon, weapCharIndex );
w = CG_DrawStrlen( weapon ) * SMALLCHAR_WIDTH;
CG_DrawSmallStringColor( x - w / 2, y + lineWidth, showWeap, color );
y += SMALLCHAR_HEIGHT;
}
break;
}
}
}
/*
=================
CG_ScanForCrosshairEntity
=================
*/
static void CG_ScanForCrosshairEntity( qboolean scanAll )
{
trace_t trace;
gentity_t *traceEnt;
vec3_t start, end;
int content;
//FIXME: debounce this to about 10fps?
VectorCopy( cg.refdef.vieworg, start );
VectorMA( start, 4096, cg.refdef.viewaxis[0], end );//was 8192
//YES! This is very very bad... but it works! James made me do it. Really, he did. Blame James.
gi.trace( &trace, start, vec3_origin, vec3_origin, end,
cg.snap->ps.clientNum, MASK_OPAQUE|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE );
/*
CG_Trace( &trace, start, vec3_origin, vec3_origin, end,
cg.snap->ps.clientNum, MASK_PLAYERSOLID|CONTENTS_CORPSE|CONTENTS_ITEM );
*/
//FIXME: pick up corpses
traceEnt = &g_entities[trace.entityNum];
// if the object is "dead", don't show it
/* if ( cg.crosshairClientNum && g_entities[cg.crosshairClientNum].health <= 0 )
{
cg.crosshairClientNum = 0;
return;
}
*/
g_crosshairEntNum = trace.entityNum;
g_crosshairEntDist = 4096*trace.fraction;
if ( !traceEnt )
{//not looking at anything
g_crosshairSameEntTime = 0;
g_crosshairEntTime = 0;
}
else
{//looking at a valid ent
//store the distance
if ( trace.entityNum != g_crosshairEntNum )
{//new crosshair ent
g_crosshairSameEntTime = 0;
}
else if ( g_crosshairEntDist < 256 )
{//close enough to start counting how long you've been looking
g_crosshairSameEntTime += cg.frametime;
}
//remember the last time you looked at the person
g_crosshairEntTime = cg.time;
}
if ( !traceEnt || !traceEnt->infoString || !traceEnt->infoString[0] || (traceEnt->s.eFlags & EF_NO_TED) )
{
if ( traceEnt && scanAll )
{
}
else
{
return;
}
}
// if the player is in fog, don't show it
content = cgi_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
if ( cg.crosshairClientNum != trace.entityNum )
{
infoStringCount = 0;
}
cg.crosshairClientNum = trace.entityNum;
cg.crosshairClientTime = cg.time;
}
static char *RaceNameForEnum( int race ) {
switch(race)
{
case RACE_HUMAN:
return ingame_text[IGT_HUMAN];
break;
case RACE_BORG:
return ingame_text[IGT_BORG];
break;
case RACE_PARASITE:
return ingame_text[IGT_FERROVORE];
break;
case RACE_KLINGON:
return ingame_text[IGT_KLINGON];
break;
case RACE_MALON:
return ingame_text[IGT_MALON];
break;
case RACE_HIROGEN:
return ingame_text[IGT_HIROGEN];
break;
case RACE_STASIS:
return ingame_text[IGT_ETHERIAN];
break;
case RACE_8472:
return ingame_text[IGT_SPECIES8472];
break;
case RACE_BOT:
return ingame_text[IGT_DREADBOT];
break;
case RACE_HARVESTER:
return ingame_text[IGT_HARVESTER];
break;
case RACE_REAVER:
return ingame_text[IGT_REAVER];
break;
case RACE_AVATAR:
return ingame_text[IGT_AVATAR];
break;
case RACE_VULCAN:
return ingame_text[IGT_VULCAN];
break;
case RACE_BETAZOID:
return ingame_text[IGT_BETAZOID];
break;
case RACE_BOLIAN:
return ingame_text[IGT_BOLIAN];
break;
case RACE_TALAXIAN:
return ingame_text[IGT_TALAXIAN];
break;
case RACE_BAJORAN:
return ingame_text[IGT_BAJORAN];
break;
case RACE_HOLOGRAM:
return ingame_text[IGT_PHOTONIC];
break;
default:
case RACE_NONE:
return ingame_text[IGT_UNKNOWN];
break;
}
}
static char *RankNameForEnum( int rank ) {
switch ( rank )
{
default:
case RANK_CIVILIAN:
return ingame_text[IGT_CIVILIAN];
break;
case RANK_CREWMAN:
return ingame_text[IGT_CREWMAN];
break;
case RANK_ENSIGN:
return ingame_text[IGT_ENSIGN];
break;
case RANK_LT_JG:
return ingame_text[IGT_LTJG];
break;
case RANK_LT:
return ingame_text[IGT_LT];
break;
case RANK_LT_COMM:
return ingame_text[IGT_LTCOMMANDER];
break;
case RANK_COMMANDER:
return ingame_text[IGT_COMMANDER];
break;
case RANK_CAPTAIN:
return ingame_text[IGT_CAPTAIN];
break;
}
}
/*
=====================
CG_DrawCrosshairNames
=====================
*/
extern void CG_ColorForGivenHealth( vec4_t hcolor, int health );
static void CG_DrawCrosshairNames( void ) {
float *color;
gentity_t *crossEnt;
char *string;
int textI;
qboolean scanAll = qfalse;
centity_t *player = &cg_entities[0];
if ( (!cg_drawCrosshairNames.integer&&player->gent->client->ps.weapon!=WP_TRICORDER) || cg.renderingThirdPerson ) //!cg_drawCrosshair.integer still need infoStrings if no crosshair
{
infoStringCount = 0;
return;
}
if ( !player->gent )
{
return;
}
if ( !player->gent->client )
{
return;
}
if ( player->gent->client->ps.weapon == WP_TRICORDER )
{
if ( player->gent->client->buttons & BUTTON_ATTACK )
{
scanAll = qtrue;
}
}
// scan the known entities to see if the crosshair is sighted on one
CG_ScanForCrosshairEntity( scanAll );
// draw the name of the player being looked at
//FIXME: make it fade out even if still looking at it
//FIXME: if first time, play sound
color = CG_FadeColor( cg.crosshairClientTime, 1000 );
if ( !color )
{
cgi_R_SetColor( NULL );
infoStringCount = 0;
return;
}
if(cg.crosshairClientNum > 0 && cg.crosshairClientNum < ENTITYNUM_WORLD)
{
crossEnt = &g_entities[cg.crosshairClientNum];
//if(crossEnt && (crossEnt->client || (crossEnt->svFlags & SVF_OBJECTIVE)) && crossEnt->targetname)
if ( crossEnt )
{
if ( crossEnt->client )
{
if ( player->gent->client->playerTeam == crossEnt->client->playerTeam )
{
//Allies are green
color[0] = 0.0;//R
color[1] = 1.0;//G fa
color[2] = 0.0;//B
}
else
{
//Enemies are red
color[0] = 1.0;//R
color[1] = 0.0;//G
color[2] = 0.0;//B
}
}
else
{
VectorCopy( crossEnt->startRGBA, color );
if ( !color[0] && !color[1] && !color[2] )
{
// We really don't want black, so set it to yellow
color[0] = 0.9F;//R
color[1] = 0.7F;//G
color[2] = 0.0F;//B
}
}
color[3] *= 0.5;
if ( !scanAll && crossEnt->infoString && crossEnt->infoString[0] )
{//Just drawing infoString
if (crossEnt->infoString[0] == '@')
{
textI = CG_SearchTextPrecache(crossEnt->infoString);
if (textI >= 0)
{
string = precacheText[textI].text;
}
else
{
string = crossEnt->infoString;
}
}
else
{
string = crossEnt->infoString;
}
CG_LabelViewEntity( crossEnt, string, scanAll, color, qfalse, NULL, NULL, NULL, NULL, NULL );
}
else
{//Drawing full info
char *name = NULL;
char *rank = NULL;
char *race = NULL;
vec3_t size;
float ht = 0;
float wt = 0;
char *weap = NULL;
char namestr[128];
char rankstr[128];
char racestr[128];
char htstr[128];
char wtstr[128];
char weapstr[128];
qboolean showSize = qfalse;
if ( ( crossEnt->infoString && crossEnt->infoString[0] ) || scanAll )
{
showSize = qtrue;
}
if ( showSize )
{
vec3_t maxs, mins;
VectorCopy( crossEnt->maxs, maxs );
VectorCopy( crossEnt->mins, mins );
if ( crossEnt->client && crossEnt->NPC )
{//only use the standing height of the NPCs because people can't understand the complex dynamics of height in weight in a ceiling-installed anti-gravitic plating environment
maxs[2] = crossEnt->client->standheight;
}
VectorSubtract(maxs, mins, size);
ht = (maxs[2] - mins[2]) * 3.46875;//magic number
wt = VectorLength(size)*1.4;//magic number
if ( crossEnt->client && crossEnt->NPC )
{
if ( strstr( crossEnt->client->renderInfo.legsModelName, "female" ) ||
strstr( crossEnt->client->renderInfo.legsModelName, "seven" ) )
{//crewfemale, hazardfemale or seven of nine
wt *= 0.73f;//magic number, women are lighter than men
}
}
}
if ( crossEnt->client && crossEnt->NPC )
{
race = RaceNameForEnum( crossEnt->client->race );
sprintf( racestr, "%s: %s",ingame_text[IGT_RACE], race );
if ( crossEnt->client->playerTeam == TEAM_STARFLEET )
{
if ( crossEnt->fullName && crossEnt->fullName[0] )
{
name = crossEnt->fullName;
}
else
{
name = ingame_text[IGT_DATANOTAVAILABLE];//crossEnt->targetname;
}
sprintf( namestr, "%s: %s", ingame_text[IGT_NAME],name );
rank = RankNameForEnum( crossEnt->NPC->rank );
sprintf( rankstr, "%s: %s",ingame_text[IGT_RANK], rank );
}
else if ( crossEnt->fullName && crossEnt->fullName[0] )
{
name = crossEnt->fullName;
strcpy( namestr, name );
}
else
{
name = ingame_text[IGT_UNKNOWNENTITY];
strcpy( namestr, name );
}
if ( crossEnt->client && showSize )
{
ht *= (float)(crossEnt->client->renderInfo.scaleXYZ[2])/100.0f;
wt *= (float)(crossEnt->client->renderInfo.scaleXYZ[0]+crossEnt->client->renderInfo.scaleXYZ[1]+crossEnt->client->renderInfo.scaleXYZ[2])/300.0f;
}
if ( cg_weapons[ crossEnt->client->ps.weapon ].item )
{
weap = cg_weapons[ crossEnt->client->ps.weapon ].item->pickup_name;
sprintf( weapstr, "%s: %s", ingame_text[IGT_WEAPON],weap );
}
}
else
{
if ( crossEnt->fullName && crossEnt->fullName[0] )
{
name = crossEnt->fullName;
strcpy( namestr, name );
}
else if ( crossEnt->infoString && crossEnt->infoString[0] )
{
name = crossEnt->infoString;
strcpy( namestr, name );
}
else if ( scanAll )
{
name = ingame_text[IGT_UNKNOWNOBJECT];
strcpy( namestr, name );
}
//also, Federation uses very lightweight materials, so fudge the weight of non-living things
wt /= 10.0f;
}
if ( showSize )
{
sprintf( htstr, "%s: %4.2f %s",ingame_text[IGT_HT], ht,ingame_text[IGT_CM] );
sprintf( wtstr, "%s: %4.2f %s",ingame_text[IGT_WT],wt,ingame_text[IGT_KG] );
}
CG_LabelViewEntity( crossEnt,
name ? namestr : NULL,
scanAll,
color, qtrue,
rank ? rankstr : NULL,
race ? racestr : NULL,
ht ? htstr : NULL,
wt ? wtstr : NULL,
weap ? weapstr : NULL );
}
}
else
{
infoStringCount = 0;
}
}
cgi_R_SetColor( NULL );
}
/*
void CG_DrawTED (void)
Finds any NPCs (lifesigns) in PVS within 1024, draws them as an arrow
indicating their direction
*/
static float TEDrange = 1024.0f;
#define MIN_TED_RANGE 128.0f
#define MAX_TED_RANGE 1024.0f
#define TED_ZOOM_SPEED 8.0f
#define RADAR_ACTIVATE_TIME 500
#define RADAR_DEBOUNCE_TIME 200
static void CG_DrawTED (void) {
int num;
float dist, x, y, xdiff, ydiff, xofs, yofs, w, h, angle, pangle;
float sin0, sin90, sin270;
float TEDsize = 64.0f;//Assumes graphic is 64x64
float TEDscale;
float scale;
vec3_t vec;
centity_t *cent;
centity_t *player = &cg_entities[0];
static qboolean radarOn = qfalse;
static int radarToggleDebounceTime = 0;
if ( !player->gent )
{
return;
}
if ( !player->gent->client )
{
return;
}
if ( player->gent->client->ps.weapon != WP_TRICORDER )
{//switched away from Tricorder
if ( radarOn )
{//turn it off
radarOn = qfalse;
radarToggleDebounceTime = cg.time + RADAR_DEBOUNCE_TIME;
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.triRadarSound );
}
}
else if ( player->gent->client->buttons & BUTTON_ALT_ATTACK )
{//holding alt-fire and have tricorder in-hand
if ( radarToggleDebounceTime < cg.time )
{//toggle tricorder on & off
radarOn = !radarOn;
radarToggleDebounceTime = cg.time + RADAR_DEBOUNCE_TIME;
cgi_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.triRadarSound );
}
}
if ( !radarOn && radarToggleDebounceTime < cg.time - RADAR_ACTIVATE_TIME )
{//has been off for more than half a second, scaled down by now, stop drawing it
return;
}
//Draw the display
scale = ((float)(cg.time - radarToggleDebounceTime + RADAR_DEBOUNCE_TIME))/((float)(RADAR_ACTIVATE_TIME));//scale up over 500 ms
if ( scale > 1.0 )
{
scale = 1.0;
}
if ( !radarOn )
{
scale = 1.0 - scale;
}
TEDsize *= scale;
TEDscale = TEDsize/2.0f/TEDrange;
x = 600;
y = 380;
w = h = TEDsize;
cgi_R_SetColor( NULL );
CG_DrawPic( x - (0.5 * w), y - (0.5 * h), w, h, cgs.media.TEDshader );
//Draw us
w = h = 8*scale;
pangle = player->gent->client->ps.viewangles[1];
sin0 = sin(DEG2RAD(pangle));
sin90 = sin(DEG2RAD(pangle+90));
sin270 = sin(DEG2RAD(pangle+270));
CG_DrawPic( x-(0.5*w), y-(0.5*h), w, h, cgs.media.pICONshader );
//Draw them
w = h = 4*scale;
for ( num = 0 ; num < cg.snap->numEntities ; num++ )
{
//These should all be in PVS
if ( cg.snap->entities[ num ].number == 0)
{//We draw ourselves
continue;
}
cent = &cg_entities[ cg.snap->entities[ num ].number ];
if ( !cent->gent )
{
continue;
}
if ( !cent->gent->client )
{
continue;
}
if ( !cent->gent->NPC )
{
continue;
}
VectorSubtract( cent->lerpOrigin, player->lerpOrigin, vec );
dist = VectorLengthSquared( vec );
if ( dist > TEDrange*TEDrange )
{
continue;
}
//Ok, there is an NPC in our PVS and within 1024 of our position
//Should be relative to player's facing, so as he turns, the
//ents move in the display
angle = AngleNormalize360( (cent->gent->client->ps.viewangles[1] - pangle - 135) * -1);
xdiff = (cent->lerpOrigin[0] - player->lerpOrigin[0]);
ydiff = (player->lerpOrigin[1] - cent->lerpOrigin[1]);
xofs = xdiff * sin0 + ydiff * sin90;
yofs = ydiff * sin0 + xdiff * sin270;
xofs *= TEDscale;
yofs *= TEDscale;
//FIXME: the angles backwards if facing along the y axis?!
if ( cent->gent->client->playerTeam != player->gent->client->playerTeam )
{
CG_DrawRotatePic( x+xofs, y+yofs, w, h, angle, cgs.media.eICONshader );
}
else
{
CG_DrawRotatePic( x+xofs, y+yofs, w, h, angle, cgs.media.aICONshader );
}
}
cgi_R_SetColor( NULL );
}
//console functions to change zoom range on TED
//used from cg_consolecommands
void CG_RangeIncrease_f (void){
if ( TEDrange < MAX_TED_RANGE - TED_ZOOM_SPEED ){
TEDrange += TED_ZOOM_SPEED;
} else {
TEDrange = MAX_TED_RANGE;
}
}
void CG_RangeDecrease_f (void){
if ( TEDrange > MIN_TED_RANGE + TED_ZOOM_SPEED ){
TEDrange -= TED_ZOOM_SPEED;
} else {
TEDrange = MIN_TED_RANGE;
}
}
//==============================================================================
/*
=================
CG_DrawIntermission
=================
*/
static void CG_DrawIntermission( void ) {
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;
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 );
return qtrue;
}
/*
==================
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_ProportionalStringWidth(s,UI_BIGFONT);
CG_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 = cgi_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_ProportionalStringWidth(s,UI_BIGFONT);
CG_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;
seconds = cg.time / 1000;
mins = seconds / 60;
seconds -= mins * 60;
tens = seconds / 10;
seconds -= tens * 10;
s = va( "%i:%i%i", mins, tens, seconds );
w = CG_ProportionalStringWidth(s,UI_BIGFONT);
CG_DrawProportionalString(635 - w, y + 2, s, UI_BIGFONT, colorTable[CT_LTGOLD1]);
return y + BIGCHAR_HEIGHT + 10;
}
/*
=================
CG_DrawAmmoWarning
=================
*/
static void CG_DrawAmmoWarning( void ) {
const char *s;
int w;
if ( cg_drawAmmoWarning.integer == 0 ) {
return;
}
if ( !cg.lowAmmoWarning ) {
return;
}
// Don't bother drawing the ammo warning when have no weapon selected
if ( cg.snap->ps.weapon == WP_NONE )
{
return;
}
if ( weaponData[cg.snap->ps.weapon].ammoIndex == AMMO_NONE )
{//doesn't use ammo, so no warning
return;
}
if ( cg.lowAmmoWarning == 2 ) {
s = ingame_text[IGT_INSUFFICIENTENERGY];
} else {
return;
//s = "LOW AMMO WARNING";
}
w = CG_ProportionalStringWidth(s,CG_SMALLFONT);
CG_DrawProportionalString(320 - w / 2, 64, s, CG_SMALLFONT,colorTable[CT_LTGOLD1] );
}
void CG_DrawAssimilation( void )
{
static vec4_t color;
if ( g_entities[0].s.eFlags & EF_ASSIMILATED )
{//throw the assimilation shader on view
for ( int i = 0; i < 4; i++ )
{
color[i] += 0.001f;
if ( color[i] > 3.0f )
{
color[i] = 3.0f;
}
}
if ( color[3] > 0.5f )
{
color[3] = 0.5f;
}
cgi_R_SetColor( color );
//I HATE SHADERS!! Why can't I just draw circuitry creeping across the view? Crap-ass system
cgi_R_DrawStretchPic( cg.refdef.x, cg.refdef.y, cg.refdef.width, cg.refdef.height, 0, 0, 1, 1, cgi_R_RegisterShader("gfx/effects/assimilation") );
cgi_R_SetColor( NULL );
}
else
{
color[0] = color[1] = color[2] = color[3] = 0.0f;
}
}
/*
=================
CG_Draw2D
=================
*/
static void CG_Draw2D( void )
{
centity_t *cent;
cent = &cg_entities[cg.snap->ps.clientNum];
// 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;
}
CGCam_DrawWideScreen();
// Draw this before the text so that any text won't get clipped off
if ( !cg.renderingThirdPerson && !in_camera )
{
CG_DrawZoomMask();
}
CG_DrawScrollText();
CG_DrawCaptionText();
CG_DrawGameText();
CG_DrawLCARSText();
if ( in_camera )
return;
// don't draw any status if dead
if ( cg.snap->ps.stats[STAT_HEALTH] > 0 )
{
CG_DrawTeleportEffects();
CG_DrawStats();
CG_DrawAmmoWarning();
CG_DrawCrosshair();
CG_DrawCrosshairNames();
CG_DrawTED();
CG_DrawWeaponSelect();
CG_DrawPickupItem();
CG_DrawAssimilation();
}
float y = 0;
if (cg_drawSnapshot.integer) {
y=CG_DrawSnapshot(y);
}
if (cg_drawFPS.integer) {
y=CG_DrawFPS(y);
}
if (cg_drawTimer.integer) {
y=CG_DrawTimer(y);
}
CG_DrawFollow();
// don't draw center string if scoreboard is up
if ( !CG_DrawScoreboard() ) {
CG_DrawCenterString();
}
if (cg.showInformation)
{
CG_DrawMissionInformation();
}
else if (missionInfo_Updated)
{
if (cg.predicted_player_state.pm_type != PM_DEAD)
{
if (!cg.missionInfoFlashTime)
{
cg.missionInfoFlashTime = cg.time + cg_missionInfoFlashTime.integer;
}
if (cg.missionInfoFlashTime < cg.time) // Time's up. They didn't read it.
{
cg.missionInfoFlashTime = 0;
missionInfo_Updated = qfalse;
}
if (cg_virtualVoyager.value == 0)
{
CG_DrawProportionalString((SCREEN_WIDTH/2), (SCREEN_HEIGHT/2), ingame_text[IGT_MISSIONINFO_UPDATED],
CG_PULSE | CG_CENTER| CG_SMALLFONT, colorTable[CT_LTRED1] );
// if (cent->gent->client->sess.missionObjectivesShown<3)
// {
CG_DrawProportionalString((SCREEN_WIDTH/2), (SCREEN_HEIGHT/2) + 20, ingame_text[IGT_MISSIONINFO_UPDATED2],
CG_PULSE | CG_CENTER| CG_SMALLFONT, colorTable[CT_LTRED1] );
// }
}
else
{
CG_DrawProportionalString((SCREEN_WIDTH/2), (SCREEN_HEIGHT/2), ingame_text[IGT_SECURITYINFO_UPDATED],
CG_PULSE | CG_CENTER| CG_SMALLFONT, colorTable[CT_LTRED1] );
CG_DrawProportionalString((SCREEN_WIDTH/2), (SCREEN_HEIGHT/2) + 20, ingame_text[IGT_SECURITYINFO_UPDATED2],
CG_PULSE | CG_CENTER| CG_SMALLFONT, colorTable[CT_LTRED1] );
}
}
}
}
/*
=====================
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;
}
//FIXME: these globals done once at start of frame for various funcs
AngleVectors (cg.refdefViewAngles, vfwd, vright, vup);
VectorCopy( vfwd, vfwd_n );
VectorCopy( vright, vright_n );
VectorCopy( vup, vup_n );
VectorNormalize( vfwd_n );
VectorNormalize( vright_n );
VectorNormalize( vup_n );
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
cgi_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();
}