From 3f53afd39e8bcb35d707deb5d73ec2cecaa907ff Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Tue, 7 Aug 2018 12:35:02 +0200 Subject: [PATCH] Core/Maps: fixed several issues with liquids by merging some 335 branch commits that are taking mopg flags into account --- .../Collision/Management/IVMapManager.h | 28 +- .../Collision/Management/VMapManager2.cpp | 38 +- .../Collision/Management/VMapManager2.h | 5 +- src/common/Collision/Maps/MapTree.h | 3 +- src/common/Collision/Models/WorldModel.cpp | 5 +- .../game/Entities/Creature/Creature.cpp | 2 +- src/server/game/Entities/Object/Object.cpp | 33 +- src/server/game/Entities/Object/Object.h | 24 +- src/server/game/Entities/Player/Player.cpp | 104 ++---- src/server/game/Entities/Player/Player.h | 2 +- src/server/game/Entities/Unit/Unit.cpp | 64 ++-- src/server/game/Entities/Unit/Unit.h | 3 +- src/server/game/Maps/Map.cpp | 346 ++++++++++++------ src/server/game/Maps/Map.h | 42 ++- src/server/game/Miscellaneous/SharedDefines.h | 2 + src/server/game/Spells/Spell.cpp | 4 +- src/server/scripts/Commands/cs_misc.cpp | 2 +- 17 files changed, 435 insertions(+), 272 deletions(-) diff --git a/src/common/Collision/Management/IVMapManager.h b/src/common/Collision/Management/IVMapManager.h index cfa4c19290e..5e7a8c42898 100644 --- a/src/common/Collision/Management/IVMapManager.h +++ b/src/common/Collision/Management/IVMapManager.h @@ -50,6 +50,28 @@ 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 { @@ -101,8 +123,10 @@ namespace VMAP Query world model area info. \param z gets adjusted to the ground height for which this are info is valid */ - 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; + virtual bool getAreaInfo(uint32 mapId, float x, float y, float &z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const = 0; + virtual bool GetLiquidLevel(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32 &type, uint32& mogpFlagss) const = 0; + // get both area + liquid data in a single vmap lookup + virtual void getAreaAndLiquidData(uint32 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 6b31750a676..fad9ed00bf1 100644 --- a/src/common/Collision/Management/VMapManager2.cpp +++ b/src/common/Collision/Management/VMapManager2.cpp @@ -261,7 +261,7 @@ namespace VMAP return VMAP_INVALID_HEIGHT_VALUE; } - bool VMapManager2::getAreaInfo(unsigned int mapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const + bool VMapManager2::getAreaInfo(uint32 mapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const { if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG)) { @@ -279,7 +279,7 @@ namespace VMAP return false; } - bool VMapManager2::GetLiquidLevel(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type) const + bool VMapManager2::GetLiquidLevel(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type, uint32& mogpFlags) const { if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LIQUIDSTATUS)) { @@ -294,6 +294,7 @@ namespace VMAP ASSERT(floor < std::numeric_limits::max()); ASSERT(info.hitModel); type = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc + mogpFlags = info.hitModel->GetMogpFlags(); if (reqLiquidType && !(GetLiquidFlagsPtr(type) & reqLiquidType)) return false; ASSERT(info.hitInstance); @@ -306,6 +307,39 @@ namespace VMAP return false; } + void VMapManager2::getAreaAndLiquidData(uint32 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; + } + auto instanceTree = GetMapTree(mapId); + if (instanceTree != iInstanceMapTrees.end()) + { + LocationInfo info; + Vector3 pos = convertPositionToInternalRep(x, y, z); + if (instanceTree->second->GetLocationInfo(pos, info)) + { + ASSERT(info.hitModel); + ASSERT(info.hitInstance); + 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 b617e6e8578..c12045f2c3d 100644 --- a/src/common/Collision/Management/VMapManager2.h +++ b/src/common/Collision/Management/VMapManager2.h @@ -120,8 +120,9 @@ namespace VMAP bool processCommand(char* /*command*/) override { return false; } // for debug and extensions - 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; + bool getAreaInfo(uint32 mapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const override; + bool GetLiquidLevel(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type, uint32& mogpFlags) const override; + void getAreaAndLiquidData(uint32 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 3956db4c62e..8ea19bef092 100644 --- a/src/common/Collision/Maps/MapTree.h +++ b/src/common/Collision/Maps/MapTree.h @@ -34,7 +34,8 @@ namespace VMAP struct TC_COMMON_API LocationInfo { - LocationInfo(): hitInstance(nullptr), hitModel(nullptr), ground_Z(-G3D::finf()) { } + LocationInfo(): rootId(-1), 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 465fda2f0ce..0cad9648b54 100644 --- a/src/common/Collision/Models/WorldModel.cpp +++ b/src/common/Collision/Models/WorldModel.cpp @@ -399,8 +399,7 @@ namespace VMAP vertices(vert.begin()), triangles(tris.begin()), hit(false) { } bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool /*pStopAtFirstHit*/) { - bool result = IntersectTriangle(triangles[entry], vertices, ray, distance); - if (result) hit=true; + hit = IntersectTriangle(triangles[entry], vertices, ray, distance) || hit; return hit; } std::vector::const_iterator vertices; @@ -422,7 +421,6 @@ namespace VMAP { if (triangles.empty() || !iBound.contains(pos)) return false; - GModelRayCallback callback(triangles, vertices); Vector3 rPos = pos - 0.1f * down; float dist = G3D::finf(); G3D::Ray ray(rPos, down); @@ -559,6 +557,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 5f9d4c3b9ba..10d2baf45e5 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2858,7 +2858,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(GetPhaseShift(), 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 e0789a231f1..4faf5d82d0e 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1228,8 +1228,9 @@ void MovementInfo::OutDebug() WorldObject::WorldObject(bool isWorldObject) : WorldLocation(), LastUsedScriptID(0), m_name(""), m_isActive(false), m_isFarVisible(false), m_isWorldObject(isWorldObject), m_zoneScript(nullptr), -m_transport(nullptr), m_currMap(nullptr), 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) +m_transport(nullptr), m_zoneId(0), m_areaId(0), m_staticFloorZ(VMAP_INVALID_HEIGHT), m_currMap(nullptr), 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) { m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST); m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE); @@ -1310,21 +1311,28 @@ void WorldObject::_Create(ObjectGuid::LowType guidlow, HighGuid guidhigh, uint32 m_phaseMask = phaseMask; } -uint32 WorldObject::GetZoneId() const +void WorldObject::UpdatePositionData() { - return GetBaseMap()->GetZoneId(GetPhaseShift(), m_positionX, m_positionY, m_positionZ); + PositionFullTerrainStatus data; + GetMap()->GetFullTerrainStatusForPosition(GetPhaseShift(), GetPositionX(), GetPositionY(), GetPositionZ(), data); + ProcessPositionDataChanged(data); } -uint32 WorldObject::GetAreaId() const +void WorldObject::ProcessPositionDataChanged(PositionFullTerrainStatus const& data) { - return GetBaseMap()->GetAreaId(GetPhaseShift(), m_positionX, m_positionY, m_positionZ); + m_zoneId = m_areaId = data.areaId; + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(m_areaId)) + if (area->zone) + m_zoneId = area->zone; + m_outdoors = data.outdoors; + m_staticFloorZ = data.floorZ; } -void WorldObject::GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const +void WorldObject::AddToWorld() { - GetBaseMap()->GetZoneAndAreaId(GetPhaseShift(), zoneid, areaid, m_positionX, m_positionY, m_positionZ); + Object::AddToWorld(); + GetMap()->GetZoneAndAreaId(GetPhaseShift(), m_zoneId, m_areaId, GetPositionX(), GetPositionY(), GetPositionZ()); } - void WorldObject::RemoveFromWorld() { if (!IsInWorld()) @@ -2748,6 +2756,13 @@ ObjectGuid WorldObject::GetTransGUID() const return ObjectGuid::Empty; } +float WorldObject::GetFloorZ() const +{ + if (!IsInWorld()) + return m_staticFloorZ; + return std::max(m_staticFloorZ, GetMap()->GetGameObjectFloor(GetPhaseShift(), 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 ffcf4be74a7..2fdbe42570c 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -275,7 +275,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation virtual void Update (uint32 /*time_diff*/) { } void _Create(ObjectGuid::LowType guidlow, HighGuid guidhigh, uint32 phaseMask); - virtual void RemoveFromWorld(); + void AddToWorld() override; + void RemoveFromWorld() override; void GetNearPoint2D(float &x, float &y, float distance, float absAngle) const; void GetNearPoint(WorldObject const* searcher, float &x, float &y, float &z, float searcher_size, float distance2d, float absAngle) const; @@ -312,9 +313,10 @@ 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; } + bool IsOutdoors() const { return m_outdoors; } InstanceScript* GetInstanceScript() const; @@ -420,10 +422,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation void DestroyForNearbyPlayers(); virtual void UpdateObjectVisibility(bool forced = true); - virtual void UpdateObjectVisibilityOnCreate() - { - UpdateObjectVisibility(true); - } + virtual void UpdateObjectVisibilityOnCreate() { UpdateObjectVisibility(true); } + void UpdatePositionData(); void BuildUpdate(UpdateDataMapType&) override; @@ -467,6 +467,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; } @@ -484,6 +486,12 @@ 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; + bool m_outdoors; + //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! diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c99bd0bf9da..a8244b39e0d 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -6438,15 +6438,11 @@ void Player::CheckAreaExploreAndOutdoor() if (IsInFlight()) return; - bool isOutdoor; - uint32 areaId = GetBaseMap()->GetAreaId(GetPhaseShift(), GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor); - AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); - - if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !isOutdoor) + if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !IsOutdoors()) RemoveAurasWithAttribute(SPELL_ATTR0_OUTDOORS_ONLY); - if (!areaId) - return; + uint32 const areaId = GetAreaId(); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); if (!areaEntry) { @@ -25276,77 +25272,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(GetPhaseShift(), x, y, z, MAP_ALL_LIQUIDS, &liquid_status); - if (!res) - { - m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | UNDERWATER_INSLIME | UNDERWATER_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 != liquid) - 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 |= UNDERWATER_INDARKWATER; else - m_MirrorTimerFlags &= ~UNDERWATER_INWATER; - } + m_MirrorTimerFlags &= ~UNDERWATER_INDARKWATER; - // Allow travel in dark water on taxi or transport - if ((liquid_status.type_flags & MAP_LIQUID_TYPE_DARK_WATER) && !IsInFlight() && !GetTransport()) - m_MirrorTimerFlags |= UNDERWATER_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 &= ~UNDERWATER_INDARKWATER; + m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | UNDERWATER_INSLIME | UNDERWATER_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 a2178f71f25..1a8b3525079 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1867,7 +1867,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(Position const& 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 8b258a3056e..fd06bb90530 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -485,7 +485,6 @@ void Unit::Update(uint32 p_time) UpdateSplineMovement(p_time); i_motionMaster->UpdateMotion(p_time); - UpdateUnderwaterState(GetMap(), GetPositionX(), GetPositionY(), GetPositionZ()); } bool Unit::haveOffhandWeapon() const @@ -3207,51 +3206,37 @@ bool Unit::IsUnderWater() const return GetBaseMap()->IsUnderWater(GetPhaseShift(), GetPositionX(), GetPositionY(), GetPositionZ()); } -void Unit::UpdateUnderwaterState(Map* m, float x, float y, float z) +void Unit::ProcessPositionDataChanged(PositionFullTerrainStatus const& data) { - if (IsFlying() || (!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(GetPhaseShift(), 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; + Player* player = GetCharmerOrOwnerPlayerOrPlayerItself(); + if (curLiquid && curLiquid->SpellId && (!player || !player->IsGameMaster())) + CastSpell(this, curLiquid->SpellId, true); + _lastLiquid = curLiquid; } } - void Unit::DeMorph() { SetDisplayId(GetNativeDisplayId()); @@ -13849,9 +13834,6 @@ 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); - return (relocated || turn); } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index e0630f449ee..65032ea00a3 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1260,7 +1260,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); @@ -1980,6 +1979,8 @@ 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 e4377a1997c..26adefa581c 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -56,6 +56,9 @@ u_map_magic MapAreaMagic = { {'A','R','E','A'} }; u_map_magic MapHeightMagic = { {'M','H','G','T'} }; u_map_magic MapLiquidMagic = { {'M','L','I','Q'} }; +static uint16 const holetab_h[4] = { 0x1111, 0x2222, 0x4444, 0x8888 }; +static uint16 const holetab_v[4] = { 0x000F, 0x00F0, 0x0F00, 0xF000 }; + #define DEFAULT_GRID_EXPIRY 300 #define MAX_GRID_LOAD_TIME 50 #define MAX_CREATURE_ATTACK_RADIUS (45.0f * sWorld->getRate(RATE_CREATURE_AGGRO)) @@ -1122,6 +1125,7 @@ void Map::PlayerRelocation(Player* player, float x, float y, float z, float orie AddToGrid(player, new_cell); } + player->UpdatePositionData(); player->UpdateObjectVisibility(false); } @@ -1156,6 +1160,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); } @@ -1186,6 +1191,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); } @@ -1218,6 +1224,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); } @@ -1311,6 +1318,7 @@ void Map::MoveAllCreaturesInMoveList() if (c->IsVehicle()) c->GetVehicleKit()->RelocatePassengers(); //CreatureRelocationNotify(c, new_cell, new_cell.cellCoord()); + c->UpdatePositionData(); c->UpdateObjectVisibility(false); } else @@ -1365,6 +1373,7 @@ void Map::MoveAllGameObjectsInMoveList() // update pos go->Relocate(go->_newPosition); go->UpdateModelPosition(); + go->UpdatePositionData(); go->UpdateObjectVisibility(false); } else @@ -1409,6 +1418,7 @@ void Map::MoveAllDynamicObjectsInMoveList() { // update pos dynObj->Relocate(dynObj->_newPosition); + dynObj->UpdatePositionData(); dynObj->UpdateObjectVisibility(false); } else @@ -1629,6 +1639,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; } @@ -1654,6 +1665,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; } @@ -1809,6 +1821,7 @@ GridMap::GridMap() _liquidEntry = nullptr; _liquidFlags = nullptr; _liquidMap = nullptr; + _holes = nullptr; _fileExists = false; } @@ -1859,6 +1872,13 @@ bool GridMap::loadData(char const* filename) fclose(in); return false; } + // loadup holes data (if any. check header.holesOffset) + if (header.holesSize && !loadHolesData(in, header.holesOffset, header.holesSize)) + { + TC_LOG_ERROR("maps", "Error loading map holes data\n"); + fclose(in); + return false; + } fclose(in); return true; } @@ -1877,12 +1897,14 @@ void GridMap::unloadData() delete[] _liquidEntry; delete[] _liquidFlags; delete[] _liquidMap; + delete[] _holes; _areaMap = nullptr; m_V9 = nullptr; m_V8 = nullptr; _liquidEntry = nullptr; _liquidFlags = nullptr; _liquidMap = nullptr; + _holes = nullptr; _gridGetHeight = &GridMap::getHeightFromFlat; _fileExists = false; } @@ -1987,6 +2009,18 @@ bool GridMap::loadLiquidData(FILE* in, uint32 offset, uint32 /*size*/) return true; } +bool GridMap::loadHolesData(FILE* in, uint32 offset, uint32 size) +{ + if (fseek(in, offset, SEEK_SET) != 0) + return false; + + _holes = new uint16[16 * 16]; + if (fread(_holes, sizeof(uint16), 16 * 16, in) != 16 * 16) + return false; + + return true; +} + uint16 GridMap::getArea(float x, float y) const { if (!_areaMap) @@ -2019,6 +2053,9 @@ float GridMap::getHeightFromFloat(float x, float y) const x_int&=(MAP_RESOLUTION - 1); y_int&=(MAP_RESOLUTION - 1); + if (isHole(x_int, y_int)) + return INVALID_HEIGHT; + // Height stored as: h5 - its v8 grid, h1-h4 - its v9 grid // +--------------> X // | h1-------h2 Coordinates is: @@ -2101,6 +2138,9 @@ float GridMap::getHeightFromUint8(float x, float y) const x_int&=(MAP_RESOLUTION - 1); y_int&=(MAP_RESOLUTION - 1); + if (isHole(x_int, y_int)) + return INVALID_HEIGHT; + int32 a, b, c; uint8 *V9_h1_ptr = &m_uint8_V9[x_int*128 + x_int + y_int]; if (x+y < 1) @@ -2168,6 +2208,9 @@ float GridMap::getHeightFromUint16(float x, float y) const x_int&=(MAP_RESOLUTION - 1); y_int&=(MAP_RESOLUTION - 1); + if (isHole(x_int, y_int)) + return INVALID_HEIGHT; + int32 a, b, c; uint16 *V9_h1_ptr = &m_uint16_V9[x_int*128 + x_int + y_int]; if (x+y < 1) @@ -2220,6 +2263,21 @@ float GridMap::getHeightFromUint16(float x, float y) const return (float)((a * x) + (b * y) + c)*_gridIntHeightMultiplier + _gridHeight; } +bool GridMap::isHole(int row, int col) const +{ + if (!_holes) + return false; + + int cellRow = row / 8; // 8 squares per cell + int cellCol = col / 8; + int holeRow = row % 8 / 2; + int holeCol = (col - (cellCol * 8)) / 2; + + uint16 hole = _holes[cellRow * 16 + cellCol]; + + return (hole & holetab_h[holeCol] & holetab_v[holeRow]) != 0; +} + float GridMap::getLiquidLevel(float x, float y) const { if (!_liquidMap) @@ -2239,19 +2297,6 @@ float GridMap::getLiquidLevel(float x, float y) const return _liquidMap[cx_int*_liquidWidth + cy_int]; } -// Why does this return LIQUID data? -uint8 GridMap::getTerrainType(float x, float y) const -{ - if (!_liquidFlags) - return 0; - - x = 16 * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); - y = 16 * (CENTER_GRID_ID - y/SIZE_OF_GRIDS); - int lx = (int)x & 15; - int ly = (int)y & 15; - return _liquidFlags[lx*16 + ly]; -} - // Get water state on map inline ZLiquidStatus GridMap::GetLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data) { @@ -2410,7 +2455,7 @@ float Map::GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float { float gridHeight = gmap->getHeight(x, y); // look from a bit higher pos to find the floor, ignore under surface case - if (z + 2.0f > gridHeight) + if (G3D::fuzzyGe(z, gridHeight - GROUND_HEIGHT_TOLERANCE)) mapHeight = gridHeight; } @@ -2444,107 +2489,45 @@ float Map::GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float return mapHeight; // explicitly use map data } -inline bool IsOutdoorWMO(uint32 mogpFlags, int32 /*adtId*/, int32 /*rootId*/, int32 /*groupId*/, WMOAreaTableEntry const* wmoEntry, AreaTableEntry const* atEntry) +static inline bool IsInWMOInterior(uint32 mogpFlags) { - bool outdoor = true; - - if (wmoEntry && atEntry) - { - if (atEntry->flags & AREA_FLAG_OUTSIDE) - return true; - if (atEntry->flags & AREA_FLAG_INSIDE) - return false; - } - - outdoor = (mogpFlags & 0x8) != 0; - - if (wmoEntry) - { - if (wmoEntry->Flags & 4) - return true; - if (wmoEntry->Flags & 2) - outdoor = false; - } - return outdoor; + return (mogpFlags & 0x2000) != 0; } -bool Map::IsOutdoors(PhaseShift const& phaseShift, float x, float y, float z) const +uint32 Map::GetAreaId(PhaseShift const& phaseShift, float x, float y, float z) const { uint32 mogpFlags; int32 adtId, rootId, groupId; - - // no wmo found? -> outside by default - if (!GetAreaInfo(phaseShift, x, y, z, mogpFlags, adtId, rootId, groupId)) - return true; - - AreaTableEntry const* atEntry = nullptr; - WMOAreaTableEntry const* wmoEntry= GetWMOAreaTableEntryByTripple(rootId, adtId, groupId); - if (wmoEntry) - { - TC_LOG_DEBUG("maps", "Got WMOAreaTableEntry! flag %u, areaid %u", wmoEntry->Flags, wmoEntry->areaId); - atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId); - } - return IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry); -} - -bool Map::GetAreaInfo(PhaseShift const& phaseShift, float x, float y, float z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const -{ - float vmap_z = z; + float vmapZ; uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, this, x, y); - VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); - if (vmgr->getAreaInfo(terrainMapId, x, y, vmap_z, flags, adtId, rootId, groupId)) - { - // check if there's terrain between player height and object height - if (GridMap* gmap = m_parentTerrainMap->GetGrid(terrainMapId, x, y)) - { - float _mapheight = gmap->getHeight(x, y); - // z + 2.0f condition taken from GetHeight(), not sure if it's such a great choice... - if (z + 2.0f > _mapheight && _mapheight > vmap_z) - return false; - } - return true; - } - return false; -} + bool hasVmapArea = VMAP::VMapFactory::createOrGetVMapManager()->getAreaInfo(terrainMapId, x, y, vmapZ, mogpFlags, adtId, rootId, groupId); + + uint32 gridAreaId = 0; + float gridMapHeight = INVALID_HEIGHT; + if (GridMap* gmap = m_parentTerrainMap->GetGrid(terrainMapId, x, y)) + { + gridAreaId = gmap->getArea(x, y); + gridMapHeight = gmap->getHeight(x, y); + } -uint32 Map::GetAreaId(PhaseShift const& phaseShift, float x, float y, float z, bool *isOutdoors) const -{ - uint32 mogpFlags; - int32 adtId, rootId, groupId; - WMOAreaTableEntry const* wmoEntry = nullptr; - AreaTableEntry const* atEntry = nullptr; - bool haveAreaInfo = false; uint32 areaId = 0; - if (GetAreaInfo(phaseShift, x, y, z, mogpFlags, adtId, rootId, groupId)) + // floor is the height we are closer to (but only if above) + if (hasVmapArea && G3D::fuzzyGe(z, vmapZ - GROUND_HEIGHT_TOLERANCE) && (G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || vmapZ > gridMapHeight)) { - haveAreaInfo = true; - wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId); - if (wmoEntry) - { + // wmo found + if (WMOAreaTableEntry const* wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId)) areaId = wmoEntry->areaId; - atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId); - } + if (!areaId) + areaId = gridAreaId; } + else + areaId = gridAreaId; if (!areaId) - { - if (GridMap* gmap = m_parentTerrainMap->GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) - areaId = gmap->getArea(x, y); + areaId = i_mapEntry->linked_zone; - // this used while not all *.map files generated (instances) - if (!areaId) - areaId = i_mapEntry->linked_zone; - } - - if (isOutdoors) - { - if (haveAreaInfo) - *isOutdoors = IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry); - else - *isOutdoors = true; - } return areaId; } @@ -2566,14 +2549,6 @@ void Map::GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& zoneid = area->zone; } -uint8 Map::GetTerrainType(PhaseShift const& phaseShift, float x, float y) const -{ - if (GridMap* gmap = m_parentTerrainMap->GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) - return gmap->getTerrainType(x, y); - else - return 0; -} - ZLiquidStatus Map::GetLiquidStatus(PhaseShift const& phaseShift, float x, float y, float z, uint8 ReqLiquidType, LiquidData* data) const { ZLiquidStatus result = LIQUID_MAP_NO_WATER; @@ -2582,11 +2557,14 @@ ZLiquidStatus Map::GetLiquidStatus(PhaseShift const& phaseShift, float x, float float ground_level = INVALID_HEIGHT; uint32 liquid_type = 0; uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, this, x, y); - if (vmgr->GetLiquidLevel(terrainMapId, x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type)) + uint32 mogpFlags = 0; + bool useGridLiquid = true; + if (vmgr->GetLiquidLevel(terrainMapId, x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type, mogpFlags)) { + useGridLiquid = !IsInWMOInterior(mogpFlags); 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) + if (liquid_level > ground_level && G3D::fuzzyGe(z, ground_level - GROUND_HEIGHT_TOLERANCE)) { // All ok in water -> store data if (data) @@ -2639,27 +2617,157 @@ ZLiquidStatus Map::GetLiquidStatus(PhaseShift const& phaseShift, float x, float } } - if (GridMap* gmap = m_parentTerrainMap->GetGrid(terrainMapId, x, y)) + if (useGridLiquid) { - LiquidData 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)) + if (GridMap* gmap = m_parentTerrainMap->GetGrid(terrainMapId, x, y)) { - if (data) + LiquidData 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)) { - // hardcoded in client like this - if (GetId() == 530 && map_data.entry == 2) - map_data.entry = 15; + if (data) + { + // hardcoded in client like this + if (GetId() == 530 && map_data.entry == 2) + map_data.entry = 15; - *data = map_data; + *data = map_data; + } + return map_result; } - return map_result; } } return result; } +void Map::GetFullTerrainStatusForPosition(PhaseShift const& phaseShift, float x, float y, float z, PositionFullTerrainStatus& data, uint8 reqLiquidType) const +{ + VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + VMAP::AreaAndLiquidData vmapData; + uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, this, x, y); + GridMap* gmap = m_parentTerrainMap->GetGrid(terrainMapId, x, y); + vmgr->getAreaAndLiquidData(terrainMapId, x, y, z, reqLiquidType, vmapData); + + uint32 gridAreaId = 0; + float gridMapHeight = INVALID_HEIGHT; + if (gmap) + { + gridAreaId = gmap->getArea(x, y); + gridMapHeight = gmap->getHeight(x, y); + } + + bool vmapLocation = false; + bool useGridLiquid = true; + + // floor is the height we are closer to (but only if above) + data.floorZ = VMAP_INVALID_HEIGHT; + if (gridMapHeight > INVALID_HEIGHT && G3D::fuzzyGe(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE)) + data.floorZ = gridMapHeight; + if (vmapData.floorZ > VMAP_INVALID_HEIGHT && + G3D::fuzzyGe(z, vmapData.floorZ - GROUND_HEIGHT_TOLERANCE) && + (G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || vmapData.floorZ > gridMapHeight)) + { + data.floorZ = vmapData.floorZ; + vmapLocation = true; + } + + if (vmapLocation) + { + if (vmapData.areaInfo) + { + data.areaInfo = boost::in_place(vmapData.areaInfo->adtId, vmapData.areaInfo->rootId, vmapData.areaInfo->groupId, vmapData.areaInfo->mogpFlags); + // wmo found + WMOAreaTableEntry const* wmoEntry = GetWMOAreaTableEntryByTripple(vmapData.areaInfo->rootId, vmapData.areaInfo->adtId, vmapData.areaInfo->groupId); + data.outdoors = (vmapData.areaInfo->mogpFlags & 0x8) != 0; + if (wmoEntry) + { + data.areaId = wmoEntry->areaId; + if (wmoEntry->Flags & 4) + data.outdoors = true; + else if (wmoEntry->Flags & 2) + data.outdoors = false; + } + + if (!data.areaId) + data.areaId = gridAreaId; + + useGridLiquid = !IsInWMOInterior(vmapData.areaInfo->mogpFlags); + } + } + else + { + data.outdoors = true; + data.areaId = gridAreaId; + if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(data.areaId)) + data.outdoors = (areaEntry->flags & (AREA_FLAG_INSIDE | AREA_FLAG_OUTSIDE)) != AREA_FLAG_INSIDE; + } + + if (!data.areaId) + data.areaId = i_mapEntry->linked_zone; + + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(data.areaId); + + // liquid processing + data.liquidStatus = LIQUID_MAP_NO_WATER; + if (vmapLocation && vmapData.liquidInfo && vmapData.liquidInfo->level > 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 && useGridLiquid) + { + 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(PhaseShift const& phaseShift, float x, float y) const { if (GridMap* gmap = m_parentTerrainMap->GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index fcf88102a32..0341757dbdd 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -145,6 +145,9 @@ enum ZLiquidStatus : uint32 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 @@ -163,6 +166,25 @@ 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; + bool outdoors; + ZLiquidStatus liquidStatus; + Optional areaInfo; + Optional liquidInfo; +}; + class TC_GAME_API GridMap { uint32 _flags; @@ -195,11 +217,16 @@ class TC_GAME_API GridMap uint8 _liquidOffY; uint8 _liquidWidth; uint8 _liquidHeight; + + uint16* _holes; + bool _fileExists; bool loadAreaData(FILE* in, uint32 offset, uint32 size); bool loadHeightData(FILE* in, uint32 offset, uint32 size); bool loadLiquidData(FILE* in, uint32 offset, uint32 size); + bool loadHolesData(FILE* in, uint32 offset, uint32 size); + bool isHole(int row, int col) const; // Get height functions and pointers typedef float (GridMap::*GetHeightPtr) (float x, float y) const; @@ -218,7 +245,6 @@ public: uint16 getArea(float x, float y) const; 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); bool fileExists() const { return _fileExists; } }; @@ -370,17 +396,10 @@ class TC_GAME_API Map : public GridRefManager // can return INVALID_HEIGHT if under z+2 z coord not found height float GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; + void GetFullTerrainStatusForPosition(PhaseShift const& phaseShift, float x, float y, float z, PositionFullTerrainStatus& data, uint8 reqLiquidType = MAP_ALL_LIQUIDS) const; ZLiquidStatus GetLiquidStatus(PhaseShift const& phaseShift, float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = nullptr) const; - bool GetAreaInfo(PhaseShift const& phaseShift, float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const; - - - uint32 GetAreaId(PhaseShift const& phaseShift, float x, float y, float z, bool *isOutdoors) const; - uint32 GetAreaId(PhaseShift const& phaseShift, float x, float y, float z) const - { - return GetAreaId(phaseShift, x, y, z, nullptr); - } - + uint32 GetAreaId(PhaseShift const& phaseShift, float x, float y, float z) const; uint32 GetAreaId(PhaseShift const& phaseShift, Position const& pos) const { return GetAreaId(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); @@ -398,9 +417,6 @@ class TC_GAME_API Map : public GridRefManager GetZoneAndAreaId(phaseShift, zoneid, areaid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } - bool IsOutdoors(PhaseShift const& phaseShift, float x, float y, float z) const; - - uint8 GetTerrainType(PhaseShift const& phaseShift, float x, float y) const; float GetWaterLevel(PhaseShift const& phaseShift, float x, float y) const; bool IsInWater(PhaseShift const& phaseShift, float x, float y, float z, LiquidData* data = nullptr) const; bool IsUnderWater(PhaseShift const& phaseShift, float x, float y, float z) const; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index fdd81766f14..26aba208b73 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -22,6 +22,8 @@ #include "Define.h" #include "DetourNavMesh.h" +float const GROUND_HEIGHT_TOLERANCE = 0.05f; // Extra tolerance to z position to check if it is in air or on ground. + enum SpellEffIndex : uint8 { EFFECT_0 = 0, diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index a9d9580bfa4..7ad68bd7725 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5095,11 +5095,11 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_caster->GetTypeId() == TYPEID_PLAYER && VMAP::VMapFactory::createOrGetVMapManager()->isLineOfSightCalcEnabled()) { if (m_spellInfo->HasAttribute(SPELL_ATTR0_OUTDOORS_ONLY) && - !m_caster->GetMap()->IsOutdoors(m_caster->GetPhaseShift(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ())) + !m_caster->IsOutdoors()) return SPELL_FAILED_ONLY_OUTDOORS; if (m_spellInfo->HasAttribute(SPELL_ATTR0_INDOORS_ONLY) && - m_caster->GetMap()->IsOutdoors(m_caster->GetPhaseShift(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ())) + !m_caster->IsOutdoors()) return SPELL_FAILED_ONLY_INDOORS; } diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 130f27b3da6..a278b7c0f6e 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -265,7 +265,7 @@ public: if (haveVMap) { - if (map->IsOutdoors(object->GetPhaseShift(), object->GetPositionX(), object->GetPositionY(), object->GetPositionZ())) + if (object->IsOutdoors()) handler->PSendSysMessage(LANG_GPS_POSITION_OUTDOORS); else handler->PSendSysMessage(LANG_GPS_POSITION_INDOORS);