mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-01 11:13:51 +00:00
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:
- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module
Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.
game_handler.cpp reduced from ~10,188 to ~9,432 lines.
Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
199 lines
8 KiB
C++
199 lines
8 KiB
C++
#pragma once
|
|
|
|
#include "game/world_packets.hpp"
|
|
#include "game/opcode_table.hpp"
|
|
#include "game/handler_types.hpp"
|
|
#include "network/packet.hpp"
|
|
#include <array>
|
|
#include <chrono>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
namespace wowee {
|
|
namespace game {
|
|
|
|
class GameHandler;
|
|
enum class QuestGiverStatus : uint8_t;
|
|
|
|
class QuestHandler {
|
|
public:
|
|
using PacketHandler = std::function<void(network::Packet&)>;
|
|
using DispatchTable = std::unordered_map<LogicalOpcode, PacketHandler>;
|
|
|
|
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<GossipPoi>& 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<uint32_t, std::pair<uint32_t, uint32_t>> killCounts;
|
|
std::unordered_map<uint32_t, uint32_t> itemCounts;
|
|
std::unordered_map<uint32_t, uint32_t> requiredItemCounts;
|
|
struct KillObjective {
|
|
int32_t npcOrGoId = 0;
|
|
uint32_t required = 0;
|
|
};
|
|
std::array<KillObjective, 4> killObjectives{};
|
|
struct ItemObjective {
|
|
uint32_t itemId = 0;
|
|
uint32_t required = 0;
|
|
};
|
|
std::array<ItemObjective, 6> itemObjectives{};
|
|
int32_t rewardMoney = 0;
|
|
std::array<QuestRewardItem, 4> rewardItems{};
|
|
std::array<QuestRewardItem, 6> rewardChoiceItems{};
|
|
};
|
|
const std::vector<QuestLogEntry>& 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<uint32_t>& 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<uint64_t, QuestGiverStatus>& 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<uint16_t, uint32_t>& 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<uint32_t, float>& pendingQuestAcceptTimeoutsRef() { return pendingQuestAcceptTimeouts_; }
|
|
std::unordered_map<uint32_t, uint64_t>& 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<uint64_t, QuestGiverStatus>& npcQuestStatusRef() { return npcQuestStatus_; }
|
|
|
|
private:
|
|
// --- Packet handlers ---
|
|
void handleGossipMessage(network::Packet& packet);
|
|
void handleQuestgiverQuestList(network::Packet& packet);
|
|
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<GossipPoi> 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<uint32_t, float> pendingQuestAcceptTimeouts_;
|
|
std::unordered_map<uint32_t, uint64_t> pendingQuestAcceptNpcGuids_;
|
|
bool questOfferRewardOpen_ = false;
|
|
QuestOfferRewardData currentQuestOfferReward_;
|
|
|
|
// Quest log
|
|
std::vector<QuestLogEntry> questLog_;
|
|
int selectedQuestLogIndex_ = 0;
|
|
std::unordered_set<uint32_t> pendingQuestQueryIds_;
|
|
std::unordered_set<uint32_t> trackedQuestIds_;
|
|
bool pendingLoginQuestResync_ = false;
|
|
float pendingLoginQuestResyncTimeout_ = 0.0f;
|
|
|
|
// Quest giver status per NPC
|
|
std::unordered_map<uint64_t, QuestGiverStatus> 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
|