From 03cb7e699ebd3ff55713c45ea31f78e5d67dcb59 Mon Sep 17 00:00:00 2001 From: VDm Date: Sun, 18 May 2025 16:12:28 +0400 Subject: [PATCH] feat(glue): implement methods of CCharacterCreation --- src/glue/CCharacterComponent.cpp | 7 ++ src/glue/CCharacterComponent.hpp | 2 + src/glue/CCharacterCreation.cpp | 64 ++++++++++++- src/glue/CCharacterCreation.hpp | 17 ++++ src/ui/ScriptFunctionsCharCreate.cpp | 133 ++++++++++++++++++++++++--- 5 files changed, 207 insertions(+), 16 deletions(-) diff --git a/src/glue/CCharacterComponent.cpp b/src/glue/CCharacterComponent.cpp index 6941ef0..ba5bfed 100644 --- a/src/glue/CCharacterComponent.cpp +++ b/src/glue/CCharacterComponent.cpp @@ -110,6 +110,9 @@ void CCharacterComponent::FreeComponent(CCharacterComponent* component) { // TODO: ObjectFree() } +void CCharacterComponent::ValidateComponentData(ComponentData* data, COMPONENT_CONTEXT context) { +} + CCharacterComponent::CCharacterComponent() { } @@ -135,3 +138,7 @@ bool CCharacterComponent::Init(ComponentData* data, const char* a3) { this->m_data = *data; return false; } + +void CCharacterComponent::RenderPrep(int32_t a2) { + // TODO +} diff --git a/src/glue/CCharacterComponent.hpp b/src/glue/CCharacterComponent.hpp index e4d27da..c5d4935 100644 --- a/src/glue/CCharacterComponent.hpp +++ b/src/glue/CCharacterComponent.hpp @@ -42,6 +42,7 @@ class CCharacterComponent { static void Initialize(EGxTexFormat format, uint32_t mipLevels, int32_t useThreads, int32_t useCompression); static CCharacterComponent* AllocComponent(); static void FreeComponent(CCharacterComponent* component); + static void ValidateComponentData(ComponentData* data, COMPONENT_CONTEXT context = DEFAULT_CONTEXT); CCharacterComponent(); ~CCharacterComponent(); @@ -53,6 +54,7 @@ class CCharacterComponent { void SetRandomFacialFeature(COMPONENT_CONTEXT context = DEFAULT_CONTEXT); bool Init(ComponentData* data, const char* a3); + void RenderPrep(int32_t a2); public: uint32_t m_handle; diff --git a/src/glue/CCharacterCreation.cpp b/src/glue/CCharacterCreation.cpp index c609e9b..7b77d95 100644 --- a/src/glue/CCharacterCreation.cpp +++ b/src/glue/CCharacterCreation.cpp @@ -93,7 +93,9 @@ void CCharacterCreation::ResetCharCustomizeInfo() { CCharacterCreation::CalcClasses(data.m_info.raceID); CCharacterCreation::InitCharacterComponent(&data, 1); - // TODO + CCharacterCreation::SetSelectedClass(CCharacterCreation::GetRandomClassID()); + + data.m_info.classID = CCharacterCreation::m_selectedClassID; CCharacterCreation::m_raceIndex = -1; for (uint32_t i = 0; i < CCharacterCreation::m_races.Count(); ++i) { @@ -103,7 +105,7 @@ void CCharacterCreation::ResetCharCustomizeInfo() { } } - // TODO + // TODO: CNameGen::LoadNames } void CCharacterCreation::GetRandomRaceAndSex(ComponentData* data) { @@ -193,5 +195,63 @@ void CCharacterCreation::SetSelectedSex(int32_t sexID) { } void CCharacterCreation::SetSelectedClass(int32_t classID) { + if (!CCharacterCreation::IsClassValid(classID)) { + return; + } + + CCharacterCreation::m_selectedClassID = classID; + ComponentData data; + data.m_info = CCharacterCreation::m_character->m_data.m_info; + data.m_info.classID = classID; + CCharacterComponent::ValidateComponentData(&data); + CCharacterCreation::InitCharacterComponent(&data, 0); + // TODO: sub_4E0FD0 + // TODO: sub_4E6AE0((int)CCharacterCreation::m_character, 1); +} + +void CCharacterCreation::CycleCharCustomization(CHAR_CUSTOMIZATION_TYPE customization, int32_t delta) { // TODO } + +void CCharacterCreation::RandomizeCharCustomization() { + CCharacterCreation::RandomizeCharFeatures(); + // TODO +} + +void CCharacterCreation::SetCharFacing(float facing) { + // TODO +} + +void CCharacterCreation::CreateCharacter(const char* name) { + // TODO +} + +void CCharacterCreation::SetToExistingCharacter(uint32_t index) { + // TODO +} + +int32_t CCharacterCreation::IsRaceClassValid(int32_t raceID, int32_t classID) { + for (int32_t i = 0; i < g_charBaseInfoDB.GetNumRecords(); ++i) { + auto record = g_charBaseInfoDB.GetRecordByIndex(i); + if (record && record->m_raceID == raceID && record->m_classID == classID) { + return 1; + } + } + + return 0; +} + +int32_t CCharacterCreation::IsClassValid(int32_t classID) { + for (uint32_t i = 0; i < CCharacterCreation::m_classes.Count(); i) { + auto record = CCharacterCreation::m_classes[i]; + if (record && record->m_ID == classID) { + return 1; + } + } + + return 0; +} + +int32_t CCharacterCreation::GetRandomClassID() { + return 1; +} diff --git a/src/glue/CCharacterCreation.hpp b/src/glue/CCharacterCreation.hpp index e99cf09..96f2144 100644 --- a/src/glue/CCharacterCreation.hpp +++ b/src/glue/CCharacterCreation.hpp @@ -13,6 +13,15 @@ class ChrClassesRec; class CCharacterCreation { + public: + enum CHAR_CUSTOMIZATION_TYPE { + CHAR_CUSTOMIZATION_SKIN = 0, + CHAR_CUSTOMIZATION_FACE = 1, + CHAR_CUSTOMIZATION_HAIR_STYLE = 2, + CHAR_CUSTOMIZATION_HAIR_COLOR = 3, + CHAR_CUSTOMIZATION_FACIAL_FEATURE = 4 + }; + public: // Static variables static int32_t m_selectedClassID; @@ -41,6 +50,14 @@ class CCharacterCreation { static void SetSelectedRace(int32_t raceID); static void SetSelectedSex(int32_t sexID); static void SetSelectedClass(int32_t classID); + static void CycleCharCustomization(CHAR_CUSTOMIZATION_TYPE customization, int32_t delta); + static void RandomizeCharCustomization(); + static void SetCharFacing(float facing); + static void CreateCharacter(const char* name); + static void SetToExistingCharacter(uint32_t index); + static int32_t IsRaceClassValid(int32_t raceID, int32_t classID); + static int32_t IsClassValid(int32_t classID); + static int32_t GetRandomClassID(); }; #endif // GLUE_C_CHARACTER_CREATION_HPP diff --git a/src/ui/ScriptFunctionsCharCreate.cpp b/src/ui/ScriptFunctionsCharCreate.cpp index 784e67d..75d5be2 100644 --- a/src/ui/ScriptFunctionsCharCreate.cpp +++ b/src/ui/ScriptFunctionsCharCreate.cpp @@ -9,6 +9,7 @@ #include "clientobject/Unit_C.hpp" #include "glue/CCharacterCreation.hpp" #include "glue/CCharacterComponent.hpp" +#include "glue/CCharacterSelection.hpp" #include "client/ClientServices.hpp" #include @@ -264,40 +265,75 @@ int32_t Script_SetSelectedClass(lua_State* L) { return 0; } -int32_t Script_UpdateCustomizationBackground(lua_State* L) { - WHOA_UNIMPLEMENTED(0); +int32_t Script_UpdateCustomizationBackground(lua_State*) { + CCharacterCreation::SetSelectedRace(CCharacterCreation::m_raceIndex); + return 0; } -int32_t Script_UpdateCustomizationScene(lua_State* L) { - WHOA_UNIMPLEMENTED(0); +int32_t Script_UpdateCustomizationScene(lua_State*) { + CCharacterCreation::m_character->RenderPrep(0); + return 0; } int32_t Script_CycleCharCustomization(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) { + return luaL_error(L, "Usage: CycleCharCustomization(index, delta)"); + } + + using CHAR_CUSTOMIZATION_TYPE = CCharacterCreation::CHAR_CUSTOMIZATION_TYPE; + + auto customization = static_cast(lua_tonumber(L, 1) - 1.0); + int32_t delta = static_cast(lua_tonumber(L, 2)); + + CCharacterCreation::CycleCharCustomization(customization, delta); + return 0; } -int32_t Script_RandomizeCharCustomization(lua_State* L) { - WHOA_UNIMPLEMENTED(0); +int32_t Script_RandomizeCharCustomization(lua_State*) { + CCharacterCreation::RandomizeCharCustomization(); + return 0; } int32_t Script_GetCharacterCreateFacing(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + lua_pushnumber(L, CCharacterCreation::m_charFacing * 57.29578); + return 1; } int32_t Script_SetCharacterCreateFacing(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isnumber(L, 1)) { + return luaL_error(L, "Usage: SetCharacterCreateFacing(degrees)"); + } + + // Degree to Radian + float facing = lua_tonumber(L, 1) * 0.017453292; + CCharacterCreation::SetCharFacing(facing); + return 1; } int32_t Script_GetRandomName(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + // TODO: Proper implementation + // WORKAROUND: + lua_pushstring(L, "RandomName"); + return 1; } int32_t Script_CreateCharacter(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (lua_isstring(L, 1)) { + lua_tolstring(L, 1, nullptr); + } + + CCharacterCreation::CreateCharacter(lua_tolstring(L, 1, nullptr)); + return 0; } int32_t Script_CustomizeExistingCharacter(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isnumber(L, 1)) { + return luaL_error(L, "Usage: CustomizeExistingCharacter(index)"); + } + + uint32_t index = static_cast(lua_tonumber(L, 1)) - 1; + CCharacterCreation::SetToExistingCharacter(index); + return 0; } int32_t Script_PaidChange_GetPreviousRaceIndex(lua_State* L) { @@ -317,11 +353,80 @@ int32_t Script_PaidChange_GetName(lua_State* L) { } int32_t Script_IsRaceClassValid(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) { + return luaL_error(L, "Usage: IsRaceClassValid(raceIndex, classIndex)"); + } + + uint32_t raceIndex = static_cast(lua_tonumber(L, 1)) - 1; + int32_t classIndex = static_cast(lua_tonumber(L, 2)) - 1; + + auto classRecord = g_chrClassesDB.GetRecordByIndex(classIndex); + if (!classRecord) { + return 0; + } + + int32_t raceID = 0; + if (raceIndex < CCharacterCreation::m_races.Count()) { + raceID = CCharacterCreation::m_races[raceIndex]; + } + + if (CCharacterCreation::IsRaceClassValid(raceID, classRecord->m_ID)) { + lua_pushnumber(L, 1.0); + } else { + lua_pushnil(L); + } + return 1; } int32_t Script_IsRaceClassRestricted(lua_State* L) { - WHOA_UNIMPLEMENTED(0); + if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) { + return luaL_error(L, "Usage: Script_IsRaceClassRestricted(raceID, classID)"); + } + + int32_t raceID = static_cast(lua_tonumber(L, 1)); + int32_t classID = static_cast(lua_tonumber(L, 2)); + + classID = (1 << classID); + + bool restricted = false; + switch (raceID) { + case 1: + restricted = (CCharacterSelection::m_restrictHuman & classID) != 0; + break; + case 2: + restricted = (CCharacterSelection::m_restrictOrc & classID) != 0; + break; + case 3: + restricted = (CCharacterSelection::m_restrictDwarf & classID) != 0; + break; + case 4: + restricted = (CCharacterSelection::m_restrictNightElf & classID) != 0; + break; + case 5: + restricted = (CCharacterSelection::m_restrictUndead & classID) != 0; + break; + case 6: + restricted = (CCharacterSelection::m_restrictTauren & classID) != 0; + break; + case 7: + restricted = (CCharacterSelection::m_restrictGnome & classID) != 0; + break; + case 8: + restricted = (CCharacterSelection::m_restrictTroll & classID) != 0; + break; + case 10: + restricted = (CCharacterSelection::m_restrictBloodElf & classID) != 0; + break; + case 11: + restricted = (CCharacterSelection::m_restrictDraenei & classID) != 0; + break; + default: + luaL_error(L, "Script_IsRaceClassRestricted: unsupported race ID(%d)", raceID); + break; + } + + lua_pushnumber(L, restricted ? 1.0 : 0.0); + return 1; } int32_t Script_GetCreateBackgroundModel(lua_State* L) {