From 0c39457ed1a2a5b3444439e4c6ce0077d9c8d8c0 Mon Sep 17 00:00:00 2001 From: VDm Date: Sat, 5 Apr 2025 01:42:43 +0400 Subject: [PATCH] feat(glue): update Character Selection screen to support switching --- src/client/Client.cpp | 2 + src/client/Client.hpp | 1 + src/client/ClientServices.cpp | 6 ++ src/client/ClientServices.hpp | 1 + src/glue/CCharacterSelection.cpp | 83 +++++++++++++++++++------ src/glue/CCharacterSelection.hpp | 18 +++++- src/net/Types.hpp | 2 + src/net/connection/ClientConnection.cpp | 7 +++ src/net/connection/ClientConnection.hpp | 1 + src/ui/ScriptFunctionsCharSelect.cpp | 20 ++++-- 10 files changed, 117 insertions(+), 24 deletions(-) diff --git a/src/client/Client.cpp b/src/client/Client.cpp index 28ff04c..4a98693 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -33,6 +33,7 @@ CVar* Client::g_accountUsesTokenVar; CVar* Client::g_movieVar; CVar* Client::g_expansionMovieVar; CVar* Client::g_movieSubtitleVar; +CVar* Client::g_lastCharacterIndex; HEVENTCONTEXT Client::g_clientEventContext; @@ -83,6 +84,7 @@ void ClientRegisterConsoleCommands() { Client::g_movieVar = CVar::Register("movie", "Show movie on startup", 0, "1", nullptr, game); Client::g_expansionMovieVar = CVar::Register("expansionMovie", "Show expansion movie on startup", 0, "1", nullptr, game); Client::g_movieSubtitleVar = CVar::Register("movieSubtitle", "Show movie subtitles", 0, "0", nullptr, game); + Client::g_lastCharacterIndex = CVar::Register("lastCharacterIndex", "Last character selected", 0, "0", nullptr, game); // TODO } diff --git a/src/client/Client.hpp b/src/client/Client.hpp index 0569966..31d8902 100644 --- a/src/client/Client.hpp +++ b/src/client/Client.hpp @@ -14,6 +14,7 @@ namespace Client { extern CVar* g_movieVar; extern CVar* g_expansionMovieVar; extern CVar* g_movieSubtitleVar; + extern CVar* g_lastCharacterIndex; extern HEVENTCONTEXT g_clientEventContext; extern char g_currentLocaleName[5]; } diff --git a/src/client/ClientServices.cpp b/src/client/ClientServices.cpp index a306b70..fde53d0 100644 --- a/src/client/ClientServices.cpp +++ b/src/client/ClientServices.cpp @@ -73,9 +73,15 @@ void ClientServices::GetRealmList() { } void ClientServices::GetCharacterList() { + STORM_ASSERT(ClientServices::s_currentConnection); ClientServices::s_currentConnection->GetCharacterList(); } +void ClientServices::EnumerateCharacters(ENUMERATE_CHARACTERS_CALLBACK fcn, void* param) { + STORM_ASSERT(ClientServices::s_currentConnection); + ClientServices::s_currentConnection->EnumerateCharacters(fcn, param); +} + void ClientServices::CharacterLogin(uint64_t id, const C3Vector& position) { ClientServices::s_currentConnection->CharacterLogin(id); } diff --git a/src/client/ClientServices.hpp b/src/client/ClientServices.hpp index 4cc7b1f..b3bcf75 100644 --- a/src/client/ClientServices.hpp +++ b/src/client/ClientServices.hpp @@ -38,6 +38,7 @@ class ClientServices : public LoginResponse { static void SetMessageHandler(NETMESSAGE msgId, MESSAGE_HANDLER handler, void* param); static void GetRealmList(); static void GetCharacterList(); + static void EnumerateCharacters(ENUMERATE_CHARACTERS_CALLBACK fcn, void* param); static void CharacterLogin(uint64_t id, const C3Vector& position); static REALM_INFO* GetRealmInfoByIndex(int32_t index); static const char* GetSelectedRealmName(); diff --git a/src/glue/CCharacterSelection.cpp b/src/glue/CCharacterSelection.cpp index 8ed69c5..bea4642 100644 --- a/src/glue/CCharacterSelection.cpp +++ b/src/glue/CCharacterSelection.cpp @@ -2,33 +2,48 @@ #include "model/CM2Shared.hpp" #include "ui/CSimpleModelFFX.hpp" #include "client/ClientServices.hpp" +#include "client/Client.hpp" +#include "console/CVar.hpp" #include "net/Connection.hpp" +CSimpleModelFFX* CCharacterSelection::m_modelFrame = nullptr; +uint32_t CCharacterSelection::m_characterCount = 0; +float CCharacterSelection::m_charFacing = 0.0f; +uint32_t CCharacterSelection::m_restrictHuman = 0; +uint32_t CCharacterSelection::m_restrictDwarf = 0; +uint32_t CCharacterSelection::m_restrictGnome = 0; +uint32_t CCharacterSelection::m_restrictNightElf = 0; +uint32_t CCharacterSelection::m_restrictDraenei = 0; +uint32_t CCharacterSelection::m_restrictOrc = 0; +uint32_t CCharacterSelection::m_restrictTroll = 0; +uint32_t CCharacterSelection::m_restrictTauren = 0; +uint32_t CCharacterSelection::m_restrictUndead = 0; +uint32_t CCharacterSelection::m_restrictBloodElf = 0; TSGrowableArray CCharacterSelection::s_characterList; -CSimpleModelFFX* CCharacterSelection::s_modelFrame; -float CCharacterSelection::s_charFacing = 0.0f; +int32_t CCharacterSelection::m_selectionIndex = 0; + void CCharacterSelection::RenderPrep() { // TODO } void CCharacterSelection::SetBackgroundModel(const char* modelPath) { - if (!CCharacterSelection::s_modelFrame || !modelPath || !*modelPath) { + if (!CCharacterSelection::m_modelFrame || !modelPath || !*modelPath) { return; } - auto model = CCharacterSelection::s_modelFrame->m_model; + auto model = CCharacterSelection::m_modelFrame->m_model; // Check if already set if (model && !SStrCmpI(modelPath, model->m_shared->m_filePath, STORM_MAX_STR)) { return; } - CCharacterSelection::s_modelFrame->SetModel(modelPath); + CCharacterSelection::m_modelFrame->SetModel(modelPath); // TODO BYTE1(CCharacterSelection::m_modelFrame->simplemodelffx_dword510[3]) = 1; - model = CCharacterSelection::s_modelFrame->m_model; + model = CCharacterSelection::m_modelFrame->m_model; if (model) { // TODO lighting callback + arg @@ -37,8 +52,18 @@ void CCharacterSelection::SetBackgroundModel(const char* modelPath) { } } +void CCharacterSelection::EnumerateCharactersCallback(CHARACTER_INFO& info, void* param) { + auto character = CCharacterSelection::s_characterList.New(); + character->m_characterInfo = info; + // TODO: LoadAddOnEnableState(a1 + 8); +} + +void CCharacterSelection::ShowCharacter() { + // TODO +} + void CCharacterSelection::SetCharFacing(float facing) { - // TODO: + // TODO } void CCharacterSelection::ClearCharacterList() { @@ -46,20 +71,42 @@ void CCharacterSelection::ClearCharacterList() { void CCharacterSelection::UpdateCharacterList() { // TODO: ClearAddOnEnableState(0); - // TODO: Proper implementation - auto& received = ClientServices::Connection()->m_characterList; - CCharacterSelection::s_characterList.SetCount(received.Count()); - for (uint32_t i = 0; i < received.Count(); ++i) { - CCharacterSelection::s_characterList[i].m_characterInfo = received[i]; - } + CCharacterSelection::s_characterList.SetCount(0); - if (CCharacterSelection::GetNumCharacters()) { - int32_t currentIndex = 0; - FrameScript_SignalEvent(8, "%d", currentIndex + 1); + CCharacterSelection::m_restrictHuman = 0; + CCharacterSelection::m_restrictDwarf = 0; + CCharacterSelection::m_restrictGnome = 0; + CCharacterSelection::m_restrictNightElf = 0; + CCharacterSelection::m_restrictDraenei = 0; + CCharacterSelection::m_restrictOrc = 0; + CCharacterSelection::m_restrictTroll = 0; + CCharacterSelection::m_restrictTauren = 0; + CCharacterSelection::m_restrictUndead = 0; + CCharacterSelection::m_restrictBloodElf = 0; + + ClientServices::EnumerateCharacters(&CCharacterSelection::EnumerateCharactersCallback, nullptr); + + if (CCharacterSelection::s_characterList.Count()) { + // TODO: Apply restrictions (m_restrictHuman, etc) + // TODO: CRealmList::m_preferredCategory = 0; + + int32_t selectionIndex = Client::g_lastCharacterIndex->GetInt(); + if (selectionIndex < 0 || selectionIndex >= CCharacterSelection::s_characterList.Count()) { + selectionIndex = 0; + } + + CCharacterSelection::m_selectionIndex = selectionIndex; + CCharacterSelection::ShowCharacter(); + + FrameScript_SignalEvent(8, "%d", CCharacterSelection::m_selectionIndex + 1); } else { - int32_t currentIndex = 0; - FrameScript_SignalEvent(8, "%d", currentIndex + 1); + CCharacterSelection::m_selectionIndex = 0; + CCharacterSelection::ShowCharacter(); + FrameScript_SignalEvent(8, "%d", CCharacterSelection::m_selectionIndex + 1); + if (CCharacterSelection::m_modelFrame) { + // TODO + } } FrameScript_SignalEvent(7, nullptr); } diff --git a/src/glue/CCharacterSelection.hpp b/src/glue/CCharacterSelection.hpp index 7d25381..2915f32 100644 --- a/src/glue/CCharacterSelection.hpp +++ b/src/glue/CCharacterSelection.hpp @@ -13,13 +13,27 @@ struct CharacterSelectionDisplay { class CCharacterSelection { public: // Static variables + static CSimpleModelFFX* m_modelFrame; + static uint32_t m_characterCount; + static float m_charFacing; + static uint32_t m_restrictHuman; + static uint32_t m_restrictDwarf; + static uint32_t m_restrictGnome; + static uint32_t m_restrictNightElf; + static uint32_t m_restrictDraenei; + static uint32_t m_restrictOrc; + static uint32_t m_restrictTroll; + static uint32_t m_restrictTauren; + static uint32_t m_restrictUndead; + static uint32_t m_restrictBloodElf; static TSGrowableArray s_characterList; - static CSimpleModelFFX* s_modelFrame; - static float s_charFacing; + static int32_t m_selectionIndex; // Static functions static void RenderPrep(); static void SetBackgroundModel(const char* modelPath); + static void EnumerateCharactersCallback(CHARACTER_INFO& info, void* param); + static void ShowCharacter(); static void SetCharFacing(float facing); static void ClearCharacterList(); static void UpdateCharacterList(); diff --git a/src/net/Types.hpp b/src/net/Types.hpp index fa3d356..b7792cd 100644 --- a/src/net/Types.hpp +++ b/src/net/Types.hpp @@ -1285,5 +1285,7 @@ struct CHARACTER_INFO { uint8_t firstLogin; }; +typedef void (*ENUMERATE_CHARACTERS_CALLBACK)(CHARACTER_INFO&, void*); + #endif diff --git a/src/net/connection/ClientConnection.cpp b/src/net/connection/ClientConnection.cpp index 9138d20..61025ab 100644 --- a/src/net/connection/ClientConnection.cpp +++ b/src/net/connection/ClientConnection.cpp @@ -141,6 +141,13 @@ void ClientConnection::GetCharacterList() { } } +void ClientConnection::EnumerateCharacters(ENUMERATE_CHARACTERS_CALLBACK fcn, void* param) { + STORM_ASSERT(fcn); + for (uint32_t i = 0; i < this->m_characterList.Count(); ++i) { + fcn(this->m_characterList[i], param); + } +} + void ClientConnection::CharacterLogin(uint64_t id) { this->Initiate(COP_LOGIN_CHARACTER, 76, nullptr); if (this->m_connected) { diff --git a/src/net/connection/ClientConnection.hpp b/src/net/connection/ClientConnection.hpp index a579464..2b79dd9 100644 --- a/src/net/connection/ClientConnection.hpp +++ b/src/net/connection/ClientConnection.hpp @@ -28,6 +28,7 @@ class ClientConnection : public RealmConnection { void AccountLogin_Finish(int32_t authResult); void AccountLogin_Queued(); void GetCharacterList(); + void EnumerateCharacters(ENUMERATE_CHARACTERS_CALLBACK fcn, void* param); void CharacterLogin(uint64_t id); void Cancel(int32_t errorCode); void Cleanup(); diff --git a/src/ui/ScriptFunctionsCharSelect.cpp b/src/ui/ScriptFunctionsCharSelect.cpp index 51d464f..bebef3c 100644 --- a/src/ui/ScriptFunctionsCharSelect.cpp +++ b/src/ui/ScriptFunctionsCharSelect.cpp @@ -19,7 +19,7 @@ int32_t Script_SetCharSelectModelFrame(lua_State* L) { auto frame = CScriptObject::GetScriptObjectByName(name, type); if (frame) { - CCharacterSelection::s_modelFrame = static_cast(frame); + CCharacterSelection::m_modelFrame = static_cast(frame); } return 0; @@ -54,7 +54,7 @@ int32_t Script_GetCharacterInfo(lua_State* L) { luaL_error(L, "Usage: GetCharacterInfo(index)"); } - int index = static_cast(lua_tonumber(L, 1)) - 1; + int32_t index = static_cast(lua_tonumber(L, 1)) - 1; if (index < 0 || index > CCharacterSelection::GetNumCharacters()) { lua_pushnil(L); // name lua_pushnil(L); // race @@ -108,7 +108,19 @@ int32_t Script_GetCharacterInfo(lua_State* L) { } int32_t Script_SelectCharacter(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isnumber(L, 1)) { + luaL_error(L, "Usage: SelectCharacter(index)"); + } + + int32_t index = static_cast(lua_tonumber(L, 1)) - 1; + if (index < 1 || index >= CCharacterSelection::GetNumCharacters()) { + index = 0; + } + + CCharacterSelection::m_selectionIndex = index; + CCharacterSelection::ShowCharacter(); + FrameScript_SignalEvent(8u, "%d", CCharacterSelection::m_selectionIndex + 1); + return 0; } int32_t Script_DeleteCharacter(lua_State* L) { @@ -131,7 +143,7 @@ int32_t Script_UpdateSelectionCustomizationScene(lua_State* L) { int32_t Script_GetCharacterSelectFacing(lua_State* L) { // Radian to Degree - lua_pushnumber(L, CCharacterSelection::s_charFacing * 57.29578f); + lua_pushnumber(L, CCharacterSelection::m_charFacing * 57.29578f); return 1; }