#ifndef _PLAYERBOT_NEWRPGINFO_H #define _PLAYERBOT_NEWRPGINFO_H #include #include "Define.h" #include "ObjectGuid.h" #include "ObjectMgr.h" #include "QuestDef.h" #include "Strategy.h" #include "Timer.h" #include "TravelMgr.h" #include "TravelNode.h" using NewRpgStatusTransitionProb = std::vector>; struct NewRpgInfo { NewRpgInfo() : data(Idle{}) {} ~NewRpgInfo() = default; // RPG_GO_GRIND struct GoGrind { WorldPosition pos{}; }; // RPG_GO_CAMP struct GoCamp { WorldPosition pos{}; }; // RPG_WANDER_NPC struct WanderNpc { ObjectGuid npcOrGo{}; uint32 lastReach{0}; }; // RPG_WANDER_RANDOM struct WanderRandom { WanderRandom() = default; }; // RPG_DO_QUEST struct DoQuest { const Quest* quest{nullptr}; uint32 questId{0}; int32 objectiveIdx{0}; WorldPosition pos{}; uint32 lastReachPOI{0}; // committed target per objective type. stops zig-zagging in // dense spawn clusters when "nearest" would flip each tick. ObjectGuid pursuedLootGO{}; // GOs we loot (lilies, eggs) ObjectGuid pursuedUseGO{}; // GOs we click or focus on ObjectGuid pursuedUseTarget{}; // creature we apply an item to }; // RPG_TRAVEL_FLIGHT struct TravelFlight { uint32 flightMasterEntry{0}; WorldPosition flightMasterPos{}; std::vector path; bool inFlight{false}; }; // RPG_REST struct Rest { Rest() = default; }; // RPG_OUTDOOR_PVP struct OutdoorPvP { ObjectGuid::LowType capturePointSpawnId{0}; }; struct Idle { }; uint32 startT{0}; // start timestamp of the current status // Travel Node System TravelPlan travelPlan; bool HasActiveTravelPlan() const { return travelPlan.IsActive(); } void ClearTravel() { travelPlan.Reset(); } // MoveFar attempt history. Records the last 3 path commits (node // plan or mmap) so MoveFarTo can detect when the same dest + // strategy has failed repeatedly and force the alternative // routing this tick. Breaks deterministic-loop scenarios where // the chained probe (or node graph) keeps returning the same // dead-end path. Cmangos doesn't do this — they wait 5+ minutes // for UnstuckAction. We're more aggressive here for UX. struct MoveFarAttempt { WorldPosition dest; // requested destination bool wasNodeTravel{false}; // true=node plan, false=mmap/spline uint32 timestamp{0}; }; std::deque recentMoveFarAttempts; void RecordMoveFarAttempt(WorldPosition const& dest, bool wasNodeTravel); int CountRecentAttempts(WorldPosition const& dest, bool wasNodeTravel) const; using RpgData = std::variant< Idle, GoGrind, GoCamp, WanderNpc, WanderRandom, DoQuest, Rest, TravelFlight, OutdoorPvP >; RpgData data; NewRpgStatus GetStatus(); bool HasStatusPersisted(uint32 maxDuration) { return GetMSTimeDiffToNow(startT) > maxDuration; } void ChangeToGoGrind(WorldPosition pos); void ChangeToGoCamp(WorldPosition pos); void ChangeToWanderNpc(); void ChangeToWanderRandom(); void ChangeToDoQuest(uint32 questId, const Quest* quest); void ChangeToTravelFlight(uint32 flightMasterEntry, WorldPosition flightMasterPos, std::vector path); void ChangeToOutdoorPvp(ObjectGuid::LowType capturePointSpawnId = 0); void ChangeToRest(); void ChangeToIdle(); bool CanChangeTo(NewRpgStatus status); void Reset(); std::string ToString(); }; struct NewRpgStatistic { uint32 questAccepted{0}; uint32 questCompleted{0}; uint32 questAbandoned{0}; uint32 questRewarded{0}; uint32 questDropped{0}; NewRpgStatistic operator+(const NewRpgStatistic& other) const { NewRpgStatistic result; result.questAccepted = this->questAccepted + other.questAccepted; result.questCompleted = this->questCompleted + other.questCompleted; result.questAbandoned = this->questAbandoned + other.questAbandoned; result.questRewarded = this->questRewarded + other.questRewarded; result.questDropped = this->questDropped + other.questDropped; return result; } NewRpgStatistic& operator+=(const NewRpgStatistic& other) { this->questAccepted += other.questAccepted; this->questCompleted += other.questCompleted; this->questAbandoned += other.questAbandoned; this->questRewarded += other.questRewarded; this->questDropped += other.questDropped; return *this; } }; #endif