mirror of
https://github.com/UberGames/rpgxEF.git
synced 2024-11-13 08:27:17 +00:00
3164fdfa33
-replaced target_safezone with target_sone (backwardscompability is in place) --It now servers for more purposes than just escapepods. Also included in the MSD-System -Removed list-iteration for safezones. --They gave me such a hard time and put unneeded limits in place (like one time shots) --now we loop trough all entities instead and find those with the right criteria and go from there -For safezones clients are now tagged in the backend (new gclients_t-field) --only those that aren't taggesd this way will be killed --Closed FS#30 -First version of shiphealth HUD (FS#8, needs testing) --There were som modifications made to the full UI as well -Both Selfdestruct and Shiphealth communication to the client go via command now, the events got removed -new Version Notation: RPG-X V 2.3 CB 4 Signed-off-by: Harry Young <hendrik.gerritzen@googlemail.com>
3552 lines
97 KiB
C
3552 lines
97 KiB
C
/*
|
|
* Copyright (C) 1999-2000 Id Software, Inc.
|
|
*
|
|
* cg_draw.c -- draw all of the graphical elements during
|
|
* active (after loading) gameplay
|
|
*/
|
|
|
|
#include "cg_local.h"
|
|
#include "cg_text.h"
|
|
#include "cg_screenfx.h"
|
|
|
|
/* set in CG_ParseTeamInfo */
|
|
int sortedTeamPlayers[TEAM_MAXOVERLAY];
|
|
int numSortedTeamPlayers;
|
|
int drawTeamOverlayModificationCount = -1;
|
|
|
|
/*
|
|
* TiM: dCross
|
|
* qboolean CG_WorldCoordToScreenCoord(vec3_t worldCoord, float *x, float *y, qboolean clamp);
|
|
* end dCross
|
|
*/
|
|
|
|
/* TiM: Tricorder Parameters */
|
|
vec3_t vfwd;
|
|
vec3_t vright;
|
|
vec3_t vup;
|
|
vec3_t vfwd_n;
|
|
vec3_t vright_n;
|
|
vec3_t vup_n;
|
|
int infoStringCount;
|
|
|
|
static qboolean drawCrosshairName=qfalse;
|
|
|
|
extern void InitPostGameMenuStruct(void);
|
|
|
|
static void CG_InterfaceStartup(void);
|
|
|
|
char *ingame_text[IGT_MAX]; /* Holds pointers to ingame text */
|
|
|
|
int zoomFlashTime=0;
|
|
|
|
interfacegraphics_s interface_graphics[IG_MAX] =
|
|
{
|
|
/* type timer x y width height file/text graphic, min max color style ptr */
|
|
{ SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0 }, /* IG_GROW */
|
|
|
|
{ SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0 }, /* IG_HEALTH_START */
|
|
{ SG_GRAPHIC, 0.0, 5, 429, 32, 64, "gfx/interface/rpgx_healthbar_leftcorner", 0, 0, 0, CT_DKBROWN1, 0 }, /* IG_HEALTH_BEGINCAP */
|
|
{ SG_GRAPHIC, 0.0, 64, 429, 6, 25, "gfx/interface/ammobar", 0, 0, 0, CT_DKBROWN1, 0 }, /* IG_HEALTH_BOX1 */
|
|
{ SG_GRAPHIC, 0.0, 72, 429, 0, 25, "gfx/interface/ammobar", 0, 0, 0, CT_LTBROWN1, 0 }, /* IG_HEALTH_SLIDERFULL */
|
|
{ SG_GRAPHIC, 0.0, 0, 429, 0, 25, "gfx/interface/ammobar", 0, 0, 0, CT_DKBROWN1, 0 }, /* IG_HEALTH_SLIDEREMPTY */
|
|
{ SG_GRAPHIC, 0.0, 72, 429, 16, 32, "gfx/interface/rpgx_healthbar_endcap", 0, 0, 147, CT_DKBROWN1, 0 }, /* IG_HEALTH_ENDCAP */
|
|
{ SG_NUMBER, 0.0, 23, 425, 16, 32, NULL, 0, 0, 0, CT_LTBROWN1, NUM_FONT_BIG }, /* IG_HEALTH_COUNT */
|
|
{ SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0 }, /* IG_HEALTH_END */
|
|
|
|
{ SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0 }, /* IG_ARMOR_START */
|
|
{ SG_GRAPHIC, 0.0, 20, 458, 32, 16, "gfx/interface/armorcap1", 0, 0, 0, CT_DKPURPLE1, 0 }, /* IG_ARMOR_BEGINCAP */
|
|
{ SG_GRAPHIC, 0.0, 64, 458, 6, 12, "gfx/interface/ammobar", 0, 0, 0, CT_DKPURPLE1, 0 }, /* IG_ARMOR_BOX1 */
|
|
{ SG_GRAPHIC, 0.0, 72, 458, 0, 12, "gfx/interface/ammobar", 0, 0, 0, CT_LTPURPLE1, 0 }, /* IG_ARMOR_SLIDERFULL */
|
|
{ SG_GRAPHIC, 0.0, 0, 458, 0, 12, "gfx/interface/ammobar", 0, 0, 0, CT_DKPURPLE1, 0 }, /* IG_ARMOR_SLIDEREMPTY */
|
|
{ SG_GRAPHIC, 0.0, 72, 458, 16, 16, "gfx/interface/armorcap2", 0, 0, 147, CT_DKPURPLE1, 0 }, /* IG_ARMOR_ENDCAP */
|
|
{ SG_NUMBER, 0.0, 44, 458, 16, 16, NULL, 0, 0, 0, CT_LTPURPLE1, NUM_FONT_SMALL }, /* IG_ARMOR_COUNT */
|
|
{ SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0 }, /* IG_ARMOR_END */
|
|
|
|
{ SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0 }, /* IG_AMMO_START */
|
|
{ SG_GRAPHIC, 0.0, 613, 429, 32, 64, "gfx/interface/ammouppercap1", 0, 0, 0, CT_LTPURPLE2, 0 }, /* IG_AMMO_UPPER_BEGINCAP */
|
|
{ SG_GRAPHIC, 0.0, 607, 429, 16, 32, "gfx/interface/ammouppercap2", 0, 0, 572, CT_LTPURPLE2, 0 }, /* IG_AMMO_UPPER_ENDCAP */
|
|
{ SG_GRAPHIC, 0.0, 613, 458, 16, 16, "gfx/interface/ammolowercap1", 0, 0, 0, CT_LTPURPLE2, 0 }, /* IG_AMMO_LOWER_BEGINCAP */
|
|
{ SG_GRAPHIC, 0.0, 578, 458, 0, 12, "gfx/interface/ammobar", 0, 0, 0, CT_LTPURPLE1, 0 }, /* IG_AMMO_SLIDERFULL */
|
|
{ SG_GRAPHIC, 0.0, 0, 458, 0, 12, "gfx/interface/ammobar", 0, 0, 0, CT_DKPURPLE1, 0 }, /* IG_AMMO_SLIDEREMPTY */
|
|
{ SG_GRAPHIC, 0.0, 607, 458, 16, 16, "gfx/interface/ammolowercap2", 0, 0, 572, CT_LTPURPLE2, 0 }, /* IG_AMMO_LOWER_ENDCAP */
|
|
{ SG_NUMBER, 0.0, 573, 425, 16, 32, NULL, 0, 0, 0, CT_LTPURPLE1, NUM_FONT_BIG }, /* IG_AMMO_COUNT */
|
|
{ SG_VAR, 0.0, 0, 0, 0, 0, NULL, 0, 0, 0, CT_NONE, 0 }, /* IG_AMMO_END */
|
|
|
|
};
|
|
|
|
#define LOWEROVERLAY_Y (SCREEN_HEIGHT - ICON_SIZE - 15)
|
|
|
|
/*------------------------------------------------------*/
|
|
|
|
lensFlare_t lensFlare[MAX_LENS_FLARES];
|
|
|
|
lensReflec_s lensReflec[10] =
|
|
{
|
|
/* width, height, offset, positive, color, shadername, shaders placeholder */
|
|
{ 23, 23, 0.192, qtrue, { 0.73, 0.50, 0.23 }, "gfx/effects/flares/flare_straight", 0 }, /* Brown1 5.2 */
|
|
{ 9, 9, 0.37, qtrue, { 0.37, 0.58, 0.55 }, "gfx/effects/flares/flare_straight", 0 }, /* Aqua1 2.7 */
|
|
{ 14, 14, 0.25, qfalse, { 0.37, 0.79, 0.76 }, "gfx/effects/flares/flare_radial", 0 }, /* Turquoise1 4.0 */
|
|
{ 86, 86, 0.556, qfalse, { 0.73, 0.50, 0.23 }, "gfx/effects/flares/flare_inverseradial", 0 }, /* BigBrownInverseRad 1.8 */
|
|
{ 49, 49, 0.476, qfalse, { 0.73, 0.50, 0.23 }, "gfx/effects/flares/flare_straight", 0 }, /* StraightBrown2 2.1 */
|
|
{ 35, 35, 0.667, qfalse, { 0.34, 0.40, 0.44 }, "gfx/effects/flares/flare_straight", 0 }, /* Grey1 1.5 */
|
|
{ 32, 32, 0.769, qfalse, { 0.20, 0.38, 0.62 }, "gfx/effects/flares/flare_radial", 0 }, /* BlueRad 1.3 */
|
|
{ 122, 122, 1.1, qfalse, { 0.31, 0.65, 0.36 }, "gfx/effects/flares/flare_inverseradial", 0 }, /* BigInverseGreen 0.9 */
|
|
{ 254, 254, 1.429, qfalse, { 1.00, 1.00, 1.00 }, "gfx/effects/flares/flare_chromadisc", 0 }, /* ChromaHoop 0.7 */
|
|
{ 52, 52, 1.429, qtrue, { 0.40, 0.56, 0.42 }, "gfx/effects/flares/flare_inverseradial", 0 }, /* Green offset 0.7 */
|
|
};
|
|
|
|
#define HALF_SCREEN_WIDTH (SCREEN_WIDTH*0.5)
|
|
#define HALF_SCREEN_HEIGHT (SCREEN_HEIGHT*0.5)
|
|
|
|
void CG_InitLensFlare( vec3_t worldCoord,
|
|
int w1, int h1,
|
|
vec3_t glowColor, float glowOffset, float hazeOffset, int minDist, int maxDist,
|
|
vec3_t streakColor, int streakDistMin, int streakDistMax, int streakW, int streakH, qboolean whiteStreaks,
|
|
int reflecDistMin, int reflecDistMax, qboolean reflecAnamorphic, qboolean defReflecs,
|
|
qboolean clamp, float maxAlpha, int startTime, int upTime, int holdTime, int downTime )
|
|
{
|
|
int i;
|
|
|
|
/* First thing's first.... I understand if you hate flares :'( */
|
|
if (!cg_dynamiclensflares.value)
|
|
return;
|
|
|
|
for (i = 0; i < MAX_LENS_FLARES; i++) { /* find the next free slot */
|
|
if ( !lensFlare[i].qfull ) {
|
|
lensFlare[i].worldCoord[0] = worldCoord[0];
|
|
lensFlare[i].worldCoord[1] = worldCoord[1];
|
|
lensFlare[i].worldCoord[2] = worldCoord[2];
|
|
lensFlare[i].w1 = w1;
|
|
lensFlare[i].h1 = h1;
|
|
lensFlare[i].glowColor[0] = glowColor[0];
|
|
lensFlare[i].glowColor[1] = glowColor[1];
|
|
lensFlare[i].glowColor[2] = glowColor[2];
|
|
lensFlare[i].glowOffset = glowOffset;
|
|
lensFlare[i].hazeOffset = hazeOffset;
|
|
lensFlare[i].minDist = minDist;
|
|
lensFlare[i].maxDist = maxDist;
|
|
lensFlare[i].streakColor[0] = streakColor[0];
|
|
lensFlare[i].streakColor[1] = streakColor[1];
|
|
lensFlare[i].streakColor[2] = streakColor[2];
|
|
lensFlare[i].streakDistMin = streakDistMin;
|
|
lensFlare[i].streakDistMax = streakDistMax;
|
|
lensFlare[i].streakW = streakW;
|
|
lensFlare[i].streakH = streakH;
|
|
lensFlare[i].whiteStreaks = whiteStreaks;
|
|
lensFlare[i].reflecDistMin = reflecDistMin;
|
|
lensFlare[i].reflecDistMax = reflecDistMax;
|
|
lensFlare[i].reflecAnamorphic = reflecAnamorphic;
|
|
lensFlare[i].defReflecs = defReflecs;
|
|
lensFlare[i].clamp = clamp;
|
|
lensFlare[i].maxAlpha = maxAlpha;
|
|
lensFlare[i].startTime = startTime;
|
|
lensFlare[i].upTime = upTime;
|
|
lensFlare[i].holdTime = holdTime;
|
|
lensFlare[i].downTime = downTime;
|
|
lensFlare[i].qfull = qtrue;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CG_WorldCoordToScreenCoord
|
|
**Blatently plagiarised from EF SP**
|
|
|
|
OMFG this is some damn whacky maths!
|
|
It basically takes a vector variable and somehow
|
|
correlates that to an XY value on your screen!! O_o
|
|
=================
|
|
*/
|
|
|
|
static qboolean CG_WorldCoordToScreenCoord(vec3_t worldCoord, float *x, float *y, qboolean clamp)
|
|
{
|
|
int xcenter, ycenter;
|
|
vec3_t local, transformed;
|
|
vec3_t fwd;
|
|
vec3_t right;
|
|
vec3_t up;
|
|
float xzi;
|
|
float yzi;
|
|
|
|
/*
|
|
* NOTE: did it this way because most draw functions expect virtual 640x480 coords
|
|
* and adjust them for current resolution
|
|
*/
|
|
xcenter = 640 >> 1;
|
|
ycenter = 480 >> 1;
|
|
|
|
AngleVectors (cg.refdefViewAngles, fwd, right, up);
|
|
VectorSubtract (worldCoord, cg.refdef.vieworg, local);
|
|
|
|
transformed[0] = DotProduct(local,right);
|
|
transformed[1] = DotProduct(local,up);
|
|
transformed[2] = DotProduct(local,fwd);
|
|
|
|
/* 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. */
|
|
xzi = xcenter / transformed[2] * (96.0/cg.refdef.fov_x);/*90*/ /*95*/
|
|
yzi = ycenter / transformed[2] * (102.0/cg.refdef.fov_y);/*90*/ /*105*/
|
|
|
|
*x = (float)(xcenter + xzi * transformed[0]);
|
|
*y = (float)(ycenter - yzi * transformed[1]);
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
/*************************************
|
|
CG_FlareScreenTrans - TiM
|
|
|
|
Used to return an alpha value
|
|
based on how far the xy value is
|
|
from two boundaries (Used mainly
|
|
for when the flare exits the screen
|
|
and fades out)
|
|
|
|
The function works by drawing an imaginary
|
|
line from the minimum point to the maximum
|
|
point. If a point is above that line,
|
|
the Y value is used to calculate the alpha,
|
|
else, the X value does.
|
|
There is a slight bit
|
|
of jerkiness if the point crosses this line,
|
|
but much less worse than what was before. :)
|
|
*************************************/
|
|
|
|
static float CG_FlareScreenTrans(int x, int y, int xmin, int ymin, int xmax, int ymax )
|
|
{
|
|
/*
|
|
* Think about it, when the XY points are in separate quadrants of the screen,
|
|
* they're all the same values anyway, but just either negative or positive.
|
|
* Making them all positive, and working on just that set kills about 8 birds with a fricken' huge stone. >:)
|
|
*/
|
|
int lx = abs(x);
|
|
int ly = abs(y);
|
|
int lxmin = abs(xmin);
|
|
int lymin = abs(ymin);
|
|
int lxmax = abs(xmax);
|
|
int lymax = abs(ymax);
|
|
int xDif = lxmax - lxmin;
|
|
int yDif = lymax - lymin;
|
|
float grad = ( (float)lymax/(float)lxmax ); /* calc the grad as if (xmin, ymin) were the origin */
|
|
|
|
float alpha = 1.0;
|
|
|
|
/* if xy is under minimums, just make it 1 :P */
|
|
if (lx < lxmin && ly < lymin ) {
|
|
return alpha;
|
|
}
|
|
|
|
if ( ly < (lx * grad) ) {/* point is running along the side bar */
|
|
alpha = (float)( 1.0 - ( (float)lx - (float)lxmin ) / (float)xDif );
|
|
}
|
|
|
|
if ( ly > ( lx * grad) ) {/* point is running along the top bar */
|
|
alpha = (float)( 1.0 - ( (float)ly - (float)lymin ) / (float)yDif );
|
|
}
|
|
|
|
/* if xy has exceeded maxes, just make it 0 :P */
|
|
if ( lx >= lxmax || ly >= lymax )
|
|
alpha = 0.0;
|
|
|
|
/* Lock it just in case something weird happened. :S */
|
|
if ( alpha > 1.0 )
|
|
alpha = 1.0;
|
|
if ( alpha < 0.0 )
|
|
alpha = 0.0;
|
|
|
|
return alpha;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
CG_CorrelateMaxMinDist
|
|
|
|
Calcuates an alpha value
|
|
between a min and a max point
|
|
so elements can fade in or out
|
|
depending on relative distance :)
|
|
================
|
|
*/
|
|
static float CG_CorrelateMaxMinDist( float len, int min, int max ) {
|
|
|
|
float alpha = 1.0;
|
|
|
|
if ( min == max && max == 0 ) /* This means it will always be off */
|
|
return 0.0;
|
|
|
|
if ( min <= 0 ) /* this means that the parameter wants it to always be on */
|
|
return alpha;
|
|
|
|
alpha = ( len - (float)min ) / ((float)max - (float)min); /* calculate the alpha */
|
|
|
|
if (alpha > 1.0 ) /* Clamp it.... again */
|
|
alpha = 1.0;
|
|
if (alpha < 0.0 )
|
|
alpha = 0.0;
|
|
|
|
return alpha;
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_FadeAlpha
|
|
|
|
Modified version of
|
|
CG_FadeColor. Only
|
|
covers alpha values now,
|
|
and also has an option
|
|
to fade in as well as out
|
|
================
|
|
*/
|
|
float CG_FadeAlpha( int startMsec, int totalMsec, qboolean fade_in ) {
|
|
static float alpha;
|
|
int t;
|
|
|
|
if ( startMsec == 0 ) {
|
|
return (fade_in ? 0.0 : 1.0);
|
|
}
|
|
|
|
t = cg.time - startMsec;
|
|
|
|
if ( t >= totalMsec ) {
|
|
return (fade_in ? 1.0 : 0.0);
|
|
}
|
|
|
|
// fade out
|
|
if ( totalMsec - t < FADE_TIME ) {
|
|
if (!fade_in)
|
|
alpha = ( totalMsec - t ) * 1.0/FADE_TIME;
|
|
else
|
|
alpha = 1.0 - (( totalMsec - t ) * 1.0/FADE_TIME);
|
|
} else {
|
|
alpha = fade_in ? 0.0 : 1.0;
|
|
}
|
|
|
|
return alpha;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_FlareTraceTrans
|
|
|
|
Performs a trace between player
|
|
and origin, and if anything gets in
|
|
the way, an alpha value is generated
|
|
to make the flare fade out
|
|
================
|
|
*/
|
|
|
|
static float prevFrac = 0.0;
|
|
static int fadeTime, fadeInTime;
|
|
|
|
static qboolean CG_FlareTraceTrans ( vec3_t origin, float* alpha )
|
|
{
|
|
trace_t trace;
|
|
|
|
CG_Trace( &trace, origin, NULL, NULL, cg.refdef.vieworg, -1, CONTENTS_SOLID|CONTENTS_BODY ); //Do a trace // switched start and end
|
|
|
|
if ( fadeTime > 0 && fadeInTime == 0 ) {
|
|
*alpha = CG_FadeAlpha( fadeTime, 199, qfalse );
|
|
if (*alpha == 0.0)
|
|
fadeTime = 0.0f;
|
|
}
|
|
|
|
if ( fadeInTime > 0 && fadeTime == 0 ) {
|
|
*alpha = CG_FadeAlpha( fadeInTime, 199, qtrue );
|
|
if (*alpha == 1.0)
|
|
fadeInTime = 0.0f;
|
|
}
|
|
|
|
//fade out the flare
|
|
if (trace.fraction < 1.0 && prevFrac == 1.0 ) {
|
|
fadeTime = cg.time;
|
|
prevFrac = trace.fraction;
|
|
}
|
|
|
|
//fade in the flare
|
|
if (trace.fraction == 1.0 && prevFrac < 1.0 ) {
|
|
fadeInTime = cg.time;
|
|
prevFrac = trace.fraction;
|
|
}
|
|
|
|
if (fadeTime > 0 && fadeInTime > 0) { //Whoa, how did this happen???
|
|
fadeTime = 0; //reset them both and all is good :)
|
|
fadeInTime = 0;
|
|
}
|
|
|
|
if ( (fadeTime == 0.0 && fadeInTime == 0.0 ) && ( *alpha > 0.0 && *alpha < 1.0 ) ) //Now THIS effect was weird O_o
|
|
*alpha = 1.0;
|
|
|
|
if (trace.fraction < 1.0 && prevFrac < 1.0 && fadeTime == 0 && fadeInTime == 0) {
|
|
prevFrac = trace.fraction;
|
|
return qfalse;
|
|
}
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
/*************************************************************
|
|
CG_DrawLensFlare - RPG-X : TiM
|
|
OMFG LENSFLARES R COOL!!!!! ^_^!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
Yes, I know I'm over-doing it now, coding this uber-huge
|
|
processor-intensive, totally un-necessary lensflare engine ;P
|
|
|
|
Parameters Key:
|
|
|
|
vec3_t worldCoord : Position in world to draw the flare
|
|
int w1, h1 : Initial (Maximum) w + h of the flare core
|
|
vec3_t glowColor : Color of the flare's glow
|
|
float glowOffset : Multiplier how much bigger the glow is than the core
|
|
float hazeOffset : Multiplier how much bigger the surrounding haze is to the core
|
|
int minDist : Minimum distance before the flare loses all brightness (Set to 0 if always normal size)
|
|
int maxDist : Maximum distance for flare's brightness
|
|
vec3_t streakColor : Color of the flare's lens reflections (if 0,0,0, then a default blue is used)
|
|
int streakDistMin : Distance at where the flare is totally transparent (Set to 0 if always on)
|
|
int streakDistMax : Distance at where the flare is totally opaque (Set to same as above to turn it always off)
|
|
int streakW : Length of the anamorphic lens streak
|
|
int streakH : Height of the anamorphic lens streak
|
|
qboolean whiteStreaks : Adds white streaks to the center of normal streaks ;P
|
|
int reflecDistMin : Distance at where the reflections are totally transparent (Set to NULL if always on)
|
|
int reflecDistMax : Distance at where the reflections are totally opaque (Set to same value as above if wanted off)
|
|
qboolean reflecAnamorphic : Enables anamorphic lens reflections
|
|
qboolean defReflecs : Makes the Lens Reflections default colors
|
|
qboolean clamp : If qtrue, the lensflare will not resize as the distance changes
|
|
float maxAlpha : All alpha values of the elements in the flare will not exceed this number
|
|
int upTime : How long it takes for the flare to go from 0 intense to maximum intense
|
|
int holdTime : How long the flare stays at max intensity for
|
|
int downTime : How long it takes for the flare to go from max intensity to 0.
|
|
|
|
**************************************************************/
|
|
|
|
void CG_DrawLensFlare( lensFlare_t *flare )
|
|
{
|
|
int w = flare->w1;
|
|
int h = flare->h1;
|
|
float x, y, streakX, streakY;
|
|
int xCart, yCart;
|
|
int i;
|
|
vec4_t color, reflecColor, strkColor;
|
|
int xMax, yMax;
|
|
vec3_t distDif, black = {0.0, 0.0, 0.0};
|
|
int maxTime = flare->upTime + flare->holdTime + flare->downTime;
|
|
int tMaxTime = maxTime + flare->startTime;
|
|
int tUpTime = flare->upTime + flare->startTime;
|
|
int tHoldTime = flare->upTime + flare->holdTime + flare->startTime;
|
|
int tDownTime = flare->upTime + flare->holdTime + flare->downTime + flare->startTime;
|
|
float length;
|
|
|
|
float reflecAlpha = 1.0; //alpha channel of reflections
|
|
float streakAlpha = 1.0; //alpha channel of streaks
|
|
float boundAlpha = 1.0; //alpha if flare leaves screen
|
|
float commonAlpha = 1.0; //alpha variables common too all elements
|
|
float hazeAlpha = 1.0;
|
|
|
|
static float fadeAlpha; //This can't have a default value otherwise it screws up the flare fade transition
|
|
static float timeAlpha; //Alpha/w/h over the specified time
|
|
|
|
//First thing's first.... I understand if you hate flares :'(
|
|
if (!cg_dynamiclensflares.value)
|
|
return;
|
|
|
|
//if we can't get an XY value, screw it :P
|
|
if ( !CG_WorldCoordToScreenCoord( flare->worldCoord, &x, &y, qfalse) )
|
|
return;
|
|
|
|
//if we can't actually see the flare in line of sight, screw it again. :P
|
|
if( !CG_FlareTraceTrans( flare->worldCoord, &fadeAlpha) )
|
|
return;
|
|
|
|
if (maxTime > 0 && cg.time <= tMaxTime) {
|
|
|
|
if ( cg.time <= tUpTime )
|
|
timeAlpha = (float)(cg.time - flare->startTime) * (float)(1.0/(float)flare->upTime);
|
|
|
|
if (cg.time <= tHoldTime && cg.time > tUpTime )
|
|
timeAlpha = 1.0;
|
|
|
|
if (cg.time <= tDownTime && cg.time > tHoldTime )
|
|
timeAlpha = 1.0 - ( (float)(cg.time - flare->startTime) * (float)(1.0/(float)flare->downTime) );
|
|
}
|
|
|
|
if (maxTime == 0 )
|
|
timeAlpha = 1.0;
|
|
|
|
w = w * timeAlpha;
|
|
h = h * timeAlpha;
|
|
|
|
//calc the distance between the player and the flare
|
|
VectorSubtract( flare->worldCoord, cg.refdef.vieworg, distDif );
|
|
length = VectorNormalize( distDif );
|
|
|
|
//if the clamp boolean is false, resize the flare over player distance from it
|
|
if ( !flare->clamp ) {
|
|
w = w * CG_CorrelateMaxMinDist(length, flare->minDist, flare->maxDist ); //Change size/height in relation to distance
|
|
h = h * CG_CorrelateMaxMinDist(length, flare->minDist, flare->maxDist );
|
|
}
|
|
|
|
xCart = (int)(x - HALF_SCREEN_WIDTH ); //Re-orient the EF drawing engine so co-ord (0,0) is in the middle of the screen)
|
|
yCart = (int)(y - HALF_SCREEN_HEIGHT );
|
|
|
|
streakX = (xCart - (flare->streakW*0.5)) + HALF_SCREEN_WIDTH; //Calculate X value of lens streak based on flare position
|
|
streakY = (yCart - (flare->streakH*0.5)) + HALF_SCREEN_HEIGHT; //Calculate Y value of lens streak based on flare position
|
|
|
|
xMax = (w*0.5) + HALF_SCREEN_WIDTH; //define the point the flare should fully fade out
|
|
yMax = (h*0.5) + HALF_SCREEN_HEIGHT;
|
|
|
|
if ( boundAlpha > 0.0 ) { //Calculate the reflections' opacity in contrast to the edge of the screen
|
|
boundAlpha = CG_FlareScreenTrans( xCart, yCart, HALF_SCREEN_WIDTH, HALF_SCREEN_HEIGHT, xMax, yMax);
|
|
}
|
|
|
|
//set up all of the elements with their various alphas :P
|
|
commonAlpha = commonAlpha * fadeAlpha * boundAlpha * flare->maxAlpha;
|
|
|
|
if (commonAlpha * timeAlpha < 0.01 ) //no point in drawing if it's really really faint
|
|
return;
|
|
|
|
reflecAlpha = reflecAlpha * commonAlpha * timeAlpha * CG_CorrelateMaxMinDist(length, flare->reflecDistMin, flare->reflecDistMax );
|
|
streakAlpha = streakAlpha * commonAlpha * timeAlpha * CG_CorrelateMaxMinDist(length, flare->streakDistMin, flare->streakDistMax );
|
|
hazeAlpha = hazeAlpha * commonAlpha;
|
|
|
|
//Copy in the color the user wants, but we need control of the alpha.
|
|
VectorCopy( flare->glowColor, color );
|
|
color[3] = hazeAlpha;
|
|
|
|
if ( VectorCompare( flare->streakColor, black) ) { //If they specified no streakcolor, use this awesome default blue one :)
|
|
strkColor[0] = 0.31;
|
|
strkColor[1] = 0.45;
|
|
strkColor[2] = 1.0;
|
|
strkColor[3] = streakAlpha;
|
|
}
|
|
else { //else, use the color they wanted
|
|
VectorCopy( flare->streakColor, strkColor );
|
|
strkColor[3] = streakAlpha;
|
|
}
|
|
|
|
//Lens Reflections - those cool circly bits that go in the opposite direction of the flare
|
|
if ( reflecAlpha != 0.0 ) {//Sheez, only do this if we really WANT it O_o
|
|
for( i = 0; i < 10; i++ ) {
|
|
|
|
//if they wanted the cool photoshoppy style reflections
|
|
if ( flare->defReflecs ) {
|
|
VectorCopy( lensReflec[i].color, reflecColor );
|
|
reflecColor[3] = reflecAlpha;
|
|
}
|
|
else { //otherwise, just use the color they picked
|
|
VectorCopy( color, reflecColor );
|
|
reflecColor[3] = reflecAlpha;
|
|
}
|
|
|
|
trap_R_SetColor( reflecColor );
|
|
|
|
CG_DrawPic(
|
|
( ( ( lensReflec[i].positive ? xCart : -xCart ) * lensReflec[i].offset ) + HALF_SCREEN_WIDTH ) - ( flare->reflecAnamorphic ? lensReflec[i].width : lensReflec[i].width*0.5 ), //X
|
|
( ( ( lensReflec[i].positive ? yCart : -yCart ) * lensReflec[i].offset ) + HALF_SCREEN_HEIGHT ) - ( lensReflec[i].height*0.5 ), //Y
|
|
flare->reflecAnamorphic ? lensReflec[i].width * 2 : lensReflec[i].width, //W
|
|
lensReflec[i].height, //H
|
|
lensReflec[i].graphic //pic
|
|
);
|
|
}
|
|
}
|
|
|
|
//Colored Middle + Streaks
|
|
trap_R_SetColor( color );
|
|
if ( color[3] > 0.0 ) {
|
|
x = ( xCart - ( (w*flare->hazeOffset) *0.5) + HALF_SCREEN_WIDTH );
|
|
y = ( yCart - ( (h*flare->hazeOffset) *0.5) + HALF_SCREEN_HEIGHT );
|
|
CG_DrawPic( x, y, w*flare->hazeOffset, h*flare->hazeOffset, cgs.media.flareHaze ); //Surrounding ambient haze
|
|
}
|
|
|
|
trap_R_SetColor( strkColor );
|
|
if ( strkColor[3] > 0.0f )
|
|
CG_DrawPic( streakX , streakY , flare->streakW, flare->streakH, cgs.media.flareStreak ); //Colored portion of the anamorphic streaks
|
|
|
|
trap_R_SetColor( color );
|
|
if ( color[3] > 0.0f ) {
|
|
x = ( xCart - ( (w*flare->glowOffset) *0.5) + HALF_SCREEN_WIDTH );
|
|
y = ( yCart - ( (h*flare->glowOffset) *0.5) + HALF_SCREEN_HEIGHT );
|
|
CG_DrawPic( x, y, w*flare->glowOffset, h*flare->glowOffset, cgs.media.flareCore ); //Main colored glow bit of the main flare
|
|
}
|
|
|
|
if ( flare->whiteStreaks ) { //if player wanted white streaks in their streaks
|
|
strkColor[0] = strkColor[1] = strkColor[2] = 1.0;
|
|
|
|
trap_R_SetColor( strkColor ); //White
|
|
if ( strkColor[3] > 0.0 )
|
|
CG_DrawPic( streakX + (flare->streakW*0.2), streakY + (flare->streakH*0.2), flare->streakW*0.6, flare->streakH*0.6, cgs.media.flareStreak ); //White Core of streak is ALWAYS 20% smaller.
|
|
}
|
|
|
|
color[0] = color[1] = color [2] = 1.0f;
|
|
color[3] = hazeAlpha;
|
|
|
|
trap_R_SetColor( color );
|
|
if ( color[3] > 0.0 ) {
|
|
x = ( xCart - (w *0.5) + HALF_SCREEN_WIDTH );
|
|
y = ( yCart - (h *0.5) + HALF_SCREEN_HEIGHT );
|
|
CG_DrawPic( x, y, w, h, cgs.media.flareCore ); //Draw teh main fl4r3 :)
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_Draw3DModel
|
|
|
|
================
|
|
*/
|
|
static void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, qhandle_t shader, vec3_t origin, vec3_t angles ) {
|
|
refdef_t refdef;
|
|
refEntity_t ent;
|
|
|
|
if ( !cg_draw3dIcons.integer || !cg_drawIcons.integer ) {
|
|
return;
|
|
}
|
|
|
|
CG_AdjustFrom640( &x, &y, &w, &h );
|
|
|
|
memset( &refdef, 0, sizeof( refdef ) );
|
|
|
|
memset( &ent, 0, sizeof( ent ) );
|
|
AnglesToAxis( angles, ent.axis );
|
|
VectorCopy( origin, ent.origin );
|
|
ent.hModel = model;
|
|
ent.customSkin = skin;
|
|
ent.customShader = shader;
|
|
ent.renderfx = RF_NOSHADOW; // no stencil shadows
|
|
|
|
refdef.rdflags = RDF_NOWORLDMODEL;
|
|
|
|
AxisClear( refdef.viewaxis );
|
|
|
|
refdef.fov_x = 30;
|
|
refdef.fov_y = 30;
|
|
|
|
refdef.x = x;
|
|
refdef.y = y;
|
|
refdef.width = w;
|
|
refdef.height = h;
|
|
|
|
refdef.time = cg.time;
|
|
|
|
trap_R_ClearScene();
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
trap_R_RenderScene( &refdef );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawHead
|
|
|
|
Used for both the status bar and the scoreboard
|
|
================
|
|
*/
|
|
void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ) {
|
|
clipHandle_t cm;
|
|
clientInfo_t *ci;
|
|
playerState_t *ps;
|
|
float value;
|
|
float len;
|
|
vec3_t origin;
|
|
vec3_t mins, maxs;
|
|
|
|
ci = &cgs.clientinfo[ clientNum ];
|
|
|
|
ps = &cg.snap->ps;
|
|
|
|
value = ps->stats[STAT_HEALTH];
|
|
|
|
if ( cg_draw3dIcons.integer && (ci->headOffset[0] != 404) ) {
|
|
cm = ci->headModel;
|
|
if ( !cm ) {
|
|
return;
|
|
}
|
|
|
|
// offset the origin y and z to center the head
|
|
trap_R_ModelBounds( cm, mins, maxs );
|
|
|
|
origin[2] = -0.5 * ( mins[2] + maxs[2] );
|
|
origin[1] = 0.5 * ( mins[1] + maxs[1] );
|
|
|
|
// calculate distance so the head nearly fills the box
|
|
// assume heads are taller than wide
|
|
len = 0.7 * ( maxs[2] - mins[2] );
|
|
origin[0] = len / 0.268; // len / tan( fov/2 )
|
|
|
|
// allow per-model tweaking
|
|
VectorAdd( origin, ci->headOffset, origin );
|
|
|
|
CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, 0, origin, headAngles );
|
|
|
|
if ((value < 82.000000) && (value >= 65.000000)){
|
|
CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, 0, origin, headAngles );
|
|
}else if(value >= 49.000000){
|
|
CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, 0, origin, headAngles );
|
|
}else if(value >= 32.000000){
|
|
CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, 0, origin, headAngles );
|
|
}else if(value >= 2.000000){
|
|
CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, 0, origin, headAngles );
|
|
}else if(value <= 1.000000){
|
|
CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, 0, origin, headAngles );
|
|
}
|
|
} else if ( cg_drawIcons.integer ) {
|
|
CG_DrawPic( x, y, w, h, ci->modelIcon );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawFlagModel
|
|
|
|
Used for both the status bar and the scoreboard
|
|
================
|
|
*/
|
|
void CG_DrawFlagModel( float x, float y, float w, float h, int team ) {
|
|
qhandle_t cm;
|
|
float len;
|
|
vec3_t origin, angles;
|
|
vec3_t mins, maxs;
|
|
|
|
if ( cg_draw3dIcons.integer ) {
|
|
|
|
VectorClear( angles );
|
|
|
|
cm = cgs.media.redFlagModel;
|
|
|
|
// offset the origin y and z to center the flag
|
|
trap_R_ModelBounds( cm, mins, maxs );
|
|
|
|
origin[2] = -0.5 * ( mins[2] + maxs[2] );
|
|
origin[1] = 0.5 * ( mins[1] + maxs[1] );
|
|
|
|
// calculate distance so the flag nearly fills the box
|
|
// assume heads are taller than wide
|
|
len = 0.5 * ( maxs[2] - mins[2] );
|
|
origin[0] = len / 0.268; // len / tan( fov/2 )
|
|
|
|
angles[YAW] = 60 * sin( cg.time / 2000.0 );;
|
|
|
|
CG_Draw3DModel( x, y, w, h,
|
|
team == TEAM_RED ? cgs.media.redFlagModel : cgs.media.blueFlagModel, 0,
|
|
team == TEAM_RED ? cgs.media.redFlagShader[3] : cgs.media.blueFlagShader[3], origin, angles );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawStatusBarHead
|
|
RPG-X | Phenix | 09/06/2005
|
|
I dont know who commented this out but it's going back in ;)
|
|
================
|
|
*/
|
|
static int CG_DrawStatusBarHead( float x ) {
|
|
vec3_t angles;
|
|
float size, stretch;
|
|
float frac;
|
|
|
|
VectorClear( angles );
|
|
|
|
if ( cg.damageTime && cg.time - cg.damageTime < DAMAGE_TIME ) {
|
|
frac = (float)(cg.time - cg.damageTime ) / DAMAGE_TIME;
|
|
size = ICON_SIZE * 1.25 * ( 1.5 - frac * 0.5 );
|
|
|
|
stretch = size - ICON_SIZE * 1.25;
|
|
// kick in the direction of damage
|
|
x -= stretch * 0.5 + cg.damageX * stretch * 0.5;
|
|
|
|
cg.headStartYaw = 180 + cg.damageX * 45;
|
|
|
|
cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI );
|
|
cg.headEndPitch = 5 * cos( crandom()*M_PI );
|
|
|
|
cg.headStartTime = cg.time;
|
|
cg.headEndTime = cg.time + 100 + random() * 2000;
|
|
} else {
|
|
if ( cg.time >= cg.headEndTime ) {
|
|
// select a new head angle
|
|
cg.headStartYaw = cg.headEndYaw;
|
|
cg.headStartPitch = cg.headEndPitch;
|
|
cg.headStartTime = cg.headEndTime;
|
|
cg.headEndTime = cg.time + 100 + random() * 2000;
|
|
|
|
cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI );
|
|
cg.headEndPitch = 5 * cos( crandom()*M_PI );
|
|
}
|
|
|
|
size = ICON_SIZE * 1.25;
|
|
}
|
|
|
|
size = size * 3;
|
|
|
|
// if the server was frozen for a while we may have a bad head start time
|
|
if ( cg.headStartTime > cg.time ) {
|
|
cg.headStartTime = cg.time;
|
|
}
|
|
|
|
frac = ( cg.time - cg.headStartTime ) / (float)( cg.headEndTime - cg.headStartTime );
|
|
frac = frac * frac * ( 3 - 2 * frac );
|
|
angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac;
|
|
angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac;
|
|
|
|
CG_DrawHead( x, 480 - (size + BIGCHAR_HEIGHT + 5), size, size,
|
|
cg.snap->ps.clientNum, angles );
|
|
|
|
return size;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawTeamBackground
|
|
|
|
================
|
|
*/
|
|
void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team, qboolean scoreboard )
|
|
{
|
|
vec4_t hcolor;
|
|
|
|
hcolor[3] = alpha;
|
|
if ( team == TEAM_RED )
|
|
{
|
|
hcolor[0] = 1;
|
|
hcolor[1] = 0;
|
|
hcolor[2] = 0;
|
|
}
|
|
else if ( team == TEAM_BLUE )
|
|
{
|
|
hcolor[0] = 0;
|
|
hcolor[1] = 0;
|
|
hcolor[2] = 1;
|
|
}
|
|
else
|
|
{
|
|
return; // no team
|
|
}
|
|
|
|
trap_R_SetColor( hcolor );
|
|
CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
|
|
trap_R_SetColor( NULL );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawAmmo
|
|
|
|
================
|
|
*/
|
|
static void CG_DrawAmmo(centity_t *cent)
|
|
{
|
|
// unused function ... lol might be removed
|
|
return;
|
|
}
|
|
|
|
static int CG_DrawHealth(centity_t *cent)
|
|
{
|
|
float value;
|
|
playerState_t *ps;
|
|
char *health_str = NULL;
|
|
int health_barwidth;
|
|
vec_t *health_txtcolor = NULL;
|
|
int health_txteffect = 0;
|
|
int x, y;
|
|
|
|
ps = &cg.snap->ps;
|
|
|
|
value = ps->stats[STAT_HEALTH];
|
|
|
|
//RPG-X: RedTechie - The GROSS math part icky icky!
|
|
if(value >= 82.000000){
|
|
health_str = ingame_text[IGT_SB_HEALTHSTATUS1]; //RPG-X and SFEF
|
|
health_txtcolor = colorTable[CT_DKPURPLE2];
|
|
health_txteffect = UI_BIGFONT;
|
|
}else if(value >= 65.000000){
|
|
health_str = ingame_text[IGT_SB_HEALTHSTATUS2]; //Scott Carter, after being locked in a room with rpg-x team for 20 minuts..
|
|
health_txtcolor = colorTable[CT_DKPURPLE2];
|
|
health_txteffect = UI_BIGFONT;
|
|
}else if(value >= 49.000000){
|
|
health_str = ingame_text[IGT_SB_HEALTHSTATUS3]; //Results after 10 minutes
|
|
health_txtcolor = colorTable[CT_LTBLUE2];
|
|
health_txteffect = UI_BIGFONT;
|
|
}else if(value >= 32.000000){
|
|
health_str = ingame_text[IGT_SB_HEALTHSTATUS4]; //Results after 15 minutes
|
|
health_txtcolor = colorTable[CT_LTBLUE2];
|
|
health_txteffect = UI_BIGFONT;
|
|
}else if(value >= 2.000000){
|
|
health_str = ingame_text[IGT_SB_HEALTHSTATUS5]; //Results after 20 minutes
|
|
health_txtcolor = colorTable[CT_VDKBLUE2];
|
|
health_txteffect = UI_BIGFONT;
|
|
}else if(value <= 1.000000){
|
|
health_str = ingame_text[IGT_SB_HEALTHSTATUS6]; //Final result - post your comments here coders ;) -
|
|
//What do you mean final result - this is like after 30 seconds in a room with the rpg-x team :P (Phenix)
|
|
health_txtcolor = colorTable[CT_RED]; //More like 10 -TiM
|
|
health_txteffect = UI_BIGFONT;
|
|
}
|
|
//Get a accurate width
|
|
health_barwidth = UI_ProportionalStringWidth(health_str,UI_BIGFONT);
|
|
|
|
//Doom Style Health!
|
|
if (doomHead.integer == 1)
|
|
{
|
|
health_barwidth = CG_DrawStatusBarHead( 2 );
|
|
health_barwidth = ((health_barwidth / 2) + 2) - (UI_ProportionalStringWidth(health_str,UI_BIGFONT) / 2);
|
|
UI_DrawProportionalString(health_barwidth, 460 - BIGCHAR_HEIGHT, health_str, health_txteffect, health_txtcolor);
|
|
|
|
return health_barwidth;
|
|
} else {
|
|
x = 3;
|
|
y = 435;
|
|
|
|
//Draw the text
|
|
UI_DrawProportionalString(x + 46, y + 11, health_str, health_txteffect, health_txtcolor);
|
|
|
|
//RPG-X: - RedTechie The Graphics :)
|
|
|
|
trap_R_SetColor( colorTable[CT_DKBLUE1] );
|
|
CG_DrawPic( x, y, 85, 22, cgs.media.healthbigcurve ); //RPG-X: Big Curve //x,y,w,h=32
|
|
CG_DrawPic( x + 49 + health_barwidth, y, 8, 7, cgs.media.healthendcap ); //RPG-X: Top End Cap - 133
|
|
CG_DrawPic( x + 49 + health_barwidth, y + 37, 8, 7, cgs.media.healthendcap ); //RPG-X: Bottum End Cap - 133 //CG_DrawPic( x + 49 + health_barwidth, y + 49, 16, 16, cgs.media.healthendcap );
|
|
CG_FillRect( x, y + 15, 40, 20, colorTable[CT_DKBLUE1]); //Extra bit to fill in the gap under the curve graphic
|
|
|
|
CG_FillRect( x, y + 37, 40, 7, colorTable[CT_DKGOLD1]); //RPG-X: Middle bar //15
|
|
CG_FillRect( x + 42, y + 37, 5+health_barwidth, 7, colorTable[CT_DKBLUE1]); //RPG-X: Bottum Horizontal bar - CG_FillRect( 45, 469, 86+health_barwidth, 15, colorTable[CT_DKBLUE1]);
|
|
CG_FillRect( x + 47, y, health_barwidth, 7, colorTable[CT_DKBLUE1]); //RPG-X: Top Horizontal bar - CG_FillRect( 61, 420, 70+health_barwidth, 15, colorTable[CT_DKBLUE1]);
|
|
|
|
//RPG-X: RedTechie - Some eye candy text
|
|
UI_DrawProportionalString( x +40-3, y + 23, ingame_text[IGT_SB_HEALTHBARLCARS], UI_TINYFONT|UI_RIGHT, colorTable[CT_BLACK]);
|
|
return health_barwidth + 82;
|
|
}
|
|
}
|
|
|
|
static float CG_DrawShipHealth( void )
|
|
{
|
|
CG_FillRect( 3, 400, 102, 31, colorTable[CT_BLACK]);
|
|
//shield
|
|
CG_FillRect( 4, 401, 100, 14, colorTable[CT_VDKBLUE1]);
|
|
if(cg.shieldState == 1){
|
|
CG_FillRect( 4, 401, floor(cg.relativeHullStrength), 14, colorTable[CT_DKBLUE1]);
|
|
UI_DrawProportionalString(5, 403, va("Shields: %i Pct.", cg.relativeHullStrength), UI_TINYFONT, colorTable[CT_MDGREY]);
|
|
} else if(cg.shieldState == 0){
|
|
UI_DrawProportionalString(5, 403, "Shields: standby", UI_TINYFONT, colorTable[CT_MDGREY]);
|
|
} else if(cg.shieldState == -2){
|
|
UI_DrawProportionalString(5, 403, "Shields: offline", UI_TINYFONT, colorTable[CT_MDGREY]);
|
|
} else if(cg.shieldState == -1){
|
|
UI_DrawProportionalString(5, 403, "Shields: inoperable", UI_TINYFONT, colorTable[CT_MDGREY]);
|
|
}
|
|
//hull
|
|
CG_FillRect( 4, 416, 100, 14, colorTable[CT_VDKRED1]);
|
|
CG_FillRect( 4, 416, floor(cg.relativeHullStrength), 14, colorTable[CT_DKRED1]);
|
|
UI_DrawProportionalString(5, 418, va("Structural: %i Pct.", cg.relativeHullStrength), UI_TINYFONT, colorTable[CT_MDGREY]);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawStatusBar
|
|
|
|
================
|
|
*/
|
|
static void CG_DrawStatusBar( void )
|
|
{
|
|
centity_t *cent;
|
|
vec3_t angles;
|
|
int y=0;
|
|
vec4_t whiteA;
|
|
int x, z, i, h, yZ;
|
|
vec3_t tmpVec, eAngle, forward, dAngle;
|
|
int healthBarWidth;
|
|
|
|
whiteA[0] = whiteA[1] = whiteA[2] = 1.0f; whiteA[3] = 0.3f;
|
|
|
|
cent = &cg_entities[cg.snap->ps.clientNum];
|
|
|
|
if ( cg_drawStatus.integer == 0 ) {
|
|
return;
|
|
}
|
|
|
|
// draw the team background
|
|
CG_DrawTeamBackground( 0, 420, 640, 60, 0.33, cg.snap->ps.persistant[PERS_TEAM], qfalse );
|
|
|
|
VectorClear( angles );
|
|
|
|
// draw any 3D icons first, so the changes back to 2D are minimized
|
|
y = (SCREEN_HEIGHT - (4*ICON_SIZE) - 20);
|
|
|
|
// Do start
|
|
if (!cg.interfaceStartupDone)
|
|
{
|
|
CG_InterfaceStartup();
|
|
}
|
|
|
|
//
|
|
// ammo
|
|
//
|
|
if ( cent->currentState.weapon )
|
|
{
|
|
CG_DrawAmmo(cent);
|
|
}
|
|
|
|
|
|
//
|
|
// health
|
|
//
|
|
//RPG-X | Phenix | 09/06/2005
|
|
// Added return of the width for the cloak etc messages
|
|
healthBarWidth = CG_DrawHealth(cent);
|
|
|
|
//shiphealth
|
|
if(cg.relativeHullStrength > 0)
|
|
CG_DrawShipHealth();
|
|
|
|
|
|
// RPG-X
|
|
// Print RPG Flags
|
|
//By: RedTechie & Phenix
|
|
//
|
|
if(cg.predictedPlayerState.powerups[PW_EVOSUIT] || cg.predictedPlayerState.powerups[PW_FLIGHT] || cg.predictedPlayerState.powerups[PW_INVIS]){
|
|
//RPG-X | Phenix | 08/06/2005
|
|
yZ = 478 - SMALLCHAR_HEIGHT;
|
|
// UI_BIGFONT
|
|
//DEBUG
|
|
if(cg.predictedPlayerState.powerups[PW_EVOSUIT]) {
|
|
UI_DrawProportionalString(healthBarWidth, yZ, ingame_text[IGT_SB_EVOSUITSTATUS], UI_SMALLFONT, colorTable[CT_CYAN]);
|
|
yZ -= SMALLCHAR_HEIGHT + 2;
|
|
}
|
|
if(cg.predictedPlayerState.powerups[PW_INVIS]){
|
|
UI_DrawProportionalString(healthBarWidth, yZ, ingame_text[IGT_SB_CLOAKSTATUS], UI_SMALLFONT, colorTable[CT_RED]);
|
|
yZ -= SMALLCHAR_HEIGHT + 2;
|
|
}
|
|
if(cg.predictedPlayerState.powerups[PW_FLIGHT]){
|
|
UI_DrawProportionalString(healthBarWidth, yZ, ingame_text[IGT_SB_FLIGHTSTATUS], UI_SMALLFONT, colorTable[CT_RED]);
|
|
yZ -= SMALLCHAR_HEIGHT + 2;
|
|
}
|
|
}
|
|
|
|
//
|
|
// armor
|
|
//
|
|
//RPG-X: - Redtechie IT A FRICKEN RP NOOOO ARMOR! OMG!
|
|
//CG_DrawArmor(cent);
|
|
|
|
// Radar
|
|
// By Sam "-=Jazz=-"Dickinson
|
|
// http://www.telefragged.com/jazz
|
|
if ( ( cg.snap->ps.weapon == WP_2 || cg.snap->ps.weapon == WP_6 ) && cg_drawradar.integer != 0 && !cg.zoomed )
|
|
{
|
|
vec4_t radColor;
|
|
|
|
CG_DrawPic(40, 100, 100, 100, cgs.media.radarShader);
|
|
for (i = 0; i < cg.snap->numEntities; i++) // Go through all entities in VIS range
|
|
{
|
|
if ( cg.snap->entities[i].eType == ET_PLAYER ) // If the Entity is a Player
|
|
{
|
|
// Calculate How Far Away They Are
|
|
x = (cg.snap->entities[i].pos.trBase[0] - cg.predictedPlayerState.origin[0]);
|
|
y = (cg.snap->entities[i].pos.trBase[1] - cg.predictedPlayerState.origin[1]);
|
|
z = (cg.snap->entities[i].pos.trBase[2] - cg.predictedPlayerState.origin[2]);
|
|
tmpVec[0] = x;
|
|
tmpVec[1] = y;
|
|
tmpVec[2] = 0.0;
|
|
|
|
// Convert Vector to Angle
|
|
vectoangles(tmpVec, eAngle);
|
|
h = sqrt((x*x) + (y*y)); // Get Range
|
|
|
|
// We only Want "YAW" value
|
|
dAngle[0] = 0.0;
|
|
dAngle[1] = AngleSubtract(eAngle[1] - 180, cg.predictedPlayerState.viewangles[1]) + 180;
|
|
dAngle[0] = 0.0;
|
|
|
|
// Convert Angle back to Vector
|
|
AngleVectors(dAngle, forward, NULL, NULL);
|
|
VectorScale(forward, h/32, forward);
|
|
|
|
//RPG-X: RedTechie - If Dead show them as a medical symbol
|
|
//.number
|
|
if (h/32 < 100 && h/32 > 0) { // Limit Radar Range
|
|
if ( cg_entities[cg.snap->entities[i].number].currentState.eFlags & EF_DEAD )
|
|
{
|
|
if (z > 64)
|
|
{
|
|
CG_DrawStretchPic( 86 - forward[1], 146 - forward[0], 16, 8, 0, 0, 1, 0.5, cgs.media.rd_injured_level );
|
|
}
|
|
else if (z < -64)
|
|
{
|
|
CG_DrawStretchPic( 86 - forward[1], 146 - forward[0], 16, 8, 0, 0.5, 1, 1, cgs.media.rd_injured_level );
|
|
}
|
|
else
|
|
{
|
|
CG_DrawPic(86 - forward[1], 146 - forward[0], 16, 16, cgs.media.rd_injured_level);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( cgs.clientinfo[cg.snap->entities[i].number].pClass >= 0 )
|
|
{
|
|
radColor[0] = (float)cgs.classData[cgs.clientinfo[cg.snap->entities[i].number].pClass].radarColor[0] / 255.0f;
|
|
radColor[1] = (float)cgs.classData[cgs.clientinfo[cg.snap->entities[i].number].pClass].radarColor[1] / 255.0f;
|
|
radColor[2] = (float)cgs.classData[cgs.clientinfo[cg.snap->entities[i].number].pClass].radarColor[2] / 255.0f;
|
|
radColor[3] = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( colorTable[CT_BLACK], radColor );
|
|
radColor[3] = colorTable[CT_BLACK][3];
|
|
}
|
|
|
|
if ( cgs.clientinfo[cg.snap->entities[i].number].isAdmin && !cgs.clientinfo[cg.snap->ps.clientNum].isAdmin )
|
|
continue;
|
|
|
|
if ( z > 64 )
|
|
{
|
|
trap_R_SetColor( radColor );
|
|
CG_DrawStretchPic( 86 - forward[1], 146 - forward[0], 8, 4, 0, 0, 1, 0.5, cgs.media.radarMain );
|
|
}
|
|
else if ( z < -64 )
|
|
{
|
|
trap_R_SetColor( radColor );
|
|
CG_DrawStretchPic( 86 - forward[1], 146 - forward[0], 8, 4, 0, 0.5, 1, 1, cgs.media.radarMain );
|
|
}
|
|
else
|
|
{
|
|
trap_R_SetColor( radColor );
|
|
CG_DrawPic( 86 - forward[1], 146 - forward[0], 8, 8, cgs.media.radarMain );
|
|
}
|
|
trap_R_SetColor( NULL );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// End Radar
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_InterfaceStartup
|
|
================
|
|
*/
|
|
static void CG_InterfaceStartup()
|
|
{
|
|
|
|
// Turn on Health Graphics
|
|
if ((interface_graphics[IG_HEALTH_START].timer < cg.time) && (interface_graphics[IG_HEALTH_BEGINCAP].type == SG_OFF))
|
|
{
|
|
trap_S_StartLocalSound( cgs.media.interfaceSnd1, CHAN_LOCAL_SOUND );
|
|
|
|
interface_graphics[IG_HEALTH_BEGINCAP].type = SG_GRAPHIC;
|
|
interface_graphics[IG_HEALTH_BOX1].type = SG_GRAPHIC;
|
|
interface_graphics[IG_HEALTH_ENDCAP].type = SG_GRAPHIC;
|
|
}
|
|
|
|
// Turn on Ammo Graphics
|
|
if (interface_graphics[IG_AMMO_START].timer < cg.time)
|
|
{
|
|
if (interface_graphics[IG_AMMO_UPPER_BEGINCAP].type == SG_OFF)
|
|
{
|
|
trap_S_StartLocalSound( cgs.media.interfaceSnd1, CHAN_LOCAL_SOUND );
|
|
interface_graphics[IG_GROW].type = SG_VAR;
|
|
interface_graphics[IG_GROW].timer = cg.time;
|
|
}
|
|
|
|
interface_graphics[IG_AMMO_UPPER_BEGINCAP].type = SG_GRAPHIC;
|
|
interface_graphics[IG_AMMO_UPPER_ENDCAP].type = SG_GRAPHIC;
|
|
interface_graphics[IG_AMMO_LOWER_BEGINCAP].type = SG_GRAPHIC;
|
|
interface_graphics[IG_AMMO_LOWER_ENDCAP].type = SG_GRAPHIC;
|
|
}
|
|
|
|
if (interface_graphics[IG_GROW].type == SG_VAR)
|
|
{
|
|
interface_graphics[IG_HEALTH_ENDCAP].x += 2;
|
|
interface_graphics[IG_ARMOR_ENDCAP].x += 2;
|
|
interface_graphics[IG_AMMO_UPPER_ENDCAP].x -= 1;
|
|
interface_graphics[IG_AMMO_LOWER_ENDCAP].x -= 1;
|
|
|
|
if (interface_graphics[IG_HEALTH_ENDCAP].x >= interface_graphics[IG_HEALTH_ENDCAP].max)
|
|
{
|
|
interface_graphics[IG_HEALTH_ENDCAP].x = interface_graphics[IG_HEALTH_ENDCAP].max;
|
|
interface_graphics[IG_ARMOR_ENDCAP].x = interface_graphics[IG_ARMOR_ENDCAP].max;
|
|
|
|
interface_graphics[IG_AMMO_UPPER_ENDCAP].x = interface_graphics[IG_AMMO_UPPER_ENDCAP].max;
|
|
interface_graphics[IG_AMMO_LOWER_ENDCAP].x = interface_graphics[IG_AMMO_LOWER_ENDCAP].max;
|
|
interface_graphics[IG_GROW].type = SG_OFF;
|
|
|
|
interface_graphics[IG_HEALTH_SLIDERFULL].type = SG_GRAPHIC;
|
|
interface_graphics[IG_HEALTH_SLIDEREMPTY].type = SG_GRAPHIC;
|
|
interface_graphics[IG_HEALTH_COUNT].type = SG_NUMBER;
|
|
|
|
interface_graphics[IG_ARMOR_SLIDERFULL].type = SG_GRAPHIC;
|
|
interface_graphics[IG_ARMOR_SLIDEREMPTY].type = SG_GRAPHIC;
|
|
interface_graphics[IG_ARMOR_COUNT].type = SG_NUMBER;
|
|
|
|
interface_graphics[IG_AMMO_SLIDERFULL].type = SG_GRAPHIC;
|
|
interface_graphics[IG_AMMO_SLIDEREMPTY].type = SG_GRAPHIC;
|
|
interface_graphics[IG_AMMO_COUNT].type = SG_NUMBER;
|
|
|
|
trap_S_StartLocalSound( cgs.media.interfaceSnd1, CHAN_LOCAL_SOUND );
|
|
cg.interfaceStartupDone = 1; // All done
|
|
}
|
|
|
|
interface_graphics[IG_GROW].timer = cg.time + 10;
|
|
}
|
|
|
|
cg.interfaceStartupTime = cg.time;
|
|
|
|
// kef -- init struct for post game awards
|
|
InitPostGameMenuStruct();
|
|
}
|
|
|
|
/*
|
|
===========================================================================================
|
|
|
|
UPPER RIGHT CORNER
|
|
|
|
===========================================================================================
|
|
*/
|
|
|
|
/*
|
|
==================
|
|
CG_DrawSnapshot
|
|
==================
|
|
*/
|
|
static float CG_DrawSnapshot( float y ) {
|
|
char *s;
|
|
int w;
|
|
|
|
s = va( "time:%i frametime:%i snap:%i cmd:%i", cg.snap->serverTime, cg.frametime,
|
|
cg.latestSnapshotNum, cgs.serverCommandSequence );
|
|
|
|
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
|
|
|
|
if ( cg_lagometer.integer && ( y < (BIGCHAR_HEIGHT * 2) + 20) ) {
|
|
w = w + 52;
|
|
}
|
|
|
|
UI_DrawProportionalString(635 - (w - 2), y + 2, s, UI_BIGFONT, colorTable[CT_LTGOLD1]);
|
|
|
|
return y + BIGCHAR_HEIGHT + 10;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_DrawFPS
|
|
==================
|
|
*/
|
|
#define FPS_FRAMES 4
|
|
static float CG_DrawFPS( float y ) {
|
|
char *s;
|
|
int w;
|
|
static int previousTimes[FPS_FRAMES];
|
|
static int index;
|
|
int i, total;
|
|
int fps;
|
|
static int previous;
|
|
int t, frameTime;
|
|
|
|
// don't use serverTime, because that will be drifting to
|
|
// correct for internet lag changes, timescales, timedemos, etc
|
|
t = trap_Milliseconds();
|
|
frameTime = t - previous;
|
|
previous = t;
|
|
|
|
previousTimes[index % FPS_FRAMES] = frameTime;
|
|
index++;
|
|
if ( index > FPS_FRAMES ) {
|
|
// average multiple frames together to smooth changes out a bit
|
|
total = 0;
|
|
for ( i = 0 ; i < FPS_FRAMES ; i++ ) {
|
|
total += previousTimes[i];
|
|
}
|
|
if ( !total ) {
|
|
total = 1;
|
|
}
|
|
fps = 1000 * FPS_FRAMES / total;
|
|
|
|
s = va( "%ifps", fps );
|
|
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
|
|
//RPG-X | Phenix | 08/06/2005
|
|
// Changed "- w" to "- (w + 50)" to account for lagometer
|
|
if ( !cg_lagometer.integer ) {
|
|
w = w - 52;
|
|
}
|
|
UI_DrawProportionalString(635 - (w + 52), y + 2, s, UI_BIGFONT, colorTable[CT_LTGOLD1]);
|
|
}
|
|
|
|
return y + BIGCHAR_HEIGHT + 10;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_DrawTimer
|
|
=================
|
|
*/
|
|
static float CG_DrawTimer( float y ) {
|
|
char *s;
|
|
int w;
|
|
int mins, seconds, tens;
|
|
int msec;
|
|
|
|
msec = cg.time - cgs.levelStartTime;
|
|
|
|
seconds = msec / 1000;
|
|
mins = seconds / 60;
|
|
seconds -= mins * 60;
|
|
tens = seconds / 10;
|
|
seconds -= tens * 10;
|
|
|
|
s = va( "%i:%i%i", mins, tens, seconds );
|
|
|
|
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
|
|
// RPG-X | Phenix | 08/06/2005
|
|
// Changed "- w" to "- (w + 50)" to account for lagometer
|
|
if ( !cg_lagometer.integer ) {
|
|
w = w - 52;
|
|
}
|
|
UI_DrawProportionalString(635 - (w + 52), y + 2, s, UI_BIGFONT, colorTable[CT_LTGOLD1]);
|
|
|
|
return y + BIGCHAR_HEIGHT + 10;
|
|
}
|
|
#define TINYPAD 1.25
|
|
|
|
/*
|
|
=====================
|
|
CG_DrawUpperRight
|
|
|
|
=====================
|
|
*/
|
|
static void CG_DrawUpperRight( void ) {
|
|
float y;
|
|
|
|
cgs.widescreen.state = WIDESCREEN_RIGHT;
|
|
|
|
y = 0;
|
|
if ( cg_drawFPS.integer ) {
|
|
y = CG_DrawFPS( y );
|
|
}
|
|
if ( cg_drawTimer.integer ) {
|
|
y = CG_DrawTimer( y );
|
|
}
|
|
|
|
if ( cg_drawSnapshot.integer ) {
|
|
y = CG_DrawSnapshot( y );
|
|
}
|
|
|
|
cgs.widescreen.state = WIDESCREEN_NONE;
|
|
}
|
|
|
|
/*
|
|
===========================================================================================
|
|
|
|
LOWER RIGHT CORNER
|
|
|
|
===========================================================================================
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
=================
|
|
CG_DrawScores
|
|
|
|
Draw the small two score display
|
|
=================
|
|
*/
|
|
static float CG_DrawScores( float y )
|
|
{
|
|
float y1;
|
|
|
|
y -= BIGCHAR_HEIGHT + 8;
|
|
y1 = y;
|
|
|
|
return y1 - 8;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawPowerups
|
|
================
|
|
*/
|
|
static float CG_DrawPowerups( float y ) {
|
|
int sorted[MAX_POWERUPS];
|
|
int sortedTime[MAX_POWERUPS];
|
|
int i, j, k;
|
|
int active;
|
|
playerState_t *ps;
|
|
int t;
|
|
gitem_t *item;
|
|
int x;
|
|
int color;
|
|
float size;
|
|
float f;
|
|
static float colors[2][4] = {
|
|
{ 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 } };
|
|
int hasHoldable;
|
|
|
|
hasHoldable = cg.snap->ps.stats[STAT_HOLDABLE_ITEM];
|
|
|
|
ps = &cg.snap->ps;
|
|
|
|
if ( ps->stats[STAT_HEALTH] <= 0 ) {
|
|
return y;
|
|
}
|
|
|
|
// sort the list by time remaining
|
|
active = 0;
|
|
for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
|
|
if ( !ps->powerups[ i ] ) {
|
|
continue;
|
|
}
|
|
t = ps->powerups[ i ] - cg.time;
|
|
// ZOID--don't draw if the power up has unlimited time (999 seconds)
|
|
// This is true of the CTF flags
|
|
if ( t < 0 || t > 999000) {
|
|
continue;
|
|
}
|
|
|
|
// insert into the list
|
|
for ( j = 0 ; j < active ; j++ ) {
|
|
if ( sortedTime[j] >= t ) {
|
|
for ( k = active - 1 ; k >= j ; k-- ) {
|
|
sorted[k+1] = sorted[k];
|
|
sortedTime[k+1] = sortedTime[k];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
sorted[j] = i;
|
|
sortedTime[j] = t;
|
|
active++;
|
|
}
|
|
|
|
// draw the icons and timers
|
|
x = 648;
|
|
for ( i = 0 ; i < active ; i++ ) {
|
|
|
|
// Don't draw almost timed out powerups if we have more than 3 and a holdable item
|
|
if (!(hasHoldable && i<active-2))
|
|
{
|
|
item = BG_FindItemForPowerup( (powerup_t)sorted[i] );
|
|
|
|
if (NULL == item)
|
|
{
|
|
continue;
|
|
}
|
|
color = 1;
|
|
|
|
y -= ICON_SIZE;
|
|
|
|
trap_R_SetColor( colors[color] );
|
|
|
|
t = ps->powerups[ sorted[i] ];
|
|
if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
|
|
trap_R_SetColor( NULL );
|
|
} else {
|
|
vec4_t modulate;
|
|
|
|
f = (float)( t - cg.time ) / POWERUP_BLINK_TIME;
|
|
f -= (int)f;
|
|
modulate[0] = modulate[1] = modulate[2] = modulate[3] = f;
|
|
trap_R_SetColor( modulate );
|
|
}
|
|
|
|
if ( cg.powerupActive == sorted[i] &&
|
|
cg.time - cg.powerupTime < PULSE_TIME ) {
|
|
f = 1.0 - ( ( (float)cg.time - cg.powerupTime ) / PULSE_TIME );
|
|
size = ICON_SIZE * ( 1.0 + ( PULSE_SCALE - 1.0 ) * f );
|
|
} else {
|
|
size = ICON_SIZE;
|
|
}
|
|
|
|
x -= size + 10;
|
|
|
|
CG_DrawPic( x, 478 - size,
|
|
size, size, trap_R_RegisterShader( item->icon ) );
|
|
}
|
|
}
|
|
trap_R_SetColor( NULL );
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
CG_DrawLowerRight
|
|
|
|
=====================
|
|
*/
|
|
static void CG_DrawLowerRight( void ) {
|
|
float y;
|
|
|
|
y = LOWEROVERLAY_Y;
|
|
|
|
cgs.widescreen.state = WIDESCREEN_RIGHT;
|
|
|
|
y = CG_DrawScores( y );
|
|
y = CG_DrawPowerups( y );
|
|
|
|
cgs.widescreen.state = WIDESCREEN_NONE;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
CG_DrawPickupItem
|
|
===================
|
|
*/
|
|
static int CG_DrawPickupItem( int y ) {
|
|
int value;
|
|
float *fadeColor;
|
|
|
|
if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
|
|
return y;
|
|
}
|
|
|
|
y -= ICON_SIZE;
|
|
|
|
value = cg.itemPickup;
|
|
if ( value ) {
|
|
fadeColor = CG_FadeColor( cg.itemPickupTime, 3000 );
|
|
if ( fadeColor ) {
|
|
CG_RegisterItemVisuals( value );
|
|
trap_R_SetColor( fadeColor );
|
|
CG_DrawPic( 8, y, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
|
|
UI_DrawProportionalString( ICON_SIZE + 16, y + (ICON_SIZE/2 - BIGCHAR_HEIGHT/2), bg_itemlist[ value ].pickup_name, UI_SMALLFONT, fadeColor);
|
|
|
|
trap_R_SetColor( NULL );
|
|
}
|
|
}
|
|
|
|
return y;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CG_DrawLowerLeft
|
|
|
|
=====================
|
|
*/
|
|
static void CG_DrawLowerLeft( void ) {
|
|
float y;
|
|
|
|
y = LOWEROVERLAY_Y;
|
|
|
|
cgs.widescreen.state = WIDESCREEN_LEFT;
|
|
|
|
y = CG_DrawPickupItem( y );
|
|
|
|
cgs.widescreen.state = WIDESCREEN_NONE;
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================================
|
|
|
|
/*
|
|
===================
|
|
CG_DrawHoldableItem
|
|
===================
|
|
*/
|
|
static void CG_DrawHoldableItem( void ) {
|
|
int value;
|
|
|
|
value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM];
|
|
if ( value )
|
|
{
|
|
CG_RegisterItemVisuals( value );
|
|
if ( cg.snap->ps.stats[STAT_USEABLE_PLACED] && cg.snap->ps.stats[STAT_USEABLE_PLACED] != 2 )
|
|
{//draw detpack... Borg 2-part teleporter will just draw the same until done
|
|
CG_DrawPic( 640-ICON_SIZE, 480-ICON_SIZE, ICON_SIZE, ICON_SIZE, cgs.media.detpackPlacedIcon );
|
|
}
|
|
else
|
|
{
|
|
CG_DrawPic( 640-ICON_SIZE, 480-ICON_SIZE, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
|
|
}
|
|
}
|
|
else
|
|
{//holding nothing...
|
|
if ( cg.snap->ps.stats[STAT_USEABLE_PLACED] > 0 )
|
|
{//it's a timed countdown to getting a holdable, display the number in seconds
|
|
int sec;
|
|
char *s;
|
|
int w;
|
|
|
|
sec = cg.snap->ps.stats[STAT_USEABLE_PLACED];
|
|
|
|
if ( sec < 0 )
|
|
{
|
|
sec = 0;
|
|
}
|
|
|
|
s = va( "%i", sec );
|
|
|
|
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
|
|
UI_DrawProportionalString(640-(ICON_SIZE/2)-(w/2), (SCREEN_HEIGHT-ICON_SIZE)/2+(BIGCHAR_HEIGHT/2), s, UI_BIGFONT, colorTable[CT_WHITE]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
CG_DrawReward
|
|
===================
|
|
*/
|
|
static void CG_DrawReward( void ) {
|
|
float *color;
|
|
int i;
|
|
float x, y;
|
|
|
|
if ( !cg_drawRewards.integer ) {
|
|
return;
|
|
}
|
|
color = CG_FadeColor( cg.rewardTime, REWARD_TIME );
|
|
if ( !color ) {
|
|
return;
|
|
}
|
|
|
|
trap_R_SetColor( color );
|
|
y = 56;
|
|
x = 320 - cg.rewardCount * ICON_SIZE/2;
|
|
for ( i = 0 ; i < cg.rewardCount ; i++ ) {
|
|
CG_DrawPic( x, y, ICON_SIZE-4, ICON_SIZE-4, cg.rewardShader );
|
|
x += ICON_SIZE;
|
|
}
|
|
trap_R_SetColor( NULL );
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
LAGOMETER
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
#define LAG_SAMPLES 128
|
|
|
|
|
|
typedef struct {
|
|
int frameSamples[LAG_SAMPLES];
|
|
int frameCount;
|
|
int snapshotFlags[LAG_SAMPLES];
|
|
int snapshotSamples[LAG_SAMPLES];
|
|
int snapshotCount;
|
|
} lagometer_t;
|
|
|
|
lagometer_t lagometer;
|
|
|
|
/*
|
|
==============
|
|
CG_AddLagometerFrameInfo
|
|
|
|
Adds the current interpolate / extrapolate bar for this frame
|
|
==============
|
|
*/
|
|
void CG_AddLagometerFrameInfo( void ) {
|
|
int offset;
|
|
|
|
offset = cg.time - cg.latestSnapshotTime;
|
|
lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1) ] = offset;
|
|
lagometer.frameCount++;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
CG_AddLagometerSnapshotInfo
|
|
|
|
Each time a snapshot is received, log its ping time and
|
|
the number of snapshots that were dropped before it.
|
|
|
|
Pass NULL for a dropped packet.
|
|
==============
|
|
*/
|
|
void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) {
|
|
// dropped packet
|
|
if ( !snap ) {
|
|
lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = -1;
|
|
lagometer.snapshotCount++;
|
|
return;
|
|
}
|
|
|
|
// add this snapshot's info
|
|
lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->ping;
|
|
lagometer.snapshotFlags[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->snapFlags;
|
|
lagometer.snapshotCount++;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
CG_DrawDisconnect
|
|
|
|
Should we draw something differnet for long lag vs no packets?
|
|
==============
|
|
*/
|
|
static void CG_DrawDisconnect( void ) {
|
|
float x, y;
|
|
int cmdNum;
|
|
usercmd_t cmd;
|
|
const char *s;
|
|
int w;
|
|
|
|
// draw the phone jack if we are completely past our buffers
|
|
cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1;
|
|
trap_GetUserCmd( cmdNum, &cmd );
|
|
if ( cmd.serverTime <= cg.snap->ps.commandTime ||
|
|
cmd.serverTime > cg.time
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// also add text in center of screen
|
|
s = ingame_text[IGT_CONNECTIONINTERRUPTED];
|
|
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
|
|
// Used to be (Height) 100
|
|
UI_DrawProportionalString(320 - w/2, 240, s, UI_BIGFONT, colorTable[CT_LTGOLD1]);
|
|
|
|
// blink the icon
|
|
if ( ( cg.time >> 9 ) & 1 ) {
|
|
return;
|
|
}
|
|
|
|
// RPG-X | Phenix | 08/06/2005
|
|
x = 296; //640 - 50;
|
|
y = 182;
|
|
|
|
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 ) {
|
|
CG_DrawDisconnect();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// draw the graph
|
|
//
|
|
// 640, 480 (-48)
|
|
x = 640 - 50; //move it left of the ammo numbers
|
|
y = 2;
|
|
|
|
trap_R_SetColor( NULL );
|
|
CG_DrawPic( x, y, 48, 48, cgs.media.lagometerShader );
|
|
|
|
ax = x;
|
|
ay = y;
|
|
aw = 48;
|
|
ah = 48;
|
|
CG_AdjustFrom640( &ax, &ay, &aw, &ah );
|
|
|
|
color = -1;
|
|
range = ah / 3;
|
|
mid = ay + range;
|
|
|
|
vscale = range / MAX_LAGOMETER_RANGE;
|
|
|
|
// draw the frame interpoalte / extrapolate graph
|
|
for ( a = 0 ; a < aw ; a++ ) {
|
|
i = ( lagometer.frameCount - 1 - a ) & (LAG_SAMPLES - 1);
|
|
v = lagometer.frameSamples[i];
|
|
v *= vscale;
|
|
if ( v > 0 ) {
|
|
if ( color != 1 ) {
|
|
color = 1;
|
|
trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] );
|
|
}
|
|
if ( v > range ) {
|
|
v = range;
|
|
}
|
|
trap_R_DrawStretchPic ( ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
|
|
} else if ( v < 0 ) {
|
|
if ( color != 2 ) {
|
|
color = 2;
|
|
trap_R_SetColor( g_color_table[ColorIndex(COLOR_BLUE)] );
|
|
}
|
|
v = -v;
|
|
if ( v > range ) {
|
|
v = range;
|
|
}
|
|
trap_R_DrawStretchPic( ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
|
|
}
|
|
}
|
|
|
|
// draw the snapshot latency / drop graph
|
|
range = ah / 2;
|
|
vscale = range / MAX_LAGOMETER_PING;
|
|
|
|
for ( a = 0 ; a < aw ; a++ ) {
|
|
i = ( lagometer.snapshotCount - 1 - a ) & (LAG_SAMPLES - 1);
|
|
v = lagometer.snapshotSamples[i];
|
|
if ( v > 0 ) {
|
|
if ( lagometer.snapshotFlags[i] & SNAPFLAG_RATE_DELAYED ) {
|
|
if ( color != 5 ) {
|
|
color = 5; // YELLOW for rate delay
|
|
trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] );
|
|
}
|
|
} else {
|
|
if ( color != 3 ) {
|
|
color = 3;
|
|
trap_R_SetColor( g_color_table[ColorIndex(COLOR_GREEN)] );
|
|
}
|
|
}
|
|
v = v * vscale;
|
|
if ( v > range ) {
|
|
v = range;
|
|
}
|
|
trap_R_DrawStretchPic( ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
|
|
} else if ( v < 0 ) {
|
|
if ( color != 4 ) {
|
|
color = 4; // RED for dropped snapshots
|
|
trap_R_SetColor( g_color_table[ColorIndex(COLOR_RED)] );
|
|
}
|
|
trap_R_DrawStretchPic( ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader );
|
|
}
|
|
}
|
|
|
|
trap_R_SetColor( NULL );
|
|
|
|
if ( cg_nopredict.integer || cg_synchronousClients.integer ) {
|
|
// CG_DrawBigString( ax, ay, "snc", 1.0 );
|
|
UI_DrawProportionalString(ax, ay, "snc", UI_BIGFONT, colorTable[CT_LTGOLD1]);
|
|
}
|
|
|
|
CG_DrawDisconnect();
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
CENTER PRINTING
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
=================
|
|
CG_DrawSelfdestructTimer
|
|
=================
|
|
*/
|
|
static float CG_DrawSelfdestructTimer( void ) {
|
|
char *s;
|
|
int w;
|
|
int mins, tens, seconds, remainder;
|
|
int msec;
|
|
|
|
msec = cg.selfdestructTime - cg.time;
|
|
|
|
if (msec > 0){
|
|
|
|
mins = msec / 60000;
|
|
tens = (msec - (mins * 60000)) / 10000;
|
|
seconds = (msec - (mins * 60000) - (tens * 10000)) / 1000;
|
|
remainder = msec - (mins * 60000) - (tens * 10000) - (seconds * 1000);
|
|
|
|
s = va( "%i:%i%i.%i", mins, tens, seconds, remainder );
|
|
|
|
w = UI_ProportionalStringWidth("SELF-DESTRUCT IN",UI_SMALLFONT);
|
|
UI_DrawProportionalString(320 - (w / 2), 10, "SELF-DESTRUCT IN", UI_SMALLFONT, colorTable[CT_RED]);
|
|
|
|
w = UI_ProportionalStringWidth(s,UI_SMALLFONT);
|
|
UI_DrawProportionalString(320 - (w / 2), 30, s, UI_SMALLFONT, colorTable[CT_RED]);
|
|
|
|
if((10000 - 0.5 * cg.frametime) < msec && msec < (10000 + 0.5 * cg.frametime))
|
|
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( "sound/voice/computer/voy3/10sec.mp3" ) );
|
|
if((9000 - 0.5 * cg.frametime) < msec && msec < (9000 + 0.5 * cg.frametime))
|
|
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( "sound/voice/computer/voy3/9sec.mp3" ) );
|
|
if((8000 - 0.5 * cg.frametime) < msec && msec < (8000 + 0.5 * cg.frametime))
|
|
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( "sound/voice/computer/voy3/8sec.mp3" ) );
|
|
if((7000 - 0.5 * cg.frametime) < msec && msec < (7000 + 0.5 * cg.frametime))
|
|
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( "sound/voice/computer/voy3/7sec.mp3" ) );
|
|
if((6000 - 0.5 * cg.frametime) < msec && msec < (6000 + 0.5 * cg.frametime))
|
|
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( "sound/voice/computer/voy3/6sec.mp3" ) );
|
|
if((5000 - 0.5 * cg.frametime) < msec && msec < (5000 + 0.5 * cg.frametime))
|
|
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( "sound/voice/computer/voy3/5sec.mp3" ) );
|
|
if((4000 - 0.5 * cg.frametime) < msec && msec < (4000 + 0.5 * cg.frametime))
|
|
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( "sound/voice/computer/voy3/4sec.mp3" ) );
|
|
if((3000 - 0.5 * cg.frametime) < msec && msec < (3000 + 0.5 * cg.frametime))
|
|
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( "sound/voice/computer/voy3/3sec.mp3" ) );
|
|
if((2000 - 0.5 * cg.frametime) < msec && msec < (2000 + 0.5 * cg.frametime))
|
|
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( "sound/voice/computer/voy3/2sec.mp3" ) );
|
|
if((1000 - 0.5 * cg.frametime) < msec && msec < (1000 + 0.5 * cg.frametime))
|
|
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( "sound/voice/computer/voy3/1sec.mp3" ) );
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
CG_CenterPrint
|
|
|
|
Called for important messages that should stay in the center of the screen
|
|
for a few moments
|
|
==============
|
|
*/
|
|
void CG_CenterPrint( const char *str, int y, int charWidth ) {
|
|
char *s;
|
|
|
|
Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
|
|
|
|
cg.centerPrintTime = cg.time;
|
|
cg.centerPrintY = y;
|
|
cg.centerPrintCharWidth = charWidth;
|
|
|
|
// count the number of lines for centering
|
|
cg.centerPrintLines = 1;
|
|
s = cg.centerPrint;
|
|
while( *s ) {
|
|
if (*s == '\n')
|
|
cg.centerPrintLines++;
|
|
s++;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
CG_DrawCenterString
|
|
===================
|
|
*/
|
|
static void CG_DrawCenterString( void ) {
|
|
char *start;
|
|
int l;
|
|
int x, y, w;
|
|
float *color;
|
|
|
|
if ( !cg.centerPrintTime ) {
|
|
return;
|
|
}
|
|
|
|
color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value );
|
|
if ( !color ) {
|
|
return;
|
|
}
|
|
|
|
trap_R_SetColor( color );
|
|
|
|
start = cg.centerPrint;
|
|
|
|
y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2;
|
|
|
|
while ( 1 ) {
|
|
char linebuffer[1024];
|
|
|
|
for ( l = 0; l < 60; l++ ) {
|
|
if ( !start[l] || start[l] == '\n' ) {
|
|
break;
|
|
}
|
|
linebuffer[l] = start[l];
|
|
}
|
|
linebuffer[l] = 0;
|
|
|
|
w = UI_ProportionalStringWidth(linebuffer,UI_BIGFONT);
|
|
|
|
x = ( SCREEN_WIDTH - w ) / 2;
|
|
|
|
UI_DrawProportionalString( x, y, linebuffer, UI_BIGFONT|UI_DROPSHADOW, color);
|
|
|
|
y += cg.centerPrintCharWidth * 1.5;
|
|
|
|
while ( *start && ( *start != '\n' ) ) {
|
|
start++;
|
|
}
|
|
if ( !*start ) {
|
|
break;
|
|
}
|
|
start++;
|
|
}
|
|
|
|
trap_R_SetColor( NULL );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_DrawCrosshair
|
|
=================
|
|
*/
|
|
static void CG_DrawCrosshair(void) {
|
|
float w, h;
|
|
float f;
|
|
float x = 0;
|
|
float y = 0; //float
|
|
int weaponCrosshairNum;
|
|
|
|
trace_t trace;
|
|
vec3_t start, end;
|
|
int ignore;
|
|
vec3_t d_f;
|
|
vec3_t pitchConstraint;
|
|
vec3_t worldPoint;
|
|
|
|
crosshairsData_t *cd;
|
|
|
|
if( cg.zoomed ) { //RPG-X - TiM: We dun need crosshairs when zoomed anymore :P
|
|
return;
|
|
}
|
|
|
|
if ( !cg_drawCrosshair.integer ) {
|
|
return;
|
|
}
|
|
|
|
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
|
|
return;
|
|
}
|
|
|
|
//clamp crosshair num
|
|
if ( (weaponCrosshairNum = cg.predictedPlayerState.weapon - 1) < 0 ){
|
|
weaponCrosshairNum = 0;
|
|
}
|
|
else if ( weaponCrosshairNum >= MAX_CROSSHAIRS ) {
|
|
weaponCrosshairNum = 14;
|
|
}
|
|
|
|
cd = &cgs.crosshairsData[weaponCrosshairNum];
|
|
|
|
ignore = cg.predictedPlayerState.clientNum;
|
|
|
|
//if noDraw was specified in the crosshair script
|
|
if ( cd->noDraw ) {
|
|
return;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
//dCross
|
|
|
|
if( cg_dynamicCrosshair.value == 1 && cg.renderingThirdPerson) {
|
|
|
|
VectorCopy( cg.predictedPlayerState.viewangles, pitchConstraint); //cg.predictedPlayerState.viewangles //cg.refdefViewAngles //vieworg
|
|
|
|
AngleVectors( pitchConstraint, d_f, NULL, NULL );
|
|
|
|
VectorCopy( cg.predictedPlayerState.origin, start);
|
|
if ( !(cg.predictedPlayerState.eFlags & EF_FULL_ROTATE) && Q_fabs( cg.predictedPlayerState.viewangles[PITCH] ) > 89.9f )
|
|
start[2] -= 20;
|
|
else
|
|
start[2] += (float)cg.predictedPlayerState.viewheight * cgs.clientinfo[cg.predictedPlayerState.clientNum].height;
|
|
|
|
VectorMA( start, 6000.0f, d_f, end ); //cg.distanceCull
|
|
|
|
CG_Trace( &trace, start, vec3_origin, vec3_origin, end, ignore, CONTENTS_SOLID|CONTENTS_BODY );
|
|
|
|
//TiM - if we hit a cloaked admin, bypass them so the crosshair doesn't jump randomly
|
|
//NOTE: Possibly could cause errors
|
|
while ( cg_entities[trace.entityNum].currentState.powerups & ( 1 <<PW_INVIS ) ) {
|
|
VectorMA( trace.endpos, 6000.0f, d_f, end ); //cg.distanceCull
|
|
CG_Trace( &trace, trace.endpos, vec3_origin, vec3_origin, end, trace.entityNum, CONTENTS_SOLID|CONTENTS_BODY );
|
|
}
|
|
|
|
VectorCopy( trace.endpos, worldPoint );
|
|
|
|
if ( VectorLength( worldPoint ) ) {
|
|
if ( !CG_WorldCoordToScreenCoord( worldPoint, &x, &y, qfalse) )
|
|
{//off screen, don't draw it
|
|
return;
|
|
}
|
|
|
|
x -= 320;
|
|
y -= 240;
|
|
}
|
|
} else {
|
|
x = cg_crosshairX.integer;
|
|
y = cg_crosshairY.integer;
|
|
}
|
|
|
|
//end dCross
|
|
|
|
//If admins scan non-players
|
|
if ( cg.predictedPlayerState.weapon == WP_2 && cg.predictedPlayerState.eFlags & EF_FIRING ) {
|
|
if (cg_showEntityNums.integer && cgs.clientinfo[cg.snap->ps.clientNum].isAdmin && cg.crosshairClientNum < ENTITYNUM_WORLD ) {
|
|
vec4_t ccolor;
|
|
ccolor[0] = 0.694f; //0.9F;//R
|
|
ccolor[1] = 0.816f; //0.7F;//G
|
|
ccolor[2] = 1.0f; //0.0F;//B
|
|
ccolor[3] = 0.8f;
|
|
|
|
//TiM
|
|
cgs.widescreen.state = WIDESCREEN_CENTER;
|
|
|
|
UI_DrawProportionalString(x + 320,
|
|
y + 270,
|
|
va("Entity: %i", cg.crosshairClientNum),
|
|
UI_CENTER|UI_SMALLFONT,
|
|
ccolor); //170
|
|
|
|
}
|
|
}
|
|
|
|
cgs.widescreen.state = WIDESCREEN_LEFT;
|
|
CG_AdjustFrom640( &x, &y, &w, &h );
|
|
|
|
trap_R_SetColor( cd->color );
|
|
|
|
//TiM: Huh... we have a problem cap'n.
|
|
//Even though we have absolutely perfect alignment, the ingame drawing (regardless of mipmapping)
|
|
//appears to be blurring the icons to the point where they overlap, leaving little smudges at the corners
|
|
//of certain crosshairs ingame :'(
|
|
//So I'm attempting to fix this by creating a very very subtle offset to scale the scan region inwards a bit.
|
|
//Addendum: FRAK! Okay... offsetting will not work. It clips any of the hairs that are in their full boundary. Which looks crap :P
|
|
|
|
//Magic number! 0.0078125 = 1 pixel in a 128x128 bitmap - Edited out. 1 pixel = WAY TOO MUCH!
|
|
trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * (cg.refdef.width - w), //X
|
|
y + cg.refdef.y + 0.5 * (cg.refdef.height - h), //Y
|
|
w, h, //W+H
|
|
((float)cd->s1/128.0f), ((float)cd->t1/128.0f), //s1 + t1
|
|
((float)cd->s2/128.0f), ((float)cd->t2/128.0f), //s2 + t2
|
|
cgs.media.crosshairSheet );
|
|
|
|
trap_R_SetColor( NULL );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_LabelCrosshairEntity
|
|
=================
|
|
*/
|
|
|
|
static void CG_LabelViewEntity( int clientNum, vec3_t origin, vec3_t entMins, vec3_t entMaxs, char *name, qboolean scanAll, vec4_t color, qboolean drawHealth, int health, char *pClass, char *rank, char *race, char* age, 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};
|
|
float x = 0, y = 0;
|
|
float topLeftx, topLefty, topRightx, topRighty, bottomLeftx, bottomLefty, bottomRightx, bottomRighty;
|
|
int corner, topSize, bottomSize, leftSize, rightSize;
|
|
int charIndex, classCharIndex, rankCharIndex, ageCharIndex, raceCharIndex, htCharIndex, wtCharIndex, weapCharIndex, healthCharIndex;
|
|
float lineHorzLength = 8.0f, lineVertLength = 8.0f, lineWidth = 2.0f;
|
|
float fUpDot, fEastDot, fNorthDot, uNorthDot, uEastDot;
|
|
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];
|
|
char showHealth[1024];
|
|
char showAge[1024];
|
|
char showClass[1024];
|
|
|
|
infoStringCount += cg.frametime;
|
|
rankCharIndex = raceCharIndex = classCharIndex = ageCharIndex = htCharIndex = wtCharIndex = weapCharIndex = charIndex = healthCharIndex = floor(infoStringCount/33);
|
|
//TODO: have box scale in from corners of screen? Or out from center?
|
|
|
|
//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
|
|
|
|
VectorCopy( origin, center ); //crossEnt->currentOrigin//cent->lerpOrigin
|
|
VectorCopy( entMaxs, maxs ); //crossEnt->maxs //playerMaxs
|
|
VectorCopy( entMins, mins ); //crossEnt->mins //playerMins
|
|
|
|
//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 < 13; corner++ ) //11
|
|
{//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 )
|
|
{
|
|
//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 ( ageCharIndex > 0 && age )
|
|
{
|
|
if ( y >= SMALLCHAR_HEIGHT )
|
|
{
|
|
y -= SMALLCHAR_HEIGHT;
|
|
}
|
|
}
|
|
if ( y > 0.01 )
|
|
{
|
|
if ( classCharIndex > 0 && pClass )
|
|
{
|
|
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 ( !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 ( !drawHealth || !health )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
Com_sprintf( showHealth, sizeof( showHealth ), "%s: %i", "Health", health );
|
|
|
|
if ( healthCharIndex > 0 && showHealth[0] ) {
|
|
int len = strlen( showHealth );
|
|
|
|
if ( healthCharIndex > len+1 )
|
|
{
|
|
healthCharIndex = len+1;
|
|
}
|
|
else
|
|
{
|
|
trap_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.tedTextSound );
|
|
}
|
|
w = CG_DrawStrlen( showHealth ) * SMALLCHAR_WIDTH;
|
|
Q_strncpyz( showHealth, showHealth, healthCharIndex );
|
|
CG_DrawSmallStringColor( x - w / 2, y + lineWidth, showHealth, color );
|
|
y += SMALLCHAR_HEIGHT;
|
|
}
|
|
}
|
|
break;
|
|
case 5:
|
|
if ( charIndex > 0 && name )
|
|
{
|
|
int len = strlen(name);
|
|
if ( charIndex > len+1 )
|
|
{
|
|
charIndex = len+1;
|
|
}
|
|
else
|
|
{
|
|
trap_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://class
|
|
if ( classCharIndex > 0 && pClass )
|
|
{
|
|
int len = strlen(pClass);
|
|
if ( classCharIndex > len+1 )
|
|
{
|
|
classCharIndex = len+1;
|
|
}
|
|
else
|
|
{
|
|
trap_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.tedTextSound );
|
|
}
|
|
Q_strncpyz( showClass, pClass, classCharIndex );
|
|
w = CG_DrawStrlen( pClass ) * SMALLCHAR_WIDTH;
|
|
CG_DrawSmallStringColor( x - w / 2, y + lineWidth, showClass, color );
|
|
y += SMALLCHAR_HEIGHT;
|
|
}
|
|
break;
|
|
case 7://rank
|
|
if ( rankCharIndex > 0 && rank )
|
|
{
|
|
int len = strlen(rank);
|
|
if ( rankCharIndex > len+1 )
|
|
{
|
|
rankCharIndex = len+1;
|
|
}
|
|
else
|
|
{
|
|
trap_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 8://age
|
|
if ( ageCharIndex > 0 && age )
|
|
{
|
|
int len = strlen(age);
|
|
if ( ageCharIndex > len+1 )
|
|
{
|
|
ageCharIndex = len+1;
|
|
}
|
|
else
|
|
{
|
|
trap_S_StartSound( NULL, 0, CHAN_ITEM, cgs.media.tedTextSound );
|
|
}
|
|
Q_strncpyz( showAge, age, ageCharIndex );
|
|
w = CG_DrawStrlen( age ) * SMALLCHAR_WIDTH;
|
|
CG_DrawSmallStringColor( x - w / 2, y + lineWidth, showAge, color );
|
|
y += SMALLCHAR_HEIGHT;
|
|
}
|
|
break;
|
|
case 9://race
|
|
if ( raceCharIndex > 0 && race )
|
|
{
|
|
int len = strlen(race);
|
|
if ( raceCharIndex > len+1 )
|
|
{
|
|
raceCharIndex = len+1;
|
|
}
|
|
else
|
|
{
|
|
trap_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 10://height
|
|
if ( htCharIndex > 0 && height )
|
|
{
|
|
int len = strlen(height);
|
|
if ( htCharIndex > len+1 )
|
|
{
|
|
htCharIndex = len+1;
|
|
}
|
|
else
|
|
{
|
|
trap_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 11://weight
|
|
if ( wtCharIndex > 0 && weight )
|
|
{
|
|
int len = strlen(weight);
|
|
if ( wtCharIndex > len+1 )
|
|
{
|
|
wtCharIndex = len+1;
|
|
}
|
|
else
|
|
{
|
|
trap_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 12://weapon
|
|
if ( weapCharIndex > 0 && weapon )
|
|
{
|
|
int len = strlen(weapon);
|
|
if ( weapCharIndex > len+1 )
|
|
{
|
|
weapCharIndex = len+1;
|
|
}
|
|
else
|
|
{
|
|
trap_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( void ) {
|
|
trace_t trace;
|
|
vec3_t start, end;
|
|
int content;
|
|
vec3_t pitchConstraint, df_f;
|
|
|
|
VectorCopy( cg.predictedPlayerState.origin, start ); //cg.refdef.vieworg
|
|
start[2] += (float)cg.predictedPlayerState.viewheight * cgs.clientinfo[cg.predictedPlayerState.clientNum].height;
|
|
|
|
VectorCopy( cg.predictedPlayerState.viewangles, pitchConstraint );
|
|
AngleVectors( pitchConstraint, df_f, NULL, NULL );
|
|
|
|
VectorMA( start, 8912, df_f, end);
|
|
|
|
if ( cg.snap->ps.weapon == WP_7 && cg.zoomed ) {
|
|
CG_Trace( &trace, start, vec3_origin, vec3_origin, end,
|
|
cg.snap->ps.clientNum, CONTENTS_BODY );
|
|
|
|
// if the player is invisible, don't show it
|
|
if ( cg_entities[ trace.entityNum ].currentState.powerups & ( 1 << PW_INVIS ) && !cgs.clientinfo[cg.snap->ps.clientNum].isAdmin/*cg.snap->ps.persistant[PERS_CLASS] != PC_ADMIN*/ ) {
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
CG_Trace( &trace, start, vec3_origin, vec3_origin, end,
|
|
cg.snap->ps.clientNum, MASK_SHOT ); //CONTENTS_SOLID|CONTENTS_BODY
|
|
|
|
if ( trace.entityNum >= MAX_CLIENTS && !cgs.clientinfo[cg.snap->ps.clientNum].isAdmin/*cg.predictedPlayerState.persistant[PERS_CLASS] != PC_ADMIN*/ ) {
|
|
return;
|
|
}
|
|
|
|
// if the player is in fog, don't show it
|
|
content = trap_CM_PointContents( trace.endpos, 0 );
|
|
if ( content & CONTENTS_FOG ) {
|
|
return;
|
|
}
|
|
|
|
// if the player is invisible, don't show it
|
|
if ( cg_entities[ trace.entityNum ].currentState.powerups & ( 1 << PW_INVIS ) && !cgs.clientinfo[cg.snap->ps.clientNum].isAdmin/*cg.snap->ps.persistant[PERS_CLASS] != PC_ADMIN*/ ) {
|
|
return;
|
|
}
|
|
|
|
if ( cg.crosshairClientNum != trace.entityNum) {
|
|
infoStringCount = 0;
|
|
}
|
|
|
|
}
|
|
|
|
// update the fade timer
|
|
cg.crosshairClientNum = trace.entityNum;
|
|
cg.crosshairClientTime = cg.time;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
CG_DrawCrosshairNames
|
|
=====================
|
|
*/
|
|
|
|
extern qboolean PM_PlayerCrouching ( int legsAnim );
|
|
|
|
static vec3_t playerMins = {-12, -12, -24}; //RPG-X : TiM - {-15, -15, -24}
|
|
static vec3_t playerMaxs = {12, 12, 32}; // {15, 15, 32}
|
|
static void CG_DrawCrosshairNames( void ) {
|
|
float *color;
|
|
char name[MAX_QPATH];
|
|
centity_t *cent;
|
|
int x, y;
|
|
qboolean tinyFont;
|
|
int drawFlags;
|
|
|
|
if ( !cg_drawCrosshair.integer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// scan the known entities to see if the crosshair is sighted on one
|
|
CG_ScanForCrosshairEntity();
|
|
|
|
// draw the name of the player being looked at
|
|
color = CG_FadeColor( cg.crosshairClientTime, 1000 );
|
|
if ( !color )
|
|
{
|
|
trap_R_SetColor( NULL );
|
|
infoStringCount = 0;
|
|
return;
|
|
}
|
|
|
|
color[3] *= 0.9;
|
|
|
|
//If they're actively firing the tricorder
|
|
if( ( (cg.snap->ps.eFlags & EF_FIRING) && !(cg.snap->ps.eFlags & EF_ALT_FIRING) )
|
|
&& cg.snap->ps.weapon == WP_2 ) {
|
|
if(cg.crosshairClientNum != cg.predictedPlayerState.clientNum && cg.crosshairClientNum < MAX_CLIENTS ) { //ENTITYNUM_WORLD
|
|
|
|
drawCrosshairName = qfalse;
|
|
|
|
cent = &cg_entities[cg.crosshairClientNum];
|
|
|
|
if ( cent ) {
|
|
char *name = NULL;
|
|
char *rank = NULL;
|
|
char *race = NULL;
|
|
char *age = NULL;
|
|
char *pClass = NULL;
|
|
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];
|
|
char agestr[128];
|
|
char classstr[128];
|
|
int i, irank;
|
|
int score = 0;
|
|
clientInfo_t *ci;
|
|
|
|
for ( i = 0; i < cgs.maxclients; i++ ) {
|
|
if ( cg.scores[i].client == cg.crosshairClientNum ) {
|
|
score = cg.scores[i].score;
|
|
break;
|
|
}
|
|
}
|
|
|
|
irank = score; //Q_log2( score );
|
|
|
|
ci = &cgs.clientinfo[cg.crosshairClientNum];
|
|
//over-ride the color, since we can't get teams in this case
|
|
//use that good old LCARS yellow
|
|
color[0] = 0.694f; //0.9F;//R
|
|
color[1] = 0.816f; //0.7F;//G
|
|
color[2] = 1.0f; //0.0F;//B
|
|
color[3] *= 0.5;
|
|
|
|
ht = ci->height * (float)BASE_HEIGHT;
|
|
wt = ci->weight * ci->height * (float)BASE_WEIGHT;
|
|
if ( ci->gender == GENDER_FEMALE ) {
|
|
wt *= (float)FEMALE_OFFSET; //magic number, women are lighter than men
|
|
}
|
|
|
|
if ( ci->race && ci->race[0] ) {
|
|
race = ci->race;
|
|
Com_sprintf( racestr, sizeof( racestr ), "%s: %s", "Race", race );
|
|
}
|
|
|
|
if ( ci->age && ci->age[0] ) {
|
|
age = ci->age;
|
|
Com_sprintf( agestr, sizeof( agestr ), "%s: %s", "Age", age );
|
|
}
|
|
|
|
pClass = cgs.classData[ci->pClass].formalName;
|
|
|
|
if ( pClass ) {
|
|
Com_sprintf( classstr, sizeof(classstr), "%s: %s", "Class", pClass );
|
|
}
|
|
|
|
if ( cgs.classData[ci->pClass].showRanks ) {
|
|
if ( cgs.ranksData[irank].formalName[0] ) {
|
|
rank = cgs.ranksData[irank].formalName;
|
|
Com_sprintf( rankstr, sizeof( rankstr ), "%s: %s", "Rank", rank );
|
|
}
|
|
}
|
|
|
|
if ( ci->name && ci->name[0] ) {
|
|
name = ci->name;
|
|
}
|
|
else {
|
|
name = "Data Not Available"; //crossEnt->targetname;
|
|
}
|
|
|
|
Com_sprintf( namestr, sizeof( namestr), "%s: %s", "Name", name );
|
|
|
|
if ( cent->currentState.weapon != WP_1 )
|
|
{
|
|
if ( cg_weapons[ cent->currentState.weapon ].item->pickup_name ) {
|
|
weap = cg_weapons[ cent->currentState.weapon ].item->pickup_name;
|
|
Com_sprintf( weapstr, sizeof( weapstr), "%s: %s", "Weapon", weap );
|
|
}
|
|
}
|
|
|
|
Com_sprintf( htstr, sizeof(htstr), "%s: %4.2f %s","Height", ht, HEIGHT_UNIT );
|
|
Com_sprintf( wtstr, sizeof(wtstr), "%s: %4.2f %s","Weight", wt, WEIGHT_UNIT );
|
|
|
|
CG_LabelViewEntity( cg.crosshairClientNum, cent->lerpOrigin, playerMins, playerMaxs,
|
|
name ? namestr : NULL, qfalse, color,
|
|
(cgs.clientinfo[cg.snap->ps.clientNum].isAdmin || cgs.classData[cg.snap->ps.persistant[PERS_CLASS]].isMedic) ? qtrue : qfalse, ci->health,
|
|
pClass ? classstr : NULL,
|
|
rank ? rankstr : NULL,
|
|
race ? racestr : NULL,
|
|
age ? agestr : NULL,
|
|
ht ? htstr : NULL,
|
|
wt ? wtstr : NULL,
|
|
weap ? weapstr : NULL);
|
|
}
|
|
else {
|
|
infoStringCount = 0;
|
|
}
|
|
}
|
|
else {
|
|
if ( (cg_entities[cg.crosshairClientNum].currentState.eType == ET_TRIC_STRING || cg_entities[cg.crosshairClientNum].currentState.eType == ET_MOVER_STR) && cgs.scannablePanels )
|
|
{
|
|
entityState_t *eState;
|
|
vec3_t origin;
|
|
vec3_t mins, maxs;
|
|
char *renderString;
|
|
|
|
eState = &cg_entities[cg.crosshairClientNum].currentState;
|
|
|
|
color[0] = 0.694f; //0.9F;//R
|
|
color[1] = 0.816f; //0.7F;//G
|
|
color[2] = 1.0f; //0.0F;//B
|
|
color[3] *= 0.5;
|
|
|
|
//TiM: Since dynamic brush ents seem to have no freaking origin in them, let's
|
|
// calc our own using the bounding box dimensions (At least we have those lol )
|
|
VectorAverage( eState->origin2, eState->angles2, origin );
|
|
|
|
//The algorithm needs the max and min dimensions to be symmetrical on either side
|
|
//of the origin. This set of random code does that. :)
|
|
VectorSubtract( origin, eState->origin2, mins );
|
|
|
|
VectorSet( maxs, Q_fabs( mins[0] ), Q_fabs( mins[1] ), Q_fabs( mins[2] ) );
|
|
VectorScale( maxs, -1, mins );
|
|
|
|
if ( eState->time2 > 0 )
|
|
renderString = (char *)CG_ConfigString( CS_TRIC_STRINGS + eState->time2 );
|
|
else if ( eState->weapon > 0 && cgs.scannableStrings[eState->weapon-1][0] )
|
|
renderString = cgs.scannableStrings[eState->weapon-1]; //subtracted since '0' is a valid cell value
|
|
else
|
|
renderString = "<undefined>";
|
|
|
|
|
|
CG_LabelViewEntity( cg.crosshairClientNum, origin,
|
|
mins, maxs, renderString,
|
|
qfalse, color,
|
|
qfalse, 0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
drawCrosshairName = qtrue;
|
|
|
|
if ( !cg_drawCrosshairNames.integer || cg.crosshairClientNum > MAX_CLIENTS || !drawCrosshairName )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Now only draw team names + health if specifically wanted
|
|
Q_strncpyz (name, cgs.clientinfo[ cg.crosshairClientNum ].name, sizeof (name) );
|
|
|
|
color[0] = colorTable[CT_YELLOW][0];
|
|
color[1] = colorTable[CT_YELLOW][1];
|
|
color[2] = colorTable[CT_YELLOW][2];
|
|
|
|
if ( !cg_dynamicCrosshairNames.integer )
|
|
{
|
|
x = 320;
|
|
y = 170;
|
|
|
|
tinyFont = qfalse;
|
|
|
|
drawFlags = UI_CENTER|UI_SMALLFONT;
|
|
}
|
|
else
|
|
{
|
|
vec3_t org;
|
|
centity_t *cent;
|
|
float x2, y2;
|
|
|
|
cent = &cg_entities[ cg.crosshairClientNum ];
|
|
|
|
VectorCopy( cent->lerpOrigin, org );
|
|
|
|
if ( PM_PlayerCrouching( cent->currentState.legsAnim ) )
|
|
org[2] += CROUCH_VIEWHEIGHT + 7;
|
|
else
|
|
org[2] += DEFAULT_VIEWHEIGHT + 7;
|
|
|
|
CG_WorldCoordToScreenCoord( org, &x2, &y2, qfalse);
|
|
|
|
x = (int)x2;
|
|
y = (int)y2;
|
|
|
|
tinyFont = qtrue;
|
|
drawFlags = UI_CENTER|UI_BOTTOM|UI_TINYFONT;
|
|
}
|
|
|
|
//FIXME: need health (&armor?) of teammates (if not TEAM_FREE) or everyone (if SPECTATOR) (or just crosshairEnt?) sent to me
|
|
if (cgs.clientinfo[ cg.snap->ps.clientNum ].team == TEAM_SPECTATOR
|
|
|| cgs.classData[cgs.clientinfo[ cg.snap->ps.clientNum ].pClass].isMedic
|
|
|| cgs.clientinfo[ cg.snap->ps.clientNum ].isAdmin ) /*|| cgs.clientinfo[ cg.snap->ps.clientNum ].pClass == PC_MEDIC*/
|
|
{//if I'm a spectator, draw colored health of target under crosshair
|
|
CG_GetColorForHealth( cgs.clientinfo[ cg.crosshairClientNum ].health, cgs.clientinfo[ cg.crosshairClientNum ].armor, color );
|
|
|
|
y -= ( tinyFont ? TINYCHAR_HEIGHT : SMALLCHAR_HEIGHT );
|
|
|
|
UI_DrawProportionalString(x,y+(tinyFont ? TINYCHAR_HEIGHT+5 : SMALLCHAR_HEIGHT),va( "^7%i", cgs.clientinfo[ cg.crosshairClientNum ].health ), drawFlags,color);
|
|
}
|
|
|
|
UI_DrawProportionalString(x,y, va("^7%s ^7(%i)", name, cg.crosshairClientNum), drawFlags, color);
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
/*
|
|
=================
|
|
CG_DrawSpectator
|
|
=================
|
|
*/
|
|
static void CG_DrawSpectator(void) {
|
|
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR )
|
|
{
|
|
UI_DrawProportionalString(SCREEN_WIDTH/2, SCREEN_HEIGHT - ((BIGCHAR_HEIGHT * 1.50) * 2) , ingame_text[IGT_SPECTATOR], UI_BIGFONT|UI_CENTER, colorTable[CT_LTGOLD1]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_DrawVote
|
|
=================
|
|
*/
|
|
static void CG_DrawVote(void) {
|
|
char *s;
|
|
int sec;
|
|
|
|
if ( !cgs.voteTime ) {
|
|
return;
|
|
}
|
|
|
|
// play a talk beep whenever it is modified
|
|
if ( cgs.voteModified ) {
|
|
cgs.voteModified = qfalse;
|
|
trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
|
|
}
|
|
|
|
sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000;
|
|
if ( sec < 0 ) {
|
|
sec = 0;
|
|
}
|
|
s = va("%s(%i):%s %s(F1):%i %s(F2):%i", ingame_text[IGT_VOTE],sec, cgs.voteString,ingame_text[IGT_YES], cgs.voteYes,ingame_text[IGT_NO] ,cgs.voteNo);
|
|
|
|
UI_DrawProportionalString( 0, 58, s, UI_SMALLFONT, colorTable[CT_YELLOW]);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_DrawIntermission
|
|
=================
|
|
*/
|
|
static void CG_DrawIntermission( void ) {
|
|
cg.scoreFadeTime = cg.time;
|
|
CG_DrawScoreboard();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_DrawAbridgedObjective
|
|
=================
|
|
*/
|
|
static void CG_DrawAbridgedObjective(void)
|
|
{
|
|
int i,pixelLen,x,y;
|
|
|
|
for (i=0;i<MAX_OBJECTIVES;i++)
|
|
{
|
|
if (cgs.objectives[i].abridgedText[0])
|
|
{
|
|
if (!cgs.objectives[i].complete)
|
|
{
|
|
pixelLen = UI_ProportionalStringWidth( cgs.objectives[i].abridgedText,UI_TINYFONT);
|
|
|
|
x = 364 - (pixelLen/2);
|
|
y = SCREEN_HEIGHT - PROP_TINY_HEIGHT;
|
|
UI_DrawProportionalString(x, y, cgs.objectives[i].abridgedText, UI_TINYFONT, colorTable[CT_GREEN] );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_DrawFollow
|
|
=================
|
|
*/
|
|
static qboolean CG_DrawFollow( void ) {
|
|
float y;
|
|
vec4_t color;
|
|
const char *name;
|
|
|
|
if ( !(cg.snap->ps.pm_flags & PMF_FOLLOW) ) {
|
|
return qfalse;
|
|
}
|
|
color[0] = 1;
|
|
color[1] = 1;
|
|
color[2] = 1;
|
|
color[3] = 1;
|
|
|
|
y = 16;
|
|
|
|
UI_DrawProportionalString((SCREEN_WIDTH/2), y, ingame_text[IGT_FOLLOWING], UI_BIGFONT|UI_CENTER, colorTable[CT_LTGOLD1]);
|
|
|
|
name = cgs.clientinfo[ cg.snap->ps.clientNum ].name;
|
|
|
|
y += (BIGCHAR_HEIGHT * 1.25);
|
|
UI_DrawProportionalString( (SCREEN_WIDTH/2), 40, name, UI_BIGFONT|UI_CENTER, color);
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=================
|
|
CG_DrawAmmoWarning
|
|
=================
|
|
RPG-X | Marcin | 30/12/2008
|
|
Don't!!!
|
|
*/
|
|
static void CG_DrawAmmoWarning( void )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_DrawWarmup
|
|
=================
|
|
*/
|
|
extern void CG_AddGameModNameToGameName( char *gamename );
|
|
static void CG_DrawWarmup( void ) {
|
|
int w;
|
|
int sec;
|
|
const char *s;
|
|
|
|
sec = cg.warmup;
|
|
if ( !sec ) {
|
|
return;
|
|
}
|
|
|
|
if ( sec < 0 ) {
|
|
s = ingame_text[IGT_WAITINGFORPLAYERS];
|
|
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
|
|
UI_DrawProportionalString(320 - w / 2, 40, s, UI_BIGFONT, colorTable[CT_LTGOLD1]);
|
|
|
|
cg.warmupCount = 0;
|
|
return;
|
|
}
|
|
|
|
char gamename[1024];
|
|
|
|
s = ingame_text[IGT_GAME_FREEFORALL];
|
|
|
|
Q_strncpyz( gamename, s, sizeof(gamename) );
|
|
|
|
CG_AddGameModNameToGameName( gamename );
|
|
|
|
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
|
|
|
|
UI_DrawProportionalString((SCREEN_WIDTH/2) , 20,gamename, UI_BIGFONT|UI_CENTER, colorTable[CT_LTGOLD1]);
|
|
|
|
sec = ( sec - cg.time ) / 1000;
|
|
if ( sec < 0 ) {
|
|
sec = 0;
|
|
}
|
|
s = va( "%s: %i",ingame_text[IGT_STARTSIN], sec + 1 );
|
|
if ( sec != cg.warmupCount ) {
|
|
cg.warmupCount = sec;
|
|
switch ( sec ) {
|
|
case 0:
|
|
trap_S_StartLocalSound( cgs.media.count1Sound, CHAN_ANNOUNCER );
|
|
break;
|
|
case 1:
|
|
trap_S_StartLocalSound( cgs.media.count2Sound, CHAN_ANNOUNCER );
|
|
break;
|
|
case 2:
|
|
trap_S_StartLocalSound( cgs.media.count3Sound, CHAN_ANNOUNCER );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
w = UI_ProportionalStringWidth(s,UI_BIGFONT);
|
|
UI_DrawProportionalString( (SCREEN_WIDTH/2), 70, s, UI_BIGFONT|UI_CENTER, colorTable[CT_LTGOLD1]);
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawZoomMask
|
|
|
|
================
|
|
*/
|
|
static void CG_DrawZoomMask( void )
|
|
{
|
|
float amt = 1, size, /*val,*/ start_x, start_y;
|
|
int width, height, i;
|
|
vec4_t color1;
|
|
int x, y;
|
|
|
|
//TiM: New system. :) Base zoom on current active weapon. :)
|
|
if ( !(cg.snap->ps.weapon == WP_6 || cg.snap->ps.weapon == WP_7) )
|
|
{
|
|
cg.zoomed = qfalse;
|
|
cg.zoomLocked = 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
|
|
trap_R_SetColor( color1 );
|
|
|
|
if ( cg.snap->ps.weapon == WP_7 ) {
|
|
static int TR116LoopTime = 0;
|
|
|
|
//Loop the whirring sight sound
|
|
if ( TR116LoopTime < cg.time )
|
|
{
|
|
trap_S_StartSound( cg.refdef.vieworg, ENTITYNUM_WORLD, CHAN_LOCAL, cgs.media.tr116Whir );
|
|
TR116LoopTime = cg.time + 900;
|
|
}
|
|
|
|
CG_DrawPic( start_x, start_y, width, height, cgs.media.zoomMask116Shader );
|
|
|
|
//if we're zoomed over a potential target, start flashing the red crosshair
|
|
if ( cg.crosshairClientNum != cg.snap->ps.clientNum
|
|
&& cg.crosshairClientNum < MAX_CLIENTS )
|
|
{
|
|
vec4_t alphaColor;
|
|
float amt;
|
|
|
|
if ( cg.time > zoomFlashTime ) {
|
|
zoomFlashTime = cg.time + 800;
|
|
trap_S_StartLocalSound( cgs.media.tr116Chirp, CHAN_LOCAL_SOUND );
|
|
}
|
|
|
|
amt = ( ( zoomFlashTime % cg.time ) / 500.0f );
|
|
amt = Com_Clamp( 0.0, 1.0, amt );
|
|
|
|
VectorSet( alphaColor, 1, 1, 1 );
|
|
alphaColor[3] = amt;
|
|
|
|
trap_R_SetColor( alphaColor );
|
|
//========================================
|
|
|
|
CG_DrawPic( 256, 176, 128, 128, cgs.media.zoomGlow116Shader );
|
|
trap_R_SetColor( color1 );
|
|
}
|
|
else
|
|
{
|
|
zoomFlashTime = 0;
|
|
}
|
|
}
|
|
else {
|
|
CG_DrawPic( start_x, start_y, width, height, cgs.media.zoomMaskShader );
|
|
}
|
|
|
|
//yellow
|
|
if ( cg.snap->ps.weapon == WP_7 ) {
|
|
color1[0] = 0.886f;
|
|
color1[1] = 0.749f;
|
|
color1[2] = 0.0f;
|
|
color1[3] = 0.60f;
|
|
}
|
|
else { // red
|
|
color1[0] = 1.0f;
|
|
color1[1] = 0.0f;
|
|
color1[2] = 0.0f;
|
|
color1[3] = 0.60f;
|
|
}
|
|
|
|
// Convert zoom and view axis into some numbers to throw onto the screen
|
|
if ( cg.snap->ps.weapon == WP_7 ) {
|
|
x = 74;
|
|
y = 340;
|
|
}
|
|
else {
|
|
x = 468;
|
|
y = 300;
|
|
}
|
|
|
|
trap_R_SetColor( color1 );
|
|
CG_DrawNumField( x, y, 5, cg_zoomFov.value * 1000 + 9999, 18, 10 ,NUM_FONT_BIG ); //100
|
|
CG_DrawNumField( x, y+20, 5, cg.refdef.viewaxis[0][0] * 9999 + 20000, 18, 10,NUM_FONT_BIG );
|
|
CG_DrawNumField( x, y+40, 5, cg.refdef.viewaxis[0][1] * 9999 + 20000, 18, 10,NUM_FONT_BIG );
|
|
CG_DrawNumField( x, y+60, 5, cg.refdef.viewaxis[0][2] * 9999 + 20000, 18, 10,NUM_FONT_BIG );
|
|
}
|
|
else
|
|
{
|
|
if ( cg.time - cg.zoomTime <= ZOOM_OUT_TIME )
|
|
{
|
|
amt = 1.0f - ( cg.time - cg.zoomTime ) / ZOOM_OUT_TIME;
|
|
|
|
// Fade mask away
|
|
for ( i = 0; i < 4; i++ ) {
|
|
color1[i] = amt;
|
|
}
|
|
|
|
trap_R_SetColor( color1 );
|
|
if ( cg.snap->ps.weapon == WP_7 ) {
|
|
CG_DrawPic( start_x, start_y, width, height, cgs.media.zoomMask116Shader );
|
|
}
|
|
else {
|
|
CG_DrawPic( start_x, start_y, width, height, cgs.media.zoomMaskShader );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//==================================================================================
|
|
|
|
|
|
/*
|
|
=====================
|
|
CG_Drawcg.adminMsg
|
|
RPG-X | Phenix | 08/06/2005
|
|
RPG-X | Marcin | 30/12/2008
|
|
|
|
Now I'm going to kill you Phenix!!!!
|
|
=====================
|
|
*/
|
|
static void CG_DrawAdminMsg( void ) {
|
|
float y;
|
|
int t;
|
|
int i, msgRow, msgCol;
|
|
int biggestW, w;
|
|
char message[35][45];
|
|
char *thisMessage;
|
|
char *p, *currRow;
|
|
static vec4_t color;
|
|
static vec4_t Boxcolor;
|
|
|
|
y = 460;
|
|
|
|
//Nothing to display
|
|
if ( cg.adminMsgTime < cg.time )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//No message!
|
|
if ( cg.adminMsgMsg[0] == '\0' )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Colour Fade.
|
|
t = cg.adminMsgTime - cg.time;
|
|
|
|
// fade out
|
|
if (t < 500) {
|
|
color[3] = t * 1.0/500;
|
|
} else {
|
|
color[3] = 1.0;
|
|
}
|
|
color[0] = color[1] = color[2] = 1;
|
|
|
|
Boxcolor[0] = 0.016;
|
|
Boxcolor[1] = 0.055;
|
|
Boxcolor[2] = 0.170;
|
|
Boxcolor[3] = color[3];
|
|
|
|
trap_R_SetColor( color );
|
|
|
|
i = 0;
|
|
|
|
msgRow = 0;
|
|
msgCol = 0;
|
|
p = cg.adminMsgMsg;
|
|
currRow = p;
|
|
|
|
while ( qtrue ) {
|
|
if ( !*p ) {
|
|
break;
|
|
}
|
|
|
|
if ( NextWordEndsHere( p ) - currRow > 43 ) { //we need to wrap...
|
|
message[msgRow][msgCol] = '\0';
|
|
currRow = p++;
|
|
++msgRow, msgCol = 0;
|
|
continue;
|
|
}
|
|
|
|
message[msgRow][msgCol] = *p;
|
|
++p, ++msgCol;
|
|
}
|
|
|
|
message[msgRow][msgCol] = '\0';
|
|
++msgRow;
|
|
|
|
biggestW = 0;
|
|
for (i = 0; i < msgRow; i++)
|
|
{
|
|
thisMessage = va("%s", message[i]);
|
|
w = UI_ProportionalStringWidth(thisMessage, UI_SMALLFONT);
|
|
|
|
if (w > biggestW)
|
|
{
|
|
biggestW = w;
|
|
}
|
|
}
|
|
|
|
CG_FillRect( 640 - (biggestW + 22), y - (((SMALLCHAR_HEIGHT + 2) * msgRow) + 2), biggestW + 4, ((SMALLCHAR_HEIGHT + 2) * msgRow) + 4, Boxcolor );
|
|
|
|
for (i = (msgRow - 1); i >= 0; i--)
|
|
{
|
|
y -= (SMALLCHAR_HEIGHT + 2);
|
|
|
|
thisMessage = va("%s", message[i]);
|
|
UI_DrawProportionalString(640 - (biggestW + 20), y, thisMessage, UI_SMALLFONT, color);
|
|
}
|
|
|
|
|
|
trap_R_SetColor( NULL );
|
|
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_Draw2D
|
|
=================
|
|
*/
|
|
static void CG_Draw2D( void ) {
|
|
int i;
|
|
|
|
//TiM : Testing this API function...
|
|
//trap_R_SetColor( colorTable[ CT_RED ] );
|
|
//trap_R_DrawStretchPic( 100, 100, 800, 600, 0, 0, 0.5, 0.5, cgs.media.charsetPropB );
|
|
|
|
// 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_CCAM ) {
|
|
return;
|
|
}
|
|
|
|
if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
|
|
#ifndef FINAL_BUILD
|
|
CG_DrawUpperRight();
|
|
#endif
|
|
CG_DrawIntermission();
|
|
return;
|
|
}
|
|
|
|
if ( !cg.renderingThirdPerson )
|
|
{
|
|
CG_DrawZoomMask();
|
|
}
|
|
|
|
//RPG-X: RedTechie - Keep Lagometer on the botum always
|
|
|
|
cgs.widescreen.state = WIDESCREEN_RIGHT;
|
|
CG_DrawLagometer();
|
|
cgs.widescreen.state = WIDESCREEN_NONE;
|
|
|
|
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR /*|| (cg.snap->ps.eFlags&EF_ELIMINATED)*/ ) {
|
|
CG_DrawCrosshair();
|
|
CG_DrawCrosshairNames();
|
|
} else {
|
|
cgs.widescreen.state = WIDESCREEN_LEFT;
|
|
CG_DrawStatusBar(); //RPG-X: RedTechie - We want health displayed when dead
|
|
// don't draw any status if dead
|
|
if ( cg.snap->ps.stats[STAT_HEALTH] > 1 ) { //RPG-X: RedTechie - No weapons at health 1 (you die at health 1 now)
|
|
CG_DrawAmmoWarning();
|
|
|
|
cgs.widescreen.state = WIDESCREEN_NONE;
|
|
CG_DrawCrosshair();
|
|
|
|
cgs.widescreen.state = WIDESCREEN_CENTER;
|
|
CG_DrawCrosshairNames();
|
|
|
|
cgs.widescreen.state = WIDESCREEN_LEFT;
|
|
CG_DrawWeaponSelect();
|
|
|
|
cgs.widescreen.state = WIDESCREEN_RIGHT;
|
|
CG_DrawHoldableItem();
|
|
CG_DrawReward();
|
|
CG_DrawAbridgedObjective();
|
|
|
|
cgs.widescreen.state = WIDESCREEN_NONE;
|
|
}
|
|
cgs.widescreen.state = WIDESCREEN_NONE;
|
|
}
|
|
|
|
if (cg.showObjectives)
|
|
{
|
|
CG_DrawObjectiveInformation();
|
|
}
|
|
|
|
CG_DrawVote();
|
|
|
|
//RPG-X: RedTechie - Moved above others to keep on the bottum
|
|
//CG_DrawLagometer();
|
|
|
|
CG_DrawUpperRight();
|
|
|
|
CG_DrawLowerRight();
|
|
|
|
CG_DrawLowerLeft();
|
|
|
|
CG_DrawSelfdestructTimer();
|
|
|
|
//RPG-X | Phenix | 08/06/2005
|
|
cgs.widescreen.state = WIDESCREEN_CENTER;
|
|
CG_DrawAdminMsg();
|
|
cgs.widescreen.state = WIDESCREEN_NONE;
|
|
|
|
cgs.widescreen.state = WIDESCREEN_CENTER;
|
|
if ( !CG_DrawFollow() ) {
|
|
CG_DrawWarmup();
|
|
}
|
|
cgs.widescreen.state = WIDESCREEN_NONE;
|
|
|
|
// don't draw center string if scoreboard is up
|
|
cgs.widescreen.state = WIDESCREEN_CENTER;
|
|
if ( !CG_DrawScoreboard() ) {
|
|
CG_DrawCenterString();
|
|
}
|
|
cgs.widescreen.state = WIDESCREEN_NONE;
|
|
|
|
// kef -- need the "use TEAM menu to play" message to draw on top of the bottom bar of scoreboard
|
|
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR /*|| (cg.snap->ps.eFlags&EF_ELIMINATED)*/ )
|
|
{
|
|
cgs.widescreen.state = WIDESCREEN_CENTER;
|
|
CG_DrawSpectator();
|
|
cgs.widescreen.state = WIDESCREEN_NONE;
|
|
}
|
|
|
|
//TiM - Draw teh fl4r3s
|
|
|
|
for (i = 0; i < MAX_LENS_FLARES; i++) {
|
|
if ( lensFlare[i].qfull )
|
|
CG_DrawLensFlare( &lensFlare[i] );
|
|
|
|
if ( lensFlare[i].upTime + lensFlare[i].holdTime + lensFlare[i].downTime > 0 &&
|
|
cg.time > lensFlare[i].startTime + lensFlare[i].upTime + lensFlare[i].holdTime + lensFlare[i].downTime )
|
|
lensFlare[i].qfull = qfalse;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
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;
|
|
}
|
|
|
|
//vectors needed for tricorder
|
|
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 );
|
|
|
|
// 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();
|
|
}
|
|
|