Add bag bar drag-to-reorder, fix three wrong WotLK opcodes

Bag bar: left-click drag bags to swap positions using CMSG_SWAP_ITEM
with INVENTORY_SLOT_BAG_0 (255). Local optimistic swap for instant
feedback. Camera controller now respects ImGui WantCaptureMouse.
Vendor auto-open bags only triggers once per session.

Fix opcodes: CMSG_GAMEOBJECT_USE 0x01B→0x0B1 (typo caused
SMSG_FORCEACTIONSHOW spam), CMSG_CANCEL_AURA 0x033→0x136,
SMSG_SELL_ITEM 0x1A4→0x1A1.
This commit is contained in:
Kelsi 2026-02-19 22:34:22 -08:00
parent 328ec9ea78
commit 38ad368c82
9 changed files with 168 additions and 48 deletions

View file

@ -9476,6 +9476,33 @@ void GameHandler::swapContainerItems(uint8_t srcBag, uint8_t srcSlot, uint8_t ds
socket->send(packet);
}
void GameHandler::swapBagSlots(int srcBagIndex, int dstBagIndex) {
if (srcBagIndex < 0 || srcBagIndex > 3 || dstBagIndex < 0 || dstBagIndex > 3) return;
if (srcBagIndex == dstBagIndex) return;
// Local swap for immediate visual feedback
auto srcEquip = static_cast<game::EquipSlot>(static_cast<int>(game::EquipSlot::BAG1) + srcBagIndex);
auto dstEquip = static_cast<game::EquipSlot>(static_cast<int>(game::EquipSlot::BAG1) + dstBagIndex);
auto srcItem = inventory.getEquipSlot(srcEquip).item;
auto dstItem = inventory.getEquipSlot(dstEquip).item;
inventory.setEquipSlot(srcEquip, dstItem);
inventory.setEquipSlot(dstEquip, srcItem);
// Also swap bag contents locally
inventory.swapBagContents(srcBagIndex, dstBagIndex);
// Send to server using CMSG_SWAP_ITEM with INVENTORY_SLOT_BAG_0 (255)
// CMSG_SWAP_INV_ITEM doesn't support bag equip slots (19-22)
if (socket && socket->isConnected()) {
uint8_t srcSlot = static_cast<uint8_t>(19 + srcBagIndex);
uint8_t dstSlot = static_cast<uint8_t>(19 + dstBagIndex);
LOG_INFO("swapBagSlots: bag ", srcBagIndex, " (slot ", (int)srcSlot,
") <-> bag ", dstBagIndex, " (slot ", (int)dstSlot, ")");
auto packet = SwapItemPacket::build(255, dstSlot, 255, srcSlot);
socket->send(packet);
}
}
void GameHandler::destroyItem(uint8_t bag, uint8_t slot, uint8_t count) {
if (state != WorldState::IN_WORLD || !socket) return;
if (count == 0) count = 1;

View file

@ -115,6 +115,12 @@ void Inventory::setBankBagSize(int bagIndex, int size) {
bankBags_[bagIndex].size = std::min(size, MAX_BAG_SIZE);
}
void Inventory::swapBagContents(int bagA, int bagB) {
if (bagA < 0 || bagA >= NUM_BAG_SLOTS || bagB < 0 || bagB >= NUM_BAG_SLOTS) return;
if (bagA == bagB) return;
std::swap(bags[bagA], bags[bagB]);
}
int Inventory::findFreeBackpackSlot() const {
for (int i = 0; i < BACKPACK_SLOTS; i++) {
if (backpack[i].empty()) return i;

View file

@ -513,7 +513,7 @@ void OpcodeTable::loadWotlkDefaults() {
{LogicalOpcode::SMSG_ENVIRONMENTALDAMAGELOG, 0x1FC},
{LogicalOpcode::CMSG_CAST_SPELL, 0x12E},
{LogicalOpcode::CMSG_CANCEL_CAST, 0x12F},
{LogicalOpcode::CMSG_CANCEL_AURA, 0x033},
{LogicalOpcode::CMSG_CANCEL_AURA, 0x136},
{LogicalOpcode::SMSG_CAST_FAILED, 0x130},
{LogicalOpcode::SMSG_SPELL_START, 0x131},
{LogicalOpcode::SMSG_SPELL_GO, 0x132},
@ -592,7 +592,7 @@ void OpcodeTable::loadWotlkDefaults() {
{LogicalOpcode::CMSG_LIST_INVENTORY, 0x19E},
{LogicalOpcode::SMSG_LIST_INVENTORY, 0x19F},
{LogicalOpcode::CMSG_SELL_ITEM, 0x1A0},
{LogicalOpcode::SMSG_SELL_ITEM, 0x1A4},
{LogicalOpcode::SMSG_SELL_ITEM, 0x1A1},
{LogicalOpcode::CMSG_BUY_ITEM, 0x1A2},
{LogicalOpcode::CMSG_BUYBACK_ITEM, 0x290},
{LogicalOpcode::SMSG_BUY_FAILED, 0x1A5},