/* * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #ifndef WORLD_STATE_H #define WORLD_STATE_H #include "AreaDefines.h" #include "Player.h" #include "WorldStateDefines.h" #include enum WorldStateCondition { WORLD_STATE_CONDITION_TROLLBANES_COMMAND = 39911, WORLD_STATE_CONDITION_NAZGRELS_FAVOR = 39913, // Zeppelins WORLD_STATE_CONDITION_THE_THUNDERCALLER = 164871, WORLD_STATE_CONDITION_THE_IRON_EAGLE = 175080, WORLD_STATE_CONDITION_THE_PURPLE_PRINCESS = 176495, }; enum WorldStateConditionState { WORLD_STATE_CONDITION_STATE_NONE = 0, }; enum WorldStateEvent { WORLD_STATE_CUSTOM_EVENT_ON_ADALS_SONG_OF_BATTLE = 39953, WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_SPAWN = 184640, WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_DESPAWN = 184641, }; enum WorldStateSpells { SPELL_ADAL_SONG_OF_BATTLE = 39953, SPELL_TROLLBANES_COMMAND = 39911, SPELL_NAZGRELS_FAVOR = 39913, SPELL_KIRU_SONG_OF_VICTORY = 46302, }; enum WorldStateSaveIds { SAVE_ID_SCOURGE_INVASION = 3, SAVE_ID_QUEL_DANAS = 20, }; enum WorldStateGameEvents { // Scourge Invasion GAME_EVENT_SCOURGE_INVASION = 17, GAME_EVENT_SCOURGE_INVASION_BOSSES = 120, GAME_EVENT_SCOURGE_INVASION_WINTERSPRING = 121, GAME_EVENT_SCOURGE_INVASION_TANARIS = 122, GAME_EVENT_SCOURGE_INVASION_AZSHARA = 123, GAME_EVENT_SCOURGE_INVASION_BLASTED_LANDS = 124, GAME_EVENT_SCOURGE_INVASION_EASTERN_PLAGUELANDS = 125, GAME_EVENT_SCOURGE_INVASION_BURNING_STEPPES = 126, GAME_EVENT_SCOURGE_INVASION_50_INVASIONS = 127, GAME_EVENT_SCOURGE_INVASION_100_INVASIONS = 128, GAME_EVENT_SCOURGE_INVASION_150_INVASIONS = 129, GAME_EVENT_SCOURGE_INVASION_INVASIONS_DONE = 130, // Zombie infestation GAME_EVENT_ZOMBIE_INFESTATION_PHASE_1 = 110, GAME_EVENT_ZOMBIE_INFESTATION_PHASE_2 = 111, GAME_EVENT_ZOMBIE_INFESTATION_PHASE_3 = 112, GAME_EVENT_ZOMBIE_INFESTATION_PHASE_4 = 113, GAME_EVENT_ZOMBIE_INFESTATION_PHASE_5 = 114, GAME_EVENT_ZOMBIE_INFESTATION_PHASE_6 = 115, // Isle phases GAME_EVENT_QUEL_DANAS_PHASE_1 = 101, GAME_EVENT_QUEL_DANAS_PHASE_2_ONLY = 102, GAME_EVENT_QUEL_DANAS_PHASE_2_PERMANENT = 103, GAME_EVENT_QUEL_DANAS_PHASE_2_NO_PORTAL = 104, GAME_EVENT_QUEL_DANAS_PHASE_2_PORTAL = 105, GAME_EVENT_QUEL_DANAS_PHASE_3_ONLY = 106, GAME_EVENT_QUEL_DANAS_PHASE_3_PERMANENT = 107, GAME_EVENT_QUEL_DANAS_PHASE_3_NO_ANVIL = 108, GAME_EVENT_QUEL_DANAS_PHASE_3_ANVIL = 109, GAME_EVENT_QUEL_DANAS_PHASE_4 = 110, GAME_EVENT_QUEL_DANAS_PHASE_4_NO_MONUMENT = 111, GAME_EVENT_QUEL_DANAS_PHASE_4_MONUMENT = 112, GAME_EVENT_QUEL_DANAS_PHASE_4_NO_ALCHEMY_LAB= 113, GAME_EVENT_QUEL_DANAS_PHASE_4_ALCHEMY_LAB = 114, GAME_EVENT_QUEL_DANAS_PHASE_4_KIRU = 115, // SWP Phases GAME_EVENT_SWP_GATES_PHASE_0 = 116, // All Gates Closed GAME_EVENT_SWP_GATES_PHASE_1 = 117, // First Gate Open GAME_EVENT_SWP_GATES_PHASE_2 = 118, // Second Gate Open GAME_EVENT_SWP_GATES_PHASE_3 = 119, // All Gates Open }; enum SIMisc { EVENT_HERALD_OF_THE_LICH_KING_ZONE_START = 7, EVENT_HERALD_OF_THE_LICH_KING_ZONE_STOP = 8, NPC_PALLID_HORROR = 16394, NPC_PATCHWORK_TERROR = 16382, NPC_HERALD_OF_THE_LICH_KING = 16995, ITEM_A_LETTER_FROM_THE_KEEPER_OF_THE_ROLLS = 22723, NPC_ARGENT_EMISSARY = 16285, MAIL_TEMPLATE_ARGENT_DAWN_NEEDS_YOUR_HELP = 171, }; enum SIState : uint32 { STATE_0_DISABLED, STATE_1_ENABLED, SI_STATE_MAX, }; enum SIZoneIds { SI_ZONE_AZSHARA, SI_ZONE_BLASTED_LANDS, SI_ZONE_BURNING_STEPPES, SI_ZONE_EASTERN_PLAGUELANDS, SI_ZONE_TANARIS, SI_ZONE_WINTERSPRING, SI_ZONE_STORMWIND, SI_ZONE_UNDERCITY }; enum SITimers { SI_TIMER_AZSHARA, SI_TIMER_BLASTED_LANDS, SI_TIMER_BURNING_STEPPES, SI_TIMER_EASTERN_PLAGUELANDS, SI_TIMER_TANARIS, SI_TIMER_WINTERSPRING, SI_TIMER_STORMWIND, SI_TIMER_UNDERCITY, SI_TIMER_MAX, }; enum SIRemaining { SI_REMAINING_AZSHARA, SI_REMAINING_BLASTED_LANDS, SI_REMAINING_BURNING_STEPPES, SI_REMAINING_EASTERN_PLAGUELANDS, SI_REMAINING_TANARIS, SI_REMAINING_WINTERSPRING, SI_REMAINING_MAX, }; enum SICityTimers { // These timers may fail if you set it under 1 minute. ZONE_ATTACK_TIMER_MIN = 60 * 45, // 45 min. ZONE_ATTACK_TIMER_MAX = 60 * 60, // 60 min. CITY_ATTACK_TIMER_MIN = 60 * 45, // 45 min. CITY_ATTACK_TIMER_MAX = 60 * 60, // 60 min. }; struct ScourgeInvasionData { struct InvasionZone { uint32 map; uint32 zoneId; uint32 necropolisCount; uint32 remainingNecropoli; std::vector mouth; ObjectGuid mouthGuid {}; }; struct CityAttack { uint32 map; uint32 zoneId; std::vector pallid; ObjectGuid pallidGuid {}; }; SIState m_state; TimePoint m_timers[SI_TIMER_MAX]; uint32 m_battlesWon; uint32 m_lastAttackZone; uint32 m_remaining[SI_REMAINING_MAX]; uint64 m_broadcastTimer; std::mutex m_siMutex; std::set m_pendingInvasions; std::set m_pendingPallids; std::map m_activeInvasions; std::map m_cityAttacks; ScourgeInvasionData(); void Reset(); std::string GetData(); }; enum SunsReachPhases { SUNS_REACH_PHASE_1_STAGING_AREA, SUNS_REACH_PHASE_2_SANCTUM, SUNS_REACH_PHASE_3_ARMORY, SUNS_REACH_PHASE_4_HARBOR, }; enum SunsReachSubPhases { SUBPHASE_PORTAL = 0x01, SUBPHASE_ANVIL = 0x02, SUBPHASE_ALCHEMY_LAB = 0x04, SUBPHASE_MONUMENT = 0x08, SUBPHASE_ALL = SUBPHASE_PORTAL | SUBPHASE_ANVIL | SUBPHASE_ALCHEMY_LAB | SUBPHASE_MONUMENT, }; enum SunsReachCounters { COUNTER_ERRATIC_BEHAVIOR, COUNTER_SANCTUM_WARDS, COUNTER_BATTLE_FOR_THE_SUNS_REACH_ARMORY, COUNTER_DISTRACTION_AT_THE_DEAD_SCAR, COUNTER_INTERCEPTING_THE_MANA_CELLS, COUNTER_INTERCEPT_THE_REINFORCEMENTS, COUNTER_TAKING_THE_HARBOR, COUNTER_MAKING_READY, COUNTER_DISCOVERING_YOUR_ROOTS, COUNTER_A_CHARITABLE_DONATION, COUNTERS_MAX, }; enum SunwellGates { SUNWELL_ALL_GATES_CLOSED, SUNWELL_AGAMATH_GATE1_OPEN, SUNWELL_ROHENDOR_GATE2_OPEN, SUNWELL_ARCHONISUS_GATE3_OPEN, }; enum SunwellGateCounters { COUNTER_AGAMATH_THE_FIRST_GATE, COUNTER_ROHENDOR_THE_SECOND_GATE, COUNTER_ARCHONISUS_THE_FINAL_GATE, COUNTERS_MAX_GATES, }; struct SunsReachReclamationData { uint32 m_phase; uint32 m_subphaseMask; uint32 m_sunsReachReclamationCounters[COUNTERS_MAX]; GuidVector m_sunsReachReclamationPlayers; std::mutex m_sunsReachReclamationMutex; uint32 m_gate; uint32 m_gateCounters[COUNTERS_MAX_GATES]; SunsReachReclamationData() : m_phase(SUNS_REACH_PHASE_1_STAGING_AREA), m_subphaseMask(0), m_gate(SUNWELL_ALL_GATES_CLOSED) { memset(m_sunsReachReclamationCounters, 0, sizeof(m_sunsReachReclamationCounters)); memset(m_gateCounters, 0, sizeof(m_gateCounters)); } std::string GetData(); uint32 GetPhasePercentage(uint32 phase); uint32 GetSubPhasePercentage(uint32 subPhase); uint32 GetSunwellGatePercentage(uint32 gate); }; // Intended for implementing server wide scripts, note: all behaviour must be safeguarded towards multithreading class WorldState { public: WorldState(); virtual ~WorldState(); static WorldState* instance(); void Load(); void LoadWorldStates(); void setWorldState(uint32 index, uint64 value); [[nodiscard]] uint64 getWorldState(uint32 index) const; void Save(WorldStateSaveIds saveId); void SaveHelper(std::string& stringToSave, WorldStateSaveIds saveId); void HandlePlayerEnterZone(Player* player, AreaTableIDs zoneId); void HandlePlayerLeaveZone(Player* player, AreaTableIDs zoneId); bool IsConditionFulfilled(uint32 conditionId, uint32 state = WORLD_STATE_CONDITION_STATE_NONE) const; void HandleConditionStateChange(WorldStateCondition conditionId, WorldStateConditionState state); void HandleExternalEvent(WorldStateEvent eventId, uint32 param); void Update(uint32 diff); void AddSunwellGateProgress(uint32 questId); void AddSunsReachProgress(uint32 questId); std::string GetSunsReachPrintout(); void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& packet, uint32 zoneId, uint32 /*areaId*/); void HandleSunsReachPhaseTransition(uint32 newPhase); void HandleSunsReachSubPhaseTransition(int32 subPhaseMask, bool initial = false); void SetSunsReachCounter(SunsReachCounters index, uint32 value); void HandleSunwellGateTransition(uint32 newGate); void SetSunwellGateCounter(SunwellGateCounters index, uint32 value); private: typedef std::map WorldStatesMap; WorldStatesMap _worldstates; void SendWorldstateUpdate(std::mutex& mutex, GuidVector const& guids, uint32 value, uint32 worldStateId); void StopSunsReachPhase(bool forward); void StartSunsReachPhase(bool initial = false); void StartSunwellGatePhase(); void StopSunwellGatePhase(); void BuffAdalsSongOfBattle(); void DispelAdalsSongOfBattle(); uint32 _adalSongOfBattleTimer; void BuffMagtheridonTeam(TeamId team); void DispelMagtheridonTeam(TeamId team); bool _isMagtheridonHeadSpawnedHorde; bool _isMagtheridonHeadSpawnedAlliance; SunsReachReclamationData m_sunsReachData; std::map> _transportStates; // atomic to avoid having to lock std::mutex _mutex; // all World State operations are threat unsafe // Scourge Invasion public: void SetScourgeInvasionState(SIState state); void StartScourgeInvasion(bool sendMail); void StopScourgeInvasion(); [[nodiscard]] uint32 GetSIRemaining(SIRemaining remaining) const; [[nodiscard]] uint32 GetSIRemainingByZone(uint32 zoneId) const; void SetSIRemaining(SIRemaining remaining, uint32 value); TimePoint GetSITimer(SITimers timer); void SetSITimer(SITimers timer, TimePoint timePoint); uint32 GetBattlesWon(); void AddBattlesWon(int32 count); uint32 GetLastAttackZone(); void SetLastAttackZone(uint32 zoneId); void BroadcastSIWorldstates(); void HandleDefendedZones(); std::string GetScourgeInvasionPrintout(); void StartZoneEvent(SIZoneIds eventId); void StartNewInvasionIfTime(uint32 attackTimeVar, uint32 zoneId); void StartNewCityAttackIfTime(uint32 attackTimeVar, uint32 zoneId); void StartNewInvasion(uint32 zoneId); void StartNewCityAttack(uint32 zoneId); bool ResumeInvasion(ScourgeInvasionData::InvasionZone& zone); bool SummonMouth(Map* map, ScourgeInvasionData::InvasionZone& zone, Position position, bool newInvasion); bool SummonPallid(Map* map, ScourgeInvasionData::CityAttack& zone, const Position& position, uint32 spawnLoc); void HandleActiveZone(uint32 attackTimeVar, uint32 zoneId, uint32 remainingVar, TimePoint now); Map* GetMap(uint32 mapId, Position const& invZone); bool IsActiveZone(uint32 zoneId); uint32 GetActiveZones(); uint32 GetTimerIdForZone(uint32 zoneId); void SetPallidGuid(uint32 zoneId, ObjectGuid guid); void SetMouthGuid(uint32 zoneId, ObjectGuid guid); void AddPendingInvasion(uint32 zoneId); void RemovePendingInvasion(uint32 zoneId); void AddPendingPallid(uint32 zoneId); void RemovePendingPallid(uint32 zoneId); void OnEnable(ScourgeInvasionData::InvasionZone& zone); void OnDisable(ScourgeInvasionData::InvasionZone& zone); void OnDisable(ScourgeInvasionData::CityAttack& zone); private: void SendScourgeInvasionMail(); ScourgeInvasionData m_siData; }; #define sWorldState WorldState::instance() #endif