#pragma once #include "game/world_packets.hpp" #include "game/opcode_table.hpp" #include "game/handler_types.hpp" #include "network/packet.hpp" #include #include #include #include #include #include #include #include namespace wowee { namespace game { class GameHandler; enum class QuestGiverStatus : uint8_t; class QuestHandler { public: using PacketHandler = std::function; using DispatchTable = std::unordered_map; explicit QuestHandler(GameHandler& owner); void registerOpcodes(DispatchTable& table); // --- Public API (delegated from GameHandler) --- // NPC Gossip void selectGossipOption(uint32_t optionId); void selectGossipQuest(uint32_t questId); void acceptQuest(); void declineQuest(); void closeGossip(); void offerQuestFromItem(uint64_t itemGuid, uint32_t questId); bool isGossipWindowOpen() const { return gossipWindowOpen_; } const GossipMessageData& getCurrentGossip() const { return currentGossip_; } // Quest details bool isQuestDetailsOpen() { if (questDetailsOpen_) return true; if (questDetailsOpenTime_ != std::chrono::steady_clock::time_point{}) { if (std::chrono::steady_clock::now() >= questDetailsOpenTime_) { questDetailsOpen_ = true; questDetailsOpenTime_ = std::chrono::steady_clock::time_point{}; return true; } } return false; } const QuestDetailsData& getQuestDetails() const { return currentQuestDetails_; } // Gossip / quest map POI markers (aliased from handler_types.hpp) using GossipPoi = game::GossipPoi; const std::vector& getGossipPois() const { return gossipPois_; } void clearGossipPois() { gossipPois_.clear(); } // Quest turn-in bool isQuestRequestItemsOpen() const { return questRequestItemsOpen_; } const QuestRequestItemsData& getQuestRequestItems() const { return currentQuestRequestItems_; } void completeQuest(); void closeQuestRequestItems(); bool isQuestOfferRewardOpen() const { return questOfferRewardOpen_; } const QuestOfferRewardData& getQuestOfferReward() const { return currentQuestOfferReward_; } void chooseQuestReward(uint32_t rewardIndex); void closeQuestOfferReward(); // Quest log struct QuestLogEntry { uint32_t questId = 0; std::string title; std::string objectives; bool complete = false; std::unordered_map> killCounts; std::unordered_map itemCounts; std::unordered_map requiredItemCounts; struct KillObjective { int32_t npcOrGoId = 0; uint32_t required = 0; }; std::array killObjectives{}; struct ItemObjective { uint32_t itemId = 0; uint32_t required = 0; }; std::array itemObjectives{}; int32_t rewardMoney = 0; std::array rewardItems{}; std::array rewardChoiceItems{}; }; const std::vector& getQuestLog() const { return questLog_; } int getSelectedQuestLogIndex() const { return selectedQuestLogIndex_; } void setSelectedQuestLogIndex(int idx) { selectedQuestLogIndex_ = idx; } void abandonQuest(uint32_t questId); void shareQuestWithParty(uint32_t questId); bool requestQuestQuery(uint32_t questId, bool force = false); bool isQuestTracked(uint32_t questId) const { return trackedQuestIds_.count(questId) > 0; } void setQuestTracked(uint32_t questId, bool tracked); const std::unordered_set& getTrackedQuestIds() const { return trackedQuestIds_; } bool isQuestQueryPending(uint32_t questId) const { return pendingQuestQueryIds_.count(questId) > 0; } void clearQuestQueryPending(uint32_t questId) { pendingQuestQueryIds_.erase(questId); } // Quest giver status (! and ? markers) QuestGiverStatus getQuestGiverStatus(uint64_t guid) const; const std::unordered_map& getNpcQuestStatuses() const { return npcQuestStatus_; } // Shared quest bool hasPendingSharedQuest() const { return pendingSharedQuest_; } uint32_t getSharedQuestId() const { return sharedQuestId_; } const std::string& getSharedQuestTitle() const { return sharedQuestTitle_; } const std::string& getSharedQuestSharerName() const { return sharedQuestSharerName_; } void acceptSharedQuest(); void declineSharedQuest(); // --- Internal helpers called from GameHandler --- bool hasQuestInLog(uint32_t questId) const; int findQuestLogSlotIndexFromServer(uint32_t questId) const; void addQuestToLocalLogIfMissing(uint32_t questId, const std::string& title, const std::string& objectives); bool resyncQuestLogFromServerSlots(bool forceQueryMetadata); void applyQuestStateFromFields(const std::map& fields); void applyPackedKillCountsFromFields(QuestLogEntry& quest); void clearPendingQuestAccept(uint32_t questId); void triggerQuestAcceptResync(uint32_t questId, uint64_t npcGuid, const char* reason); // Pending quest accept timeout state (used by GameHandler::update) std::unordered_map& pendingQuestAcceptTimeoutsRef() { return pendingQuestAcceptTimeouts_; } std::unordered_map& pendingQuestAcceptNpcGuidsRef() { return pendingQuestAcceptNpcGuids_; } bool& pendingLoginQuestResyncRef() { return pendingLoginQuestResync_; } float& pendingLoginQuestResyncTimeoutRef() { return pendingLoginQuestResyncTimeout_; } // Direct state access for vendor/gossip interaction in GameHandler bool& gossipWindowOpenRef() { return gossipWindowOpen_; } GossipMessageData& currentGossipRef() { return currentGossip_; } std::unordered_map& npcQuestStatusRef() { return npcQuestStatus_; } private: // --- Packet handlers --- void handleGossipMessage(network::Packet& packet); void handleQuestgiverQuestList(network::Packet& packet); void classifyGossipQuests(bool updateQuestLog); void handleGossipComplete(network::Packet& packet); void handleQuestPoiQueryResponse(network::Packet& packet); void handleQuestDetails(network::Packet& packet); void handleQuestRequestItems(network::Packet& packet); void handleQuestOfferReward(network::Packet& packet); void handleQuestConfirmAccept(network::Packet& packet); GameHandler& owner_; // --- State --- // Gossip bool gossipWindowOpen_ = false; GossipMessageData currentGossip_; std::vector gossipPois_; // Quest details bool questDetailsOpen_ = false; std::chrono::steady_clock::time_point questDetailsOpenTime_{}; QuestDetailsData currentQuestDetails_; // Quest turn-in bool questRequestItemsOpen_ = false; QuestRequestItemsData currentQuestRequestItems_; uint32_t pendingTurnInQuestId_ = 0; uint64_t pendingTurnInNpcGuid_ = 0; bool pendingTurnInRewardRequest_ = false; std::unordered_map pendingQuestAcceptTimeouts_; std::unordered_map pendingQuestAcceptNpcGuids_; bool questOfferRewardOpen_ = false; QuestOfferRewardData currentQuestOfferReward_; // Quest log std::vector questLog_; int selectedQuestLogIndex_ = 0; std::unordered_set pendingQuestQueryIds_; std::unordered_set trackedQuestIds_; bool pendingLoginQuestResync_ = false; float pendingLoginQuestResyncTimeout_ = 0.0f; // Quest giver status per NPC std::unordered_map npcQuestStatus_; // Shared quest state bool pendingSharedQuest_ = false; uint32_t sharedQuestId_ = 0; std::string sharedQuestTitle_; std::string sharedQuestSharerName_; uint64_t sharedQuestSharerGuid_ = 0; }; } // namespace game } // namespace wowee