mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: implement trade window UI with item slots and gold offering
Previously trade only showed an accept/decline popup with no way to actually offer items or gold. This commit adds the complete trade flow: Packets: - CMSG_SET_TRADE_ITEM (tradeSlot, bag, bagSlot) — add item to slot - CMSG_CLEAR_TRADE_ITEM (tradeSlot) — remove item from slot - CMSG_SET_TRADE_GOLD (uint64 copper) — set gold offered - CMSG_UNACCEPT_TRADE — unaccept without cancelling - SMSG_TRADE_STATUS_EXTENDED parser — updates trade slot/gold state State: - TradeSlot struct: itemId, displayId, stackCount, bag, bagSlot - myTradeSlots_/peerTradeSlots_ arrays (6 slots each) - myTradeGold_/peerTradeGold_ (copper) - resetTradeState() helper clears all state on cancel/complete/close UI (renderTradeWindow): - Two-column layout: my offer | peer offer - Each column shows 6 item slots with item names - Double-click own slot to remove; right-click empty slot to open backpack picker popup - Gold input field (copper, Enter to set) - Accept Trade / Cancel buttons - Window close button triggers cancel trade
This commit is contained in:
parent
7c5d688c00
commit
06facc0060
6 changed files with 337 additions and 5 deletions
|
|
@ -933,13 +933,38 @@ public:
|
|||
enum class TradeStatus : uint8_t {
|
||||
None = 0, PendingIncoming, Open, Accepted, Complete
|
||||
};
|
||||
|
||||
static constexpr int TRADE_SLOT_COUNT = 6; // WoW has 6 normal trade slots + slot 6 for non-trade item
|
||||
|
||||
struct TradeSlot {
|
||||
uint32_t itemId = 0;
|
||||
uint32_t displayId = 0;
|
||||
uint32_t stackCount = 0;
|
||||
uint64_t itemGuid = 0;
|
||||
uint8_t bag = 0xFF; // 0xFF = not set
|
||||
uint8_t bagSlot = 0xFF;
|
||||
bool occupied = false;
|
||||
};
|
||||
|
||||
TradeStatus getTradeStatus() const { return tradeStatus_; }
|
||||
bool hasPendingTradeRequest() const { return tradeStatus_ == TradeStatus::PendingIncoming; }
|
||||
bool isTradeOpen() const { return tradeStatus_ == TradeStatus::Open || tradeStatus_ == TradeStatus::Accepted; }
|
||||
const std::string& getTradePeerName() const { return tradePeerName_; }
|
||||
|
||||
// My trade slots (what I'm offering)
|
||||
const std::array<TradeSlot, TRADE_SLOT_COUNT>& getMyTradeSlots() const { return myTradeSlots_; }
|
||||
// Peer's trade slots (what they're offering)
|
||||
const std::array<TradeSlot, TRADE_SLOT_COUNT>& getPeerTradeSlots() const { return peerTradeSlots_; }
|
||||
uint64_t getMyTradeGold() const { return myTradeGold_; }
|
||||
uint64_t getPeerTradeGold() const { return peerTradeGold_; }
|
||||
|
||||
void acceptTradeRequest(); // respond to incoming SMSG_TRADE_STATUS(1) with CMSG_BEGIN_TRADE
|
||||
void declineTradeRequest(); // respond with CMSG_CANCEL_TRADE
|
||||
void acceptTrade(); // lock in offer: CMSG_ACCEPT_TRADE
|
||||
void cancelTrade(); // CMSG_CANCEL_TRADE
|
||||
void setTradeItem(uint8_t tradeSlot, uint8_t bag, uint8_t bagSlot);
|
||||
void clearTradeItem(uint8_t tradeSlot);
|
||||
void setTradeGold(uint64_t copper);
|
||||
|
||||
// ---- Duel ----
|
||||
bool hasPendingDuelRequest() const { return pendingDuelRequest_; }
|
||||
|
|
@ -1653,6 +1678,8 @@ private:
|
|||
void handleQuestConfirmAccept(network::Packet& packet);
|
||||
void handleSummonRequest(network::Packet& packet);
|
||||
void handleTradeStatus(network::Packet& packet);
|
||||
void handleTradeStatusExtended(network::Packet& packet);
|
||||
void resetTradeState();
|
||||
void handleDuelRequested(network::Packet& packet);
|
||||
void handleDuelComplete(network::Packet& packet);
|
||||
void handleDuelWinner(network::Packet& packet);
|
||||
|
|
@ -2077,6 +2104,10 @@ private:
|
|||
TradeStatus tradeStatus_ = TradeStatus::None;
|
||||
uint64_t tradePeerGuid_= 0;
|
||||
std::string tradePeerName_;
|
||||
std::array<TradeSlot, TRADE_SLOT_COUNT> myTradeSlots_{};
|
||||
std::array<TradeSlot, TRADE_SLOT_COUNT> peerTradeSlots_{};
|
||||
uint64_t myTradeGold_ = 0;
|
||||
uint64_t peerTradeGold_ = 0;
|
||||
|
||||
// Duel state
|
||||
bool pendingDuelRequest_ = false;
|
||||
|
|
|
|||
|
|
@ -1356,6 +1356,33 @@ public:
|
|||
static network::Packet build();
|
||||
};
|
||||
|
||||
/** CMSG_SET_TRADE_ITEM packet builder (tradeSlot, bag, bagSlot) */
|
||||
class SetTradeItemPacket {
|
||||
public:
|
||||
// tradeSlot: 0-5 (normal) or 6 (backpack money-only slot)
|
||||
// bag: 255 = main backpack, 19-22 = bag slots
|
||||
// bagSlot: slot within bag
|
||||
static network::Packet build(uint8_t tradeSlot, uint8_t bag, uint8_t bagSlot);
|
||||
};
|
||||
|
||||
/** CMSG_CLEAR_TRADE_ITEM packet builder (remove item from trade slot) */
|
||||
class ClearTradeItemPacket {
|
||||
public:
|
||||
static network::Packet build(uint8_t tradeSlot);
|
||||
};
|
||||
|
||||
/** CMSG_SET_TRADE_GOLD packet builder (gold offered, in copper) */
|
||||
class SetTradeGoldPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t copper);
|
||||
};
|
||||
|
||||
/** CMSG_UNACCEPT_TRADE packet builder (unaccept without cancelling) */
|
||||
class UnacceptTradePacket {
|
||||
public:
|
||||
static network::Packet build();
|
||||
};
|
||||
|
||||
/** CMSG_ATTACKSWING packet builder */
|
||||
class AttackSwingPacket {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ private:
|
|||
void renderDuelRequestPopup(game::GameHandler& gameHandler);
|
||||
void renderLootRollPopup(game::GameHandler& gameHandler);
|
||||
void renderTradeRequestPopup(game::GameHandler& gameHandler);
|
||||
void renderTradeWindow(game::GameHandler& gameHandler);
|
||||
void renderSummonRequestPopup(game::GameHandler& gameHandler);
|
||||
void renderSharedQuestPopup(game::GameHandler& gameHandler);
|
||||
void renderItemTextWindow(game::GameHandler& gameHandler);
|
||||
|
|
|
|||
|
|
@ -3102,9 +3102,11 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
addSystemChatMessage("Summon cancelled.");
|
||||
break;
|
||||
case Opcode::SMSG_TRADE_STATUS:
|
||||
case Opcode::SMSG_TRADE_STATUS_EXTENDED:
|
||||
handleTradeStatus(packet);
|
||||
break;
|
||||
case Opcode::SMSG_TRADE_STATUS_EXTENDED:
|
||||
handleTradeStatusExtended(packet);
|
||||
break;
|
||||
case Opcode::SMSG_LOOT_ROLL:
|
||||
handleLootRoll(packet);
|
||||
break;
|
||||
|
|
@ -19047,13 +19049,17 @@ void GameHandler::handleTradeStatus(network::Packet& packet) {
|
|||
break;
|
||||
}
|
||||
case 2: // OPEN_WINDOW
|
||||
myTradeSlots_.fill(TradeSlot{});
|
||||
peerTradeSlots_.fill(TradeSlot{});
|
||||
myTradeGold_ = 0;
|
||||
peerTradeGold_ = 0;
|
||||
tradeStatus_ = TradeStatus::Open;
|
||||
addSystemChatMessage("Trade window opened.");
|
||||
break;
|
||||
case 3: // CANCELLED
|
||||
case 9: // REJECTED
|
||||
case 12: // CLOSE_WINDOW
|
||||
tradeStatus_ = TradeStatus::None;
|
||||
resetTradeState();
|
||||
addSystemChatMessage("Trade cancelled.");
|
||||
break;
|
||||
case 4: // ACCEPTED (partner accepted)
|
||||
|
|
@ -19061,9 +19067,8 @@ void GameHandler::handleTradeStatus(network::Packet& packet) {
|
|||
addSystemChatMessage("Trade accepted. Awaiting other player...");
|
||||
break;
|
||||
case 8: // COMPLETE
|
||||
tradeStatus_ = TradeStatus::Complete;
|
||||
addSystemChatMessage("Trade complete!");
|
||||
tradeStatus_ = TradeStatus::None; // reset after notification
|
||||
resetTradeState();
|
||||
break;
|
||||
case 7: // BACK_TO_TRADE (unaccepted after a change)
|
||||
tradeStatus_ = TradeStatus::Open;
|
||||
|
|
@ -19102,10 +19107,104 @@ void GameHandler::acceptTrade() {
|
|||
|
||||
void GameHandler::cancelTrade() {
|
||||
if (!socket) return;
|
||||
tradeStatus_ = TradeStatus::None;
|
||||
resetTradeState();
|
||||
socket->send(CancelTradePacket::build());
|
||||
}
|
||||
|
||||
void GameHandler::setTradeItem(uint8_t tradeSlot, uint8_t bag, uint8_t bagSlot) {
|
||||
if (!isTradeOpen() || !socket || tradeSlot >= TRADE_SLOT_COUNT) return;
|
||||
socket->send(SetTradeItemPacket::build(tradeSlot, bag, bagSlot));
|
||||
}
|
||||
|
||||
void GameHandler::clearTradeItem(uint8_t tradeSlot) {
|
||||
if (!isTradeOpen() || !socket || tradeSlot >= TRADE_SLOT_COUNT) return;
|
||||
myTradeSlots_[tradeSlot] = TradeSlot{};
|
||||
socket->send(ClearTradeItemPacket::build(tradeSlot));
|
||||
}
|
||||
|
||||
void GameHandler::setTradeGold(uint64_t copper) {
|
||||
if (!isTradeOpen() || !socket) return;
|
||||
myTradeGold_ = copper;
|
||||
socket->send(SetTradeGoldPacket::build(copper));
|
||||
}
|
||||
|
||||
void GameHandler::resetTradeState() {
|
||||
tradeStatus_ = TradeStatus::None;
|
||||
myTradeGold_ = 0;
|
||||
peerTradeGold_ = 0;
|
||||
myTradeSlots_.fill(TradeSlot{});
|
||||
peerTradeSlots_.fill(TradeSlot{});
|
||||
}
|
||||
|
||||
void GameHandler::handleTradeStatusExtended(network::Packet& packet) {
|
||||
// WotLK 3.3.5a SMSG_TRADE_STATUS_EXTENDED format:
|
||||
// uint8 isSelfState (1 = my trade window, 0 = peer's)
|
||||
// uint32 tradeId
|
||||
// uint32 slotCount (7: 6 normal + 1 extra for enchanting)
|
||||
// Per slot (up to slotCount):
|
||||
// uint8 slotIndex
|
||||
// uint32 itemId
|
||||
// uint32 displayId
|
||||
// uint32 stackCount
|
||||
// uint8 isWrapped
|
||||
// uint64 giftCreatorGuid
|
||||
// uint32 enchantId (and several more enchant/stat fields)
|
||||
// ... (complex; we parse only the essential fields)
|
||||
// uint64 coins (gold offered by the sender of this message)
|
||||
|
||||
size_t rem = packet.getSize() - packet.getReadPos();
|
||||
if (rem < 9) return;
|
||||
|
||||
uint8_t isSelf = packet.readUInt8();
|
||||
uint32_t tradeId = packet.readUInt32(); (void)tradeId;
|
||||
uint32_t slotCount= packet.readUInt32();
|
||||
|
||||
auto& slots = isSelf ? myTradeSlots_ : peerTradeSlots_;
|
||||
|
||||
for (uint32_t i = 0; i < slotCount && (packet.getSize() - packet.getReadPos()) >= 14; ++i) {
|
||||
uint8_t slotIdx = packet.readUInt8();
|
||||
uint32_t itemId = packet.readUInt32();
|
||||
uint32_t displayId = packet.readUInt32();
|
||||
uint32_t stackCount = packet.readUInt32();
|
||||
|
||||
// isWrapped + giftCreatorGuid + several enchant fields — skip them all
|
||||
// We need at least 1+8+4*5 = 29 bytes for the rest of this slot entry
|
||||
bool isWrapped = false;
|
||||
if (packet.getSize() - packet.getReadPos() >= 1) {
|
||||
isWrapped = (packet.readUInt8() != 0);
|
||||
}
|
||||
// Skip giftCreatorGuid (8) + enchantId*5 (20) + suffixFactor (4) + randPropId (4) + lockId (4)
|
||||
// + maxDurability (4) + durability (4) = 49 bytes
|
||||
// Plus if wrapped: giftCreatorGuid already consumed; additional guid = 0
|
||||
constexpr size_t SLOT_TRAIL = 49;
|
||||
if (packet.getSize() - packet.getReadPos() >= SLOT_TRAIL) {
|
||||
packet.setReadPos(packet.getReadPos() + SLOT_TRAIL);
|
||||
} else {
|
||||
packet.setReadPos(packet.getSize());
|
||||
return;
|
||||
}
|
||||
(void)isWrapped;
|
||||
|
||||
if (slotIdx < TRADE_SLOT_COUNT) {
|
||||
TradeSlot& s = slots[slotIdx];
|
||||
s.itemId = itemId;
|
||||
s.displayId = displayId;
|
||||
s.stackCount = stackCount;
|
||||
s.occupied = (itemId != 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Gold offered (uint64 copper)
|
||||
if (packet.getSize() - packet.getReadPos() >= 8) {
|
||||
uint64_t coins = packet.readUInt64();
|
||||
if (isSelf) myTradeGold_ = coins;
|
||||
else peerTradeGold_ = coins;
|
||||
}
|
||||
|
||||
LOG_DEBUG("SMSG_TRADE_STATUS_EXTENDED: isSelf=", (int)isSelf,
|
||||
" myGold=", myTradeGold_, " peerGold=", peerTradeGold_);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Group loot roll (SMSG_LOOT_ROLL / SMSG_LOOT_ROLL_WON / CMSG_LOOT_ROLL)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -2177,6 +2177,35 @@ network::Packet AcceptTradePacket::build() {
|
|||
return packet;
|
||||
}
|
||||
|
||||
network::Packet SetTradeItemPacket::build(uint8_t tradeSlot, uint8_t bag, uint8_t bagSlot) {
|
||||
network::Packet packet(wireOpcode(Opcode::CMSG_SET_TRADE_ITEM));
|
||||
packet.writeUInt8(tradeSlot);
|
||||
packet.writeUInt8(bag);
|
||||
packet.writeUInt8(bagSlot);
|
||||
LOG_DEBUG("Built CMSG_SET_TRADE_ITEM slot=", (int)tradeSlot, " bag=", (int)bag, " bagSlot=", (int)bagSlot);
|
||||
return packet;
|
||||
}
|
||||
|
||||
network::Packet ClearTradeItemPacket::build(uint8_t tradeSlot) {
|
||||
network::Packet packet(wireOpcode(Opcode::CMSG_CLEAR_TRADE_ITEM));
|
||||
packet.writeUInt8(tradeSlot);
|
||||
LOG_DEBUG("Built CMSG_CLEAR_TRADE_ITEM slot=", (int)tradeSlot);
|
||||
return packet;
|
||||
}
|
||||
|
||||
network::Packet SetTradeGoldPacket::build(uint64_t copper) {
|
||||
network::Packet packet(wireOpcode(Opcode::CMSG_SET_TRADE_GOLD));
|
||||
packet.writeUInt64(copper);
|
||||
LOG_DEBUG("Built CMSG_SET_TRADE_GOLD copper=", copper);
|
||||
return packet;
|
||||
}
|
||||
|
||||
network::Packet UnacceptTradePacket::build() {
|
||||
network::Packet packet(wireOpcode(Opcode::CMSG_UNACCEPT_TRADE));
|
||||
LOG_DEBUG("Built CMSG_UNACCEPT_TRADE");
|
||||
return packet;
|
||||
}
|
||||
|
||||
network::Packet InitiateTradePacket::build(uint64_t targetGuid) {
|
||||
network::Packet packet(wireOpcode(Opcode::CMSG_INITIATE_TRADE));
|
||||
packet.writeUInt64(targetGuid);
|
||||
|
|
|
|||
|
|
@ -414,6 +414,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
renderDuelRequestPopup(gameHandler);
|
||||
renderLootRollPopup(gameHandler);
|
||||
renderTradeRequestPopup(gameHandler);
|
||||
renderTradeWindow(gameHandler);
|
||||
renderSummonRequestPopup(gameHandler);
|
||||
renderSharedQuestPopup(gameHandler);
|
||||
renderItemTextWindow(gameHandler);
|
||||
|
|
@ -5980,6 +5981,150 @@ void GameScreen::renderTradeRequestPopup(game::GameHandler& gameHandler) {
|
|||
ImGui::End();
|
||||
}
|
||||
|
||||
void GameScreen::renderTradeWindow(game::GameHandler& gameHandler) {
|
||||
if (!gameHandler.isTradeOpen()) return;
|
||||
|
||||
const auto& mySlots = gameHandler.getMyTradeSlots();
|
||||
const auto& peerSlots = gameHandler.getPeerTradeSlots();
|
||||
const uint64_t myGold = gameHandler.getMyTradeGold();
|
||||
const uint64_t peerGold = gameHandler.getPeerTradeGold();
|
||||
const auto& peerName = gameHandler.getTradePeerName();
|
||||
|
||||
auto* window = core::Application::getInstance().getWindow();
|
||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2.0f - 240.0f, screenH / 2.0f - 180.0f), ImGuiCond_Once);
|
||||
ImGui::SetNextWindowSize(ImVec2(480.0f, 360.0f), ImGuiCond_Once);
|
||||
|
||||
bool open = true;
|
||||
if (ImGui::Begin(("Trade with " + peerName).c_str(), &open,
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize)) {
|
||||
|
||||
auto formatGold = [](uint64_t copper, char* buf, size_t bufsz) {
|
||||
uint64_t g = copper / 10000;
|
||||
uint64_t s = (copper % 10000) / 100;
|
||||
uint64_t c = copper % 100;
|
||||
if (g > 0) std::snprintf(buf, bufsz, "%llug %llus %lluc",
|
||||
(unsigned long long)g, (unsigned long long)s, (unsigned long long)c);
|
||||
else if (s > 0) std::snprintf(buf, bufsz, "%llus %lluc",
|
||||
(unsigned long long)s, (unsigned long long)c);
|
||||
else std::snprintf(buf, bufsz, "%lluc", (unsigned long long)c);
|
||||
};
|
||||
|
||||
auto renderSlotColumn = [&](const char* label,
|
||||
const std::array<game::GameHandler::TradeSlot,
|
||||
game::GameHandler::TRADE_SLOT_COUNT>& slots,
|
||||
uint64_t gold, bool isMine) {
|
||||
ImGui::Text("%s", label);
|
||||
ImGui::Separator();
|
||||
|
||||
for (int i = 0; i < game::GameHandler::TRADE_SLOT_COUNT; ++i) {
|
||||
const auto& slot = slots[i];
|
||||
ImGui::PushID(i * (isMine ? 1 : -1) - (isMine ? 0 : 100));
|
||||
|
||||
if (slot.occupied && slot.itemId != 0) {
|
||||
const auto* info = gameHandler.getItemInfo(slot.itemId);
|
||||
std::string name = (info && info->valid && !info->name.empty())
|
||||
? info->name
|
||||
: ("Item " + std::to_string(slot.itemId));
|
||||
if (slot.stackCount > 1)
|
||||
name += " x" + std::to_string(slot.stackCount);
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.9f, 0.5f, 1.0f), " %d. %s", i + 1, name.c_str());
|
||||
|
||||
if (isMine && ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||
gameHandler.clearTradeItem(static_cast<uint8_t>(i));
|
||||
}
|
||||
if (isMine && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Double-click to remove");
|
||||
}
|
||||
} else {
|
||||
ImGui::TextDisabled(" %d. (empty)", i + 1);
|
||||
|
||||
// Allow dragging inventory items into trade slots via right-click context menu
|
||||
if (isMine && ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||
ImGui::OpenPopup(("##additem" + std::to_string(i)).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (isMine) {
|
||||
// Drag-from-inventory: show small popup listing bag items
|
||||
if (ImGui::BeginPopup(("##additem" + std::to_string(i)).c_str())) {
|
||||
ImGui::TextDisabled("Add from inventory:");
|
||||
const auto& inv = gameHandler.getInventory();
|
||||
// Backpack slots 0-15 (bag=255)
|
||||
for (int si = 0; si < game::Inventory::BACKPACK_SLOTS; ++si) {
|
||||
const auto& slot = inv.getBackpackSlot(si);
|
||||
if (slot.empty()) continue;
|
||||
const auto* ii = gameHandler.getItemInfo(slot.item.itemId);
|
||||
std::string iname = (ii && ii->valid && !ii->name.empty())
|
||||
? ii->name
|
||||
: (!slot.item.name.empty() ? slot.item.name
|
||||
: ("Item " + std::to_string(slot.item.itemId)));
|
||||
if (ImGui::Selectable(iname.c_str())) {
|
||||
// bag=255 = main backpack
|
||||
gameHandler.setTradeItem(static_cast<uint8_t>(i), 255u,
|
||||
static_cast<uint8_t>(si));
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
// Gold row
|
||||
char gbuf[48];
|
||||
formatGold(gold, gbuf, sizeof(gbuf));
|
||||
ImGui::Spacing();
|
||||
if (isMine) {
|
||||
ImGui::Text("Gold offered: %s", gbuf);
|
||||
static char goldInput[32] = "0";
|
||||
ImGui::SetNextItemWidth(120.0f);
|
||||
if (ImGui::InputText("##goldset", goldInput, sizeof(goldInput),
|
||||
ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
uint64_t copper = std::strtoull(goldInput, nullptr, 10);
|
||||
gameHandler.setTradeGold(copper);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("(copper, Enter to set)");
|
||||
} else {
|
||||
ImGui::Text("Gold offered: %s", gbuf);
|
||||
}
|
||||
};
|
||||
|
||||
// Two-column layout: my offer | peer offer
|
||||
float colW = ImGui::GetContentRegionAvail().x * 0.5f - 4.0f;
|
||||
ImGui::BeginChild("##myoffer", ImVec2(colW, 240.0f), true);
|
||||
renderSlotColumn("Your offer", mySlots, myGold, true);
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginChild("##peroffer", ImVec2(colW, 240.0f), true);
|
||||
renderSlotColumn((peerName + "'s offer").c_str(), peerSlots, peerGold, false);
|
||||
ImGui::EndChild();
|
||||
|
||||
// Buttons
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
float bw = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||
if (ImGui::Button("Accept Trade", ImVec2(bw, 0))) {
|
||||
gameHandler.acceptTrade();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel", ImVec2(bw, 0))) {
|
||||
gameHandler.cancelTrade();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (!open) {
|
||||
gameHandler.cancelTrade();
|
||||
}
|
||||
}
|
||||
|
||||
void GameScreen::renderLootRollPopup(game::GameHandler& gameHandler) {
|
||||
if (!gameHandler.hasPendingLootRoll()) return;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue