From 6f2d9174c99dbc34aed2a7d9e877e64adc9e813c Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 5 Feb 2026 18:22:11 -0800 Subject: [PATCH] Clamp character appearance using DBC ranges --- include/ui/character_create_screen.hpp | 7 ++ src/ui/character_create_screen.cpp | 106 +++++++++++++++++++++++-- 2 files changed, 108 insertions(+), 5 deletions(-) diff --git a/include/ui/character_create_screen.hpp b/include/ui/character_create_screen.hpp index 490f1723..eb000991 100644 --- a/include/ui/character_create_screen.hpp +++ b/include/ui/character_create_screen.hpp @@ -34,6 +34,7 @@ private: int classIndex = 0; int genderIndex = 0; int skin = 0, face = 0, hairStyle = 0, hairColor = 0, facialHair = 0; + int maxSkin = 9, maxFace = 9, maxHairStyle = 11, maxHairColor = 9, maxFacialHair = 8; std::string statusMessage; bool statusIsError = false; @@ -45,6 +46,7 @@ private: // 3D model preview std::unique_ptr preview_; + pipeline::AssetManager* assetManager_ = nullptr; int prevRaceIndex_ = -1; int prevGenderIndex_ = -1; int prevSkin_ = -1; @@ -52,10 +54,15 @@ private: int prevHairStyle_ = -1; int prevHairColor_ = -1; int prevFacialHair_ = -1; + int prevRangeRace_ = -1; + int prevRangeGender_ = -1; + int prevRangeSkin_ = -1; + int prevRangeHairStyle_ = -1; bool draggingPreview_ = false; float dragStartX_ = 0.0f; void updatePreviewIfNeeded(); + void updateAppearanceRanges(); }; } // namespace ui diff --git a/src/ui/character_create_screen.cpp b/src/ui/character_create_screen.cpp index aa9eac29..792ff829 100644 --- a/src/ui/character_create_screen.cpp +++ b/src/ui/character_create_screen.cpp @@ -1,6 +1,7 @@ #include "ui/character_create_screen.hpp" #include "rendering/character_preview.hpp" #include "game/game_handler.hpp" +#include "pipeline/asset_manager.hpp" #include #include @@ -41,6 +42,11 @@ void CharacterCreateScreen::reset() { hairStyle = 0; hairColor = 0; facialHair = 0; + maxSkin = 9; + maxFace = 9; + maxHairStyle = 11; + maxHairColor = 9; + maxFacialHair = 8; statusMessage.clear(); statusIsError = false; updateAvailableClasses(); @@ -53,9 +59,14 @@ void CharacterCreateScreen::reset() { prevHairStyle_ = -1; prevHairColor_ = -1; prevFacialHair_ = -1; + prevRangeRace_ = -1; + prevRangeGender_ = -1; + prevRangeSkin_ = -1; + prevRangeHairStyle_ = -1; } void CharacterCreateScreen::initializePreview(pipeline::AssetManager* am) { + assetManager_ = am; if (!preview_) { preview_ = std::make_unique(); preview_->initialize(am); @@ -120,6 +131,90 @@ void CharacterCreateScreen::updatePreviewIfNeeded() { } } +void CharacterCreateScreen::updateAppearanceRanges() { + if (raceIndex == prevRangeRace_ && + genderIndex == prevRangeGender_ && + skin == prevRangeSkin_ && + hairStyle == prevRangeHairStyle_) { + return; + } + + prevRangeRace_ = raceIndex; + prevRangeGender_ = genderIndex; + prevRangeSkin_ = skin; + prevRangeHairStyle_ = hairStyle; + + maxSkin = 9; + maxFace = 9; + maxHairStyle = 11; + maxHairColor = 9; + maxFacialHair = 8; + + if (!assetManager_) return; + auto dbc = assetManager_->loadDBC("CharSections.dbc"); + if (!dbc) return; + + uint32_t targetRaceId = static_cast(allRaces[raceIndex]); + uint32_t targetSexId = (genderIndex == 1) ? 1u : 0u; + + int skinMax = -1; + int hairStyleMax = -1; + for (uint32_t r = 0; r < dbc->getRecordCount(); r++) { + uint32_t raceId = dbc->getUInt32(r, 1); + uint32_t sexId = dbc->getUInt32(r, 2); + if (raceId != targetRaceId || sexId != targetSexId) continue; + + uint32_t baseSection = dbc->getUInt32(r, 3); + uint32_t variationIndex = dbc->getUInt32(r, 8); + uint32_t colorIndex = dbc->getUInt32(r, 9); + + if (baseSection == 0 && variationIndex == 0) { + skinMax = std::max(skinMax, static_cast(colorIndex)); + } else if (baseSection == 3) { + hairStyleMax = std::max(hairStyleMax, static_cast(variationIndex)); + } + } + + if (skinMax >= 0) { + maxSkin = skinMax; + if (skin > maxSkin) skin = maxSkin; + } + if (hairStyleMax >= 0) { + maxHairStyle = hairStyleMax; + if (hairStyle > maxHairStyle) hairStyle = maxHairStyle; + } + + int faceMax = -1; + int hairColorMax = -1; + for (uint32_t r = 0; r < dbc->getRecordCount(); r++) { + uint32_t raceId = dbc->getUInt32(r, 1); + uint32_t sexId = dbc->getUInt32(r, 2); + if (raceId != targetRaceId || sexId != targetSexId) continue; + + uint32_t baseSection = dbc->getUInt32(r, 3); + uint32_t variationIndex = dbc->getUInt32(r, 8); + uint32_t colorIndex = dbc->getUInt32(r, 9); + + if (baseSection == 1 && colorIndex == static_cast(skin)) { + faceMax = std::max(faceMax, static_cast(variationIndex)); + } else if (baseSection == 3 && variationIndex == static_cast(hairStyle)) { + hairColorMax = std::max(hairColorMax, static_cast(colorIndex)); + } + } + + if (faceMax >= 0) { + maxFace = faceMax; + if (face > maxFace) face = maxFace; + } + if (hairColorMax >= 0) { + maxHairColor = hairColorMax; + if (hairColor > maxHairColor) hairColor = maxHairColor; + } + if (facialHair > maxFacialHair) { + facialHair = maxFacialHair; + } +} + void CharacterCreateScreen::render(game::GameHandler& /*gameHandler*/) { // Render the preview to FBO before the ImGui frame if (preview_) { @@ -257,6 +352,7 @@ void CharacterCreateScreen::render(game::GameHandler& /*gameHandler*/) { ImGui::Spacing(); // Appearance sliders + updateAppearanceRanges(); game::Race currentRace = allRaces[raceIndex]; game::Gender currentGender = static_cast(genderIndex); @@ -275,11 +371,11 @@ void CharacterCreateScreen::render(game::GameHandler& /*gameHandler*/) { ImGui::SliderInt(id, val, 0, maxVal); }; - slider("Skin", &skin, game::getMaxSkin(currentRace, currentGender)); - slider("Face", &face, game::getMaxFace(currentRace, currentGender)); - slider("Hair Style", &hairStyle, game::getMaxHairStyle(currentRace, currentGender)); - slider("Hair Color", &hairColor, game::getMaxHairColor(currentRace, currentGender)); - slider("Facial Feature", &facialHair, game::getMaxFacialFeature(currentRace, currentGender)); + slider("Skin", &skin, maxSkin); + slider("Face", &face, maxFace); + slider("Hair Style", &hairStyle, maxHairStyle); + slider("Hair Color", &hairColor, maxHairColor); + slider("Facial Feature", &facialHair, maxFacialHair); ImGui::Spacing();