mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Fix vendor buying and add quest turn-in flow
CMSG_BUY_ITEM was missing the trailing uint8 bag field, causing the server to silently drop undersized packets. Add handlers for SMSG_QUESTGIVER_REQUEST_ITEMS and SMSG_QUESTGIVER_OFFER_REWARD with UI windows for quest completion and reward selection.
This commit is contained in:
parent
6296c32a47
commit
5cc3d9645c
6 changed files with 447 additions and 0 deletions
|
|
@ -363,6 +363,17 @@ public:
|
|||
bool isQuestDetailsOpen() const { return questDetailsOpen; }
|
||||
const QuestDetailsData& getQuestDetails() const { return currentQuestDetails; }
|
||||
|
||||
// Quest turn-in
|
||||
bool isQuestRequestItemsOpen() const { return questRequestItemsOpen_; }
|
||||
const QuestRequestItemsData& getQuestRequestItems() const { return currentQuestRequestItems_; }
|
||||
void completeQuest(); // Send CMSG_QUESTGIVER_COMPLETE_QUEST
|
||||
void closeQuestRequestItems();
|
||||
|
||||
bool isQuestOfferRewardOpen() const { return questOfferRewardOpen_; }
|
||||
const QuestOfferRewardData& getQuestOfferReward() const { return currentQuestOfferReward_; }
|
||||
void chooseQuestReward(uint32_t rewardIndex); // Send CMSG_QUESTGIVER_CHOOSE_REWARD
|
||||
void closeQuestOfferReward();
|
||||
|
||||
// Quest log
|
||||
struct QuestLogEntry {
|
||||
uint32_t questId = 0;
|
||||
|
|
@ -538,6 +549,8 @@ private:
|
|||
void handleGossipMessage(network::Packet& packet);
|
||||
void handleGossipComplete(network::Packet& packet);
|
||||
void handleQuestDetails(network::Packet& packet);
|
||||
void handleQuestRequestItems(network::Packet& packet);
|
||||
void handleQuestOfferReward(network::Packet& packet);
|
||||
void handleListInventory(network::Packet& packet);
|
||||
LootResponseData generateLocalLoot(uint64_t guid);
|
||||
void simulateLootResponse(const LootResponseData& data);
|
||||
|
|
@ -689,6 +702,12 @@ private:
|
|||
bool questDetailsOpen = false;
|
||||
QuestDetailsData currentQuestDetails;
|
||||
|
||||
// Quest turn-in
|
||||
bool questRequestItemsOpen_ = false;
|
||||
QuestRequestItemsData currentQuestRequestItems_;
|
||||
bool questOfferRewardOpen_ = false;
|
||||
QuestOfferRewardData currentQuestOfferReward_;
|
||||
|
||||
// Quest log
|
||||
std::vector<QuestLogEntry> questLog_;
|
||||
|
||||
|
|
|
|||
|
|
@ -1238,6 +1238,62 @@ public:
|
|||
static bool parse(network::Packet& packet, QuestDetailsData& data);
|
||||
};
|
||||
|
||||
/** Reward item entry (shared by quest detail/offer windows) */
|
||||
struct QuestRewardItem {
|
||||
uint32_t itemId = 0;
|
||||
uint32_t count = 0;
|
||||
uint32_t displayInfoId = 0;
|
||||
};
|
||||
|
||||
/** SMSG_QUESTGIVER_REQUEST_ITEMS data (turn-in progress check) */
|
||||
struct QuestRequestItemsData {
|
||||
uint64_t npcGuid = 0;
|
||||
uint32_t questId = 0;
|
||||
std::string title;
|
||||
std::string completionText;
|
||||
uint32_t requiredMoney = 0;
|
||||
uint32_t completableFlags = 0; // 0x03 = completable
|
||||
std::vector<QuestRewardItem> requiredItems;
|
||||
|
||||
bool isCompletable() const { return (completableFlags & 0x03) != 0; }
|
||||
};
|
||||
|
||||
/** SMSG_QUESTGIVER_REQUEST_ITEMS parser */
|
||||
class QuestRequestItemsParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, QuestRequestItemsData& data);
|
||||
};
|
||||
|
||||
/** SMSG_QUESTGIVER_OFFER_REWARD data (choose reward) */
|
||||
struct QuestOfferRewardData {
|
||||
uint64_t npcGuid = 0;
|
||||
uint32_t questId = 0;
|
||||
std::string title;
|
||||
std::string rewardText;
|
||||
uint32_t rewardMoney = 0;
|
||||
uint32_t rewardXp = 0;
|
||||
std::vector<QuestRewardItem> choiceRewards; // Pick one
|
||||
std::vector<QuestRewardItem> fixedRewards; // Always given
|
||||
};
|
||||
|
||||
/** SMSG_QUESTGIVER_OFFER_REWARD parser */
|
||||
class QuestOfferRewardParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, QuestOfferRewardData& data);
|
||||
};
|
||||
|
||||
/** CMSG_QUESTGIVER_COMPLETE_QUEST packet builder */
|
||||
class QuestgiverCompleteQuestPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t npcGuid, uint32_t questId);
|
||||
};
|
||||
|
||||
/** CMSG_QUESTGIVER_CHOOSE_REWARD packet builder */
|
||||
class QuestgiverChooseRewardPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t npcGuid, uint32_t questId, uint32_t rewardIndex);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Phase 5: Vendor
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@ private:
|
|||
void renderLootWindow(game::GameHandler& gameHandler);
|
||||
void renderGossipWindow(game::GameHandler& gameHandler);
|
||||
void renderQuestDetailsWindow(game::GameHandler& gameHandler);
|
||||
void renderQuestRequestItemsWindow(game::GameHandler& gameHandler);
|
||||
void renderQuestOfferRewardWindow(game::GameHandler& gameHandler);
|
||||
void renderVendorWindow(game::GameHandler& gameHandler);
|
||||
void renderTeleporterPanel();
|
||||
void renderDeathScreen(game::GameHandler& gameHandler);
|
||||
|
|
|
|||
|
|
@ -1285,7 +1285,11 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
break;
|
||||
}
|
||||
case Opcode::SMSG_QUESTGIVER_REQUEST_ITEMS:
|
||||
handleQuestRequestItems(packet);
|
||||
break;
|
||||
case Opcode::SMSG_QUESTGIVER_OFFER_REWARD:
|
||||
handleQuestOfferReward(packet);
|
||||
break;
|
||||
case Opcode::SMSG_GROUP_SET_LEADER:
|
||||
LOG_DEBUG("Ignoring known opcode: 0x", std::hex, opcode, std::dec);
|
||||
break;
|
||||
|
|
@ -4144,6 +4148,78 @@ void GameHandler::abandonQuest(uint32_t questId) {
|
|||
}
|
||||
}
|
||||
|
||||
void GameHandler::handleQuestRequestItems(network::Packet& packet) {
|
||||
QuestRequestItemsData data;
|
||||
if (!QuestRequestItemsParser::parse(packet, data)) {
|
||||
LOG_WARNING("Failed to parse SMSG_QUESTGIVER_REQUEST_ITEMS");
|
||||
return;
|
||||
}
|
||||
currentQuestRequestItems_ = data;
|
||||
questRequestItemsOpen_ = true;
|
||||
gossipWindowOpen = false;
|
||||
questDetailsOpen = false;
|
||||
|
||||
// Query item names for required items
|
||||
for (const auto& item : data.requiredItems) {
|
||||
queryItemInfo(item.itemId, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GameHandler::handleQuestOfferReward(network::Packet& packet) {
|
||||
QuestOfferRewardData data;
|
||||
if (!QuestOfferRewardParser::parse(packet, data)) {
|
||||
LOG_WARNING("Failed to parse SMSG_QUESTGIVER_OFFER_REWARD");
|
||||
return;
|
||||
}
|
||||
currentQuestOfferReward_ = data;
|
||||
questOfferRewardOpen_ = true;
|
||||
questRequestItemsOpen_ = false;
|
||||
gossipWindowOpen = false;
|
||||
questDetailsOpen = false;
|
||||
|
||||
// Query item names for reward items
|
||||
for (const auto& item : data.choiceRewards)
|
||||
queryItemInfo(item.itemId, 0);
|
||||
for (const auto& item : data.fixedRewards)
|
||||
queryItemInfo(item.itemId, 0);
|
||||
}
|
||||
|
||||
void GameHandler::completeQuest() {
|
||||
if (!questRequestItemsOpen_ || state != WorldState::IN_WORLD || !socket) return;
|
||||
auto packet = QuestgiverCompleteQuestPacket::build(
|
||||
currentQuestRequestItems_.npcGuid, currentQuestRequestItems_.questId);
|
||||
socket->send(packet);
|
||||
questRequestItemsOpen_ = false;
|
||||
currentQuestRequestItems_ = QuestRequestItemsData{};
|
||||
}
|
||||
|
||||
void GameHandler::closeQuestRequestItems() {
|
||||
questRequestItemsOpen_ = false;
|
||||
currentQuestRequestItems_ = QuestRequestItemsData{};
|
||||
}
|
||||
|
||||
void GameHandler::chooseQuestReward(uint32_t rewardIndex) {
|
||||
if (!questOfferRewardOpen_ || state != WorldState::IN_WORLD || !socket) return;
|
||||
uint64_t npcGuid = currentQuestOfferReward_.npcGuid;
|
||||
auto packet = QuestgiverChooseRewardPacket::build(
|
||||
npcGuid, currentQuestOfferReward_.questId, rewardIndex);
|
||||
socket->send(packet);
|
||||
questOfferRewardOpen_ = false;
|
||||
currentQuestOfferReward_ = QuestOfferRewardData{};
|
||||
|
||||
// Re-query quest giver status so markers update
|
||||
if (npcGuid) {
|
||||
network::Packet qsPkt(static_cast<uint16_t>(Opcode::CMSG_QUESTGIVER_STATUS_QUERY));
|
||||
qsPkt.writeUInt64(npcGuid);
|
||||
socket->send(qsPkt);
|
||||
}
|
||||
}
|
||||
|
||||
void GameHandler::closeQuestOfferReward() {
|
||||
questOfferRewardOpen_ = false;
|
||||
currentQuestOfferReward_ = QuestOfferRewardData{};
|
||||
}
|
||||
|
||||
void GameHandler::closeGossip() {
|
||||
gossipWindowOpen = false;
|
||||
currentGossip = GossipMessageData{};
|
||||
|
|
|
|||
|
|
@ -2087,6 +2087,124 @@ bool GossipMessageParser::parse(network::Packet& packet, GossipMessageData& data
|
|||
return true;
|
||||
}
|
||||
|
||||
bool QuestRequestItemsParser::parse(network::Packet& packet, QuestRequestItemsData& data) {
|
||||
if (packet.getSize() - packet.getReadPos() < 20) return false;
|
||||
data.npcGuid = packet.readUInt64();
|
||||
data.questId = packet.readUInt32();
|
||||
data.title = packet.readString();
|
||||
data.completionText = packet.readString();
|
||||
|
||||
if (packet.getReadPos() + 20 > packet.getSize()) {
|
||||
LOG_INFO("Quest request items (short): id=", data.questId, " title='", data.title, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
/*emoteDelay*/ packet.readUInt32();
|
||||
/*emote*/ packet.readUInt32();
|
||||
/*autoCloseOnCancel*/ packet.readUInt32();
|
||||
/*flags*/ packet.readUInt32();
|
||||
/*suggestedPlayers*/ packet.readUInt32();
|
||||
|
||||
if (packet.getReadPos() + 4 > packet.getSize()) return true;
|
||||
data.requiredMoney = packet.readUInt32();
|
||||
|
||||
if (packet.getReadPos() + 4 > packet.getSize()) return true;
|
||||
uint32_t requiredItemCount = packet.readUInt32();
|
||||
for (uint32_t i = 0; i < requiredItemCount; ++i) {
|
||||
if (packet.getReadPos() + 12 > packet.getSize()) break;
|
||||
QuestRewardItem item;
|
||||
item.itemId = packet.readUInt32();
|
||||
item.count = packet.readUInt32();
|
||||
item.displayInfoId = packet.readUInt32();
|
||||
if (item.itemId > 0)
|
||||
data.requiredItems.push_back(item);
|
||||
}
|
||||
|
||||
if (packet.getReadPos() + 4 > packet.getSize()) return true;
|
||||
data.completableFlags = packet.readUInt32();
|
||||
|
||||
LOG_INFO("Quest request items: id=", data.questId, " title='", data.title,
|
||||
"' items=", data.requiredItems.size(), " completable=", data.isCompletable());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QuestOfferRewardParser::parse(network::Packet& packet, QuestOfferRewardData& data) {
|
||||
if (packet.getSize() - packet.getReadPos() < 20) return false;
|
||||
data.npcGuid = packet.readUInt64();
|
||||
data.questId = packet.readUInt32();
|
||||
data.title = packet.readString();
|
||||
data.rewardText = packet.readString();
|
||||
|
||||
if (packet.getReadPos() + 10 > packet.getSize()) {
|
||||
LOG_INFO("Quest offer reward (short): id=", data.questId, " title='", data.title, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
/*autoFinish*/ packet.readUInt8();
|
||||
/*flags*/ packet.readUInt32();
|
||||
/*suggestedPlayers*/ packet.readUInt32();
|
||||
|
||||
// Emotes
|
||||
if (packet.getReadPos() + 4 > packet.getSize()) return true;
|
||||
uint32_t emoteCount = packet.readUInt32();
|
||||
for (uint32_t i = 0; i < emoteCount; ++i) {
|
||||
if (packet.getReadPos() + 8 > packet.getSize()) break;
|
||||
packet.readUInt32(); // delay
|
||||
packet.readUInt32(); // emote
|
||||
}
|
||||
|
||||
// Choice reward items (pick one): count + 6 * (id, count, displayInfo)
|
||||
if (packet.getReadPos() + 4 > packet.getSize()) return true;
|
||||
/*choiceCount*/ packet.readUInt32();
|
||||
for (uint32_t i = 0; i < 6; ++i) {
|
||||
if (packet.getReadPos() + 12 > packet.getSize()) break;
|
||||
QuestRewardItem item;
|
||||
item.itemId = packet.readUInt32();
|
||||
item.count = packet.readUInt32();
|
||||
item.displayInfoId = packet.readUInt32();
|
||||
if (item.itemId > 0)
|
||||
data.choiceRewards.push_back(item);
|
||||
}
|
||||
|
||||
// Fixed reward items: count + 4 * (id, count, displayInfo)
|
||||
if (packet.getReadPos() + 4 > packet.getSize()) return true;
|
||||
/*rewardCount*/ packet.readUInt32();
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
if (packet.getReadPos() + 12 > packet.getSize()) break;
|
||||
QuestRewardItem item;
|
||||
item.itemId = packet.readUInt32();
|
||||
item.count = packet.readUInt32();
|
||||
item.displayInfoId = packet.readUInt32();
|
||||
if (item.itemId > 0)
|
||||
data.fixedRewards.push_back(item);
|
||||
}
|
||||
|
||||
// Money and XP
|
||||
if (packet.getReadPos() + 4 <= packet.getSize())
|
||||
data.rewardMoney = packet.readUInt32();
|
||||
if (packet.getReadPos() + 4 <= packet.getSize())
|
||||
data.rewardXp = packet.readUInt32();
|
||||
|
||||
LOG_INFO("Quest offer reward: id=", data.questId, " title='", data.title,
|
||||
"' choices=", data.choiceRewards.size(), " fixed=", data.fixedRewards.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
network::Packet QuestgiverCompleteQuestPacket::build(uint64_t npcGuid, uint32_t questId) {
|
||||
network::Packet packet(static_cast<uint16_t>(Opcode::CMSG_QUESTGIVER_COMPLETE_QUEST));
|
||||
packet.writeUInt64(npcGuid);
|
||||
packet.writeUInt32(questId);
|
||||
return packet;
|
||||
}
|
||||
|
||||
network::Packet QuestgiverChooseRewardPacket::build(uint64_t npcGuid, uint32_t questId, uint32_t rewardIndex) {
|
||||
network::Packet packet(static_cast<uint16_t>(Opcode::CMSG_QUESTGIVER_CHOOSE_REWARD));
|
||||
packet.writeUInt64(npcGuid);
|
||||
packet.writeUInt32(questId);
|
||||
packet.writeUInt32(rewardIndex);
|
||||
return packet;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Phase 5: Vendor
|
||||
// ============================================================
|
||||
|
|
@ -2103,6 +2221,7 @@ network::Packet BuyItemPacket::build(uint64_t vendorGuid, uint32_t itemId, uint3
|
|||
packet.writeUInt32(itemId);
|
||||
packet.writeUInt32(slot);
|
||||
packet.writeUInt8(count);
|
||||
packet.writeUInt8(0); // bag slot (0 = find any available bag slot)
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
renderLootWindow(gameHandler);
|
||||
renderGossipWindow(gameHandler);
|
||||
renderQuestDetailsWindow(gameHandler);
|
||||
renderQuestRequestItemsWindow(gameHandler);
|
||||
renderQuestOfferRewardWindow(gameHandler);
|
||||
renderVendorWindow(gameHandler);
|
||||
renderQuestMarkers(gameHandler);
|
||||
renderMinimapMarkers(gameHandler);
|
||||
|
|
@ -2273,6 +2275,179 @@ void GameScreen::renderQuestDetailsWindow(game::GameHandler& gameHandler) {
|
|||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Quest Request Items Window (turn-in progress check)
|
||||
// ============================================================
|
||||
|
||||
void GameScreen::renderQuestRequestItemsWindow(game::GameHandler& gameHandler) {
|
||||
if (!gameHandler.isQuestRequestItemsOpen()) return;
|
||||
|
||||
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 - 225, screenH / 2 - 200), ImGuiCond_Appearing);
|
||||
ImGui::SetNextWindowSize(ImVec2(450, 350), ImGuiCond_Appearing);
|
||||
|
||||
bool open = true;
|
||||
const auto& quest = gameHandler.getQuestRequestItems();
|
||||
if (ImGui::Begin(quest.title.c_str(), &open, ImGuiWindowFlags_NoCollapse)) {
|
||||
if (!quest.completionText.empty()) {
|
||||
ImGui::TextWrapped("%s", quest.completionText.c_str());
|
||||
}
|
||||
|
||||
// Required items
|
||||
if (!quest.requiredItems.empty()) {
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "Required Items:");
|
||||
for (const auto& item : quest.requiredItems) {
|
||||
auto* info = gameHandler.getItemInfo(item.itemId);
|
||||
if (info && info->valid)
|
||||
ImGui::Text(" %s x%u", info->name.c_str(), item.count);
|
||||
else
|
||||
ImGui::Text(" Item %u x%u", item.itemId, item.count);
|
||||
}
|
||||
}
|
||||
|
||||
if (quest.requiredMoney > 0) {
|
||||
ImGui::Spacing();
|
||||
uint32_t g = quest.requiredMoney / 10000;
|
||||
uint32_t s = (quest.requiredMoney % 10000) / 100;
|
||||
uint32_t c = quest.requiredMoney % 100;
|
||||
ImGui::Text("Required money: %ug %us %uc", g, s, c);
|
||||
}
|
||||
|
||||
// Complete / Cancel buttons
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
float buttonW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||
if (quest.isCompletable()) {
|
||||
if (ImGui::Button("Complete Quest", ImVec2(buttonW, 0))) {
|
||||
gameHandler.completeQuest();
|
||||
}
|
||||
} else {
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Button("Incomplete", ImVec2(buttonW, 0));
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel", ImVec2(buttonW, 0))) {
|
||||
gameHandler.closeQuestRequestItems();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (!open) {
|
||||
gameHandler.closeQuestRequestItems();
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Quest Offer Reward Window (choose reward)
|
||||
// ============================================================
|
||||
|
||||
void GameScreen::renderQuestOfferRewardWindow(game::GameHandler& gameHandler) {
|
||||
if (!gameHandler.isQuestOfferRewardOpen()) return;
|
||||
|
||||
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 - 225, screenH / 2 - 200), ImGuiCond_Appearing);
|
||||
ImGui::SetNextWindowSize(ImVec2(450, 400), ImGuiCond_Appearing);
|
||||
|
||||
bool open = true;
|
||||
const auto& quest = gameHandler.getQuestOfferReward();
|
||||
static int selectedChoice = -1;
|
||||
|
||||
if (ImGui::Begin(quest.title.c_str(), &open, ImGuiWindowFlags_NoCollapse)) {
|
||||
if (!quest.rewardText.empty()) {
|
||||
ImGui::TextWrapped("%s", quest.rewardText.c_str());
|
||||
}
|
||||
|
||||
// Choice rewards (pick one)
|
||||
if (!quest.choiceRewards.empty()) {
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "Choose a reward:");
|
||||
for (size_t i = 0; i < quest.choiceRewards.size(); ++i) {
|
||||
const auto& item = quest.choiceRewards[i];
|
||||
auto* info = gameHandler.getItemInfo(item.itemId);
|
||||
char label[256];
|
||||
if (info && info->valid)
|
||||
snprintf(label, sizeof(label), "%s x%u", info->name.c_str(), item.count);
|
||||
else
|
||||
snprintf(label, sizeof(label), "Item %u x%u", item.itemId, item.count);
|
||||
|
||||
bool selected = (selectedChoice == static_cast<int>(i));
|
||||
if (ImGui::Selectable(label, selected)) {
|
||||
selectedChoice = static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fixed rewards (always given)
|
||||
if (!quest.fixedRewards.empty()) {
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "You will also receive:");
|
||||
for (const auto& item : quest.fixedRewards) {
|
||||
auto* info = gameHandler.getItemInfo(item.itemId);
|
||||
if (info && info->valid)
|
||||
ImGui::Text(" %s x%u", info->name.c_str(), item.count);
|
||||
else
|
||||
ImGui::Text(" Item %u x%u", item.itemId, item.count);
|
||||
}
|
||||
}
|
||||
|
||||
// Money / XP rewards
|
||||
if (quest.rewardXp > 0 || quest.rewardMoney > 0) {
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.82f, 0.0f, 1.0f), "Rewards:");
|
||||
if (quest.rewardXp > 0)
|
||||
ImGui::Text(" %u experience", quest.rewardXp);
|
||||
if (quest.rewardMoney > 0) {
|
||||
uint32_t g = quest.rewardMoney / 10000;
|
||||
uint32_t s = (quest.rewardMoney % 10000) / 100;
|
||||
uint32_t c = quest.rewardMoney % 100;
|
||||
if (g > 0) ImGui::Text(" %ug %us %uc", g, s, c);
|
||||
else if (s > 0) ImGui::Text(" %us %uc", s, c);
|
||||
else ImGui::Text(" %uc", c);
|
||||
}
|
||||
}
|
||||
|
||||
// Complete button
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
float buttonW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||
|
||||
bool canComplete = quest.choiceRewards.empty() || selectedChoice >= 0;
|
||||
if (!canComplete) ImGui::BeginDisabled();
|
||||
if (ImGui::Button("Complete Quest", ImVec2(buttonW, 0))) {
|
||||
uint32_t rewardIdx = quest.choiceRewards.empty() ? 0 : static_cast<uint32_t>(selectedChoice);
|
||||
gameHandler.chooseQuestReward(rewardIdx);
|
||||
selectedChoice = -1;
|
||||
}
|
||||
if (!canComplete) ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel", ImVec2(buttonW, 0))) {
|
||||
gameHandler.closeQuestOfferReward();
|
||||
selectedChoice = -1;
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (!open) {
|
||||
gameHandler.closeQuestOfferReward();
|
||||
selectedChoice = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Vendor Window (Phase 5)
|
||||
// ============================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue