aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Phasing
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2018-01-28 11:37:20 +0100
committerShauren <shauren.trinity@gmail.com>2018-03-25 19:28:36 +0300
commit91be2332e249147ce3169c46a7da77f0f8c2211d (patch)
treeeab9e0ab76ae523f7a843fe1c2300eba17f53af2 /src/server/game/Phasing
parent1e62b53c66829dc9820117e17bfb44cb2652f814 (diff)
Core/Entities: Phasing rewrite
* Optimized phase visibility checking * Handle all phase flags Closes #16758 Closes #21119
Diffstat (limited to 'src/server/game/Phasing')
-rw-r--r--src/server/game/Phasing/PhaseShift.cpp178
-rw-r--r--src/server/game/Phasing/PhaseShift.h122
-rw-r--r--src/server/game/Phasing/PhasingHandler.cpp545
-rw-r--r--src/server/game/Phasing/PhasingHandler.h72
4 files changed, 917 insertions, 0 deletions
diff --git a/src/server/game/Phasing/PhaseShift.cpp b/src/server/game/Phasing/PhaseShift.cpp
new file mode 100644
index 00000000000..b2885ae15bb
--- /dev/null
+++ b/src/server/game/Phasing/PhaseShift.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "PhaseShift.h"
+#include "Containers.h"
+
+bool PhaseShift::AddPhase(uint32 phaseId, PhaseFlags flags, std::vector<Condition*> const* areaConditions, int32 references /*= 1*/)
+{
+ auto insertResult = Phases.emplace(phaseId, flags, nullptr);
+ ModifyPhasesReferences(insertResult.first, references);
+ if (areaConditions)
+ insertResult.first->AreaConditions = areaConditions;
+
+ return insertResult.second;
+}
+
+PhaseShift::EraseResult<PhaseShift::PhaseContainer> PhaseShift::RemovePhase(uint32 phaseId)
+{
+ auto itr = Phases.find(PhaseRef(phaseId, PhaseFlags::None, nullptr));
+ if (itr != Phases.end())
+ {
+ ModifyPhasesReferences(itr, -1);
+ if (!itr->References)
+ return { Phases.erase(itr), true };
+ return { itr, false };
+ }
+ return { Phases.end(), false };
+}
+
+bool PhaseShift::AddVisibleMapId(uint32 visibleMapId, TerrainSwapInfo const* visibleMapInfo, int32 references /*= 1*/)
+{
+ auto insertResult = VisibleMapIds.emplace(visibleMapId, VisibleMapIdRef{ 0, visibleMapInfo });
+ insertResult.first->second.References += references;
+ return insertResult.second;
+}
+
+PhaseShift::EraseResult<PhaseShift::VisibleMapIdContainer> PhaseShift::RemoveVisibleMapId(uint32 visibleMapId)
+{
+ auto itr = VisibleMapIds.find(visibleMapId);
+ if (itr != VisibleMapIds.end())
+ {
+ if (!--itr->second.References)
+ return { VisibleMapIds.erase(itr), true };
+ return { itr, false };
+ }
+ return { VisibleMapIds.end(), false };
+}
+
+bool PhaseShift::AddUiWorldMapAreaIdSwap(uint32 uiWorldMapAreaId, int32 references /*= 1*/)
+{
+ auto insertResult = UiWorldMapAreaIdSwaps.emplace(uiWorldMapAreaId, UiWorldMapAreaIdSwapRef{ 0 });
+ insertResult.first->second.References += references;
+ return insertResult.second;
+}
+
+PhaseShift::EraseResult<PhaseShift::UiWorldMapAreaIdSwapContainer> PhaseShift::RemoveUiWorldMapAreaIdSwap(uint32 uiWorldMapAreaId)
+{
+ auto itr = UiWorldMapAreaIdSwaps.find(uiWorldMapAreaId);
+ if (itr != UiWorldMapAreaIdSwaps.end())
+ {
+ if (!--itr->second.References)
+ return { UiWorldMapAreaIdSwaps.erase(itr), true };
+ return { itr, false };
+ }
+ return { UiWorldMapAreaIdSwaps.end(), false };
+}
+
+void PhaseShift::Clear()
+{
+ ClearPhases();
+ PersonalGuid.Clear();
+ VisibleMapIds.clear();
+ UiWorldMapAreaIdSwaps.clear();
+}
+
+void PhaseShift::ClearPhases()
+{
+ Flags &= EnumClassFlag<PhaseShiftFlags>(PhaseShiftFlags::AlwaysVisible) | PhaseShiftFlags::Inverse;
+ Phases.clear();
+ NonCosmeticReferences = 0;
+ CosmeticReferences = 0;
+ DefaultReferences = 0;
+ UpdateUnphasedFlag();
+}
+
+bool PhaseShift::CanSee(PhaseShift const& other) const
+{
+ if (Flags.HasFlag(PhaseShiftFlags::Unphased) && other.Flags.HasFlag(PhaseShiftFlags::Unphased))
+ return true;
+ if (Flags.HasFlag(PhaseShiftFlags::AlwaysVisible) || other.Flags.HasFlag(PhaseShiftFlags::AlwaysVisible))
+ return true;
+ if (Flags.HasFlag(PhaseShiftFlags::Inverse) && other.Flags.HasFlag(PhaseShiftFlags::Inverse))
+ return true;
+
+ PhaseFlags excludePhasesWithFlag = PhaseFlags::None;
+ if (Flags.HasFlag(PhaseShiftFlags::NoCosmetic) && other.Flags.HasFlag(PhaseShiftFlags::NoCosmetic))
+ excludePhasesWithFlag = PhaseFlags::Cosmetic;
+
+ if (!Flags.HasFlag(PhaseShiftFlags::Inverse) && !other.Flags.HasFlag(PhaseShiftFlags::Inverse))
+ {
+ ObjectGuid ownerGuid = PersonalGuid;
+ ObjectGuid otherPersonalGuid = other.PersonalGuid;
+ return Trinity::Containers::Intersects(Phases.begin(), Phases.end(), other.Phases.begin(), other.Phases.end(),
+ [&ownerGuid, &otherPersonalGuid, excludePhasesWithFlag](PhaseRef const& myPhase, PhaseRef const& /*otherPhase*/)
+ {
+ return !myPhase.Flags.HasFlag(excludePhasesWithFlag) && (!myPhase.Flags.HasFlag(PhaseFlags::Personal) || ownerGuid == otherPersonalGuid);
+ });
+ }
+
+ auto checkInversePhaseShift = [excludePhasesWithFlag](PhaseShift const& phaseShift, PhaseShift const& excludedPhaseShift)
+ {
+ if (phaseShift.Flags.HasFlag(PhaseShiftFlags::Unphased) && !excludedPhaseShift.Flags.HasFlag(PhaseShiftFlags::InverseUnphased))
+ return true;
+
+ for (auto itr = phaseShift.Phases.begin(); itr != phaseShift.Phases.end(); ++itr)
+ {
+ if (itr->Flags.HasFlag(excludePhasesWithFlag))
+ continue;
+
+ auto itr2 = std::find(excludedPhaseShift.Phases.begin(), excludedPhaseShift.Phases.end(), *itr);
+ if (itr2 == excludedPhaseShift.Phases.end() || itr2->Flags.HasFlag(excludePhasesWithFlag))
+ return true;
+ }
+
+ return false;
+ };
+
+ if (other.Flags.HasFlag(PhaseShiftFlags::Inverse))
+ return checkInversePhaseShift(*this, other);
+
+ return checkInversePhaseShift(other, *this);
+}
+
+void PhaseShift::ModifyPhasesReferences(PhaseContainer::iterator itr, int32 references)
+{
+ itr->References += references;
+
+ if (!IsDbPhaseShift)
+ {
+ if (itr->Flags.HasFlag(PhaseFlags::Cosmetic))
+ CosmeticReferences += references;
+ else if (itr->Id != DEFAULT_PHASE)
+ NonCosmeticReferences += references;
+ else
+ DefaultReferences += references;
+
+ if (CosmeticReferences)
+ Flags |= PhaseShiftFlags::NoCosmetic;
+ else
+ Flags &= ~EnumClassFlag<PhaseShiftFlags>(PhaseShiftFlags::NoCosmetic);
+
+ UpdateUnphasedFlag();
+ }
+}
+
+void PhaseShift::UpdateUnphasedFlag()
+{
+ EnumClassFlag<PhaseShiftFlags> unphasedFlag = !Flags.HasFlag(PhaseShiftFlags::Inverse) ? PhaseShiftFlags::Unphased : PhaseShiftFlags::InverseUnphased;
+ Flags &= ~EnumClassFlag<PhaseShiftFlags>(!Flags.HasFlag(PhaseShiftFlags::Inverse) ? PhaseShiftFlags::InverseUnphased : PhaseShiftFlags::Unphased);
+ if (NonCosmeticReferences && !DefaultReferences)
+ Flags &= ~unphasedFlag;
+ else
+ Flags |= unphasedFlag;
+}
diff --git a/src/server/game/Phasing/PhaseShift.h b/src/server/game/Phasing/PhaseShift.h
new file mode 100644
index 00000000000..8755868ae9d
--- /dev/null
+++ b/src/server/game/Phasing/PhaseShift.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PhaseShift_h__
+#define PhaseShift_h__
+
+#include "Define.h"
+#include "EnumClassFlag.h"
+#include "ObjectGuid.h"
+#include <boost/container/flat_set.hpp>
+#include <map>
+
+class PhasingHandler;
+struct Condition;
+struct TerrainSwapInfo;
+
+#define DEFAULT_PHASE 169
+
+enum class PhaseShiftFlags : uint32
+{
+ None = 0x00,
+ AlwaysVisible = 0x01, // Ignores all phasing, can see everything and be seen by everything
+ Inverse = 0x02, // By default having at least one shared phase for two objects means they can see each other
+ // this flag makes objects see each other if they have at least one non-shared phase
+ InverseUnphased = 0x04,
+ Unphased = 0x08,
+ NoCosmetic = 0x10 // This flag ignores shared cosmetic phases (two players that both have shared cosmetic phase but no other phase cannot see each other)
+};
+
+enum class PhaseFlags : uint16
+{
+ None = 0x0,
+ Cosmetic = 0x1,
+ Personal = 0x2
+};
+
+class TC_GAME_API PhaseShift
+{
+public:
+ struct PhaseRef
+ {
+ PhaseRef(uint32 id, PhaseFlags flags, std::vector<Condition*> const* conditions)
+ : Id(id), Flags(flags), References(0), AreaConditions(conditions) { }
+
+ uint16 Id;
+ EnumClassFlag<PhaseFlags> Flags;
+ int32 References;
+ std::vector<Condition*> const* AreaConditions;
+ bool operator<(PhaseRef const& right) const { return Id < right.Id; }
+ bool operator==(PhaseRef const& right) const { return Id == right.Id; }
+ };
+ struct VisibleMapIdRef
+ {
+ int32 References = 0;
+ TerrainSwapInfo const* VisibleMapInfo = nullptr;
+ };
+ struct UiWorldMapAreaIdSwapRef
+ {
+ int32 References = 0;
+ };
+ template<typename Container>
+ struct EraseResult
+ {
+ typename Container::iterator Iterator;
+ bool Erased;
+ };
+ typedef boost::container::flat_set<PhaseRef> PhaseContainer;
+ typedef std::map<uint32, VisibleMapIdRef> VisibleMapIdContainer;
+ typedef std::map<uint32, UiWorldMapAreaIdSwapRef> UiWorldMapAreaIdSwapContainer;
+
+ bool AddPhase(uint32 phaseId, PhaseFlags flags, std::vector<Condition*> const* areaConditions, int32 references = 1);
+ EraseResult<PhaseContainer> RemovePhase(uint32 phaseId);
+ bool HasPhase(uint32 phaseId) const { return Phases.find(PhaseRef(phaseId, PhaseFlags::None, nullptr)) != Phases.end(); }
+ PhaseContainer const& GetPhases() const { return Phases; }
+
+ bool AddVisibleMapId(uint32 visibleMapId, TerrainSwapInfo const* visibleMapInfo, int32 references = 1);
+ EraseResult<VisibleMapIdContainer> RemoveVisibleMapId(uint32 visibleMapId);
+ bool HasVisibleMapId(uint32 visibleMapId) const { return VisibleMapIds.find(visibleMapId) != VisibleMapIds.end(); }
+ VisibleMapIdContainer const& GetVisibleMapIds() const { return VisibleMapIds; }
+
+ bool AddUiWorldMapAreaIdSwap(uint32 uiWorldMapAreaId, int32 references = 1);
+ EraseResult<UiWorldMapAreaIdSwapContainer> RemoveUiWorldMapAreaIdSwap(uint32 uiWorldMapAreaId);
+ bool HasUiWorldMapAreaIdSwap(uint32 uiWorldMapAreaId) const { return UiWorldMapAreaIdSwaps.find(uiWorldMapAreaId) != UiWorldMapAreaIdSwaps.end(); }
+ UiWorldMapAreaIdSwapContainer const& GetUiWorldMapAreaIdSwaps() const { return UiWorldMapAreaIdSwaps; }
+
+ void Clear();
+ void ClearPhases();
+
+ bool CanSee(PhaseShift const& other) const;
+
+protected:
+ friend class PhasingHandler;
+
+ EnumClassFlag<PhaseShiftFlags> Flags = PhaseShiftFlags::Unphased;
+ ObjectGuid PersonalGuid;
+ PhaseContainer Phases;
+ VisibleMapIdContainer VisibleMapIds;
+ UiWorldMapAreaIdSwapContainer UiWorldMapAreaIdSwaps;
+
+ void ModifyPhasesReferences(PhaseContainer::iterator itr, int32 references);
+ void UpdateUnphasedFlag();
+ int32 NonCosmeticReferences = 0;
+ int32 CosmeticReferences = 0;
+ int32 DefaultReferences = 0;
+ bool IsDbPhaseShift = false;
+};
+
+#endif // PhaseShift_h__
diff --git a/src/server/game/Phasing/PhasingHandler.cpp b/src/server/game/Phasing/PhasingHandler.cpp
new file mode 100644
index 00000000000..2f0fd7d870e
--- /dev/null
+++ b/src/server/game/Phasing/PhasingHandler.cpp
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "PhasingHandler.h"
+#include "Chat.h"
+#include "ConditionMgr.h"
+#include "Creature.h"
+#include "DB2Stores.h"
+#include "Language.h"
+#include "Map.h"
+#include "MiscPackets.h"
+#include "ObjectMgr.h"
+#include "PartyPackets.h"
+#include "PhaseShift.h"
+#include "Player.h"
+#include "SpellAuraEffects.h"
+
+namespace
+{
+inline PhaseFlags GetPhaseFlags(uint32 phaseId)
+{
+ if (PhaseEntry const* phase = sPhaseStore.LookupEntry(phaseId))
+ {
+ if (phase->Flags & PHASE_FLAG_COSMETIC)
+ return PhaseFlags::Cosmetic;
+
+ if (phase->Flags & PHASE_FLAG_PERSONAL)
+ return PhaseFlags::Personal;
+ }
+
+ return PhaseFlags::None;
+}
+
+template<typename Func>
+inline void ForAllControlled(Unit* unit, Func&& func)
+{
+ for (Unit* controlled : unit->m_Controlled)
+ if (controlled->GetTypeId() != TYPEID_PLAYER)
+ func(controlled);
+
+ for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
+ if (!unit->m_SummonSlot[i].IsEmpty())
+ if (Creature* summon = unit->GetMap()->GetCreature(unit->m_SummonSlot[i]))
+ func(summon);
+}
+}
+
+void PhasingHandler::AddPhase(WorldObject* object, uint32 phaseId, bool updateVisibility)
+{
+ bool changed = object->GetPhaseShift().AddPhase(phaseId, GetPhaseFlags(phaseId), nullptr);
+
+ if (Unit* unit = object->ToUnit())
+ {
+ unit->OnPhaseChange();
+ ForAllControlled(unit, [&](Unit* controlled)
+ {
+ AddPhase(controlled, phaseId, updateVisibility);
+ });
+ unit->RemoveNotOwnSingleTargetAuras(true);
+ }
+
+ UpdateVisibilityIfNeeded(object, updateVisibility, changed);
+}
+
+void PhasingHandler::RemovePhase(WorldObject* object, uint32 phaseId, bool updateVisibility)
+{
+ bool changed = object->GetPhaseShift().RemovePhase(phaseId).Erased;
+
+ if (Unit* unit = object->ToUnit())
+ {
+ unit->OnPhaseChange();
+ ForAllControlled(unit, [&](Unit* controlled)
+ {
+ RemovePhase(controlled, phaseId, updateVisibility);
+ });
+ unit->RemoveNotOwnSingleTargetAuras(true);
+ }
+
+ UpdateVisibilityIfNeeded(object, updateVisibility, changed);
+}
+
+void PhasingHandler::AddPhaseGroup(WorldObject* object, uint32 phaseGroupId, bool updateVisibility)
+{
+ std::vector<uint32> const* phasesInGroup = sDB2Manager.GetPhasesForGroup(phaseGroupId);
+ if (!phasesInGroup)
+ return;
+
+ bool changed = false;
+ for (uint32 phaseId : *phasesInGroup)
+ changed = object->GetPhaseShift().AddPhase(phaseId, GetPhaseFlags(phaseId), nullptr) || changed;
+
+ if (Unit* unit = object->ToUnit())
+ {
+ unit->OnPhaseChange();
+ ForAllControlled(unit, [&](Unit* controlled)
+ {
+ AddPhaseGroup(controlled, phaseGroupId, updateVisibility);
+ });
+ unit->RemoveNotOwnSingleTargetAuras(true);
+ }
+
+ UpdateVisibilityIfNeeded(object, updateVisibility, changed);
+}
+
+void PhasingHandler::RemovePhaseGroup(WorldObject* object, uint32 phaseGroupId, bool updateVisibility)
+{
+ std::vector<uint32> const* phasesInGroup = sDB2Manager.GetPhasesForGroup(phaseGroupId);
+ if (!phasesInGroup)
+ return;
+
+ bool changed = false;
+ for (uint32 phaseId : *phasesInGroup)
+ changed = object->GetPhaseShift().RemovePhase(phaseId).Erased || changed;
+
+ if (Unit* unit = object->ToUnit())
+ {
+ unit->OnPhaseChange();
+ ForAllControlled(unit, [&](Unit* controlled)
+ {
+ RemovePhaseGroup(controlled, phaseGroupId, updateVisibility);
+ });
+ unit->RemoveNotOwnSingleTargetAuras(true);
+ }
+
+ UpdateVisibilityIfNeeded(object, updateVisibility, changed);
+}
+
+void PhasingHandler::AddVisibleMapId(WorldObject* object, uint32 visibleMapId)
+{
+ TerrainSwapInfo const* terrainSwapInfo = sObjectMgr->GetTerrainSwapInfo(visibleMapId);
+ bool changed = object->GetPhaseShift().AddVisibleMapId(visibleMapId, terrainSwapInfo);
+
+ for (uint32 uiWorldMapAreaIDSwap : terrainSwapInfo->UiWorldMapAreaIDSwaps)
+ changed = object->GetPhaseShift().AddUiWorldMapAreaIdSwap(uiWorldMapAreaIDSwap) || changed;
+
+ if (Unit* unit = object->ToUnit())
+ {
+ ForAllControlled(unit, [&](Unit* controlled)
+ {
+ AddVisibleMapId(controlled, visibleMapId);
+ });
+ }
+
+ UpdateVisibilityIfNeeded(object, false, changed);
+}
+
+void PhasingHandler::RemoveVisibleMapId(WorldObject* object, uint32 visibleMapId)
+{
+ TerrainSwapInfo const* terrainSwapInfo = sObjectMgr->GetTerrainSwapInfo(visibleMapId);
+ bool changed = object->GetPhaseShift().RemoveVisibleMapId(visibleMapId).Erased;
+
+ for (uint32 uiWorldMapAreaIDSwap : terrainSwapInfo->UiWorldMapAreaIDSwaps)
+ changed = object->GetPhaseShift().RemoveUiWorldMapAreaIdSwap(uiWorldMapAreaIDSwap).Erased || changed;
+
+ if (Unit* unit = object->ToUnit())
+ {
+ ForAllControlled(unit, [&](Unit* controlled)
+ {
+ RemoveVisibleMapId(controlled, visibleMapId);
+ });
+ }
+
+ UpdateVisibilityIfNeeded(object, false, changed);
+}
+
+void PhasingHandler::ResetPhaseShift(WorldObject* object)
+{
+ object->GetPhaseShift().Clear();
+ object->GetSuppressedPhaseShift().Clear();
+}
+
+void PhasingHandler::InheritPhaseShift(WorldObject* target, WorldObject const* source)
+{
+ target->GetPhaseShift() = source->GetPhaseShift();
+ target->GetSuppressedPhaseShift() = source->GetSuppressedPhaseShift();
+}
+
+void PhasingHandler::OnMapChange(WorldObject* object)
+{
+ PhaseShift& phaseShift = object->GetPhaseShift();
+ PhaseShift& suppressedPhaseShift = object->GetSuppressedPhaseShift();
+ ConditionSourceInfo srcInfo = ConditionSourceInfo(object);
+
+ object->GetPhaseShift().VisibleMapIds.clear();
+ object->GetPhaseShift().UiWorldMapAreaIdSwaps.clear();
+ object->GetSuppressedPhaseShift().VisibleMapIds.clear();
+
+ if (std::vector<TerrainSwapInfo*> const* visibleMapIds = sObjectMgr->GetTerrainSwapsForMap(object->GetMapId()))
+ {
+ for (TerrainSwapInfo const* visibleMapInfo : *visibleMapIds)
+ {
+ if (sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_TERRAIN_SWAP, visibleMapInfo->Id, srcInfo))
+ {
+ phaseShift.AddVisibleMapId(visibleMapInfo->Id, visibleMapInfo);
+ for (uint32 uiWorldMapAreaIdSwap : visibleMapInfo->UiWorldMapAreaIDSwaps)
+ phaseShift.AddUiWorldMapAreaIdSwap(uiWorldMapAreaIdSwap);
+ }
+ else
+ suppressedPhaseShift.AddVisibleMapId(visibleMapInfo->Id, visibleMapInfo);
+ }
+ }
+
+ UpdateVisibilityIfNeeded(object, false, true);
+}
+
+void PhasingHandler::OnAreaChange(WorldObject* object)
+{
+ PhaseShift& phaseShift = object->GetPhaseShift();
+ PhaseShift& suppressedPhaseShift = object->GetSuppressedPhaseShift();
+ PhaseShift::PhaseContainer oldPhases = std::move(phaseShift.Phases); // for comparison
+ ConditionSourceInfo srcInfo = ConditionSourceInfo(object);
+
+ object->GetPhaseShift().ClearPhases();
+ object->GetSuppressedPhaseShift().ClearPhases();
+
+ uint32 areaId = object->GetAreaId();
+ AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId);
+ while (areaEntry)
+ {
+ if (std::vector<PhaseAreaInfo> const* newAreaPhases = sObjectMgr->GetPhasesForArea(areaEntry->ID))
+ {
+ for (PhaseAreaInfo const& phaseArea : *newAreaPhases)
+ {
+ if (phaseArea.SubAreaExclusions.find(areaId) != phaseArea.SubAreaExclusions.end())
+ continue;
+
+ uint32 phaseId = phaseArea.PhaseInfo->Id;
+ if (sConditionMgr->IsObjectMeetToConditions(srcInfo, phaseArea.Conditions))
+ phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), &phaseArea.Conditions);
+ else
+ suppressedPhaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), &phaseArea.Conditions);
+ }
+ }
+
+ areaEntry = sAreaTableStore.LookupEntry(areaEntry->ParentAreaID);
+ }
+
+ bool changed = phaseShift.Phases != oldPhases;
+ if (Unit* unit = object->ToUnit())
+ {
+ for (AuraEffect const* aurEff : unit->GetAuraEffectsByType(SPELL_AURA_PHASE))
+ {
+ uint32 phaseId = uint32(aurEff->GetMiscValueB());
+ changed = phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), nullptr) || changed;
+ }
+
+ for (AuraEffect const* aurEff : unit->GetAuraEffectsByType(SPELL_AURA_PHASE_GROUP))
+ if (std::vector<uint32> const* phasesInGroup = sDB2Manager.GetPhasesForGroup(uint32(aurEff->GetMiscValueB())))
+ for (uint32 phaseId : *phasesInGroup)
+ changed = phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), nullptr) || changed;
+
+ if (changed)
+ unit->OnPhaseChange();
+
+ ForAllControlled(unit, [&](Unit* controlled)
+ {
+ InheritPhaseShift(controlled, unit);
+ });
+
+ if (changed)
+ unit->RemoveNotOwnSingleTargetAuras(true);
+ }
+
+ UpdateVisibilityIfNeeded(object, true, changed);
+}
+
+void PhasingHandler::OnConditionChange(WorldObject* object)
+{
+ PhaseShift& phaseShift = object->GetPhaseShift();
+ PhaseShift& suppressedPhaseShift = object->GetSuppressedPhaseShift();
+ PhaseShift newSuppressions;
+ ConditionSourceInfo srcInfo = ConditionSourceInfo(object);
+ bool changed = false;
+
+ for (auto itr = phaseShift.Phases.begin(); itr != phaseShift.Phases.end();)
+ {
+ if (itr->AreaConditions && !sConditionMgr->IsObjectMeetToConditions(srcInfo, *itr->AreaConditions))
+ {
+ newSuppressions.AddPhase(itr->Id, itr->Flags, itr->AreaConditions, itr->References);
+ itr = phaseShift.Phases.erase(itr);
+ }
+ else
+ ++itr;
+ }
+
+ for (auto itr = suppressedPhaseShift.Phases.begin(); itr != suppressedPhaseShift.Phases.end();)
+ {
+ if (sConditionMgr->IsObjectMeetToConditions(srcInfo, *ASSERT_NOTNULL(itr->AreaConditions)))
+ {
+ changed = phaseShift.AddPhase(itr->Id, itr->Flags, itr->AreaConditions, itr->References) || changed;
+ itr = suppressedPhaseShift.Phases.erase(itr);
+ }
+ else
+ ++itr;
+ }
+
+ for (auto itr = phaseShift.VisibleMapIds.begin(); itr != phaseShift.VisibleMapIds.end();)
+ {
+ if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_TERRAIN_SWAP, itr->first, srcInfo))
+ {
+ newSuppressions.AddVisibleMapId(itr->first, itr->second.VisibleMapInfo, itr->second.References);
+ for (uint32 uiWorldMapAreaIdSwap : itr->second.VisibleMapInfo->UiWorldMapAreaIDSwaps)
+ changed = phaseShift.RemoveUiWorldMapAreaIdSwap(uiWorldMapAreaIdSwap).Erased || changed;
+
+ itr = phaseShift.VisibleMapIds.erase(itr);
+ }
+ else
+ ++itr;
+ }
+
+ for (auto itr = suppressedPhaseShift.VisibleMapIds.begin(); itr != suppressedPhaseShift.VisibleMapIds.end();)
+ {
+ if (sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_TERRAIN_SWAP, itr->first, srcInfo))
+ {
+ changed = phaseShift.AddVisibleMapId(itr->first, itr->second.VisibleMapInfo, itr->second.References) || changed;
+ for (uint32 uiWorldMapAreaIdSwap : itr->second.VisibleMapInfo->UiWorldMapAreaIDSwaps)
+ changed = phaseShift.AddUiWorldMapAreaIdSwap(uiWorldMapAreaIdSwap) || changed;
+
+ itr = suppressedPhaseShift.VisibleMapIds.erase(itr);
+ }
+ else
+ ++itr;
+ }
+
+ Unit* unit = object->ToUnit();
+ if (unit)
+ {
+ for (AuraEffect const* aurEff : unit->GetAuraEffectsByType(SPELL_AURA_PHASE))
+ {
+ uint32 phaseId = uint32(aurEff->GetMiscValueB());
+ auto eraseResult = newSuppressions.RemovePhase(phaseId);
+ // if condition was met previously there is nothing to erase
+ if (eraseResult.Iterator != newSuppressions.Phases.end() || eraseResult.Erased)
+ phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), nullptr);
+ }
+
+ for (AuraEffect const* aurEff : unit->GetAuraEffectsByType(SPELL_AURA_PHASE_GROUP))
+ {
+ if (std::vector<uint32> const* phasesInGroup = sDB2Manager.GetPhasesForGroup(uint32(aurEff->GetMiscValueB())))
+ {
+ for (uint32 phaseId : *phasesInGroup)
+ {
+ auto eraseResult = newSuppressions.RemovePhase(phaseId);
+ // if condition was met previously there is nothing to erase
+ if (eraseResult.Iterator != newSuppressions.Phases.end() || eraseResult.Erased)
+ phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), nullptr);
+ }
+ }
+ }
+ }
+
+ changed = changed || !newSuppressions.Phases.empty() || !newSuppressions.VisibleMapIds.empty();
+ for (auto itr = newSuppressions.Phases.begin(); itr != newSuppressions.Phases.end(); ++itr)
+ suppressedPhaseShift.AddPhase(itr->Id, itr->Flags, itr->AreaConditions, itr->References);
+
+ for (auto itr = newSuppressions.VisibleMapIds.begin(); itr != newSuppressions.VisibleMapIds.end(); ++itr)
+ suppressedPhaseShift.AddVisibleMapId(itr->first, itr->second.VisibleMapInfo, itr->second.References);
+
+ if (unit)
+ {
+ if (changed)
+ unit->OnPhaseChange();
+
+ ForAllControlled(unit, [&](Unit* controlled)
+ {
+ InheritPhaseShift(controlled, unit);
+ });
+
+ if (changed)
+ unit->RemoveNotOwnSingleTargetAuras(true);
+ }
+
+ UpdateVisibilityIfNeeded(object, true, changed);
+}
+
+void PhasingHandler::SendToPlayer(Player const* player, PhaseShift const& phaseShift)
+{
+ WorldPackets::Misc::PhaseShiftChange phaseShiftChange;
+ phaseShiftChange.Client = player->GetGUID();
+ phaseShiftChange.Phaseshift.PhaseShiftFlags = phaseShift.Flags.AsUnderlyingType();
+ phaseShiftChange.Phaseshift.PersonalGUID = phaseShift.PersonalGuid;
+ phaseShiftChange.Phaseshift.Phases.reserve(phaseShift.Phases.size());
+ std::transform(phaseShift.Phases.begin(), phaseShift.Phases.end(), std::back_inserter(phaseShiftChange.Phaseshift.Phases),
+ [](PhaseShift::PhaseRef const& phase) -> WorldPackets::Misc::PhaseShiftDataPhase { return { phase.Flags.AsUnderlyingType(), phase.Id }; });
+ phaseShiftChange.VisibleMapIDs.reserve(phaseShift.VisibleMapIds.size());
+ std::transform(phaseShift.VisibleMapIds.begin(), phaseShift.VisibleMapIds.end(), std::back_inserter(phaseShiftChange.VisibleMapIDs),
+ [](PhaseShift::VisibleMapIdContainer::value_type const& visibleMapId) { return visibleMapId.first; });
+ phaseShiftChange.UiWorldMapAreaIDSwaps.reserve(phaseShift.UiWorldMapAreaIdSwaps.size());
+ std::transform(phaseShift.UiWorldMapAreaIdSwaps.begin(), phaseShift.UiWorldMapAreaIdSwaps.end(), std::back_inserter(phaseShiftChange.UiWorldMapAreaIDSwaps),
+ [](PhaseShift::UiWorldMapAreaIdSwapContainer::value_type const& uiWorldMapAreaIdSwap) { return uiWorldMapAreaIdSwap.first; });
+
+ player->SendDirectMessage(phaseShiftChange.Write());
+}
+
+void PhasingHandler::SendToPlayer(Player const* player)
+{
+ SendToPlayer(player, player->GetPhaseShift());
+}
+
+void PhasingHandler::FillPartyMemberPhase(WorldPackets::Party::PartyMemberPhaseStates* partyMemberPhases, PhaseShift const& phaseShift)
+{
+ partyMemberPhases->PhaseShiftFlags = phaseShift.Flags.AsUnderlyingType();
+ partyMemberPhases->PersonalGUID = phaseShift.PersonalGuid;
+ partyMemberPhases->List.reserve(phaseShift.Phases.size());
+ std::transform(phaseShift.Phases.begin(), phaseShift.Phases.end(), std::back_inserter(partyMemberPhases->List),
+ [](PhaseShift::PhaseRef const& phase) -> WorldPackets::Party::PartyMemberPhase { return { phase.Flags.AsUnderlyingType(), phase.Id }; });
+}
+
+void PhasingHandler::InitDbPhaseShift(PhaseShift& phaseShift, uint8 phaseUseFlags, uint16 phaseId, uint32 phaseGroupId)
+{
+ phaseShift.IsDbPhaseShift = true;
+
+ EnumClassFlag<PhaseShiftFlags> flags = PhaseShiftFlags::None;
+ if (phaseUseFlags & PHASE_USE_FLAGS_ALWAYS_VISIBLE)
+ flags = flags | PhaseShiftFlags::AlwaysVisible | PhaseShiftFlags::Unphased;
+ if (phaseUseFlags & PHASE_USE_FLAGS_INVERSE)
+ flags |= PhaseShiftFlags::Inverse;
+
+ if (phaseId)
+ phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), nullptr);
+ else if (std::vector<uint32> const* phasesInGroup = sDB2Manager.GetPhasesForGroup(phaseGroupId))
+ for (uint32 phaseInGroup : *phasesInGroup)
+ phaseShift.AddPhase(phaseInGroup, GetPhaseFlags(phaseInGroup), nullptr);
+
+ if (phaseShift.Phases.empty() || phaseShift.HasPhase(DEFAULT_PHASE))
+ {
+ if (flags.HasFlag(PhaseShiftFlags::Inverse))
+ flags |= PhaseShiftFlags::InverseUnphased;
+ else
+ flags |= PhaseShiftFlags::Unphased;
+ }
+
+ phaseShift.Flags = flags;
+}
+
+void PhasingHandler::InitDbVisibleMapId(PhaseShift& phaseShift, int32 visibleMapId)
+{
+ if (visibleMapId != -1)
+ phaseShift.AddVisibleMapId(visibleMapId, sObjectMgr->GetTerrainSwapInfo(visibleMapId));
+}
+
+bool PhasingHandler::InDbPhaseShift(WorldObject const* object, uint8 phaseUseFlags, uint16 phaseId, uint32 phaseGroupId)
+{
+ PhaseShift phaseShift;
+ InitDbPhaseShift(phaseShift, phaseUseFlags, phaseId, phaseGroupId);
+ return object->GetPhaseShift().CanSee(phaseShift);
+}
+
+void PhasingHandler::SetAlwaysVisible(PhaseShift& phaseShift, bool apply)
+{
+ if (apply)
+ phaseShift.Flags |= PhaseShiftFlags::AlwaysVisible;
+ else
+ phaseShift.Flags &= ~EnumClassFlag<PhaseShiftFlags>(PhaseShiftFlags::AlwaysVisible);
+}
+
+void PhasingHandler::SetInversed(PhaseShift& phaseShift, bool apply)
+{
+ if (apply)
+ phaseShift.Flags |= PhaseShiftFlags::Inverse;
+ else
+ phaseShift.Flags &= ~EnumClassFlag<PhaseShiftFlags>(PhaseShiftFlags::Inverse);
+
+ phaseShift.UpdateUnphasedFlag();
+}
+
+void PhasingHandler::PrintToChat(ChatHandler* chat, PhaseShift const& phaseShift)
+{
+ chat->PSendSysMessage(LANG_PHASESHIFT_STATUS, phaseShift.Flags.AsUnderlyingType(), phaseShift.PersonalGuid.ToString().c_str());
+ if (!phaseShift.Phases.empty())
+ {
+ std::ostringstream phases;
+ std::string cosmetic = sObjectMgr->GetTrinityString(LANG_PHASE_FLAG_COSMETIC, chat->GetSessionDbLocaleIndex());
+ std::string personal = sObjectMgr->GetTrinityString(LANG_PHASE_FLAG_PERSONAL, chat->GetSessionDbLocaleIndex());
+ for (PhaseShift::PhaseRef const& phase : phaseShift.Phases)
+ {
+ phases << phase.Id;
+ if (phase.Flags.HasFlag(PhaseFlags::Cosmetic))
+ phases << ' ' << '(' << cosmetic << ')';
+ if (phase.Flags.HasFlag(PhaseFlags::Personal))
+ phases << ' ' << '(' << personal << ')';
+ phases << ", ";
+ }
+
+ chat->PSendSysMessage(LANG_PHASESHIFT_PHASES, phases.str().c_str());
+ }
+
+ if (!phaseShift.VisibleMapIds.empty())
+ {
+ std::ostringstream visibleMapIds;
+ for (PhaseShift::VisibleMapIdContainer::value_type const& visibleMapId : phaseShift.VisibleMapIds)
+ visibleMapIds << visibleMapId.first << ',' << ' ';
+
+ chat->PSendSysMessage(LANG_PHASESHIFT_VISIBLE_MAP_IDS, visibleMapIds.str().c_str());
+ }
+
+ if (!phaseShift.UiWorldMapAreaIdSwaps.empty())
+ {
+ std::ostringstream uiWorldMapAreaIdSwaps;
+ for (PhaseShift::UiWorldMapAreaIdSwapContainer::value_type const& uiWorldMapAreaIdSwap : phaseShift.UiWorldMapAreaIdSwaps)
+ uiWorldMapAreaIdSwaps << uiWorldMapAreaIdSwap.first << ',' << ' ';
+
+ chat->PSendSysMessage(LANG_PHASESHIFT_UI_WORLD_MAP_AREA_SWAPS, uiWorldMapAreaIdSwaps.str().c_str());
+ }
+}
+
+std::string PhasingHandler::FormatPhases(PhaseShift const& phaseShift)
+{
+ std::ostringstream phases;
+ for (PhaseShift::PhaseRef const& phase : phaseShift.Phases)
+ phases << phase.Id << ',';
+
+ return phases.str();
+}
+
+void PhasingHandler::UpdateVisibilityIfNeeded(WorldObject* object, bool updateVisibility, bool changed)
+{
+ if (changed && object->IsInWorld())
+ {
+ if (Player* player = object->ToPlayer())
+ SendToPlayer(player);
+
+ if (updateVisibility)
+ {
+ if (Player* player = object->ToPlayer())
+ player->GetMap()->SendUpdateTransportVisibility(player);
+
+ object->UpdateObjectVisibility();
+ }
+ }
+}
diff --git a/src/server/game/Phasing/PhasingHandler.h b/src/server/game/Phasing/PhasingHandler.h
new file mode 100644
index 00000000000..e759f2e68e1
--- /dev/null
+++ b/src/server/game/Phasing/PhasingHandler.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PhasingHandler_h__
+#define PhasingHandler_h__
+
+#include "Define.h"
+#include <string>
+
+class ChatHandler;
+class PhaseShift;
+class Player;
+class WorldObject;
+namespace WorldPackets
+{
+ namespace Party
+ {
+ struct PartyMemberPhaseStates;
+ }
+}
+
+class TC_GAME_API PhasingHandler
+{
+public:
+ static void AddPhase(WorldObject* object, uint32 phaseId, bool updateVisibility);
+ static void RemovePhase(WorldObject* object, uint32 phaseId, bool updateVisibility);
+ static void AddPhaseGroup(WorldObject* object, uint32 phaseGroupId, bool updateVisibility);
+ static void RemovePhaseGroup(WorldObject* object, uint32 phaseGroupId, bool updateVisibility);
+
+ static void AddVisibleMapId(WorldObject* object, uint32 visibleMapId);
+ static void RemoveVisibleMapId(WorldObject* object, uint32 visibleMapId);
+
+ static void ResetPhaseShift(WorldObject* object);
+ static void InheritPhaseShift(WorldObject* target, WorldObject const* source);
+
+ static void OnMapChange(WorldObject* object);
+ static void OnAreaChange(WorldObject* object);
+ static void OnConditionChange(WorldObject* object);
+
+ static void SendToPlayer(Player const* player, PhaseShift const& phaseShift);
+ static void SendToPlayer(Player const* player);
+ static void FillPartyMemberPhase(WorldPackets::Party::PartyMemberPhaseStates* partyMemberPhases, PhaseShift const& phaseShift);
+
+ static void InitDbPhaseShift(PhaseShift& phaseShift, uint8 phaseUseFlags, uint16 phaseId, uint32 phaseGroupId);
+ static void InitDbVisibleMapId(PhaseShift& phaseShift, int32 visibleMapId);
+ static bool InDbPhaseShift(WorldObject const* object, uint8 phaseUseFlags, uint16 phaseId, uint32 phaseGroupId);
+
+ static void SetAlwaysVisible(PhaseShift& phaseShift, bool apply);
+ static void SetInversed(PhaseShift& phaseShift, bool apply);
+
+ static void PrintToChat(ChatHandler* chat, PhaseShift const& phaseShift);
+ static std::string FormatPhases(PhaseShift const& phaseShift);
+
+private:
+ static void UpdateVisibilityIfNeeded(WorldObject* object, bool updateVisibility, bool changed);
+};
+
+#endif // PhasingHandler_h__