From 50228fc9daa7b9bcafbd4dfc5d94b79a58bdd2d6 Mon Sep 17 00:00:00 2001 From: Rat Date: Sat, 1 Nov 2014 19:36:05 +0100 Subject: [PATCH] Core/Phases: - Implemented CONDITION_SOURCE_TYPE_PHASE (26) - Implemented phase_area DB table to link phases to specific areas using conditions - updated logic to correctly handle phases gained from auras and areas (they should not remove each other's phases) --- sql/updates/world/2014_11_01_00_world.sql | 7 ++ src/server/game/Conditions/ConditionMgr.cpp | 1 + src/server/game/Conditions/ConditionMgr.h | 3 +- .../game/Entities/GameObject/GameObject.cpp | 6 +- .../game/Entities/GameObject/GameObject.h | 2 +- src/server/game/Entities/Object/Object.cpp | 110 +++++++++++++++++- src/server/game/Entities/Object/Object.h | 4 +- src/server/game/Entities/Player/Player.cpp | 2 + src/server/game/Entities/Unit/Unit.cpp | 8 +- src/server/game/Entities/Unit/Unit.h | 2 +- src/server/game/Globals/ObjectMgr.cpp | 31 +++++ src/server/game/Globals/ObjectMgr.h | 5 + src/server/game/World/World.cpp | 3 + 13 files changed, 169 insertions(+), 15 deletions(-) create mode 100644 sql/updates/world/2014_11_01_00_world.sql diff --git a/sql/updates/world/2014_11_01_00_world.sql b/sql/updates/world/2014_11_01_00_world.sql new file mode 100644 index 0000000000..2556589377 --- /dev/null +++ b/sql/updates/world/2014_11_01_00_world.sql @@ -0,0 +1,7 @@ +DROP TABLE IF EXISTS `phase_area`; +CREATE TABLE `phase_area` ( + `AreaId` int(10) unsigned NOT NULL, + `PhaseId` int(10) unsigned NOT NULL, + `Comment` varchar(255) DEFAULT NULL, + PRIMARY KEY (`AreaId`,`PhaseId`) +); diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 8ec1974459..dcb7e3a56b 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1535,6 +1535,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) case CONDITION_SOURCE_TYPE_SMART_EVENT: case CONDITION_SOURCE_TYPE_NONE: case CONDITION_SOURCE_TYPE_TERRAIN_SWAP: + case CONDITION_SOURCE_TYPE_PHASE: default: break; } diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h index e2d317102a..ed2c3a618b 100644 --- a/src/server/game/Conditions/ConditionMgr.h +++ b/src/server/game/Conditions/ConditionMgr.h @@ -130,7 +130,8 @@ enum ConditionSourceType CONDITION_SOURCE_TYPE_NPC_VENDOR = 23, CONDITION_SOURCE_TYPE_SPELL_PROC = 24, CONDITION_SOURCE_TYPE_TERRAIN_SWAP = 25, - CONDITION_SOURCE_TYPE_MAX = 26 // MAX + CONDITION_SOURCE_TYPE_PHASE = 26, + CONDITION_SOURCE_TYPE_MAX = 27 // MAX }; enum RelationType diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 0ed4f0d21b..6132ac7cad 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -2150,11 +2150,13 @@ void GameObject::SetDisplayId(uint32 displayid) UpdateModel(); } -void GameObject::SetInPhase(uint32 id, bool update, bool apply) +bool GameObject::SetInPhase(uint32 id, bool update, bool apply) { - WorldObject::SetInPhase(id, update, apply); + bool res = WorldObject::SetInPhase(id, update, apply); if (m_model && m_model->isEnabled()) EnableCollision(true); + + return res; } void GameObject::EnableCollision(bool enable) diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index bb6a862ad4..9c29ab31f3 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -739,7 +739,7 @@ class GameObject : public WorldObject, public GridObject, public Map void SetGoAnimProgress(uint8 animprogress) { SetByteValue(GAMEOBJECT_BYTES_1, 3, animprogress); } static void SetGoArtKit(uint8 artkit, GameObject* go, uint32 lowguid = 0); - void SetInPhase(uint32 id, bool update, bool apply); + bool SetInPhase(uint32 id, bool update, bool apply); void EnableCollision(bool enable); void Use(Unit* user); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index ef3e93963d..608e0df67f 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -2718,19 +2718,119 @@ void WorldObject::SetPhaseMask(uint32 newPhaseMask, bool update) UpdateObjectVisibility(); } -void WorldObject::SetInPhase(uint32 id, bool update, bool apply) +bool WorldObject::HasInPhaseList(uint32 phase) +{ + return _phases.find(phase) != _phases.end(); +} + +// Updates Area based phases, does not remove phases from auras +// Phases from gm commands are not taken into calculations, they can be lost!! +void WorldObject::UpdateAreaPhase() +{ + bool updateNeeded = false; + PhaseInfo phases = sObjectMgr->GetAreaPhases(); + for (PhaseInfo::const_iterator itr = phases.begin(); itr != phases.end(); ++itr) + { + uint32 areaId = itr->first; + for (uint32 phaseId : itr->second) + { + if (areaId == GetAreaId()) + { + ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_PHASE, phaseId); + if (sConditionMgr->IsObjectMeetToConditions(this, conditions)) + { + // add new phase if condition passed, true if it wasnt added before + bool up = SetInPhase(phaseId, false, true); + if (!updateNeeded && up) + updateNeeded = true; + } + else + { + // condition failed, remove phase, true if there was something removed + bool up = SetInPhase(phaseId, false, false); + if (!updateNeeded && up) + updateNeeded = true; + } + } + else + { + // not in area, remove phase, true if there was something removed + bool up = SetInPhase(phaseId, false, false); + if (!updateNeeded && up) + updateNeeded = true; + } + } + } + + // do not remove a phase if it would be removed by an area but we have the same phase from an aura + if (Unit* unit = ToUnit()) + { + Unit::AuraEffectList const& auraPhaseList = unit->GetAuraEffectsByType(SPELL_AURA_PHASE); + for (Unit::AuraEffectList::const_iterator itr = auraPhaseList.begin(); itr != auraPhaseList.end(); ++itr) + { + uint32 phase = uint32((*itr)->GetMiscValueB()); + bool up = SetInPhase(phase, false, true); + if (!updateNeeded && up) + updateNeeded = true; + } + Unit::AuraEffectList const& auraPhaseGroupList = unit->GetAuraEffectsByType(SPELL_AURA_PHASE_GROUP); + for (Unit::AuraEffectList::const_iterator itr = auraPhaseGroupList.begin(); itr != auraPhaseGroupList.end(); ++itr) + { + bool up = false; + uint32 phaseGroup = uint32((*itr)->GetMiscValueB()); + std::set const& phases = GetPhasesForGroup(phaseGroup); + for (uint32 phase : phases) + up = SetInPhase(phase, false, true); + if (!updateNeeded && up) + updateNeeded = true; + } + } + + // only update visibility and send packets if there was a change in the phase list + + if (updateNeeded && GetTypeId() == TYPEID_PLAYER && IsInWorld()) + ToPlayer()->GetSession()->SendSetPhaseShift(GetPhases(), GetTerrainSwaps(), GetWorldMapAreaSwaps()); + + // only update visibilty once, to prevent objects appearing for a moment while adding in multiple phases + if (updateNeeded && IsInWorld()) + UpdateObjectVisibility(); +} + +bool WorldObject::SetInPhase(uint32 id, bool update, bool apply) { if (id) { if (apply) + { + if (HasInPhaseList(id)) // do not run the updates if we are already in this phase + return false; _phases.insert(id); + } else + { + for (uint32 phaseId : sObjectMgr->GetPhasesForArea(GetAreaId())) + { + if (id == phaseId) + { + ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_PHASE, phaseId); + if (sConditionMgr->IsObjectMeetToConditions(this, conditions)) + { + // if area phase passes the condition we should not remove it (ie: if remove called from aura remove) + // this however breaks the .mod phase command, you wont be able to remove any area based phases with it + return false; + } + } + } + if (!HasInPhaseList(id)) // do not run the updates if we are not in this phase + return false; _phases.erase(id); + } } RebuildTerrainSwaps(); if (update && IsInWorld()) UpdateObjectVisibility(); + return true; } void WorldObject::CopyPhaseFrom(WorldObject* obj, bool update) @@ -2757,14 +2857,10 @@ void WorldObject::ClearPhases(bool update) bool WorldObject::IsInPhase(WorldObject const* obj) const { - // if object has no phasing it should be always visible - if (obj->GetPhases().empty()) - return true; - + // PhaseId 169 is the default fallback phase if (_phases.empty() && obj->GetPhases().empty()) return true; - // PhaseId 169 is the default fallback phase in dbc if (_phases.empty() && obj->IsInPhase(169)) return true; @@ -2960,6 +3056,8 @@ void WorldObject::RebuildTerrainSwaps() if (sConditionMgr->IsObjectMeetToConditions(this, conditions)) _terrainSwaps.insert(swap); } + + // online players have a game client with world map display if (GetTypeId() == TYPEID_PLAYER) RebuildWorldMapAreaSwaps(); } diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index f245f3e0e9..0d52ce7cdf 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -592,11 +592,13 @@ class WorldObject : public Object, public WorldLocation uint32 GetInstanceId() const { return m_InstanceId; } virtual void SetPhaseMask(uint32 newPhaseMask, bool update); - virtual void SetInPhase(uint32 id, bool update, bool apply); + virtual bool SetInPhase(uint32 id, bool update, bool apply); void CopyPhaseFrom(WorldObject* obj, bool update = false); + void UpdateAreaPhase(); void ClearPhases(bool update = false); void RebuildTerrainSwaps(); void RebuildWorldMapAreaSwaps(); + bool HasInPhaseList(uint32 phase); uint32 GetPhaseMask() const { return m_phaseMask; } bool IsInPhase(uint32 phase) const { return _phases.find(phase) != _phases.end(); } bool IsInPhase(WorldObject const* obj) const; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 274ba31cbe..87f9baf873 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -7883,6 +7883,8 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) UpdateLocalChannels(newZone); UpdateZoneDependentAuras(newZone); + + UpdateAreaPhase(); } //If players are too far away from the duel flag... they lose the duel diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index ecf14089b9..83b62f8e62 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -14734,12 +14734,12 @@ float Unit::MeleeSpellMissChance(const Unit* victim, WeaponAttackType attType, u return missChance; } -void Unit::SetInPhase(uint32 id, bool update, bool apply) +bool Unit::SetInPhase(uint32 id, bool update, bool apply) { - WorldObject::SetInPhase(id, update, apply); + bool res = WorldObject::SetInPhase(id, update, apply); if (!IsInWorld()) - return; + return res; if (GetTypeId() == TYPEID_UNIT || (!ToPlayer()->IsGameMaster() && !ToPlayer()->GetSession()->PlayerLogout())) { @@ -14782,6 +14782,8 @@ void Unit::SetInPhase(uint32 id, bool update, bool apply) summon->SetInPhase(id, true, apply); RemoveNotOwnSingleTargetAuras(0, true); + + return res; } void Unit::UpdateObjectVisibility(bool forced) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 4d6bb4cd6f..25ff338460 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1936,7 +1936,7 @@ class Unit : public WorldObject void SetVisible(bool x); // common function for visibility checks for player/creatures with detection code - void SetInPhase(uint32 id, bool update, bool apply); + bool SetInPhase(uint32 id, bool update, bool apply); void UpdateObjectVisibility(bool forced = true) override; SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY]; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 3f794e516f..38f48ff937 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -9382,6 +9382,37 @@ void ObjectMgr::LoadTerrainWorldMaps() TC_LOG_INFO("server.loading", ">> Loaded %u terrain world maps in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } +void ObjectMgr::LoadAreaPhases() +{ + _phases.clear(); + + uint32 oldMSTime = getMSTime(); + + // 0 1 + QueryResult result = WorldDatabase.Query("SELECT AreaId, PhaseId FROM `phase_area`"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 phase areas. DB table `phase_area` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + + uint32 area = fields[0].GetUInt32(); + uint32 phase = fields[1].GetUInt32(); + + _phases[area].push_back(phase); + + ++count; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u phase areas in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); +} + GameObjectTemplate const* ObjectMgr::GetGameObjectTemplate(uint32 entry) { GameObjectTemplateContainer::const_iterator itr = _gameObjectTemplateStore.find(entry); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 8de6896e01..4694e9e635 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -687,6 +687,7 @@ struct HotfixInfo typedef std::vector HotfixData; typedef std::unordered_map> TerrainPhaseInfo; +typedef std::unordered_map> PhaseInfo; class PlayerDumpReader; @@ -1031,6 +1032,7 @@ class ObjectMgr void LoadTerrainPhaseInfo(); void LoadTerrainSwapDefaults(); void LoadTerrainWorldMaps(); + void LoadAreaPhases(); std::string GeneratePetName(uint32 entry); uint32 GetBaseXP(uint8 level); @@ -1291,6 +1293,8 @@ class ObjectMgr std::list& GetDefaultTerrainSwaps(uint32 mapid) { return _terrainMapDefaultStore[mapid]; } std::list& GetTerrainWorldMaps(uint32 terrainId) { return _terrainWorldMapStore[terrainId]; } TerrainPhaseInfo& GetDefaultTerrainSwapStore() { return _terrainMapDefaultStore; } + std::list& GetPhasesForArea(uint32 area) { return _phases[area]; } + PhaseInfo& GetAreaPhases() { return _phases; } // for wintergrasp only GraveYardContainer GraveYardStore; @@ -1410,6 +1414,7 @@ class ObjectMgr TerrainPhaseInfo _terrainPhaseInfoStore; TerrainPhaseInfo _terrainMapDefaultStore; TerrainPhaseInfo _terrainWorldMapStore; + PhaseInfo _phases; private: void LoadScripts(ScriptsType type); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index d0dbe6a87a..cd3edfb1c8 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1739,6 +1739,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Terrain World Map definitions..."); sObjectMgr->LoadTerrainWorldMaps(); + TC_LOG_INFO("server.loading", "Loading Phase Area definitions..."); + sObjectMgr->LoadAreaPhases(); + TC_LOG_INFO("server.loading", "Loading Conditions..."); sConditionMgr->LoadConditions();