Skip to content

Commit

Permalink
Client: Add HUD sprite scaling support
Browse files Browse the repository at this point in the history
  • Loading branch information
tmp64 committed Nov 25, 2023
1 parent 4df9d89 commit 16688b0
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 5 deletions.
9 changes: 9 additions & 0 deletions src/common/convar.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef CONVAR_H
#define CONVAR_H
#include <cvardef.h>
#include <MinMax.h>

enum class ConItemType
{
Expand Down Expand Up @@ -52,6 +53,14 @@ class ConVar : public ConItemBase
bool GetBool();
const char *GetString();

template <typename T>
T GetEnumClamped()
{
int minValue = (int)T::_Min;
int maxValue = (int)T::_Max;
return (T)clamp(GetInt(), minValue, maxValue);
}

void SetValue(float val);
void SetValue(int val);
void SetValue(const char *val);
Expand Down
112 changes: 108 additions & 4 deletions src/game/client/hud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <cstring>
#include <cstdio>
#include <FileSystem.h>
#include <vgui/IScheme.h>
#include <vgui_controls/AnimationController.h>
#include <vgui_controls/Controls.h>
Expand All @@ -42,6 +43,7 @@
#include "bhlcfg.h"
#include "results.h"
#include "svc_messages.h"
#include "sdl_rt.h"

#if USE_UPDATER
#include "updater/update_checker.h"
Expand Down Expand Up @@ -87,6 +89,35 @@
#include "hud/ag/ag_timeout.h"
#include "hud/ag/ag_vote.h"

struct HudScaleInfo
{
//! The sprite resolution.
int iRes = 0;

//! The minimum height for this scale to be selected automatically.
int iHeight = 0;

//! Enum value.
EHudScale nScale = EHudScale::Auto;

//! The file to test for support.
const char *szTestFile = nullptr;

//! @returns Whether this scale is supported by the given max scale.
bool IsSupported(EHudScale maxScale) const
{
return nScale <= maxScale;
}
};

//! The list of allowed HUD sizes.
static constexpr HudScaleInfo HUD_SCALE_INFO[] = {
HudScaleInfo { 320, 240, EHudScale ::X05, "sprites/320hud1.spr" },
HudScaleInfo { 640, 480, EHudScale ::X1, "sprites/640hud1.spr" },
HudScaleInfo { 1280, 960, EHudScale ::X2, "sprites/1280/hud_bucket0.spr" },
HudScaleInfo { 2560, 1920, EHudScale ::X4, "sprites/2560/hud_bucket0.spr" },
};

extern cvar_t *cl_lw;

ConVar cl_bhopcap("cl_bhopcap", "2", FCVAR_BHL_ARCHIVE, "Enables/disables bhop speed cap, '2' - detect automatically");
Expand All @@ -96,6 +127,7 @@ ConVar hud_color2("hud_color2", "255 160 0", FCVAR_BHL_ARCHIVE, "HUD color when
ConVar hud_color3("hud_color3", "255 96 0", FCVAR_BHL_ARCHIVE, "HUD color when (25%; 50%)");
ConVar hud_draw("hud_draw", "1", FCVAR_ARCHIVE, "Opacity of the HUD");
ConVar hud_dim("hud_dim", "1", FCVAR_BHL_ARCHIVE, "Dim inactive HUD elements");
ConVar hud_scale("hud_scale", "0", FCVAR_BHL_ARCHIVE, "HUD Scale: Auto, 50%, 100%, 200%, 400% (restart required)");
ConVar hud_capturemouse("hud_capturemouse", "1", FCVAR_ARCHIVE);
ConVar hud_classautokill("hud_classautokill", "1", FCVAR_ARCHIVE | FCVAR_USERINFO, "Whether or not to suicide immediately on TF class switch");
ConVar cl_autowepswitch("cl_autowepswitch", "1", FCVAR_BHL_ARCHIVE | FCVAR_USERINFO, "Controls autoswitching to best weapon on pickup\n 0 - never, 1 - always, 2 - unless firing");
Expand Down Expand Up @@ -170,6 +202,49 @@ static void AboutCommand(void)
ConPrintf("Discussion forum: " BHL_FORUM_URL "\n");
}

//! Gets the current HUD size (either user-selected or auto-detected).
//! @returns iRes.
static int GetHudSize(const SCREENINFO &screenInfo, EHudScale maxScale)
{
EHudScale userScale = hud_scale.GetEnumClamped<EHudScale>();

if (userScale != EHudScale::Auto)
{
// Use user override
userScale = clamp(userScale, EHudScale::X05, maxScale);
const HudScaleInfo &info = *std::find_if(std::begin(HUD_SCALE_INFO), std::end(HUD_SCALE_INFO), [&](const HudScaleInfo &x)
{ return x.nScale == userScale; });

gEngfuncs.Con_DPrintf("HUD Size Override: %dx%d\n", info.iRes, info.iHeight);
return info.iRes;
}

// Auto-detect
for (auto it = std::rbegin(HUD_SCALE_INFO); it != std::rend(HUD_SCALE_INFO); ++it)
{
if (!it->IsSupported(maxScale))
continue;

if (screenInfo.iHeight >= it->iHeight)
{
// Found the largest one.
gEngfuncs.Con_DPrintf(
"HUD Size Auto-detect: %dx%d for screen %dx%d\n",
it->iRes, it->iHeight,
screenInfo.iWidth, screenInfo.iHeight);
return it->iRes;
}
}

// Too low resolution. Fall back to the smallest one.
const HudScaleInfo &fallbackInfo = HUD_SCALE_INFO[0];
gEngfuncs.Con_DPrintf(
"HUD Size Auto-detect: fallback %dx%d for too small screen %dx%d\n",
fallbackInfo.iRes, fallbackInfo.iHeight,
screenInfo.iWidth, screenInfo.iHeight);
return fallbackInfo.iRes;
}

CHud::CHud()
{
}
Expand All @@ -195,6 +270,8 @@ void CHud::Init(void)
m_bIsAg = !strcmp(gEngfuncs.pfnGetGameDirectory(), "ag");
PM_SetIsAG(m_bIsAg);

m_MaxHudScale = DetectMaxHudScale();

HookHudMessage<&CHud::MsgFunc_Logo>("Logo");
HookHudMessage<&CHud::MsgFunc_ResetHUD>("ResetHUD");
HookHudMessage<&CHud::MsgFunc_GameMode>("GameMode");
Expand Down Expand Up @@ -353,10 +430,9 @@ void CHud::VidInit(void)

m_hsprLogo = 0;

if (ScreenWidth < 640)
m_iRes = 320;
else
m_iRes = 640;
// Only update the scale once - otherwise sprites break
if (m_iRes == -1)
m_iRes = GetHudSize(m_scrinfo, GetMaxHudScale());

// Only load this once
if (!m_pSpriteList)
Expand Down Expand Up @@ -732,6 +808,34 @@ void CHud::UpdateSupportsCvar()
gEngfuncs.pfnClientCmd(buf);
}

EHudScale CHud::DetectMaxHudScale()
{
const HudScaleInfo *pMaxScaleInfo = nullptr;

for (const HudScaleInfo& i : HUD_SCALE_INFO)
{
if (g_pFullFileSystem->FileExists(i.szTestFile))
{
pMaxScaleInfo = &i;
}
else
{
// If i is not supported, then i + 1 isn't supported as well.
// Limited by the use of "x <= maxScale" check.
break;
}
}

if (!pMaxScaleInfo)
{
GetSDL()->ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "BugfixedHL Error", "HUD sprites are missing. Verify game files.");
std::abort();
}

gEngfuncs.Con_DPrintf("Maximum HUD scale: %dx%d\n", pMaxScaleInfo->iRes, pMaxScaleInfo->iHeight);
return pMaxScaleInfo->nScale;
}

CON_COMMAND(append, "Puts a command into the end of the command buffer")
{
if (gEngfuncs.Cmd_Argc() != 2)
Expand Down
35 changes: 34 additions & 1 deletion src/game/client/hud.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,27 @@ enum class ColorCodeAction
Strip = 2, //!< Color codes don't change the color but are removed from the string.
};

enum class EHudScale
{
//! Detect automatically based on resolution.
Auto,

//! 50% scale (320_x.spr).
X05,

//! 100% scale (640_x.spr).
X1,

//! 200% scale (1280_x.spr).
X2,

//! 400% scale (2560_x.spr).
X4,

_Min = Auto,
_Max = X4,
};

struct NoTeamColor
{
static const Color Orange;
Expand All @@ -98,7 +119,7 @@ class CHud
int m_iHideHUDDisplay;
int m_iFOV;
int m_Teamplay;
int m_iRes;
int m_iRes = -1;
int m_iFontHeight;
int m_iWeaponBits;
int m_fPlayerDead;
Expand Down Expand Up @@ -226,6 +247,12 @@ class CHud
//! @returns The engine build number.
int GetEngineBuild() const { return m_iEngineBuildNumber; }

//! @returns Maximum supported HUD scale by the game version.
EHudScale GetMaxHudScale() const { return m_MaxHudScale; }

//! @returns Currently loaded sprite size. Returns Auto if not loaded.
EHudScale GetCurrentHudScale() const { return m_CurrentHudScale; }

private:
struct SpriteName
{
Expand All @@ -244,6 +271,9 @@ class CHud
char m_szEngineVersion[128];
int m_iEngineBuildNumber = 0;

EHudScale m_MaxHudScale = EHudScale::Auto; //! Maximum supported HUD scale by the game version.
EHudScale m_CurrentHudScale = EHudScale::Auto; //! Currently loaded sprite size.

// the memory for these arrays are allocated in the first call
// to CHud::VidInit(), when the hud.txt and associated sprites are loaded.
std::vector<HSPRITE> m_rghSprites; /*[HUD_SPRITE_COUNT]*/ // the sprites loaded from hud.txt
Expand All @@ -267,6 +297,9 @@ class CHud
void UpdateHudColors();
void UpdateSupportsCvar();

//! Detects the maximum supported HUD scale.
EHudScale DetectMaxHudScale();

template <typename T>
inline T *RegisterHudElem()
{
Expand Down

0 comments on commit 16688b0

Please sign in to comment.