mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
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:
parent
328ec9ea78
commit
38ad368c82
9 changed files with 168 additions and 48 deletions
|
|
@ -111,7 +111,7 @@
|
|||
"SMSG_ENVIRONMENTALDAMAGELOG": "0x1FC",
|
||||
"CMSG_CAST_SPELL": "0x12E",
|
||||
"CMSG_CANCEL_CAST": "0x12F",
|
||||
"CMSG_CANCEL_AURA": "0x033",
|
||||
"CMSG_CANCEL_AURA": "0x136",
|
||||
"SMSG_CAST_FAILED": "0x130",
|
||||
"SMSG_SPELL_START": "0x131",
|
||||
"SMSG_SPELL_GO": "0x132",
|
||||
|
|
@ -162,7 +162,7 @@
|
|||
"SMSG_GOSSIP_MESSAGE": "0x17D",
|
||||
"SMSG_GOSSIP_COMPLETE": "0x17E",
|
||||
"SMSG_NPC_TEXT_UPDATE": "0x180",
|
||||
"CMSG_GAMEOBJECT_USE": "0x01B",
|
||||
"CMSG_GAMEOBJECT_USE": "0x0B1",
|
||||
"CMSG_QUESTGIVER_STATUS_QUERY": "0x182",
|
||||
"SMSG_QUESTGIVER_STATUS": "0x183",
|
||||
"SMSG_QUESTGIVER_STATUS_MULTIPLE": "0x198",
|
||||
|
|
@ -186,7 +186,7 @@
|
|||
"CMSG_LIST_INVENTORY": "0x19E",
|
||||
"SMSG_LIST_INVENTORY": "0x19F",
|
||||
"CMSG_SELL_ITEM": "0x1A0",
|
||||
"SMSG_SELL_ITEM": "0x1A4",
|
||||
"SMSG_SELL_ITEM": "0x1A1",
|
||||
"CMSG_BUY_ITEM": "0x1A2",
|
||||
"CMSG_BUYBACK_ITEM": "0x290",
|
||||
"SMSG_BUY_FAILED": "0x1A5",
|
||||
|
|
|
|||
|
|
@ -833,6 +833,7 @@ public:
|
|||
void useItemInBag(int bagIndex, int slotIndex);
|
||||
void destroyItem(uint8_t bag, uint8_t slot, uint8_t count = 1);
|
||||
void swapContainerItems(uint8_t srcBag, uint8_t srcSlot, uint8_t dstBag, uint8_t dstSlot);
|
||||
void swapBagSlots(int srcBagIndex, int dstBagIndex);
|
||||
void useItemById(uint32_t itemId);
|
||||
bool isVendorWindowOpen() const { return vendorWindowOpen; }
|
||||
const ListInventoryData& getVendorItems() const { return currentVendorItems; }
|
||||
|
|
|
|||
|
|
@ -95,6 +95,9 @@ public:
|
|||
uint8_t getPurchasedBankBagSlots() const { return purchasedBankBagSlots_; }
|
||||
void setPurchasedBankBagSlots(uint8_t count) { purchasedBankBagSlots_ = count; }
|
||||
|
||||
// Swap two bag slots (equip items + contents)
|
||||
void swapBagContents(int bagA, int bagB);
|
||||
|
||||
// Utility
|
||||
int findFreeBackpackSlot() const;
|
||||
bool addItem(const ItemDef& item);
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ private:
|
|||
bool editingOfficerNote_ = false;
|
||||
char guildNoteEditBuffer_[256] = {0};
|
||||
bool refocusChatInput = false;
|
||||
bool vendorBagsOpened_ = false; // Track if bags were auto-opened for current vendor session
|
||||
bool chatWindowLocked = true;
|
||||
ImVec2 chatWindowPos_ = ImVec2(0.0f, 0.0f);
|
||||
bool chatWindowPosInit_ = false;
|
||||
|
|
@ -223,9 +224,11 @@ private:
|
|||
int actionBarDragSlot_ = -1;
|
||||
GLuint actionBarDragIcon_ = 0;
|
||||
|
||||
// Bag bar textures
|
||||
// Bag bar state
|
||||
GLuint backpackIconTexture_ = 0;
|
||||
GLuint emptyBagSlotTexture_ = 0;
|
||||
int bagBarPickedSlot_ = -1; // Visual drag in progress (-1 = none)
|
||||
int bagBarDragSource_ = -1; // Mouse pressed on this slot, waiting for drag or click (-1 = none)
|
||||
|
||||
// Chat settings
|
||||
bool chatShowTimestamps_ = false;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "rendering/camera_controller.hpp"
|
||||
#include <algorithm>
|
||||
#include <imgui.h>
|
||||
#include "rendering/terrain_manager.hpp"
|
||||
#include "rendering/wmo_renderer.hpp"
|
||||
#include "rendering/m2_renderer.hpp"
|
||||
|
|
@ -1416,14 +1417,17 @@ void CameraController::processMouseButton(const SDL_MouseButtonEvent& event) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Don't capture mouse when ImGui wants it (hovering UI windows)
|
||||
bool uiWantsMouse = ImGui::GetIO().WantCaptureMouse;
|
||||
|
||||
if (event.button == SDL_BUTTON_LEFT) {
|
||||
leftMouseDown = (event.state == SDL_PRESSED);
|
||||
leftMouseDown = (event.state == SDL_PRESSED) && !uiWantsMouse;
|
||||
if (event.state == SDL_PRESSED && event.clicks >= 2) {
|
||||
autoRunning = false;
|
||||
}
|
||||
}
|
||||
if (event.button == SDL_BUTTON_RIGHT) {
|
||||
rightMouseDown = (event.state == SDL_PRESSED);
|
||||
rightMouseDown = (event.state == SDL_PRESSED) && !uiWantsMouse;
|
||||
}
|
||||
|
||||
bool anyDown = leftMouseDown || rightMouseDown;
|
||||
|
|
|
|||
|
|
@ -360,19 +360,18 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
// Set vendor mode before rendering inventory
|
||||
inventoryScreen.setVendorMode(gameHandler.isVendorWindowOpen(), &gameHandler);
|
||||
|
||||
// Auto-open bags when vendor window opens
|
||||
// Auto-open bags once when vendor window first opens
|
||||
if (gameHandler.isVendorWindowOpen()) {
|
||||
if (inventoryScreen.isSeparateBags()) {
|
||||
if (!inventoryScreen.isBackpackOpen() &&
|
||||
!inventoryScreen.isBagOpen(0) &&
|
||||
!inventoryScreen.isBagOpen(1) &&
|
||||
!inventoryScreen.isBagOpen(2) &&
|
||||
!inventoryScreen.isBagOpen(3)) {
|
||||
if (!vendorBagsOpened_) {
|
||||
vendorBagsOpened_ = true;
|
||||
if (inventoryScreen.isSeparateBags()) {
|
||||
inventoryScreen.openAllBags();
|
||||
} else if (!inventoryScreen.isOpen()) {
|
||||
inventoryScreen.setOpen(true);
|
||||
}
|
||||
} else if (!inventoryScreen.isOpen()) {
|
||||
inventoryScreen.setOpen(true);
|
||||
}
|
||||
} else {
|
||||
vendorBagsOpened_ = false;
|
||||
}
|
||||
|
||||
// Bags (B key toggle handled inside)
|
||||
|
|
@ -3591,6 +3590,10 @@ void GameScreen::renderBagBar(game::GameHandler& gameHandler) {
|
|||
}
|
||||
}
|
||||
|
||||
// Track bag slot screen rects for drop detection
|
||||
ImVec2 bagSlotMins[4], bagSlotMaxs[4];
|
||||
GLuint bagIcons[4] = {};
|
||||
|
||||
// Slots 1-4: Bag slots (leftmost)
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (i > 0) ImGui::SameLine(0, spacing);
|
||||
|
|
@ -3603,47 +3606,59 @@ void GameScreen::renderBagBar(game::GameHandler& gameHandler) {
|
|||
if (!bagItem.empty() && bagItem.item.displayInfoId != 0) {
|
||||
bagIcon = inventoryScreen.getItemIcon(bagItem.item.displayInfoId);
|
||||
}
|
||||
bagIcons[i] = bagIcon;
|
||||
|
||||
// Render the slot as an invisible button so we control all interaction
|
||||
ImVec2 cpos = ImGui::GetCursorScreenPos();
|
||||
ImGui::InvisibleButton("##bagSlot", ImVec2(slotSize, slotSize));
|
||||
bagSlotMins[i] = cpos;
|
||||
bagSlotMaxs[i] = ImVec2(cpos.x + slotSize, cpos.y + slotSize);
|
||||
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
|
||||
// Draw background + icon
|
||||
if (bagIcon) {
|
||||
if (ImGui::ImageButton("##bag", (ImTextureID)(uintptr_t)bagIcon,
|
||||
ImVec2(slotSize, slotSize),
|
||||
ImVec2(0, 0), ImVec2(1, 1),
|
||||
ImVec4(0.1f, 0.1f, 0.1f, 0.9f),
|
||||
ImVec4(1, 1, 1, 1))) {
|
||||
if (inventoryScreen.isSeparateBags())
|
||||
inventoryScreen.toggleBag(i);
|
||||
else
|
||||
inventoryScreen.toggle();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", bagItem.item.name.c_str());
|
||||
}
|
||||
dl->AddRectFilled(bagSlotMins[i], bagSlotMaxs[i], IM_COL32(25, 25, 25, 230));
|
||||
dl->AddImage((ImTextureID)(uintptr_t)bagIcon, bagSlotMins[i], bagSlotMaxs[i]);
|
||||
} else {
|
||||
// Empty bag slot
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.15f, 0.15f, 0.8f));
|
||||
if (ImGui::Button("##empty", ImVec2(slotSize, slotSize))) {
|
||||
// Empty slot - no bag equipped
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Empty Bag Slot");
|
||||
}
|
||||
dl->AddRectFilled(bagSlotMins[i], bagSlotMaxs[i], IM_COL32(38, 38, 38, 204));
|
||||
}
|
||||
|
||||
if (inventoryScreen.isSeparateBags() &&
|
||||
inventoryScreen.isBagOpen(i)) {
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
ImVec2 r0 = ImGui::GetItemRectMin();
|
||||
ImVec2 r1 = ImGui::GetItemRectMax();
|
||||
dl->AddRect(r0, r1, IM_COL32(255, 255, 255, 255), 3.0f, 0, 2.0f);
|
||||
// Hover highlight
|
||||
bool hovered = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
|
||||
if (hovered && bagBarPickedSlot_ < 0) {
|
||||
dl->AddRect(bagSlotMins[i], bagSlotMaxs[i], IM_COL32(255, 255, 255, 100));
|
||||
}
|
||||
|
||||
// Track which slot was pressed for drag detection
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && bagBarPickedSlot_ < 0 && bagIcon) {
|
||||
bagBarDragSource_ = i;
|
||||
}
|
||||
|
||||
// Click toggles bag open/close (handled in mouse release section below)
|
||||
|
||||
// Dim the slot being dragged
|
||||
if (bagBarPickedSlot_ == i) {
|
||||
dl->AddRectFilled(bagSlotMins[i], bagSlotMaxs[i], IM_COL32(0, 0, 0, 150));
|
||||
}
|
||||
|
||||
// Tooltip
|
||||
if (hovered && bagBarPickedSlot_ < 0) {
|
||||
if (bagIcon)
|
||||
ImGui::SetTooltip("%s", bagItem.item.name.c_str());
|
||||
else
|
||||
ImGui::SetTooltip("Empty Bag Slot");
|
||||
}
|
||||
|
||||
// Open bag indicator
|
||||
if (inventoryScreen.isSeparateBags() && inventoryScreen.isBagOpen(i)) {
|
||||
dl->AddRect(bagSlotMins[i], bagSlotMaxs[i], IM_COL32(255, 255, 255, 255), 3.0f, 0, 2.0f);
|
||||
}
|
||||
|
||||
// Accept dragged item from inventory
|
||||
if (ImGui::IsItemHovered() && inventoryScreen.isHoldingItem()) {
|
||||
if (hovered && inventoryScreen.isHoldingItem()) {
|
||||
const auto& heldItem = inventoryScreen.getHeldItem();
|
||||
// Check if held item is a bag (bagSlots > 0)
|
||||
if (heldItem.bagSlots > 0 && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
||||
// Equip the bag to inventory
|
||||
auto& inventory = gameHandler.getInventory();
|
||||
inventory.setEquipSlot(bagSlot, heldItem);
|
||||
inventoryScreen.returnHeldItem(inventory);
|
||||
|
|
@ -3653,6 +3668,46 @@ void GameScreen::renderBagBar(game::GameHandler& gameHandler) {
|
|||
ImGui::PopID();
|
||||
}
|
||||
|
||||
// Drag lifecycle: press on a slot sets bagBarDragSource_,
|
||||
// dragging 3+ pixels promotes to bagBarPickedSlot_ (visual drag),
|
||||
// releasing completes swap or click
|
||||
if (bagBarDragSource_ >= 0) {
|
||||
if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, 3.0f) && bagBarPickedSlot_ < 0) {
|
||||
// Mouse moved enough — start visual drag
|
||||
bagBarPickedSlot_ = bagBarDragSource_;
|
||||
}
|
||||
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
||||
if (bagBarPickedSlot_ >= 0) {
|
||||
// Was dragging — check for drop target
|
||||
ImVec2 mousePos = ImGui::GetIO().MousePos;
|
||||
int dropTarget = -1;
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
if (j == bagBarPickedSlot_) continue;
|
||||
if (mousePos.x >= bagSlotMins[j].x && mousePos.x <= bagSlotMaxs[j].x &&
|
||||
mousePos.y >= bagSlotMins[j].y && mousePos.y <= bagSlotMaxs[j].y) {
|
||||
dropTarget = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dropTarget >= 0) {
|
||||
gameHandler.swapBagSlots(bagBarPickedSlot_, dropTarget);
|
||||
}
|
||||
bagBarPickedSlot_ = -1;
|
||||
} else {
|
||||
// Was just a click (no drag) — toggle bag
|
||||
int slot = bagBarDragSource_;
|
||||
auto equip = static_cast<game::EquipSlot>(static_cast<int>(game::EquipSlot::BAG1) + slot);
|
||||
if (!inv.getEquipSlot(equip).empty()) {
|
||||
if (inventoryScreen.isSeparateBags())
|
||||
inventoryScreen.toggleBag(slot);
|
||||
else
|
||||
inventoryScreen.toggle();
|
||||
}
|
||||
}
|
||||
bagBarDragSource_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Backpack (rightmost slot)
|
||||
ImGui::SameLine(0, spacing);
|
||||
ImGui::PushID(0);
|
||||
|
|
@ -3692,6 +3747,27 @@ void GameScreen::renderBagBar(game::GameHandler& gameHandler) {
|
|||
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar(4);
|
||||
|
||||
// Draw dragged bag icon following cursor
|
||||
if (bagBarPickedSlot_ >= 0) {
|
||||
auto& inv2 = gameHandler.getInventory();
|
||||
auto pickedEquip = static_cast<game::EquipSlot>(
|
||||
static_cast<int>(game::EquipSlot::BAG1) + bagBarPickedSlot_);
|
||||
const auto& pickedItem = inv2.getEquipSlot(pickedEquip);
|
||||
GLuint pickedIcon = 0;
|
||||
if (!pickedItem.empty() && pickedItem.item.displayInfoId != 0) {
|
||||
pickedIcon = inventoryScreen.getItemIcon(pickedItem.item.displayInfoId);
|
||||
}
|
||||
if (pickedIcon) {
|
||||
ImVec2 mousePos = ImGui::GetIO().MousePos;
|
||||
float sz = 40.0f;
|
||||
ImVec2 p0(mousePos.x - sz * 0.5f, mousePos.y - sz * 0.5f);
|
||||
ImVec2 p1(mousePos.x + sz * 0.5f, mousePos.y + sz * 0.5f);
|
||||
ImDrawList* fg = ImGui::GetForegroundDrawList();
|
||||
fg->AddImage((ImTextureID)(uintptr_t)pickedIcon, p0, p1);
|
||||
fg->AddRect(p0, p1, IM_COL32(200, 200, 200, 255), 0.0f, 0, 2.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue