mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 00:03:50 +00:00
fix(quest): quest log population, NPC marker updates on accept/abandon
- Delegate GameHandler::getQuestGiverStatus() to QuestHandler instead of reading from GameHandler's own empty npcQuestStatus_ map - Immediately add quest to local log in acceptQuest() instead of waiting for field updates, fixing quests not appearing after accept - Handle duplicate accept path (server already has quest) by also adding to local log - Remove early return on empty questLog_ in applyQuestStateFromFields() - Re-query nearby quest giver NPC statuses on abandon so markers refresh Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
This commit is contained in:
parent
9c1ffae140
commit
759d6046bb
3 changed files with 49 additions and 7 deletions
|
|
@ -1699,10 +1699,7 @@ public:
|
||||||
bool isServerMovementAllowed() const;
|
bool isServerMovementAllowed() const;
|
||||||
|
|
||||||
// Quest giver status (! and ? markers)
|
// Quest giver status (! and ? markers)
|
||||||
QuestGiverStatus getQuestGiverStatus(uint64_t guid) const {
|
QuestGiverStatus getQuestGiverStatus(uint64_t guid) const;
|
||||||
auto it = npcQuestStatus_.find(guid);
|
|
||||||
return (it != npcQuestStatus_.end()) ? it->second : QuestGiverStatus::NONE;
|
|
||||||
}
|
|
||||||
const std::unordered_map<uint64_t, QuestGiverStatus>& getNpcQuestStatuses() const;
|
const std::unordered_map<uint64_t, QuestGiverStatus>& getNpcQuestStatuses() const;
|
||||||
|
|
||||||
// Charge callback — fires when player casts a charge spell toward target
|
// Charge callback — fires when player casts a charge spell toward target
|
||||||
|
|
|
||||||
|
|
@ -2261,6 +2261,10 @@ const std::unordered_map<uint64_t, QuestGiverStatus>& GameHandler::getNpcQuestSt
|
||||||
static const std::unordered_map<uint64_t, QuestGiverStatus> empty;
|
static const std::unordered_map<uint64_t, QuestGiverStatus> empty;
|
||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
|
QuestGiverStatus GameHandler::getQuestGiverStatus(uint64_t guid) const {
|
||||||
|
if (questHandler_) return questHandler_->getQuestGiverStatus(guid);
|
||||||
|
return QuestGiverStatus::NONE;
|
||||||
|
}
|
||||||
const std::vector<GameHandler::QuestLogEntry>& GameHandler::getQuestLog() const {
|
const std::vector<GameHandler::QuestLogEntry>& GameHandler::getQuestLog() const {
|
||||||
if (questHandler_) return questHandler_->getQuestLog();
|
if (questHandler_) return questHandler_->getQuestLog();
|
||||||
static const std::vector<QuestLogEntry> empty;
|
static const std::vector<QuestLogEntry> empty;
|
||||||
|
|
|
||||||
|
|
@ -339,6 +339,8 @@ void QuestHandler::registerOpcodes(DispatchTable& table) {
|
||||||
if (packet.hasRemaining(9)) {
|
if (packet.hasRemaining(9)) {
|
||||||
uint64_t npcGuid = packet.readUInt64();
|
uint64_t npcGuid = packet.readUInt64();
|
||||||
uint8_t status = owner_.getPacketParsers()->readQuestGiverStatus(packet);
|
uint8_t status = owner_.getPacketParsers()->readQuestGiverStatus(packet);
|
||||||
|
LOG_INFO("SMSG_QUESTGIVER_STATUS: npcGuid=0x", std::hex, npcGuid, std::dec,
|
||||||
|
" status=", static_cast<int>(status));
|
||||||
npcQuestStatus_[npcGuid] = static_cast<QuestGiverStatus>(status);
|
npcQuestStatus_[npcGuid] = static_cast<QuestGiverStatus>(status);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1075,8 +1077,17 @@ void QuestHandler::acceptQuest() {
|
||||||
const bool inLocalLog = hasQuestInLog(questId);
|
const bool inLocalLog = hasQuestInLog(questId);
|
||||||
const int serverSlot = findQuestLogSlotIndexFromServer(questId);
|
const int serverSlot = findQuestLogSlotIndexFromServer(questId);
|
||||||
if (serverSlot >= 0) {
|
if (serverSlot >= 0) {
|
||||||
LOG_INFO("Ignoring duplicate quest accept already in server quest log: questId=", questId,
|
LOG_INFO("Quest already in server quest log: questId=", questId,
|
||||||
" slot=", serverSlot);
|
" slot=", serverSlot, " inLocalLog=", inLocalLog);
|
||||||
|
// Ensure it's in our local log even if server already has it
|
||||||
|
addQuestToLocalLogIfMissing(questId, currentQuestDetails_.title, currentQuestDetails_.objectives);
|
||||||
|
requestQuestQuery(questId, false);
|
||||||
|
// Re-query NPC status from server
|
||||||
|
if (npcGuid && owner_.getSocket()) {
|
||||||
|
network::Packet qsPkt(wireOpcode(Opcode::CMSG_QUESTGIVER_STATUS_QUERY));
|
||||||
|
qsPkt.writeUInt64(npcGuid);
|
||||||
|
owner_.getSocket()->send(qsPkt);
|
||||||
|
}
|
||||||
questDetailsOpen_ = false;
|
questDetailsOpen_ = false;
|
||||||
questDetailsOpenTime_ = std::chrono::steady_clock::time_point{};
|
questDetailsOpenTime_ = std::chrono::steady_clock::time_point{};
|
||||||
currentQuestDetails_ = QuestDetailsData{};
|
currentQuestDetails_ = QuestDetailsData{};
|
||||||
|
|
@ -1094,6 +1105,9 @@ void QuestHandler::acceptQuest() {
|
||||||
pendingQuestAcceptTimeouts_[questId] = 5.0f;
|
pendingQuestAcceptTimeouts_[questId] = 5.0f;
|
||||||
pendingQuestAcceptNpcGuids_[questId] = npcGuid;
|
pendingQuestAcceptNpcGuids_[questId] = npcGuid;
|
||||||
|
|
||||||
|
// Immediately add to local quest log using available details
|
||||||
|
addQuestToLocalLogIfMissing(questId, currentQuestDetails_.title, currentQuestDetails_.objectives);
|
||||||
|
|
||||||
// Play quest-accept sound
|
// Play quest-accept sound
|
||||||
if (auto* ac = owner_.services().audioCoordinator) {
|
if (auto* ac = owner_.services().audioCoordinator) {
|
||||||
if (auto* sfx = ac->getUiSoundManager())
|
if (auto* sfx = ac->getUiSoundManager())
|
||||||
|
|
@ -1223,6 +1237,19 @@ void QuestHandler::abandonQuest(uint32_t questId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-query nearby quest giver NPCs so markers refresh (e.g. "?" → "!")
|
||||||
|
if (owner_.getSocket()) {
|
||||||
|
for (const auto& [guid, entity] : owner_.getEntityManager().getEntities()) {
|
||||||
|
if (entity->getType() != ObjectType::UNIT) continue;
|
||||||
|
auto unit = std::static_pointer_cast<Unit>(entity);
|
||||||
|
if (unit->getNpcFlags() & 0x02) {
|
||||||
|
network::Packet qsPkt(wireOpcode(Opcode::CMSG_QUESTGIVER_STATUS_QUERY));
|
||||||
|
qsPkt.writeUInt64(guid);
|
||||||
|
owner_.getSocket()->send(qsPkt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Remove any quest POI minimap markers for this quest.
|
// Remove any quest POI minimap markers for this quest.
|
||||||
gossipPois_.erase(
|
gossipPois_.erase(
|
||||||
std::remove_if(gossipPois_.begin(), gossipPois_.end(),
|
std::remove_if(gossipPois_.begin(), gossipPois_.end(),
|
||||||
|
|
@ -1376,7 +1403,7 @@ bool QuestHandler::resyncQuestLogFromServerSlots(bool forceQueryMetadata) {
|
||||||
|
|
||||||
void QuestHandler::applyQuestStateFromFields(const std::map<uint16_t, uint32_t>& fields) {
|
void QuestHandler::applyQuestStateFromFields(const std::map<uint16_t, uint32_t>& fields) {
|
||||||
const uint16_t ufQuestStart = fieldIndex(UF::PLAYER_QUEST_LOG_START);
|
const uint16_t ufQuestStart = fieldIndex(UF::PLAYER_QUEST_LOG_START);
|
||||||
if (ufQuestStart == 0xFFFF || questLog_.empty()) return;
|
if (ufQuestStart == 0xFFFF) return;
|
||||||
|
|
||||||
const uint8_t qStride = owner_.getPacketParsers() ? owner_.getPacketParsers()->questLogStride() : 5;
|
const uint8_t qStride = owner_.getPacketParsers() ? owner_.getPacketParsers()->questLogStride() : 5;
|
||||||
if (qStride < 2) return;
|
if (qStride < 2) return;
|
||||||
|
|
@ -1391,6 +1418,20 @@ void QuestHandler::applyQuestStateFromFields(const std::map<uint16_t, uint32_t>&
|
||||||
uint32_t questId = idIt->second;
|
uint32_t questId = idIt->second;
|
||||||
if (questId == 0) continue;
|
if (questId == 0) continue;
|
||||||
|
|
||||||
|
// Add quest to local log only if we have a pending accept for it
|
||||||
|
if (!hasQuestInLog(questId) && pendingQuestAcceptTimeouts_.count(questId) != 0) {
|
||||||
|
addQuestToLocalLogIfMissing(questId, "Quest #" + std::to_string(questId), "");
|
||||||
|
requestQuestQuery(questId, false);
|
||||||
|
// Re-query quest giver status for the NPC that gave us this quest
|
||||||
|
auto pendingIt = pendingQuestAcceptNpcGuids_.find(questId);
|
||||||
|
if (pendingIt != pendingQuestAcceptNpcGuids_.end() && pendingIt->second != 0 && owner_.getSocket()) {
|
||||||
|
network::Packet qsPkt(wireOpcode(Opcode::CMSG_QUESTGIVER_STATUS_QUERY));
|
||||||
|
qsPkt.writeUInt64(pendingIt->second);
|
||||||
|
owner_.getSocket()->send(qsPkt);
|
||||||
|
}
|
||||||
|
clearPendingQuestAccept(questId);
|
||||||
|
}
|
||||||
|
|
||||||
auto stateIt = fields.find(stateField);
|
auto stateIt = fields.find(stateField);
|
||||||
if (stateIt == fields.end()) continue;
|
if (stateIt == fields.end()) continue;
|
||||||
bool serverComplete = ((stateIt->second & 0xFF) == kQuestStatusComplete);
|
bool serverComplete = ((stateIt->second & 0xFF) == kQuestStatusComplete);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue