Skip to content

Instantly share code, notes, and snippets.

@zturtleman
Last active December 4, 2024 19:25
Show Gist options
  • Select an option

  • Save zturtleman/7350915 to your computer and use it in GitHub Desktop.

Select an option

Save zturtleman/7350915 to your computer and use it in GitHub Desktop.
Patch for ioquake3 to allow aspect correct HUD/FOV and scaling or disabling parts of the HUD using cvars. And replace head on HUD with health item.
This github gist is no longer maintained. See web pages below.
webpage: https://clover.moe/flexible-hud-for-ioq3/
git branch: https://github.com/zturtleman/ioq3/tree/flexible_hud
----
New cvars:
cg_stretch [0 - 1] stretch 4:3 HUD to fit screen (default: 1)
cg_fovAspectAdjust [0 - 1] automatically convert 4:3 cg_fov values to current aspect (default: 0)
cg_fovGunAdjust [0 - 1] controls gun offset based on FOV. 0 uses Q3A code, 1 uses code (works with FOV < 90 and fixed FOV dmflags) (default: 0)
These all default to 1.
cg_statusScale [0.0 - 1.0] set scale of status bar (ammo, health, head, armor, CTF flag).
cg_drawStatusHead [0 - 2] 0 disables drawing head, 1 acts like Q3A, 2 drawing health item instead (supports 3D and 2D icons).
cg_drawPickups [0.0 - 1.0] set scale of item pickup message (0 to disable).
cg_drawWeaponBar [0.0 - 1.0] set scale of weapon select popup (0 to disable).
cg_drawScores [0 - 1] toggle drawing scores in bottom right corner (no scaling support).
Recommended settings:
cg_stretch 0; cg_fovAspectAdjust 1; cg_fovGunAdjust 1; cg_fov 90;
diff --git a/code/cgame/cg_draw.c b/code/cgame/cg_draw.c
index aa22657..89d866c 100644
--- a/code/cgame/cg_draw.c
+++ b/code/cgame/cg_draw.c
@@ -248,6 +248,10 @@ static void CG_DrawField (int x, int y, int width, int value) {
l = width;
x += 2 + CHAR_WIDTH*(width - l);
+ // center x, move y to bottom.
+ x += (1.0f-cg_statusScale.value)*l*CHAR_WIDTH*0.5f;
+ y += (1.0f-cg_statusScale.value)*CHAR_HEIGHT;
+
ptr = num;
while (*ptr && l)
{
@@ -256,8 +260,8 @@ static void CG_DrawField (int x, int y, int width, int value) {
else
frame = *ptr -'0';
- CG_DrawPic( x,y, CHAR_WIDTH, CHAR_HEIGHT, cgs.media.numberShaders[frame] );
- x += CHAR_WIDTH;
+ CG_DrawPic( x,y, CHAR_WIDTH*cg_statusScale.value, CHAR_HEIGHT*cg_statusScale.value, cgs.media.numberShaders[frame] );
+ x += CHAR_WIDTH*cg_statusScale.value;
ptr++;
l--;
}
@@ -310,6 +314,58 @@ void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandl
/*
================
+CG_DrawHealthModel
+
+================
+*/
+void CG_DrawHealthModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, qhandle_t model2, vec3_t origin, vec3_t angles, float yaw2 ) {
+ 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.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 );
+
+ if ( model2 ) {
+ ent.hModel = model2;
+ angles[YAW] = yaw2;
+ AnglesToAxis( angles, ent.axis );
+ trap_R_AddRefEntityToScene( &ent );
+ }
+
+ trap_R_RenderScene( &refdef );
+}
+
+/*
+================
CG_DrawHead
Used for both the status bar and the scoreboard
@@ -471,7 +527,7 @@ static void CG_DrawStatusBarHead( float x ) {
angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac;
angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac;
- CG_DrawHead( x, 480 - size, size, size,
+ CG_DrawHead( x+(1.0f-cg_statusScale.value)*size*0.5f, 480 - size*cg_statusScale.value, size*cg_statusScale.value, size*cg_statusScale.value,
cg.snap->ps.clientNum, angles );
}
#endif // MISSIONPACK
@@ -484,7 +540,8 @@ CG_DrawStatusBarFlag
*/
#ifndef MISSIONPACK
static void CG_DrawStatusBarFlag( float x, int team ) {
- CG_DrawFlagModel( x, 480 - ICON_SIZE, ICON_SIZE, ICON_SIZE, team, qfalse );
+ int iconSize = ICON_SIZE*cg_statusScale.value;
+ CG_DrawFlagModel( x+(1.0f-cg_statusScale.value)*ICON_SIZE*0.5f, 480 - iconSize, iconSize, iconSize, team, qfalse );
}
#endif // MISSIONPACK
@@ -511,7 +568,9 @@ void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team )
return;
}
trap_R_SetColor( hcolor );
+ CG_SetScreenPlacement(PLACE_STRETCH, CG_GetScreenVerticalPlacement());
CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
+ CG_PopScreenPlacement();
trap_R_SetColor( NULL );
}
@@ -530,6 +589,7 @@ static void CG_DrawStatusBar( void ) {
vec4_t hcolor;
vec3_t angles;
vec3_t origin;
+ float scale, iconSize;
static float colors[4][4] = {
// { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} };
@@ -542,8 +602,14 @@ static void CG_DrawStatusBar( void ) {
return;
}
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_BOTTOM);
+
+ scale = Com_Clamp( 0.1f, 2, cg_statusScale.value);
+
+ iconSize = scale * ICON_SIZE;
+
// draw the team background
- CG_DrawTeamBackground( 0, 420, 640, 60, 0.33f, cg.snap->ps.persistant[PERS_TEAM] );
+ CG_DrawTeamBackground( 0, 480 - 60*scale, 640, 60*scale, 0.33f, cg.snap->ps.persistant[PERS_TEAM] );
cent = &cg_entities[cg.snap->ps.clientNum];
ps = &cg.snap->ps;
@@ -556,11 +622,25 @@ static void CG_DrawStatusBar( void ) {
origin[1] = 0;
origin[2] = 0;
angles[YAW] = 90 + 20 * sin( cg.time / 1000.0 );
- CG_Draw3DModel( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE,
+ CG_Draw3DModel( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 480-iconSize, iconSize, iconSize,
cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles );
}
- CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE );
+ if ( cg_drawStatusHead.integer == 2 ) {
+ origin[0] = 60;
+ origin[1] = 0;
+ origin[2] = -5;
+ angles[YAW] = ( cg.time & 2047 ) * 360 / 4096.0;
+ CG_DrawHealthModel( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 480-iconSize, iconSize, iconSize,
+ cg_items[ 6 /*item_health_large*/].models[0], 0, cg_items[ 6 /*item_health_large*/].models[1], origin, angles, 0 );
+
+ // if we didn't draw a 3D icon, draw a 2D icon for health
+ if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) {
+ CG_DrawPic( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 480 - iconSize, iconSize, iconSize, cg_items[6/*item_health_large*/].icon );
+ }
+ }
+ else if ( cg_drawStatusHead.integer == 1 )
+ CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE );
if( cg.predictedPlayerState.powerups[PW_REDFLAG] ) {
CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_RED );
@@ -575,7 +655,7 @@ static void CG_DrawStatusBar( void ) {
origin[1] = 0;
origin[2] = -10;
angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0;
- CG_Draw3DModel( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE,
+ CG_Draw3DModel( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 480 - iconSize, iconSize, iconSize,
cgs.media.armorModel, 0, origin, angles );
}
//
@@ -606,7 +686,7 @@ static void CG_DrawStatusBar( void ) {
icon = cg_weapons[ cg.predictedPlayerState.weapon ].ammoIcon;
if ( icon ) {
- CG_DrawPic( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, icon );
+ CG_DrawPic( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 480 - iconSize, iconSize, iconSize, icon );
}
}
}
@@ -643,7 +723,7 @@ static void CG_DrawStatusBar( void ) {
trap_R_SetColor( NULL );
// if we didn't draw a 3D icon, draw a 2D icon for armor
if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) {
- CG_DrawPic( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cgs.media.armorIcon );
+ CG_DrawPic( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 480 - iconSize, iconSize, iconSize, cgs.media.armorIcon );
}
}
@@ -982,6 +1062,8 @@ static void CG_DrawUpperRight(stereoFrame_t stereoFrame)
y = 0;
+ CG_SetScreenPlacement(PLACE_RIGHT, PLACE_TOP);
+
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 1 ) {
y = CG_DrawTeamOverlay( y, qtrue, qtrue );
}
@@ -1025,6 +1107,10 @@ static float CG_DrawScores( float y ) {
float y1;
gitem_t *item;
+ if ( !cg_drawScores.integer ) {
+ return y;
+ }
+
s1 = cgs.scores1;
s2 = cgs.scores2;
@@ -1276,6 +1362,8 @@ static void CG_DrawLowerRight( void ) {
y = 480 - ICON_SIZE;
+ CG_SetScreenPlacement(PLACE_RIGHT, PLACE_BOTTOM);
+
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2 ) {
y = CG_DrawTeamOverlay( y, qtrue, qfalse );
}
@@ -1294,12 +1382,21 @@ CG_DrawPickupItem
static int CG_DrawPickupItem( int y ) {
int value;
float *fadeColor;
+ float iconSize, charWidth, charHeight;
+
+ if ( cg_drawPickups.value <= 0 ) {
+ return y;
+ }
if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
return y;
}
- y -= ICON_SIZE;
+ iconSize = ICON_SIZE*cg_drawPickups.value;
+ charWidth = BIGCHAR_WIDTH*cg_drawPickups.value;
+ charHeight = BIGCHAR_HEIGHT*cg_drawPickups.value;
+
+ y -= iconSize;
value = cg.itemPickup;
if ( value ) {
@@ -1307,8 +1404,8 @@ static int CG_DrawPickupItem( int y ) {
if ( fadeColor ) {
CG_RegisterItemVisuals( value );
trap_R_SetColor( fadeColor );
- CG_DrawPic( 8, y, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
- CG_DrawBigString( ICON_SIZE + 16, y + (ICON_SIZE/2 - BIGCHAR_HEIGHT/2), bg_itemlist[ value ].pickup_name, fadeColor[0] );
+ CG_DrawPic( 8, y, iconSize, iconSize, cg_items[ value ].icon );
+ CG_DrawStringExt( iconSize + 16, y + (iconSize/2 - charHeight/2), bg_itemlist[ value ].pickup_name, fadeColor, qfalse, qtrue, charWidth, charHeight, 0 );
trap_R_SetColor( NULL );
}
}
@@ -1329,6 +1426,8 @@ static void CG_DrawLowerLeft( void ) {
y = 480 - ICON_SIZE;
+ CG_SetScreenPlacement(PLACE_LEFT, PLACE_BOTTOM);
+
if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 3 ) {
y = CG_DrawTeamOverlay( y, qfalse, qfalse );
}
@@ -1363,6 +1462,8 @@ static void CG_DrawTeamInfo( void ) {
if (chatHeight <= 0)
return; // disabled
+ CG_SetScreenPlacement( PLACE_LEFT, PLACE_BOTTOM );
+
if (cgs.teamLastChatPos != cgs.teamChatPos) {
if (cg.time - cgs.teamChatMsgTimes[cgs.teamLastChatPos % chatHeight] > cg_teamChatTime.integer) {
cgs.teamLastChatPos++;
@@ -1413,6 +1514,8 @@ CG_DrawHoldableItem
static void CG_DrawHoldableItem( void ) {
int value;
+ CG_SetScreenPlacement(PLACE_RIGHT, PLACE_CENTER);
+
value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM];
if ( value ) {
CG_RegisterItemVisuals( value );
@@ -1432,6 +1535,8 @@ CG_DrawPersistantPowerup
static void CG_DrawPersistantPowerup( void ) {
int value;
+ CG_SetScreenPlacement(PLACE_RIGHT, PLACE_CENTER);
+
value = cg.snap->ps.stats[STAT_PERSISTANT_POWERUP];
if ( value ) {
CG_RegisterItemVisuals( value );
@@ -1457,6 +1562,8 @@ static void CG_DrawReward( void ) {
return;
}
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
+
color = CG_FadeColor( cg.rewardTime, REWARD_TIME );
if ( !color ) {
if (cg.rewardStack > 0) {
@@ -1597,6 +1704,8 @@ static void CG_DrawDisconnect( void ) {
return;
}
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
+
// also add text in center of screen
s = "Connection Interrupted";
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
@@ -1607,6 +1716,8 @@ static void CG_DrawDisconnect( void ) {
return;
}
+ CG_SetScreenPlacement(PLACE_RIGHT, PLACE_BOTTOM);
+
x = 640 - 48;
y = 480 - 48;
@@ -1634,6 +1745,8 @@ static void CG_DrawLagometer( void ) {
return;
}
+ CG_SetScreenPlacement(PLACE_RIGHT, PLACE_BOTTOM);
+
//
// draw the graph
//
@@ -1791,6 +1904,8 @@ static void CG_DrawCenterString( void ) {
return;
}
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
+
trap_R_SetColor( color );
start = cg.centerPrint;
@@ -1872,6 +1987,8 @@ static void CG_DrawCrosshair(void)
return;
}
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
+
// set color based on health
if ( cg_crosshairHealth.integer ) {
vec4_t hcolor;
@@ -1894,7 +2011,6 @@ static void CG_DrawCrosshair(void)
x = cg_crosshairX.integer;
y = cg_crosshairY.integer;
- CG_AdjustFrom640( &x, &y, &w, &h );
ca = cg_drawCrosshair.integer;
if (ca < 0) {
@@ -1902,9 +2018,7 @@ static void CG_DrawCrosshair(void)
}
hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ];
- trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * (cg.refdef.width - w),
- y + cg.refdef.y + 0.5 * (cg.refdef.height - h),
- w, h, 0, 0, 1, 1, hShader );
+ CG_DrawPic( ((SCREEN_WIDTH-w)*0.5f)+x, ((SCREEN_HEIGHT-h)*0.5f)+y, w, h, hShader );
}
/*
@@ -2040,6 +2154,8 @@ static void CG_DrawCrosshairNames( void ) {
return;
}
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
+
// scan the known entities to see if the crosshair is sighted on one
CG_ScanForCrosshairEntity();
@@ -2071,6 +2187,7 @@ CG_DrawSpectator
=================
*/
static void CG_DrawSpectator(void) {
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_BOTTOM);
CG_DrawBigString(320 - 9 * 8, 440, "SPECTATOR", 1.0F);
if ( cgs.gametype == GT_TOURNAMENT ) {
CG_DrawBigString(320 - 15 * 8, 460, "waiting to play", 1.0F);
@@ -2093,6 +2210,8 @@ static void CG_DrawVote(void) {
return;
}
+ CG_SetScreenPlacement(PLACE_LEFT, PLACE_TOP);
+
// play a talk beep whenever it is modified
if ( cgs.voteModified ) {
cgs.voteModified = qfalse;
@@ -2134,6 +2253,8 @@ static void CG_DrawTeamVote(void) {
return;
}
+ CG_SetScreenPlacement(PLACE_LEFT, PLACE_TOP);
+
// play a talk beep whenever it is modified
if ( cgs.teamVoteModified[cs_offset] ) {
cgs.teamVoteModified[cs_offset] = qfalse;
@@ -2154,6 +2275,8 @@ static qboolean CG_DrawScoreboard( void ) {
#ifdef MISSIONPACK
static qboolean firstTime = qtrue;
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
+
if (menuScoreboard) {
menuScoreboard->window.flags &= ~WINDOW_FORCED;
}
@@ -2248,6 +2371,9 @@ static qboolean CG_DrawFollow( void ) {
if ( !(cg.snap->ps.pm_flags & PMF_FOLLOW) ) {
return qfalse;
}
+
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_TOP);
+
color[0] = 1;
color[1] = 1;
color[2] = 1;
@@ -2284,6 +2410,8 @@ static void CG_DrawAmmoWarning( void ) {
return;
}
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_TOP);
+
if ( cg.lowAmmoWarning == 2 ) {
s = "OUT OF AMMO";
} else {
@@ -2312,6 +2440,8 @@ static void CG_DrawProxWarning( void ) {
return;
}
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_TOP);
+
if (proxTime == 0) {
proxTime = cg.time + 5000;
proxCounter = 5;
@@ -2357,6 +2487,8 @@ static void CG_DrawWarmup( void ) {
return;
}
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_TOP);
+
if ( sec < 0 ) {
s = "Waiting for players";
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
@@ -2553,6 +2685,8 @@ static void CG_Draw2D(stereoFrame_t stereoFrame)
#ifdef MISSIONPACK
if ( cg_drawStatus.integer ) {
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_BOTTOM);
+
Menu_PaintAll();
CG_DrawTimedMenus();
}
diff --git a/code/cgame/cg_drawtools.c b/code/cgame/cg_drawtools.c
index c0ce1ef..868d290 100644
--- a/code/cgame/cg_drawtools.c
+++ b/code/cgame/cg_drawtools.c
@@ -23,6 +23,56 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// cg_drawtools.c -- helper functions called by cg_draw, cg_scoreboard, cg_info, etc
#include "cg_local.h"
+static screenPlacement_e cg_horizontalPlacement = PLACE_CENTER;
+static screenPlacement_e cg_verticalPlacement = PLACE_CENTER;
+static screenPlacement_e cg_lastHorizontalPlacement = PLACE_CENTER;
+static screenPlacement_e cg_lastVerticalPlacement = PLACE_CENTER;
+
+/*
+================
+CG_SetScreenPlacement
+================
+*/
+void CG_SetScreenPlacement(screenPlacement_e hpos, screenPlacement_e vpos)
+{
+ cg_lastHorizontalPlacement = cg_horizontalPlacement;
+ cg_lastVerticalPlacement = cg_verticalPlacement;
+
+ cg_horizontalPlacement = hpos;
+ cg_verticalPlacement = vpos;
+}
+
+/*
+================
+CG_PopScreenPlacement
+================
+*/
+void CG_PopScreenPlacement(void)
+{
+ cg_horizontalPlacement = cg_lastHorizontalPlacement;
+ cg_verticalPlacement = cg_lastVerticalPlacement;
+}
+
+/*
+================
+CG_GetScreenHorizontalPlacement
+================
+*/
+screenPlacement_e CG_GetScreenHorizontalPlacement(void)
+{
+ return cg_horizontalPlacement;
+}
+
+/*
+================
+CG_GetScreenVerticalPlacement
+================
+*/
+screenPlacement_e CG_GetScreenVerticalPlacement(void)
+{
+ return cg_verticalPlacement;
+}
+
/*
================
CG_AdjustFrom640
@@ -32,6 +82,7 @@ Adjusted for resolution and screen aspect ratio
*/
void CG_AdjustFrom640( float *x, float *y, float *w, float *h ) {
#if 0
+#if 0
// adjust for wide screens
if ( cgs.glconfig.vidWidth * 480 > cgs.glconfig.vidHeight * 640 ) {
*x += 0.5 * ( cgs.glconfig.vidWidth - ( cgs.glconfig.vidHeight * 640 / 480 ) );
@@ -42,6 +93,37 @@ void CG_AdjustFrom640( float *x, float *y, float *w, float *h ) {
*y *= cgs.screenYScale;
*w *= cgs.screenXScale;
*h *= cgs.screenYScale;
+#endif
+
+ if (cg_horizontalPlacement == PLACE_STRETCH || cg_stretch.integer) {
+ // scale for screen sizes (not aspect correct in wide screen)
+ *w *= cgs.screenXScaleStretch;
+ *x *= cgs.screenXScaleStretch;
+ } else {
+ // scale for screen sizes
+ *w *= cgs.screenXScale;
+ *x *= cgs.screenXScale;
+
+ if (cg_horizontalPlacement == PLACE_CENTER) {
+ *x += cgs.screenXBias;
+ } else if (cg_horizontalPlacement == PLACE_RIGHT) {
+ *x += cgs.screenXBias*2;
+ }
+ }
+
+ if (cg_verticalPlacement == PLACE_STRETCH || cg_stretch.integer) {
+ *h *= cgs.screenYScaleStretch;
+ *y *= cgs.screenYScaleStretch;
+ } else {
+ *h *= cgs.screenYScale;
+ *y *= cgs.screenYScale;
+
+ if (cg_verticalPlacement == PLACE_CENTER) {
+ *y += cgs.screenYBias;
+ } else if (cg_verticalPlacement == PLACE_BOTTOM) {
+ *y += cgs.screenYBias*2;
+ }
+ }
}
/*
@@ -604,7 +686,7 @@ static void UI_DrawBannerString2( int x, int y, const char* str, vec4_t color )
trap_R_SetColor( color );
ax = x * cgs.screenXScale + cgs.screenXBias;
- ay = y * cgs.screenYScale;
+ ay = y * cgs.screenYScale + cgs.screenYBias;
s = str;
while ( *s )
@@ -714,7 +796,7 @@ static void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t co
trap_R_SetColor( color );
ax = x * cgs.screenXScale + cgs.screenXBias;
- ay = y * cgs.screenYScale;
+ ay = y * cgs.screenYScale + cgs.screenYBias;
s = str;
while ( *s )
diff --git a/code/cgame/cg_info.c b/code/cgame/cg_info.c
index 1bf23ec..64e2ae4 100644
--- a/code/cgame/cg_info.c
+++ b/code/cgame/cg_info.c
@@ -164,7 +164,7 @@ void CG_DrawInformation( void ) {
levelshot = trap_R_RegisterShaderNoMip( "menu/art/unknownmap" );
}
trap_R_SetColor( NULL );
- CG_DrawPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, levelshot );
+ trap_R_DrawStretchPic( 0, 0, cgs.glconfig.vidWidth, cgs.glconfig.vidHeight, 0, 0, 1, 1, levelshot );
// blend a detail texture over it
detail = trap_R_RegisterShader( "levelShotDetail" );
diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h
index 4c4f6ea..2f6dd26 100644
--- a/code/cgame/cg_local.h
+++ b/code/cgame/cg_local.h
@@ -522,6 +522,7 @@ typedef struct {
// view rendering
refdef_t refdef;
vec3_t refdefViewAngles; // will be converted to refdef.viewaxis
+ float fov; // either range checked cg_fov or forced value
// zoom key
qboolean zoomed;
@@ -999,6 +1000,10 @@ typedef struct {
float screenXScale; // derived from glconfig
float screenYScale;
float screenXBias;
+ float screenYBias;
+ float screenXScaleStretch;
+ float screenYScaleStretch;
+
int serverCommandSequence; // reliable command stream counter
int processedSnapshotNum;// the number of snapshots cgame has requested
@@ -1106,6 +1111,14 @@ extern vmCvar_t cg_drawCrosshair;
extern vmCvar_t cg_drawCrosshairNames;
extern vmCvar_t cg_drawRewards;
extern vmCvar_t cg_drawTeamOverlay;
+extern vmCvar_t cg_drawScores;
+extern vmCvar_t cg_drawPickups;
+extern vmCvar_t cg_drawWeaponBar;
+extern vmCvar_t cg_drawStatusHead;
+extern vmCvar_t cg_statusScale;
+extern vmCvar_t cg_fovAspectAdjust;
+extern vmCvar_t cg_fovGunAdjust;
+extern vmCvar_t cg_stretch;
extern vmCvar_t cg_teamOverlayUserinfo;
extern vmCvar_t cg_crosshairX;
extern vmCvar_t cg_crosshairY;
@@ -1237,6 +1250,23 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
//
// cg_drawtools.c
//
+typedef enum {
+ PLACE_STRETCH,
+ PLACE_CENTER,
+
+ // horizontal only
+ PLACE_LEFT,
+ PLACE_RIGHT,
+
+ // vertical only
+ PLACE_TOP,
+ PLACE_BOTTOM
+} screenPlacement_e;
+
+void CG_SetScreenPlacement(screenPlacement_e hpos, screenPlacement_e vpos);
+void CG_PopScreenPlacement(void);
+screenPlacement_e CG_GetScreenHorizontalPlacement(void);
+screenPlacement_e CG_GetScreenVerticalPlacement(void);
void CG_AdjustFrom640( float *x, float *y, float *w, float *h );
void CG_FillRect( float x, float y, float width, float height, const float *color );
void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader );
diff --git a/code/cgame/cg_main.c b/code/cgame/cg_main.c
index 3b1f77b..c08d5ac 100644
--- a/code/cgame/cg_main.c
+++ b/code/cgame/cg_main.c
@@ -108,6 +108,14 @@ vmCvar_t cg_drawAmmoWarning;
vmCvar_t cg_drawCrosshair;
vmCvar_t cg_drawCrosshairNames;
vmCvar_t cg_drawRewards;
+vmCvar_t cg_drawScores;
+vmCvar_t cg_drawPickups;
+vmCvar_t cg_drawWeaponBar;
+vmCvar_t cg_drawStatusHead;
+vmCvar_t cg_statusScale;
+vmCvar_t cg_fovAspectAdjust;
+vmCvar_t cg_fovGunAdjust;
+vmCvar_t cg_stretch;
vmCvar_t cg_crosshairSize;
vmCvar_t cg_crosshairX;
vmCvar_t cg_crosshairY;
@@ -226,6 +234,14 @@ static cvarTable_t cvarTable[] = {
{ &cg_drawCrosshair, "cg_drawCrosshair", "4", CVAR_ARCHIVE },
{ &cg_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE },
{ &cg_drawRewards, "cg_drawRewards", "1", CVAR_ARCHIVE },
+ { &cg_drawScores, "cg_drawScores", "1", CVAR_ARCHIVE },
+ { &cg_drawPickups, "cg_drawPickups", "1", CVAR_ARCHIVE },
+ { &cg_drawWeaponBar, "cg_drawWeaponBar", "1", CVAR_ARCHIVE },
+ { &cg_drawStatusHead, "cg_drawStatusHead", "1", CVAR_ARCHIVE },
+ { &cg_statusScale, "cg_statusScale", "1", CVAR_ARCHIVE },
+ { &cg_fovAspectAdjust, "cg_fovAspectAdjust", "0", CVAR_ARCHIVE },
+ { &cg_fovGunAdjust, "cg_fovGunAdjust", "0", CVAR_ARCHIVE },
+ { &cg_stretch, "cg_stretch", "1", CVAR_ARCHIVE },
{ &cg_crosshairSize, "cg_crosshairSize", "24", CVAR_ARCHIVE },
{ &cg_crosshairHealth, "cg_crosshairHealth", "1", CVAR_ARCHIVE },
{ &cg_crosshairX, "cg_crosshairX", "0", CVAR_ARCHIVE },
@@ -1017,6 +1033,9 @@ static void CG_RegisterGraphics( void ) {
}
}
+ // can be used by HUD so always load it
+ CG_RegisterItemVisuals( 6 /* item_health_large */ );
+
// wall marks
cgs.media.bulletMarkShader = trap_R_RegisterShader( "gfx/damage/bullet_mrk" );
cgs.media.burnMarkShader = trap_R_RegisterShader( "gfx/damage/burn_med_mrk" );
@@ -1877,8 +1896,25 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) {
// get the rendering configuration from the client system
trap_GetGlconfig( &cgs.glconfig );
- cgs.screenXScale = cgs.glconfig.vidWidth / 640.0;
- cgs.screenYScale = cgs.glconfig.vidHeight / 480.0;
+ cgs.screenXScaleStretch = cgs.glconfig.vidWidth * (1.0/640.0);
+ cgs.screenYScaleStretch = cgs.glconfig.vidHeight * (1.0/480.0);
+ if ( cgs.glconfig.vidWidth * 480 > cgs.glconfig.vidHeight * 640 ) {
+ cgs.screenXScale = cgs.glconfig.vidWidth * (1.0/640.0);
+ cgs.screenYScale = cgs.glconfig.vidHeight * (1.0/480.0);
+ // wide screen
+ cgs.screenXBias = 0.5 * ( cgs.glconfig.vidWidth - ( cgs.glconfig.vidHeight * (640.0/480.0) ) );
+ cgs.screenXScale = cgs.screenYScale;
+ // no narrow screen
+ cgs.screenYBias = 0;
+ } else {
+ cgs.screenXScale = cgs.glconfig.vidWidth * (1.0/640.0);
+ cgs.screenYScale = cgs.glconfig.vidHeight * (1.0/480.0);
+ // narrow screen
+ cgs.screenYBias = 0.5 * ( cgs.glconfig.vidHeight - ( cgs.glconfig.vidWidth * (480.0/640.0) ) );
+ cgs.screenYScale = cgs.screenXScale;
+ // no wide screen
+ cgs.screenXBias = 0;
+ }
// get the gamestate from the client system
trap_GetGameState( &cgs.gameState );
diff --git a/code/cgame/cg_scoreboard.c b/code/cgame/cg_scoreboard.c
index 34935fe..04c7d5c 100644
--- a/code/cgame/cg_scoreboard.c
+++ b/code/cgame/cg_scoreboard.c
@@ -272,6 +272,8 @@ qboolean CG_DrawOldScoreboard( void ) {
int lineHeight;
int topBorderSize, bottomBorderSize;
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
+
// don't draw amuthing if the menu or console is up
if ( cg_paused.integer ) {
cg.deferredPlayerLoading = 0;
@@ -457,6 +459,8 @@ void CG_DrawOldTourneyScoreboard( void ) {
int y;
int i;
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_CENTER);
+
// request more scores regularly
if ( cg.scoresRequestTime + 2000 < cg.time ) {
cg.scoresRequestTime = cg.time;
@@ -466,7 +470,9 @@ void CG_DrawOldTourneyScoreboard( void ) {
// draw the dialog background
color[0] = color[1] = color[2] = 0;
color[3] = 1;
+ CG_SetScreenPlacement(PLACE_STRETCH, PLACE_STRETCH);
CG_FillRect( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, color );
+ CG_PopScreenPlacement();
color[0] = 1;
color[1] = 1;
diff --git a/code/cgame/cg_view.c b/code/cgame/cg_view.c
index 1131841..fe57aed 100644
--- a/code/cgame/cg_view.c
+++ b/code/cgame/cg_view.c
@@ -474,7 +474,7 @@ static int CG_CalcFov( void ) {
if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
// if in intermission, use a fixed value
- fov_x = 90;
+ cg.fov = fov_x = 90;
} else {
// user selectable
if ( cgs.dmflags & DF_FIXED_FOV ) {
@@ -489,6 +489,8 @@ static int CG_CalcFov( void ) {
}
}
+ cg.fov = fov_x;
+
// account for zooms
zoomFov = cg_zoomFov.value;
if ( zoomFov < 1 ) {
@@ -512,6 +514,16 @@ static int CG_CalcFov( void ) {
}
}
+ if ( cg_fovAspectAdjust.integer ) {
+ // Based on LordHavoc's code for Darkplaces
+ // http://www.quakeworld.nu/forum/topic/53/what-does-your-qw-look-like/page/30
+ const float baseAspect = 0.75f; // 3/4
+ const float aspect = (float)cg.refdef.width/(float)cg.refdef.height;
+ const float desiredFov = fov_x;
+
+ fov_x = atan2( tan( desiredFov*M_PI / 360.0f ) * baseAspect*aspect, 1 )*360.0f / M_PI;
+ }
+
x = cg.refdef.width / tan( fov_x / 360 * M_PI );
fov_y = atan2( cg.refdef.height, x );
fov_y = fov_y * 360 / M_PI;
diff --git a/code/cgame/cg_weapons.c b/code/cgame/cg_weapons.c
index a2b8325..5e12e99 100644
--- a/code/cgame/cg_weapons.c
+++ b/code/cgame/cg_weapons.c
@@ -1360,7 +1360,7 @@ void CG_AddViewWeapon( playerState_t *ps ) {
refEntity_t hand;
centity_t *cent;
clientInfo_t *ci;
- float fovOffset;
+ vec3_t fovOffset;
vec3_t angles;
weaponInfo_t *weapon;
@@ -1397,11 +1397,19 @@ void CG_AddViewWeapon( playerState_t *ps ) {
return;
}
- // drop gun lower at higher fov
- if ( cg_fov.integer > 90 ) {
- fovOffset = -0.2 * ( cg_fov.integer - 90 );
- } else {
- fovOffset = 0;
+ VectorClear(fovOffset);
+
+ if ( cg_fovGunAdjust.integer ) {
+ if ( cg.fov > 90 ) {
+ // drop gun lower at higher fov
+ fovOffset[2] = -0.2 * ( cg.fov - 90 ) * cg.refdef.fov_x / cg.fov;
+ } else if ( cg.fov < 90 ) {
+ // move gun forward at lowerer fov
+ fovOffset[0] = -0.2 * ( cg.fov - 90 ) * cg.refdef.fov_x / cg.fov;
+ }
+ } else if ( cg_fov.integer > 90 ) {
+ // Q3A's auto adjust
+ fovOffset[2] = -0.2 * ( cg_fov.integer - 90 );
}
cent = &cg.predictedPlayerEntity; // &cg_entities[cg.snap->ps.clientNum];
@@ -1413,9 +1421,9 @@ void CG_AddViewWeapon( playerState_t *ps ) {
// set up gun position
CG_CalculateWeaponPosition( hand.origin, angles );
- VectorMA( hand.origin, cg_gun_x.value, cg.refdef.viewaxis[0], hand.origin );
- VectorMA( hand.origin, cg_gun_y.value, cg.refdef.viewaxis[1], hand.origin );
- VectorMA( hand.origin, (cg_gun_z.value+fovOffset), cg.refdef.viewaxis[2], hand.origin );
+ VectorMA( hand.origin, (cg_gun_x.value+fovOffset[0]), cg.refdef.viewaxis[0], hand.origin );
+ VectorMA( hand.origin, (cg_gun_y.value+fovOffset[1]), cg.refdef.viewaxis[1], hand.origin );
+ VectorMA( hand.origin, (cg_gun_z.value+fovOffset[2]), cg.refdef.viewaxis[2], hand.origin );
AnglesToAxis( angles, hand.axis );
@@ -1459,6 +1467,13 @@ void CG_DrawWeaponSelect( void ) {
int x, y, w;
char *name;
float *color;
+ float markerSize, iconSize, offsetSize, charWidth, charHeight;
+
+ CG_SetScreenPlacement(PLACE_CENTER, PLACE_BOTTOM);
+
+ if ( cg_drawWeaponBar.value <= 0 ) {
+ return;
+ }
// don't display if dead
if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
@@ -1483,9 +1498,16 @@ void CG_DrawWeaponSelect( void ) {
}
}
- x = 320 - count * 20;
+ x = 320 - count * 20 * cg_drawWeaponBar.value;
y = 380;
+ markerSize = 40 * cg_drawWeaponBar.value;
+ iconSize = 32 * cg_drawWeaponBar.value;
+ offsetSize = (markerSize - iconSize) * 0.5f;
+
+ charWidth = BIGCHAR_WIDTH * cg_drawWeaponBar.value;
+ charHeight = BIGCHAR_HEIGHT * cg_drawWeaponBar.value;
+
for ( i = 1 ; i < MAX_WEAPONS ; i++ ) {
if ( !( bits & ( 1 << i ) ) ) {
continue;
@@ -1494,28 +1516,28 @@ void CG_DrawWeaponSelect( void ) {
CG_RegisterWeapon( i );
// draw weapon icon
- CG_DrawPic( x, y, 32, 32, cg_weapons[i].weaponIcon );
+ CG_DrawPic( x, y, iconSize, iconSize, cg_weapons[i].weaponIcon );
// draw selection marker
if ( i == cg.weaponSelect ) {
- CG_DrawPic( x-4, y-4, 40, 40, cgs.media.selectShader );
+ CG_DrawPic( x-offsetSize, y-offsetSize, markerSize, markerSize, cgs.media.selectShader );
}
// no ammo cross on top
if ( !cg.snap->ps.ammo[ i ] ) {
- CG_DrawPic( x, y, 32, 32, cgs.media.noammoShader );
+ CG_DrawPic( x, y, iconSize, iconSize, cgs.media.noammoShader );
}
- x += 40;
+ x += markerSize;
}
// draw the selected name
if ( cg_weapons[ cg.weaponSelect ].item ) {
name = cg_weapons[ cg.weaponSelect ].item->pickup_name;
if ( name ) {
- w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH;
+ w = CG_DrawStrlen( name ) * charWidth;
x = ( SCREEN_WIDTH - w ) / 2;
- CG_DrawBigStringColor(x, y - 22, name, color);
+ CG_DrawStringExt(x, y - 22*cg_drawWeaponBar.value, name, color, qfalse, qtrue, charWidth, charHeight, 0 );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment