mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 17:43:52 +00:00
Fix gossip quest flow and questgiver marker state
This commit is contained in:
parent
b3ea6d8e81
commit
512d60c9be
3 changed files with 120 additions and 36 deletions
|
|
@ -5069,6 +5069,7 @@ void Application::updateQuestMarkers() {
|
||||||
markerType = 0; // Available (yellow !)
|
markerType = 0; // Available (yellow !)
|
||||||
break;
|
break;
|
||||||
case QuestGiverStatus::REWARD:
|
case QuestGiverStatus::REWARD:
|
||||||
|
case QuestGiverStatus::REWARD_REP:
|
||||||
markerType = 1; // Turn-in (yellow ?)
|
markerType = 1; // Turn-in (yellow ?)
|
||||||
break;
|
break;
|
||||||
case QuestGiverStatus::INCOMPLETE:
|
case QuestGiverStatus::INCOMPLETE:
|
||||||
|
|
|
||||||
|
|
@ -1837,11 +1837,16 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
||||||
}
|
}
|
||||||
case Opcode::SMSG_QUESTUPDATE_ADD_KILL: {
|
case Opcode::SMSG_QUESTUPDATE_ADD_KILL: {
|
||||||
// Quest kill count update
|
// Quest kill count update
|
||||||
if (packet.getSize() - packet.getReadPos() >= 16) {
|
// Compatibility: some classic-family opcode tables swap ADD_KILL and COMPLETE.
|
||||||
|
size_t rem = packet.getSize() - packet.getReadPos();
|
||||||
|
if (rem >= 12) {
|
||||||
uint32_t questId = packet.readUInt32();
|
uint32_t questId = packet.readUInt32();
|
||||||
uint32_t entry = packet.readUInt32(); // Creature entry
|
uint32_t entry = packet.readUInt32(); // Creature entry
|
||||||
uint32_t count = packet.readUInt32(); // Current kills
|
uint32_t count = packet.readUInt32(); // Current kills
|
||||||
uint32_t reqCount = packet.readUInt32(); // Required kills
|
uint32_t reqCount = 0;
|
||||||
|
if (packet.getSize() - packet.getReadPos() >= 4) {
|
||||||
|
reqCount = packet.readUInt32(); // Required kills (if present)
|
||||||
|
}
|
||||||
|
|
||||||
LOG_INFO("Quest kill update: questId=", questId, " entry=", entry,
|
LOG_INFO("Quest kill update: questId=", questId, " entry=", entry,
|
||||||
" count=", count, "/", reqCount);
|
" count=", count, "/", reqCount);
|
||||||
|
|
@ -1849,10 +1854,14 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
||||||
// Update quest log with kill count
|
// Update quest log with kill count
|
||||||
for (auto& quest : questLog_) {
|
for (auto& quest : questLog_) {
|
||||||
if (quest.questId == questId) {
|
if (quest.questId == questId) {
|
||||||
// Store kill progress (using entry as objective index)
|
// Preserve prior required count if this packet variant omits it.
|
||||||
|
if (reqCount == 0) {
|
||||||
|
auto it = quest.killCounts.find(entry);
|
||||||
|
if (it != quest.killCounts.end()) reqCount = it->second.second;
|
||||||
|
if (reqCount == 0) reqCount = count;
|
||||||
|
}
|
||||||
quest.killCounts[entry] = {count, reqCount};
|
quest.killCounts[entry] = {count, reqCount};
|
||||||
|
|
||||||
// Show progress message
|
|
||||||
std::string progressMsg = quest.title + ": " +
|
std::string progressMsg = quest.title + ": " +
|
||||||
std::to_string(count) + "/" +
|
std::to_string(count) + "/" +
|
||||||
std::to_string(reqCount);
|
std::to_string(reqCount);
|
||||||
|
|
@ -1863,6 +1872,17 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (rem >= 4) {
|
||||||
|
// Swapped mapping fallback: treat as QUESTUPDATE_COMPLETE packet.
|
||||||
|
uint32_t questId = packet.readUInt32();
|
||||||
|
LOG_INFO("Quest objectives completed (compat via ADD_KILL): questId=", questId);
|
||||||
|
for (auto& quest : questLog_) {
|
||||||
|
if (quest.questId == questId) {
|
||||||
|
quest.complete = true;
|
||||||
|
addSystemChatMessage("Quest Complete: " + quest.title);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1895,16 +1915,37 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Opcode::SMSG_QUESTUPDATE_COMPLETE: {
|
case Opcode::SMSG_QUESTUPDATE_COMPLETE: {
|
||||||
// Quest objectives completed - mark as ready to turn in
|
// Quest objectives completed - mark as ready to turn in.
|
||||||
uint32_t questId = packet.readUInt32();
|
// Compatibility: some classic-family opcode tables swap COMPLETE and ADD_KILL.
|
||||||
LOG_INFO("Quest objectives completed: questId=", questId);
|
size_t rem = packet.getSize() - packet.getReadPos();
|
||||||
|
if (rem >= 12) {
|
||||||
|
uint32_t questId = packet.readUInt32();
|
||||||
|
uint32_t entry = packet.readUInt32();
|
||||||
|
uint32_t count = packet.readUInt32();
|
||||||
|
uint32_t reqCount = 0;
|
||||||
|
if (packet.getSize() - packet.getReadPos() >= 4) reqCount = packet.readUInt32();
|
||||||
|
if (reqCount == 0) reqCount = count;
|
||||||
|
LOG_INFO("Quest kill update (compat via COMPLETE): questId=", questId,
|
||||||
|
" entry=", entry, " count=", count, "/", reqCount);
|
||||||
|
for (auto& quest : questLog_) {
|
||||||
|
if (quest.questId == questId) {
|
||||||
|
quest.killCounts[entry] = {count, reqCount};
|
||||||
|
addSystemChatMessage(quest.title + ": " + std::to_string(count) +
|
||||||
|
"/" + std::to_string(reqCount));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (rem >= 4) {
|
||||||
|
uint32_t questId = packet.readUInt32();
|
||||||
|
LOG_INFO("Quest objectives completed: questId=", questId);
|
||||||
|
|
||||||
for (auto& quest : questLog_) {
|
for (auto& quest : questLog_) {
|
||||||
if (quest.questId == questId) {
|
if (quest.questId == questId) {
|
||||||
quest.complete = true;
|
quest.complete = true;
|
||||||
addSystemChatMessage("Quest Complete: " + quest.title);
|
addSystemChatMessage("Quest Complete: " + quest.title);
|
||||||
LOG_INFO("Marked quest ", questId, " as complete");
|
LOG_INFO("Marked quest ", questId, " as complete");
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -8681,12 +8722,46 @@ void GameHandler::selectGossipOption(uint32_t optionId) {
|
||||||
void GameHandler::selectGossipQuest(uint32_t questId) {
|
void GameHandler::selectGossipQuest(uint32_t questId) {
|
||||||
if (state != WorldState::IN_WORLD || !socket || !gossipWindowOpen) return;
|
if (state != WorldState::IN_WORLD || !socket || !gossipWindowOpen) return;
|
||||||
|
|
||||||
// Always query quest from gossip and let the server drive next step:
|
// Prefer current gossip icon semantics to choose flow.
|
||||||
// - details (new quest), or
|
// WotLK/classic gossip icon conventions commonly use:
|
||||||
// - request items (turn-in check), or
|
// 2 = available (!), 4 = active/incomplete (?), 5 = completable (?)
|
||||||
// - offer reward.
|
const GossipQuestItem* gossipQuest = nullptr;
|
||||||
auto packet = QuestgiverQueryQuestPacket::build(currentGossip.npcGuid, questId);
|
for (const auto& q : currentGossip.quests) {
|
||||||
socket->send(packet);
|
if (q.questId == questId) {
|
||||||
|
gossipQuest = &q;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool iconSaysAvailable = gossipQuest && gossipQuest->questIcon == 2;
|
||||||
|
const bool iconSaysActive = gossipQuest &&
|
||||||
|
(gossipQuest->questIcon == 4 || gossipQuest->questIcon == 5);
|
||||||
|
|
||||||
|
// Keep quest-log fallback for servers that don't use canonical icon values.
|
||||||
|
const QuestLogEntry* activeQuest = nullptr;
|
||||||
|
for (const auto& q : questLog_) {
|
||||||
|
if (q.questId == questId) {
|
||||||
|
activeQuest = &q;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool shouldStartProgressFlow = iconSaysActive || (!iconSaysAvailable && activeQuest);
|
||||||
|
if (shouldStartProgressFlow) {
|
||||||
|
pendingTurnInQuestId_ = questId;
|
||||||
|
pendingTurnInNpcGuid_ = currentGossip.npcGuid;
|
||||||
|
pendingTurnInRewardRequest_ = activeQuest ? activeQuest->complete : false;
|
||||||
|
auto packet = QuestgiverCompleteQuestPacket::build(currentGossip.npcGuid, questId);
|
||||||
|
socket->send(packet);
|
||||||
|
} else {
|
||||||
|
pendingTurnInQuestId_ = 0;
|
||||||
|
pendingTurnInNpcGuid_ = 0;
|
||||||
|
pendingTurnInRewardRequest_ = false;
|
||||||
|
auto packet = packetParsers_
|
||||||
|
? packetParsers_->buildQueryQuestPacket(currentGossip.npcGuid, questId)
|
||||||
|
: QuestgiverQueryQuestPacket::build(currentGossip.npcGuid, questId);
|
||||||
|
socket->send(packet);
|
||||||
|
}
|
||||||
|
|
||||||
gossipWindowOpen = false;
|
gossipWindowOpen = false;
|
||||||
}
|
}
|
||||||
|
|
@ -9227,7 +9302,11 @@ void GameHandler::handleGossipMessage(network::Packet& packet) {
|
||||||
gossipWindowOpen = true;
|
gossipWindowOpen = true;
|
||||||
vendorWindowOpen = false; // Close vendor if gossip opens
|
vendorWindowOpen = false; // Close vendor if gossip opens
|
||||||
|
|
||||||
// Query quest data and update quest log based on gossip quests
|
// Update known quest-log entries based on gossip quests.
|
||||||
|
// Do not synthesize new "active quest" entries from gossip alone.
|
||||||
|
bool hasAvailableQuest = false;
|
||||||
|
bool hasRewardQuest = false;
|
||||||
|
bool hasIncompleteQuest = false;
|
||||||
for (const auto& questItem : currentGossip.quests) {
|
for (const auto& questItem : currentGossip.quests) {
|
||||||
// WotLK gossip questIcon is an integer enum, NOT a bitmask:
|
// WotLK gossip questIcon is an integer enum, NOT a bitmask:
|
||||||
// 2 = yellow ! (available, not yet accepted)
|
// 2 = yellow ! (available, not yet accepted)
|
||||||
|
|
@ -9237,28 +9316,30 @@ void GameHandler::handleGossipMessage(network::Packet& packet) {
|
||||||
// quests as completable and causing the server to reject the turn-in request.
|
// quests as completable and causing the server to reject the turn-in request.
|
||||||
bool isCompletable = (questItem.questIcon == 5); // Gold ? = can turn in
|
bool isCompletable = (questItem.questIcon == 5); // Gold ? = can turn in
|
||||||
bool isIncomplete = (questItem.questIcon == 4); // Gray ? = in progress
|
bool isIncomplete = (questItem.questIcon == 4); // Gray ? = in progress
|
||||||
|
bool isAvailable = (questItem.questIcon == 2); // Yellow ! = available
|
||||||
|
|
||||||
// Add or update quest in log
|
hasAvailableQuest |= isAvailable;
|
||||||
bool found = false;
|
hasRewardQuest |= isCompletable;
|
||||||
|
hasIncompleteQuest |= isIncomplete;
|
||||||
|
|
||||||
|
// Update existing quest entry if present
|
||||||
for (auto& quest : questLog_) {
|
for (auto& quest : questLog_) {
|
||||||
if (quest.questId == questItem.questId) {
|
if (quest.questId == questItem.questId) {
|
||||||
quest.complete = isCompletable;
|
quest.complete = isCompletable;
|
||||||
quest.title = questItem.title;
|
quest.title = questItem.title;
|
||||||
found = true;
|
|
||||||
LOG_INFO("Updated quest ", questItem.questId, " in log: complete=", isCompletable);
|
LOG_INFO("Updated quest ", questItem.questId, " in log: complete=", isCompletable);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!found && (isCompletable || isIncomplete)) {
|
// Keep overhead marker aligned with what this gossip actually offers.
|
||||||
// Quest is active (either completable or incomplete) - add to log
|
if (currentGossip.npcGuid != 0) {
|
||||||
QuestLogEntry entry;
|
QuestGiverStatus derivedStatus = QuestGiverStatus::NONE;
|
||||||
entry.questId = questItem.questId;
|
if (hasRewardQuest) derivedStatus = QuestGiverStatus::REWARD;
|
||||||
entry.complete = isCompletable;
|
else if (hasAvailableQuest) derivedStatus = QuestGiverStatus::AVAILABLE;
|
||||||
entry.title = questItem.title;
|
else if (hasIncompleteQuest) derivedStatus = QuestGiverStatus::INCOMPLETE;
|
||||||
questLog_.push_back(entry);
|
npcQuestStatus_[currentGossip.npcGuid] = derivedStatus;
|
||||||
LOG_INFO("Added quest ", questItem.questId, " to log: complete=", isCompletable);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Play NPC greeting voice
|
// Play NPC greeting voice
|
||||||
|
|
|
||||||
|
|
@ -6013,7 +6013,8 @@ void GameScreen::renderQuestMarkers(game::GameHandler& gameHandler) {
|
||||||
} else if (status == game::QuestGiverStatus::AVAILABLE_LOW) {
|
} else if (status == game::QuestGiverStatus::AVAILABLE_LOW) {
|
||||||
marker = "!";
|
marker = "!";
|
||||||
color = IM_COL32(160, 160, 160, 255); // gray
|
color = IM_COL32(160, 160, 160, 255); // gray
|
||||||
} else if (status == game::QuestGiverStatus::REWARD) {
|
} else if (status == game::QuestGiverStatus::REWARD ||
|
||||||
|
status == game::QuestGiverStatus::REWARD_REP) {
|
||||||
marker = "?";
|
marker = "?";
|
||||||
} else if (status == game::QuestGiverStatus::INCOMPLETE) {
|
} else if (status == game::QuestGiverStatus::INCOMPLETE) {
|
||||||
marker = "?";
|
marker = "?";
|
||||||
|
|
@ -6115,7 +6116,8 @@ void GameScreen::renderMinimapMarkers(game::GameHandler& gameHandler) {
|
||||||
} else if (status == game::QuestGiverStatus::AVAILABLE_LOW) {
|
} else if (status == game::QuestGiverStatus::AVAILABLE_LOW) {
|
||||||
dotColor = IM_COL32(160, 160, 160, 255);
|
dotColor = IM_COL32(160, 160, 160, 255);
|
||||||
marker = "!";
|
marker = "!";
|
||||||
} else if (status == game::QuestGiverStatus::REWARD) {
|
} else if (status == game::QuestGiverStatus::REWARD ||
|
||||||
|
status == game::QuestGiverStatus::REWARD_REP) {
|
||||||
dotColor = IM_COL32(255, 210, 0, 255);
|
dotColor = IM_COL32(255, 210, 0, 255);
|
||||||
marker = "?";
|
marker = "?";
|
||||||
} else if (status == game::QuestGiverStatus::INCOMPLETE) {
|
} else if (status == game::QuestGiverStatus::INCOMPLETE) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue