From 0d31d3f357b9cf6548b8dd14718f612e498676a8 Mon Sep 17 00:00:00 2001 From: skuzzis Date: Tue, 21 Jan 2025 01:29:48 +0000 Subject: [PATCH 1/2] update(config): Add Kinds --- plugin_files/configs/core.json | 4 +++- src/server/configuration/Configuration.cpp | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugin_files/configs/core.json b/plugin_files/configs/core.json index 68ea1f8be..9b89e2b33 100644 --- a/plugin_files/configs/core.json +++ b/plugin_files/configs/core.json @@ -39,6 +39,8 @@ "option": false, "button": "tab" } - } + }, + "kind": "screen", + "available_kinds": ["screen", "center"] } } \ No newline at end of file diff --git a/src/server/configuration/Configuration.cpp b/src/server/configuration/Configuration.cpp index 16d1e1889..2c252432b 100644 --- a/src/server/configuration/Configuration.cpp +++ b/src/server/configuration/Configuration.cpp @@ -474,6 +474,9 @@ bool Configuration::LoadConfiguration() RegisterConfiguration(wasEdited, coreConfigFile, "core", "core", "menu.buttons.exit.option", false); RegisterConfiguration(wasEdited, coreConfigFile, "core", "core", "menu.buttons.exit.button", "tab"); + RegisterConfiguration(wasEdited, coreConfigFile, "core", "core", "menu.kind", "screen"); + RegisterConfigurationVector(wasEdited, coreConfigFile, "core", "core", "menu.available_kinds", {"screen", "center"}, true, " "); + if (wasEdited) { WritePluginFile("addons/swiftly/configs/core.json", coreConfigFile); From 5d60ae0bc28554974a1a1cd919578357dbecc021 Mon Sep 17 00:00:00 2001 From: skuzzis Date: Tue, 21 Jan 2025 17:20:03 +0000 Subject: [PATCH 2/2] feat(core): Menu Renderer --- .../translations/translation.core.json | 16 +- src/entrypoint.cpp | 4 +- src/player/player/Player.cpp | 195 +------------ src/player/player/Player.h | 23 +- src/plugins/core/scripting.h | 4 +- .../core/scripting/engine/gameevents.cpp | 2 +- src/plugins/core/scripting/player/player.cpp | 18 +- src/plugins/core/scripting/server/menus.cpp | 8 +- src/server/menus/Menu.h | 33 +-- src/server/menus/MenuManager.cpp | 20 +- src/server/menus/MenuManager.h | 5 +- src/server/menus/MenuRenderer.cpp | 256 ++++++++++++++++++ src/server/menus/MenuRenderer.h | 51 ++++ src/server/menus/kinds/CenterMenu.cpp | 145 ++++++++++ src/server/menus/kinds/CenterMenu.h | 36 +++ .../menus/{Menu.cpp => kinds/ScreenMenu.cpp} | 44 +-- src/server/menus/kinds/ScreenMenu.h | 36 +++ 17 files changed, 617 insertions(+), 279 deletions(-) create mode 100644 src/server/menus/MenuRenderer.cpp create mode 100644 src/server/menus/MenuRenderer.h create mode 100644 src/server/menus/kinds/CenterMenu.cpp create mode 100644 src/server/menus/kinds/CenterMenu.h rename src/server/menus/{Menu.cpp => kinds/ScreenMenu.cpp} (79%) create mode 100644 src/server/menus/kinds/ScreenMenu.h diff --git a/plugin_files/translations/translation.core.json b/plugin_files/translations/translation.core.json index 552fb22db..0cf4967e0 100644 --- a/plugin_files/translations/translation.core.json +++ b/plugin_files/translations/translation.core.json @@ -59,13 +59,25 @@ "pl": "Wyjście", "de": "Beenden" }, - "menu.footer": { + "menu.center.footer": { + "en": "{CYCLE_BUTTON} - Cycle | {USE_BUTTON} - Select | Page {PAGE}/{MAXPAGES}", + "ro": "{CYCLE_BUTTON} - Schimbă | {USE_BUTTON} - Selectează | Pagina {PAGE}/{MAXPAGES}", + "pl": "{CYCLE_BUTTON} - Przeglądaj | {USE_BUTTON} - Wybierz | Strona {PAGE}/{MAXPAGES}", + "de": "{CYCLE_BUTTON} - Durchblättern | {USE_BUTTON} - Auswählen | Seite {PAGE}/{MAXPAGES}" + }, + "menu.center.footer.nooption": { + "en": "{CYCLE_BUTTON} - Cycle | {USE_BUTTON} - Select | {EXIT_BUTTON} - Exit | Page {PAGE}/{MAXPAGES}", + "ro": "{CYCLE_BUTTON} - Schimbă | {USE_BUTTON} - Selectează | {EXIT_BUTTON} - Ieșire | Pagina {PAGE}/{MAXPAGES}", + "pl": "{CYCLE_BUTTON} - Przeglądaj | {USE_BUTTON} - Wybierz | {EXIT_BUTTON} - Wyjście | Strona {PAGE}/{MAXPAGES}", + "de": "{CYCLE_BUTTON} - Durchblättern | {USE_BUTTON} - Auswählen | {EXIT_BUTTON} - Beenden | Seite {PAGE}/{MAXPAGES}" + }, + "menu.screen.footer": { "en": "{CYCLE_BUTTON} - Cycle | {USE_BUTTON} - Select\nPage {PAGE}/{MAXPAGES}", "ro": "{CYCLE_BUTTON} - Schimbă | {USE_BUTTON} - Selectează\nPagina {PAGE}/{MAXPAGES}", "pl": "{CYCLE_BUTTON} - Przeglądaj | {USE_BUTTON} - Wybierz\nStrona {PAGE}/{MAXPAGES}", "de": "{CYCLE_BUTTON} - Durchblättern | {USE_BUTTON} - Auswählen\nSeite {PAGE}/{MAXPAGES}" }, - "menu.footer.nooption": { + "menu.screen.footer.nooption": { "en": "{CYCLE_BUTTON} - Cycle | {USE_BUTTON} - Select\n{EXIT_BUTTON} - Exit | Page {PAGE}/{MAXPAGES}", "ro": "{CYCLE_BUTTON} - Schimbă | {USE_BUTTON} - Selectează\n{EXIT_BUTTON} - Ieșire | Pagina {PAGE}/{MAXPAGES}", "pl": "{CYCLE_BUTTON} - Przeglądaj | {USE_BUTTON} - Wybierz\n{EXIT_BUTTON} - Wyjście | Strona {PAGE}/{MAXPAGES}", diff --git a/src/entrypoint.cpp b/src/entrypoint.cpp index 77eccb942..6beb24ab3 100644 --- a/src/entrypoint.cpp +++ b/src/entrypoint.cpp @@ -440,7 +440,9 @@ void Swiftly::Hook_GameFrame(bool simulating, bool bFirstTick, bool bLastTick) auto buttonStates = pawn->m_pMovementServices()->m_nButtons().m_pButtonStates(); player->SetButtons(buttonStates[0]); - if (player->HasCenterText()) + if(player->menu_renderer->ShouldRenderEachTick()) + player->menu_renderer->RenderMenuTick(); + else if (player->HasCenterText()) player->RenderCenterText(time); if(!pawn->m_pObserverServices()) continue; diff --git a/src/player/player/Player.cpp b/src/player/player/Player.cpp index 9847cf32f..e7119f8ad 100644 --- a/src/player/player/Player.cpp +++ b/src/player/player/Player.cpp @@ -67,6 +67,7 @@ Player::Player(bool m_isFakeClient, int m_slot, const char* m_name, uint64 m_xui this->language = g_Config->FetchValue("core.language"); centerMessageEvent = g_gameEventManager->CreateEvent("show_survival_respawn_status", true); + menu_renderer = new MenuRenderer(this); centerMessageEvent->SetUint64("duration", 1); centerMessageEvent->SetInt("userid", this->slot); @@ -78,18 +79,7 @@ Player::~Player() { this->isFakeClient = true; g_gameEventManager->FreeEvent(centerMessageEvent); - if(menuTextID != 0) { - g_pVGUI->DeleteScreenText(menuTextID); - } - if(menuPanelID != 0) { - g_pVGUI->DeleteScreenPanel(menuPanelID); - } - if(menuFooterID != 0) { - g_pVGUI->DeleteScreenText(menuFooterID); - } - if(menuPanelExtendID != 0) { - g_pVGUI->DeleteScreenPanel(menuPanelExtendID); - } + if(menu_renderer) delete menu_renderer; } CBasePlayerController* Player::GetController() @@ -375,187 +365,6 @@ void Player::SetButtons(uint64_t new_buttons) } } -bool Player::HasMenuShown() { return (this->menu != nullptr); } -Menu* Player::GetMenu() { return this->menu; } - -int Player::GetPage() { return this->page; } -void Player::SetPage(int pg) -{ - this->page = pg; - this->selected = 0; - this->menu->RegeneratePage(this->slot, this->page, this->selected); - this->RegenerateMenu(); -} -int Player::GetSelection() { return this->selected; } -void Player::MoveSelection() -{ - if (this->page == 0) - return; - - int itemsPerPage = this->menu->GetItemsOnPage(this->page); - ++this->selected; - if (itemsPerPage == this->selected) - this->selected = 0; - - this->menu->RegeneratePage(this->slot, this->page, this->selected); - this->RegenerateMenu(); -} - -void Player::RegenerateMenu() -{ - auto menuText = g_pVGUI->GetScreenText(menuTextID); - if(!menuText) return; - - menuText->SetText(this->menu->GeneratedItems(this->slot, this->page)); - - auto menuFooter = g_pVGUI->GetScreenText(menuFooterID); - if(!menuFooter) return; - menuFooter->SetText(this->menu->GenerateFooter(this->page)); -} - -void Player::ShowMenu(std::string menuid) -{ - if (this->menu != nullptr) - return; - - Menu* m = g_MenuManager->FetchMenu(menuid); - if (m == nullptr) - return; - - this->menu = m; - this->page = 1; - this->selected = 0; - - this->menu->RegeneratePage(this->slot, this->page, this->selected); - - menuTextID = g_pVGUI->RegisterScreenText(); - menuPanelID = g_pVGUI->RegisterScreenPanel(); - menuPanelExtendID = g_pVGUI->RegisterScreenPanel(); - menuFooterID = g_pVGUI->RegisterScreenText(); - - auto menuText = g_pVGUI->GetScreenText(menuTextID); - auto menuPanel = g_pVGUI->GetScreenPanel(menuPanelID); - auto menuPanelExtend = g_pVGUI->GetScreenPanel(menuPanelExtendID); - auto menuFooter = g_pVGUI->GetScreenText(menuFooterID); - - menuFooter->Create(Color(255,255,255,255)); - menuFooter->SetupViewForPlayer(this); - - menuText->Create(this->menu->GetColor()); - menuText->SetupViewForPlayer(this); - RegenerateMenu(); - menuText->SetPosition(0.14, 0.68); - - menuFooter->SetPosition(0.14, 0.27); - - menuPanel->Create(Color(18, 18, 18, 255)); - menuPanel->SetupViewForPlayer(this); - menuPanel->SetText("█"); - menuPanel->SetPosition(0.13, 0.7); - - menuPanelExtend->Create(Color(18, 18, 18, 255)); - menuPanelExtend->SetupViewForPlayer(this); - menuPanelExtend->SetText("█"); - menuPanelExtend->SetPosition(0.17, 0.7); -} - -void Player::HideMenu() -{ - if (this->menu == nullptr) - return; - - this->page = 0; - this->selected = 0; - if (this->menu->IsTemporary()) - { - std::string menuID = this->menu->GetID(); - g_MenuManager->UnregisterMenu(menuID); - } - this->menu = nullptr; - - g_pVGUI->DeleteScreenText(menuFooterID); - g_pVGUI->DeleteScreenText(menuTextID); - g_pVGUI->DeleteScreenPanel(menuPanelID); - g_pVGUI->DeleteScreenPanel(menuPanelExtendID); - - menuTextID = 0; - menuPanelID = 0; - menuFooterID = 0; - menuPanelExtendID = 0; -} - -void Player::PerformMenuAction(std::string button) -{ - if (!this->HasMenuShown()) - return; - - if (button == g_Config->FetchValue("core.menu.buttons.scroll")) - { - CCSPlayerController* controller = this->GetPlayerController(); - CSingleRecipientFilter filter(this->slot); - if (controller) - controller->EmitSoundFilter(filter, g_Config->FetchValue("core.menu.sound.scroll.name"), 1.0, g_Config->FetchValue("core.menu.sound.scroll.volume")); - - this->MoveSelection(); - this->RegenerateMenu(); - } - else if (!g_Config->FetchValue("core.menu.buttons.exit.option") && button == g_Config->FetchValue("core.menu.buttons.exit.button")) - { - CCSPlayerController* controller = this->GetPlayerController(); - CSingleRecipientFilter filter(this->slot); - if (controller) - controller->EmitSoundFilter(filter, g_Config->FetchValue("core.menu.sound.exit.name"), 1.0, g_Config->FetchValue("core.menu.sound.exit.volume")); - this->HideMenu(); - } - else if (button == g_Config->FetchValue("core.menu.buttons.use")) - { - std::string cmd = this->GetMenu()->GetCommandFromOption(this->GetPage(), this->GetSelection()); - CCSPlayerController* controller = this->GetPlayerController(); - CSingleRecipientFilter filter(this->slot); - if (controller && cmd != "menuexit") - controller->EmitSoundFilter(filter, g_Config->FetchValue("core.menu.sound.use.name"), 1.0, g_Config->FetchValue("core.menu.sound.use.volume")); - if (cmd == "menunext") - { - this->SetPage(this->GetPage() + 1); - this->RegenerateMenu(); - } - else if (cmd == "menuback") - { - this->SetPage(this->GetPage() - 1); - this->RegenerateMenu(); - } - else if (g_Config->FetchValue("core.menu.buttons.exit.option") && cmd == "menuexit") - { - CCSPlayerController* controller = this->GetPlayerController(); - CSingleRecipientFilter filter(this->slot); - if (controller) - controller->EmitSoundFilter(filter, g_Config->FetchValue("core.menu.sound.exit.name"), 1.0, g_Config->FetchValue("core.menu.sound.exit.volume")); - this->HideMenu(); - } - else if (g_MenuManager->FetchMenu(cmd)) - { - this->HideMenu(); - this->ShowMenu(cmd); - } - else if (starts_with(cmd, "sw_")) - { - CCommand tokenizedArgs; - tokenizedArgs.Tokenize(cmd.c_str()); - - std::vector cmdString = TokenizeCommand(cmd); - cmdString.erase(cmdString.begin()); - - std::string commandName = replace(tokenizedArgs[0], "sw_", ""); - - Command* cmd = g_commandsManager->FetchCommand(commandName); - if (cmd) - cmd->Execute(this->slot, cmdString, true, "sw_"); - } - else if (cmd != "") - this->PerformCommand(cmd); - } -} - void Player::PerformCommand(std::string command) { engine->ClientCommand(this->GetSlot(), command.c_str()); diff --git a/src/player/player/Player.h b/src/player/player/Player.h index c613cf4e4..f99e52273 100644 --- a/src/player/player/Player.h +++ b/src/player/player/Player.h @@ -8,6 +8,7 @@ #include "../../sdk/entity/CCSPlayerPawnBase.h" #include "../../sdk/entity/CBaseViewModel.h" #include "../../server/menus/Menu.h" +#include "../../server/menus/MenuRenderer.h" #include #include @@ -82,19 +83,6 @@ class Player bool HasCenterText(); - void ShowMenu(std::string menuid); - void HideMenu(); - bool HasMenuShown(); - Menu* GetMenu(); - void RegenerateMenu(); - - int GetPage(); - void SetPage(int pg); - int GetSelection(); - void MoveSelection(); - - void PerformMenuAction(std::string button); - void PerformCommand(std::string command); void SetClientConvar(std::string cmd, std::string val); @@ -112,6 +100,7 @@ class Player CPlayerBitVec m_selfMutes[64] = {}; std::string language = ""; + MenuRenderer* menu_renderer = nullptr; private: int slot; @@ -130,14 +119,6 @@ class Player bool firstSpawn = true; - Menu* menu = nullptr; - int page = 0; - int selected = 0; - uint64_t menuTextID = 0; - uint64_t menuFooterID = 0; - uint64_t menuPanelID = 0; - uint64_t menuPanelExtendID = 0; - uint64_t buttons = 0; std::map internalVars; diff --git a/src/plugins/core/scripting.h b/src/plugins/core/scripting.h index cdbb12705..e70e53e08 100644 --- a/src/plugins/core/scripting.h +++ b/src/plugins/core/scripting.h @@ -535,8 +535,8 @@ class PluginMenus public: PluginMenus(std::string m_plugin_name); - void Register(std::string custom_id, std::string title, std::string color, std::vector> options); - void RegisterTemporary(std::string custom_id, std::string title, std::string color, std::vector> options); + void Register(std::string custom_id, std::string title, std::string color, std::vector> options, std::optional okind); + void RegisterTemporary(std::string custom_id, std::string title, std::string color, std::vector> options, std::optional okind); void Unregister(std::string id); }; diff --git a/src/plugins/core/scripting/engine/gameevents.cpp b/src/plugins/core/scripting/engine/gameevents.cpp index 87f54fbb5..067bccd1e 100644 --- a/src/plugins/core/scripting/engine/gameevents.cpp +++ b/src/plugins/core/scripting/engine/gameevents.cpp @@ -13,7 +13,7 @@ void OnClientKeyStateChange(int playerid, std::string key, bool pressed) if (!player) return; - player->PerformMenuAction(key); + player->menu_renderer->PerformMenuAction(key); } if (noReturnEvent == nullptr) noReturnEvent = new PluginEvent("core", nullptr, nullptr); diff --git a/src/plugins/core/scripting/player/player.cpp b/src/plugins/core/scripting/player/player.cpp index e92f3500e..950365163 100644 --- a/src/plugins/core/scripting/player/player.cpp +++ b/src/plugins/core/scripting/player/player.cpp @@ -266,10 +266,10 @@ void PluginPlayer::HideMenu() if (!player) return; - if (!player->HasMenuShown()) + if (!player->menu_renderer->HasMenuShown()) return; - player->HideMenu(); + player->menu_renderer->HideMenu(); } bool PluginPlayer::IsFakeClient() @@ -331,10 +331,10 @@ void PluginPlayer::ShowMenu(std::string menuid) if (!player) return; - if (player->HasMenuShown()) + if (player->menu_renderer->HasMenuShown()) return; - player->ShowMenu(menuid); + player->menu_renderer->ShowMenu(menuid); } void PluginPlayer::SwitchTeam(int team) @@ -469,12 +469,12 @@ void PluginPlayer::PerformMenuAction(std::string action, int value) if (!self) return; if(action == "useOption") { - while(self->GetSelection() != value-1) - self->MoveSelection(); + while(self->menu_renderer->GetSelection() != value-1) + self->menu_renderer->MoveSelection(); - self->PerformMenuAction(g_Config->FetchValue("core.menu.buttons.use")); + self->menu_renderer->PerformMenuAction(g_Config->FetchValue("core.menu.buttons.use")); } else if(action == "scrollToOption") { - while(self->GetSelection() != value-1) - self->MoveSelection(); + while(self->menu_renderer->GetSelection() != value-1) + self->menu_renderer->MoveSelection(); } } \ No newline at end of file diff --git a/src/plugins/core/scripting/server/menus.cpp b/src/plugins/core/scripting/server/menus.cpp index 41b22c70c..51534284e 100644 --- a/src/plugins/core/scripting/server/menus.cpp +++ b/src/plugins/core/scripting/server/menus.cpp @@ -7,18 +7,18 @@ PluginMenus::PluginMenus(std::string m_plugin_name) this->plugin_name = m_plugin_name; } -void PluginMenus::Register(std::string custom_id, std::string title, std::string color, std::vector> options) +void PluginMenus::Register(std::string custom_id, std::string title, std::string color, std::vector> options, std::optional okind) { REGISTER_CALLSTACK(this->plugin_name, string_format("PluginMenus::Register(custom_id=\"%s\", title=\"%s\", color=\"%s\")", custom_id.c_str(), title.c_str(), color.c_str())); - g_MenuManager->RegisterMenu(this->plugin_name, custom_id, title, color, options, false); + g_MenuManager->RegisterMenu(this->plugin_name, custom_id, title, color, options, false, okind.value_or(g_Config->FetchValue("core.menu.kind"))); } -void PluginMenus::RegisterTemporary(std::string custom_id, std::string title, std::string color, std::vector> options) +void PluginMenus::RegisterTemporary(std::string custom_id, std::string title, std::string color, std::vector> options, std::optional okind) { REGISTER_CALLSTACK(this->plugin_name, string_format("PluginMenus::RegisterTemporary(custom_id=\"%s\", title=\"%s\", color=\"%s\")", custom_id.c_str(), title.c_str(), color.c_str())); - g_MenuManager->RegisterMenu(this->plugin_name, custom_id, title, color, options, true); + g_MenuManager->RegisterMenu(this->plugin_name, custom_id, title, color, options, true, okind.value_or(g_Config->FetchValue("core.menu.kind"))); } void PluginMenus::Unregister(std::string id) diff --git a/src/server/menus/Menu.h b/src/server/menus/Menu.h index ab266775a..673325ad6 100644 --- a/src/server/menus/Menu.h +++ b/src/server/menus/Menu.h @@ -8,29 +8,18 @@ class Menu { -private: - std::string id; - std::string title; - Color color; - std::vector> options; - std::vector>> processedOptions; - bool temporary; - - std::map> generatedPages; - public: - Menu(std::string id, std::string title, std::string color, std::vector> options, bool tmp); - ~Menu(); - - std::string GetID(); - void ProcessOptions(); - std::string GeneratedItems(int playerid, int page); - std::string GenerateFooter(int page); - void RegeneratePage(int playerid, int page, int selected); + virtual std::string GetID() = 0; + virtual void ProcessOptions() = 0; + virtual std::string GeneratedItems(int playerid, int page) = 0; + virtual std::string GenerateFooter(int page) = 0; + virtual void RegeneratePage(int playerid, int page, int selected) = 0; - std::string GetCommandFromOption(int page, int selected); - size_t GetItemsOnPage(int page); - bool IsTemporary(); + virtual std::string GetCommandFromOption(int page, int selected) = 0; + virtual size_t GetItemsOnPage(int page) = 0; + virtual bool IsTemporary() = 0; - Color GetColor(); + virtual Color GetColor() = 0; + virtual bool RenderEachTick() = 0; + virtual std::string GetKind() = 0; }; \ No newline at end of file diff --git a/src/server/menus/MenuManager.cpp b/src/server/menus/MenuManager.cpp index 5ccb1ca6e..461ebf1e6 100644 --- a/src/server/menus/MenuManager.cpp +++ b/src/server/menus/MenuManager.cpp @@ -1,6 +1,7 @@ #include "MenuManager.h" #include "../../player/playermanager/PlayerManager.h" +#include "../configuration/Configuration.h" std::map scheduledForDelete; @@ -8,12 +9,21 @@ MenuManager::MenuManager() { } -void MenuManager::RegisterMenu(std::string plugin_name, std::string id, std::string title, std::string color, std::vector> options, bool temporary) +void MenuManager::RegisterMenu(std::string plugin_name, std::string id, std::string title, std::string color, std::vector> options, bool temporary, std::string kind) { if (this->menu_ids.find(id) != this->menu_ids.end()) return; - Menu *menu = new Menu(id, title, color, options, temporary); + if(kind != "screen" && kind != "center") kind = g_Config->FetchValue("core.menu.kind"); + + Menu* menu = nullptr; + if(kind == "screen") { + menu = new ScreenMenu(id, title, color, options, temporary); + } else if(kind == "center") { + menu = new CenterMenu(id, title, color, options, temporary); + } + if(!menu) return; + this->menu_ids.insert(std::make_pair(id, menu)); if (this->menu_id_by_plugin.find(plugin_name) == this->menu_id_by_plugin.end()) this->menu_id_by_plugin.insert({plugin_name, {}}); @@ -36,11 +46,11 @@ void MenuManager::UnregisterMenu(std::string id) continue; if (player->IsFakeClient()) continue; - if (!player->HasMenuShown()) + if (!player->menu_renderer->HasMenuShown()) continue; - if (player->GetMenu()->GetID() == id) - player->HideMenu(); + if (player->menu_renderer->GetMenu()->GetID() == id) + player->menu_renderer->HideMenu(); } delete this->menu_ids[id]; diff --git a/src/server/menus/MenuManager.h b/src/server/menus/MenuManager.h index e640e4070..2aa81938f 100644 --- a/src/server/menus/MenuManager.h +++ b/src/server/menus/MenuManager.h @@ -4,7 +4,8 @@ #include #include -#include "Menu.h" +#include "kinds/ScreenMenu.h" +#include "kinds/CenterMenu.h" class MenuManager { @@ -14,7 +15,7 @@ class MenuManager public: MenuManager(); - void RegisterMenu(std::string plugin_name, std::string id, std::string title, std::string color, std::vector> options, bool temporary); + void RegisterMenu(std::string plugin_name, std::string id, std::string title, std::string color, std::vector> options, bool temporary, std::string kind); void UnregisterMenu(std::string id); Menu *FetchMenu(std::string menu_id); diff --git a/src/server/menus/MenuRenderer.cpp b/src/server/menus/MenuRenderer.cpp new file mode 100644 index 000000000..181dd98d3 --- /dev/null +++ b/src/server/menus/MenuRenderer.cpp @@ -0,0 +1,256 @@ +#include "MenuManager.h" +#include "MenuRenderer.h" + +#include "../../engine/vgui/VGUI.h" +#include "../../memory/signatures/Signatures.h" +#include "../commands/CommandsManager.h" + +typedef IGameEventListener2* (*GetLegacyGameEventListener)(CPlayerSlot slot); + +MenuRenderer::MenuRenderer(Player* player) +{ + m_player = player; + + CPlayerSlot slot = player->GetSlot(); + + centerMessageEvent = g_gameEventManager->CreateEvent("show_survival_respawn_status", true); + centerMessageEvent->SetUint64("duration", 1); + centerMessageEvent->SetInt("userid", slot.Get()); + + playerListener = g_Signatures->FetchSignature("LegacyGameEventListener")(slot); + + menu = nullptr; +} + +MenuRenderer::~MenuRenderer() +{ + HideMenu(); + g_gameEventManager->FreeEvent(centerMessageEvent); +} + +void MenuRenderer::ShowMenu(std::string menu_id) +{ + if (menu != nullptr) return; + + Menu* m = g_MenuManager->FetchMenu(menu_id); + if (m == nullptr) return; + + menu = m; + page = 1; + selected = 0; + + menu->RegeneratePage(m_player->GetSlot().Get(), page, selected); + + std::string kind = menu->GetKind(); + if(kind == "center") { + RenderMenu(); + } else if(kind == "screen") { + menuTextID = g_pVGUI->RegisterScreenText(); + menuPanelID = g_pVGUI->RegisterScreenPanel(); + menuPanelExtendID = g_pVGUI->RegisterScreenPanel(); + menuFooterID = g_pVGUI->RegisterScreenText(); + + auto menuText = g_pVGUI->GetScreenText(menuTextID); + auto menuPanel = g_pVGUI->GetScreenPanel(menuPanelID); + auto menuPanelExtend = g_pVGUI->GetScreenPanel(menuPanelExtendID); + auto menuFooter = g_pVGUI->GetScreenText(menuFooterID); + + menuFooter->Create(Color(255,255,255,255)); + menuFooter->SetupViewForPlayer(m_player); + + menuText->Create(menu->GetColor()); + menuText->SetupViewForPlayer(m_player); + RenderMenu(); + menuText->SetPosition(0.14, 0.68); + + menuFooter->SetPosition(0.14, 0.27); + + menuPanel->Create(Color(18, 18, 18, 255)); + menuPanel->SetupViewForPlayer(m_player); + menuPanel->SetText("█"); + menuPanel->SetPosition(0.13, 0.7); + + menuPanelExtend->Create(Color(18, 18, 18, 255)); + menuPanelExtend->SetupViewForPlayer(m_player); + menuPanelExtend->SetText("█"); + menuPanelExtend->SetPosition(0.17, 0.7); + } +} + +void MenuRenderer::HideMenu() +{ + if(menu == nullptr) return; + + page = 0; + selected = 0; + + std::string kind = menu->GetKind(); + if(menu->IsTemporary()) { + std::string menuID = menu->GetID(); + g_MenuManager->UnregisterMenu(menuID); + } + + menu = nullptr; + + if(kind == "center") { + if(centerMessageEvent) { + centerMessageEvent->SetString("loc_token", ""); + playerListener->FireGameEvent(centerMessageEvent); + } + } else if(kind == "screen") { + g_pVGUI->DeleteScreenText(menuFooterID); + g_pVGUI->DeleteScreenText(menuTextID); + g_pVGUI->DeleteScreenPanel(menuPanelID); + g_pVGUI->DeleteScreenPanel(menuPanelExtendID); + + menuTextID = 0; + menuPanelID = 0; + menuFooterID = 0; + menuPanelExtendID = 0; + } +} + +bool MenuRenderer::HasMenuShown() +{ + return (menu != nullptr); +} + +Menu* MenuRenderer::GetMenu() +{ + return menu; +} + +int MenuRenderer::GetPage() +{ + return page; +} + +void MenuRenderer::SetPage(int pg) +{ + page = pg; + selected = 0; + menu->RegeneratePage(m_player->GetSlot().Get(), page, selected); + RenderMenu(); +} + +int MenuRenderer::GetSelection() +{ + return selected; +} + +void MenuRenderer::MoveSelection() +{ + if (page == 0) + return; + + int itemsPerPage = menu->GetItemsOnPage(page); + ++selected; + if (itemsPerPage == selected) + selected = 0; + + menu->RegeneratePage(m_player->GetSlot().Get(), page, selected); + RenderMenu(); +} + +void MenuRenderer::RenderMenu() { + if(!menu) return; + std::string kind = menu->GetKind(); + if(kind == "center") { + centerMessageEvent->SetString("loc_token", menu->GeneratedItems(m_player->GetSlot().Get(), page).c_str()); + } else if(kind == "screen") { + auto menuText = g_pVGUI->GetScreenText(menuTextID); + if(!menuText) return; + + menuText->SetText(menu->GeneratedItems(m_player->GetSlot().Get(), page)); + + auto menuFooter = g_pVGUI->GetScreenText(menuFooterID); + if(!menuFooter) return; + menuFooter->SetText(menu->GenerateFooter(page)); + } +} + +void MenuRenderer::RenderMenuTick() +{ + if(!menu) return; + if(!menu->RenderEachTick()) return; + + std::string kind = menu->GetKind(); + if(kind == "center") { + playerListener->FireGameEvent(centerMessageEvent); + } +} + +bool MenuRenderer::ShouldRenderEachTick() +{ + if(!menu) return false; + return menu->RenderEachTick(); +} + +void MenuRenderer::PerformMenuAction(std::string button) +{ + if (!HasMenuShown()) + return; + + if (button == g_Config->FetchValue("core.menu.buttons.scroll")) + { + CCSPlayerController* controller = m_player->GetPlayerController(); + CSingleRecipientFilter filter(m_player->GetSlot().Get()); + if (controller) + controller->EmitSoundFilter(filter, g_Config->FetchValue("core.menu.sound.scroll.name"), 1.0, g_Config->FetchValue("core.menu.sound.scroll.volume")); + + MoveSelection(); + } + else if (!g_Config->FetchValue("core.menu.buttons.exit.option") && button == g_Config->FetchValue("core.menu.buttons.exit.button")) + { + CCSPlayerController* controller = m_player->GetPlayerController(); + CSingleRecipientFilter filter(m_player->GetSlot().Get()); + if (controller) + controller->EmitSoundFilter(filter, g_Config->FetchValue("core.menu.sound.exit.name"), 1.0, g_Config->FetchValue("core.menu.sound.exit.volume")); + HideMenu(); + } + else if (button == g_Config->FetchValue("core.menu.buttons.use")) + { + std::string cmd = GetMenu()->GetCommandFromOption(GetPage(), GetSelection()); + CCSPlayerController* controller = m_player->GetPlayerController(); + CSingleRecipientFilter filter(m_player->GetSlot().Get()); + if (controller && cmd != "menuexit") + controller->EmitSoundFilter(filter, g_Config->FetchValue("core.menu.sound.use.name"), 1.0, g_Config->FetchValue("core.menu.sound.use.volume")); + + if (cmd == "menunext") + { + SetPage(GetPage() + 1); + } + else if (cmd == "menuback") + { + SetPage(GetPage() - 1); + } + else if (g_Config->FetchValue("core.menu.buttons.exit.option") && cmd == "menuexit") + { + if (controller) + controller->EmitSoundFilter(filter, g_Config->FetchValue("core.menu.sound.exit.name"), 1.0, g_Config->FetchValue("core.menu.sound.exit.volume")); + + HideMenu(); + } + else if (g_MenuManager->FetchMenu(cmd)) + { + HideMenu(); + ShowMenu(cmd); + } + else if (starts_with(cmd, "sw_")) + { + CCommand tokenizedArgs; + tokenizedArgs.Tokenize(cmd.c_str()); + + std::vector cmdString = TokenizeCommand(cmd); + cmdString.erase(cmdString.begin()); + + std::string commandName = replace(tokenizedArgs[0], "sw_", ""); + + Command* cmd = g_commandsManager->FetchCommand(commandName); + if (cmd) + cmd->Execute(m_player->GetSlot().Get(), cmdString, true, "sw_"); + } + else if (cmd != "") + m_player->PerformCommand(cmd); + } +} \ No newline at end of file diff --git a/src/server/menus/MenuRenderer.h b/src/server/menus/MenuRenderer.h new file mode 100644 index 000000000..e1d92f905 --- /dev/null +++ b/src/server/menus/MenuRenderer.h @@ -0,0 +1,51 @@ +#ifndef _menus_menurenderer_h +#define _menus_menurenderer_h + +#include +#include +#include "Menu.h" + +class Player; + +class MenuRenderer +{ +private: + Player* m_player; + + Menu* menu = nullptr; + int page = 0; + int selected = 0; + + // Screen Kind + uint64_t menuTextID = 0; + uint64_t menuFooterID = 0; + uint64_t menuPanelID = 0; + uint64_t menuPanelExtendID = 0; + + // Center Kind + IGameEvent* centerMessageEvent = nullptr; + IGameEventListener2* playerListener = nullptr; + +public: + MenuRenderer(Player* player); + ~MenuRenderer(); + + void ShowMenu(std::string menu_id); + + void HideMenu(); + bool HasMenuShown(); + Menu* GetMenu(); + + int GetPage(); + void SetPage(int pg); + int GetSelection(); + void MoveSelection(); + + void PerformMenuAction(std::string button); + + bool ShouldRenderEachTick(); + void RenderMenu(); + void RenderMenuTick(); +}; + +#endif \ No newline at end of file diff --git a/src/server/menus/kinds/CenterMenu.cpp b/src/server/menus/kinds/CenterMenu.cpp new file mode 100644 index 000000000..d3a5befd3 --- /dev/null +++ b/src/server/menus/kinds/CenterMenu.cpp @@ -0,0 +1,145 @@ +#include "CenterMenu.h" + +#include "../../../utils/utils.h" +#include "../../../server/configuration/Configuration.h" + +#include + +CenterMenu::CenterMenu(std::string id, std::string title, std::string color, std::vector> options, bool tmp) +{ + this->id = id; + this->color = color; + this->options = options; + this->title = title; + this->temporary = tmp; + + ProcessOptions(); +} + +CenterMenu::~CenterMenu() +{ + this->id.clear(); + this->title.clear(); + this->color.clear(); + this->options.clear(); + this->processedOptions.clear(); +} + +std::string CenterMenu::GetID() +{ + return this->id; +} + +std::string CenterMenu::GetCommandFromOption(int page, int selected) +{ + if (page < 1) + return ""; + + return processedOptions[page - 1][selected].second; +} + +size_t CenterMenu::GetItemsOnPage(int page) +{ + if (page < 1) + return 0; + + return processedOptions[page - 1].size(); +} + +void CenterMenu::ProcessOptions() +{ + int pages = 0; + int processedItems = 0; + int totalProcessedItems = 0; + std::vector> tempmap; + + int maxProcessedItems = (g_Config->FetchValue("core.menu.buttons.exit.option") ? (pages == 0 ? 4 : 3) : (pages == 0 ? 5 : 4)); + for (const std::pair entry : this->options) + { + ++processedItems; + ++totalProcessedItems; + tempmap.push_back({entry.first, entry.second}); + if (processedItems == maxProcessedItems) + { + if (options.size() - totalProcessedItems > 0) + tempmap.push_back({g_translations->FetchTranslation("core.menu.next"), "menunext"}); + if (pages != 0) + tempmap.push_back({g_translations->FetchTranslation("core.menu.back"), "menuback"}); + + if (g_Config->FetchValue("core.menu.buttons.exit.option")) + tempmap.push_back({g_translations->FetchTranslation("core.menu.exit"), "menuexit"}); + + processedItems = 0; + pages++; + this->processedOptions.push_back(tempmap); + tempmap.clear(); + } + } + + if (tempmap.size() > 0) + { + if (this->processedOptions.size() != 0) + tempmap.push_back({g_translations->FetchTranslation("core.menu.back"), "menuback"}); + + if (g_Config->FetchValue("core.menu.buttons.exit.option")) + tempmap.push_back({g_translations->FetchTranslation("core.menu.exit"), "menuexit"}); + + processedItems = 0; + this->processedOptions.push_back(tempmap); + tempmap.clear(); + } +} + +std::string CenterMenu::GeneratedItems(int playerid, int page) +{ + return this->generatedPages[playerid][page - 1]; +} + +void CenterMenu::RegeneratePage(int playerid, int page, int selected) +{ + if (this->generatedPages.find(playerid) == this->generatedPages.end()) + this->generatedPages.insert({playerid, {}}); + + while (this->generatedPages[playerid].size() < page) + { + this->generatedPages[playerid].push_back(""); + } + + std::string stringPage = string_format("
   %s

", this->color.c_str(), this->title.c_str()); + for (int i = 0; i < processedOptions[page - 1].size(); i++) + stringPage += string_format("
%s%s

", (i == selected ? this->color.c_str() : "ffffff"), (i == selected ? (g_Config->FetchValue("core.menu.navigation_prefix") + " ").c_str() : "    "), processedOptions[page - 1][i].first.c_str()); + std::string footer = replace(g_translations->FetchTranslation(g_Config->FetchValue("core.menu.buttons.exit.option") ? "core.menu.center.footer" : "core.menu.center.footer.nooption"), "{PAGE}", std::to_string(page)); + footer = replace(footer, "{MAXPAGES}", std::to_string(processedOptions.size())); + footer = replace(footer, "{CYCLE_BUTTON}", str_toupper(g_Config->FetchValue("core.menu.buttons.scroll"))); + footer = replace(footer, "{USE_BUTTON}", str_toupper(g_Config->FetchValue("core.menu.buttons.use"))); + footer = replace(footer, "{EXIT_BUTTON}", str_toupper(g_Config->FetchValue("core.menu.buttons.exit.button"))); + + stringPage += string_format("%s", footer.c_str()); + + this->generatedPages[playerid][page - 1] = stringPage; +} + +bool CenterMenu::IsTemporary() +{ + return this->temporary; +} + +Color CenterMenu::GetColor() +{ + return Color(0,0,0); +} + +std::string CenterMenu::GenerateFooter(int page) +{ + return ""; +} + +bool CenterMenu::RenderEachTick() +{ + return true; +} + +std::string CenterMenu::GetKind() +{ + return "center"; +} \ No newline at end of file diff --git a/src/server/menus/kinds/CenterMenu.h b/src/server/menus/kinds/CenterMenu.h new file mode 100644 index 000000000..54097a150 --- /dev/null +++ b/src/server/menus/kinds/CenterMenu.h @@ -0,0 +1,36 @@ +#ifndef _menus_kinds_center_h +#define _menus_kinds_center_h + +#include "../Menu.h" + +class CenterMenu: public Menu +{ +private: + std::string id; + std::string title; + std::string color; + std::vector> options; + std::vector>> processedOptions; + bool temporary; + + std::map> generatedPages; +public: + CenterMenu(std::string id, std::string title, std::string color, std::vector> options, bool tmp); + ~CenterMenu(); + + std::string GetID(); + void ProcessOptions(); + std::string GeneratedItems(int playerid, int page); + std::string GenerateFooter(int page); + void RegeneratePage(int playerid, int page, int selected); + + std::string GetCommandFromOption(int page, int selected); + size_t GetItemsOnPage(int page); + bool IsTemporary(); + + Color GetColor(); + bool RenderEachTick(); + std::string GetKind(); +}; + +#endif \ No newline at end of file diff --git a/src/server/menus/Menu.cpp b/src/server/menus/kinds/ScreenMenu.cpp similarity index 79% rename from src/server/menus/Menu.cpp rename to src/server/menus/kinds/ScreenMenu.cpp index 4b07c8f23..91b9ce322 100644 --- a/src/server/menus/Menu.cpp +++ b/src/server/menus/kinds/ScreenMenu.cpp @@ -1,11 +1,11 @@ -#include "Menu.h" +#include "ScreenMenu.h" -#include "../../utils/utils.h" -#include "../../server/configuration/Configuration.h" +#include "../../../utils/utils.h" +#include "../../../server/configuration/Configuration.h" #include -Menu::Menu(std::string id, std::string title, std::string color, std::vector> options, bool tmp) +ScreenMenu::ScreenMenu(std::string id, std::string title, std::string color, std::vector> options, bool tmp) { int r=255,g=255,b=255,a=255; try { @@ -35,7 +35,7 @@ Menu::Menu(std::string id, std::string title, std::string color, std::vectorid.clear(); this->title.clear(); @@ -43,12 +43,12 @@ Menu::~Menu() this->processedOptions.clear(); } -std::string Menu::GetID() +std::string ScreenMenu::GetID() { return this->id; } -std::string Menu::GetCommandFromOption(int page, int selected) +std::string ScreenMenu::GetCommandFromOption(int page, int selected) { if (page < 1) return ""; @@ -56,7 +56,7 @@ std::string Menu::GetCommandFromOption(int page, int selected) return processedOptions[page - 1][selected].second; } -size_t Menu::GetItemsOnPage(int page) +size_t ScreenMenu::GetItemsOnPage(int page) { if (page < 1) return 0; @@ -87,7 +87,7 @@ std::string RemoveHtmlTags(std::string input) { return std::regex_replace(input, pattern, ""); } -void Menu::ProcessOptions() +void ScreenMenu::ProcessOptions() { int pages = 0; int processedItems = 0; @@ -120,10 +120,10 @@ void Menu::ProcessOptions() if (tempmap.size() > 0) { if (this->processedOptions.size() != 0) - tempmap.push_back({g_translations->FetchTranslation("core.menu.back"), "menuback"}); + tempmap.push_back({stringWithSplit(RemoveHtmlTags(g_translations->FetchTranslation("core.menu.back")), 25), "menuback"}); if (g_Config->FetchValue("core.menu.buttons.exit.option")) - tempmap.push_back({g_translations->FetchTranslation("core.menu.exit"), "menuexit"}); + tempmap.push_back({stringWithSplit(RemoveHtmlTags(g_translations->FetchTranslation("core.menu.exit")), 25), "menuexit"}); processedItems = 0; this->processedOptions.push_back(tempmap); @@ -131,12 +131,12 @@ void Menu::ProcessOptions() } } -std::string Menu::GeneratedItems(int playerid, int page) +std::string ScreenMenu::GeneratedItems(int playerid, int page) { return this->generatedPages[playerid][page - 1]; } -void Menu::RegeneratePage(int playerid, int page, int selected) +void ScreenMenu::RegeneratePage(int playerid, int page, int selected) { if (this->generatedPages.find(playerid) == this->generatedPages.end()) this->generatedPages.insert({playerid, {}}); @@ -154,22 +154,32 @@ void Menu::RegeneratePage(int playerid, int page, int selected) this->generatedPages[playerid][page - 1] = stringPage; } -bool Menu::IsTemporary() +bool ScreenMenu::IsTemporary() { return this->temporary; } -Color Menu::GetColor() +Color ScreenMenu::GetColor() { return this->color; } -std::string Menu::GenerateFooter(int page) +std::string ScreenMenu::GenerateFooter(int page) { - std::string footer = replace(g_translations->FetchTranslation(g_Config->FetchValue("core.menu.buttons.exit.option") ? "core.menu.footer" : "core.menu.footer.nooption"), "{PAGE}", std::to_string(page)); + std::string footer = replace(g_translations->FetchTranslation(g_Config->FetchValue("core.menu.buttons.exit.option") ? "core.menu.screen.footer" : "core.menu.screen.footer.nooption"), "{PAGE}", std::to_string(page)); footer = replace(footer, "{MAXPAGES}", std::to_string(processedOptions.size())); footer = replace(footer, "{CYCLE_BUTTON}", str_toupper(g_Config->FetchValue("core.menu.buttons.scroll"))); footer = replace(footer, "{USE_BUTTON}", str_toupper(g_Config->FetchValue("core.menu.buttons.use"))); footer = replace(footer, "{EXIT_BUTTON}", str_toupper(g_Config->FetchValue("core.menu.buttons.exit.button"))); return footer; +} + +bool ScreenMenu::RenderEachTick() +{ + return false; +} + +std::string ScreenMenu::GetKind() +{ + return "screen"; } \ No newline at end of file diff --git a/src/server/menus/kinds/ScreenMenu.h b/src/server/menus/kinds/ScreenMenu.h new file mode 100644 index 000000000..4a14ecb60 --- /dev/null +++ b/src/server/menus/kinds/ScreenMenu.h @@ -0,0 +1,36 @@ +#ifndef _menus_kinds_screen_h +#define _menus_kinds_screen_h + +#include "../Menu.h" + +class ScreenMenu: public Menu +{ +private: + std::string id; + std::string title; + Color color; + std::vector> options; + std::vector>> processedOptions; + bool temporary; + + std::map> generatedPages; +public: + ScreenMenu(std::string id, std::string title, std::string color, std::vector> options, bool tmp); + ~ScreenMenu(); + + std::string GetID(); + void ProcessOptions(); + std::string GeneratedItems(int playerid, int page); + std::string GenerateFooter(int page); + void RegeneratePage(int playerid, int page, int selected); + + std::string GetCommandFromOption(int page, int selected); + size_t GetItemsOnPage(int page); + bool IsTemporary(); + + Color GetColor(); + bool RenderEachTick(); + std::string GetKind(); +}; + +#endif \ No newline at end of file