From f8d9d954d113ff161baf879014d9ed2f5180dd26 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Fri, 9 Jun 2017 13:20:37 +0200 Subject: [PATCH] Get zone/area IDs from vmap data in the liquid update * Add new method Map::getFullVMapDataForPosition to get area info and liquid info in a single vmap lookup * Use this lookup in Map:: relocation methods to update m_areaId and m_zoneId fields on WorldObject * Adjust GetZoneId/GetAreaId on WorldObject to always return these cached fields * Clean up liquid state handling on Unit and Player * Hand floor's Z coord up through GetFullTerrainStatusForPosition, use it to update a new field in WorldObject, and use that to feed a new GetFloorZ call on WorldObject. --- .../Collision/Management/IVMapManager.h | 25 ++++ .../Collision/Management/VMapManager2.cpp | 31 +++++ .../Collision/Management/VMapManager2.h | 1 + src/common/Collision/Maps/MapTree.h | 1 + src/common/Collision/Models/WorldModel.cpp | 1 + .../game/Entities/Creature/Creature.cpp | 2 +- src/server/game/Entities/Object/Object.cpp | 48 ++++--- src/server/game/Entities/Object/Object.h | 25 ++-- src/server/game/Entities/Player/Player.cpp | 94 +++++-------- src/server/game/Entities/Player/Player.h | 2 +- src/server/game/Entities/Unit/Unit.cpp | 62 ++++----- src/server/game/Entities/Unit/Unit.h | 4 +- src/server/game/Maps/Map.cpp | 123 ++++++++++++++++-- src/server/game/Maps/Map.h | 33 ++++- src/server/game/Movement/PathGenerator.cpp | 4 +- src/server/game/Spells/Spell.cpp | 2 +- src/server/scripts/Commands/cs_misc.cpp | 2 +- 17 files changed, 315 insertions(+), 145 deletions(-) diff --git a/src/common/Collision/Management/IVMapManager.h b/src/common/Collision/Management/IVMapManager.h index 2188da1cadc..61d96aec2c7 100644 --- a/src/common/Collision/Management/IVMapManager.h +++ b/src/common/Collision/Management/IVMapManager.h @@ -22,6 +22,7 @@ #include #include "Define.h" #include "ModelIgnoreFlags.h" +#include "Common.h" //=========================================================== @@ -49,6 +50,27 @@ namespace VMAP #define VMAP_INVALID_HEIGHT -100000.0f // for check #define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case + struct AreaAndLiquidData + { + struct AreaInfo + { + AreaInfo(int32 _adtId, int32 _rootId, int32 _groupId, uint32 _flags) : adtId(_adtId), rootId(_rootId), groupId(_groupId), mogpFlags(_flags) { } + int32 const adtId; + int32 const rootId; + int32 const groupId; + uint32 const mogpFlags; + }; + struct LiquidInfo + { + LiquidInfo(uint32 _type, float _level) : type(_type), level(_level) { } + uint32 const type; + float const level; + }; + + float floorZ = VMAP_INVALID_HEIGHT; + Optional areaInfo; + Optional liquidInfo; + }; //=========================================================== class TC_COMMON_API IVMapManager { @@ -102,6 +124,9 @@ namespace VMAP */ virtual bool getAreaInfo(unsigned int pMapId, float x, float y, float &z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const=0; virtual bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float &level, float &floor, uint32 &type) const=0; + // get both area + liquid data in a single vmap lookup + virtual void getAreaAndLiquidData(unsigned int mapId, float x, float y, float z, uint8 reqLiquidType, AreaAndLiquidData& data) const=0; + }; } diff --git a/src/common/Collision/Management/VMapManager2.cpp b/src/common/Collision/Management/VMapManager2.cpp index 4689f468d8f..caa19d6b8d4 100644 --- a/src/common/Collision/Management/VMapManager2.cpp +++ b/src/common/Collision/Management/VMapManager2.cpp @@ -279,6 +279,37 @@ namespace VMAP return false; } + void VMapManager2::getAreaAndLiquidData(unsigned int mapId, float x, float y, float z, uint8 reqLiquidType, AreaAndLiquidData& data) const + { + if (IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LIQUIDSTATUS)) + { + data.floorZ = z; + int32 adtId, rootId, groupId; + uint32 flags; + if (getAreaInfo(mapId, x, y, data.floorZ, flags, adtId, rootId, groupId)) + data.areaInfo = boost::in_place(adtId, rootId, groupId, flags); + return; + } + InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId); + if (instanceTree != iInstanceMapTrees.end()) + { + LocationInfo info; + Vector3 pos = convertPositionToInternalRep(x, y, z); + if (instanceTree->second->GetLocationInfo(pos, info)) + { + data.floorZ = info.ground_Z; + uint32 liquidType = info.hitModel->GetLiquidType(); + float liquidLevel; + if (!reqLiquidType || (GetLiquidFlagsPtr(liquidType) & reqLiquidType)) + if (info.hitInstance->GetLiquidLevel(pos, info, liquidLevel)) + data.liquidInfo = boost::in_place(liquidType, liquidLevel); + + if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG)) + data.areaInfo = boost::in_place(info.hitInstance->adtId, info.rootId, info.hitModel->GetWmoID(), info.hitModel->GetMogpFlags()); + } + } + } + WorldModel* VMapManager2::acquireModelInstance(const std::string& basepath, const std::string& filename, uint32 flags/* Only used when creating the model */) { //! Critical section, thread safe access to iLoadedModelFiles diff --git a/src/common/Collision/Management/VMapManager2.h b/src/common/Collision/Management/VMapManager2.h index c444e504127..f874afdf5d4 100644 --- a/src/common/Collision/Management/VMapManager2.h +++ b/src/common/Collision/Management/VMapManager2.h @@ -118,6 +118,7 @@ namespace VMAP bool getAreaInfo(unsigned int pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const override; bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type) const override; + void getAreaAndLiquidData(unsigned int mapId, float x, float y, float z, uint8 reqLiquidType, AreaAndLiquidData& data) const override; WorldModel* acquireModelInstance(const std::string& basepath, const std::string& filename, uint32 flags = 0); void releaseModelInstance(const std::string& filename); diff --git a/src/common/Collision/Maps/MapTree.h b/src/common/Collision/Maps/MapTree.h index f5c11dd2bce..bc18e07789b 100644 --- a/src/common/Collision/Maps/MapTree.h +++ b/src/common/Collision/Maps/MapTree.h @@ -35,6 +35,7 @@ namespace VMAP struct TC_COMMON_API LocationInfo { LocationInfo(): hitInstance(nullptr), hitModel(nullptr), ground_Z(-G3D::finf()) { } + int32 rootId; const ModelInstance* hitInstance; const GroupModel* hitModel; float ground_Z; diff --git a/src/common/Collision/Models/WorldModel.cpp b/src/common/Collision/Models/WorldModel.cpp index 19b40b6de18..d04bcf4d241 100644 --- a/src/common/Collision/Models/WorldModel.cpp +++ b/src/common/Collision/Models/WorldModel.cpp @@ -531,6 +531,7 @@ namespace VMAP groupTree.intersectPoint(p, callback); if (callback.hit != groupModels.end()) { + info.rootId = RootWMOID; info.hitModel = &(*callback.hit); dist = callback.zDist; return true; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 0fc7059078a..ace5f42ec1b 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2725,7 +2725,7 @@ void Creature::UpdateMovementFlags() return; // Set the movement flags if the creature is in that mode. (Only fly if actually in air, only swim if in water, etc) - float ground = GetMap()->GetHeight(GetPhases(), GetPositionX(), GetPositionY(), GetPositionZMinusOffset()); + float ground = GetFloorZ(); bool isInAir = (G3D::fuzzyGt(GetPositionZMinusOffset(), ground + 0.05f) || G3D::fuzzyLt(GetPositionZMinusOffset(), ground - 0.05f)); // Can be underground too, prevent the falling diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 1cd79d93ba5..c3231df6115 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1194,7 +1194,7 @@ void MovementInfo::OutDebug() WorldObject::WorldObject(bool isWorldObject) : WorldLocation(), LastUsedScriptID(0), m_name(""), m_isActive(false), m_isWorldObject(isWorldObject), m_zoneScript(NULL), -m_transport(NULL), m_currMap(NULL), m_InstanceId(0), +m_transport(NULL), m_zoneId(0), m_areaId(0), m_staticFloorZ(VMAP_INVALID_HEIGHT), m_currMap(NULL), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), _dbPhase(0), m_notifyflags(0), m_executed_notifies(0), m_aiAnimKitId(0), m_movementAnimKitId(0), m_meleeAnimKitId(0) { @@ -1269,6 +1269,28 @@ void WorldObject::_Create(ObjectGuid::LowType guidlow, HighGuid guidhigh, uint32 m_phaseMask = phaseMask; } +void WorldObject::UpdatePositionData() +{ + PositionFullTerrainStatus data; + GetMap()->GetFullTerrainStatusForPosition(GetPositionX(), GetPositionY(), GetPositionZ(), data); + ProcessPositionDataChanged(data); +} + +void WorldObject::ProcessPositionDataChanged(PositionFullTerrainStatus const& data) +{ + m_zoneId = m_areaId = data.areaId; + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(m_areaId)) + if (area->zone) + m_zoneId = area->zone; + m_staticFloorZ = data.floorZ; +} + +void WorldObject::AddToWorld() +{ + Object::AddToWorld(); + GetBaseMap()->GetZoneAndAreaId(m_zoneId, m_areaId, GetPositionX(), GetPositionY(), GetPositionZ()); +} + void WorldObject::RemoveFromWorld() { if (!IsInWorld()) @@ -1279,21 +1301,6 @@ void WorldObject::RemoveFromWorld() Object::RemoveFromWorld(); } -uint32 WorldObject::GetZoneId() const -{ - return GetBaseMap()->GetZoneId(m_positionX, m_positionY, m_positionZ); -} - -uint32 WorldObject::GetAreaId() const -{ - return GetBaseMap()->GetAreaId(m_positionX, m_positionY, m_positionZ); -} - -void WorldObject::GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const -{ - GetBaseMap()->GetZoneAndAreaId(zoneid, areaid, m_positionX, m_positionY, m_positionZ); -} - InstanceScript* WorldObject::GetInstanceScript() { Map* map = GetMap(); @@ -2476,7 +2483,7 @@ float NormalizeZforCollision(WorldObject* obj, float x, float y, float z) return z; } LiquidData liquid_status; - ZLiquidStatus res = obj->GetMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquid_status); + ZLiquidStatus res = obj->GetMap()->GetLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquid_status); if (res && liquid_status.level > helper) // water must be above ground { if (liquid_status.level > z) // z is underwater @@ -2884,6 +2891,13 @@ ObjectGuid WorldObject::GetTransGUID() const return ObjectGuid::Empty; } +float WorldObject::GetFloorZ() const +{ + if (!IsInWorld()) + return m_staticFloorZ; + return std::max(m_staticFloorZ, GetMap()->GetGameObjectFloor(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ())); +} + template TC_GAME_API void WorldObject::GetGameObjectListWithEntryInGrid(std::list&, uint32, float) const; template TC_GAME_API void WorldObject::GetGameObjectListWithEntryInGrid(std::deque&, uint32, float) const; template TC_GAME_API void WorldObject::GetGameObjectListWithEntryInGrid(std::vector&, uint32, float) const; diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 21d68033848..f9a4034fd3d 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -101,7 +101,8 @@ class TC_GAME_API Object bool IsInWorld() const { return m_inWorld; } virtual void AddToWorld(); - virtual void RemoveFromWorld(); + void AddToWorld() override; + void RemoveFromWorld() override; ObjectGuid GetGUID() const { return GetGuidValue(OBJECT_FIELD_GUID); } PackedGuid const& GetPackGUID() const { return m_PackGUID; } @@ -498,9 +499,9 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation // if negative it is used as PhaseGroupId void SetDBPhase(int32 p) { _dbPhase = p; } - uint32 GetZoneId() const; - uint32 GetAreaId() const; - void GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const; + uint32 GetZoneId() const { return m_zoneId; } + uint32 GetAreaId() const { return m_areaId; } + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const { zoneid = m_zoneId, areaid = m_areaId; } InstanceScript* GetInstanceScript(); @@ -608,6 +609,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation { UpdateObjectVisibility(true); } + void UpdatePositionData(); void BuildUpdate(UpdateDataMapType&) override; @@ -652,6 +654,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation virtual float GetStationaryZ() const { return GetPositionZ(); } virtual float GetStationaryO() const { return GetOrientation(); } + float GetFloorZ() const; + uint16 GetAIAnimKitId() const { return m_aiAnimKitId; } void SetAIAnimKitId(uint16 animKitId); uint16 GetMovementAnimKitId() const { return m_movementAnimKitId; } @@ -668,6 +672,11 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation // transports Transport* m_transport; + virtual void ProcessPositionDataChanged(PositionFullTerrainStatus const& data); + uint32 m_zoneId; + uint32 m_areaId; + float m_staticFloorZ; + //these functions are used mostly for Relocate() and Corpse/Player specific stuff... //use them ONLY in LoadFromDB()/Create() funcs and nowhere else! //mapId/instanceId should be set in SetMap() function! @@ -680,11 +689,11 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation //difference from IsAlwaysVisibleFor: 1. after distance check; 2. use owner or charmer as seer virtual bool IsAlwaysDetectableFor(WorldObject const* /*seer*/) const { return false; } private: - Map* m_currMap; //current object's Map location + Map* m_currMap; // current object's Map location - //uint32 m_mapId; // object at map with map_id - uint32 m_InstanceId; // in map copy with instance id - uint32 m_phaseMask; // in area phase state + //uint32 m_mapId; // object at map with map_id + uint32 m_InstanceId; // in map copy with instance id + uint32 m_phaseMask; // in area phase state std::set _phases; std::set _terrainSwaps; std::set _worldMapAreaSwaps; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b4ef879fbeb..da8a654655f 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -24666,77 +24666,53 @@ void Player::SetOriginalGroup(Group* group, int8 subgroup) } } -void Player::UpdateUnderwaterState(Map* m, float x, float y, float z) +void Player::ProcessTerrainStatusUpdate(ZLiquidStatus status, Optional const& liquidData) { - LiquidData liquid_status; - ZLiquidStatus res = m->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquid_status); - if (!res) - { - m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | UNDERWATER_INSLIME | UNDERWARER_INDARKWATER); - if (_lastLiquid && _lastLiquid->SpellId) - RemoveAurasDueToSpell(_lastLiquid->SpellId); - - _lastLiquid = nullptr; + if (IsFlying()) return; - } - if (uint32 liqEntry = liquid_status.entry) + // process liquid auras using generic unit code + Unit::ProcessTerrainStatusUpdate(status, liquidData); + + // player specific logic for mirror timers + if (status && liquidData) { - LiquidTypeEntry const* liquid = sLiquidTypeStore.LookupEntry(liqEntry); - if (_lastLiquid && _lastLiquid->SpellId && _lastLiquid->Id != liqEntry) - RemoveAurasDueToSpell(_lastLiquid->SpellId); - - if (liquid && liquid->SpellId) + // Breath bar state (under water in any liquid type) + if (liquidData->type_flags & MAP_ALL_LIQUIDS) { - if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER)) - { - if (!HasAura(liquid->SpellId)) - CastSpell(this, liquid->SpellId, true); - } + if (status & LIQUID_MAP_UNDER_WATER) + m_MirrorTimerFlags |= UNDERWATER_INWATER; else - RemoveAurasDueToSpell(liquid->SpellId); + m_MirrorTimerFlags &= ~UNDERWATER_INWATER; } - _lastLiquid = liquid; - } - else if (_lastLiquid && _lastLiquid->SpellId) - { - RemoveAurasDueToSpell(_lastLiquid->SpellId); - _lastLiquid = nullptr; - } - - - // All liquids type - check under water position - if (liquid_status.type_flags & (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_SLIME)) - { - if (res & LIQUID_MAP_UNDER_WATER) - m_MirrorTimerFlags |= UNDERWATER_INWATER; + // Fatigue bar state (if not on flight path or transport) + if ((liquidData->type_flags & MAP_LIQUID_TYPE_DARK_WATER) && !IsInFlight() && !GetTransport()) + m_MirrorTimerFlags |= UNDERWARER_INDARKWATER; else - m_MirrorTimerFlags &= ~UNDERWATER_INWATER; - } + m_MirrorTimerFlags &= ~UNDERWARER_INDARKWATER; - // Allow travel in dark water on taxi or transport - if ((liquid_status.type_flags & MAP_LIQUID_TYPE_DARK_WATER) && !IsInFlight() && !GetTransport()) - m_MirrorTimerFlags |= UNDERWARER_INDARKWATER; + // Lava state (any contact) + if (liquidData->type_flags & MAP_LIQUID_TYPE_MAGMA) + { + if (status & MAP_LIQUID_STATUS_IN_CONTACT) + m_MirrorTimerFlags |= UNDERWATER_INLAVA; + else + m_MirrorTimerFlags &= ~UNDERWATER_INLAVA; + } + + // Slime state (any contact) + if (liquidData->type_flags & MAP_LIQUID_TYPE_SLIME) + { + if (status & MAP_LIQUID_STATUS_IN_CONTACT) + m_MirrorTimerFlags |= UNDERWATER_INSLIME; + else + m_MirrorTimerFlags &= ~UNDERWATER_INSLIME; + } + } else - m_MirrorTimerFlags &= ~UNDERWARER_INDARKWATER; + m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | UNDERWATER_INSLIME | UNDERWARER_INDARKWATER); - // in lava check, anywhere in lava level - if (liquid_status.type_flags & MAP_LIQUID_TYPE_MAGMA) - { - if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER | LIQUID_MAP_WATER_WALK)) - m_MirrorTimerFlags |= UNDERWATER_INLAVA; - else - m_MirrorTimerFlags &= ~UNDERWATER_INLAVA; - } - // in slime check, anywhere in slime level - if (liquid_status.type_flags & MAP_LIQUID_TYPE_SLIME) - { - if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER | LIQUID_MAP_WATER_WALK)) - m_MirrorTimerFlags |= UNDERWATER_INSLIME; - else - m_MirrorTimerFlags &= ~UNDERWATER_INSLIME; - } } void Player::SetCanParry(bool value) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index b9cd7867b79..2e7929d9090 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2039,7 +2039,7 @@ class TC_GAME_API Player : public Unit, public GridObject bool UpdatePosition(float x, float y, float z, float orientation, bool teleport = false) override; bool UpdatePosition(const Position &pos, bool teleport = false) override { return UpdatePosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); } - void UpdateUnderwaterState(Map* m, float x, float y, float z) override; + void ProcessTerrainStatusUpdate(ZLiquidStatus status, Optional const& liquidData) override; void SendMessageToSet(WorldPacket* data, bool self) override { SendMessageToSetInRange(data, GetVisibilityRange(), self); } void SendMessageToSetInRange(WorldPacket* data, float dist, bool self) override; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 575e5c4b6cd..76960f8f017 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -467,7 +467,6 @@ void Unit::Update(uint32 p_time) UpdateSplineMovement(p_time); i_motionMaster->UpdateMotion(p_time); - UpdateUnderwaterState(GetMap(), GetPositionX(), GetPositionY(), GetPositionZ()); } bool Unit::haveOffhandWeapon() const @@ -3115,48 +3114,34 @@ bool Unit::IsUnderWater() const return GetBaseMap()->IsUnderWater(GetPositionX(), GetPositionY(), GetPositionZ()); } -void Unit::UpdateUnderwaterState(Map* m, float x, float y, float z) +void Unit::ProcessPositionDataChanged(PositionFullTerrainStatus const& data) { - if (!IsPet() && !IsVehicle()) + WorldObject::ProcessPositionDataChanged(data); + ProcessTerrainStatusUpdate(data.liquidStatus, data.liquidInfo); +} + +void Unit::ProcessTerrainStatusUpdate(ZLiquidStatus status, Optional const& liquidData) +{ + if (IsFlying() || (!IsControlledByPlayer())) return; - LiquidData liquid_status; - ZLiquidStatus res = m->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquid_status); - if (!res) + // remove appropriate auras if we are swimming/not swimming respectively + if (status & MAP_LIQUID_STATUS_SWIMMING) + RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_ABOVEWATER); + else + RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER); + + // liquid aura handling + LiquidTypeEntry const* curLiquid = nullptr; + if ((status & MAP_LIQUID_STATUS_SWIMMING) && liquidData) + curLiquid = sLiquidTypeStore.LookupEntry(liquidData->entry); + if (curLiquid != _lastLiquid) { if (_lastLiquid && _lastLiquid->SpellId) RemoveAurasDueToSpell(_lastLiquid->SpellId); - - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER); - _lastLiquid = NULL; - return; - } - - if (uint32 liqEntry = liquid_status.entry) - { - LiquidTypeEntry const* liquid = sLiquidTypeStore.LookupEntry(liqEntry); - if (_lastLiquid && _lastLiquid->SpellId && _lastLiquid->Id != liqEntry) - RemoveAurasDueToSpell(_lastLiquid->SpellId); - - if (liquid && liquid->SpellId) - { - if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER)) - { - if (!HasAura(liquid->SpellId)) - CastSpell(this, liquid->SpellId, true); - } - else - RemoveAurasDueToSpell(liquid->SpellId); - } - - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_ABOVEWATER); - _lastLiquid = liquid; - } - else if (_lastLiquid && _lastLiquid->SpellId) - { - RemoveAurasDueToSpell(_lastLiquid->SpellId); - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER); - _lastLiquid = NULL; + if (curLiquid && curLiquid->SpellId) + CastSpell(this, curLiquid->SpellId, true); + _lastLiquid = curLiquid; } } @@ -13563,8 +13548,7 @@ bool Unit::UpdatePosition(float x, float y, float z, float orientation, bool tel else if (turn) UpdateOrientation(orientation); - // code block for underwater state update - UpdateUnderwaterState(GetMap(), x, y, z); + UpdatePositionData(); return (relocated || turn); } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index ee1c2c3e008..5a6490eb0ea 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1586,7 +1586,6 @@ class TC_GAME_API Unit : public WorldObject virtual bool IsInWater() const; virtual bool IsUnderWater() const; - virtual void UpdateUnderwaterState(Map* m, float x, float y, float z); bool isInAccessiblePlaceFor(Creature const* c) const; void SendHealSpellLog(HealInfo& healInfo, bool critical = false); @@ -2301,6 +2300,9 @@ class TC_GAME_API Unit : public WorldObject void DisableSpline(); + void ProcessPositionDataChanged(PositionFullTerrainStatus const& data) override; + virtual void ProcessTerrainStatusUpdate(ZLiquidStatus status, Optional const& liquidData); + private: void UpdateSplineMovement(uint32 t_diff); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index f0b3b105dca..853664df79d 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -988,6 +988,7 @@ void Map::PlayerRelocation(Player* player, float x, float y, float z, float orie AddToGrid(player, new_cell); } + player->UpdatePositionData(); player->UpdateObjectVisibility(false); } @@ -1022,6 +1023,7 @@ void Map::CreatureRelocation(Creature* creature, float x, float y, float z, floa if (creature->IsVehicle()) creature->GetVehicleKit()->RelocatePassengers(); creature->UpdateObjectVisibility(false); + creature->UpdatePositionData(); RemoveCreatureFromMoveList(creature); } @@ -1052,6 +1054,7 @@ void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float { go->Relocate(x, y, z, orientation); go->UpdateModelPosition(); + go->UpdatePositionData(); go->UpdateObjectVisibility(false); RemoveGameObjectFromMoveList(go); } @@ -1084,6 +1087,7 @@ void Map::DynamicObjectRelocation(DynamicObject* dynObj, float x, float y, float else { dynObj->Relocate(x, y, z, orientation); + dynObj->UpdatePositionData(); dynObj->UpdateObjectVisibility(false); RemoveDynamicObjectFromMoveList(dynObj); } @@ -1177,6 +1181,7 @@ void Map::MoveAllCreaturesInMoveList() if (c->IsVehicle()) c->GetVehicleKit()->RelocatePassengers(); //CreatureRelocationNotify(c, new_cell, new_cell.cellCoord()); + c->UpdatePositionData(); c->UpdateObjectVisibility(false); } else @@ -1231,6 +1236,7 @@ void Map::MoveAllGameObjectsInMoveList() // update pos go->Relocate(go->_newPosition); go->UpdateModelPosition(); + go->UpdatePositionData(); go->UpdateObjectVisibility(false); } else @@ -1275,6 +1281,7 @@ void Map::MoveAllDynamicObjectsInMoveList() { // update pos dynObj->Relocate(dynObj->_newPosition); + dynObj->UpdatePositionData(); dynObj->UpdateObjectVisibility(false); } else @@ -1495,6 +1502,7 @@ bool Map::CreatureRespawnRelocation(Creature* c, bool diffGridOnly) c->Relocate(resp_x, resp_y, resp_z, resp_o); c->GetMotionMaster()->Initialize(); // prevent possible problems with default move generators //CreatureRelocationNotify(c, resp_cell, resp_cell.GetCellCoord()); + c->UpdatePositionData(); c->UpdateObjectVisibility(false); return true; } @@ -1520,6 +1528,7 @@ bool Map::GameObjectRespawnRelocation(GameObject* go, bool diffGridOnly) if (GameObjectCellRelocation(go, resp_cell)) { go->Relocate(resp_x, resp_y, resp_z, resp_o); + go->UpdatePositionData(); go->UpdateObjectVisibility(false); return true; } @@ -2123,7 +2132,7 @@ uint8 GridMap::getTerrainType(float x, float y) const } // Get water state on map -inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data) +inline ZLiquidStatus GridMap::GetLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data) { // Check water type (if no water return) if (!_liquidType && !_liquidFlags) @@ -2392,11 +2401,6 @@ uint32 Map::GetAreaId(float x, float y, float z, bool *isOutdoors) const return areaId; } -uint32 Map::GetAreaId(float x, float y, float z) const -{ - return GetAreaId(x, y, z, nullptr); -} - uint32 Map::GetZoneId(float x, float y, float z) const { uint32 areaId = GetAreaId(x, y, z); @@ -2423,7 +2427,7 @@ uint8 Map::GetTerrainType(float x, float y) const return 0; } -ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data) const +ZLiquidStatus Map::GetLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data) const { ZLiquidStatus result = LIQUID_MAP_NO_WATER; VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); @@ -2432,7 +2436,7 @@ ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidTyp uint32 liquid_type = 0; if (vmgr->GetLiquidLevel(GetId(), x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type)) { - TC_LOG_DEBUG("maps", "getLiquidStatus(): vmap liquid level: %f ground: %f type: %u", liquid_level, ground_level, liquid_type); + TC_LOG_DEBUG("maps", "GetLiquidStatus(): vmap liquid level: %f ground: %f type: %u", liquid_level, ground_level, liquid_type); // Check water level and ground level if (liquid_level > ground_level && z > ground_level - 2) { @@ -2490,7 +2494,7 @@ ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidTyp if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) { LiquidData map_data; - ZLiquidStatus map_result = gmap->getLiquidStatus(x, y, z, ReqLiquidType, &map_data); + ZLiquidStatus map_result = gmap->GetLiquidStatus(x, y, z, ReqLiquidType, &map_data); // Not override LIQUID_MAP_ABOVE_WATER with LIQUID_MAP_NO_WATER: if (map_result != LIQUID_MAP_NO_WATER && (map_data.level > ground_level)) { @@ -2508,6 +2512,103 @@ ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidTyp return result; } +void Map::GetFullTerrainStatusForPosition(float x, float y, float z, PositionFullTerrainStatus& data, uint8 reqLiquidType) const +{ + VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + VMAP::AreaAndLiquidData vmapData; + vmgr->getAreaAndLiquidData(GetId(), x, y, z, reqLiquidType, vmapData); + if (vmapData.areaInfo) + data.areaInfo = boost::in_place(vmapData.areaInfo->adtId, vmapData.areaInfo->rootId, vmapData.areaInfo->groupId, vmapData.areaInfo->mogpFlags); + + GridMap* gmap = const_cast(this)->GetGrid(x, y); + float mapHeight = gmap->getHeight(x, y); + + // area lookup + AreaTableEntry const* areaEntry = nullptr; + if (vmapData.areaInfo && (z + 2.0f <= mapHeight || mapHeight <= vmapData.floorZ)) + if (WMOAreaTableEntry const* wmoEntry = GetWMOAreaTableEntryByTripple(vmapData.areaInfo->rootId, vmapData.areaInfo->adtId, vmapData.areaInfo->groupId)) + areaEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId); + + if (areaEntry) + { + data.floorZ = vmapData.floorZ; + data.areaId = areaEntry->ID; + } + else + { + data.floorZ = mapHeight; + if (gmap) + data.areaId = gmap->getArea(x, y); + else + data.areaId = 0; + + if (!data.areaId) + data.areaId = i_mapEntry->linked_zone; + + if (data.areaId) + areaEntry = sAreaTableStore.LookupEntry(data.areaId); + } + + // liquid processing + data.liquidStatus = LIQUID_MAP_NO_WATER; + if (vmapData.liquidInfo && vmapData.liquidInfo->level > vmapData.floorZ && z + 2.0f > vmapData.floorZ) + { + uint32 liquidType = vmapData.liquidInfo->type; + if (GetId() == 530 && liquidType == 2) // gotta love blizzard hacks + liquidType = 15; + + uint32 liquidFlagType = 0; + if (LiquidTypeEntry const* liquidData = sLiquidTypeStore.LookupEntry(liquidType)) + liquidFlagType = liquidData->Type; + + if (liquidType && liquidType < 21 && areaEntry) + { + uint32 overrideLiquid = areaEntry->LiquidTypeOverride[liquidFlagType]; + if (!overrideLiquid && areaEntry->zone) + { + AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(areaEntry->zone); + if (zoneEntry) + overrideLiquid = zoneEntry->LiquidTypeOverride[liquidFlagType]; + } + + if (LiquidTypeEntry const* overrideData = sLiquidTypeStore.LookupEntry(overrideLiquid)) + { + liquidType = overrideLiquid; + liquidFlagType = overrideData->Type; + } + } + + data.liquidInfo = boost::in_place(); + data.liquidInfo->level = vmapData.liquidInfo->level; + data.liquidInfo->depth_level = vmapData.floorZ; + data.liquidInfo->entry = liquidType; + data.liquidInfo->type_flags = 1 << liquidFlagType; + + float delta = vmapData.liquidInfo->level - z; + if (delta > 2.0f) + data.liquidStatus = LIQUID_MAP_UNDER_WATER; + else if (delta > 0.0f) + data.liquidStatus = LIQUID_MAP_IN_WATER; + else if (delta > -0.1f) + data.liquidStatus = LIQUID_MAP_WATER_WALK; + else + data.liquidStatus = LIQUID_MAP_ABOVE_WATER; + } + // look up liquid data from grid map + if (gmap && (data.liquidStatus == LIQUID_MAP_ABOVE_WATER || data.liquidStatus == LIQUID_MAP_NO_WATER)) + { + LiquidData gridMapLiquid; + ZLiquidStatus gridMapStatus = gmap->GetLiquidStatus(x, y, z, reqLiquidType, &gridMapLiquid); + if (gridMapStatus != LIQUID_MAP_NO_WATER && (gridMapLiquid.level > vmapData.floorZ)) + { + if (GetId() == 530 && gridMapLiquid.entry == 2) + gridMapLiquid.entry = 15; + data.liquidInfo = gridMapLiquid; + data.liquidStatus = gridMapStatus; + } + } +} + float Map::GetWaterLevel(float x, float y) const { if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) @@ -2545,12 +2646,12 @@ bool Map::IsInWater(float x, float y, float pZ, LiquidData* data) const { LiquidData liquid_status; LiquidData* liquid_ptr = data ? data : &liquid_status; - return (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, liquid_ptr) & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER)) != 0; + return (GetLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, liquid_ptr) & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER)) != 0; } bool Map::IsUnderWater(float x, float y, float z) const { - return (getLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN) & LIQUID_MAP_UNDER_WATER) != 0; + return (GetLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN) & LIQUID_MAP_UNDER_WATER) != 0; } bool Map::CheckGridIntegrity(Creature* c, bool moved) const diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 6e611c3b127..03e4a7a678e 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -135,6 +135,9 @@ enum ZLiquidStatus LIQUID_MAP_UNDER_WATER = 0x00000008 }; +#define MAP_LIQUID_STATUS_SWIMMING (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER) +#define MAP_LIQUID_STATUS_IN_CONTACT (MAP_LIQUID_STATUS_SWIMMING | LIQUID_MAP_WATER_WALK) + #define MAP_LIQUID_TYPE_NO_WATER 0x00 #define MAP_LIQUID_TYPE_WATER 0x01 #define MAP_LIQUID_TYPE_OCEAN 0x02 @@ -154,6 +157,24 @@ struct LiquidData float depth_level; }; +struct PositionFullTerrainStatus +{ + struct AreaInfo + { + AreaInfo(int32 _adtId, int32 _rootId, int32 _groupId, uint32 _flags) : adtId(_adtId), rootId(_rootId), groupId(_groupId), mogpFlags(_flags) { } + int32 const adtId; + int32 const rootId; + int32 const groupId; + uint32 const mogpFlags; + }; + + uint32 areaId; + float floorZ; + ZLiquidStatus liquidStatus; + Optional areaInfo; + Optional liquidInfo; +}; + class TC_GAME_API GridMap { uint32 _flags; @@ -209,7 +230,7 @@ public: inline float getHeight(float x, float y) const {return (this->*_gridGetHeight)(x, y);} float getLiquidLevel(float x, float y) const; uint8 getTerrainType(float x, float y) const; - ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = 0); + ZLiquidStatus GetLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = 0); }; #pragma pack(push, 1) @@ -331,11 +352,11 @@ class TC_GAME_API Map : public GridRefManager // can return INVALID_HEIGHT if under z+2 z coord not found height float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; - ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = nullptr) const; + void GetFullTerrainStatusForPosition(float x, float y, float z, PositionFullTerrainStatus& data, uint8 reqLiquidType = MAP_ALL_LIQUIDS) const; + ZLiquidStatus GetLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = nullptr) const; - uint32 GetAreaId(float x, float y, float z, bool *isOutdoors) const; bool GetAreaInfo(float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const; - uint32 GetAreaId(float x, float y, float z) const; + uint32 GetAreaId(float x, float y, float z, bool *isOutdoors = nullptr) const; uint32 GetZoneId(float x, float y, float z) const; void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const; @@ -499,6 +520,10 @@ class TC_GAME_API Map : public GridRefManager void RemoveGameObjectModel(const GameObjectModel& model) { _dynamicTree.remove(model); } void InsertGameObjectModel(const GameObjectModel& model) { _dynamicTree.insert(model); } bool ContainsGameObjectModel(const GameObjectModel& model) const { return _dynamicTree.contains(model);} + float GetGameObjectFloor(uint32 phasemask, float x, float y, float z, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const + { + return _dynamicTree.getHeight(x, y, z, phasemask, maxSearchDist); + } bool getObjectHitPos(std::set const& phases, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float modifyDist); virtual uint32 GetOwnerGuildId(uint32 /*team*/ = TEAM_OTHER) const { return 0; } diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp index b354ef9e744..51083ab19a1 100644 --- a/src/server/game/Movement/PathGenerator.cpp +++ b/src/server/game/Movement/PathGenerator.cpp @@ -185,7 +185,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con // Check both start and end points, if they're both in water, then we can *safely* let the creature move for (uint32 i = 0; i < _pathPoints.size(); ++i) { - ZLiquidStatus status = _sourceUnit->GetBaseMap()->getLiquidStatus(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z, MAP_ALL_LIQUIDS, NULL); + ZLiquidStatus status = _sourceUnit->GetBaseMap()->GetLiquidStatus(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z, MAP_ALL_LIQUIDS, NULL); // One of the points is not in the water, cancel movement. if (status == LIQUID_MAP_NO_WATER) { @@ -648,7 +648,7 @@ void PathGenerator::UpdateFilter() NavTerrain PathGenerator::GetNavTerrain(float x, float y, float z) { LiquidData data; - ZLiquidStatus liquidStatus = _sourceUnit->GetBaseMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &data); + ZLiquidStatus liquidStatus = _sourceUnit->GetBaseMap()->GetLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &data); if (liquidStatus == LIQUID_MAP_NO_WATER) return NAV_GROUND; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 58a4c0053ea..8e72d47a679 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1289,7 +1289,7 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici float ground = m_caster->GetMap()->GetHeight(m_caster->GetPhases(), x, y, z, true, 50.0f); float liquidLevel = VMAP_INVALID_HEIGHT_VALUE; LiquidData liquidData; - if (m_caster->GetMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquidData)) + if (m_caster->GetMap()->GetLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquidData)) liquidLevel = liquidData.level; if (liquidLevel <= ground) // When there is no liquid Map::GetWaterOrGroundLevel returns ground level diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 908612edd2f..d5de09090a2 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -276,7 +276,7 @@ public: zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMap); LiquidData liquidStatus; - ZLiquidStatus status = map->getLiquidStatus(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), MAP_ALL_LIQUIDS, &liquidStatus); + ZLiquidStatus status = map->GetLiquidStatus(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), MAP_ALL_LIQUIDS, &liquidStatus); if (status) handler->PSendSysMessage(LANG_LIQUID_STATUS, liquidStatus.level, liquidStatus.depth_level, liquidStatus.entry, liquidStatus.type_flags, status);