Fix character appearance, previews, mount seat, and online unequip

This commit is contained in:
Kelsi 2026-02-12 14:55:27 -08:00
parent 4a023e773b
commit 275914b4db
19 changed files with 743 additions and 113 deletions

View file

@ -117,6 +117,9 @@ private:
game::Race playerRace_ = game::Race::HUMAN;
game::Gender playerGender_ = game::Gender::MALE;
game::Class playerClass_ = game::Class::WARRIOR;
uint64_t spawnedPlayerGuid_ = 0;
uint32_t spawnedAppearanceBytes_ = 0;
uint8_t spawnedFacialFeatures_ = 0;
// Weapon model ID counter (starting high to avoid collision with character model IDs)
uint32_t nextWeaponModelId_ = 1000;

View file

@ -236,6 +236,7 @@ public:
Inventory& getInventory() { return inventory; }
const Inventory& getInventory() const { return inventory; }
bool consumeOnlineEquipmentDirty() { bool d = onlineEquipDirty_; onlineEquipDirty_ = false; return d; }
void unequipToBackpack(EquipSlot equipSlot);
// Targeting
void setTarget(uint64_t guid);

View file

@ -261,6 +261,8 @@ enum class Opcode : uint16_t {
SMSG_ITEM_QUERY_SINGLE_RESPONSE = 0x058,
CMSG_USE_ITEM = 0x00AB,
CMSG_AUTOEQUIP_ITEM = 0x10A,
CMSG_SWAP_ITEM = 0x10C,
CMSG_SWAP_INV_ITEM = 0x10D,
SMSG_INVENTORY_CHANGE_FAILURE = 0x112,
CMSG_INSPECT = 0x114,
SMSG_INSPECT_RESULTS = 0x115,

View file

@ -1495,6 +1495,21 @@ public:
static network::Packet build(uint8_t srcBag, uint8_t srcSlot);
};
/** CMSG_SWAP_ITEM packet builder */
class SwapItemPacket {
public:
// Order matches AzerothCore handler: destBag, destSlot, srcBag, srcSlot.
static network::Packet build(uint8_t dstBag, uint8_t dstSlot, uint8_t srcBag, uint8_t srcSlot);
};
/** CMSG_SWAP_INV_ITEM packet builder */
class SwapInvItemPacket {
public:
// WoW inventory: slots are in the "inventory" range (equipment 0-18, bags 19-22, backpack 23-38).
// This swaps two inventory slots directly.
static network::Packet build(uint8_t srcSlot, uint8_t dstSlot);
};
/** CMSG_LOOT_MONEY packet builder (empty body) */
class LootMoneyPacket {
public:

View file

@ -4,6 +4,8 @@
#include <GL/glew.h>
#include <memory>
#include <cstdint>
#include <string>
#include <vector>
namespace wowee {
namespace pipeline { class AssetManager; }
@ -25,6 +27,9 @@ public:
uint8_t hairStyle, uint8_t hairColor,
uint8_t facialHair, bool useFemaleModel = false);
// Apply equipment overlays/geosets using SMSG_CHAR_ENUM equipment data (ItemDisplayInfo.dbc).
bool applyEquipment(const std::vector<game::EquipmentItem>& equipment);
void update(float deltaTime);
void render();
void rotate(float yawDelta);
@ -56,6 +61,16 @@ private:
uint32_t instanceId_ = 0;
bool modelLoaded_ = false;
float modelYaw_ = 180.0f;
// Cached info from loadCharacter() for later recompositing.
game::Race race_ = game::Race::HUMAN;
game::Gender gender_ = game::Gender::MALE;
bool useFemaleModel_ = false;
uint8_t hairStyle_ = 0;
uint8_t facialHair_ = 0;
std::string bodySkinPath_;
std::vector<std::string> baseLayers_; // face + underwear, etc.
uint32_t skinTextureSlotIndex_ = 0;
};
} // namespace rendering

View file

@ -7,6 +7,7 @@
#include <functional>
#include <vector>
#include <memory>
#include <cstdint>
namespace wowee {
namespace game { class GameHandler; }
@ -39,6 +40,10 @@ private:
std::string statusMessage;
bool statusIsError = false;
// For many races/styles, CharSections hair color IDs are not guaranteed to be contiguous.
// We expose an index (hairColor) in the UI and map it to the actual DBC hairColorId here.
std::vector<uint8_t> hairColorIds_;
std::vector<game::Class> availableClasses;
void updateAvailableClasses();

View file

@ -4,8 +4,12 @@
#include <imgui.h>
#include <string>
#include <functional>
#include <memory>
namespace wowee { namespace ui {
namespace wowee {
namespace pipeline { class AssetManager; }
namespace rendering { class CharacterPreview; }
namespace ui {
/**
* Character selection screen UI
@ -22,6 +26,11 @@ public:
*/
void render(game::GameHandler& gameHandler);
void setAssetManager(pipeline::AssetManager* am) {
assetManager_ = am;
previewInitialized_ = false;
}
/**
* Set callback for character selection
* @param callback Function to call when character is selected (receives character GUID)
@ -83,6 +92,17 @@ private:
static std::string getConfigDir();
void saveLastCharacter(uint64_t guid);
uint64_t loadLastCharacter();
// Preview (3D character portrait)
pipeline::AssetManager* assetManager_ = nullptr;
std::unique_ptr<rendering::CharacterPreview> preview_;
bool previewInitialized_ = false;
uint64_t previewGuid_ = 0;
uint32_t previewAppearanceBytes_ = 0;
uint8_t previewFacialFeatures_ = 0;
bool previewUseFemaleModel_ = false;
uint64_t previewEquipHash_ = 0;
};
}} // namespace wowee::ui
} // namespace ui
} // namespace wowee