From 75d7e00cbf301e4297f05bf2c6d10394c7da4294 Mon Sep 17 00:00:00 2001 From: Vesper <3692034+noodlecollie@users.noreply.github.com> Date: Mon, 22 Jun 2026 14:30:06 +0100 Subject: [PATCH 01/11] Skeleton of style guide menu --- game/content-hash.txt | 2 +- game/game_libs/ui_new/CMakeLists.txt | 2 ++ game/game_libs/ui_new/src/framework/MenuDirectory.cpp | 2 ++ game/game_libs/ui_new/src/menus/StyleGuide.cpp | 6 ++++++ game/game_libs/ui_new/src/menus/StyleGuide.h | 11 +++++++++++ 5 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 game/game_libs/ui_new/src/menus/StyleGuide.cpp create mode 100644 game/game_libs/ui_new/src/menus/StyleGuide.h diff --git a/game/content-hash.txt b/game/content-hash.txt index bb4f5a91..6bb8d8f9 100644 --- a/game/content-hash.txt +++ b/game/content-hash.txt @@ -1 +1 @@ -master-8c32ce3998a72bac6a3ccd7097ce3958af7524b6 +style-guide-impl-ad77ccc80400dcee12c006415edcb55903738767 diff --git a/game/game_libs/ui_new/CMakeLists.txt b/game/game_libs/ui_new/CMakeLists.txt index 249361d0..28a05e26 100644 --- a/game/game_libs/ui_new/CMakeLists.txt +++ b/game/game_libs/ui_new/CMakeLists.txt @@ -66,6 +66,8 @@ set(SOURCES_UI src/menus/PauseMenu.cpp src/menus/ServerConnectionScreen.h src/menus/ServerConnectionScreen.cpp + src/menus/StyleGuide.h + src/menus/StyleGuide.cpp src/models/CreateMultiplayerGamePageModel.h src/models/CreateMultiplayerGamePageModel.cpp src/models/CvarModel.h diff --git a/game/game_libs/ui_new/src/framework/MenuDirectory.cpp b/game/game_libs/ui_new/src/framework/MenuDirectory.cpp index f7d17ac7..3aac9502 100644 --- a/game/game_libs/ui_new/src/framework/MenuDirectory.cpp +++ b/game/game_libs/ui_new/src/framework/MenuDirectory.cpp @@ -9,6 +9,7 @@ #include "menus/CreditsMenu.h" #include "menus/ServerConnectionScreen.h" #include "menus/CreateMultiplayerGameMenu.h" +#include "menus/StyleGuide.h" #include "menus/options/KeysOptionsMenu.h" #include "menus/options/MouseOptionsMenu.h" #include "menus/options/AvOptionsMenu.h" @@ -28,6 +29,7 @@ void MenuDirectory::Populate() AddToMap(); AddToMap(); AddToMap(); + AddToMap(); } void MenuDirectory::Clear() diff --git a/game/game_libs/ui_new/src/menus/StyleGuide.cpp b/game/game_libs/ui_new/src/menus/StyleGuide.cpp new file mode 100644 index 00000000..a2c3634f --- /dev/null +++ b/game/game_libs/ui_new/src/menus/StyleGuide.cpp @@ -0,0 +1,6 @@ +#include "menus/StyleGuide.h" + +StyleGuide::StyleGuide() : + MenuPage("style_guide", "resource/rml/style_guide.rml") +{ +} diff --git a/game/game_libs/ui_new/src/menus/StyleGuide.h b/game/game_libs/ui_new/src/menus/StyleGuide.h new file mode 100644 index 00000000..04af438c --- /dev/null +++ b/game/game_libs/ui_new/src/menus/StyleGuide.h @@ -0,0 +1,11 @@ +#pragma once + +#include "framework/MenuPage.h" +#include "framework/MenuEventListenerObject.h" +#include "templatebindings/MenuFrameDataBinding.h" + +class StyleGuide : public MenuPage +{ +public: + StyleGuide(); +}; From c396db44ffbbe814d8aa324e2e842313adf4620a Mon Sep 17 00:00:00 2001 From: Vesper <3692034+noodlecollie@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:49:06 +0100 Subject: [PATCH 02/11] Allowed customising the menu frame --- game/content-hash.txt | 2 +- .../game_libs/ui_new/src/menus/StyleGuide.cpp | 10 ++- game/game_libs/ui_new/src/menus/StyleGuide.h | 6 ++ .../templatebindings/MenuFrameDataBinding.cpp | 76 +++++++++++++++++-- .../templatebindings/MenuFrameDataBinding.h | 16 +++- 5 files changed, 100 insertions(+), 10 deletions(-) diff --git a/game/content-hash.txt b/game/content-hash.txt index 6bb8d8f9..02dce0c8 100644 --- a/game/content-hash.txt +++ b/game/content-hash.txt @@ -1 +1 @@ -style-guide-impl-ad77ccc80400dcee12c006415edcb55903738767 +style-guide-impl-e33184a1016da50e09d4e9a6ca4b2516c4015237 diff --git a/game/game_libs/ui_new/src/menus/StyleGuide.cpp b/game/game_libs/ui_new/src/menus/StyleGuide.cpp index a2c3634f..0ff4d916 100644 --- a/game/game_libs/ui_new/src/menus/StyleGuide.cpp +++ b/game/game_libs/ui_new/src/menus/StyleGuide.cpp @@ -1,6 +1,14 @@ #include "menus/StyleGuide.h" StyleGuide::StyleGuide() : - MenuPage("style_guide", "resource/rml/style_guide.rml") + MenuPage("style_guide", "resource/rml/style_guide.rml"), + m_MenuFrameDataBinding(this) { } + +void StyleGuide::OnDocumentLoaded() +{ + m_MenuFrameDataBinding.SetDefaultTooltipText("None"); + m_MenuFrameDataBinding.SetTooltipInnerRml("Colour constant: {{footerTooltip}}"); + m_MenuFrameDataBinding.SetTitle("Style Guide"); +} diff --git a/game/game_libs/ui_new/src/menus/StyleGuide.h b/game/game_libs/ui_new/src/menus/StyleGuide.h index 04af438c..140ea3a5 100644 --- a/game/game_libs/ui_new/src/menus/StyleGuide.h +++ b/game/game_libs/ui_new/src/menus/StyleGuide.h @@ -8,4 +8,10 @@ class StyleGuide : public MenuPage { public: StyleGuide(); + +protected: + void OnDocumentLoaded() override; + +private: + MenuFrameDataBinding m_MenuFrameDataBinding; }; diff --git a/game/game_libs/ui_new/src/templatebindings/MenuFrameDataBinding.cpp b/game/game_libs/ui_new/src/templatebindings/MenuFrameDataBinding.cpp index 63bb5dc0..4a9c462e 100644 --- a/game/game_libs/ui_new/src/templatebindings/MenuFrameDataBinding.cpp +++ b/game/game_libs/ui_new/src/templatebindings/MenuFrameDataBinding.cpp @@ -6,20 +6,82 @@ MenuFrameDataBinding::MenuFrameDataBinding(BaseMenu* parentMenu) : BaseMenuObserver(parentMenu), m_Tooltip {"footerTooltip", ""}, + m_Title {"frameTitle", ""}, m_DocumentListener(parentMenu, this, &MenuFrameDataBinding::HandleDocumentHide, {Rml::EventId::Hide}), m_TooltipListener( parentMenu, this, &MenuFrameDataBinding::HandleMouseEvents, - "bigbutton[tooltip], button[tooltip], label[tooltip]", + "bigbutton[tooltip], button[tooltip], label[tooltip], .hover-tooltip[tooltip]", {Rml::EventId::Mouseover, Rml::EventId::Mouseout} ) { } +void MenuFrameDataBinding::SetTooltipInnerRml(const Rml::String& rml) +{ + if ( m_TooltipDisplayElement ) + { + m_TooltipDisplayElement->SetInnerRML(rml); + } + else + { + ASSERT(false); + Rml::Log::Message(Rml::Log::Type::LT_WARNING, "SetTooltipInnerRml: Tooltip display element was null"); + } +} + +Rml::String MenuFrameDataBinding::DefaultTooltipText() const +{ + return m_DefaultTooltipText; +} + +void MenuFrameDataBinding::SetDefaultTooltipText(Rml::String text) +{ + m_DefaultTooltipText = std::move(text); + + if ( !m_CurrentTooltipElement ) + { + // Call this if we're not currently showing a tooltip + ResetTooltip(); + } +} + +Rml::String MenuFrameDataBinding::Title() const +{ + return m_Title.value; +} + +void MenuFrameDataBinding::SetTitle(Rml::String title) +{ + if ( m_Title.value != title ) + { + m_Title.value = title; + DirtyVariable(m_Title.name); + } +} + +void MenuFrameDataBinding::DocumentLoaded(Rml::ElementDocument* document) +{ + m_TooltipDisplayElement = document->QuerySelector("#main_menu_footer_tooltip"); + + if ( !m_TooltipDisplayElement ) + { + Rml::Log::Message( + Rml::Log::Type::LT_WARNING, + "Could not find menu frame tooltip element with ID #main_menu_footer_tooltip" + ); + } +} + +void MenuFrameDataBinding::DocumentUnloaded(Rml::ElementDocument*) +{ + m_TooltipDisplayElement = nullptr; +} + bool MenuFrameDataBinding::SetUpDataModelBindings(Rml::DataModelConstructor& constructor) { - if ( !constructor.Bind(m_Tooltip.name, &m_Tooltip.value) ) + if ( !constructor.Bind(m_Tooltip.name, &m_Tooltip.value) || !constructor.Bind(m_Title.name, &m_Title.value) ) { return false; } @@ -30,7 +92,7 @@ bool MenuFrameDataBinding::SetUpDataModelBindings(Rml::DataModelConstructor& con void MenuFrameDataBinding::HandleDocumentHide(Rml::Event&) { // The document is being hidden, so forcibly clear the tooltip. - ClearTooltip(); + ResetTooltip(); } void MenuFrameDataBinding::HandleMouseEvents(Rml::Event& event) @@ -49,7 +111,7 @@ void MenuFrameDataBinding::HandleMouseEvents(Rml::Event& event) if ( element && element == m_CurrentTooltipElement ) { - ClearTooltip(); + ResetTooltip(); } break; @@ -93,13 +155,13 @@ void MenuFrameDataBinding::SetTooltip(Rml::Event& event) } } -void MenuFrameDataBinding::ClearTooltip() +void MenuFrameDataBinding::ResetTooltip() { m_CurrentTooltipElement = nullptr; - if ( !m_Tooltip.value.empty() ) + if ( m_Tooltip.value != m_DefaultTooltipText ) { - m_Tooltip.value.clear(); + m_Tooltip.value = m_DefaultTooltipText; DirtyVariable(m_Tooltip.name); } } diff --git a/game/game_libs/ui_new/src/templatebindings/MenuFrameDataBinding.h b/game/game_libs/ui_new/src/templatebindings/MenuFrameDataBinding.h index 98f68773..34e390b8 100644 --- a/game/game_libs/ui_new/src/templatebindings/MenuFrameDataBinding.h +++ b/game/game_libs/ui_new/src/templatebindings/MenuFrameDataBinding.h @@ -10,17 +10,31 @@ class MenuFrameDataBinding : private BaseMenuObserver public: MenuFrameDataBinding(BaseMenu* parentMenu); + void SetTooltipInnerRml(const Rml::String& rml); + + Rml::String DefaultTooltipText() const; + void SetDefaultTooltipText(Rml::String text); + + Rml::String Title() const; + void SetTitle(Rml::String title); + protected: + void DocumentLoaded(Rml::ElementDocument* document) override; + void DocumentUnloaded(Rml::ElementDocument* document) override; bool SetUpDataModelBindings(Rml::DataModelConstructor& constructor) override; private: void HandleDocumentHide(Rml::Event& event); void HandleMouseEvents(Rml::Event& event); void SetTooltip(Rml::Event& event); - void ClearTooltip(); + void ResetTooltip(); DataVar m_Tooltip; + DataVar m_Title; + MenuEventListenerObject m_DocumentListener; MenuEventListenerObject m_TooltipListener; Rml::Element* m_CurrentTooltipElement = nullptr; + Rml::Element* m_TooltipDisplayElement = nullptr; + Rml::String m_DefaultTooltipText; }; From 025e2ef1a51b76182f6b6c76abdb2264672ed21a Mon Sep 17 00:00:00 2001 From: Vesper <3692034+noodlecollie@users.noreply.github.com> Date: Tue, 23 Jun 2026 07:53:03 +0100 Subject: [PATCH 03/11] Fixed incorrect path --- game/game_libs/ui_new/src/game/Utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/game_libs/ui_new/src/game/Utils.cpp b/game/game_libs/ui_new/src/game/Utils.cpp index d0c6dafa..260059dd 100644 --- a/game/game_libs/ui_new/src/game/Utils.cpp +++ b/game/game_libs/ui_new/src/game/Utils.cpp @@ -1,6 +1,6 @@ #include "game/Utils.h" #include -#include "Utils/InFilePtr.h" +#include "utils/InFilePtr.h" #include "udll_int.h" Rml::String EscapeStringForConsoleCommand(Rml::String input) From a51141950ce2fb2d22f9f6c841fb46417746ee52 Mon Sep 17 00:00:00 2001 From: Vesper <3692034+noodlecollie@users.noreply.github.com> Date: Tue, 23 Jun 2026 09:11:31 +0100 Subject: [PATCH 04/11] Enabled UI debugger only in debug builds --- game/content-hash.txt | 2 +- game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/game/content-hash.txt b/game/content-hash.txt index 02dce0c8..17c79577 100644 --- a/game/content-hash.txt +++ b/game/content-hash.txt @@ -1 +1 @@ -style-guide-impl-e33184a1016da50e09d4e9a6ca4b2516c4015237 +style-guide-impl-7fca47fee5d96e50c7308927a3312a39fbf8302f-dirty diff --git a/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp b/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp index 2c850f7d..86362a79 100644 --- a/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp +++ b/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp @@ -66,7 +66,10 @@ void RmlUiBackend::Initialise() return; } +#ifdef _DEBUG Rml::Debugger::Initialise(m_RmlContext); +#endif + m_MenuDirectory.LoadAllMenus(*m_RmlContext); m_Initialised = true; @@ -585,7 +588,9 @@ void RmlUiBackend::Render() void RmlUiBackend::ReleaseResources() { +#ifdef _DEBUG Rml::Debugger::Shutdown(); +#endif if ( m_RmlContext ) { From 0c5e5711074c4dcd7b7f1a472cb557cd22847a59 Mon Sep 17 00:00:00 2001 From: Vesper <3692034+noodlecollie@users.noreply.github.com> Date: Tue, 23 Jun 2026 12:47:58 +0100 Subject: [PATCH 05/11] Added function to reload current menu --- .../ui_new/src/framework/MenuDirectory.cpp | 122 +++++++++++++++--- .../ui_new/src/framework/MenuDirectory.h | 17 ++- .../ui_new/src/rmlui/RmlUiBackend.cpp | 30 ++++- .../game_libs/ui_new/src/rmlui/RmlUiBackend.h | 1 + 4 files changed, 138 insertions(+), 32 deletions(-) diff --git a/game/game_libs/ui_new/src/framework/MenuDirectory.cpp b/game/game_libs/ui_new/src/framework/MenuDirectory.cpp index 3aac9502..ff66bf0d 100644 --- a/game/game_libs/ui_new/src/framework/MenuDirectory.cpp +++ b/game/game_libs/ui_new/src/framework/MenuDirectory.cpp @@ -1,6 +1,7 @@ #include "framework/MenuDirectory.h" #include "framework/BaseMenu.h" #include +#include #include "UIDebug.h" #include "menus/MainMenu.h" @@ -17,6 +18,14 @@ void MenuDirectory::Populate() { + ASSERT(!m_Context); + + // We cannot already be set up. + if ( m_Context ) + { + return; + } + m_MenuMap.clear(); AddToMap(); @@ -32,23 +41,38 @@ void MenuDirectory::Populate() AddToMap(); } -void MenuDirectory::Clear() +void MenuDirectory::AcquireContext(Rml::Context* context) { - m_MenuMap.clear(); -} + ReleaseContext(); + + m_Context = context; + + if ( !m_Context ) + { + ASSERT(false); + return; + } -void MenuDirectory::LoadAllMenus(Rml::Context& context) -{ - // Do two passes, one for each operation, to make the logic clear. for ( MenuMap::iterator it = m_MenuMap.begin(); it != m_MenuMap.end(); ++it ) { - SetUpDataBindings(it->second, context); + SetUpDataBindings(it->second); } for ( MenuMap::iterator it = m_MenuMap.begin(); it != m_MenuMap.end(); ++it ) { - LoadMenuRml(it->second, context); + LoadMenuRml(it->second); + } +} + +void MenuDirectory::ReleaseContext() +{ + if ( !m_Context ) + { + return; } + + UnloadAllMenus(); + m_Context = nullptr; } const MenuDirectoryEntry* MenuDirectory::GetMenuEntry(const Rml::String& name) const @@ -57,16 +81,50 @@ const MenuDirectoryEntry* MenuDirectory::GetMenuEntry(const Rml::String& name) c return it != m_MenuMap.end() ? &it->second.menuEntry : nullptr; } +void MenuDirectory::ReloadMenu(const Rml::String& name, bool reloadModel) +{ + ASSERT(m_Context); + + if ( !m_Context ) + { + return; + } + + MenuMap::iterator it = m_MenuMap.find(name); + + if ( it == m_MenuMap.end() ) + { + return; + } + + MapEntry& entry = it->second; + const bool wasVisible = entry.menuEntry.document && entry.menuEntry.document->IsVisible(); + + UnloadMenu(entry, reloadModel); + + if ( reloadModel ) + { + SetUpDataBindings(entry); + } + + LoadMenuRml(entry); + + if ( wasVisible && entry.menuEntry.document ) + { + entry.menuEntry.document->Show(); + } +} + void MenuDirectory::AddToMap(BaseMenu* newMenu) { m_MenuMap.insert({Rml::String(newMenu->Name()), MapEntry {MenuDirectoryEntry(std::unique_ptr(newMenu))}}); } -void MenuDirectory::SetUpDataBindings(MapEntry& entry, Rml::Context& context) +void MenuDirectory::SetUpDataBindings(MapEntry& entry) { - Rml::String dataModelName = entry.menuEntry.menuPtr->Name() + Rml::String("_model"); + ASSERT(m_Context); - Rml::DataModelConstructor constructor = context.CreateDataModel(dataModelName); + Rml::DataModelConstructor constructor = m_Context->CreateDataModel(entry.menuEntry.dataModelName); bool success = false; if ( constructor ) @@ -89,18 +147,18 @@ void MenuDirectory::SetUpDataBindings(MapEntry& entry, Rml::Context& context) Rml::Log::Message( Rml::Log::Type::LT_ERROR, "Failed to construct data model \"%s\" for menu %s", - dataModelName.c_str(), + entry.menuEntry.dataModelName.c_str(), entry.menuEntry.menuPtr->Name() ); } if ( !success ) { - context.RemoveDataModel(dataModelName); + m_Context->RemoveDataModel(entry.menuEntry.dataModelName); } } -void MenuDirectory::LoadMenuRml(MapEntry& entry, Rml::Context& context) +void MenuDirectory::LoadMenuRml(MapEntry& entry) { static const char* FALLBACK_RML = "\n" @@ -121,8 +179,10 @@ void MenuDirectory::LoadMenuRml(MapEntry& entry, Rml::Context& context) "\n" "\n"; + ASSERT(m_Context); + entry.loadedDocument = false; - entry.menuEntry.document = context.LoadDocument(entry.menuEntry.menuPtr->RmlFilePath()); + entry.menuEntry.document = m_Context->LoadDocument(entry.menuEntry.menuPtr->RmlFilePath()); if ( entry.menuEntry.document ) { @@ -138,17 +198,37 @@ void MenuDirectory::LoadMenuRml(MapEntry& entry, Rml::Context& context) entry.menuEntry.menuPtr->Name() ); - entry.menuEntry.document = context.LoadDocumentFromMemory(FALLBACK_RML); + entry.menuEntry.document = m_Context->LoadDocumentFromMemory(FALLBACK_RML); ASSERT(entry.menuEntry.document); } -void MenuDirectory::UnloadAllDocuments() +void MenuDirectory::UnloadMenu(MapEntry& entry, bool unloadModel) { + ASSERT(m_Context); + + if ( entry.loadedDocument ) + { + ASSERT(entry.menuEntry.document); + + entry.menuEntry.menuPtr->DocumentUnloaded(); + m_Context->UnloadDocument(entry.menuEntry.document); + + entry.menuEntry.document = nullptr; + entry.loadedDocument = false; + } + + if ( unloadModel ) + { + m_Context->RemoveDataModel(entry.menuEntry.dataModelName); + } +} + +void MenuDirectory::UnloadAllMenus() +{ + ASSERT(m_Context); + for ( MenuMap::iterator it = m_MenuMap.begin(); it != m_MenuMap.end(); ++it ) { - if ( it->second.loadedDocument ) - { - it->second.menuEntry.menuPtr->DocumentUnloaded(); - } + UnloadMenu(it->second, true); } } diff --git a/game/game_libs/ui_new/src/framework/MenuDirectory.h b/game/game_libs/ui_new/src/framework/MenuDirectory.h index e78217ff..c17a8974 100644 --- a/game/game_libs/ui_new/src/framework/MenuDirectory.h +++ b/game/game_libs/ui_new/src/framework/MenuDirectory.h @@ -16,6 +16,7 @@ struct MenuDirectoryEntry { std::unique_ptr menuPtr; Rml::ElementDocument* document = nullptr; + Rml::String dataModelName; template T* MenuDynamicCast(bool assertSuccessInDebug = true) const @@ -38,7 +39,8 @@ struct MenuDirectoryEntry friend class MenuDirectory; explicit MenuDirectoryEntry(std::unique_ptr&& ptr) : - menuPtr(std::move(ptr)) + menuPtr(std::move(ptr)), + dataModelName(menuPtr->Name() + Rml::String("_model")) { } }; @@ -47,11 +49,12 @@ class MenuDirectory { public: void Populate(); - void Clear(); - void LoadAllMenus(Rml::Context& context); + void AcquireContext(Rml::Context* context); + void ReleaseContext(); const MenuDirectoryEntry* GetMenuEntry(const Rml::String& name) const; + void ReloadMenu(const Rml::String& name, bool reloadModel = false); template T* GetMenu(const Rml::String& name, bool assertSuccessInDebug = true) const @@ -89,9 +92,11 @@ class MenuDirectory } void AddToMap(BaseMenu* newMenu); - void SetUpDataBindings(MapEntry& entry, Rml::Context& context); - void LoadMenuRml(MapEntry& entry, Rml::Context& context); - void UnloadAllDocuments(); + void SetUpDataBindings(MapEntry& entry); + void LoadMenuRml(MapEntry& entry); + void UnloadMenu(MapEntry& entry, bool unloadModel); + void UnloadAllMenus(); MenuMap m_MenuMap; + Rml::Context* m_Context = nullptr; }; diff --git a/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp b/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp index 86362a79..b357d08f 100644 --- a/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp +++ b/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp @@ -70,8 +70,7 @@ void RmlUiBackend::Initialise() Rml::Debugger::Initialise(m_RmlContext); #endif - m_MenuDirectory.LoadAllMenus(*m_RmlContext); - + m_MenuDirectory.AcquireContext(m_RmlContext); m_Initialised = true; } @@ -247,13 +246,19 @@ void RmlUiBackend::ReceiveKey(int key, bool pressed) Rml::Input::KeyIdentifier rmlKey = EngineKeyToRmlKey(key); - // TODO: A better solution for this? + // TODO: A better solution for these bindings #ifdef _DEBUG if ( rmlKey == Rml::Input::KeyIdentifier::KI_F1 && pressed && (m_Modifiers & (Rml::Input::KeyModifier::KM_CTRL | Rml::Input::KeyModifier::KM_SHIFT)) ) { Rml::Debugger::SetVisible(!Rml::Debugger::IsVisible()); } + + if ( rmlKey == Rml::Input::KeyIdentifier::KI_F2 && pressed && + (m_Modifiers & (Rml::Input::KeyModifier::KM_CTRL | Rml::Input::KeyModifier::KM_SHIFT)) ) + { + ReloadCurrentMenu(); + } #endif if ( rmlKey == Rml::Input::KeyIdentifier::KI_UNKNOWN ) @@ -592,14 +597,14 @@ void RmlUiBackend::ReleaseResources() Rml::Debugger::Shutdown(); #endif + m_MenuDirectory.ReleaseContext(); + if ( m_RmlContext ) { - m_RmlContext->UnloadAllDocuments(); Rml::RemoveContext(CONTEXT_NAME); m_RmlContext = nullptr; } - m_MenuDirectory.Clear(); Rml::ReleaseFontResources(); } @@ -695,3 +700,18 @@ void RmlUiBackend::HandleMenuPopCommand() m_MenuStack.CommandPopMenu(replacementMenuName); } + +void RmlUiBackend::ReloadCurrentMenu() +{ + const MenuDirectoryEntry* entry = m_MenuStack.Top(); + + if ( !entry || !entry->menuPtr ) + { + return; + } + + const Rml::String menuName = entry->menuPtr->Name(); + + Rml::Log::Message(Rml::Log::Type::LT_INFO, "Reloading menu: %s", menuName.c_str()); + m_MenuDirectory.ReloadMenu(menuName); +} diff --git a/game/game_libs/ui_new/src/rmlui/RmlUiBackend.h b/game/game_libs/ui_new/src/rmlui/RmlUiBackend.h index 02f418cb..94d668c2 100644 --- a/game/game_libs/ui_new/src/rmlui/RmlUiBackend.h +++ b/game/game_libs/ui_new/src/rmlui/RmlUiBackend.h @@ -85,6 +85,7 @@ class RmlUiBackend void HandleMenuPushCommand(); void HandleMenuPopCommand(); + void ReloadCurrentMenu(); SystemInterfaceImpl m_SystemInterface; RenderInterfaceImpl m_RenderInterface; From e8b9b95db13e57a04a13ba4021427a90b4ffdd0d Mon Sep 17 00:00:00 2001 From: Vesper <3692034+noodlecollie@users.noreply.github.com> Date: Thu, 25 Jun 2026 07:25:44 +0100 Subject: [PATCH 06/11] Added colour codes to swatches --- game/content-hash.txt | 2 +- .../game_libs/ui_new/src/menus/StyleGuide.cpp | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/game/content-hash.txt b/game/content-hash.txt index 17c79577..f689da4f 100644 --- a/game/content-hash.txt +++ b/game/content-hash.txt @@ -1 +1 @@ -style-guide-impl-7fca47fee5d96e50c7308927a3312a39fbf8302f-dirty +style-guide-impl-5c09932a0af45354d50394b88ea0cf568cdf1ca2 diff --git a/game/game_libs/ui_new/src/menus/StyleGuide.cpp b/game/game_libs/ui_new/src/menus/StyleGuide.cpp index 0ff4d916..da48656e 100644 --- a/game/game_libs/ui_new/src/menus/StyleGuide.cpp +++ b/game/game_libs/ui_new/src/menus/StyleGuide.cpp @@ -1,4 +1,7 @@ #include "menus/StyleGuide.h" +#include "framework/ElementFinder.h" +#include +#include StyleGuide::StyleGuide() : MenuPage("style_guide", "resource/rml/style_guide.rml"), @@ -11,4 +14,23 @@ void StyleGuide::OnDocumentLoaded() m_MenuFrameDataBinding.SetDefaultTooltipText("None"); m_MenuFrameDataBinding.SetTooltipInnerRml("Colour constant: {{footerTooltip}}"); m_MenuFrameDataBinding.SetTitle("Style Guide"); + + ElementFinder finder; + + Rml::Element* table = Document()->GetElementById("colours_table"); + Rml::ElementList swatches; + finder.AddMulti(&table, ".palette-swatch", &swatches); + finder.FindAll(); + + for ( Rml::Element* swatch : swatches ) + { + const Rml::Property* bgProp = swatch->GetProperty(Rml::PropertyId::BackgroundColor); + + if ( !bgProp ) + { + continue; + } + + swatch->SetInnerRML(Rml::StringUtilities::EncodeRml(bgProp->ToString())); + } } From ada67bac3c2597ae8594dbead3701a68e76de939 Mon Sep 17 00:00:00 2001 From: Vesper <3692034+noodlecollie@users.noreply.github.com> Date: Fri, 26 Jun 2026 14:23:23 +0100 Subject: [PATCH 07/11] Fixed blending function --- xash3d_engine/engine/ref/gl/src/gl_ui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xash3d_engine/engine/ref/gl/src/gl_ui.c b/xash3d_engine/engine/ref/gl/src/gl_ui.c index 2fd04aee..cab8b4f1 100644 --- a/xash3d_engine/engine/ref/gl/src/gl_ui.c +++ b/xash3d_engine/engine/ref/gl/src/gl_ui.c @@ -70,7 +70,7 @@ void GL_UI_BeginFrame(const struct ref_viewpass_s* rvp) pglDisableClientState(GL_TEXTURE_COORD_ARRAY); pglEnable(GL_BLEND); - pglBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + pglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); pglEnable(GL_STENCIL_TEST); pglStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF); From ad42f0ea49f0a5fb12bf1c59439dd568d7bc34b1 Mon Sep 17 00:00:00 2001 From: Vesper <3692034+noodlecollie@users.noreply.github.com> Date: Sat, 27 Jun 2026 11:05:27 +0100 Subject: [PATCH 08/11] Updated content hash --- game/content-hash.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/content-hash.txt b/game/content-hash.txt index f689da4f..f2b8f58a 100644 --- a/game/content-hash.txt +++ b/game/content-hash.txt @@ -1 +1 @@ -style-guide-impl-5c09932a0af45354d50394b88ea0cf568cdf1ca2 +style-guide-impl-7f11b93a58e0737ae5478f0d05ec86904a50d1c9 From f79408d0d2d0780896123875f267128d9d1096b1 Mon Sep 17 00:00:00 2001 From: Vesper <3692034+noodlecollie@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:37:01 +0100 Subject: [PATCH 09/11] Refactored DPI calculations --- game/content-hash.txt | 2 +- .../ui_new/src/rmlui/RmlUiBackend.cpp | 38 ++++++++++++------- .../game_libs/ui_new/src/rmlui/RmlUiBackend.h | 2 + 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/game/content-hash.txt b/game/content-hash.txt index f2b8f58a..54cbd470 100644 --- a/game/content-hash.txt +++ b/game/content-hash.txt @@ -1 +1 @@ -style-guide-impl-7f11b93a58e0737ae5478f0d05ec86904a50d1c9 +style-guide-impl-473aa172413d2a0026ef4dc8af43c0722fa1e7f5 diff --git a/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp b/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp index b357d08f..1a938fc4 100644 --- a/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp +++ b/game/game_libs/ui_new/src/rmlui/RmlUiBackend.cpp @@ -83,19 +83,7 @@ bool RmlUiBackend::VidInit(int width, int height) m_RenderInterface.SetViewport(width, height); m_RmlContext->SetDimensions(Rml::Vector2i(width, height)); - - float dpiScale = 1.0f; - - if ( height >= 2160 ) - { - dpiScale = 2.0f; - } - else if ( height >= 1080 ) - { - dpiScale = 1.5f; - } - - m_RmlContext->SetDensityIndependentPixelRatio(dpiScale); + m_RmlContext->SetDensityIndependentPixelRatio(CalculateDpiScale(width, height)); return true; } @@ -715,3 +703,27 @@ void RmlUiBackend::ReloadCurrentMenu() Rml::Log::Message(Rml::Log::Type::LT_INFO, "Reloading menu: %s", menuName.c_str()); m_MenuDirectory.ReloadMenu(menuName); } + +float RmlUiBackend::CalculateDpiScale(int /* width */, int height) +{ + static const Rml::Vector2i WIDE_4K = {3840, 2160}; + static const Rml::Vector2i WIDE_FHD = {1920, 1080}; + static const Rml::Vector2i WIDE_WXGA = {1280, 720}; + + if ( height >= WIDE_4K.y ) + { + return 2.0f; + } + else if ( height >= WIDE_FHD.y ) + { + return 1.5f; + } + else if ( height >= WIDE_WXGA.y ) + { + return 1.0f; + } + else + { + return 0.5f; + } +} diff --git a/game/game_libs/ui_new/src/rmlui/RmlUiBackend.h b/game/game_libs/ui_new/src/rmlui/RmlUiBackend.h index 94d668c2..ee97e0d3 100644 --- a/game/game_libs/ui_new/src/rmlui/RmlUiBackend.h +++ b/game/game_libs/ui_new/src/rmlui/RmlUiBackend.h @@ -87,6 +87,8 @@ class RmlUiBackend void HandleMenuPopCommand(); void ReloadCurrentMenu(); + static float CalculateDpiScale(int width, int height); + SystemInterfaceImpl m_SystemInterface; RenderInterfaceImpl m_RenderInterface; FileInterfaceImpl m_FileInterface; From 80106764a97cbc7b933b43a4f200f474da6e5fb0 Mon Sep 17 00:00:00 2001 From: Vesper <3692034+noodlecollie@users.noreply.github.com> Date: Sat, 15 Aug 2026 12:41:38 +0100 Subject: [PATCH 10/11] Merging this for now --- game/content-hash.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/content-hash.txt b/game/content-hash.txt index 54cbd470..1f099dd4 100644 --- a/game/content-hash.txt +++ b/game/content-hash.txt @@ -1 +1 @@ -style-guide-impl-473aa172413d2a0026ef4dc8af43c0722fa1e7f5 +master-ce858bc87812b4d7b5e905da455f2cd80d5249db From 16c11ef862001373e9e99890d5e14744ba3e9fcb Mon Sep 17 00:00:00 2001 From: Vesper <3692034+noodlecollie@users.noreply.github.com> Date: Sat, 15 Aug 2026 13:20:02 +0100 Subject: [PATCH 11/11] Fixed vcpkg build --- .github/workflows/CI-Dedicated-Server.yaml | 12 +++++++++++- .github/workflows/CI-Game.yaml | 12 +++++++++++- readme.md | 10 ++++++++++ vcpkg | 2 +- vcpkg.json | 1 + 5 files changed, 34 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI-Dedicated-Server.yaml b/.github/workflows/CI-Dedicated-Server.yaml index 9a244c76..db1bb645 100644 --- a/.github/workflows/CI-Dedicated-Server.yaml +++ b/.github/workflows/CI-Dedicated-Server.yaml @@ -16,7 +16,17 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt update && sudo apt install -y libsdl2-dev + run: | + sudo apt update + sudo apt install -y \ + build-essential \ + mesa-utils \ + libsdl2-dev \ + autoconf \ + automake \ + autoconf-archive \ + libtool \ + python3-jinja2 - name: Build uses: ashutoshvarma/action-cmake-build@master diff --git a/.github/workflows/CI-Game.yaml b/.github/workflows/CI-Game.yaml index c573e7bc..3489391f 100644 --- a/.github/workflows/CI-Game.yaml +++ b/.github/workflows/CI-Game.yaml @@ -17,7 +17,17 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt update && sudo apt install -y libsdl2-dev + run: | + sudo apt update + sudo apt install -y \ + build-essential \ + mesa-utils \ + libsdl2-dev \ + autoconf \ + automake \ + autoconf-archive \ + libtool \ + python3-jinja2 - name: Build uses: ashutoshvarma/action-cmake-build@master diff --git a/readme.md b/readme.md index 40223729..36a59528 100644 --- a/readme.md +++ b/readme.md @@ -75,6 +75,16 @@ cd build\install\nightfire-open bond.exe ``` +### Troubleshooting `vcpkg` + +If `vcpkg` goes stale, dependencies may fail to download or build. To update `vcpkg`: + +1. Check https://github.com/microsoft/vcpkg/releases for the latest release. +2. In the `vcpkg` submodule directory, run `git checkout tags/` to update to the release tag you want to use. +3. Update the `builtin-baseline` hash in `vcpkg.json` to the commit hash for the release chosen above. +4. From the root of the repo, run `.\vcpkg\bootstrap-vcpkg.bat` (for Windows), or `./vcpkg/bootstrap-vcpkg.sh` (for Linux) to ensure that the `vcpkg` executable is up to date. +5. Delete your build directory, and rebuild from scratch. + ## About The primary purpose of this game is to recreate and improve the experience of the PC version of Nightfire, and to provide in the process an open source codebase that will allow for extension and refinement. diff --git a/vcpkg b/vcpkg index b02e341c..9e593bb1 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit b02e341c927f16d991edbd915d8ea43eac52096c +Subproject commit 9e593bb18ea69cc5095e012465dcd675a822ed0d diff --git a/vcpkg.json b/vcpkg.json index 4e3e4d50..8aaeeae6 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,7 @@ { "name": "nightfire-open", "version-string": "0.0.1", + "builtin-baseline": "9e593bb18ea69cc5095e012465dcd675a822ed0d", "dependencies": [ "sdl2", "freetype",