From cae0ebe70dbf28f22a8a0c517b74256fb08a4654 Mon Sep 17 00:00:00 2001 From: Rain Date: Mon, 2 Mar 2026 19:24:36 +0200 Subject: [PATCH 01/10] wip --- game/neo/resource/NeoModEvents.res | 16 ++++++ src/game/client/neo/ui/neo_root.cpp | 55 +++++++++++++++++++- src/game/client/neo/ui/neo_root_settings.cpp | 15 ++++++ src/game/client/neo/ui/neo_root_settings.h | 6 +++ src/game/client/neo/ui/neo_ui_shared.h | 21 ++++++++ src/game/shared/neo/neo_gamerules.cpp | 31 ++++++++++- 6 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 src/game/client/neo/ui/neo_ui_shared.h diff --git a/game/neo/resource/NeoModEvents.res b/game/neo/resource/NeoModEvents.res index 9d62eb6227..7ee716d22a 100644 --- a/game/neo/resource/NeoModEvents.res +++ b/game/neo/resource/NeoModEvents.res @@ -124,4 +124,20 @@ "area" "long" "blocked" "bool" } + + "comp_match_start" + { + } + + "comp_match_end" + { + } + + "match_start" + { + } + + "comp_round_start" + { + } } diff --git a/src/game/client/neo/ui/neo_root.cpp b/src/game/client/neo/ui/neo_root.cpp index 0a4c3e3cc4..45cb3f0d4c 100644 --- a/src/game/client/neo/ui/neo_root.cpp +++ b/src/game/client/neo/ui/neo_root.cpp @@ -21,6 +21,8 @@ #include "tier1/interface.h" #include #include "ui/neo_loading.h" +#include "ui/neo_ui.h" +#include "ui/neo_ui_shared.h" #include "ui/neo_utils.h" #include "neo_gamerules.h" #include "neo_misc.h" @@ -104,6 +106,18 @@ enum ENeoPopup ConCommand neo_toggleconsole("neo_toggleconsole", NeoToggleconsole, "toggle the console", FCVAR_DONTRECORD); +ConVar neo_flash_taskbar("neo_flash_taskbar", "0", FCVAR_ARCHIVE, + "0: Never" + " 1: When comp match starts" + " 2: When comp round starts" + " 3: When any match starts" + " 4: When any round starts", + true, 0, true, NeoUI::ENeoFlashTaskbarOption::MaxValue); + +ConVar neo_flash_taskbar_no_spec("neo_flash_taskbar_no_spec", "1", FCVAR_ARCHIVE, + "Whether to only use neo_flash_taskbar when you are in a player team.", + true, false, true, true); + struct YMD { YMD(const struct tm tm) @@ -448,8 +462,13 @@ CNeoRoot::CNeoRoot(VPANEL parent) SetMouseInputEnabled(true); UpdateControls(); ivgui()->AddTickSignal(GetVPanel(), 200); - ListenForGameEvent("server_spawn"); + ListenForGameEvent("comp_match_start"); + ListenForGameEvent("comp_round_start"); ListenForGameEvent("game_newmap"); + ListenForGameEvent("game_start"); + ListenForGameEvent("match_start"); + ListenForGameEvent("round_start"); + ListenForGameEvent("server_spawn"); vgui::IScheme *pScheme = vgui::scheme()->GetIScheme(neoscheme); ApplySchemeSettings(pScheme); @@ -626,6 +645,40 @@ void CNeoRoot::FireGameEvent(IGameEvent *event) { g_pVGuiLocalize->ConvertANSIToUnicode(event->GetString("mapname"), m_wszMap, sizeof(m_wszMap)); } + else if (!neo_flash_taskbar_no_spec.GetBool() || + C_NEO_Player::GetLocalPlayer()->GetObserverMode() == OBS_MODE_NONE) + { + Assert(engine); + if (neo_flash_taskbar.GetBool())// && !engine->IsActiveApp()) + { + const char* targetEvent; + using NeoUI::ENeoFlashTaskbarOption; + switch (neo_flash_taskbar.GetInt()) + { + case ENeoFlashTaskbarOption::AnyMatchStart: + targetEvent = "match_start"; + break; + case ENeoFlashTaskbarOption::AnyRoundStart: + targetEvent = "round_start"; + break; + case ENeoFlashTaskbarOption::CompMatchStart: + targetEvent = "comp_match_start"; + break; + case ENeoFlashTaskbarOption::CompRoundStart: + targetEvent = "comp_round_start"; + break; + default: + Assert(false); + return; + } + + if (FStrEq(targetEvent, type)) + { + DevMsg("Flash window for event: %s\n", type); + engine->FlashWindow(); + } + } + } } void CNeoRoot::OnRelayedKeyCodeTyped(vgui::KeyCode code) diff --git a/src/game/client/neo/ui/neo_root_settings.cpp b/src/game/client/neo/ui/neo_root_settings.cpp index f7acf84ed2..a14d39b0da 100644 --- a/src/game/client/neo/ui/neo_root_settings.cpp +++ b/src/game/client/neo/ui/neo_root_settings.cpp @@ -135,6 +135,14 @@ static const wchar_t *CROSSHAIR_HIPFIRE_LABELS[HIPFIREOPT__TOTAL] = { L"Enabled", }; +static const wchar_t* FLASH_TASKBAR_LABELS[NeoUI::ENeoFlashTaskbarOption::EnumCount] = { + L"Never", + L"When competitive match starts", + L"When competitive round starts", + L"When any match starts", + L"When any round starts", +}; + static const wchar_t *STARTUP_TYPE_LABELS[NeoMP3::STARTUP_TYPE__TOTAL] = { L"Default", L"Always random", @@ -443,6 +451,8 @@ void NeoSettingsRestore(NeoSettings *ns, const NeoSettings::Keys::Flags flagsKey pGeneral->bAutoDetectOBS = cvr->cl_neo_streamermode_autodetect_obs.GetBool(); pGeneral->bTachiFullAutoPreferred = cvr->cl_neo_tachi_prefer_auto.GetBool(); pGeneral->bTakingDamageSounds = cvr->cl_neo_taking_damage_sounds.GetBool(); + pGeneral->iFlashTaskbarOption = cvr->neo_flash_taskbar.GetInt(); + pGeneral->bDontFlashTaskbarIfObserver = cvr->neo_flash_taskbar_no_spec.GetBool(); pGeneral->iBackground = clamp(cvr->sv_unlockedchapters.GetInt(), 0, ns->iCBListSize - 1); NeoSettingsBackgroundWrite(ns); NeoUI::ResetTextures(); @@ -1145,6 +1155,11 @@ void NeoSettings_General(NeoSettings *ns) NeoUI::EndOverrideFgColor(); } + NeoUI::RingBox(L"Flash the game window in OS taskbar", FLASH_TASKBAR_LABELS, ARRAYSIZE(FLASH_TASKBAR_LABELS), &pGeneral->iFlashTaskbarOption); + // Hide this option if it's irrelevant for the user, to make the UI less cluttered. + if (pGeneral->iFlashTaskbarOption != NeoUI::ENeoFlashTaskbarOption::Never) + NeoUI::RingBoxBool(L"Don't flash the taskbar if spectating", &pGeneral->bDontFlashTaskbarIfObserver); + NeoUI::Pad(); NeoUI::Pad(); NeoUI::RingBoxBool(L"Streamer mode", &pGeneral->bStreamerMode); diff --git a/src/game/client/neo/ui/neo_root_settings.h b/src/game/client/neo/ui/neo_root_settings.h index 44619bc001..ee34c3d6bf 100644 --- a/src/game/client/neo/ui/neo_root_settings.h +++ b/src/game/client/neo/ui/neo_root_settings.h @@ -6,6 +6,8 @@ #include "neo_crosshair.h" #include "neo_hud_friendly_marker.h" +#include "neo_ui_shared.h" + // NEO TODO (nullsystem): Implement our own file IO dialog #include "vgui_controls/FileOpenDialog.h" @@ -73,6 +75,8 @@ struct NeoSettings bool bTachiFullAutoPreferred; int iBackground; bool bTakingDamageSounds; + std::underlying_type_t iFlashTaskbarOption; + bool bDontFlashTaskbarIfObserver; }; struct Keys @@ -284,6 +288,8 @@ struct NeoSettings CONVARREF_DEF(cl_neo_hud_context_hint_highlight_player); CONVARREF_DEF(cl_neo_equip_utility_priority); CONVARREF_DEF(cl_neo_taking_damage_sounds); + CONVARREF_DEF(neo_flash_taskbar); + CONVARREF_DEF(neo_flash_taskbar_no_spec); // Multiplayer CONVARREF_DEF(cl_spraydisable); diff --git a/src/game/client/neo/ui/neo_ui_shared.h b/src/game/client/neo/ui/neo_ui_shared.h new file mode 100644 index 0000000000..f2e1bff156 --- /dev/null +++ b/src/game/client/neo/ui/neo_ui_shared.h @@ -0,0 +1,21 @@ +#pragma once + +// Stuff shared by NeoUI, without necessarily wanting to bring in a whole other module. +namespace NeoUI { + +// Whether to flash the app window in OS task bar to get user's attention for whatever reason +enum ENeoFlashTaskbarOption : int +{ + Never = 0, + CompMatchStart, + CompRoundStart, + AnyMatchStart, + AnyRoundStart, + + // Any new options must go ABOVE this line. + // Please don't reorder existing choices for config compatability. + EnumCount, + MaxValue = EnumCount +}; + +} // namespace NeoUI diff --git a/src/game/shared/neo/neo_gamerules.cpp b/src/game/shared/neo/neo_gamerules.cpp index eeff852c6f..2b6b8b5e5f 100644 --- a/src/game/shared/neo/neo_gamerules.cpp +++ b/src/game/shared/neo/neo_gamerules.cpp @@ -3381,6 +3381,7 @@ void CNEORules::RestartGame() SetGameRelatedVars(); IGameEvent * event = gameeventmanager->CreateEvent("round_start"); + Assert(event); if (event) { event->SetInt("fraglimit", 0); @@ -3390,6 +3391,20 @@ void CNEORules::RestartGame() gameeventmanager->FireEvent(event); } + + event = gameeventmanager->CreateEvent("match_start"); + Assert(event); + if (event) + gameeventmanager->FireEvent(event); + + if (GetActiveGameConfig() && sv_neo_comp.GetBool()) + { + event = gameeventmanager->CreateEvent("comp_match_start"); + Assert(event); + if (event) + gameeventmanager->FireEvent(event); + } + FireLegacyEvent_NeoRoundStart(); } #endif @@ -3699,13 +3714,27 @@ void CNEORules::SetWinningTeam(int team, int iWinReason, bool bForceMapReset, bo return; } - if (auto pEntGameCfg = GetActiveGameConfig()) + auto* pEntGameCfg = GetActiveGameConfig(); + if (pEntGameCfg) { pEntGameCfg->m_OnRoundEnd.Set(team, nullptr, pEntGameCfg); } if (bForceMapReset) { + auto* event = gameeventmanager->CreateEvent("match_end"); + Assert(event); + if (event) + gameeventmanager->FireEvent(event); + + if (pEntGameCfg && sv_neo_comp.GetBool()) + { + event = gameeventmanager->CreateEvent("comp_match_start"); + Assert(event); + if (event) + gameeventmanager->FireEvent(event); + } + RestartGame(); } else From 854f88d6b92910e73a19969f761f9c3f09f34207 Mon Sep 17 00:00:00 2001 From: Rain Date: Mon, 2 Mar 2026 20:16:05 +0200 Subject: [PATCH 02/10] wip --- game/neo/resource/NeoModEvents.res | 4 +++ src/game/client/neo/ui/neo_root.cpp | 34 ++++++++++++-------- src/game/client/neo/ui/neo_root_settings.cpp | 4 ++- src/game/shared/neo/neo_gamerules.cpp | 15 +++++++-- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/game/neo/resource/NeoModEvents.res b/game/neo/resource/NeoModEvents.res index 7ee716d22a..c669cc0cbb 100644 --- a/game/neo/resource/NeoModEvents.res +++ b/game/neo/resource/NeoModEvents.res @@ -140,4 +140,8 @@ "comp_round_start" { } + + "lobby_all_players_ready" + { + } } diff --git a/src/game/client/neo/ui/neo_root.cpp b/src/game/client/neo/ui/neo_root.cpp index 45cb3f0d4c..2f1080d2ba 100644 --- a/src/game/client/neo/ui/neo_root.cpp +++ b/src/game/client/neo/ui/neo_root.cpp @@ -107,6 +107,7 @@ enum ENeoPopup ConCommand neo_toggleconsole("neo_toggleconsole", NeoToggleconsole, "toggle the console", FCVAR_DONTRECORD); ConVar neo_flash_taskbar("neo_flash_taskbar", "0", FCVAR_ARCHIVE, + "Flash inactive game window in the operating system taskbar. " "0: Never" " 1: When comp match starts" " 2: When comp round starts" @@ -115,7 +116,7 @@ ConVar neo_flash_taskbar("neo_flash_taskbar", "0", FCVAR_ARCHIVE, true, 0, true, NeoUI::ENeoFlashTaskbarOption::MaxValue); ConVar neo_flash_taskbar_no_spec("neo_flash_taskbar_no_spec", "1", FCVAR_ARCHIVE, - "Whether to only use neo_flash_taskbar when you are in a player team.", + "Whether to only apply neo_flash_taskbar when you are in a player (not spectator) team.", true, false, true, true); struct YMD @@ -466,6 +467,7 @@ CNeoRoot::CNeoRoot(VPANEL parent) ListenForGameEvent("comp_round_start"); ListenForGameEvent("game_newmap"); ListenForGameEvent("game_start"); + ListenForGameEvent("lobby_all_players_ready"); ListenForGameEvent("match_start"); ListenForGameEvent("round_start"); ListenForGameEvent("server_spawn"); @@ -651,31 +653,35 @@ void CNeoRoot::FireGameEvent(IGameEvent *event) Assert(engine); if (neo_flash_taskbar.GetBool())// && !engine->IsActiveApp()) { - const char* targetEvent; - using NeoUI::ENeoFlashTaskbarOption; + CUtlVector targetEvents(0, 2); switch (neo_flash_taskbar.GetInt()) { - case ENeoFlashTaskbarOption::AnyMatchStart: - targetEvent = "match_start"; + case NeoUI::ENeoFlashTaskbarOption::AnyMatchStart: + targetEvents.AddToTail("match_start"); break; - case ENeoFlashTaskbarOption::AnyRoundStart: - targetEvent = "round_start"; + case NeoUI::ENeoFlashTaskbarOption::AnyRoundStart: + targetEvents.AddToTail("round_start"); break; - case ENeoFlashTaskbarOption::CompMatchStart: - targetEvent = "comp_match_start"; + case NeoUI::ENeoFlashTaskbarOption::CompMatchStart: + targetEvents.AddToTail("lobby_all_players_ready"); + targetEvents.AddToTail("comp_match_start"); break; - case ENeoFlashTaskbarOption::CompRoundStart: - targetEvent = "comp_round_start"; + case NeoUI::ENeoFlashTaskbarOption::CompRoundStart: + targetEvents.AddToTail("comp_round_start"); break; default: Assert(false); return; } - if (FStrEq(targetEvent, type)) + for (const auto& targetEvent: targetEvents) { - DevMsg("Flash window for event: %s\n", type); - engine->FlashWindow(); + if (FStrEq(targetEvent, type)) + { + DevMsg("Flash window for event: %s\n", type); + engine->FlashWindow(); + break; + } } } } diff --git a/src/game/client/neo/ui/neo_root_settings.cpp b/src/game/client/neo/ui/neo_root_settings.cpp index a14d39b0da..07d6f9dcbd 100644 --- a/src/game/client/neo/ui/neo_root_settings.cpp +++ b/src/game/client/neo/ui/neo_root_settings.cpp @@ -811,6 +811,8 @@ void NeoSettingsSave(const NeoSettings *ns) cvr->cl_neo_tachi_prefer_auto.SetValue(pGeneral->bTachiFullAutoPreferred); cvr->sv_unlockedchapters.SetValue(pGeneral->iBackground); cvr->cl_neo_taking_damage_sounds.SetValue(pGeneral->bTakingDamageSounds); + cvr->neo_flash_taskbar.SetValue(pGeneral->iFlashTaskbarOption); + cvr->neo_flash_taskbar_no_spec.SetValue(pGeneral->bDontFlashTaskbarIfObserver); NeoSettingsBackgroundWrite(ns); } { @@ -1155,7 +1157,7 @@ void NeoSettings_General(NeoSettings *ns) NeoUI::EndOverrideFgColor(); } - NeoUI::RingBox(L"Flash the game window in OS taskbar", FLASH_TASKBAR_LABELS, ARRAYSIZE(FLASH_TASKBAR_LABELS), &pGeneral->iFlashTaskbarOption); + NeoUI::RingBox(L"Flash inactive game window in OS taskbar", FLASH_TASKBAR_LABELS, ARRAYSIZE(FLASH_TASKBAR_LABELS), &pGeneral->iFlashTaskbarOption); // Hide this option if it's irrelevant for the user, to make the UI less cluttered. if (pGeneral->iFlashTaskbarOption != NeoUI::ENeoFlashTaskbarOption::Never) NeoUI::RingBoxBool(L"Don't flash the taskbar if spectating", &pGeneral->bDontFlashTaskbarIfObserver); diff --git a/src/game/shared/neo/neo_gamerules.cpp b/src/game/shared/neo/neo_gamerules.cpp index 2b6b8b5e5f..c120b993b0 100644 --- a/src/game/shared/neo/neo_gamerules.cpp +++ b/src/game/shared/neo/neo_gamerules.cpp @@ -2714,6 +2714,10 @@ void CNEORules::StartNextRound() m_flNeoRoundStartTime = gpGlobals->curtime; m_flNeoNextRoundStartTime = gpGlobals->curtime + sv_neo_readyup_countdown.GetFloat(); UTIL_CenterPrintAll("- ALL PLAYERS READY: MATCH STARTING SOON... -\n"); + + auto* event = gameeventmanager->CreateEvent("lobby_all_players_ready"); + if (event)gameeventmanager->FireEvent(event); + return; } else @@ -2917,15 +2921,22 @@ void CNEORules::StartNextRound() SetGameRelatedVars(); IGameEvent *event = gameeventmanager->CreateEvent("round_start"); + Assert(event); if (event) { event->SetInt("fraglimit", 0); event->SetInt("priority", 6); // HLTV event priority, not transmitted - event->SetString("objective", "DEATHMATCH"); - gameeventmanager->FireEvent(event); } + + if (m_iRoundNumber == 1) + { + event = gameeventmanager->CreateEvent("match_start"); + Assert(event); + if (event) gameeventmanager->FireEvent(event); + } + FireLegacyEvent_NeoRoundStart(); DevMsg("New round start here!\n"); From 0bc56f244c3716b447939e346f529340c27e3f6e Mon Sep 17 00:00:00 2001 From: Rain Date: Mon, 2 Mar 2026 22:16:38 +0200 Subject: [PATCH 03/10] spelling is hard --- src/game/client/neo/ui/neo_ui_shared.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/neo/ui/neo_ui_shared.h b/src/game/client/neo/ui/neo_ui_shared.h index f2e1bff156..fbbb8e512e 100644 --- a/src/game/client/neo/ui/neo_ui_shared.h +++ b/src/game/client/neo/ui/neo_ui_shared.h @@ -13,7 +13,7 @@ enum ENeoFlashTaskbarOption : int AnyRoundStart, // Any new options must go ABOVE this line. - // Please don't reorder existing choices for config compatability. + // Please don't reorder existing choices for config compatibility. EnumCount, MaxValue = EnumCount }; From a35e709f972c2cff085df4c52773f7bdf7d8f855 Mon Sep 17 00:00:00 2001 From: Rain Date: Tue, 3 Mar 2026 03:30:10 +0200 Subject: [PATCH 04/10] wip: cleanup --- src/game/client/neo/ui/neo_ui_shared.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/neo/ui/neo_ui_shared.h b/src/game/client/neo/ui/neo_ui_shared.h index fbbb8e512e..a9bcddc1fa 100644 --- a/src/game/client/neo/ui/neo_ui_shared.h +++ b/src/game/client/neo/ui/neo_ui_shared.h @@ -1,6 +1,6 @@ #pragma once +// Stuff shared by NeoUI, without necessarily wanting to bring in a whole other header. -// Stuff shared by NeoUI, without necessarily wanting to bring in a whole other module. namespace NeoUI { // Whether to flash the app window in OS task bar to get user's attention for whatever reason From 57d0d06b037a38d675109153e2ac672a749bd5b5 Mon Sep 17 00:00:00 2001 From: Rain Date: Tue, 3 Mar 2026 03:38:58 +0200 Subject: [PATCH 05/10] Add IDebugGameEventManager shim For catching wrong or missing events --- src/game/server/gameinterface.cpp | 4 + src/game/shared/neo/neo_gamerules.cpp | 2 + src/public/igameevents.h | 111 ++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) diff --git a/src/game/server/gameinterface.cpp b/src/game/server/gameinterface.cpp index 3890342c41..0c1e3a252a 100644 --- a/src/game/server/gameinterface.cpp +++ b/src/game/server/gameinterface.cpp @@ -614,7 +614,11 @@ bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory, return false; if ( (filesystem = (IFileSystem *)fileSystemFactory(FILESYSTEM_INTERFACE_VERSION,NULL)) == NULL ) return false; +#if defined(NEO) && defined(DBGFLAG_ASSERT) + if ( (gameeventmanager = new IDebugGameEventManager{ (IGameEventManager2 *)appSystemFactory(INTERFACEVERSION_GAMEEVENTSMANAGER2,NULL) }) == NULL ) +#else if ( (gameeventmanager = (IGameEventManager2 *)appSystemFactory(INTERFACEVERSION_GAMEEVENTSMANAGER2,NULL)) == NULL ) +#endif return false; if ( (datacache = (IDataCache*)appSystemFactory(DATACACHE_INTERFACE_VERSION, NULL )) == NULL ) return false; diff --git a/src/game/shared/neo/neo_gamerules.cpp b/src/game/shared/neo/neo_gamerules.cpp index c120b993b0..d72265bba6 100644 --- a/src/game/shared/neo/neo_gamerules.cpp +++ b/src/game/shared/neo/neo_gamerules.cpp @@ -2920,6 +2920,8 @@ void CNEORules::StartNextRound() SetGameRelatedVars(); + gameeventmanager->CreateEvent("noexist"); + IGameEvent *event = gameeventmanager->CreateEvent("round_start"); Assert(event); if (event) diff --git a/src/public/igameevents.h b/src/public/igameevents.h index 43ce3417d4..9e124c2511 100644 --- a/src/public/igameevents.h +++ b/src/public/igameevents.h @@ -164,6 +164,117 @@ abstract_class IGameEventManager2 : public IBaseInterface virtual IGameEvent *UnserializeEvent( bf_read *buf ) = 0; // create new KeyValues, must be deleted }; +#if defined(NEO) && defined(DBGFLAG_ASSERT) +// Debug shim for IGameEventManager2 with extra asserts +class IDebugGameEventManager : public IGameEventManager2 +{ + IGameEventManager2* m_impl; + +public: + IDebugGameEventManager(IGameEventManager2* impl) + : m_impl(impl) + { + Assert(impl); + } + + virtual ~IDebugGameEventManager() + { + delete m_impl; + } + + // load game event descriptions from a file eg "resource\gameevents.res" + virtual int LoadEventsFromFile(const char* filename) + { + return m_impl->LoadEventsFromFile(filename); + } + + // removes all and anything + virtual void Reset() + { + return m_impl->Reset(); + } + + // adds a listener for a particular event + virtual bool AddListener(IGameEventListener2* listener, const char* name, bool bServerSide) + { + return m_impl->AddListener(listener, name, bServerSide); + } + + // returns true if this listener is listens to given event + virtual bool FindListener(IGameEventListener2* listener, const char* name) + { + auto ret = m_impl->FindListener(listener, name); + Assert(ret); + return ret; + } + + // removes a listener + virtual void RemoveListener(IGameEventListener2* listener) + { + return m_impl->RemoveListener(listener); + } + + // create an event by name, but doesn't fire it. returns NULL is event is not + // known or no listener is registered for it. bForce forces the creation even if no listener is active + virtual IGameEvent* CreateEvent(const char* name, bool bForce = false) + { + auto ret = m_impl->CreateEvent(name, bForce); + constexpr const char* modEventsFile = "resource/NeoModEvents.res"; + if (bForce) + { + AssertMsg2(ret, "Event \"%s\" unknown or lacks listener; check %s", name, modEventsFile); + } + else + { + AssertMsg2(ret, "Event \"%s\" unknown; check %s", name, modEventsFile); + } + return ret; + } + + // fires a server event created earlier, if bDontBroadcast is set, event is not send to clients + virtual bool FireEvent(IGameEvent* event, bool bDontBroadcast = false) + { + auto ret = m_impl->FireEvent(event, bDontBroadcast); + Assert(ret); + return ret; + } + + // fires an event for the local client only, should be used only by client code + virtual bool FireEventClientSide(IGameEvent* event) + { + auto ret = m_impl->FireEventClientSide(event); + Assert(ret); + return ret; + } + + // create a new copy of this event, must be free later + virtual IGameEvent* DuplicateEvent(IGameEvent* event) + { + return m_impl->DuplicateEvent(event); + } + + // if an event was created but not fired for some reason, it has to bee freed, same UnserializeEvent + virtual void FreeEvent(IGameEvent* event) + { + return m_impl->FreeEvent(event); + } + + // write/read event to/from bitbuffer + virtual bool SerializeEvent(IGameEvent* event, bf_write* buf) + { + auto res = m_impl->SerializeEvent(event, buf); + Assert(res); + return res; + } + + // create new KeyValues, must be deleted + virtual IGameEvent *UnserializeEvent( bf_read *buf ) + { + return m_impl->UnserializeEvent(buf); + } +}; +#endif + // the old game event manager interface, don't use it. Rest is legacy support: abstract_class IGameEventListener From 723fb940835fce181d4cad1b91a88ce65d00dab8 Mon Sep 17 00:00:00 2001 From: Rain Date: Wed, 1 Jul 2026 16:35:18 +0300 Subject: [PATCH 06/10] optimize --- src/game/client/neo/ui/neo_root.cpp | 30 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/game/client/neo/ui/neo_root.cpp b/src/game/client/neo/ui/neo_root.cpp index 2f1080d2ba..71071aa3c2 100644 --- a/src/game/client/neo/ui/neo_root.cpp +++ b/src/game/client/neo/ui/neo_root.cpp @@ -651,37 +651,35 @@ void CNeoRoot::FireGameEvent(IGameEvent *event) C_NEO_Player::GetLocalPlayer()->GetObserverMode() == OBS_MODE_NONE) { Assert(engine); - if (neo_flash_taskbar.GetBool())// && !engine->IsActiveApp()) + if (true /*!engine->IsActiveApp()*/) { - CUtlVector targetEvents(0, 2); + bool shouldFlashWindow; switch (neo_flash_taskbar.GetInt()) { case NeoUI::ENeoFlashTaskbarOption::AnyMatchStart: - targetEvents.AddToTail("match_start"); + shouldFlashWindow = FStrEq(type, "match_start"); break; case NeoUI::ENeoFlashTaskbarOption::AnyRoundStart: - targetEvents.AddToTail("round_start"); + shouldFlashWindow = FStrEq(type, "round_start"); break; case NeoUI::ENeoFlashTaskbarOption::CompMatchStart: - targetEvents.AddToTail("lobby_all_players_ready"); - targetEvents.AddToTail("comp_match_start"); + shouldFlashWindow = FStrEq(type, "comp_match_start") || + FStrEq(type, "lobby_all_players_ready"); break; case NeoUI::ENeoFlashTaskbarOption::CompRoundStart: - targetEvents.AddToTail("comp_round_start"); + shouldFlashWindow = FStrEq(type, "comp_round_start"); break; default: - Assert(false); - return; + AssertMsg(neo_flash_taskbar.GetInt() == NeoUI::ENeoFlashTaskbarOption::Never, + "Fell through switch with cvar val %d", neo_flash_taskbar.GetInt()); + shouldFlashWindow = false; + break; } - for (const auto& targetEvent: targetEvents) + if (shouldFlashWindow) { - if (FStrEq(targetEvent, type)) - { - DevMsg("Flash window for event: %s\n", type); - engine->FlashWindow(); - break; - } + DevMsg("Flash window for event: %s\n", type); + engine->FlashWindow(); } } } From e77cb4df9f0d89c1a6b671c54693860c2de62c0a Mon Sep 17 00:00:00 2001 From: Rain Date: Wed, 1 Jul 2026 16:40:24 +0300 Subject: [PATCH 07/10] Windows only, for now --- src/game/client/neo/ui/neo_root.cpp | 2 ++ src/game/client/neo/ui/neo_root_settings.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/game/client/neo/ui/neo_root.cpp b/src/game/client/neo/ui/neo_root.cpp index 71071aa3c2..b1d9987914 100644 --- a/src/game/client/neo/ui/neo_root.cpp +++ b/src/game/client/neo/ui/neo_root.cpp @@ -647,6 +647,7 @@ void CNeoRoot::FireGameEvent(IGameEvent *event) { g_pVGuiLocalize->ConvertANSIToUnicode(event->GetString("mapname"), m_wszMap, sizeof(m_wszMap)); } +#ifdef _WIN32 else if (!neo_flash_taskbar_no_spec.GetBool() || C_NEO_Player::GetLocalPlayer()->GetObserverMode() == OBS_MODE_NONE) { @@ -683,6 +684,7 @@ void CNeoRoot::FireGameEvent(IGameEvent *event) } } } +#endif } void CNeoRoot::OnRelayedKeyCodeTyped(vgui::KeyCode code) diff --git a/src/game/client/neo/ui/neo_root_settings.cpp b/src/game/client/neo/ui/neo_root_settings.cpp index 07d6f9dcbd..85c9e3e9e2 100644 --- a/src/game/client/neo/ui/neo_root_settings.cpp +++ b/src/game/client/neo/ui/neo_root_settings.cpp @@ -1157,10 +1157,12 @@ void NeoSettings_General(NeoSettings *ns) NeoUI::EndOverrideFgColor(); } +#ifdef _WIN32 NeoUI::RingBox(L"Flash inactive game window in OS taskbar", FLASH_TASKBAR_LABELS, ARRAYSIZE(FLASH_TASKBAR_LABELS), &pGeneral->iFlashTaskbarOption); // Hide this option if it's irrelevant for the user, to make the UI less cluttered. if (pGeneral->iFlashTaskbarOption != NeoUI::ENeoFlashTaskbarOption::Never) NeoUI::RingBoxBool(L"Don't flash the taskbar if spectating", &pGeneral->bDontFlashTaskbarIfObserver); +#endif NeoUI::Pad(); NeoUI::Pad(); From a0798c755022eb64be42aac42723dbaa6df4713a Mon Sep 17 00:00:00 2001 From: Rain Date: Wed, 1 Jul 2026 17:50:25 +0300 Subject: [PATCH 08/10] refactor --- src/game/client/neo/ui/neo_root.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/game/client/neo/ui/neo_root.cpp b/src/game/client/neo/ui/neo_root.cpp index b1d9987914..97a9d44340 100644 --- a/src/game/client/neo/ui/neo_root.cpp +++ b/src/game/client/neo/ui/neo_root.cpp @@ -649,10 +649,11 @@ void CNeoRoot::FireGameEvent(IGameEvent *event) } #ifdef _WIN32 else if (!neo_flash_taskbar_no_spec.GetBool() || + C_NEO_Player::GetLocalPlayer() && C_NEO_Player::GetLocalPlayer()->GetObserverMode() == OBS_MODE_NONE) { Assert(engine); - if (true /*!engine->IsActiveApp()*/) + if (!engine->IsActiveApp()) { bool shouldFlashWindow; switch (neo_flash_taskbar.GetInt()) @@ -672,7 +673,7 @@ void CNeoRoot::FireGameEvent(IGameEvent *event) break; default: AssertMsg(neo_flash_taskbar.GetInt() == NeoUI::ENeoFlashTaskbarOption::Never, - "Fell through switch with cvar val %d", neo_flash_taskbar.GetInt()); + "switch fallthrough of %s = %d", neo_flash_taskbar.GetName(), neo_flash_taskbar.GetInt()); shouldFlashWindow = false; break; } From 918b8b884da6ce2ed796898ab030679e9b534921 Mon Sep 17 00:00:00 2001 From: Rain Date: Wed, 1 Jul 2026 17:50:37 +0300 Subject: [PATCH 09/10] fix ENeoFlashTaskbarOption max value --- src/game/client/neo/ui/neo_ui_shared.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/neo/ui/neo_ui_shared.h b/src/game/client/neo/ui/neo_ui_shared.h index a9bcddc1fa..44062102be 100644 --- a/src/game/client/neo/ui/neo_ui_shared.h +++ b/src/game/client/neo/ui/neo_ui_shared.h @@ -15,7 +15,7 @@ enum ENeoFlashTaskbarOption : int // Any new options must go ABOVE this line. // Please don't reorder existing choices for config compatibility. EnumCount, - MaxValue = EnumCount + MaxValue = EnumCount-1 }; } // namespace NeoUI From 001a94d7e8ee16ff5d900bf28672a8fe4693e580 Mon Sep 17 00:00:00 2001 From: Rain Date: Wed, 1 Jul 2026 18:07:06 +0300 Subject: [PATCH 10/10] refactor --- src/game/client/neo/ui/neo_root.cpp | 6 +++--- src/game/client/neo/ui/neo_ui_shared.h | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/game/client/neo/ui/neo_root.cpp b/src/game/client/neo/ui/neo_root.cpp index 97a9d44340..9439685043 100644 --- a/src/game/client/neo/ui/neo_root.cpp +++ b/src/game/client/neo/ui/neo_root.cpp @@ -107,13 +107,13 @@ enum ENeoPopup ConCommand neo_toggleconsole("neo_toggleconsole", NeoToggleconsole, "toggle the console", FCVAR_DONTRECORD); ConVar neo_flash_taskbar("neo_flash_taskbar", "0", FCVAR_ARCHIVE, - "Flash inactive game window in the operating system taskbar. " - "0: Never" + "Flash inactive game window in the operating system taskbar." + " 0: Never" " 1: When comp match starts" " 2: When comp round starts" " 3: When any match starts" " 4: When any round starts", - true, 0, true, NeoUI::ENeoFlashTaskbarOption::MaxValue); + true, NeoUI::ENeoFlashTaskbarOption::MinValue, true, NeoUI::ENeoFlashTaskbarOption::MaxValue); ConVar neo_flash_taskbar_no_spec("neo_flash_taskbar_no_spec", "1", FCVAR_ARCHIVE, "Whether to only apply neo_flash_taskbar when you are in a player (not spectator) team.", diff --git a/src/game/client/neo/ui/neo_ui_shared.h b/src/game/client/neo/ui/neo_ui_shared.h index 44062102be..4ac3c1bfe4 100644 --- a/src/game/client/neo/ui/neo_ui_shared.h +++ b/src/game/client/neo/ui/neo_ui_shared.h @@ -15,7 +15,8 @@ enum ENeoFlashTaskbarOption : int // Any new options must go ABOVE this line. // Please don't reorder existing choices for config compatibility. EnumCount, - MaxValue = EnumCount-1 + MaxValue = EnumCount-1, + MinValue = Never }; } // namespace NeoUI