mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Fix online equipment slot mapping, auto-equip packet, and backpack slot offsets
Correct PLAYER_FIELD_INV_SLOT_HEAD default from 322 to 324 (UNIT_END+0xB0) which was shifting every equipment slot by one position. Fix auto-detection to validate against known 3.3.5a base. Change CMSG_AUTOEQUIP_ITEM to send uint8 bag+slot instead of uint64 GUID, and add slot offset 23 for backpack items in both auto-equip and use-item packets.
This commit is contained in:
parent
e38c0213e4
commit
40c016ccdb
4 changed files with 58 additions and 20 deletions
|
|
@ -1134,7 +1134,7 @@ public:
|
|||
/** CMSG_AUTOEQUIP_ITEM packet builder */
|
||||
class AutoEquipItemPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t itemGuid);
|
||||
static network::Packet build(uint8_t srcBag, uint8_t srcSlot);
|
||||
};
|
||||
|
||||
/** CMSG_LOOT_RELEASE packet builder */
|
||||
|
|
|
|||
|
|
@ -3155,7 +3155,48 @@ void GameHandler::detectInventorySlotBases(const std::map<uint16_t, uint32_t>& f
|
|||
std::sort(matchingPairs.begin(), matchingPairs.end());
|
||||
|
||||
if (invSlotBase_ < 0) {
|
||||
invSlotBase_ = matchingPairs.front();
|
||||
// The lowest matching field is the first EQUIPPED slot (not necessarily HEAD).
|
||||
// With 2+ matches we can derive the true base: all matches must be at
|
||||
// even offsets from the base, spaced 2 fields per slot.
|
||||
// Use the known 3.3.5a default (324) and verify matches align to it.
|
||||
constexpr int knownBase = 324;
|
||||
constexpr int slotStride = 2;
|
||||
bool allAlign = true;
|
||||
for (uint16_t p : matchingPairs) {
|
||||
if (p < knownBase || (p - knownBase) % slotStride != 0) {
|
||||
allAlign = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allAlign) {
|
||||
invSlotBase_ = knownBase;
|
||||
} else {
|
||||
// Fallback: if we have 2+ matches, derive base from their spacing
|
||||
if (matchingPairs.size() >= 2) {
|
||||
uint16_t lo = matchingPairs[0];
|
||||
// lo must be base + 2*slotN, and slotN is 0..22
|
||||
// Try each possible slot for 'lo' and see if all others also land on valid slots
|
||||
for (int s = 0; s <= 22; s++) {
|
||||
int candidate = lo - s * slotStride;
|
||||
if (candidate < 0) break;
|
||||
bool ok = true;
|
||||
for (uint16_t p : matchingPairs) {
|
||||
int off = p - candidate;
|
||||
if (off < 0 || off % slotStride != 0 || off / slotStride > 22) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
invSlotBase_ = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (invSlotBase_ < 0) invSlotBase_ = knownBase;
|
||||
} else {
|
||||
invSlotBase_ = knownBase;
|
||||
}
|
||||
}
|
||||
packSlotBase_ = invSlotBase_ + (game::Inventory::NUM_EQUIP_SLOTS * 2);
|
||||
LOG_INFO("Detected inventory field base: equip=", invSlotBase_,
|
||||
" pack=", packSlotBase_);
|
||||
|
|
@ -3164,8 +3205,10 @@ void GameHandler::detectInventorySlotBases(const std::map<uint16_t, uint32_t>& f
|
|||
|
||||
bool GameHandler::applyInventoryFields(const std::map<uint16_t, uint32_t>& fields) {
|
||||
bool slotsChanged = false;
|
||||
int equipBase = (invSlotBase_ >= 0) ? invSlotBase_ : 322;
|
||||
int packBase = (packSlotBase_ >= 0) ? packSlotBase_ : 368;
|
||||
// WoW 3.3.5a: PLAYER_FIELD_INV_SLOT_HEAD = UNIT_END + 0x00B0 = 324
|
||||
// PLAYER_FIELD_PACK_SLOT_1 = UNIT_END + 0x00DE = 370
|
||||
int equipBase = (invSlotBase_ >= 0) ? invSlotBase_ : 324;
|
||||
int packBase = (packSlotBase_ >= 0) ? packSlotBase_ : 370;
|
||||
|
||||
for (const auto& [key, val] : fields) {
|
||||
if (key >= equipBase && key <= equipBase + (game::Inventory::NUM_EQUIP_SLOTS * 2 - 1)) {
|
||||
|
|
@ -4074,15 +4117,10 @@ void GameHandler::autoEquipItemBySlot(int backpackIndex) {
|
|||
return;
|
||||
}
|
||||
|
||||
uint64_t itemGuid = backpackSlotGuids_[backpackIndex];
|
||||
if (itemGuid == 0) {
|
||||
itemGuid = resolveOnlineItemGuid(slot.item.itemId);
|
||||
}
|
||||
if (itemGuid != 0 && state == WorldState::IN_WORLD && socket) {
|
||||
auto packet = AutoEquipItemPacket::build(itemGuid);
|
||||
if (state == WorldState::IN_WORLD && socket) {
|
||||
// WoW inventory: equipment 0-18, bags 19-22, backpack 23-38
|
||||
auto packet = AutoEquipItemPacket::build(0xFF, static_cast<uint8_t>(23 + backpackIndex));
|
||||
socket->send(packet);
|
||||
} else if (itemGuid == 0) {
|
||||
LOG_WARNING("Auto-equip failed: missing item GUID for slot ", backpackIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4101,7 +4139,8 @@ void GameHandler::useItemBySlot(int backpackIndex) {
|
|||
itemGuid = resolveOnlineItemGuid(slot.item.itemId);
|
||||
}
|
||||
if (itemGuid != 0 && state == WorldState::IN_WORLD && socket) {
|
||||
auto packet = UseItemPacket::build(0xFF, static_cast<uint8_t>(backpackIndex), itemGuid);
|
||||
// WoW inventory: equipment 0-18, bags 19-22, backpack 23-38
|
||||
auto packet = UseItemPacket::build(0xFF, static_cast<uint8_t>(23 + backpackIndex), itemGuid);
|
||||
socket->send(packet);
|
||||
} else if (itemGuid == 0) {
|
||||
LOG_WARNING("Use item failed: missing item GUID for slot ", backpackIndex);
|
||||
|
|
|
|||
|
|
@ -1921,9 +1921,10 @@ network::Packet UseItemPacket::build(uint8_t bagIndex, uint8_t slotIndex, uint64
|
|||
return packet;
|
||||
}
|
||||
|
||||
network::Packet AutoEquipItemPacket::build(uint64_t itemGuid) {
|
||||
network::Packet AutoEquipItemPacket::build(uint8_t srcBag, uint8_t srcSlot) {
|
||||
network::Packet packet(static_cast<uint16_t>(Opcode::CMSG_AUTOEQUIP_ITEM));
|
||||
packet.writeUInt64(itemGuid);
|
||||
packet.writeUInt8(srcBag);
|
||||
packet.writeUInt8(srcSlot);
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1097,18 +1097,16 @@ void InventoryScreen::renderItemSlot(game::Inventory& inventory, const game::Ite
|
|||
inventoryDirty = true;
|
||||
}
|
||||
} else if (kind == SlotKind::BACKPACK && backpackIndex >= 0) {
|
||||
bool looksEquipable = (item.inventoryType > 0) ||
|
||||
(item.armor > 0) ||
|
||||
(!item.subclassName.empty());
|
||||
if (gameHandler_ && !gameHandler_->isSinglePlayerMode()) {
|
||||
if (looksEquipable) {
|
||||
if (item.inventoryType > 0) {
|
||||
// Auto-equip (online)
|
||||
gameHandler_->autoEquipItemBySlot(backpackIndex);
|
||||
} else {
|
||||
// Use consumable (online)
|
||||
gameHandler_->useItemBySlot(backpackIndex);
|
||||
}
|
||||
} else if (looksEquipable) {
|
||||
} else if (item.inventoryType > 0 || item.armor > 0 ||
|
||||
!item.subclassName.empty()) {
|
||||
// Auto-equip (single-player)
|
||||
uint8_t equippingType = item.inventoryType;
|
||||
game::EquipSlot targetSlot = getEquipSlotForType(equippingType, inventory);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue