From d59589550d74bc230baee6d3c5cde0174c509652 Mon Sep 17 00:00:00 2001 From: VDm Date: Sat, 9 Aug 2025 16:37:01 +0400 Subject: [PATCH] feat(gameui): implement Set UI Binding scripts --- src/gameui/CGGameUI.cpp | 8 +- src/gameui/CGUIBindings.cpp | 34 +++-- src/gameui/CGUIBindings.hpp | 11 +- .../scripts/GameScriptFunctionsUIBindings.cpp | 132 +++++++++++++++++- 4 files changed, 162 insertions(+), 23 deletions(-) diff --git a/src/gameui/CGGameUI.cpp b/src/gameui/CGGameUI.cpp index b539816..b82d09f 100644 --- a/src/gameui/CGGameUI.cpp +++ b/src/gameui/CGGameUI.cpp @@ -13,6 +13,7 @@ #include "gameui/CGDressUpModelFrame.hpp" #include "gameui/CGTabardModelFrame.hpp" #include "gameui/CGQuestPOIFrame.hpp" +#include "gameui/CGUIBindings.hpp" #include "gx/Coordinate.hpp" #include "gx/Device.hpp" #include "ui/FrameScript.hpp" @@ -84,8 +85,8 @@ void CGGameUI::Initialize() { LoadScriptFunctions(); FrameScript_CreateEvents(g_scriptEvents, 722); //CGGameUI::RegisterGameCVars(); - //CGUIBindings::Initialize(); + CGUIBindings::Initialize(); CGGameUI::RegisterFrameFactories(); // STORM_ASSERT(GetDataInterfaceVersion() == GetCodeInterfaceVersion()) @@ -139,8 +140,9 @@ void CGGameUI::Initialize() { FrameXML_FreeHashNodes(); FrameXML_CreateFrames("Interface\\FrameXML\\FrameXML.toc", 0, &md5, &status); - //if (SFile__FileExistsEx((void*)"Interface\\FrameXML\\Bindings.xml", 1)) - // CGUIBindings::Load("Interface\\FrameXML\\Bindings.xml", &md5, &status); + if (SFile::FileExistsEx("Interface\\FrameXML\\Bindings.xml", 1)) { + CGUIBindings::s_bindings->Load("Interface\\FrameXML\\Bindings.xml", &md5, &status); + } unsigned char digest2[16]; MD5Final(digest2, &md5); diff --git a/src/gameui/CGUIBindings.cpp b/src/gameui/CGUIBindings.cpp index e8fe205..5e17020 100644 --- a/src/gameui/CGUIBindings.cpp +++ b/src/gameui/CGUIBindings.cpp @@ -7,10 +7,15 @@ #include #include +#include #include static CStatus s_nullStatus; +static uint16_t s_initRound = 0; + +CGUIBindings* CGUIBindings::s_bindings = nullptr; + static bool ValidateKeyString(const char* key) { static std::pair s_prefixes[3] = { @@ -113,6 +118,13 @@ void MODIFIEDCLICK::SetBinding(BINDING_SET a1, const char* binding) { } +void CGUIBindings::Initialize() { + CGUIBindings::s_bindings = NEW(CGUIBindings); + while (!s_initRound) { + ++s_initRound; + } +} + bool CGUIBindings::Load(const char* commandsFile, MD5_CTX* md5, CStatus* status) { if (!status) { status = &s_nullStatus; @@ -232,8 +244,8 @@ void CGUIBindings::LoadBinding(const char* commandsFile, XMLNode* node, CStatus* const char* binding = node->GetAttributeByName("default"); if (binding && *binding) { - if (!this->m_bindings.Ptr(binding)) { - this->Bind(BINDING_SET_0, BINDING_MODE_0, binding, name); + if (!this->m_bindings[BINDING_DEFAULT].Ptr(binding)) { + this->Bind(BINDING_DEFAULT, BINDING_MODE_0, binding, name); } } } @@ -255,7 +267,7 @@ void CGUIBindings::LoadModifiedClick(const char* commandsFile, XMLNode* node, CS const char* binding = node->GetAttributeByName("default"); if (binding && *binding) { - modifiedClick->SetBinding(BINDING_SET_0, binding); + modifiedClick->SetBinding(BINDING_DEFAULT, binding); } } @@ -311,12 +323,12 @@ bool CGUIBindings::Bind(BINDING_SET set, BINDING_MODE mode, const char* keystrin return false; } - auto binding = this->m_bindings.Ptr(key); + auto binding = this->m_bindings[set].Ptr(key); if (!binding) { - binding = this->m_bindings.New(key, 0, 0); + binding = this->m_bindings[set].New(key, 0, 0); } - if (set != BINDING_SET_0) { + if (set != BINDING_DEFAULT) { binding->flags &= ~1u; } else { binding->flags |= 1u; @@ -366,8 +378,7 @@ int32_t CGUIBindings::GetBindingIndex(KEYBINDING* binding, BINDING_MODE mode) co } int32_t CGUIBindings::GetNumCommandKeys(BINDING_SET set, BINDING_MODE mode, const char* command) { - // TODO: Check set argument - auto binding = this->m_bindings.Head(); + auto binding = this->m_bindings[set].Head(); int32_t result = 0; @@ -377,15 +388,14 @@ int32_t CGUIBindings::GetNumCommandKeys(BINDING_SET set, BINDING_MODE mode, cons ++result; } - binding = this->m_bindings.Next(binding); + binding = this->m_bindings[set].Next(binding); } return result; } void CGUIBindings::AdjustCommandKeyIndices(BINDING_SET set, BINDING_MODE mode, const char* command, int32_t index) { - // TODO: Check set argument - auto binding = this->m_bindings.Head(); + auto binding = this->m_bindings[set].Head(); int32_t result = 0; @@ -398,6 +408,6 @@ void CGUIBindings::AdjustCommandKeyIndices(BINDING_SET set, BINDING_MODE mode, c } } - binding = this->m_bindings.Next(binding); + binding = this->m_bindings[set].Next(binding); } } diff --git a/src/gameui/CGUIBindings.hpp b/src/gameui/CGUIBindings.hpp index 07086ce..b2b484f 100644 --- a/src/gameui/CGUIBindings.hpp +++ b/src/gameui/CGUIBindings.hpp @@ -9,10 +9,10 @@ class CStatus; class XMLNode; enum BINDING_SET { - BINDING_SET_0 = 0, + BINDING_DEFAULT = 0, BINDING_SET_1, BINDING_SET_2, - BINDING_SET_3, + BINDING_SCRIPT, }; enum BINDING_MODE { @@ -50,6 +50,11 @@ class MODIFIEDCLICK : public TSHashObject { class CGUIBindings { public: + static CGUIBindings* s_bindings; + + static void Initialize(); + + CGUIBindings() = default; bool Load(const char* commandsFile, MD5_CTX* md5, CStatus* status); @@ -64,7 +69,7 @@ class CGUIBindings { int32_t m_numCommands; int32_t m_numHiddenCommands; int32_t m_numModifiedClicks; - TSHashTable m_bindings; + TSHashTable m_bindings[4]; TSHashTable m_commands; TSHashTable m_modifiedClicks; }; diff --git a/src/gameui/scripts/GameScriptFunctionsUIBindings.cpp b/src/gameui/scripts/GameScriptFunctionsUIBindings.cpp index 7c13947..32127f6 100644 --- a/src/gameui/scripts/GameScriptFunctionsUIBindings.cpp +++ b/src/gameui/scripts/GameScriptFunctionsUIBindings.cpp @@ -1,4 +1,5 @@ #include "gameui/GameScriptFunctions.hpp" +#include "gameui/CGUIBindings.hpp" #include "ui/FrameScript.hpp" #include "util/Lua.hpp" #include "util/Unimplemented.hpp" @@ -13,23 +14,144 @@ static int32_t Script_GetBinding(lua_State* L) { } static int32_t Script_SetBinding(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isstring(L, 1)) { + return luaL_error(L, "Usage: SetBinding(\"KEY\"[, \"COMMAND\"][, mode])"); + } + + int32_t mode = BINDING_MODE_0; + if (lua_isnumber(L, 3)) { + mode = static_cast(lua_tonumber(L, 3)) - 1; + if (mode < BINDING_MODE_0 || mode > BINDING_MODE_3) { + mode = BINDING_MODE_0; + } + } + auto key = lua_tolstring(L, 1, 0); + auto command = lua_tolstring(L, 2, 0); + if (CGUIBindings::s_bindings->Bind(BINDING_SCRIPT, static_cast(mode), key, command)) { + FrameScript_SignalEvent(0x177u, 0); + lua_pushnumber(L, 1.0); + } else { + lua_pushnil(L); + } + return 1; } static int32_t Script_SetBindingSpell(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isstring(L, 1) || !lua_isstring(L, 2)) { + return luaL_error(L, "Usage: SetBindingSpell(\"KEY\", \"spellname\"[, mode])"); + } + + int32_t mode = BINDING_MODE_0; + if (lua_isnumber(L, 3)) { + mode = static_cast(lua_tonumber(L, 3)) - 1; + if (mode < BINDING_MODE_0 || mode > BINDING_MODE_3) { + mode = BINDING_MODE_0; + } + } + auto key = lua_tolstring(L, 1, 0); + + auto spellName = lua_tolstring(L, 2, 0); + auto length = SStrLen(spellName) + 7; + auto command = static_cast(alloca(length)); + SStrPrintf(command, length, "SPELL %s", spellName); + + if (CGUIBindings::s_bindings->Bind(BINDING_SCRIPT, static_cast(mode), key, command)) { + FrameScript_SignalEvent(0x177u, 0); + lua_pushnumber(L, 1.0); + } else { + lua_pushnil(L); + } + return 1; } static int32_t Script_SetBindingItem(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isstring(L, 1) || !lua_isstring(L, 2)) { + return luaL_error(L, "Usage: SetBindingItem(\"KEY\", \"itemname\"[, mode])"); + } + + int32_t mode = BINDING_MODE_0; + if (lua_isnumber(L, 3)) { + mode = static_cast(lua_tonumber(L, 3)) - 1; + if (mode < BINDING_MODE_0 || mode > BINDING_MODE_3) { + mode = BINDING_MODE_0; + } + } + auto key = lua_tolstring(L, 1, 0); + + auto itemName = lua_tolstring(L, 2, 0); + auto length = SStrLen(itemName) + 7; + auto command = static_cast(alloca(length)); + SStrPrintf(command, length, "ITEM %s", itemName); + + if (CGUIBindings::s_bindings->Bind(BINDING_SCRIPT, static_cast(mode), key, command)) { + FrameScript_SignalEvent(0x177u, 0); + lua_pushnumber(L, 1.0); + } else { + lua_pushnil(L); + } + return 1; } static int32_t Script_SetBindingMacro(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isstring(L, 1) || !lua_isstring(L, 2)) { + return luaL_error(L, "Usage: SetBindingMacro(\"KEY\", \"macroname\"|macroid[, mode])"); + } + + int32_t mode = BINDING_MODE_0; + if (lua_isnumber(L, 3)) { + mode = static_cast(lua_tonumber(L, 3)) - 1; + if (mode < BINDING_MODE_0 || mode > BINDING_MODE_3) { + mode = BINDING_MODE_0; + } + } + auto key = lua_tolstring(L, 1, 0); + + auto macroName = lua_tolstring(L, 2, 0); + auto length = SStrLen(macroName) + 7; + auto command = static_cast(alloca(length)); + SStrPrintf(command, length, "MACRO %s", macroName); + + if (CGUIBindings::s_bindings->Bind(BINDING_SCRIPT, static_cast(mode), key, command)) { + FrameScript_SignalEvent(0x177u, 0); + lua_pushnumber(L, 1.0); + } else { + lua_pushnil(L); + } + return 1; } static int32_t Script_SetBindingClick(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isstring(L, 1) || !lua_isstring(L, 2)) { + return luaL_error(L, "Usage: SetBindingClick(\"KEY\", \"buttonName\"[, \"mouseButton\"][, mode])"); + } + + int32_t mode = BINDING_MODE_0; + if (lua_isnumber(L, 4)) { + mode = static_cast(lua_tonumber(L, 4)) - 1; + if (mode < BINDING_MODE_0 || mode > BINDING_MODE_3) { + mode = BINDING_MODE_0; + } + } + auto key = lua_tolstring(L, 1, 0); + + auto buttonName = lua_tolstring(L, 2, 0); + + auto mouseButton = lua_tolstring(L, 3, 0); + if (!mouseButton) { + mouseButton = "LeftButton"; + } + + auto length = SStrLen(buttonName) + SStrLen(mouseButton) + 8; + auto command = static_cast(alloca(length)); + SStrPrintf(command, length, "CLICK %s:%s", buttonName, mouseButton); + + if (CGUIBindings::s_bindings->Bind(BINDING_SCRIPT, static_cast(mode), key, command)) { + FrameScript_SignalEvent(0x177u, 0); + lua_pushnumber(L, 1.0); + } else { + lua_pushnil(L); + } + return 1; } static int32_t Script_SetOverrideBinding(lua_State* L) {