diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index e4baed7d..99de1ca5 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -364,7 +364,7 @@ public: void openVendor(uint64_t npcGuid); void closeVendor(); void buyItem(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint8_t count); - void sellItem(uint64_t vendorGuid, uint64_t itemGuid, uint8_t count); + void sellItem(uint64_t vendorGuid, uint64_t itemGuid, uint32_t count); void sellItemBySlot(int backpackIndex); void autoEquipItemBySlot(int backpackIndex); void useItemBySlot(int backpackIndex); diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index a55bfb5f..fcb7b2c0 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -1277,7 +1277,7 @@ public: /** CMSG_SELL_ITEM packet builder */ class SellItemPacket { public: - static network::Packet build(uint64_t vendorGuid, uint64_t itemGuid, uint8_t count); + static network::Packet build(uint64_t vendorGuid, uint64_t itemGuid, uint32_t count); }; /** SMSG_LIST_INVENTORY parser */ diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 84577564..9a704df7 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1183,9 +1183,49 @@ void GameHandler::handlePacket(network::Packet& packet) { } case Opcode::SMSG_LOOT_CLEAR_MONEY: case Opcode::SMSG_NPC_TEXT_UPDATE: - case Opcode::SMSG_SELL_ITEM: + break; + case Opcode::SMSG_SELL_ITEM: { + // uint64 vendorGuid, uint64 itemGuid, uint8 result + if ((packet.getSize() - packet.getReadPos()) >= 17) { + packet.readUInt64(); // vendorGuid + packet.readUInt64(); // itemGuid + uint8_t result = packet.readUInt8(); + if (result != 0) { + static const char* sellErrors[] = { + "OK", "Can't find item", "Can't sell item", + "Can't find vendor", "You don't own that item", + "Unknown error", "Only empty bag" + }; + const char* msg = (result < 7) ? sellErrors[result] : "Unknown sell error"; + addSystemChatMessage(std::string("Sell failed: ") + msg); + LOG_WARNING("SMSG_SELL_ITEM error: ", (int)result, " (", msg, ")"); + } + } + break; + } + case Opcode::SMSG_INVENTORY_CHANGE_FAILURE: { + if ((packet.getSize() - packet.getReadPos()) >= 1) { + uint8_t error = packet.readUInt8(); + if (error != 0) { + LOG_WARNING("SMSG_INVENTORY_CHANGE_FAILURE: error=", (int)error); + // Common error codes + std::string msg; + switch (error) { + case 1: msg = "Item slots occupied."; break; + case 4: msg = "Item doesn't go there."; break; + case 5: msg = "Bag is full."; break; + case 14: msg = "Can't equip that."; break; + case 23: msg = "Can't equip with two-handed weapon."; break; + case 26: msg = "Inventory full."; break; + case 29: msg = "Item is locked."; break; + default: msg = "Inventory error (" + std::to_string(error) + ")."; break; + } + addSystemChatMessage(msg); + } + } + break; + } case Opcode::SMSG_BUY_FAILED: - case Opcode::SMSG_INVENTORY_CHANGE_FAILURE: case Opcode::SMSG_GAMEOBJECT_QUERY_RESPONSE: case Opcode::MSG_RAID_TARGET_UPDATE: case Opcode::SMSG_QUESTGIVER_STATUS: @@ -2635,7 +2675,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { if (ch.guid == playerGuid) { ch.level = val; break; } } } - else if (key == 632) { playerMoneyCopper_ = val; } // PLAYER_FIELD_COINAGE + else if (key == 1219) { playerMoneyCopper_ = val; } // PLAYER_FIELD_COINAGE } if (applyInventoryFields(block.fields)) slotsChanged = true; if (slotsChanged) rebuildOnlineInventory(); @@ -2728,7 +2768,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { } } } - else if (key == 632) { + else if (key == 1219) { playerMoneyCopper_ = val; LOG_INFO("Money updated via VALUES: ", val, " copper"); } @@ -4072,7 +4112,7 @@ void GameHandler::buyItem(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, u socket->send(packet); } -void GameHandler::sellItem(uint64_t vendorGuid, uint64_t itemGuid, uint8_t count) { +void GameHandler::sellItem(uint64_t vendorGuid, uint64_t itemGuid, uint32_t count) { if (state != WorldState::IN_WORLD || !socket) return; auto packet = SellItemPacket::build(vendorGuid, itemGuid, count); socket->send(packet); @@ -4099,10 +4139,17 @@ void GameHandler::sellItemBySlot(int backpackIndex) { if (itemGuid == 0) { itemGuid = resolveOnlineItemGuid(slot.item.itemId); } + LOG_DEBUG("sellItemBySlot: slot=", backpackIndex, + " item=", slot.item.name, + " itemGuid=0x", std::hex, itemGuid, std::dec, + " vendorGuid=0x", std::hex, currentVendorItems.vendorGuid, std::dec); if (itemGuid != 0 && currentVendorItems.vendorGuid != 0) { sellItem(currentVendorItems.vendorGuid, itemGuid, 1); } else if (itemGuid == 0) { + addSystemChatMessage("Cannot sell: item not found in inventory."); LOG_WARNING("Sell failed: missing item GUID for slot ", backpackIndex); + } else { + addSystemChatMessage("Cannot sell: no vendor."); } } } diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 6118ebc6..8e01ac81 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -2104,11 +2104,11 @@ network::Packet BuyItemPacket::build(uint64_t vendorGuid, uint32_t itemId, uint3 return packet; } -network::Packet SellItemPacket::build(uint64_t vendorGuid, uint64_t itemGuid, uint8_t count) { +network::Packet SellItemPacket::build(uint64_t vendorGuid, uint64_t itemGuid, uint32_t count) { network::Packet packet(static_cast(Opcode::CMSG_SELL_ITEM)); packet.writeUInt64(vendorGuid); packet.writeUInt64(itemGuid); - packet.writeUInt8(count); + packet.writeUInt32(count); return packet; } diff --git a/src/ui/inventory_screen.cpp b/src/ui/inventory_screen.cpp index f11459fb..eeb7cb5c 100644 --- a/src/ui/inventory_screen.cpp +++ b/src/ui/inventory_screen.cpp @@ -1084,6 +1084,10 @@ void InventoryScreen::renderItemSlot(game::Inventory& inventory, const game::Ite // Right-click: vendor sell (if vendor mode) or auto-equip/unequip if (ImGui::IsItemClicked(ImGuiMouseButton_Right) && !holdingItem) { + LOG_DEBUG("Right-click slot: kind=", (int)kind, + " backpackIndex=", backpackIndex, + " vendorMode=", vendorMode_, + " hasHandler=", (gameHandler_ != nullptr)); if (vendorMode_ && gameHandler_ && kind == SlotKind::BACKPACK && backpackIndex >= 0) { // Sell to vendor gameHandler_->sellItemBySlot(backpackIndex);