Before you can add the code changes to implement the Hud Completion feature you first must have added the code changes for the Scripted Hud as described here and here. That done, we can move on to the new code changes. All changes take place in the CGame module except for some additions to the menudef.h file(s). It must be stressed that you have the latest version of the UI source code in your source tree before attempting any code changes.
cg_draw.c
About line 32 add :
extern displayContextDef_t cgDC;
// Hud groups
extern int HudGroupFlag;
// end Hud groups
menuDef_t *menuScoreboard = NULL;
In the CG_ScanForCrosshairEntity function about line 2037 add :
// Hud groups
#ifdef SCRIPTHUD
void CG_ScanForCrosshairEntity( void ) {
#else
static void CG_ScanForCrosshairEntity( void ) {
#endif
// end Hud groups
trace_t trace;
In the CG_Draw2D function about line 2585 add :
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
CG_DrawSpectator();
CG_DrawCrosshair();
// Hud groups
#ifdef SCRIPTHUD
if(!(HudGroupFlag & XHAIRNAME))
#endif
// end Hud groups
CG_DrawCrosshairNames();
In the CG_Draw2D function about line 2611 add :
CG_DrawCrosshair();
// Hud groups
#ifdef SCRIPTHUD
if(!(HudGroupFlag & XHAIRNAME))
#endif
// end Hud groups
CG_DrawCrosshairNames();
// Hud groups
#ifdef SCRIPTHUD
if(!(HudGroupFlag & XWEAPONSELECT))
#endif
// end Hud groups
CG_DrawWeaponSelect();
In the CG_Draw2D function about line 2639 add :
CG_DrawVote();
CG_DrawTeamVote();
// Hud groups
#ifdef SCRIPTHUD
if(!(HudGroupFlag & UPPERIGHT))
#endif
// end Hud groups
CG_DrawLagometer();
#ifdef SCRIPTHUD
if (!cg_paused.integer) {
// Hud groups
if(!(HudGroupFlag & UPPERIGHT))
// end Hud groups
CG_DrawUpperRight();
In the CG_Draw2D function about line 2667 add :
cg.scoreBoardShowing = CG_DrawScoreboard();
if ( !cg.scoreBoardShowing) {
// Hud groups
#ifdef SCRIPTHUD
if(!(HudGroupFlag & XCENTERPRINT))
#endif
// end Hud groups
CG_DrawCenterString();
cg_local.h
About line 444 add :
#define MAX_REWARDSTACK 10
#define MAX_SOUNDBUFFER 20
// HUD groups
#ifdef SCRIPTHUD
#define ALL_GROUPS 0x0000FFFF
#define XHAIRNAME 0x00000001
#define UPPERIGHT 0x00000002
#define XCONSOLE 0x00000004
#define XWEAPONSELECT 0x00000008
#define XCENTERPRINT 0x00000010
#endif
// end Hud groups
About line 1214 add
extern vmCvar_t cg_blueTeamName;
extern vmCvar_t cg_currentSelectedPlayer;
#endif
// Hud groups
#ifdef SCRIPTHUD
extern vmCvar_t cg_consoleLatency;
#endif
// end Hud groups
About line 1258 add :
score_t *CG_GetSelectedScore( void );
void CG_BuildSpectatorString( void );
// HUD groups
#ifdef SCRIPTHUD
void CG_RemoveConsoleLine( void );
void CG_TAUIConsole( const char *text );
#endif
// end HUD groups
About line 1361 add :
// HUD groups
#ifdef SCRIPTHUD
void CG_ScanForCrosshairEntity( void );
#endif
// end HUD groups
//
// cg_player.c
//
void CG_Player( centity_t *cent );
cg_main.c
About line 28 add :
// display context for new ui stuff
displayContextDef_t cgDC;
// Hud groups
int HudGroupFlag;
// end Hud groups
#endif
About line 193 add :
vmCvar_t cg_blueTeamName;
vmCvar_t cg_currentSelectedPlayer;
// Hud groups
#ifdef SCRIPTHUD
vmCvar_t cg_consoleLatency;
#endif
// end Hud groups
#endif
About line 298 add :
{cg_blueTeamName, "g_blueteam", DEFAULT_BLUETEAM_NAME, CVAR_ARCHIVE |
{ &cg_currentSelectedPlayer, "cg_currentSelectedPlayer", "0", CVAR_ARCHIVE},
#endif
// Hud groups
#ifdef SCRIPTHUD
{ &cg_consoleLatency, "cg_consoleLatency", "3000", CVAR_ARCHIVE },
#endif
// end Hud groups
Before function CG_Printf about line 438 add :
// Hud groups
#ifdef SCRIPTHUD
void CG_RemoveConsoleLine( void )
{
int i, offset, totalLength;
if( cg.numConsoleLines == 0 )
return;
offset = cg.consoleLines[ 0 ].length;
totalLength = strlen( cg.consoleText ) - offset;
//slide up consoleText
for( i = 0; i <= totalLength; i++ )
cg.consoleText[ i ] = cg.consoleText[ i + offset ];
//pop up the first consoleLine
for( i = 0; i < cg.numConsoleLines; i++ )
cg.consoleLines[ i ] = cg.consoleLines[ i + 1 ];
cg.numConsoleLines--;
}
void CG_TAUIConsole( const char *text )
{
if( cg.numConsoleLines == MAX_CONSOLE_LINES )
CG_RemoveConsoleLine( );
if( cg.consoleValid )
{
strcat( cg.consoleText, text );
cg.consoleLines[ cg.numConsoleLines ].time = cg.time;
cg.consoleLines[ cg.numConsoleLines ].length = strlen( text );
cg.numConsoleLines++;
}
}
#endif
// end Hud groups
void QDECL CG_Printf( const char *msg, ... ) {
In function CG_Printf about line 484 add :
vsprintf (text, msg, argptr);
va_end (argptr);
// Hud groups
#ifdef SCRIPTHUD
CG_TAUIConsole( text );
#endif
// end Hud groups
trap_Print( text );
In function Com_Printf about line 524 add :
vsprintf (text, msg, argptr);
va_end (argptr);
// Hud groups
#ifdef SCRIPTHUD
if( cg.numConsoleLines == MAX_CONSOLE_LINES )
CG_RemoveConsoleLine( );
if( cg.consoleValid )
{
strcat( cg.consoleText, text );
cg.consoleLines[ cg.numConsoleLines ].time = cg.time;
cg.consoleLines[ cg.numConsoleLines ].length = strlen( text );
cg.numConsoleLines++;
}
#endif
// end Hud groups
CG_Printf ("%s", text);
In function CG_Asset_Parse about line 1674 add :
cgDC.Assets.fxPic[6] = trap_R_RegisterShaderNoMip( tempStr);
continue;
}
// end changed RD SCRIPTHUD
// Hud groups
if (Q_stricmp(token.string, "allHudExtensions") == 0) {
int flag;
if (!PC_Int_Parse(handle, &flag)) {
return qfalse;
}
if(flag) {
HudGroupFlag = ALL_GROUPS;
}
continue;
}
if (Q_stricmp(token.string, "xhairExtension") == 0) {
int flag;
if (!PC_Int_Parse(handle, &flag)) {
return qfalse;
}
if(flag) {
HudGroupFlag |= XHAIRNAME;
}
continue;
}
if (Q_stricmp(token.string, "upperRightExtension") == 0) {
int flag;
if (!PC_Int_Parse(handle, &flag)) {
return qfalse;
}
if(flag) {
HudGroupFlag |= UPPERIGHT;
}
continue;
}
if (Q_stricmp(token.string, "consoleExtension") == 0) {
int flag;
if (!PC_Int_Parse(handle, &flag)) {
return qfalse;
}
if(flag) {
HudGroupFlag |= XCONSOLE;
}
continue;
}
if (Q_stricmp(token.string, "weaponSelectExtension") == 0) {
int flag;
if (!PC_Int_Parse(handle, &flag)) {
return qfalse;
}
if(flag) {
HudGroupFlag |= XWEAPONSELECT;
}
continue;
}
if (Q_stricmp(token.string, "centerPrintExtension") == 0) {
int flag;
if (!PC_Int_Parse(handle, &flag)) {
return qfalse;
}
if(flag) {
HudGroupFlag |= XCENTERPRINT;
}
continue;
}
// end Hud groups
}
return qfalse; // bk001204 - why not?
In function CG_LoadHudMenu about line 2134 add :
char buff[1024];
const char *hudSet;
// Hud groups
HudGroupFlag = 0;
// end Hud groups
cgDC.registerShaderNoMip = &trap_R_RegisterShaderNoMip;
In function CG_Init about line 2354 add :
trap_S_ClearLoopingSounds( qtrue );
// Hud groups
#ifdef SCRIPTHUD
//repress standard Q3 console
if(HudGroupFlag & XCONSOLE)
trap_Cvar_Set( "con_notifytime", "-2" );
#endif
// end Hud groups
cg_newdraw.c
About line 31 add :
extern displayContextDef_t cgDC;
// Hud groups
extern int HudGroupFlag;
// end Hud groups
Before function CG_OwnerDraw about line 1793 add :
// Hud groups
#ifdef SCRIPTHUD
/*
=====================
CG_DrawXCrosshairNames
=====================
*/
static void CG_DrawXCrosshairNames( rectDef_t *rect, float scale, int textStyle )
{
float *color;
char *name;
float w, x;
if(!(HudGroupFlag & XHAIRNAME))
return;
if( !cg_drawCrosshair.integer )
return;
if( !cg_drawCrosshairNames.integer )
return;
if( cg.renderingThirdPerson )
return;
// scan the known entities to see if the crosshair is sighted on one
CG_ScanForCrosshairEntity( );
// draw the name of the player being looked at
color = CG_FadeColor( cg.crosshairClientTime, 1000 );
if( !color )
{
trap_R_SetColor( NULL );
return;
}
name = cgs.clientinfo[ cg.crosshairClientNum ].name;
w = CG_Text_Width( name, scale, 0 );
x = rect->x + rect->w / 2;
CG_Text_Paint( x - w / 2, rect->y + rect->h, scale, color, name, 0, 0, textStyle );
trap_R_SetColor( NULL );
}
/*==============
DrawFieldPadded
Draws large numbers for status bar and powerups
==============*/
static void CG_DrawFieldPadded( int x, int y, int width, int cw, int ch, int value )
{
char num[ 16 ], *ptr;
int l, orgL;
int frame;
int charWidth, charHeight;
charWidth = cw;
if( !charWidth )
charWidth = CHAR_WIDTH;
charHeight = ch;
if( !charHeight )
charWidth = CHAR_HEIGHT;
if( width < 1 )
return;
// draw number string
if( width > 4 )
width = 4;
switch( width )
{
case 1:
value = value > 9 ? 9 : value;
value = value < 0 ? 0 : value;
break;
case 2:
value = value > 99 ? 99 : value;
value = value < -9 ? -9 : value;
break;
case 3:
value = value > 999 ? 999 : value;
value = value < -99 ? -99 : value;
break;
case 4:
value = value > 9999 ? 9999 : value;
value = value < -999 ? -999 : value;
break;
}
Com_sprintf( num, sizeof( num ), "%d", value );
l = strlen( num );
if( l > width )
l = width;
orgL = l;
x += 2;
ptr = num;
while( *ptr && l )
{
if( width > orgL )
{
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[ 0 ] );
width--;
x += charWidth;
continue;
}
if( *ptr == '-' )
frame = STAT_MINUS;
else
frame = *ptr - '0';
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[ frame ] );
x += charWidth;
ptr++;
l--;
}
}
/*==============
CG_DrawField
Draws large numbers for status bar and powerups
==============*/
static void CG_DrawField( int x, int y, int width, int cw, int ch, int value )
{
char num[ 16 ], *ptr;
int l;
int frame;
int charWidth, charHeight;
charWidth = cw;
if( !charWidth)
charWidth = CHAR_WIDTH;
charHeight = ch;
if( !charHeight )
charWidth = CHAR_HEIGHT;
if( width < 1 )
return;
// draw number string
if( width > 4 )
width = 4;
switch( width )
{
case 1:
value = value > 9 ? 9 : value;
value = value < 0 ? 0 : value;
break;
case 2:
value = value > 99 ? 99 : value;
value = value < -9 ? -9 : value;
break;
case 3:
value = value > 999 ? 999 : value;
value = value < -99 ? -99 : value;
break;
case 4:
value = value > 9999 ? 9999 : value;
value = value < -999 ? -999 : value;
break;
}
Com_sprintf( num, sizeof( num ), "%d", value );
l = strlen( num );
if( l > width )
l = width;
x += 2 + charWidth * ( width - l );
ptr = num;
while( *ptr && l )
{
if( *ptr == '-' )
frame = STAT_MINUS;
else
frame = *ptr -'0';
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[ frame ] );
x += charWidth;
ptr++;
l--;
}
}
/*==================
CG_DrawXFPS
==================*/
//TA: personally i think this should be longer - it should really be a cvar
#define FPS_XFRAMES 20
#define FPS_STRING "fps"
static void CG_DrawXFPS( rectDef_t *rect, float text_x, float text_y,
float scale, vec4_t color, int align, int textStyle,
qboolean scalableText )
{
char *s;
int tx, w, totalWidth, strLength;
static int previousTimes[ FPS_XFRAMES ];
static int index;
int i, total;
int fps;
static int previous;
int t, frameTime;
if(!(HudGroupFlag & UPPERIGHT))
return;
if( !cg_drawFPS.integer )
return;
// 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_XFRAMES ] = frameTime;
index++;
if( index > FPS_XFRAMES )
{
// average multiple frames together to smooth changes out a bit
total = 0;
for( i = 0 ; i < FPS_XFRAMES ; i++ )
total += previousTimes[ i ];
if( !total )
total = 1;
fps = 1000 * FPS_XFRAMES / total;
s = va( "%d", fps );
w = CG_Text_Width( "0", scale, 0 );
strLength = CG_DrawStrlen( s );
totalWidth = CG_Text_Width( FPS_STRING, scale, 0 ) + w * strLength;
switch( align )
{
case ITEM_ALIGN_LEFT:
tx = rect->x;
break;
case ITEM_ALIGN_RIGHT:
tx = rect->x + rect->w - totalWidth;
break;
case ITEM_ALIGN_CENTER:
tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f );
break;
default:
tx = 0.0f;
}
if( scalableText )
{
for( i = 0; i < strLength; i++ )
{
char c[ 2 ];
c[ 0 ] = s[ i ];
c[ 1 ] = '\0';
CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle );
}
}
else
{
trap_R_SetColor( color );
CG_DrawField( rect->x, rect->y, 3, rect->w / 3, rect->h, fps );
trap_R_SetColor( NULL );
}
if( scalableText )
CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, FPS_STRING, 0, 0, textStyle );
}
}
/*=================
CG_DrawTimerMins
=================*/
static void CG_DrawTimerMins( rectDef_t *rect, vec4_t color )
{
int mins, seconds;
int msec;
if(!(HudGroupFlag & UPPERIGHT))
return;
if( !cg_drawTimer.integer )
return;
msec = cg.time - cgs.levelStartTime;
seconds = msec / 1000;
mins = seconds / 60;
seconds -= mins * 60;
trap_R_SetColor( color );
CG_DrawField( rect->x, rect->y, 3, rect->w / 3, rect->h, mins );
trap_R_SetColor( NULL );
}
/*=================
CG_DrawTimerSecs
=================*/
static void CG_DrawTimerSecs( rectDef_t *rect, vec4_t color )
{
int mins, seconds;
int msec;
if(!(HudGroupFlag & UPPERIGHT))
return;
if( !cg_drawTimer.integer )
return;
msec = cg.time - cgs.levelStartTime;
seconds = msec / 1000;
mins = seconds / 60;
seconds -= mins * 60;
trap_R_SetColor( color );
CG_DrawFieldPadded( rect->x, rect->y, 2, rect->w / 2, rect->h, seconds );
trap_R_SetColor( NULL );
}
/*=================
CG_DrawTimer
=================*/
static void CG_DrawTimer( rectDef_t *rect, float text_x, float text_y,
float scale, vec4_t color, int align, int textStyle )
{
char *s;
int i, tx, w, totalWidth, strLength;
int mins, seconds, tens;
int msec;
if(!(HudGroupFlag & UPPERIGHT))
return;
if( !cg_drawTimer.integer )
return;
msec = cg.time - cgs.levelStartTime;
seconds = msec / 1000;
mins = seconds / 60;
seconds -= mins * 60;
tens = seconds / 10;
seconds -= tens * 10;
s = va( "%d:%d%d", mins, tens, seconds );
w = CG_Text_Width( "0", scale, 0 );
strLength = CG_DrawStrlen( s );
totalWidth = w * strLength;
switch( align )
{
case ITEM_ALIGN_LEFT:
tx = rect->x;
break;
case ITEM_ALIGN_RIGHT:
tx = rect->x + rect->w - totalWidth;
break;
case ITEM_ALIGN_CENTER:
tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f );
break;
default:
tx = 0.0f;
}
for( i = 0; i < strLength; i++ )
{
char c[ 2 ];
c[ 0 ] = s[ i ];
c[ 1 ] = '\0';
CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle );
}
}
/*==================
CG_DrawXSnapshot
==================*/
static void CG_DrawXSnapshot( rectDef_t *rect, float text_x, float text_y,
float scale, vec4_t color, int align, int textStyle )
{
char *s;
int w, tx;
if(!(HudGroupFlag & UPPERIGHT))
return;
if( !cg_drawSnapshot.integer )
return;
s = va( "time:%d snap:%d cmd:%d", cg.snap->serverTime,
cg.latestSnapshotNum, cgs.serverCommandSequence );
w = CG_Text_Width( s, scale, 0 );
switch( align )
{
case ITEM_ALIGN_LEFT:
tx = rect->x;
break;
case ITEM_ALIGN_RIGHT:
tx = rect->x + rect->w - w;
break;
case ITEM_ALIGN_CENTER:
tx = rect->x + ( rect->w / 2.0f ) - ( w / 2.0f );
break;
default:
tx = 0.0f;
}
CG_Text_Paint( text_x + tx, rect->y + text_y, scale, color, s, 0, 0, textStyle );
}
#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_DrawXDisconnect
Should we draw something differnet for long lag vs no packets?
==============*/
static void CG_DrawXDisconnect( void )
{
float x, y;
int cmdNum;
usercmd_t cmd;
const char *s;
int w;
vec4_t color = { 1.0f, 1.0f, 1.0f, 1.0f };
// draw the phone jack if we are completely past our buffers
cmdNum = trap_GetCurrentCmdNumber( ) - CMD_BACKUP + 1;
trap_GetUserCmd( cmdNum, &cmd );
// special check for map_restart
if( cmd.serverTime <= cg.snap->ps.commandTime || cmd.serverTime > cg.time )
return;
// also add text in center of screen
s = "Connection Interrupted";
w = CG_Text_Width( s, 0.7f, 0 );
CG_Text_Paint( 320 - w / 2, 100, 0.7f, color, s, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
// blink the icon
if( ( cg.time >> 9 ) & 1 )
return;
x = 640 - 48;
y = 480 - 48;
CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader( "gfx/2d/net.tga" ) );
}
#define MAX_LAGOMETER_PING 900
#define MAX_LAGOMETER_RANGE 300
#define PING_FRAMES 40
/*==============
CG_DrawXLagometer
==============*/
static void CG_DrawXLagometer( rectDef_t *rect, float text_x, float text_y,
float scale, vec4_t textColor )
{
int a, x, y, i;
float v;
float ax, ay, aw, ah, mid, range;
int color;
vec4_t adjustedColor;
float vscale;
vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
if(!(HudGroupFlag & UPPERIGHT))
return;
if( cg.snap->ps.pm_type == PM_INTERMISSION )
return;
if( !cg_lagometer.integer )
return;
if( cg.demoPlayback )
return;
Vector4Copy( textColor, adjustedColor );
adjustedColor[ 3 ] = 0.25f;
trap_R_SetColor( adjustedColor );
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.whiteShader );
trap_R_SetColor( NULL );
//
// draw the graph
//
ax = x = rect->x;
ay = y = rect->y;
aw = rect->w;
ah = rect->h;
trap_R_SetColor( NULL );
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_Text_Paint( ax, ay, 0.5, white, "snc", 0, 0, ITEM_TEXTSTYLE_NORMAL );
else
{
static int previousPings[ PING_FRAMES ];
static int index;
int i, ping = 0;
char *s;
previousPings[ index++ ] = cg.snap->ping;
index = index % PING_FRAMES;
for( i = 0; i < PING_FRAMES; i++ )
ping += previousPings[ i ];
ping /= PING_FRAMES;
s = va( "%d", ping );
ax = rect->x + ( rect->w / 2.0f ) - ( CG_Text_Width( s, scale, 0 ) / 2.0f ) + text_x;
ay = rect->y + ( rect->h / 2.0f ) + ( CG_Text_Height( s, scale, 0 ) / 2.0f ) + text_y;
Vector4Copy( textColor, adjustedColor );
adjustedColor[ 3 ] = 0.5f;
CG_Text_Paint( ax, ay, scale, adjustedColor, s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
}
CG_DrawXDisconnect( );
}
/*==============
CG_DrawConsole
==============*/
static void CG_DrawConsole( rectDef_t *rect, float text_x, float text_y, vec4_t color,
float scale, int align, int textStyle )
{
float x, y, w, h;
//for some reason if these are stored locally occasionally rendering fails
//even though they are both live until the end of the function, hence static
//possible compiler bug??
static menuDef_t dummyParent;
static itemDef_t textItem;
if(!(HudGroupFlag & XCONSOLE))
return;
//offset the text
x = rect->x;
y = rect->y;
w = rect->w - ( 16 + ( 2 * text_x ) ); //16 to ensure text within frame
h = rect->h;
textItem.text = cg.consoleText;
textItem.parent = &dummyParent;
memcpy( textItem.window.foreColor, color, sizeof( vec4_t ) );
textItem.window.flags = 0;
switch( align )
{
case ITEM_ALIGN_LEFT:
textItem.window.rect.x = x;
break;
case ITEM_ALIGN_RIGHT:
textItem.window.rect.x = x + w;
break;
case ITEM_ALIGN_CENTER:
textItem.window.rect.x = x + ( w / 2 );
break;
default:
textItem.window.rect.x = x;
break;
}
textItem.window.rect.y = y;
textItem.window.rect.w = w;
textItem.window.rect.h = h;
textItem.window.borderSize = 0;
textItem.textRect.x = 0;
textItem.textRect.y = 0;
textItem.textRect.w = 0;
textItem.textRect.h = 0;
textItem.textalignment = align;
textItem.textalignx = text_x;
textItem.textaligny = text_y;
textItem.textscale = scale;
textItem.textStyle = textStyle;
//hack to utilise existing autowrap code
Item_Text_AutoWrapped_Paint( &textItem );
}
/*====================
CG_DrawXWeaponSelect
====================*/
static void CG_DrawXWeaponSelect(rectDef_t *rect, int align, float special, float scale,
vec4_t forecolor, int textStyle) {
rectDef_t r2;
int i;
int bits;
int count;
int x, y;
float *color;
if(!(HudGroupFlag & XWEAPONSELECT))
return;
r2.x = rect->x;
r2.y = rect->y;
r2.w = rect->w;
r2.h = rect->h;
if(special==0)
special = 4;
// don't display if dead
if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
return;
}
color = CG_FadeColor( cg.weaponSelectTime, WEAPON_SELECT_TIME );
if ( !color ) {
return;
}
trap_R_SetColor( color );
// showing weapon select clears pickup item display, but not the blend blob
cg.itemPickupTime = 0;
// count the number of weapons owned
bits = cg.snap->ps.stats[ STAT_WEAPONS ];
count = 0;
for ( i = 1 ; i < 16 ; i++ ) {
if ( bits & ( 1 << i ) ) {
count++;
}
}
if (align == HUD_VERTICAL) {
x = r2.x;
y = r2.y;
}
else {
x = r2.x - count * (r2.w/2);
y = r2.y;
}
for ( i = 1 ; i < 16 ; i++ ) {
if ( !( bits & ( 1 << i ) ) ) {
continue;
}
CG_RegisterWeapon( i );
// draw weapon icon
CG_DrawPic( x+special, y+special, r2.w-(special*2), r2.h-(special*2), cg_weapons[i].weaponIcon );
// draw selection marker
if ( i == cg.weaponSelect ) {
CG_DrawPic( x, y, r2.w, r2.h, cgs.media.selectShader );
}
// no ammo cross on top
if ( !cg.snap->ps.ammo[ i ] ) {
CG_DrawPic( x+special, y+special, r2.w-(special*2), r2.h-(special*2), cgs.media.noammoShader );
}
if (align == HUD_VERTICAL) {
y += r2.h;
}
else {
x += r2.w;
}
}
trap_R_SetColor( NULL );
}
/*====================
CG_XCenterPrint
====================*/
static void CG_XCenterPrint( rectDef_t *rect, float text_x, float text_y,
float scale, vec4_t forecolor, int align, int textStyle )
{
char *start;
int l;
int y, w;
int tx ;
float *color;
if(!(HudGroupFlag & XCENTERPRINT))
return;
if ( !cg.centerPrintTime ) {
return;
}
color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value );
if ( !color ) {
return;
}
trap_R_SetColor( color );
start = cg.centerPrint;
y = text_y;
while ( 1 ) {
char linebuffer[1024];
for ( l = 0; l < 50; l++ ) {
if ( !start[l] || start[l] == '\n' ) {
break;
}
linebuffer[l] = start[l];
}
linebuffer[l] = 0;
w = CG_Text_Width(linebuffer, scale, 0);
switch( align )
{
case ITEM_ALIGN_LEFT:
tx = rect->x + text_x;
break;
case ITEM_ALIGN_RIGHT:
tx = rect->x - w + text_x;
break;
case ITEM_ALIGN_CENTER:
tx = rect->x - ( w / 2.0f ) + text_x;
break;
default:
tx = rect->x + text_x;
}
CG_Text_Paint(tx, rect->y + y, scale, color, linebuffer, 0, 0, textStyle);
y += text_y;
while ( *start && ( *start != '\n' ) ) {
start++;
}
if ( !*start ) {
break;
}
start++;
}
trap_R_SetColor( NULL );
}
#endif
// end Hud groups
In function CG_OwnerDraw about line 2919 add :
case CG_LOAD_HOSTNAME:
CG_DrawHostname( &rect, text_x, text_y, color, scale, align, textStyle );
break;
#endif
// end loadingscreen
// Hud groups
#ifdef SCRIPTHUD
case CG_PLAYER_CROSSHAIRNAMES:
CG_DrawXCrosshairNames( &rect, scale, textStyle );
break;
case CG_FPS:
CG_DrawXFPS( &rect, text_x, text_y, scale, color, align, textStyle, qtrue );
break;
case CG_FPS_FIXED:
CG_DrawXFPS( &rect, text_x, text_y, scale, color, align, textStyle, qfalse );
break;
case CG_TIMER:
CG_DrawTimer( &rect, text_x, text_y, scale, color, align, textStyle );
break;
case CG_TIMER_MINS:
CG_DrawTimerMins( &rect, color );
break;
case CG_TIMER_SECS:
CG_DrawTimerSecs( &rect, color );
break;
case CG_SNAPSHOT:
CG_DrawXSnapshot( &rect, text_x, text_y, scale, color, align, textStyle );
break;
case CG_LAGOMETER:
CG_DrawXLagometer( &rect, text_x, text_y, scale, color );
break;
case CG_CONSOLE:
CG_DrawConsole( &rect, text_x, text_y, color, scale, align, textStyle );
break;
case CG_WEAPONSELECT:
CG_DrawXWeaponSelect(&rect, align, special, scale, color, textStyle);
break;
case CG_CENTERPRINT:
CG_XCenterPrint( &rect, text_x, text_y, scale, color, align, textStyle);
break;
#endif
// end Hud groups
default:
break;
cg_view.c
In function CG_DrawActiveFrame about line 846 add :
memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) );
// Hud groups
#ifdef SCRIPTHUD
//remove expired console lines
if( cg.consoleLines[ 0 ].time + cg_consoleLatency.integer < cg.time && cg_consoleLatency.integer > 0 )
CG_RemoveConsoleLine( );
#endif
// end Hud groups
// warning sounds when powerup is wearing off
CG_PowerupTimerSounds();
menudef.h
There are two menudef.h files that must be modified for this feature. The first is in the source code tree in the ui folder. This folder is at the same level as the code folder that contains the Quake 3 source code. The second is in the baseq3/ui folder where the menu and Hud scripts are located. These files must be identical so the following changes must be done to both of them.
About line 240 add :
// Hud groups
#define CG_PLAYER_CROSSHAIRNAMES 90
#define CG_FPS 91
#define CG_FPS_FIXED 92
#define CG_TIMER 93
#define CG_TIMER_MINS 94
#define CG_TIMER_SECS 95
#define CG_SNAPSHOT 96
#define CG_LAGOMETER 97
#define CG_CONSOLE 98
#define CG_WEAPONSELECT 99
#define CG_CENTERPRINT 100
// end Hud groups
#define UI_OWNERDRAW_BASE 200
#define UI_HANDICAP 20