Skip to content

Commit 08ffd17

Browse files
committed
Add hud system with HTML formatting & priority handling
To avoid conflicting with env_hudhint usage in maps, CS2Fixes will now use this instead of HUD_PRINTCENTER
1 parent 18b06c6 commit 08ffd17

11 files changed

Lines changed: 234 additions & 6 deletions

File tree

AMBuilder

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ for sdk_target in MMSPlugin.sdk_targets:
4848
'src/events.cpp',
4949
'src/utils/entity.cpp',
5050
'src/utils/weapon.cpp',
51+
'src/utils/hud_manager.cpp',
5152
'src/cs2_sdk/entity/services.cpp',
5253
'src/cs2_sdk/entity/ccsplayerpawn.cpp',
5354
'src/cs2_sdk/entity/cbasemodelentity.cpp',

CS2Fixes.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@
217217
<ClCompile Include="src\utils\plat_unix.cpp" />
218218
<ClCompile Include="src\utils\plat_win.cpp" />
219219
<ClCompile Include="src\utils\weapon.cpp" />
220+
<ClCompile Include="src\utils\hud_manager.cpp" />
220221
<ClCompile Include="src\cs2_sdk\entity\services.cpp" />
221222
<ClCompile Include="src\cs2_sdk\entity\ccsplayerpawn.cpp" />
222223
<ClCompile Include="src\cs2_sdk\entity\cbasemodelentity.cpp" />
@@ -294,6 +295,7 @@
294295
<ClInclude Include="src\netmessages.h" />
295296
<ClInclude Include="src\utils\weapon.h" />
296297
<ClInclude Include="src\utils\version_gen_placeholder.h" />
298+
<ClInclude Include="src\utils\hud_manager.h" />
297299
</ItemGroup>
298300
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
299301
<ImportGroup Label="ExtensionTargets" />

CS2Fixes.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@
182182
<ClCompile Include="src\utils\weapon.cpp">
183183
<Filter>Source Files\utils</Filter>
184184
</ClCompile>
185+
<ClCompile Include="src\utils\hud_manager.cpp">
186+
<Filter>Source Files\utils</Filter>
187+
</ClCompile>
185188
<ClCompile Include="src\cs2_sdk\entity\services.cpp">
186189
<Filter>Source Files\cs2_sdk\entity</Filter>
187190
</ClCompile>
@@ -409,5 +412,8 @@
409412
<ClInclude Include="src\utils\version_gen_placeholder.h">
410413
<Filter>Header Files\utils</Filter>
411414
</ClInclude>
415+
<ClInclude Include="src\utils\hud_manager.h">
416+
<Filter>Header Files\utils</Filter>
417+
</ClInclude>
412418
</ItemGroup>
413419
</Project>

cfg/cs2fixes/cs2fixes.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ cs2f_prevent_using_players 0 // Whether to prevent +use from hitting players (
2626
cs2f_map_steamids_enable 0 // Whether to make Steam ID's available to maps
2727
cs2f_fix_game_bans 0 // Whether to fix CS2 game bans spreading to all new joining players
2828
cs2f_free_armor 0 // Whether kevlar (1+) and/or helmet (2) are given automatically
29+
cs2f_fix_hud_flashing 0 // Whether to fix hud flashing using a workaround, this BREAKS warmup so pick one or the other
2930
3031
cs2f_beacon_particle "particles/cs2fixes/admin_beacon.vpcf" // .vpcf file to be precached and used for player beacon
3132

src/adminsystem.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "entwatch.h"
3030
#include "filesystem.h"
3131
#include "gamesystem.h"
32+
#include "hud_manager.h"
3233
#include "icvar.h"
3334
#include "interfaces/interfaces.h"
3435
#include "map_votes.h"
@@ -560,7 +561,7 @@ CON_COMMAND_CHAT_FLAGS(hsay, "<message> - Say something as a hud hint", ADMFLAG_
560561
return;
561562
}
562563

563-
ClientPrintAll(HUD_PRINTCENTER, "%s", args.ArgS());
564+
SendHudMessageAll(10, 99, "<span class='fontSize-l'><span color='#FFFFFF'>ADMIN: </span><span color='#D11313'>%s</span></span>", args.ArgS());
564565
}
565566

566567
CON_COMMAND_CHAT_FLAGS(rcon, "<command> - Send a command to server console", ADMFLAG_RCON)

src/cs2_sdk/entity/cgamerules.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ class CCSGameRules : public CGameRules
7373
SCHEMA_FIELD_POINTER(CUtlVector<SpawnPoint*>, m_TerroristSpawnPoints)
7474
SCHEMA_FIELD(int, m_iMaxNumTerrorists)
7575
SCHEMA_FIELD(int, m_iMaxNumCTs)
76+
SCHEMA_FIELD(bool, m_bGameRestart)
77+
SCHEMA_FIELD(bool, m_bWarmupPeriod)
7678

7779
void TerminateRound(float flDelay, CSRoundEndReason reason)
7880
{

src/cs2fixes.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "gameevents.pb.h"
4242
#include "gamesystem.h"
4343
#include "httpmanager.h"
44+
#include "hud_manager.h"
4445
#include "icvar.h"
4546
#include "idlemanager.h"
4647
#include "interface.h"
@@ -1315,6 +1316,8 @@ void CS2Fixes::OnLevelInit(char const* pMapName,
13151316

13161317
if (g_cvarEnableEntWatch.Get())
13171318
EW_OnLevelInit(pMapName);
1319+
1320+
StartFlashingFixTimer();
13181321
}
13191322

13201323
void CS2Fixes::OnLevelShutdown()

src/events.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "entity/cgamerules.h"
2727
#include "entwatch.h"
2828
#include "eventlistener.h"
29+
#include "hud_manager.h"
2930
#include "idlemanager.h"
3031
#include "leader.h"
3132
#include "map_votes.h"
@@ -257,6 +258,10 @@ GAME_EVENT_F(round_start)
257258
if (g_cvarFullAllTalk.Get())
258259
g_pEngineServer2->ServerCommand("sv_full_alltalk 1");
259260

261+
// Ensure there's no warmup, because mp_warmup_online_enabled gets randomly ignored for some reason, this is a problem with cs2f_fix_hud_flashing
262+
if (g_cvarFixHudFlashing.Get() && g_pGameRules && g_pGameRules->m_bWarmupPeriod)
263+
g_pEngineServer2->ServerCommand("mp_warmup_end");
264+
260265
if (!g_cvarEnableTopDefender.Get() || !GetGlobals())
261266
return;
262267

@@ -275,6 +280,9 @@ GAME_EVENT_F(round_start)
275280

276281
GAME_EVENT_F(round_end)
277282
{
283+
if (g_cvarFixHudFlashing.Get() && g_pGameRules)
284+
g_pGameRules->m_bGameRestart = false;
285+
278286
if (!g_cvarEnableTopDefender.Get() || !GetGlobals())
279287
return;
280288

src/utils/hud_manager.cpp

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/**
2+
* =============================================================================
3+
* CS2Fixes
4+
* Copyright (C) 2023-2025 Source2ZE
5+
* =============================================================================
6+
*
7+
* This program is free software; you can redistribute it and/or modify it under
8+
* the terms of the GNU General Public License, version 3.0, as published by the
9+
* Free Software Foundation.
10+
*
11+
* This program is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14+
* details.
15+
*
16+
* You should have received a copy of the GNU General Public License along with
17+
* this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include "hud_manager.h"
21+
#include "../cs2fixes.h"
22+
#include "../ctimer.h"
23+
#include "../recipientfilters.h"
24+
#include "engine/igameeventsystem.h"
25+
#include "entity/cgamerules.h"
26+
#include "gameevents.pb.h"
27+
#include "networksystem/inetworkmessages.h"
28+
29+
extern CCSGameRules* g_pGameRules;
30+
extern IGameEventManager2* g_gameEventManager;
31+
extern IGameEventSystem* g_gameEventSystem;
32+
extern CGlobalVars* GetGlobals();
33+
34+
CConVar<bool> g_cvarFixHudFlashing("cs2f_fix_hud_flashing", FCVAR_NONE, "Whether to fix hud flashing using a workaround, this BREAKS warmup so pick one or the other", false);
35+
static std::vector<std::shared_ptr<CHudMessage>> g_vecHudMessages;
36+
37+
bool ShouldDisplayForPlayer(ZEPlayerHandle hPlayer, int iPriority)
38+
{
39+
for (std::shared_ptr<CHudMessage> pHudMessage : g_vecHudMessages)
40+
{
41+
// Require higher priority to block, so if priority matches most recent message takes precedence
42+
if (pHudMessage->HasRecipient(hPlayer) && pHudMessage->GetPriority() > iPriority)
43+
return false;
44+
}
45+
46+
return true;
47+
}
48+
49+
void CreateHudMessage(std::shared_ptr<CHudMessage> pHudMessage)
50+
{
51+
IGameEvent* pEvent = g_gameEventManager->CreateEvent("show_survival_respawn_status");
52+
53+
if (!pEvent)
54+
return;
55+
56+
pEvent->SetString("loc_token", pHudMessage->GetMessage());
57+
pEvent->SetInt("duration", pHudMessage->GetDuration());
58+
pEvent->SetInt("userid", -1);
59+
60+
INetworkMessageInternal* pMsg = g_pNetworkMessages->FindNetworkMessageById(GE_Source1LegacyGameEvent);
61+
62+
if (!pMsg)
63+
{
64+
g_gameEventManager->FreeEvent(pEvent);
65+
return;
66+
}
67+
68+
CNetMessagePB<CMsgSource1LegacyGameEvent>* data = pMsg->AllocateMessage()->ToPB<CMsgSource1LegacyGameEvent>();
69+
CRecipientFilter filter;
70+
71+
for (ZEPlayerHandle hPlayer : pHudMessage->GetRecipients())
72+
if (ShouldDisplayForPlayer(hPlayer, pHudMessage->GetPriority()))
73+
filter.AddRecipient(hPlayer.Get()->GetPlayerSlot());
74+
75+
// Store the new hud message
76+
g_vecHudMessages.push_back(pHudMessage);
77+
78+
// Start a timer to remove this hud message after its duration passes
79+
new CTimer(pHudMessage->GetDuration(), true, true, [pHudMessage]() {
80+
g_vecHudMessages.erase(std::remove(g_vecHudMessages.begin(), g_vecHudMessages.end(), pHudMessage), g_vecHudMessages.end());
81+
82+
return -1.0f;
83+
});
84+
85+
g_gameEventManager->SerializeEvent(pEvent, data);
86+
g_gameEventSystem->PostEventAbstract(-1, false, &filter, pMsg, data, 0);
87+
delete data;
88+
g_gameEventManager->FreeEvent(pEvent);
89+
}
90+
91+
void SendHudMessage(ZEPlayer* pPlayer, int iDuration, int iPriority, const char* pszMessage, ...)
92+
{
93+
va_list args;
94+
va_start(args, pszMessage);
95+
96+
char buf[1024];
97+
V_vsnprintf(buf, sizeof(buf), pszMessage, args);
98+
99+
va_end(args);
100+
101+
std::shared_ptr<CHudMessage> pHudMessage = std::make_shared<CHudMessage>(buf, iDuration, iPriority);
102+
103+
pHudMessage->AddRecipient(pPlayer->GetHandle());
104+
CreateHudMessage(pHudMessage);
105+
}
106+
107+
void SendHudMessageAll(int iDuration, int iPriority, const char* pszMessage, ...)
108+
{
109+
if (!GetGlobals())
110+
return;
111+
112+
va_list args;
113+
va_start(args, pszMessage);
114+
115+
char buf[1024];
116+
V_vsnprintf(buf, sizeof(buf), pszMessage, args);
117+
118+
va_end(args);
119+
120+
std::shared_ptr<CHudMessage> pHudMessage = std::make_shared<CHudMessage>(buf, iDuration, iPriority);
121+
122+
for (int i = 0; i < GetGlobals()->maxClients; i++)
123+
{
124+
ZEPlayer* pPlayer = g_playerManager->GetPlayer(i);
125+
126+
if (pPlayer)
127+
pHudMessage->AddRecipient(pPlayer->GetHandle());
128+
}
129+
130+
CreateHudMessage(pHudMessage);
131+
}
132+
133+
void StartFlashingFixTimer()
134+
{
135+
// Timer that fakes m_bGameRestart enabled, to fix flashing with show_survival_respawn_status
136+
new CTimer(0.5f, false, true, []() {
137+
if (!g_cvarFixHudFlashing.Get() || !g_pGameRules)
138+
return 0.5f;
139+
140+
// Faking m_bGameRestart as true close to a round ending causes UI to falsely show game is restarting
141+
if (g_pGameRules->m_flRestartRoundTime.Get().GetTime() == 0.0f)
142+
g_pGameRules->m_bGameRestart = true;
143+
else
144+
g_pGameRules->m_bGameRestart = false;
145+
146+
return 0.5f;
147+
});
148+
}

src/utils/hud_manager.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* =============================================================================
3+
* CS2Fixes
4+
* Copyright (C) 2023-2025 Source2ZE
5+
* =============================================================================
6+
*
7+
* This program is free software; you can redistribute it and/or modify it under
8+
* the terms of the GNU General Public License, version 3.0, as published by the
9+
* Free Software Foundation.
10+
*
11+
* This program is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14+
* details.
15+
*
16+
* You should have received a copy of the GNU General Public License along with
17+
* this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#pragma once
21+
22+
#include "../playermanager.h"
23+
#include <string>
24+
#include <vector>
25+
26+
class CHudMessage
27+
{
28+
public:
29+
CHudMessage(std::string sMessage, int iDuration, int iPriority)
30+
{
31+
m_strMessage = sMessage;
32+
m_iDuration = iDuration;
33+
m_iPriority = iPriority;
34+
}
35+
36+
const char* GetMessage() { return m_strMessage.c_str(); };
37+
int GetDuration() { return m_iDuration; };
38+
int GetPriority() { return m_iPriority; };
39+
std::vector<ZEPlayerHandle> GetRecipients() { return m_vecRecipients; };
40+
bool HasRecipient(ZEPlayerHandle hPlayer) { return std::find(m_vecRecipients.begin(), m_vecRecipients.end(), hPlayer) != m_vecRecipients.end(); };
41+
void AddRecipient(ZEPlayerHandle hPlayer) { m_vecRecipients.push_back(hPlayer); };
42+
43+
private:
44+
std::string m_strMessage;
45+
int m_iDuration;
46+
int m_iPriority;
47+
std::vector<ZEPlayerHandle> m_vecRecipients;
48+
};
49+
50+
// When multiple hud messages are active, whichever one has highest priority one will display
51+
// Note this is a basic implementation (TODO: is this worth expanding?), so e.g. a previously sent lower priority message will not display once a higher priority one expires
52+
void SendHudMessage(ZEPlayer* pPlayer, int iDuration, int iPriority, const char* pszMessage, ...);
53+
void SendHudMessageAll(int iDuration, int iPriority, const char* pszMessage, ...);
54+
55+
void StartFlashingFixTimer();
56+
57+
extern CConVar<bool> g_cvarFixHudFlashing;

0 commit comments

Comments
 (0)