From 64f8ed7422b4cb9d2e3cc384f746c36a078ef48c Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Mon, 24 Aug 2026 22:09:56 +0100 Subject: [PATCH] Native spectator overlay - Team name/logo presets Add a simple preset setter set by overlay/presets.txt so in the overlay settings UI can just select a team rather than typing out the name and finding the logo file. --- game/neo/overlay/presets.txt | 23 ++++++++ src/game/client/neo/ui/neo_root.cpp | 82 ++++++++++++++++++++++++++++- src/game/client/neo/ui/neo_root.h | 8 +++ 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 game/neo/overlay/presets.txt diff --git a/game/neo/overlay/presets.txt b/game/neo/overlay/presets.txt new file mode 100644 index 000000000..8deacb680 --- /dev/null +++ b/game/neo/overlay/presets.txt @@ -0,0 +1,23 @@ +// overlay/presets.txt +// +// Team name and logo presets for the overlay HUD +// +// The logo files is relative from this "overlay" +// directory, just place it all here for simplicity. +// So EX: example.png resolves to overlay/example.png + +overlay_presets +{ + // Examples: + // + // team + // { + // name "Team 1" + // logo "team_1.png" + // } + // team + // { + // name "Team 2" + // logo "team_2.png" + // } +} diff --git a/src/game/client/neo/ui/neo_root.cpp b/src/game/client/neo/ui/neo_root.cpp index 1682d3a13..a421191ca 100644 --- a/src/game/client/neo/ui/neo_root.cpp +++ b/src/game/client/neo/ui/neo_root.cpp @@ -101,6 +101,7 @@ enum ENeoPopup NEOPOPUP_ACTIONSERVER = NeoUI::INTERNALPOPUP_NIL + 1, NEOPOPUP_ACTIONBLACKLIST, NEOPOPUP_MP3, + NEOPOPUP_OVERLAY_TEAM_PRESET, }; ConCommand neo_toggleconsole("neo_toggleconsole", NeoToggleconsole, "toggle the console", FCVAR_DONTRECORD); @@ -1008,6 +1009,34 @@ void CNeoRoot::MainLoopRoot(const MainLoopParam param) m_iMatchesWonJinrai = cl_neo_hud_spectator_overlay_jinrai_matches_won.GetInt(); m_iMatchesWonNSF = cl_neo_hud_spectator_overlay_nsf_matches_won.GetInt(); + { + m_iSetTeamPreset = 0; + KeyValues *kv = new KeyValues("overlay_presets"); + if (kv->LoadFromFile(g_pFullFileSystem, "overlay/presets.txt")) + { + m_teamInfos.Purge(); + for (KeyValues *teamKv = kv->GetFirstTrueSubKey(); + teamKv; + teamKv = teamKv->GetNextTrueSubKey()) + { + const char *pszName = teamKv->GetString("name"); + const char *pszLogo = teamKv->GetString("logo"); + if (pszName && pszLogo && pszName[0] && pszLogo[0]) + { + TeamInfo teamInfo = {}; + Q_UTF8ToUnicode(pszName, teamInfo.wszName, sizeof(teamInfo.wszName)); + + char szRelLogoPath[MAX_PATH]; + V_sprintf_safe(szRelLogoPath, "overlay/%s", pszLogo); + filesystem->RelativePathToFullPath_safe(szRelLogoPath, + nullptr, teamInfo.szLogoPath); + m_teamInfos.AddToTail(teamInfo); + } + } + } + kv->deleteThis(); + } + m_state = STATE_OVERLAY; } if (NeoUI::Button(m_wszCachedTexts[MMBTN_QUIT]).bPressed || (!IsInGame() && NeoUI::Bind(KEY_ESCAPE))) @@ -2276,6 +2305,7 @@ void CNeoRoot::MainLoopOverlay(const MainLoopParam param) g_uiCtx.colors.sectionBg = COLOR_BLACK_TRANSPARENT; NeoUI::BeginContext(&g_uiCtx, param.eMode, L"OVERLAY", "CtxOverlay"); { + const int iMinPresetRows = Min(m_teamInfos.Size(), 6); const int iThisRowTall = g_uiCtx.layout.iRowTall; NeoUI::BeginSection(NeoUI::SECTIONFLAG_DEFAULTFOCUS); { @@ -2286,7 +2316,7 @@ void CNeoRoot::MainLoopOverlay(const MainLoopParam param) NeoUI::Divider(L"JINRAI"); NeoUI::SliderInt(L"Matches won:", &m_iMatchesWonJinrai, 0, COMP_MATCHES_WON_MAX); NeoUI::TextEdit(L"Team name:", m_wszTeamNameJinrai, SZWSZ_LEN(m_wszTeamNameJinrai)); - NeoUI::SetPerRowLayout(2, nullptr, iThisRowTall); + NeoUI::SetPerRowLayout(3, nullptr, iThisRowTall); { g_uiCtx.eButtonTextStyle = NeoUI::TEXTSTYLE_CENTER; if (NeoUI::Button(L"Pick logo").bPressed) @@ -2297,6 +2327,17 @@ void CNeoRoot::MainLoopOverlay(const MainLoopParam param) { m_szLogoPathJinrai[0] = '\0'; } + if (NeoUI::Button(L"Set team").bPressed) + { + NeoUI::OpenPopup(NEOPOPUP_OVERLAY_TEAM_PRESET, + NeoUI::Dim{ + .x = g_uiCtx.rWidgetArea.x0, + .y = g_uiCtx.rWidgetArea.y1, + .wide = g_uiCtx.irWidgetWide, + .tall = g_uiCtx.layout.iDefRowTall * iMinPresetRows, + }); + m_iSetTeamPreset = TEAM_JINRAI; + } g_uiCtx.eButtonTextStyle = NeoUI::TEXTSTYLE_LEFT; } NeoUI::SetPerRowLayout(1, nullptr, iThisRowTall * 4); @@ -2309,7 +2350,7 @@ void CNeoRoot::MainLoopOverlay(const MainLoopParam param) NeoUI::Divider(L"NSF"); NeoUI::SliderInt(L"Matches won:", &m_iMatchesWonNSF, 0, COMP_MATCHES_WON_MAX); NeoUI::TextEdit(L"Team name:", m_wszTeamNameNSF, SZWSZ_LEN(m_wszTeamNameNSF)); - NeoUI::SetPerRowLayout(2, nullptr, iThisRowTall); + NeoUI::SetPerRowLayout(3, nullptr, iThisRowTall); { g_uiCtx.eButtonTextStyle = NeoUI::TEXTSTYLE_CENTER; if (NeoUI::Button(L"Pick logo").bPressed) @@ -2320,6 +2361,17 @@ void CNeoRoot::MainLoopOverlay(const MainLoopParam param) { m_szLogoPathNSF[0] = '\0'; } + if (NeoUI::Button(L"Set team").bPressed) + { + NeoUI::OpenPopup(NEOPOPUP_OVERLAY_TEAM_PRESET, + NeoUI::Dim{ + .x = g_uiCtx.rWidgetArea.x0, + .y = g_uiCtx.rWidgetArea.y1, + .wide = g_uiCtx.irWidgetWide, + .tall = g_uiCtx.layout.iDefRowTall * iMinPresetRows, + }); + m_iSetTeamPreset = TEAM_NSF; + } g_uiCtx.eButtonTextStyle = NeoUI::TEXTSTYLE_LEFT; } NeoUI::SetPerRowLayout(1, nullptr, iThisRowTall * 4); @@ -2410,6 +2462,32 @@ void CNeoRoot::MainLoopOverlay(const MainLoopParam param) NeoUI::SwapFont(NeoUI::FONT_NTNORMAL); } NeoUI::EndSection(); + + if (NeoUI::BeginPopup(NEOPOPUP_OVERLAY_TEAM_PRESET, NeoUI::POPUPFLAG_COLORHOTASACTIVE)) + { + for (const TeamInfo &teamInfo : m_teamInfos) + { + if (NeoUI::Button(teamInfo.wszName).bPressed) + { + switch (m_iSetTeamPreset) + { + case TEAM_JINRAI: + V_wcscpy_safe(m_wszTeamNameJinrai, teamInfo.wszName); + V_strcpy_safe(m_szLogoPathJinrai, teamInfo.szLogoPath); + break; + case TEAM_NSF: + V_wcscpy_safe(m_wszTeamNameNSF, teamInfo.wszName); + V_strcpy_safe(m_szLogoPathNSF, teamInfo.szLogoPath); + break; + default: + break; + } + NeoUI::ClosePopup(); + break; + } + } + NeoUI::EndPopup(); + } } } diff --git a/src/game/client/neo/ui/neo_root.h b/src/game/client/neo/ui/neo_root.h index 6da806571..394a8d507 100644 --- a/src/game/client/neo/ui/neo_root.h +++ b/src/game/client/neo/ui/neo_root.h @@ -286,6 +286,14 @@ class CNeoRoot : public vgui::EditablePanel, public CGameEventListener int m_iMatchesWonJinrai = 0; int m_iMatchesWonNSF = 0; char m_szNextLogoPathStartDir[MAX_PATH] = {}; + + struct TeamInfo + { + wchar_t wszName[MAX_PLAYER_NAME_LENGTH] = {}; + char szLogoPath[MAX_PATH] = {}; + }; + CUtlVector m_teamInfos; + int m_iSetTeamPreset = 0; }; extern CNeoRoot *g_pNeoRoot;