Files like health.cpp, battery.cpp etc. implement their own HUD elements with exact coordinates in them.
I'll give you a bit of a detailed explanation so you mechanically know what's going on, if that makes sense.
For example,
battery.cpp
starts with:
//
// battery.cpp
//
// implementation of CHudBattery class
//
Implementation of
CHudBattery
huh... what's that?
It is defined in
hud.h
:
class CHudBattery : public CHudBase
{
public:
bool Init() override;
bool VidInit() override;
bool Draw(float flTime) override;
bool MsgFunc_Battery(const char* pszName, int iSize, void* pbuf);
private:
HSPRITE m_hSprite1;
HSPRITE m_hSprite2;
Rect* m_prc1;
Rect* m_prc2;
int m_iBat;
int m_iBatMax;
float m_fFade;
int m_iHeight; // width of the battery innards
};
Each of these HUD elements are C++ classes, which inherit from
CHudBase
. The HUD system contains a collection of these
CHudBase
based objects.
Now, here's the thing. When the HUD wants to actually draw itself onto the screen, it visits each of these elements and asks them to render themselves. This is what the
Draw
functions are for. If you want to find what coordinates are used, you would simply find, for instance,
CHudBattery::Draw
, like so:
bool CHudBattery::Draw(float flTime)
{
if ((gHUD.m_iHideHUDDisplay & HIDEHUD_HEALTH) != 0)
return true;
int r, g, b, x, y, a;
Rect rc;
rc = *m_prc2;
rc.top += m_iHeight * ((float)(100 - (V_min(100, m_iBat))) * 0.01); // battery can go from 0 to 100 so * 0.01 goes from 0 to 1
UnpackRGB(r, g, b, RGB_YELLOWISH);
if (!gHUD.HasSuit())
return true;
... // rest of the code
Scrolling down in the same function, we see this code:
y = ScreenHeight - gHUD.m_iFontHeight - gHUD.m_iFontHeight / 2;
x = ScreenWidth / 4;
// make sure we have the right sprite handles
if (0 == m_hSprite1)
m_hSprite1 = gHUD.GetSprite(gHUD.GetSpriteIndex("suit_empty"));
if (0 == m_hSprite2)
m_hSprite2 = gHUD.GetSprite(gHUD.GetSpriteIndex("suit_full"));
SPR_Set(m_hSprite1, r, g, b);
SPR_DrawAdditive(0, x, y - iOffset, m_prc1);
if (rc.bottom > rc.top)
{
SPR_Set(m_hSprite2, r, g, b);
SPR_DrawAdditive(0, x, y - iOffset + (rc.top - m_prc2->top), &rc);
}
This says "Hey GoldSRC, please render a sprite at these coordinates here".
SPR_Set
tells the engine to use a certain sprite, and
SPR_DrawAdditive
tells the engine to draw it. So, now you might be wondering what's up with these
x
and
y
variable things. You can declare your own temporary variables to essentially "chain" HUD elements one after another, so you don't have to manually specify every single coordinate yourself. You can use it to say "the next one will be 5 pixels more" etc.
Now if you actually know C++ and stuff, woops! I tend to assume people don't know, and this may be useful to people in the future anyway, so yeah.
So, generally, it boils down to maths. You just gotta read the code really well to see where the coordinates are defined, and you'll be fine. You can change it to whatever you want, whether it's moving something from the bottom-left corner into a top-right corner, or make them bounce up'n'down through time and anything you can imagine.