mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat(ui): show keyring in inventory
This commit is contained in:
parent
800862c50a
commit
2c32b72f95
7 changed files with 152 additions and 10 deletions
|
|
@ -2534,6 +2534,7 @@ private:
|
||||||
std::unordered_set<uint32_t> pendingItemQueries_;
|
std::unordered_set<uint32_t> pendingItemQueries_;
|
||||||
std::array<uint64_t, 23> equipSlotGuids_{};
|
std::array<uint64_t, 23> equipSlotGuids_{};
|
||||||
std::array<uint64_t, 16> backpackSlotGuids_{};
|
std::array<uint64_t, 16> backpackSlotGuids_{};
|
||||||
|
std::array<uint64_t, 32> keyringSlotGuids_{};
|
||||||
// Container (bag) contents: containerGuid -> array of item GUIDs per slot
|
// Container (bag) contents: containerGuid -> array of item GUIDs per slot
|
||||||
struct ContainerInfo {
|
struct ContainerInfo {
|
||||||
uint32_t numSlots = 0;
|
uint32_t numSlots = 0;
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ struct ItemSlot {
|
||||||
class Inventory {
|
class Inventory {
|
||||||
public:
|
public:
|
||||||
static constexpr int BACKPACK_SLOTS = 16;
|
static constexpr int BACKPACK_SLOTS = 16;
|
||||||
|
static constexpr int KEYRING_SLOTS = 32;
|
||||||
static constexpr int NUM_EQUIP_SLOTS = 23;
|
static constexpr int NUM_EQUIP_SLOTS = 23;
|
||||||
static constexpr int NUM_BAG_SLOTS = 4;
|
static constexpr int NUM_BAG_SLOTS = 4;
|
||||||
static constexpr int MAX_BAG_SIZE = 36;
|
static constexpr int MAX_BAG_SIZE = 36;
|
||||||
|
|
@ -88,6 +89,12 @@ public:
|
||||||
bool setEquipSlot(EquipSlot slot, const ItemDef& item);
|
bool setEquipSlot(EquipSlot slot, const ItemDef& item);
|
||||||
bool clearEquipSlot(EquipSlot slot);
|
bool clearEquipSlot(EquipSlot slot);
|
||||||
|
|
||||||
|
// Keyring
|
||||||
|
const ItemSlot& getKeyringSlot(int index) const;
|
||||||
|
bool setKeyringSlot(int index, const ItemDef& item);
|
||||||
|
bool clearKeyringSlot(int index);
|
||||||
|
int getKeyringSize() const { return KEYRING_SLOTS; }
|
||||||
|
|
||||||
// Extra bags
|
// Extra bags
|
||||||
int getBagSize(int bagIndex) const;
|
int getBagSize(int bagIndex) const;
|
||||||
void setBagSize(int bagIndex, int size);
|
void setBagSize(int bagIndex, int size);
|
||||||
|
|
@ -123,6 +130,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<ItemSlot, BACKPACK_SLOTS> backpack{};
|
std::array<ItemSlot, BACKPACK_SLOTS> backpack{};
|
||||||
|
std::array<ItemSlot, KEYRING_SLOTS> keyring_{};
|
||||||
std::array<ItemSlot, NUM_EQUIP_SLOTS> equipment{};
|
std::array<ItemSlot, NUM_EQUIP_SLOTS> equipment{};
|
||||||
|
|
||||||
struct BagData {
|
struct BagData {
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ enum class UF : uint16_t {
|
||||||
PLAYER_QUEST_LOG_START,
|
PLAYER_QUEST_LOG_START,
|
||||||
PLAYER_FIELD_INV_SLOT_HEAD,
|
PLAYER_FIELD_INV_SLOT_HEAD,
|
||||||
PLAYER_FIELD_PACK_SLOT_1,
|
PLAYER_FIELD_PACK_SLOT_1,
|
||||||
|
PLAYER_FIELD_KEYRING_SLOT_1,
|
||||||
PLAYER_FIELD_BANK_SLOT_1,
|
PLAYER_FIELD_BANK_SLOT_1,
|
||||||
PLAYER_FIELD_BANKBAG_SLOT_1,
|
PLAYER_FIELD_BANKBAG_SLOT_1,
|
||||||
PLAYER_SKILL_INFO_START,
|
PLAYER_SKILL_INFO_START,
|
||||||
|
|
|
||||||
|
|
@ -8447,6 +8447,7 @@ void GameHandler::selectCharacter(uint64_t characterGuid) {
|
||||||
pendingItemQueries_.clear();
|
pendingItemQueries_.clear();
|
||||||
equipSlotGuids_ = {};
|
equipSlotGuids_ = {};
|
||||||
backpackSlotGuids_ = {};
|
backpackSlotGuids_ = {};
|
||||||
|
keyringSlotGuids_ = {};
|
||||||
invSlotBase_ = -1;
|
invSlotBase_ = -1;
|
||||||
packSlotBase_ = -1;
|
packSlotBase_ = -1;
|
||||||
lastPlayerFields_.clear();
|
lastPlayerFields_.clear();
|
||||||
|
|
@ -13597,6 +13598,21 @@ bool GameHandler::applyInventoryFields(const std::map<uint16_t, uint32_t>& field
|
||||||
bool slotsChanged = false;
|
bool slotsChanged = false;
|
||||||
int equipBase = (invSlotBase_ >= 0) ? invSlotBase_ : static_cast<int>(fieldIndex(UF::PLAYER_FIELD_INV_SLOT_HEAD));
|
int equipBase = (invSlotBase_ >= 0) ? invSlotBase_ : static_cast<int>(fieldIndex(UF::PLAYER_FIELD_INV_SLOT_HEAD));
|
||||||
int packBase = (packSlotBase_ >= 0) ? packSlotBase_ : static_cast<int>(fieldIndex(UF::PLAYER_FIELD_PACK_SLOT_1));
|
int packBase = (packSlotBase_ >= 0) ? packSlotBase_ : static_cast<int>(fieldIndex(UF::PLAYER_FIELD_PACK_SLOT_1));
|
||||||
|
int bankBase = static_cast<int>(fieldIndex(UF::PLAYER_FIELD_BANK_SLOT_1));
|
||||||
|
int bankBagBase = static_cast<int>(fieldIndex(UF::PLAYER_FIELD_BANKBAG_SLOT_1));
|
||||||
|
|
||||||
|
// Derive slot counts from field gap (Classic=24/6, TBC/WotLK=28/7).
|
||||||
|
if (bankBase != 0xFFFF && bankBagBase != 0xFFFF) {
|
||||||
|
effectiveBankSlots_ = std::min((bankBagBase - bankBase) / 2, 28);
|
||||||
|
effectiveBankBagSlots_ = (effectiveBankSlots_ <= 24) ? 6 : 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
int keyringBase = static_cast<int>(fieldIndex(UF::PLAYER_FIELD_KEYRING_SLOT_1));
|
||||||
|
if (keyringBase == 0xFFFF && bankBagBase != 0xFFFF) {
|
||||||
|
// Layout fallback for profiles that don't define PLAYER_FIELD_KEYRING_SLOT_1.
|
||||||
|
// Bank bag slots are followed by 12 vendor buyback slots (24 fields), then keyring.
|
||||||
|
keyringBase = bankBagBase + (effectiveBankBagSlots_ * 2) + 24;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& [key, val] : fields) {
|
for (const auto& [key, val] : fields) {
|
||||||
if (key >= equipBase && key <= equipBase + (game::Inventory::NUM_EQUIP_SLOTS * 2 - 1)) {
|
if (key >= equipBase && key <= equipBase + (game::Inventory::NUM_EQUIP_SLOTS * 2 - 1)) {
|
||||||
|
|
@ -13617,15 +13633,17 @@ bool GameHandler::applyInventoryFields(const std::map<uint16_t, uint32_t>& field
|
||||||
else guid = (guid & 0x00000000FFFFFFFFULL) | (uint64_t(val) << 32);
|
else guid = (guid & 0x00000000FFFFFFFFULL) | (uint64_t(val) << 32);
|
||||||
slotsChanged = true;
|
slotsChanged = true;
|
||||||
}
|
}
|
||||||
}
|
} else if (keyringBase != 0xFFFF &&
|
||||||
|
key >= keyringBase &&
|
||||||
// Bank slots starting at PLAYER_FIELD_BANK_SLOT_1
|
key <= keyringBase + (game::Inventory::KEYRING_SLOTS * 2 - 1)) {
|
||||||
int bankBase = static_cast<int>(fieldIndex(UF::PLAYER_FIELD_BANK_SLOT_1));
|
int slotIndex = (key - keyringBase) / 2;
|
||||||
int bankBagBase = static_cast<int>(fieldIndex(UF::PLAYER_FIELD_BANKBAG_SLOT_1));
|
bool isLow = ((key - keyringBase) % 2 == 0);
|
||||||
// Derive slot counts from field gap (Classic=24/6, TBC/WotLK=28/7)
|
if (slotIndex < static_cast<int>(keyringSlotGuids_.size())) {
|
||||||
if (bankBase != 0xFFFF && bankBagBase != 0xFFFF) {
|
uint64_t& guid = keyringSlotGuids_[slotIndex];
|
||||||
effectiveBankSlots_ = std::min((bankBagBase - bankBase) / 2, 28);
|
if (isLow) guid = (guid & 0xFFFFFFFF00000000ULL) | val;
|
||||||
effectiveBankBagSlots_ = (effectiveBankSlots_ <= 24) ? 6 : 7;
|
else guid = (guid & 0x00000000FFFFFFFFULL) | (uint64_t(val) << 32);
|
||||||
|
slotsChanged = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (bankBase != 0xFFFF && key >= static_cast<uint16_t>(bankBase) &&
|
if (bankBase != 0xFFFF && key >= static_cast<uint16_t>(bankBase) &&
|
||||||
key <= static_cast<uint16_t>(bankBase) + (effectiveBankSlots_ * 2 - 1)) {
|
key <= static_cast<uint16_t>(bankBase) + (effectiveBankSlots_ * 2 - 1)) {
|
||||||
|
|
@ -13786,6 +13804,55 @@ void GameHandler::rebuildOnlineInventory() {
|
||||||
inventory.setBackpackSlot(i, def);
|
inventory.setBackpackSlot(i, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keyring slots
|
||||||
|
for (int i = 0; i < game::Inventory::KEYRING_SLOTS; i++) {
|
||||||
|
uint64_t guid = keyringSlotGuids_[i];
|
||||||
|
if (guid == 0) continue;
|
||||||
|
|
||||||
|
auto itemIt = onlineItems_.find(guid);
|
||||||
|
if (itemIt == onlineItems_.end()) continue;
|
||||||
|
|
||||||
|
ItemDef def;
|
||||||
|
def.itemId = itemIt->second.entry;
|
||||||
|
def.stackCount = itemIt->second.stackCount;
|
||||||
|
def.curDurability = itemIt->second.curDurability;
|
||||||
|
def.maxDurability = itemIt->second.maxDurability;
|
||||||
|
def.maxStack = 1;
|
||||||
|
|
||||||
|
auto infoIt = itemInfoCache_.find(itemIt->second.entry);
|
||||||
|
if (infoIt != itemInfoCache_.end()) {
|
||||||
|
def.name = infoIt->second.name;
|
||||||
|
def.quality = static_cast<ItemQuality>(infoIt->second.quality);
|
||||||
|
def.inventoryType = infoIt->second.inventoryType;
|
||||||
|
def.maxStack = std::max(1, infoIt->second.maxStack);
|
||||||
|
def.displayInfoId = infoIt->second.displayInfoId;
|
||||||
|
def.subclassName = infoIt->second.subclassName;
|
||||||
|
def.damageMin = infoIt->second.damageMin;
|
||||||
|
def.damageMax = infoIt->second.damageMax;
|
||||||
|
def.delayMs = infoIt->second.delayMs;
|
||||||
|
def.armor = infoIt->second.armor;
|
||||||
|
def.stamina = infoIt->second.stamina;
|
||||||
|
def.strength = infoIt->second.strength;
|
||||||
|
def.agility = infoIt->second.agility;
|
||||||
|
def.intellect = infoIt->second.intellect;
|
||||||
|
def.spirit = infoIt->second.spirit;
|
||||||
|
def.sellPrice = infoIt->second.sellPrice;
|
||||||
|
def.itemLevel = infoIt->second.itemLevel;
|
||||||
|
def.requiredLevel = infoIt->second.requiredLevel;
|
||||||
|
def.bindType = infoIt->second.bindType;
|
||||||
|
def.description = infoIt->second.description;
|
||||||
|
def.startQuestId = infoIt->second.startQuestId;
|
||||||
|
def.extraStats.clear();
|
||||||
|
for (const auto& es : infoIt->second.extraStats)
|
||||||
|
def.extraStats.push_back({es.statType, es.statValue});
|
||||||
|
} else {
|
||||||
|
def.name = "Item " + std::to_string(def.itemId);
|
||||||
|
queryItemInfo(def.itemId, guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
inventory.setKeyringSlot(i, def);
|
||||||
|
}
|
||||||
|
|
||||||
// Bag contents (BAG1-BAG4 are equip slots 19-22)
|
// Bag contents (BAG1-BAG4 are equip slots 19-22)
|
||||||
for (int bagIdx = 0; bagIdx < 4; bagIdx++) {
|
for (int bagIdx = 0; bagIdx < 4; bagIdx++) {
|
||||||
uint64_t bagGuid = equipSlotGuids_[19 + bagIdx];
|
uint64_t bagGuid = equipSlotGuids_[19 + bagIdx];
|
||||||
|
|
@ -14029,6 +14096,8 @@ void GameHandler::rebuildOnlineInventory() {
|
||||||
int c = 0; for (auto g : equipSlotGuids_) if (g) c++; return c;
|
int c = 0; for (auto g : equipSlotGuids_) if (g) c++; return c;
|
||||||
}(), " backpack=", [&](){
|
}(), " backpack=", [&](){
|
||||||
int c = 0; for (auto g : backpackSlotGuids_) if (g) c++; return c;
|
int c = 0; for (auto g : backpackSlotGuids_) if (g) c++; return c;
|
||||||
|
}(), " keyring=", [&](){
|
||||||
|
int c = 0; for (auto g : keyringSlotGuids_) if (g) c++; return c;
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,23 @@ bool Inventory::clearEquipSlot(EquipSlot slot) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ItemSlot& Inventory::getKeyringSlot(int index) const {
|
||||||
|
if (index < 0 || index >= KEYRING_SLOTS) return EMPTY_SLOT;
|
||||||
|
return keyring_[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Inventory::setKeyringSlot(int index, const ItemDef& item) {
|
||||||
|
if (index < 0 || index >= KEYRING_SLOTS) return false;
|
||||||
|
keyring_[index].item = item;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Inventory::clearKeyringSlot(int index) {
|
||||||
|
if (index < 0 || index >= KEYRING_SLOTS) return false;
|
||||||
|
keyring_[index].item = ItemDef{};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int Inventory::getBagSize(int bagIndex) const {
|
int Inventory::getBagSize(int bagIndex) const {
|
||||||
if (bagIndex < 0 || bagIndex >= NUM_BAG_SLOTS) return 0;
|
if (bagIndex < 0 || bagIndex >= NUM_BAG_SLOTS) return 0;
|
||||||
return bags[bagIndex].size;
|
return bags[bagIndex].size;
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ static const UFNameEntry kUFNames[] = {
|
||||||
{"PLAYER_QUEST_LOG_START", UF::PLAYER_QUEST_LOG_START},
|
{"PLAYER_QUEST_LOG_START", UF::PLAYER_QUEST_LOG_START},
|
||||||
{"PLAYER_FIELD_INV_SLOT_HEAD", UF::PLAYER_FIELD_INV_SLOT_HEAD},
|
{"PLAYER_FIELD_INV_SLOT_HEAD", UF::PLAYER_FIELD_INV_SLOT_HEAD},
|
||||||
{"PLAYER_FIELD_PACK_SLOT_1", UF::PLAYER_FIELD_PACK_SLOT_1},
|
{"PLAYER_FIELD_PACK_SLOT_1", UF::PLAYER_FIELD_PACK_SLOT_1},
|
||||||
|
{"PLAYER_FIELD_KEYRING_SLOT_1", UF::PLAYER_FIELD_KEYRING_SLOT_1},
|
||||||
{"PLAYER_FIELD_BANK_SLOT_1", UF::PLAYER_FIELD_BANK_SLOT_1},
|
{"PLAYER_FIELD_BANK_SLOT_1", UF::PLAYER_FIELD_BANK_SLOT_1},
|
||||||
{"PLAYER_FIELD_BANKBAG_SLOT_1", UF::PLAYER_FIELD_BANKBAG_SLOT_1},
|
{"PLAYER_FIELD_BANKBAG_SLOT_1", UF::PLAYER_FIELD_BANKBAG_SLOT_1},
|
||||||
{"PLAYER_SKILL_INFO_START", UF::PLAYER_SKILL_INFO_START},
|
{"PLAYER_SKILL_INFO_START", UF::PLAYER_SKILL_INFO_START},
|
||||||
|
|
|
||||||
|
|
@ -1017,7 +1017,11 @@ void InventoryScreen::renderBagWindow(const char* title, bool& isOpen,
|
||||||
|
|
||||||
int rows = (numSlots + columns - 1) / columns;
|
int rows = (numSlots + columns - 1) / columns;
|
||||||
float contentH = rows * (slotSize + 4.0f) + 10.0f;
|
float contentH = rows * (slotSize + 4.0f) + 10.0f;
|
||||||
if (bagIndex < 0) contentH += 25.0f; // money display for backpack
|
if (bagIndex < 0) {
|
||||||
|
int keyringRows = (inventory.getKeyringSize() + columns - 1) / columns;
|
||||||
|
contentH += 25.0f; // money display for backpack
|
||||||
|
contentH += 30.0f + keyringRows * (slotSize + 4.0f); // keyring header + slots
|
||||||
|
}
|
||||||
float gridW = columns * (slotSize + 4.0f) + 30.0f;
|
float gridW = columns * (slotSize + 4.0f) + 30.0f;
|
||||||
// Ensure window is wide enough for the title + close button
|
// Ensure window is wide enough for the title + close button
|
||||||
const char* displayTitle = title;
|
const char* displayTitle = title;
|
||||||
|
|
@ -1065,6 +1069,23 @@ void InventoryScreen::renderBagWindow(const char* title, bool& isOpen,
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bagIndex < 0) {
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.0f, 1.0f), "Keyring");
|
||||||
|
for (int i = 0; i < inventory.getKeyringSize(); ++i) {
|
||||||
|
if (i % columns != 0) ImGui::SameLine();
|
||||||
|
const auto& slot = inventory.getKeyringSlot(i);
|
||||||
|
char id[32];
|
||||||
|
snprintf(id, sizeof(id), "##skr_%d", i);
|
||||||
|
ImGui::PushID(id);
|
||||||
|
// Keyring is display-only for now.
|
||||||
|
renderItemSlot(inventory, slot, slotSize, nullptr,
|
||||||
|
SlotKind::BACKPACK, -1, game::EquipSlot::NUM_SLOTS);
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Money display at bottom of backpack
|
// Money display at bottom of backpack
|
||||||
if (bagIndex < 0 && moneyCopper > 0) {
|
if (bagIndex < 0 && moneyCopper > 0) {
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
@ -2020,6 +2041,30 @@ void InventoryScreen::renderBackpackPanel(game::Inventory& inventory, bool colla
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool keyringHasAnyItems = false;
|
||||||
|
for (int i = 0; i < inventory.getKeyringSize(); ++i) {
|
||||||
|
if (!inventory.getKeyringSlot(i).empty()) {
|
||||||
|
keyringHasAnyItems = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!collapseEmptySections || keyringHasAnyItems) {
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.0f, 1.0f), "Keyring");
|
||||||
|
for (int i = 0; i < inventory.getKeyringSize(); ++i) {
|
||||||
|
if (i % columns != 0) ImGui::SameLine();
|
||||||
|
const auto& slot = inventory.getKeyringSlot(i);
|
||||||
|
char sid[32];
|
||||||
|
snprintf(sid, sizeof(sid), "##keyring_%d", i);
|
||||||
|
ImGui::PushID(sid);
|
||||||
|
// Keyring is display-only for now.
|
||||||
|
renderItemSlot(inventory, slot, slotSize, nullptr,
|
||||||
|
SlotKind::BACKPACK, -1, game::EquipSlot::NUM_SLOTS);
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InventoryScreen::renderItemSlot(game::Inventory& inventory, const game::ItemSlot& slot,
|
void InventoryScreen::renderItemSlot(game::Inventory& inventory, const game::ItemSlot& slot,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue