diff --git a/src/common/Collision/DynamicTree.cpp b/src/common/Collision/DynamicTree.cpp index aedf3e746..66eb25e4f 100644 --- a/src/common/Collision/DynamicTree.cpp +++ b/src/common/Collision/DynamicTree.cpp @@ -8,9 +8,14 @@ #include "BoundingIntervalHierarchyWrapper.h" #include "GameObjectModel.h" #include "Log.h" +#include "MapTree.h" #include "ModelInstance.h" #include "RegularGrid.h" #include "Timer.h" +#include "VMapFactory.h" +#include "VMapManager2.h" +#include "WorldModel.h" + #include #include #include @@ -145,6 +150,52 @@ struct DynamicTreeIntersectionCallback bool didHit() const { return did_hit;} }; +struct DynamicTreeAreaInfoCallback +{ + DynamicTreeAreaInfoCallback(uint32 phaseMask) + : _phaseMask(phaseMask) {} + + void operator()(G3D::Vector3 const& p, GameObjectModel const& obj) + { + obj.IntersectPoint(p, _areaInfo, _phaseMask); + } + + VMAP::AreaInfo const& GetAreaInfo() const + { + return _areaInfo; + } + +private: + uint32 _phaseMask; + VMAP::AreaInfo _areaInfo; +}; + +struct DynamicTreeLocationInfoCallback +{ + DynamicTreeLocationInfoCallback(uint32 phaseMask) + : _phaseMask(phaseMask), _hitModel(nullptr) {} + + void operator()(G3D::Vector3 const& p, GameObjectModel const& obj) + { + if (obj.GetLocationInfo(p, _locationInfo, _phaseMask)) + _hitModel = &obj; + } + + VMAP::LocationInfo& GetLocationInfo() + { + return _locationInfo; + } + GameObjectModel const* GetHitModel() const + { + return _hitModel; + } + +private: + uint32 _phaseMask; + VMAP::LocationInfo _locationInfo; + GameObjectModel const* _hitModel; +}; + bool DynamicMapTree::GetIntersectionTime(const uint32 phasemask, const G3D::Ray& ray, const G3D::Vector3& endPos, float& maxDist) const { @@ -238,3 +289,41 @@ float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, return -G3D::finf(); } } + +bool DynamicMapTree::GetAreaInfo(float x, float y, float& z, uint32 phasemask, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const +{ + G3D::Vector3 v(x, y, z + 0.5f); + DynamicTreeAreaInfoCallback intersectionCallBack(phasemask); + impl->intersectPoint(v, intersectionCallBack); + if (intersectionCallBack.GetAreaInfo().result) + { + flags = intersectionCallBack.GetAreaInfo().flags; + adtId = intersectionCallBack.GetAreaInfo().adtId; + rootId = intersectionCallBack.GetAreaInfo().rootId; + groupId = intersectionCallBack.GetAreaInfo().groupId; + z = intersectionCallBack.GetAreaInfo().ground_Z; + return true; + } + return false; +} + +void DynamicMapTree::GetAreaAndLiquidData(float x, float y, float z, uint32 phasemask, uint8 reqLiquidType, VMAP::AreaAndLiquidData& data) const +{ + G3D::Vector3 v(x, y, z + 0.5f); + DynamicTreeLocationInfoCallback intersectionCallBack(phasemask); + impl->intersectPoint(v, intersectionCallBack); + if (intersectionCallBack.GetLocationInfo().hitModel) + { + data.floorZ = intersectionCallBack.GetLocationInfo().ground_Z; + uint32 liquidType = intersectionCallBack.GetLocationInfo().hitModel->GetLiquidType(); + float liquidLevel; + if (!reqLiquidType || (dynamic_cast(VMAP::VMapFactory::createOrGetVMapManager())->GetLiquidFlagsPtr(liquidType) & reqLiquidType)) + if (intersectionCallBack.GetHitModel()->GetLiquidLevel(v, intersectionCallBack.GetLocationInfo(), liquidLevel)) + data.liquidInfo.emplace(liquidType, liquidLevel); + + data.areaInfo.emplace(0, + intersectionCallBack.GetLocationInfo().rootId, + intersectionCallBack.GetLocationInfo().hitModel->GetWmoID(), + intersectionCallBack.GetLocationInfo().hitModel->GetMogpFlags()); + } +} diff --git a/src/common/Collision/DynamicTree.h b/src/common/Collision/DynamicTree.h index ae59e696f..aaa98a193 100644 --- a/src/common/Collision/DynamicTree.h +++ b/src/common/Collision/DynamicTree.h @@ -15,6 +15,11 @@ namespace G3D class Vector3; } +namespace VMAP +{ + struct AreaAndLiquidData; +} + class GameObjectModel; struct DynTreeImpl; @@ -32,6 +37,9 @@ public: bool GetIntersectionTime(uint32 phasemask, const G3D::Ray& ray, const G3D::Vector3& endPos, float& maxDist) const; + bool GetAreaInfo(float x, float y, float& z, uint32 phasemask, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const; + void GetAreaAndLiquidData(float x, float y, float z, uint32 phasemask, uint8 reqLiquidType, VMAP::AreaAndLiquidData& data) const; + bool GetObjectHitPos(uint32 phasemask, const G3D::Vector3& pPos1, const G3D::Vector3& pPos2, G3D::Vector3& pResultHitPos, float pModifyDist) const; diff --git a/src/common/Collision/Management/IVMapManager.h b/src/common/Collision/Management/IVMapManager.h index bc54eeb3e..c2ae897bf 100644 --- a/src/common/Collision/Management/IVMapManager.h +++ b/src/common/Collision/Management/IVMapManager.h @@ -8,6 +8,7 @@ #define _IVMAPMANAGER_H #include "Define.h" +#include "Optional.h" #include //=========================================================== @@ -25,8 +26,33 @@ namespace VMAP VMAP_LOAD_RESULT_IGNORED }; -#define VMAP_INVALID_HEIGHT -100000.0f // for check -#define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case + #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 IVMapManager @@ -79,8 +105,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 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, uint32& mogpFlags) 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 2794a8c40..11a1dbe66 100644 --- a/src/common/Collision/Management/VMapManager2.cpp +++ b/src/common/Collision/Management/VMapManager2.cpp @@ -255,7 +255,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 defined(ENABLE_VMAP_CHECKS) if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG)) @@ -275,7 +275,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 defined(ENABLE_VMAP_CHECKS) if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LIQUIDSTATUS)) @@ -291,6 +291,7 @@ namespace VMAP floor = info.ground_Z; ASSERT(floor < std::numeric_limits::max()); type = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc + mogpFlags = info.hitModel->GetMogpFlags(); if (reqLiquidType && !(GetLiquidFlagsPtr(type) & reqLiquidType)) { return false; @@ -306,6 +307,38 @@ 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.emplace(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.emplace(liquidType, liquidLevel); + + if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG)) + data.areaInfo.emplace(info.hitInstance->adtId, info.rootId, info.hitModel->GetWmoID(), info.hitModel->GetMogpFlags()); + } + } + } + WorldModel* VMapManager2::acquireModelInstance(const std::string& basepath, const std::string& filename) { //! Critical section, thread safe access to iLoadedModelFiles diff --git a/src/common/Collision/Management/VMapManager2.h b/src/common/Collision/Management/VMapManager2.h index a31526ef9..4fd65a7e2 100644 --- a/src/common/Collision/Management/VMapManager2.h +++ b/src/common/Collision/Management/VMapManager2.h @@ -117,8 +117,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 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, 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); void releaseModelInstance(const std::string& filename); diff --git a/src/common/Collision/Maps/MapTree.h b/src/common/Collision/Maps/MapTree.h index c910f3abc..7059be8f6 100644 --- a/src/common/Collision/Maps/MapTree.h +++ b/src/common/Collision/Maps/MapTree.h @@ -23,6 +23,7 @@ namespace VMAP const ModelInstance* hitInstance{nullptr}; const GroupModel* hitModel{nullptr}; float ground_Z; + int32 rootId = -1; }; class StaticMapTree diff --git a/src/common/Collision/Models/GameObjectModel.cpp b/src/common/Collision/Models/GameObjectModel.cpp index 7bab30d90..5ca1b64a3 100644 --- a/src/common/Collision/Models/GameObjectModel.cpp +++ b/src/common/Collision/Models/GameObjectModel.cpp @@ -6,6 +6,7 @@ #include "GameObjectModel.h" #include "Log.h" +#include "MapTree.h" #include "Timer.h" #include "VMapDefinitions.h" #include "VMapFactory.h" @@ -146,6 +147,7 @@ bool GameObjectModel::initialize(std::unique_ptr model #endif owner = std::move(modelOwner); + isWmo = it->second.isWmo; return true; } @@ -187,6 +189,69 @@ bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool Sto return hit; } +void GameObjectModel::IntersectPoint(G3D::Vector3 const& point, VMAP::AreaInfo& info, uint32 ph_mask) const +{ + if (!(phasemask & ph_mask) || !owner->IsSpawned() || !IsMapObject()) + return; + + if (!iBound.contains(point)) + return; + + // child bounds are defined in object space: + Vector3 pModel = iInvRot * (point - iPos) * iInvScale; + Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f); + float zDist; + if (iModel->IntersectPoint(pModel, zDirModel, zDist, info)) + { + Vector3 modelGround = pModel + zDist * zDirModel; + float world_Z = ((modelGround * iInvRot) * iScale + iPos).z; + if (info.ground_Z < world_Z) + info.ground_Z = world_Z; + } +} + +bool GameObjectModel::GetLocationInfo(G3D::Vector3 const& point, VMAP::LocationInfo& info, uint32 ph_mask) const +{ + if (!(phasemask & ph_mask) || !owner->IsSpawned() || !IsMapObject()) + return false; + + if (!iBound.contains(point)) + return false; + + // child bounds are defined in object space: + Vector3 pModel = iInvRot * (point - iPos) * iInvScale; + Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f); + float zDist; + if (iModel->GetLocationInfo(pModel, zDirModel, zDist, info)) + { + Vector3 modelGround = pModel + zDist * zDirModel; + float world_Z = ((modelGround * iInvRot) * iScale + iPos).z; + if (info.ground_Z < world_Z) + { + info.ground_Z = world_Z; + return true; + } + } + + return false; +} + +bool GameObjectModel::GetLiquidLevel(G3D::Vector3 const& point, VMAP::LocationInfo& info, float& liqHeight) const +{ + // child bounds are defined in object space: + Vector3 pModel = iInvRot * (point - iPos) * iInvScale; + //Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f); + float zDist; + if (info.hitModel->GetLiquidLevel(pModel, zDist)) + { + // calculate world height (zDist in model coords): + // assume WMO not tilted (wouldn't make much sense anyway) + liqHeight = zDist * iScale + iPos.z; + return true; + } + return false; +} + bool GameObjectModel::UpdatePosition() { if (!iModel) diff --git a/src/common/Collision/Models/GameObjectModel.h b/src/common/Collision/Models/GameObjectModel.h index 7378dfbae..21331ce97 100644 --- a/src/common/Collision/Models/GameObjectModel.h +++ b/src/common/Collision/Models/GameObjectModel.h @@ -16,6 +16,8 @@ namespace VMAP { class WorldModel; + struct AreaInfo; + struct LocationInfo; } class GameObject; @@ -37,7 +39,7 @@ public: class GameObjectModel { - GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(nullptr) { } + GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(nullptr), isWmo(false) { } public: std::string name; @@ -53,8 +55,12 @@ public: void enable(uint32 ph_mask) { phasemask = ph_mask; } [[nodiscard]] bool isEnabled() const { return phasemask != 0; } + [[nodiscard]] bool IsMapObject() const { return isWmo; } bool intersectRay(const G3D::Ray& Ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask) const; + void IntersectPoint(G3D::Vector3 const& point, VMAP::AreaInfo& info, uint32 ph_mask) const; + bool GetLocationInfo(G3D::Vector3 const& point, VMAP::LocationInfo& info, uint32 ph_mask) const; + bool GetLiquidLevel(G3D::Vector3 const& point, VMAP::LocationInfo& info, float& liqHeight) const; static GameObjectModel* Create(std::unique_ptr modelOwner, std::string const& dataPath); @@ -71,6 +77,7 @@ private: float iScale; VMAP::WorldModel* iModel; std::unique_ptr owner; + bool isWmo; }; void LoadGameObjectModelList(std::string const& dataPath); diff --git a/src/common/Collision/Models/WorldModel.cpp b/src/common/Collision/Models/WorldModel.cpp index c7e0e0fc4..7f974a00e 100644 --- a/src/common/Collision/Models/WorldModel.cpp +++ b/src/common/Collision/Models/WorldModel.cpp @@ -412,7 +412,6 @@ namespace VMAP { return false; } - GModelRayCallback callback(triangles, vertices); Vector3 rPos = pos - 0.1f * down; float dist = G3D::inf(); G3D::Ray ray(rPos, down); @@ -554,6 +553,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/Battlefield/Zones/BattlefieldWG.cpp b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp index 53bff2e09..4a96e72f4 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp @@ -531,7 +531,7 @@ void BattlefieldWG::OnCreatureCreate(Creature* creature) case NPC_TAUNKA_SPIRIT_GUIDE: { TeamId teamId = (creature->GetEntry() == NPC_DWARVEN_SPIRIT_GUIDE ? TEAM_ALLIANCE : TEAM_HORDE); - uint8 graveyardId = GetSpiritGraveyardId(creature->GetAreaId(true)); + uint8 graveyardId = GetSpiritGraveyardId(creature->GetAreaId()); // xinef: little workaround, there are 2 spirit guides in same area if (creature->IsWithinDist2d(5103.0f, 3461.5f, 5.0f)) graveyardId = BATTLEFIELD_WG_GY_WORKSHOP_NW; diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 5c9498486..238094720 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -1004,7 +1004,7 @@ namespace lfg currInternalInfoMap[sitr->first] = RBInternalInfo(guid, sitr->second.comment, !groupGuid.IsEmpty(), groupGuid, sitr->second.roles, encounterMask, instanceGuid, 1, p->getLevel(), p->getClass(), p->getRace(), p->GetAverageItemLevel(), - talents, p->m_last_area_id, p->GetArmor(), (uint32)std::max(0, spellDamage), (uint32)std::max(0, spellHeal), + talents, p->GetAreaId(), p->GetArmor(), (uint32)std::max(0, spellDamage), (uint32)std::max(0, spellHeal), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast(CR_CRIT_MELEE)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast(CR_CRIT_RANGED)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast(CR_CRIT_SPELL)), std::max(0.0f, mp5), std::max(0.0f, mp5combat), std::max(baseAP, rangedAP), (uint32)p->GetStat(STAT_AGILITY), p->GetMaxHealth(), maxPower, p->GetDefenseSkillValue(), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast(CR_DODGE)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast(CR_BLOCK)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast(CR_PARRY)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast(CR_HASTE_SPELL)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast(CR_EXPERTISE))); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index e384c1ed5..f816bcb06 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -519,8 +519,6 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele SetControlled(true, UNIT_STATE_ROOT); } - UpdateEnvironmentIfNeeded(3); - SetDetectionDistance(cInfo->detection_range); LoadSpellTemplateImmunity(); @@ -959,16 +957,23 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u //! returning correct zone id for selecting OutdoorPvP/Battlefield script Relocate(x, y, z, ang); - //oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0; - if (!CreateFromProto(guidlow, Entry, vehId, data)) - return false; - if (!IsPositionValid()) { LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow %d, entry %d) are not valid (X: %f, Y: %f, Z: %f, O: %f)", guidlow, Entry, x, y, z, ang); return false; } + // area/zone id is needed immediately for ZoneScript::GetCreatureEntry hook before it is known which creature template to load (no model/scale available yet) + PositionFullTerrainStatus terrainData; + GetMap()->GetFullTerrainStatusForPosition(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ(), DEFAULT_COLLISION_HEIGHT, terrainData); + ProcessPositionDataChanged(terrainData); + + //oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0; + if (!CreateFromProto(guidlow, Entry, vehId, data)) + return false; + + UpdateMovementFlags(); + switch (GetCreatureTemplate()->rank) { case CREATURE_ELITE_RARE: @@ -1812,6 +1817,8 @@ void Creature::setDeathState(DeathState s, bool despawn) if (HasUnitMovementFlag(MOVEMENTFLAG_FALLING)) RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING); + UpdateMovementFlags(); + SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag); ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~(UNIT_STATE_IGNORE_PATHFINDING | UNIT_STATE_NO_ENVIRONMENT_UPD))); SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool)); @@ -1822,8 +1829,6 @@ void Creature::setDeathState(DeathState s, bool despawn) LoadCreaturesAddon(true); if (GetCreatureData() && GetPhaseMask() != GetCreatureData()->phaseMask) SetPhaseMask(GetCreatureData()->phaseMask, false); - - UpdateEnvironmentIfNeeded(3); } } @@ -2866,16 +2871,13 @@ void Creature::applyInhabitFlags() if (IsLevitating()) { SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_FLY); - return; } - - if (IsHovering()) + else if (IsHovering()) { SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_HOVER); - return; } - - SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_GROUND); + else + SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_GROUND); } } @@ -3042,6 +3044,84 @@ float Creature::GetAggroRange(Unit const* target) const return (aggroRadius * aggroRate); } +void Creature::UpdateMovementFlags() +{ + // Do not update movement flags if creature is controlled by a player (charm/vehicle) + if (m_movedByPlayer) + return; + + CreatureTemplate const* info = GetCreatureTemplate(); + if (!info) + return; + + // Creatures with CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE should control MovementFlags in your own scripts + if (info->flags_extra & CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE) + return; + + float z = GetPositionZ(); + float ground = GetFloorZ(); + + bool isInAir = false; + bool Swim = false; + + bool canHover = CanHover(); + + LiquidData const& liquidData = GetLiquidData(); + if (liquidData.Status == LIQUID_MAP_NO_WATER) + { + if (ground > INVALID_HEIGHT) + isInAir = G3D::fuzzyGt(z, ground + (canHover ? GetFloatValue(UNIT_FIELD_HOVERHEIGHT) : 0.0f) + GROUND_HEIGHT_TOLERANCE) || G3D::fuzzyLt(z, ground - GROUND_HEIGHT_TOLERANCE); // Can be underground too, prevent the falling + else + isInAir = true; + } + else + { + switch (liquidData.Status) + { + case LIQUID_MAP_ABOVE_WATER: + isInAir = true; + break; + case LIQUID_MAP_WATER_WALK: + isInAir = true; + [[fallthrough]]; + case LIQUID_MAP_IN_WATER: + Swim = z - liquidData.DepthLevel > GetCollisionHeight() * 0.75f; // Shallow water at ~75% of collision height + break; + case LIQUID_MAP_UNDER_WATER: + Swim = true; + break; + default: + break; + } + } + + SetSwim(CanSwim() && Swim); + + if (info->InhabitType & INHABIT_AIR) + { + if (isInAir && !IsFalling()) + { + if (info->InhabitType & INHABIT_GROUND) + SetCanFly(true); + else + SetDisableGravity(true); + + if (!HasAuraType(SPELL_AURA_HOVER)) + SetHover(false); + } + else + { + SetCanFly(false); + SetDisableGravity(false); + + if (IsAlive() && (CanHover() || HasAuraType(SPELL_AURA_HOVER))) + SetHover(true); + } + } + else if (!HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY) && IsAlive() && (CanHover() || HasAuraType(SPELL_AURA_HOVER))) + SetHover(true); +} + void Creature::SetObjectScale(float scale) { Unit::SetObjectScale(scale); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 5bc4ad5c2..304ef11aa 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -151,6 +151,8 @@ public: [[nodiscard]] bool HasSpell(uint32 spellID) const override; + void UpdateMovementFlags(); + bool UpdateEntry(uint32 entry, const CreatureData* data = nullptr, bool changelevel = true ); bool UpdateStats(Stats stat) override; bool UpdateAllStats() override; diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index dc0e2b48b..a42a12845 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -40,7 +40,7 @@ enum CreatureFlagsExtra : uint32 CREATURE_FLAG_EXTRA_NO_XP = 0x00000040, // creature kill does not provide XP CREATURE_FLAG_EXTRA_TRIGGER = 0x00000080, // trigger creature CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and 'attack me' effects - CREATURE_FLAG_EXTRA_UNUSED_10 = 0x00000200, // TODO: Implement CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE (creature won't update movement flags) + CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE = 0x00000200, // creature won't update movement flags CREATURE_FLAG_EXTRA_GHOST_VISIBILITY = 0x00000400, // creature will only be visible to dead players CREATURE_FLAG_EXTRA_UNUSED_12 = 0x00000800, // TODO: Implement CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK (creature will use offhand attacks) CREATURE_FLAG_EXTRA_NO_SELL_VENDOR = 0x00001000, // players can't sell items to this vendor @@ -65,8 +65,7 @@ enum CreatureFlagsExtra : uint32 CREATURE_FLAG_EXTRA_UNUSED_32 = 0x80000000, // Masks - CREATURE_FLAG_EXTRA_UNUSED = (CREATURE_FLAG_EXTRA_UNUSED_10 | CREATURE_FLAG_EXTRA_UNUSED_12 | - CREATURE_FLAG_EXTRA_UNUSED_25 | CREATURE_FLAG_EXTRA_UNUSED_26 | + CREATURE_FLAG_EXTRA_UNUSED = (CREATURE_FLAG_EXTRA_UNUSED_12 | CREATURE_FLAG_EXTRA_UNUSED_25 | CREATURE_FLAG_EXTRA_UNUSED_26 | CREATURE_FLAG_EXTRA_UNUSED_27 | CREATURE_FLAG_EXTRA_UNUSED_28 | CREATURE_FLAG_EXTRA_UNUSED_32), CREATURE_FLAG_EXTRA_DB_ALLOWED = (0xFFFFFFFF & ~(CREATURE_FLAG_EXTRA_UNUSED | CREATURE_FLAG_EXTRA_DUNGEON_BOSS)) }; diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp index fd796800e..19f5e47e5 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -97,6 +97,8 @@ bool DynamicObject::CreateDynamicObject(ObjectGuid::LowType guidlow, Unit* caste WorldObject::_Create(guidlow, HighGuid::DynamicObject, caster->GetPhaseMask()); + UpdatePositionData(); + SetEntry(spellId); SetObjectScale(1); SetGuidValue(DYNAMICOBJECT_CASTER, caster->GetGUID()); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index d777a2a81..cdb813721 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -241,6 +241,8 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u SetPhaseMask(phaseMask, false); + UpdatePositionData(); + SetZoneScript(); if (m_zoneScript) { diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 87b0bb428..31902d4d4 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -950,8 +950,8 @@ WorldObject::WorldObject(bool isWorldObject) : WorldLocation(), elunaEvents(nullptr), #endif LastUsedScriptID(0), m_name(""), m_isActive(false), m_isVisibilityDistanceOverride(false), m_isWorldObject(isWorldObject), m_zoneScript(nullptr), - m_staticFloorZ(INVALID_HEIGHT), m_transport(nullptr), m_currMap(nullptr), m_InstanceId(0), - m_phaseMask(PHASEMASK_NORMAL), m_useCombinedPhases(true), m_notifyflags(0), m_executed_notifies(0) + _zoneId(0), _areaId(0), _floorZ(INVALID_HEIGHT), _outdoors(false), _liquidData(), _updatePositionData(false), m_transport(nullptr), + m_currMap(nullptr), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), m_useCombinedPhases(true), m_notifyflags(0), m_executed_notifies(0) { m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST); m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE); @@ -1040,19 +1040,51 @@ void WorldObject::_Create(ObjectGuid::LowType guidlow, HighGuid guidhigh, uint32 SetPhaseMask(phaseMask, false); } -uint32 WorldObject::GetZoneId(bool /*forceRecalc*/) const +void WorldObject::SetPositionDataUpdate() { - return GetBaseMap()->GetZoneId(m_positionX, m_positionY, m_positionZ); + _updatePositionData = true; + + // Calls immediately for charmed units + if (GetTypeId() == TYPEID_UNIT && ToUnit()->IsCharmedOwnedByPlayerOrPlayer()) + UpdatePositionData(); } -uint32 WorldObject::GetAreaId(bool /*forceRecalc*/) const +void WorldObject::UpdatePositionData() { - return GetBaseMap()->GetAreaId(m_positionX, m_positionY, m_positionZ); + _updatePositionData = false; + + PositionFullTerrainStatus data; + GetMap()->GetFullTerrainStatusForPosition(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ(), GetCollisionHeight(), data); + ProcessPositionDataChanged(data); } -void WorldObject::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool /*forceRecalc*/) const +void WorldObject::ProcessPositionDataChanged(PositionFullTerrainStatus const& data) { - GetBaseMap()->GetZoneAndAreaId(zoneid, areaid, m_positionX, m_positionY, m_positionZ); + _zoneId = _areaId = data.areaId; + + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(_areaId)) + if (area->zone) + _zoneId = area->zone; + + _outdoors = data.outdoors; + _floorZ = data.floorZ; + _liquidData = data.liquidInfo; +} + +void WorldObject::AddToWorld() +{ + Object::AddToWorld(); + GetMap()->GetZoneAndAreaId(GetPhaseMask(), _zoneId, _areaId, GetPositionX(), GetPositionY(), GetPositionZ()); +} + +void WorldObject::RemoveFromWorld() +{ + if (!IsInWorld()) + return; + + DestroyForNearbyPlayers(); + + Object::RemoveFromWorld(); } InstanceScript* WorldObject::GetInstanceScript() @@ -1470,7 +1502,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z, float* grou if (max_z > INVALID_HEIGHT) { - if (canSwim && unit->GetMap()->IsInWater(x, y, max_z - Z_OFFSET_FIND_HEIGHT)) + if (canSwim && unit->GetMap()->IsInWater(unit->GetPhaseMask(), x, y, max_z - Z_OFFSET_FIND_HEIGHT, unit->GetCollisionHeight())) { // do not allow creatures to walk on // water level while swimming @@ -2137,12 +2169,6 @@ void WorldObject::ResetMap() //m_InstanceId = 0; } -Map const* WorldObject::GetBaseMap() const -{ - ASSERT(m_currMap); - return m_currMap->GetParent(); -} - void WorldObject::AddObjectToRemoveList() { ASSERT(m_uint32Values); @@ -2307,7 +2333,7 @@ void WorldObject::SetZoneScript() m_zoneScript = (ZoneScript*)map->ToInstanceMap()->GetInstanceScript(); else if (!map->IsBattlegroundOrArena()) { - uint32 zoneId = GetZoneId(true); + uint32 zoneId = GetZoneId(); if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(zoneId)) m_zoneScript = bf; else @@ -3031,10 +3057,54 @@ float WorldObject::GetMapWaterOrGroundLevel(float x, float y, float z, float* gr float WorldObject::GetFloorZ() const { - if (!IsInWorld()) - return m_staticFloorZ; + if (_updatePositionData) + const_cast(this)->UpdatePositionData(); - return std::max(m_staticFloorZ, GetMap()->GetGameObjectFloor(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ() + std::max(GetCollisionHeight(), Z_OFFSET_FIND_HEIGHT))); + if (!IsInWorld()) + return _floorZ; + + return std::max(_floorZ, GetMap()->GetGameObjectFloor(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ() + std::max(GetCollisionHeight(), Z_OFFSET_FIND_HEIGHT))); +} + +uint32 WorldObject::GetZoneId() const +{ + if (_updatePositionData) + const_cast(this)->UpdatePositionData(); + + return _zoneId; +} + +uint32 WorldObject::GetAreaId() const +{ + if (_updatePositionData) + const_cast(this)->UpdatePositionData(); + + return _areaId; +} + +void WorldObject::GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const +{ + if (_updatePositionData) + const_cast(this)->UpdatePositionData(); + + zoneid = _zoneId; + areaid = _areaId; +} + +bool WorldObject::IsOutdoors() const +{ + if (_updatePositionData) + const_cast(this)->UpdatePositionData(); + + return _outdoors; +} + +LiquidData const& WorldObject::GetLiquidData() const +{ + if (_updatePositionData) + const_cast(this)->UpdatePositionData(); + + return _liquidData; } void WorldObject::AddAllowedLooter(ObjectGuid guid) diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 3b316eb96..0ab4fafde 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -69,6 +69,8 @@ class Transport; class StaticTransport; class MotionTransport; +struct PositionFullTerrainStatus; + typedef std::unordered_map UpdateDataMapType; typedef GuidUnorderedSet UpdatePlayerSet; @@ -750,15 +752,8 @@ public: #endif void _Create(ObjectGuid::LowType guidlow, HighGuid guidhigh, uint32 phaseMask); - void RemoveFromWorld() override - { - if (!IsInWorld()) - return; - - DestroyForNearbyPlayers(); - - Object::RemoveFromWorld(); - } + void AddToWorld() override; + void RemoveFromWorld() override; #ifdef ELUNA ElunaEventProcessor* elunaEvents; @@ -817,9 +812,11 @@ public: bool InSamePhase(WorldObject const* obj) const { return InSamePhase(obj->GetPhaseMask()); } [[nodiscard]] bool InSamePhase(uint32 phasemask) const { return m_useCombinedPhases ? GetPhaseMask() & phasemask : GetPhaseMask() == phasemask; } - [[nodiscard]] virtual uint32 GetZoneId(bool forceRecalc = false) const; - [[nodiscard]] virtual uint32 GetAreaId(bool forceRecalc = false) const; - virtual void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc = false) const; + [[nodiscard]] uint32 GetZoneId() const; + [[nodiscard]] uint32 GetAreaId() const; + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const; + [[nodiscard]] bool IsOutdoors() const; + LiquidData const& GetLiquidData() const; InstanceScript* GetInstanceScript(); @@ -948,9 +945,6 @@ public: [[nodiscard]] Map* FindMap() const { return m_currMap; } //used to check all object's GetMap() calls when object is not in world! - //this function should be removed in nearest time... - [[nodiscard]] Map const* GetBaseMap() const; - void SetZoneScript(); void ClearZoneScript(); [[nodiscard]] ZoneScript* GetZoneScript() const { return m_zoneScript; } @@ -984,6 +978,9 @@ public: void BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet& player_set) override; void GetCreaturesWithEntryInRange(std::list& creatureList, float radius, uint32 entry); + void SetPositionDataUpdate(); + void UpdatePositionData(); + void AddToObjectUpdate() override; void RemoveFromObjectUpdate() override; @@ -1060,7 +1057,13 @@ protected: const bool m_isWorldObject; ZoneScript* m_zoneScript; - float m_staticFloorZ; + virtual void ProcessPositionDataChanged(PositionFullTerrainStatus const& data); + uint32 _zoneId; + uint32 _areaId; + float _floorZ; + bool _outdoors; + LiquidData _liquidData; + bool _updatePositionData; // transports Transport* m_transport; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 047f65034..0bbc8db4e 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -666,6 +666,8 @@ bool Pet::CreateBaseAtCreature(Creature* creature) SetDisplayId(creature->GetDisplayId()); + UpdatePositionData(); + if (CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family)) SetName(cFamily->Name[sWorld->GetDefaultDbcLocale()]); else @@ -684,6 +686,8 @@ bool Pet::CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner) Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ(), owner->GetOrientation()); + UpdatePositionData(); + return true; } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 8b2e5a6b6..c996e44a5 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -591,6 +591,8 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo InitTalentForLevel(); InitPrimaryProfessions(); // to max set before any spell added + UpdatePositionData(); + // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods() UpdateMaxHealth(); // Update max Health (for add bonus from stamina) SetFullHealth(); @@ -876,7 +878,7 @@ void Player::HandleDrowning(uint32 time_diff) } // In dark water - if (m_MirrorTimerFlags & UNDERWARER_INDARKWATER) + if (m_MirrorTimerFlags & UNDERWATER_INDARKWATER) { // Fatigue timer not activated - activate it if (m_MirrorTimer[FATIGUE_TIMER] == DISABLED_MIRROR_TIMER) @@ -899,7 +901,7 @@ void Player::HandleDrowning(uint32 time_diff) else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) // Teleport ghost to graveyard RepopAtGraveyard(); } - else if (!(m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER)) + else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER)) SendMirrorTimer(FATIGUE_TIMER, getMaxTimer(FATIGUE_TIMER), m_MirrorTimer[FATIGUE_TIMER], -1); } } @@ -909,7 +911,7 @@ void Player::HandleDrowning(uint32 time_diff) m_MirrorTimer[FATIGUE_TIMER] += 10 * time_diff; if (m_MirrorTimer[FATIGUE_TIMER] >= DarkWaterTime || !IsAlive()) StopMirrorTimer(FATIGUE_TIMER); - else if (m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER) + else if (m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER) SendMirrorTimer(FATIGUE_TIMER, DarkWaterTime, m_MirrorTimer[FATIGUE_TIMER], 10); } @@ -2066,31 +2068,6 @@ GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTy return nullptr; } -bool Player::IsInWater(bool allowAbove) const -{ - if (m_isInWater || !allowAbove) - return m_isInWater; - - float distsq = GetExactDistSq(&m_last_environment_position); - if (distsq < 3.0f * 3.0f) - return m_last_islittleabovewater_status; - else - { - LiquidData liqData; - liqData.level = INVALID_HEIGHT; - const_cast(&m_last_environment_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ()); - bool inWater = GetBaseMap()->IsInWater(GetPositionX(), GetPositionY(), GetPositionZ(), &liqData); - *(const_cast(&m_last_islittleabovewater_status)) = inWater || (liqData.level > INVALID_HEIGHT && liqData.level > liqData.depth_level && liqData.level <= GetPositionZ() + 3.0f && liqData.level > GetPositionZ() - 1.0f); - return m_last_islittleabovewater_status; - } -} - -bool Player::IsUnderWater() const -{ - return IsInWater() && - GetPositionZ() < GetBaseMap()->GetWaterLevel(GetPositionX(), GetPositionY()) - GetCollisionHeight(); -} - bool Player::IsFalling() const { // Xinef: Added !IsInFlight check @@ -4355,7 +4332,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness) // trigger update zone for alive state zone updates uint32 newzone, newarea; - GetZoneAndAreaId(newzone, newarea, true); + GetZoneAndAreaId(newzone, newarea); UpdateZone(newzone, newarea); sOutdoorPvPMgr->HandlePlayerResurrects(this, newzone); @@ -4508,6 +4485,8 @@ Corpse* Player::CreateCorpse() // register for player, but not show GetMap()->AddCorpse(corpse); + UpdatePositionData(); + // we do not need to save corpses for BG/arenas if (!GetMap()->IsBattlegroundOrArena()) corpse->SaveToDB(); @@ -5563,7 +5542,7 @@ void Player::CheckAreaExploreAndOutdoor() return; bool isOutdoor = IsOutdoors(); - uint32 areaId = GetBaseMap()->GetAreaId(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor); + uint32 areaId = GetAreaId(); AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !isOutdoor) @@ -6159,7 +6138,7 @@ uint32 Player::GetZoneIdFromDB(ObjectGuid guid) if (!sMapStore.LookupEntry(map)) return 0; - zone = sMapMgr->GetZoneId(map, posx, posy, posz); + zone = sMapMgr->GetZoneId(PHASEMASK_NORMAL, map, posx, posy, posz); if (zone > 0) { @@ -6184,36 +6163,6 @@ uint32 Player::GetLevelFromStorage(ObjectGuid::LowType guid) return 0; } -uint32 Player::GetZoneId(bool forceRecalc) const -{ - if (forceRecalc) - *(const_cast(&m_last_zone_id)) = WorldObject::GetZoneId(); - - return m_last_zone_id; -} - -uint32 Player::GetAreaId(bool forceRecalc) const -{ - if (forceRecalc) - *(const_cast(&m_last_area_id)) = WorldObject::GetAreaId(); - - return m_last_area_id; -} - -void Player::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc) const -{ - if (forceRecalc) - { - WorldObject::GetZoneAndAreaId(zoneid, areaid); - *(const_cast(&m_last_zone_id)) = zoneid; - *(const_cast(&m_last_area_id)) = areaid; - return; - } - - zoneid = m_last_zone_id; - areaid = m_last_area_id; -} - //If players are too far away from the duel flag... they lose the duel void Player::CheckDuelDistance(time_t currTime) { @@ -10849,7 +10798,7 @@ void Player::SendInitialPacketsAfterAddToMap() // update zone uint32 newzone, newarea; - GetZoneAndAreaId(newzone, newarea, true); + GetZoneAndAreaId(newzone, newarea); UpdateZone(newzone, newarea); // also call SendInitWorldStates(); if (HasAuraType(SPELL_AURA_MOD_STUN)) @@ -13758,7 +13707,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans) stmt->setUInt16(index++, (uint16)m_ExtraFlags); stmt->setUInt8(index++, m_stableSlots); stmt->setUInt16(index++, (uint16)m_atLoginFlags); - stmt->setUInt16(index++, GetZoneId(true)); + stmt->setUInt16(index++, GetZoneId()); stmt->setUInt32(index++, uint32(m_deathExpireTime)); ss.str(""); @@ -13896,7 +13845,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans) stmt->setUInt16(index++, (uint16)m_ExtraFlags); stmt->setUInt8(index++, m_stableSlots); stmt->setUInt16(index++, (uint16)m_atLoginFlags); - stmt->setUInt16(index++, GetZoneId(true)); + stmt->setUInt16(index++, GetZoneId()); stmt->setUInt32(index++, uint32(m_deathExpireTime)); ss.str(""); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 3b2b0dff5..e89adb2c7 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -85,7 +85,7 @@ enum PlayerUnderwaterState UNDERWATER_INWATER = 0x01, // terrain type is water and player is afflicted by it UNDERWATER_INLAVA = 0x02, // terrain type is lava and player is afflicted by it UNDERWATER_INSLIME = 0x04, // terrain type is lava and player is afflicted by it - UNDERWARER_INDARKWATER = 0x08, // terrain type is dark water and player is afflicted by it + UNDERWATER_INDARKWATER = 0x08, // terrain type is dark water and player is afflicted by it UNDERWATER_EXIST_TIMERS = 0x10 }; @@ -1021,8 +1021,7 @@ public: void SetInWater(bool apply); - [[nodiscard]] bool IsInWater(bool allowAbove = false) const override; - [[nodiscard]] bool IsUnderWater() const override; + [[nodiscard]] bool IsInWater() const override { return m_isInWater; } [[nodiscard]] bool IsFalling() const; bool IsInAreaTriggerRadius(const AreaTrigger* trigger) const; @@ -1737,10 +1736,6 @@ public: void UpdateZone(uint32 newZone, uint32 newArea); void UpdateArea(uint32 newArea); - [[nodiscard]] uint32 GetZoneId(bool forceRecalc = false) const override; - [[nodiscard]] uint32 GetAreaId(bool forceRecalc = false) const override; - void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc = false) const override; - void UpdateZoneDependentAuras(uint32 zone_id); // zones void UpdateAreaDependentAuras(uint32 area_id); // subzones @@ -1905,7 +1900,8 @@ public: bool UpdatePosition(float x, float y, float z, float orientation, bool teleport = false) override; bool UpdatePosition(const Position& pos, bool teleport = false) { return UpdatePosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); } - void UpdateUnderwaterState(Map* m, float x, float y, float z) override; + + void ProcessTerrainStatusUpdate() override; void SendMessageToSet(WorldPacket* data, bool self) override { SendMessageToSetInRange(data, GetVisibilityRange(), self, true); } // pussywizard! void SendMessageToSetInRange(WorldPacket* data, float dist, bool self, bool includeMargin = false, Player const* skipped_rcvr = nullptr) override; // pussywizard! diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index 58aa260fc..9f8dcd918 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -5241,6 +5241,8 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons SetMap(map); StoreRaidMapDifficulty(); + UpdatePositionData(); + SaveRecallPosition(); time_t now = time(nullptr); @@ -5772,7 +5774,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) if (result) { - uint32 zoneId = GetZoneId(true); + uint32 zoneId = GetZoneId(); std::map bagMap; // fast guid lookup for bags std::map invalidBagMap; // fast guid lookup for bags diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 77d662058..1629bbacf 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -268,9 +268,7 @@ void Player::Update(uint32 p_time) } uint32 newzone, newarea; - GetZoneAndAreaId(newzone, newarea, true); - m_last_zone_id = newzone; - m_last_area_id = newarea; + GetZoneAndAreaId(newzone, newarea); if (m_zoneUpdateId != newzone) UpdateZone(newzone, newarea); // also update area @@ -354,7 +352,7 @@ void Player::Update(uint32 p_time) } // not auto-free ghost from body in instances - if (m_deathTimer > 0 && !GetBaseMap()->Instanceable() && + if (m_deathTimer > 0 && !GetMap()->Instanceable() && !HasAuraType(SPELL_AURA_PREVENT_RESURRECTION)) { if (p_time >= m_deathTimer) @@ -1975,96 +1973,6 @@ void Player::UpdateCorpseReclaimDelay() m_deathExpireTime = now + DEATH_EXPIRE_STEP; } -void Player::UpdateUnderwaterState(Map* m, float x, float y, float z) -{ - // pussywizard: optimization - if (GetExactDistSq(&m_last_underwaterstate_position) < 3.0f * 3.0f) - return; - - m_last_underwaterstate_position.Relocate(m_positionX, m_positionY, - m_positionZ); - - if (!IsPositionValid()) // pussywizard: crashfix if calculated grid coords - // would be out of range 0-64 - return; - - LiquidData liquid_status; - ZLiquidStatus res = m->getLiquidStatus( - x, y, z, MAP_ALL_LIQUIDS, &liquid_status, GetCollisionHeight()); - if (!res) - { - m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | - UNDERWATER_INSLIME | UNDERWARER_INDARKWATER); - if (_lastLiquid && _lastLiquid->SpellId) - RemoveAurasDueToSpell(_lastLiquid->SpellId); - - _lastLiquid = nullptr; - 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); - } - - _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; - else - m_MirrorTimerFlags &= ~UNDERWATER_INWATER; - } - - // 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; - else - m_MirrorTimerFlags &= ~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::UpdateCharmedAI() { // Xinef: maybe passed as argument? @@ -2361,3 +2269,58 @@ void Player::SendUpdateWorldState(uint32 Field, uint32 Value) data << Value; GetSession()->SendPacket(&data); } + +void Player::ProcessTerrainStatusUpdate() +{ + // process liquid auras using generic unit code + Unit::ProcessTerrainStatusUpdate(); + + LiquidData const& liquidData = GetLiquidData(); + + // player specific logic for mirror timers + if (liquidData.Status != LIQUID_MAP_NO_WATER) + { + // Breath bar state (under water in any liquid type) + if ((liquidData.Flags & MAP_ALL_LIQUIDS) != 0) + { + if ((liquidData.Status & LIQUID_MAP_UNDER_WATER) != 0) + m_MirrorTimerFlags |= UNDERWATER_INWATER; + else + m_MirrorTimerFlags &= ~UNDERWATER_INWATER; + } + + // Fatigue bar state (if not on flight path or transport) + if ((liquidData.Flags & MAP_LIQUID_TYPE_DARK_WATER) && !IsInFlight() && !GetTransport()) + { + // Exclude also uncontrollable vehicles + Vehicle* vehicle = GetVehicle(); + VehicleSeatEntry const* vehicleSeat = vehicle ? vehicle->GetSeatForPassenger(this) : nullptr; + if (!vehicleSeat || vehicleSeat->CanControl()) + m_MirrorTimerFlags |= UNDERWATER_INDARKWATER; + else + m_MirrorTimerFlags &= ~UNDERWATER_INDARKWATER; + } + else + m_MirrorTimerFlags &= ~UNDERWATER_INDARKWATER; + + // Lava state (any contact) + if (liquidData.Flags & MAP_LIQUID_TYPE_MAGMA) + { + if (liquidData.Status & MAP_LIQUID_STATUS_IN_CONTACT) + m_MirrorTimerFlags |= UNDERWATER_INLAVA; + else + m_MirrorTimerFlags &= ~UNDERWATER_INLAVA; + } + + // Slime state (any contact) + if (liquidData.Flags & MAP_LIQUID_TYPE_SLIME) + { + if (liquidData.Status & MAP_LIQUID_STATUS_IN_CONTACT) + m_MirrorTimerFlags |= UNDERWATER_INSLIME; + else + m_MirrorTimerFlags &= ~UNDERWATER_INSLIME; + } + } + else + m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | UNDERWATER_INSLIME | UNDERWATER_INDARKWATER); +} diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index bb00ac2e1..bd578f576 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -91,6 +91,7 @@ bool MotionTransport::CreateMoTrans(ObjectGuid::LowType guidlow, uint32 entry, u SetTransportPathRotation(0.0f, 0.0f, 0.0f, 1.0f); m_model = CreateModel(); + return true; } @@ -676,6 +677,8 @@ bool StaticTransport::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* m SetPhaseMask(phaseMask, false); + UpdatePositionData(); + SetZoneScript(); if (m_zoneScript) { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index fd6c57a78..eb0f2da62 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -284,18 +284,6 @@ Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject), m_delayed_unit_relocation_timer = 0; m_delayed_unit_ai_notify_timer = 0; bRequestForcedVisibilityUpdate = false; - m_last_underwaterstate_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); - m_last_environment_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); - m_last_isinwater_status = false; - m_last_islittleabovewater_status = false; - m_last_isunderwater_status = false; - m_is_updating_environment = false; - m_last_area_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); - m_last_zone_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); - m_last_area_id = 0; - m_last_zone_id = 0; - m_last_outdoors_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); - m_last_outdoors_status = true; // true by default m_applyResilience = false; _instantCast = false; @@ -528,8 +516,13 @@ void Unit::UpdateSplineMovement(uint32 t_diff) bool arrived = movespline->Finalized(); if (arrived) + { DisableSpline(); + if (movespline->HasAnimation() && GetTypeId() == TYPEID_UNIT && IsAlive()) + SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, movespline->GetAnimationType()); + } + // pussywizard: update always! not every 400ms, because movement generators need the actual position //m_movesplineTimer.Update(t_diff); //if (m_movesplineTimer.Passed() || arrived) @@ -3636,230 +3629,48 @@ bool Unit::isInAccessiblePlaceFor(Creature const* c) const if (IsInWater()) return IsUnderWater() ? c->CanEnterWater() : (c->CanEnterWater() || c->CanFly()); else - return c->CanWalk() || c->CanFly() || (c->CanSwim() && IsInWater(true)); + return c->CanWalk() || c->CanFly() || (c->CanSwim() && IsInWater()); } -void Unit::UpdateEnvironmentIfNeeded(const uint8 option) +void Unit::ProcessPositionDataChanged(PositionFullTerrainStatus const& data) { - if (m_is_updating_environment) + WorldObject::ProcessPositionDataChanged(data); + ProcessTerrainStatusUpdate(); +} + +void Unit::ProcessTerrainStatusUpdate() +{ + if (GetTypeId() == TYPEID_UNIT) + ToCreature()->UpdateMovementFlags(); + + if (IsFlying() || (!IsControlledByPlayer())) return; - if (GetTypeId() != TYPEID_UNIT || !IsAlive() || (!IsInWorld() && option != 3) || !FindMap() || IsDuringRemoveFromWorld() || !IsPositionValid()) - return; + LiquidData const& liquidData = GetLiquidData(); - if (option <= 2 && GetMotionMaster()->GetCleanFlags() != MMCF_NONE) + // remove appropriate auras if we are swimming/not swimming respectively + if (liquidData.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 ((liquidData.Status & MAP_LIQUID_STATUS_SWIMMING)) + curLiquid = sLiquidTypeStore.LookupEntry(liquidData.Entry); + + if (curLiquid != _lastLiquid) { - if (option == 2) - m_last_environment_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); - return; + if (_lastLiquid && _lastLiquid->SpellId) + RemoveAurasDueToSpell(_lastLiquid->SpellId); + + // Set _lastLiquid before casting liquid spell to avoid infinite loops + _lastLiquid = curLiquid; + + Player* player = GetCharmerOrOwnerPlayerOrPlayerItself(); + if (curLiquid && curLiquid->SpellId && (!player || !player->IsGameMaster())) + CastSpell(this, curLiquid->SpellId, true); } - - // run environment checks everytime the unit moves - // more than it's average radius - // TODO: find better solution here - float radiusWidth = GetCollisionRadius(); - float radiusHeight = GetCollisionHeight() / 2; - float radiusAvg = (radiusWidth + radiusHeight) / 2; - if (option <= 1 && GetExactDistSq(&m_last_environment_position) < radiusAvg * radiusAvg) - return; - - m_last_environment_position.Relocate(GetPositionX(), GetPositionY(), GetPositionZ()); - m_staticFloorZ = GetMap()->GetHeight(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ()); - - m_is_updating_environment = true; - - bool changed = false; - Map* baseMap = const_cast(GetBaseMap()); - Creature* c = this->ToCreature(); - if (!c || !baseMap) - { - m_is_updating_environment = false; - return; - } - - bool canChangeFlying = option == 3 || ((c->GetScriptId() == 0 || GetInstanceId() == 0) && GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE); - bool canFallGround = option == 0 && canChangeFlying && GetInstanceId() == 0 && !IsInCombat() && !GetVehicle() && !GetTransport() && !HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && !c->IsTrigger() && !c->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE) && GetMotionMaster()->GetCurrentMovementGeneratorType() <= RANDOM_MOTION_TYPE && !HasUnitState(UNIT_STATE_EVADE) && !IsControlledByPlayer(); - float x = GetPositionX(), y = GetPositionY(), z = GetPositionZ(); - bool isInAir = true; - float ground_z = z; - LiquidData liquidData; - liquidData.level = INVALID_HEIGHT; - ZLiquidStatus liquidStatus = baseMap->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquidData); - // IsInWater - bool enoughWater = baseMap->HasEnoughWater(this, liquidData); - m_last_isinwater_status = (liquidStatus & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER)) && enoughWater; - m_last_islittleabovewater_status = (liquidData.level > INVALID_HEIGHT && liquidData.level > liquidData.depth_level && liquidData.level <= z + 3.0f && liquidData.level > z - 1.0f); - - // IsUnderWater - m_last_isunderwater_status = (liquidStatus & LIQUID_MAP_UNDER_WATER) && enoughWater; - - // UpdateUnderwaterState - if (IsPet() || IsVehicle()) - { - if (option == 1) // Unit::IsInWater, Unit::IsUnderwater, adding/removing auras can cause crashes (eg threat change while iterating threat table), so skip - m_last_environment_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); - else - { - if (!liquidStatus) - { - if (_lastLiquid && _lastLiquid->SpellId) - RemoveAurasDueToSpell(_lastLiquid->SpellId); - - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER); - _lastLiquid = nullptr; - } - else if (uint32 liqEntry = liquidData.entry) - { - LiquidTypeEntry const* liquid = sLiquidTypeStore.LookupEntry(liqEntry); - if (_lastLiquid && _lastLiquid->SpellId && _lastLiquid->Id != liqEntry) - RemoveAurasDueToSpell(_lastLiquid->SpellId); - - if (liquid && liquid->SpellId) - { - if (liquidStatus & (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 = nullptr; - } - } - } - - bool canUpdateEnvironment = !HasUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD); - - bool flyingBarelyInWater = false; - // Refresh being in water - if (m_last_isinwater_status) - { - if (!c->CanFly() || enoughWater) - { - if (canUpdateEnvironment && c->CanSwim() && (!HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) || !HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY))) - { - SetSwim(true); - changed = true; - } - isInAir = false; - } - else - { - m_last_isinwater_status = false; - flyingBarelyInWater = true; - } - } - - if (!m_last_isinwater_status) - { - if (canUpdateEnvironment && c->CanWalk() && HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING)) - { - SetSwim(false); - changed = true; - } - } - - // if not in water, check whether in air or not - if (isInAir) - { - if (GetMap()->GetGrid(x, y)) - { - float temp = GetFloorZ(); - if (temp > INVALID_HEIGHT) - { - ground_z = (c->CanSwim() && liquidData.level > INVALID_HEIGHT) ? liquidData.level : temp; - bool canHover = c->CanHover(); - isInAir = flyingBarelyInWater || (G3D::fuzzyGt(GetPositionZ(), ground_z + (canHover ? GetFloatValue(UNIT_FIELD_HOVERHEIGHT) : 0.0f) + GROUND_HEIGHT_TOLERANCE) || G3D::fuzzyLt(GetPositionZ(), ground_z - GROUND_HEIGHT_TOLERANCE)); // Can be underground too, prevent the falling - } - else - isInAir = true; - } - else - { - m_is_updating_environment = false; - return; - } - } - - if (canUpdateEnvironment && canChangeFlying) - { - // xinef: summoned vehicles are treated as always in air, fixes flying on such units - if (IsVehicle() && !c->GetSpawnId()) - isInAir = true; - - // xinef: triggers with inhabit type air are treated as always in air - if (c->IsTrigger() && c->CanFly()) - isInAir = true; - - if (c->GetOwnerGUID().IsPlayer() && c->CanFly() && IsVehicle() && !c->GetSpawnId()) // mainly for oculus drakes - { - if (!HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY) || !HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY)) - { - SetCanFly(true); - SetDisableGravity(true); - changed = true; - } - } - else if (c->CanFly() && isInAir && !c->IsFalling()) - { - if (!HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY) || !HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY)) - { - SetCanFly(true); - SetDisableGravity(true); - changed = true; - } - - if (IsHovering() && !HasAuraType(SPELL_AURA_HOVER)) - { - SetHover(false); - changed = true; - } - } - else - { - if (HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY) || HasUnitMovementFlag(MOVEMENTFLAG_FLYING)) - { - SetCanFly(false); - RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING); - changed = true; - } - - if (!IsHovering() && IsAlive() && (c->CanHover() || HasAuraType(SPELL_AURA_HOVER))) - { - SetHover(true); - changed = true; - } - - if (HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY) && !HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING)) - { - SetDisableGravity(false); - changed = true; - } - } - - if (isInAir && !c->CanFly() && option >= 2) - m_last_environment_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); - } - - if (!isInAir && HasUnitMovementFlag(MOVEMENTFLAG_FALLING)) - RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING); - - if (changed) - propagateSpeedChange(); - - if (canUpdateEnvironment && canFallGround && !c->CanFly() && !c->IsFalling() && !m_last_isinwater_status && (c->GetUnitMovementFlags() & (MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_HOVER | MOVEMENTFLAG_SWIMMING)) == 0 && z - ground_z > 5.0f && z - ground_z < 80.0f) - GetMotionMaster()->MoveFall(); - - m_is_updating_environment = false; } SafeUnitPointer::~SafeUnitPointer() @@ -3906,20 +3717,14 @@ void Unit::HandleSafeUnitPointersOnDelete(Unit* thisUnit) thisUnit->SafeUnitPointerSet.clear(); } -bool Unit::IsInWater(bool allowAbove) const +bool Unit::IsInWater() const { - const_cast(this)->UpdateEnvironmentIfNeeded(1); - return m_last_isinwater_status || (allowAbove && m_last_islittleabovewater_status); + return (GetLiquidData().Status & MAP_LIQUID_STATUS_SWIMMING) != 0; } bool Unit::IsUnderWater() const { - const_cast(this)->UpdateEnvironmentIfNeeded(1); - return m_last_isunderwater_status; -} - -void Unit::UpdateUnderwaterState(Map* /*m*/, float /*x*/, float /*y*/, float /*z*/) -{ + return GetLiquidData().Status == LIQUID_MAP_UNDER_WATER; } void Unit::DeMorph() @@ -12996,8 +12801,6 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy, uint32 duration) if (Creature* creature = ToCreature()) { - creature->UpdateEnvironmentIfNeeded(2); - // Set home position at place of engaging combat for escorted creatures if ((IsAIEnabled && creature->AI()->IsEscorted()) || GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE || @@ -19584,62 +19387,6 @@ bool ConflagrateAuraStateDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time return true; } -uint32 Unit::GetZoneId(bool forceRecalc) const -{ - // xinef: optimization, zone calculated every few yards - if (!forceRecalc && GetExactDistSq(&m_last_zone_position) < 4.0f * 4.0f) - return m_last_zone_id; - else - { - const_cast(&m_last_zone_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ()); - *(const_cast(&m_last_zone_id)) = WorldObject::GetZoneId(); - return m_last_zone_id; - } -} - -uint32 Unit::GetAreaId(bool forceRecalc) const -{ - // xinef: optimization, area calculated every few yards - if (!forceRecalc && GetExactDistSq(&m_last_area_position) < 4.0f * 4.0f) - return m_last_area_id; - else - { - const_cast(&m_last_area_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ()); - *(const_cast(&m_last_area_id)) = WorldObject::GetAreaId(); - return m_last_area_id; - } -} - -void Unit::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc) const -{ - // xinef: optimization, zone and area calculated every few yards - if (!forceRecalc && GetExactDistSq(&m_last_area_position) < 4.0f * 4.0f && GetExactDistSq(&m_last_zone_position) < 4.0f * 4.0f) - { - zoneid = m_last_zone_id; - areaid = m_last_area_id; - return; - } - - const_cast(&m_last_zone_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ()); - const_cast(&m_last_area_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ()); - WorldObject::GetZoneAndAreaId(zoneid, areaid); - *(const_cast(&m_last_zone_id)) = zoneid; - *(const_cast(&m_last_area_id)) = areaid; -} - -bool Unit::IsOutdoors() const -{ - // xinef: optimization, outdoor status calculated every few yards - if (GetExactDistSq(&m_last_outdoors_position) < 4.0f * 4.0f) - return m_last_outdoors_status; - else - { - const_cast(&m_last_outdoors_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ()); - *(const_cast(&m_last_outdoors_status)) = GetMap()->IsOutdoors(GetPositionX(), GetPositionY(), GetPositionZ()); - return m_last_outdoors_status; - } -} - void Unit::ExecuteDelayedUnitRelocationEvent() { this->RemoveFromNotify(NOTIFY_VISIBILITY_CHANGED); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index c8cd5f9ac..52c9abad2 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1772,10 +1772,8 @@ public: bool IsValidAssistTarget(Unit const* target) const; bool _IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) const; - void UpdateEnvironmentIfNeeded(const uint8 option); - [[nodiscard]] virtual bool IsInWater(bool allowAbove = false) const; + [[nodiscard]] virtual bool IsInWater() const; [[nodiscard]] virtual bool IsUnderWater() const; - virtual void UpdateUnderwaterState(Map* m, float x, float y, float z); bool isInAccessiblePlaceFor(Creature const* c) const; void SendHealSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorb, bool critical = false); @@ -2454,25 +2452,6 @@ public: bool bRequestForcedVisibilityUpdate; void ExecuteDelayedUnitRelocationEvent(); void ExecuteDelayedUnitAINotifyEvent(); - // IsInWater, UpdateUnderwaterState, etc. optimizations - Position m_last_underwaterstate_position; - Position m_last_environment_position; - bool m_last_isinwater_status; - bool m_last_islittleabovewater_status; - bool m_last_isunderwater_status; - bool m_is_updating_environment; - // GetZone / GetArea optimization - Position m_last_area_position; - Position m_last_zone_position; - uint32 m_last_area_id; - uint32 m_last_zone_id; - Position m_last_outdoors_position; - bool m_last_outdoors_status; - [[nodiscard]] bool IsOutdoors() const; - - [[nodiscard]] uint32 GetZoneId(bool forceRecalc = false) const override; - [[nodiscard]] uint32 GetAreaId(bool forceRecalc = false) const override; - void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc = false) const override; // cooldowns [[nodiscard]] virtual bool HasSpellCooldown(uint32 /*spell_id*/) const { return false; } @@ -2503,6 +2482,9 @@ public: [[nodiscard]] float GetCollisionWidth() const override; [[nodiscard]] float GetCollisionRadius() const override; + void ProcessPositionDataChanged(PositionFullTerrainStatus const& data) override; + virtual void ProcessTerrainStatusUpdate(); + protected: explicit Unit (bool isWorldObject); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 620830985..81f399993 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1955,8 +1955,8 @@ void ObjectMgr::LoadCreatures() if (sWorld->getBoolConfig(CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA)) { - uint32 zoneId = sMapMgr->GetZoneId(data.mapid, data.posX, data.posY, data.posZ); - uint32 areaId = sMapMgr->GetAreaId(data.mapid, data.posX, data.posY, data.posZ); + uint32 zoneId = sMapMgr->GetZoneId(data.phaseMask, data.mapid, data.posX, data.posY, data.posZ); + uint32 areaId = sMapMgr->GetAreaId(data.phaseMask, data.mapid, data.posX, data.posY, data.posZ); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA); @@ -2256,8 +2256,8 @@ void ObjectMgr::LoadGameobjects() if (sWorld->getBoolConfig(CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA)) { - uint32 zoneId = sMapMgr->GetZoneId(data.mapid, data.posX, data.posY, data.posZ); - uint32 areaId = sMapMgr->GetAreaId(data.mapid, data.posX, data.posY, data.posZ); + uint32 zoneId = sMapMgr->GetZoneId(data.phaseMask, data.mapid, data.posX, data.posY, data.posZ); + uint32 areaId = sMapMgr->GetAreaId(data.phaseMask, data.mapid, data.posX, data.posY, data.posZ); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 41d9df44d..082b2179c 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1135,7 +1135,7 @@ void WorldSession::HandlePlayerLoginToCharInWorld(Player* pCurrChar) pCurrChar->CleanupChannels(); pCurrChar->SendInitialPacketsAfterAddToMap(); uint32 currZone, currArea; - pCurrChar->GetZoneAndAreaId(currZone, currArea, false); + pCurrChar->GetZoneAndAreaId(currZone, currArea); pCurrChar->SendInitWorldStates(currZone, currArea); pCurrChar->SetInGameTime(World::GetGameTimeMS()); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 0483599a9..5497ec301 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -511,7 +511,7 @@ void WorldSession::HandleZoneUpdateOpcode(WorldPacket& recv_data) // use server size data uint32 newzone, newarea; - GetPlayer()->GetZoneAndAreaId(newzone, newarea, true); + GetPlayer()->GetZoneAndAreaId(newzone, newarea); GetPlayer()->UpdateZone(newzone, newarea); //GetPlayer()->SendInitWorldStates(true, newZone); } diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 5b2fb4aa2..51e514ef5 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -90,6 +90,8 @@ void WorldSession::HandleMoveWorldportAck() GetPlayer()->ResetMap(); GetPlayer()->SetMap(newMap); + GetPlayer()->UpdatePositionData(); + GetPlayer()->SendInitialPacketsBeforeAddToMap(); if (!GetPlayer()->GetMap()->AddPlayerToMap(GetPlayer())) { @@ -216,7 +218,7 @@ void WorldSession::HandleMoveWorldportAck() // update zone immediately, otherwise leave channel will cause crash in mtmap uint32 newzone, newarea; - GetPlayer()->GetZoneAndAreaId(newzone, newarea, true); + GetPlayer()->GetZoneAndAreaId(newzone, newarea); GetPlayer()->UpdateZone(newzone, newarea); // honorless target @@ -273,7 +275,7 @@ void WorldSession::HandleMoveTeleportAck(WorldPacket& recvData) if (oldPos.GetExactDist2d(plMover) > 100.0f) { uint32 newzone, newarea; - plMover->GetZoneAndAreaId(newzone, newarea, true); + plMover->GetZoneAndAreaId(newzone, newarea); plMover->UpdateZone(newzone, newarea); // new zone @@ -482,7 +484,8 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData) if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water - plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); + plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetMap()->IsUnderWater(plrMover->GetPhaseMask(), movementInfo.pos.GetPositionX(), + movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ(), plrMover->GetCollisionHeight())); } bool jumpopcode = false; diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 5d5028c5e..96bbbbf29 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -175,6 +175,7 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, if (pet->IsCritter()) { + pet->UpdatePositionData(); map->AddToMap(pet->ToCreature(), true); pet->SetLoading(false); // xinef, mine return PET_LOAD_OK; @@ -186,6 +187,7 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, pet->GetCharmInfo()->SetPetNumber(pet_number, false); pet->SetDisplayId(fields[3].GetUInt32()); + pet->UpdatePositionData(); pet->SetNativeDisplayId(fields[3].GetUInt32()); pet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); pet->SetName(fields[8].GetString()); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 860d5be0c..2e7acbc73 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -43,6 +43,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 }; + Map::~Map() { // UnloadAll must be called before deleting the map @@ -980,7 +983,7 @@ void Map::PlayerRelocation(Player* player, float x, float y, float z, float o) player->Relocate(x, y, z, o); if (player->IsVehicle()) player->GetVehicleKit()->RelocatePassengers(); - + player->UpdatePositionData(); player->UpdateObjectVisibility(false); } @@ -1002,7 +1005,7 @@ void Map::CreatureRelocation(Creature* creature, float x, float y, float z, floa creature->Relocate(x, y, z, o); if (creature->IsVehicle()) creature->GetVehicleKit()->RelocatePassengers(); - + creature->UpdatePositionData(); creature->UpdateObjectVisibility(false); } @@ -1023,7 +1026,7 @@ void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float go->Relocate(x, y, z, o); go->UpdateModelPosition(); - + go->SetPositionDataUpdate(); go->UpdateObjectVisibility(false); } @@ -1043,7 +1046,7 @@ void Map::DynamicObjectRelocation(DynamicObject* dynObj, float x, float y, float RemoveDynamicObjectFromMoveList(dynObj); dynObj->Relocate(x, y, z, o); - + dynObj->SetPositionDataUpdate(); dynObj->UpdateObjectVisibility(false); } @@ -1307,6 +1310,7 @@ GridMap::GridMap() _liquidEntry = nullptr; _liquidFlags = nullptr; _liquidMap = nullptr; + _holes = nullptr; } GridMap::~GridMap() @@ -1354,6 +1358,13 @@ bool GridMap::loadData(char* filename) fclose(in); return false; } + // loadup holes data (if any. check header.holesOffset) + if (header.holesSize && !loadHolesData(in, header.holesOffset, header.holesSize)) + { + LOG_ERROR("maps", "Error loading map holes data\n"); + fclose(in); + return false; + } fclose(in); return true; } @@ -1372,6 +1383,7 @@ void GridMap::unloadData() delete[] _liquidEntry; delete[] _liquidFlags; delete[] _liquidMap; + delete[] _holes; _areaMap = nullptr; m_V9 = nullptr; m_V8 = nullptr; @@ -1380,6 +1392,7 @@ void GridMap::unloadData() _liquidEntry = nullptr; _liquidFlags = nullptr; _liquidMap = nullptr; + _holes = nullptr; _gridGetHeight = &GridMap::getHeightFromFlat; } @@ -1491,6 +1504,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) @@ -1523,6 +1548,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: @@ -1605,6 +1633,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) @@ -1672,6 +1703,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) @@ -1724,6 +1758,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::getMinHeight(float x, float y) const { if (!_minHeight) @@ -1803,113 +1852,96 @@ 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 * (32 - x / SIZE_OF_GRIDS); - y = 16 * (32 - 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, float collisionHeight, LiquidData* data) +inline LiquidData const GridMap::GetLiquidData(float x, float y, float z, float collisionHeight, uint8 ReqLiquidType) const { + LiquidData liquidData; + // Check water type (if no water return) - if (!_liquidType && !_liquidFlags) - return LIQUID_MAP_NO_WATER; - - // Get cell - float cx = MAP_RESOLUTION * (32 - x / SIZE_OF_GRIDS); - float cy = MAP_RESOLUTION * (32 - y / SIZE_OF_GRIDS); - - int x_int = (int)cx & (MAP_RESOLUTION - 1); - int y_int = (int)cy & (MAP_RESOLUTION - 1); - - // Check water type in cell - int idx = (x_int >> 3) * 16 + (y_int >> 3); - uint8 type = _liquidFlags ? _liquidFlags[idx] : _liquidType; - uint32 entry = 0; - if (_liquidEntry) + if (_liquidType || _liquidFlags) { - if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(_liquidEntry[idx])) - { - entry = liquidEntry->Id; - type &= MAP_LIQUID_TYPE_DARK_WATER; - uint32 liqTypeIdx = liquidEntry->Type; - if (entry < 21) - { - if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y))) - { - uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; - if (!overrideLiquid && area->zone) - { - area = sAreaTableStore.LookupEntry(area->zone); - if (area) - overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; - } + // Get cell + float cx = MAP_RESOLUTION * (32 - x / SIZE_OF_GRIDS); + float cy = MAP_RESOLUTION * (32 - y / SIZE_OF_GRIDS); - if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) + int x_int = (int) cx & (MAP_RESOLUTION - 1); + int y_int = (int) cy & (MAP_RESOLUTION - 1); + + // Check water type in cell + int idx = (x_int >> 3) * 16 + (y_int >> 3); + uint8 type = _liquidFlags ? _liquidFlags[idx] : _liquidType; + uint32 entry = 0; + if (_liquidEntry) + { + if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(_liquidEntry[idx])) + { + entry = liquidEntry->Id; + type &= MAP_LIQUID_TYPE_DARK_WATER; + uint32 liqTypeIdx = liquidEntry->Type; + if (entry < 21) + { + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y))) { - entry = overrideLiquid; - liqTypeIdx = liq->Type; + uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; + if (!overrideLiquid && area->zone) + { + area = sAreaTableStore.LookupEntry(area->zone); + if (area) + overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; + } + + if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) + { + entry = overrideLiquid; + liqTypeIdx = liq->Type; + } } } - } - type |= 1 << liqTypeIdx; + type |= 1 << liqTypeIdx; + } + } + + // Check req liquid type mask + if (type != 0 && (!ReqLiquidType || (ReqLiquidType & type) != 0)) + { + // Check water level: + // Check water height map + int lx_int = x_int - _liquidOffY; + int ly_int = y_int - _liquidOffX; + if (lx_int >= 0 && lx_int < _liquidHeight && ly_int >= 0 && ly_int < _liquidWidth) + { + // Get water level + float liquid_level = _liquidMap ? _liquidMap[lx_int * _liquidWidth + ly_int] : _liquidLevel; + // Get ground level (sub 0.2 for fix some errors) + float ground_level = getHeight(x, y); + + // Check water level and ground level + if (liquid_level >= ground_level && z >= ground_level - 2) + { + // All ok in water -> store data + liquidData.Entry = entry; + liquidData.Flags = type; + liquidData.Level = liquid_level; + liquidData.DepthLevel = ground_level; + + // For speed check as int values + float delta = liquid_level - z; + + if (delta > collisionHeight) + liquidData.Status = LIQUID_MAP_UNDER_WATER; + else if (delta > 0.2f) + liquidData.Status = LIQUID_MAP_IN_WATER; + else if (delta > -0.2f) + liquidData.Status = LIQUID_MAP_WATER_WALK; + else + liquidData.Status = LIQUID_MAP_ABOVE_WATER; + } + } } } - if (type == 0) - return LIQUID_MAP_NO_WATER; - - // Check req liquid type mask - if (ReqLiquidType && !(ReqLiquidType & type)) - return LIQUID_MAP_NO_WATER; - - // Check water level: - // Check water height map - int lx_int = x_int - _liquidOffY; - int ly_int = y_int - _liquidOffX; - if (lx_int < 0 || lx_int >= _liquidHeight) - return LIQUID_MAP_NO_WATER; - if (ly_int < 0 || ly_int >= _liquidWidth) - return LIQUID_MAP_NO_WATER; - - // Get water level - float liquid_level = _liquidMap ? _liquidMap[lx_int * _liquidWidth + ly_int] : _liquidLevel; - // Get ground level (sub 0.2 for fix some errors) - float ground_level = getHeight(x, y); - - // Check water level and ground level - if (liquid_level < ground_level || z < ground_level - 2) - return LIQUID_MAP_NO_WATER; - - // All ok in water -> store data - if (data) - { - data->entry = entry; - data->type_flags = type; - data->level = liquid_level; - data->depth_level = ground_level; - } - - // For speed check as int values - float delta = liquid_level - z; - - if (delta > collisionHeight) // Under water - return LIQUID_MAP_UNDER_WATER; - if (delta > 0.0f) // In water - return LIQUID_MAP_IN_WATER; - if (delta > -0.1f) // Walk on water - return LIQUID_MAP_WATER_WALK; - // Above water - return LIQUID_MAP_ABOVE_WATER; + return liquidData; } GridMap* Map::GetGrid(float x, float y) @@ -1933,17 +1965,15 @@ float Map::GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, fl if (ground) *ground = ground_z; - LiquidData liquid_status; - - ZLiquidStatus res = getLiquidStatus(x, y, ground_z, MAP_ALL_LIQUIDS, &liquid_status, collisionHeight); - switch (res) + LiquidData const& liquidData = const_cast(this)->GetLiquidData(phasemask, x, y, ground_z, collisionHeight, MAP_ALL_LIQUIDS); + switch (liquidData.Status) { case LIQUID_MAP_ABOVE_WATER: - return std::max(liquid_status.level, ground_z); + return std::max(liquidData.Level, ground_z); case LIQUID_MAP_NO_WATER: return ground_z; default: - return liquid_status.level; + return liquidData.Level; } } @@ -2030,115 +2060,99 @@ float Map::GetMinHeight(float x, float y) const return -500.0f; } -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; - - if (wmoEntry) - { - if (wmoEntry->Flags & 4) - return true; - if ((wmoEntry->Flags & 2) != 0) - outdoor = false; - } - return outdoor; + return (mogpFlags & 0x2000) != 0; } -bool Map::IsOutdoors(float x, float y, float z) const -{ - uint32 mogpFlags; - int32 adtId, rootId, groupId; - - // no wmo found? -> outside by default - if (!GetAreaInfo(x, y, z, mogpFlags, adtId, rootId, groupId)) - return true; - - AreaTableEntry const* atEntry = 0; - WMOAreaTableEntry const* wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId); - if (wmoEntry) - { - 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(float x, float y, float z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const +bool Map::GetAreaInfo(uint32 phaseMask, float x, float y, float z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const { float vmap_z = z; + float dynamic_z = z; + float check_z = z; VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); - if (vmgr->GetAreaInfo(GetId(), x, y, vmap_z, flags, adtId, rootId, groupId)) + uint32 vflags; + int32 vadtId; + int32 vrootId; + int32 vgroupId; + uint32 dflags; + int32 dadtId; + int32 drootId; + int32 dgroupId; + + bool hasVmapAreaInfo = vmgr->GetAreaInfo(GetId(), x, y, vmap_z, vflags, vadtId, vrootId, vgroupId); + bool hasDynamicAreaInfo = _dynamicTree.GetAreaInfo(x, y, dynamic_z, phaseMask, dflags, dadtId, drootId, dgroupId); + auto useVmap = [&]() { check_z = vmap_z; flags = vflags; adtId = vadtId; rootId = vrootId; groupId = vgroupId; }; + auto useDyn = [&]() { check_z = dynamic_z; flags = dflags; adtId = dadtId; rootId = drootId; groupId = dgroupId; }; + + if (hasVmapAreaInfo) + { + if (hasDynamicAreaInfo && dynamic_z > vmap_z) + useDyn(); + else + useVmap(); + } + else if (hasDynamicAreaInfo) + { + useDyn(); + } + + if (hasVmapAreaInfo || hasDynamicAreaInfo) { // check if there's terrain between player height and object height if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) { - float _mapheight = gmap->getHeight(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) + if (z + 2.0f > mapHeight && mapHeight > check_z) return false; } + return true; } + return false; } -uint32 Map::GetAreaId(float x, float y, float z, bool* isOutdoors) const +uint32 Map::GetAreaId(uint32 phaseMask, float x, float y, float z) const { uint32 mogpFlags; int32 adtId, rootId, groupId; - WMOAreaTableEntry const* wmoEntry = 0; - AreaTableEntry const* atEntry = 0; - bool haveAreaInfo = false; + float vmapZ = 0.f; + bool hasVmapArea = GetAreaInfo(phaseMask, x, y, vmapZ, mogpFlags, adtId, rootId, groupId); - if (GetAreaInfo(x, y, z, mogpFlags, adtId, rootId, groupId)) + uint32 gridAreaId = 0; + float gridMapHeight = INVALID_HEIGHT; + if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) { - haveAreaInfo = true; - wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId); - if (wmoEntry) - atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId); + gridAreaId = gmap->getArea(x, y); + gridMapHeight = gmap->getHeight(x, y); } uint16 areaId = 0; - if (atEntry) - areaId = atEntry->ID; - else + // 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)) { - if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) - areaId = gmap->getArea(x, y); - // this used while not all *.map files generated (instances) - if (!areaId) - areaId = i_mapEntry->linked_zone; - } + // wmo found + if (WMOAreaTableEntry const* wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId)) + areaId = wmoEntry->areaId; - if (isOutdoors) - { - if (haveAreaInfo) - *isOutdoors = IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry); - else - *isOutdoors = true; + if (!areaId) + areaId = gridAreaId; } + else + areaId = gridAreaId; + + if (!areaId) + areaId = i_mapEntry->linked_zone; + return areaId; } -uint32 Map::GetAreaId(float x, float y, float z) const +uint32 Map::GetZoneId(uint32 phaseMask, 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); + uint32 areaId = GetAreaId(phaseMask, x, y, z); if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId)) if (area->zone) return area->zone; @@ -2146,105 +2160,239 @@ uint32 Map::GetZoneId(float x, float y, float z) const return areaId; } -void Map::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const +void Map::GetZoneAndAreaId(uint32 phaseMask, uint32& zoneid, uint32& areaid, float x, float y, float z) const { - areaid = zoneid = GetAreaId(x, y, z); + areaid = zoneid = GetAreaId(phaseMask, x, y, z); if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaid)) if (area->zone) zoneid = area->zone; } -uint8 Map::GetTerrainType(float x, float y) const +LiquidData const Map::GetLiquidData(uint32 phaseMask, float x, float y, float z, float collisionHeight, uint8 ReqLiquidType) { - if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) - return gmap->getTerrainType(x, y); - else - return 0; -} + LiquidData liquidData; -ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data, float collisionHeight) const -{ - ZLiquidStatus result = LIQUID_MAP_NO_WATER; VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); float liquid_level = INVALID_HEIGHT; float ground_level = INVALID_HEIGHT; uint32 liquid_type = 0; - if (vmgr->GetLiquidLevel(GetId(), x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type)) + uint32 mogpFlags = 0; + bool useGridLiquid = true; + if (vmgr->GetLiquidLevel(GetId(), x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type, mogpFlags)) { - LOG_DEBUG("maps", "getLiquidStatus(): vmap liquid level: %f ground: %f type: %u", liquid_level, ground_level, liquid_type); + useGridLiquid = !IsInWMOInterior(mogpFlags); + 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) + // hardcoded in client like this + if (GetId() == 530 && liquid_type == 2) + liquid_type = 15; + + uint32 liquidFlagType = 0; + if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(liquid_type)) + liquidFlagType = liq->Type; + + if (liquid_type && liquid_type < 21) { - // hardcoded in client like this - if (GetId() == 530 && liquid_type == 2) - liquid_type = 15; - - uint32 liquidFlagType = 0; - if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(liquid_type)) - liquidFlagType = liq->Type; - - if (liquid_type && liquid_type < 21) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId(phaseMask, x, y, z))) { - if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId(x, y, z))) + uint32 overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; + if (!overrideLiquid && area->zone) { - uint32 overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; - if (!overrideLiquid && area->zone) - { - area = sAreaTableStore.LookupEntry(area->zone); - if (area) - overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; - } + area = sAreaTableStore.LookupEntry(area->zone); + if (area) + overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; + } - if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) - { - liquid_type = overrideLiquid; - liquidFlagType = liq->Type; - } + if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) + { + liquid_type = overrideLiquid; + liquidFlagType = liq->Type; } } - - data->level = liquid_level; - data->depth_level = ground_level; - - data->entry = liquid_type; - data->type_flags = 1 << liquidFlagType; } - float delta = liquid_level - z; - - // Get position delta - if (delta > collisionHeight) // Under water - return LIQUID_MAP_UNDER_WATER; - if (delta > 0.0f) // In water - return LIQUID_MAP_IN_WATER; - if (delta > -0.1f) // Walk on water - return LIQUID_MAP_WATER_WALK; - result = LIQUID_MAP_ABOVE_WATER; + liquidData.Level = liquid_level; + liquidData.DepthLevel = ground_level; + liquidData.Entry = liquid_type; + liquidData.Flags = 1 << liquidFlagType; } + + float delta = liquid_level - z; + + // Get position delta + if (delta > collisionHeight) + liquidData.Status = LIQUID_MAP_UNDER_WATER; + if (delta > 0.2f) + liquidData.Status = LIQUID_MAP_IN_WATER; + if (delta > -0.2f) + liquidData.Status = LIQUID_MAP_WATER_WALK; + else + liquidData.Status = LIQUID_MAP_ABOVE_WATER; } - if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) + if (useGridLiquid) { - LiquidData map_data; - ZLiquidStatus map_result = gmap->getLiquidStatus(x, y, z, ReqLiquidType, collisionHeight, &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 = const_cast(this)->GetGrid(x, y)) { - if (data) + LiquidData const& map_data = gmap->GetLiquidData(x, y, z, collisionHeight, ReqLiquidType); + // Not override LIQUID_MAP_ABOVE_WATER with LIQUID_MAP_NO_WATER: + if (map_data.Status != 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; + uint32 liquidEntry = map_data.Entry; + if (GetId() == 530 && liquidEntry == 2) + liquidEntry = 15; - *data = map_data; + liquidData = map_data; + liquidData.Entry = liquidEntry; } - return map_result; } } - return result; + + return liquidData; +} + +void Map::GetFullTerrainStatusForPosition(uint32 phaseMask, float x, float y, float z, float collisionHeight, PositionFullTerrainStatus& data, uint8 reqLiquidType) +{ + GridMap* gmap = GetGrid(x, y); + + VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + VMAP::AreaAndLiquidData vmapData; + VMAP::AreaAndLiquidData dynData; + VMAP::AreaAndLiquidData* wmoData = nullptr; + vmgr->GetAreaAndLiquidData(GetId(), x, y, z, reqLiquidType, vmapData); + _dynamicTree.GetAreaAndLiquidData(x, y, z, phaseMask, reqLiquidType, dynData); + + uint32 gridAreaId = 0; + float gridMapHeight = INVALID_HEIGHT; + if (gmap) + { + gridAreaId = gmap->getArea(x, y); + gridMapHeight = gmap->getHeight(x, y); + } + + 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; + wmoData = &vmapData; + } + + // NOTE: Objects will not detect a case when a wmo providing area/liquid despawns from under them + // but this is fine as these kind of objects are not meant to be spawned and despawned a lot + // example: Lich King platform + if (dynData.floorZ > VMAP_INVALID_HEIGHT && G3D::fuzzyGe(z, dynData.floorZ - GROUND_HEIGHT_TOLERANCE) && + (G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || dynData.floorZ > gridMapHeight) && + (G3D::fuzzyLt(z, vmapData.floorZ - GROUND_HEIGHT_TOLERANCE) || dynData.floorZ > vmapData.floorZ)) + { + data.floorZ = dynData.floorZ; + wmoData = &dynData; + } + + if (wmoData) + { + if (wmoData->areaInfo) + { + // wmo found + WMOAreaTableEntry const* wmoEntry = GetWMOAreaTableEntryByTripple(wmoData->areaInfo->rootId, wmoData->areaInfo->adtId, wmoData->areaInfo->groupId); + data.outdoors = (wmoData->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(wmoData->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 + if (wmoData && wmoData->liquidInfo && wmoData->liquidInfo->level > wmoData->floorZ) + { + uint32 liquidType = wmoData->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.Level = wmoData->liquidInfo->level; + data.liquidInfo.DepthLevel = wmoData->floorZ; + data.liquidInfo.Entry = liquidType; + data.liquidInfo.Flags = 1 << liquidFlagType; + + // Get position delta + float delta = wmoData->liquidInfo->level - z; + + if (delta > collisionHeight) + data.liquidInfo.Status = LIQUID_MAP_UNDER_WATER; + else if (delta > 0.2f) + data.liquidInfo.Status = LIQUID_MAP_IN_WATER; + else if (delta > -0.2f) + data.liquidInfo.Status = LIQUID_MAP_WATER_WALK; + else + data.liquidInfo.Status = LIQUID_MAP_ABOVE_WATER; + } + + // look up liquid data from grid map + if (gmap && useGridLiquid) + { + LiquidData const& gridLiquidData = gmap->GetLiquidData(x, y, z, collisionHeight, reqLiquidType); + if (gridLiquidData.Status != LIQUID_MAP_NO_WATER && (!wmoData || gridLiquidData.Level > wmoData->floorZ)) + { + uint32 liquidEntry = gridLiquidData.Entry; + if (GetId() == 530 && liquidEntry == 2) + liquidEntry = 15; + + data.liquidInfo = gridLiquidData; + data.liquidInfo.Entry = liquidEntry; + } + } } float Map::GetWaterLevel(float x, float y) const @@ -2288,30 +2436,28 @@ float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=tr return std::max(h1, h2); } -bool Map::IsInWater(float x, float y, float pZ, LiquidData* data) const +bool Map::IsInWater(uint32 phaseMask, float x, float y, float pZ, float collisionHeight) 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); + LiquidData const& liquidData = const_cast(this)->GetLiquidData(phaseMask, x, y, pZ, collisionHeight, MAP_ALL_LIQUIDS); + return (liquidData.Status & MAP_LIQUID_STATUS_SWIMMING) != 0; } -bool Map::IsUnderWater(float x, float y, float z) const +bool Map::IsUnderWater(uint32 phaseMask, float x, float y, float z, float collisionHeight) const { - return getLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN) & LIQUID_MAP_UNDER_WATER; + LiquidData const& liquidData = const_cast(this)->GetLiquidData(phaseMask, x, y, z, collisionHeight, MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN); + return liquidData.Status == LIQUID_MAP_UNDER_WATER; } bool Map::HasEnoughWater(WorldObject const* searcher, float x, float y, float z) const { - LiquidData liquidData; - liquidData.level = INVALID_HEIGHT; - ZLiquidStatus liquidStatus = getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquidData); - return (liquidStatus & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER)) && HasEnoughWater(searcher, liquidData); + LiquidData const& liquidData = const_cast(this)->GetLiquidData(searcher->GetPhaseMask(), x, y, z, searcher->GetCollisionHeight(), MAP_ALL_LIQUIDS); + return (liquidData.Status & MAP_LIQUID_STATUS_SWIMMING) != 0 && HasEnoughWater(searcher, liquidData); } -bool Map::HasEnoughWater(WorldObject const* searcher, LiquidData liquidData) const +bool Map::HasEnoughWater(WorldObject const* searcher, LiquidData const& liquidData) const { float minHeightInWater = searcher->GetMinHeightInWater(); - return liquidData.level > INVALID_HEIGHT && liquidData.level > liquidData.depth_level && liquidData.level - liquidData.depth_level >= minHeightInWater; + return liquidData.Level > INVALID_HEIGHT && liquidData.Level > liquidData.DepthLevel && liquidData.Level - liquidData.DepthLevel >= minHeightInWater; } char const* Map::GetMapName() const @@ -3421,6 +3567,8 @@ Corpse* Map::ConvertCorpseToBones(ObjectGuid const ownerGuid, bool insignia /*= AddCorpse(bones); + bones->UpdatePositionData(); + // add bones in grid store if grid loaded where corpse placed AddToMap(bones); } @@ -3459,7 +3607,7 @@ void Map::RemoveOldCorpses() void Map::SendZoneDynamicInfo(Player* player) { - uint32 zoneId = GetZoneId(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); + uint32 zoneId = player->GetZoneId(); ZoneDynamicInfoMap::const_iterator itr = _zoneDynamicInfo.find(zoneId); if (itr == _zoneDynamicInfo.end()) return; @@ -3671,7 +3819,7 @@ bool Map::CheckCollisionAndGetValidCoords(const WorldObject* source, float start return false; } - bool isWaterNext = IsInWater(destX, destY, destZ); + bool isWaterNext = IsInWater(source->GetPhaseMask(), destX, destY, destZ, source->GetCollisionHeight()); PathGenerator path(source); @@ -3784,6 +3932,8 @@ void Map::LoadCorpseData() } AddCorpse(corpse); + + corpse->UpdatePositionData(); } while (result->NextRow()); } diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index ae7f77faa..aaa73425f 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -76,6 +76,8 @@ struct map_fileheader uint32 heightMapSize; uint32 liquidMapOffset; uint32 liquidMapSize; + uint32 holesOffset; + uint32 holesSize; }; #define MAP_AREA_NO_AREA 0x0001 @@ -115,7 +117,7 @@ struct map_liquidHeader float liquidLevel; }; -enum ZLiquidStatus +enum LiquidStatus { LIQUID_MAP_NO_WATER = 0x00000000, LIQUID_MAP_ABOVE_WATER = 0x00000001, @@ -124,6 +126,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 @@ -135,12 +140,30 @@ enum ZLiquidStatus #define MAP_LIQUID_TYPE_DARK_WATER 0x10 #define MAP_LIQUID_TYPE_WMO_WATER 0x20 +#define MAX_HEIGHT 100000.0f // can be use for find ground height at surface +#define INVALID_HEIGHT -100000.0f // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE +#define MAX_FALL_DISTANCE 250000.0f // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT +#define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations +#define MIN_UNLOAD_DELAY 1 // immediate unload + struct LiquidData { - uint32 type_flags; - uint32 entry; - float level; - float depth_level; + LiquidData() : Entry(0), Flags(0), Level(INVALID_HEIGHT), DepthLevel(INVALID_HEIGHT), Status(LIQUID_MAP_NO_WATER) { } + + uint32 Entry; + uint32 Flags; + float Level; + float DepthLevel; + LiquidStatus Status; +}; + +struct PositionFullTerrainStatus +{ + PositionFullTerrainStatus() : areaId(0), floorZ(INVALID_HEIGHT), outdoors(false) { } + uint32 areaId; + float floorZ; + bool outdoors; + LiquidData liquidInfo; }; enum LineOfSightChecks @@ -186,10 +209,13 @@ class GridMap uint8 _liquidOffY; uint8 _liquidWidth; uint8 _liquidHeight; + uint16* _holes; 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; @@ -209,8 +235,7 @@ public: [[nodiscard]] inline float getHeight(float x, float y) const {return (this->*_gridGetHeight)(x, y);} [[nodiscard]] float getMinHeight(float x, float y) const; [[nodiscard]] float getLiquidLevel(float x, float y) const; - [[nodiscard]] uint8 getTerrainType(float x, float y) const; - ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, float collisionHeight, LiquidData* data = nullptr); + LiquidData const GetLiquidData(float x, float y, float z, float collisionHeight, uint8 ReqLiquidType) const; }; // GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push, N), also any gcc version not support it at some platform @@ -249,12 +274,6 @@ struct ZoneDynamicInfo #pragma pack(pop) #endif -#define MAX_HEIGHT 100000.0f // can be use for find ground height at surface -#define INVALID_HEIGHT -100000.0f // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE -#define MAX_FALL_DISTANCE 250000.0f // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT -#define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations -#define MIN_UNLOAD_DELAY 1 // immediate unload - typedef std::map CreatureGroupHolderType; typedef std::unordered_map ZoneDynamicInfoMap; typedef std::set TransportsContainer; @@ -352,22 +371,19 @@ public: [[nodiscard]] float GetMinHeight(float x, float y) const; Transport* GetTransportForPos(uint32 phase, float x, float y, float z, WorldObject* worldobject = nullptr); - ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = nullptr, float collisionHeight = DEFAULT_COLLISION_HEIGHT) const; + void GetFullTerrainStatusForPosition(uint32 phaseMask, float x, float y, float z, float collisionHeight, PositionFullTerrainStatus& data, uint8 reqLiquidType = MAP_ALL_LIQUIDS); + LiquidData const GetLiquidData(uint32 phaseMask, float x, float y, float z, float collisionHeight, uint8 ReqLiquidType); - 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; - [[nodiscard]] uint32 GetAreaId(float x, float y, float z) const; - [[nodiscard]] uint32 GetZoneId(float x, float y, float z) const; - void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const; + [[nodiscard]] bool GetAreaInfo(uint32 phaseMask, float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const; + [[nodiscard]] uint32 GetAreaId(uint32 phaseMask, float x, float y, float z) const; + [[nodiscard]] uint32 GetZoneId(uint32 phaseMask, float x, float y, float z) const; + void GetZoneAndAreaId(uint32 phaseMask, uint32& zoneid, uint32& areaid, float x, float y, float z) const; - [[nodiscard]] bool IsOutdoors(float x, float y, float z) const; - - [[nodiscard]] uint8 GetTerrainType(float x, float y) const; [[nodiscard]] float GetWaterLevel(float x, float y) const; - bool IsInWater(float x, float y, float z, LiquidData* data = nullptr) const; - [[nodiscard]] bool IsUnderWater(float x, float y, float z) const; + bool IsInWater(uint32 phaseMask, float x, float y, float z, float collisionHeight) const; + [[nodiscard]] bool IsUnderWater(uint32 phaseMask, float x, float y, float z, float collisionHeight) const; [[nodiscard]] bool HasEnoughWater(WorldObject const* searcher, float x, float y, float z) const; - [[nodiscard]] bool HasEnoughWater(WorldObject const* searcher, LiquidData liquidData) const; + [[nodiscard]] bool HasEnoughWater(WorldObject const* searcher, LiquidData const& liquidData) const; void MoveAllCreaturesInMoveList(); void MoveAllGameObjectsInMoveList(); diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index fe7f76d2c..4668010e4 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -37,26 +37,26 @@ public: return (iter == i_maps.end() ? nullptr : iter->second); } - [[nodiscard]] uint32 GetAreaId(uint32 mapid, float x, float y, float z) const + [[nodiscard]] uint32 GetAreaId(uint32 phaseMask, uint32 mapid, float x, float y, float z) const { Map const* m = const_cast(this)->CreateBaseMap(mapid); - return m->GetAreaId(x, y, z); + return m->GetAreaId(phaseMask, x, y, z); } - [[nodiscard]] uint32 GetAreaId(uint32 mapid, Position const& pos) const { return GetAreaId(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } - [[nodiscard]] uint32 GetAreaId(WorldLocation const& loc) const { return GetAreaId(loc.GetMapId(), loc); } + [[nodiscard]] uint32 GetAreaId(uint32 phaseMask, uint32 mapid, Position const& pos) const { return GetAreaId(phaseMask, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } + [[nodiscard]] uint32 GetAreaId(uint32 phaseMask, WorldLocation const& loc) const { return GetAreaId(phaseMask, loc.GetMapId(), loc); } - [[nodiscard]] uint32 GetZoneId(uint32 mapid, float x, float y, float z) const + [[nodiscard]] uint32 GetZoneId(uint32 phaseMask, uint32 mapid, float x, float y, float z) const { Map const* m = const_cast(this)->CreateBaseMap(mapid); - return m->GetZoneId(x, y, z); + return m->GetZoneId(phaseMask, x, y, z); } - [[nodiscard]] uint32 GetZoneId(uint32 mapid, Position const& pos) const { return GetZoneId(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } - [[nodiscard]] uint32 GetZoneId(WorldLocation const& loc) const { return GetZoneId(loc.GetMapId(), loc); } + [[nodiscard]] uint32 GetZoneId(uint32 phaseMask, uint32 mapid, Position const& pos) const { return GetZoneId(phaseMask, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } + [[nodiscard]] uint32 GetZoneId(uint32 phaseMask, WorldLocation const& loc) const { return GetZoneId(phaseMask, loc.GetMapId(), loc); } - void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) + void GetZoneAndAreaId(uint32 phaseMask, uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) { Map const* m = const_cast(this)->CreateBaseMap(mapid); - m->GetZoneAndAreaId(zoneid, areaid, x, y, z); + m->GetZoneAndAreaId(phaseMask, zoneid, areaid, x, y, z); } void Initialize(void); diff --git a/src/server/game/Misc/GameGraveyard.cpp b/src/server/game/Misc/GameGraveyard.cpp index ad177d804..fa4dc2878 100644 --- a/src/server/game/Misc/GameGraveyard.cpp +++ b/src/server/game/Misc/GameGraveyard.cpp @@ -83,7 +83,7 @@ GraveyardStruct const* Graveyard::GetDefaultGraveyard(TeamId teamId) GraveyardStruct const* Graveyard::GetClosestGraveyard(float x, float y, float z, uint32 MapId, TeamId teamId) { // search for zone associated closest graveyard - uint32 zoneId = sMapMgr->GetZoneId(MapId, x, y, z); + uint32 zoneId = sMapMgr->GetZoneId(PHASEMASK_NORMAL, MapId, x, y, z); if (!zoneId) { diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 5621f4ca4..ef334892f 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -132,11 +132,6 @@ void MotionMaster::UpdateMotion(uint32 diff) } _cleanFlag &= ~MMCF_INUSE; - - if (_owner->GetTypeId() == TYPEID_PLAYER) - _owner->UpdateUnderwaterState(_owner->GetMap(), _owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ()); - else - _owner->UpdateEnvironmentIfNeeded(0); } void MotionMaster::DirectClean(bool reset) diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp index 1f081ae1c..ced185c26 100644 --- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp @@ -26,7 +26,7 @@ void ConfusedMovementGenerator::DoInitialize(T* unit) float y = unit->GetPositionY(); float z = unit->GetPositionZ(); - Map const* map = unit->GetBaseMap(); + Map const* map = unit->GetMap(); bool is_water_ok, is_land_ok; _InitSpecific(unit, is_water_ok, is_land_ok); @@ -50,7 +50,7 @@ void ConfusedMovementGenerator::DoInitialize(T* unit) } else if (unit->IsWithinLOS(wanderX, wanderY, z)) { - bool is_water = map->IsInWater(wanderX, wanderY, z); + bool is_water = map->IsInWater(unit->GetPhaseMask(), wanderX, wanderY, z, unit->GetCollisionHeight()); if ((is_water && !is_water_ok) || (!is_water && !is_land_ok)) { diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp index 6af935bc5..6f11373b4 100644 --- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp @@ -48,7 +48,7 @@ bool FleeingMovementGenerator::_getPoint(T* owner, float& x, float& y, float& if (!owner) return false; - const Map* _map = owner->GetBaseMap(); + const Map* _map = owner->GetMap(); x = owner->GetPositionX(); y = owner->GetPositionY(); diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp index 674cfcaeb..0282df1df 100644 --- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp @@ -29,8 +29,6 @@ void HomeMovementGenerator::DoFinalize(Creature* owner) if (!owner->HasSwimmingFlagOutOfCombat()) owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SWIMMING); - - owner->UpdateEnvironmentIfNeeded(2); } void HomeMovementGenerator::DoReset(Creature*) diff --git a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp index 278429de3..84d8c800e 100644 --- a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp @@ -197,8 +197,8 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con { bool buildShotrcut = false; - bool isUnderWaterStart = _source->GetMap()->IsUnderWater(startPos.x, startPos.y, startPos.z); - bool isUnderWaterEnd = _source->GetMap()->IsUnderWater(endPos.x, endPos.y, endPos.z); + bool isUnderWaterStart = _source->GetMap()->IsUnderWater(_source->GetPhaseMask(), startPos.x, startPos.y, startPos.z, _source->GetCollisionHeight()); + bool isUnderWaterEnd = _source->GetMap()->IsUnderWater(_source->GetPhaseMask(), endPos.x, endPos.y, endPos.z, _source->GetCollisionHeight()); bool isFarUnderWater = startFarFromPoly ? isUnderWaterStart : isUnderWaterEnd; Unit const* _sourceUnit = _source->ToUnit(); @@ -565,9 +565,9 @@ void PathGenerator::BuildPointPath(const float* startPoint, const float* endPoin uint32 newPointCount = 0; for (uint32 i = 0; i < pointCount; ++i) { G3D::Vector3 vector = G3D::Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]); - ZLiquidStatus status = _source->GetMap()->getLiquidStatus(vector.x, vector.y, vector.z, MAP_ALL_LIQUIDS, nullptr); + LiquidData const& liquidData = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), vector.x, vector.y, vector.z, _source->GetCollisionHeight(), MAP_ALL_LIQUIDS); // One of the points is not in the water - if (status == LIQUID_MAP_UNDER_WATER) + if (liquidData.Status == LIQUID_MAP_UNDER_WATER) { // if the first point is under water // then set a proper z for it @@ -699,11 +699,11 @@ void PathGenerator::UpdateFilter() NavTerrain PathGenerator::GetNavTerrain(float x, float y, float z) const { LiquidData data; - ZLiquidStatus liquidStatus = _source->GetMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &data); - if (liquidStatus == LIQUID_MAP_NO_WATER) + LiquidData const& liquidData = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), x, y, z, _source->GetCollisionHeight(), MAP_ALL_LIQUIDS); + if (liquidData.Status == LIQUID_MAP_NO_WATER) return NAV_GROUND; - switch (data.type_flags) + switch (data.Flags) { case MAP_LIQUID_TYPE_WATER: case MAP_LIQUID_TYPE_OCEAN: @@ -1149,8 +1149,8 @@ bool PathGenerator::IsSwimmableSegment(float const* v1, float const* v2, bool ch bool PathGenerator::IsSwimmableSegment(float x, float y, float z, float destX, float destY, float destZ, bool checkSwim) const { Creature const* _sourceCreature = _source->ToCreature(); - return _source->GetMap()->IsInWater(x, y, z) && - _source->GetMap()->IsInWater(destX, destY, destZ) && + return _source->GetMap()->IsInWater(_source->GetPhaseMask(), x, y, z, _source->GetCollisionHeight()) && + _source->GetMap()->IsInWater(_source->GetPhaseMask(), destX, destY, destZ, _source->GetCollisionHeight()) && (!checkSwim || !_sourceCreature || _sourceCreature->CanSwim()); } diff --git a/src/server/game/Movement/Spline/MoveSpline.h b/src/server/game/Movement/Spline/MoveSpline.h index a36998669..eb4edb472 100644 --- a/src/server/game/Movement/Spline/MoveSpline.h +++ b/src/server/game/Movement/Spline/MoveSpline.h @@ -110,6 +110,9 @@ namespace Movement [[nodiscard]] Vector3 CurrentDestination() const { return Initialized() ? spline.getPoint(point_Idx + 1, false) : Vector3(); } [[nodiscard]] int32 currentPathIdx() const; + [[nodiscard]] bool HasAnimation() const { return splineflags.animation; } + [[nodiscard]] uint8 GetAnimationType() const { return splineflags.animId; } + bool onTransport; [[nodiscard]] std::string ToString() const; [[nodiscard]] bool HasStarted() const diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 9f191edb5..3e8709b25 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -5784,7 +5784,7 @@ void AuraEffect::HandlePreventResurrection(AuraApplication const* aurApp, uint8 if (apply) aurApp->GetTarget()->RemoveByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER); - else if (!aurApp->GetTarget()->GetBaseMap()->Instanceable()) + else if (!aurApp->GetTarget()->GetMap()->Instanceable()) aurApp->GetTarget()->SetByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER); } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index aeaa4ddfe..0da513fd1 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1327,9 +1327,9 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici float ground = m_caster->GetMapHeight(x, y, z, true); float liquidLevel = VMAP_INVALID_HEIGHT_VALUE; - LiquidData liquidData; - if (m_caster->GetMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquidData, m_caster->GetCollisionHeight())) - liquidLevel = liquidData.level; + LiquidData const& liquidData = m_caster->GetMap()->GetLiquidData(m_caster->GetPhaseMask(), x, y, z, m_caster->GetCollisionHeight(), MAP_ALL_LIQUIDS); + if (liquidData.Status) + 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 b5ee426da..568c2fef9 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -444,7 +444,6 @@ public: Map2ZoneCoordinates(zoneX, zoneY, zoneId); - Map const* map = object->GetMap(); float groundZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), MAX_HEIGHT); float floorZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ()); @@ -459,7 +458,7 @@ public: if (haveVMap) { - if (map->IsOutdoors(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ())) + if (object->IsOutdoors()) handler->PSendSysMessage("You are outdoors"); else handler->PSendSysMessage("You are indoors"); @@ -476,11 +475,11 @@ public: cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(), zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap); - LiquidData liquidStatus; - ZLiquidStatus status = map->getLiquidStatus(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), MAP_ALL_LIQUIDS, &liquidStatus); + LiquidData const& liquidData = object->GetLiquidData(); + + if (liquidData.Status) + handler->PSendSysMessage(LANG_LIQUID_STATUS, liquidData.Level, liquidData.DepthLevel, liquidData.Entry, liquidData.Flags, liquidData.Status); - if (status) - handler->PSendSysMessage(LANG_LIQUID_STATUS, liquidStatus.level, liquidStatus.depth_level, liquidStatus.entry, liquidStatus.type_flags, status); if (object->GetTransport()) handler->PSendSysMessage("Transport offset: %.2f, %.2f, %.2f, %.2f", object->m_movementInfo.transport.pos.GetPositionX(), object->m_movementInfo.transport.pos.GetPositionY(), object->m_movementInfo.transport.pos.GetPositionZ(), object->m_movementInfo.transport.pos.GetOrientation()); diff --git a/src/server/scripts/Commands/cs_tele.cpp b/src/server/scripts/Commands/cs_tele.cpp index 5c123c0a3..b3c4750b7 100644 --- a/src/server/scripts/Commands/cs_tele.cpp +++ b/src/server/scripts/Commands/cs_tele.cpp @@ -191,7 +191,7 @@ public: handler->PSendSysMessage(LANG_TELEPORTING_TO, nameLink.c_str(), handler->GetAcoreString(LANG_OFFLINE), tele->name.c_str()); Player::SavePositionInDB(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation, - sMapMgr->GetZoneId(tele->mapId, tele->position_x, tele->position_y, tele->position_z), target_guid); + sMapMgr->GetZoneId(PHASEMASK_NORMAL, tele->mapId, tele->position_x, tele->position_y, tele->position_z), target_guid); } return true; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index 1261cfef3..77c7b87ca 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -2171,7 +2171,7 @@ public: Position pos; caster->GetPosition(&pos); caster->GetNearPosition(pos, 5.0f, 0.0f); - pos.m_positionZ = caster->GetBaseMap()->GetHeight(caster->GetPhaseMask(), pos.GetPositionX(), pos.GetPositionY(), caster->GetPositionZ(), true, 50.0f); + pos.m_positionZ = caster->GetMap()->GetHeight(caster->GetPhaseMask(), pos.GetPositionX(), pos.GetPositionY(), caster->GetPositionZ(), true, 50.0f); pos.m_positionZ += 0.1f; caster->SendMeleeAttackStop(caster->GetVictim()); caster->GetMotionMaster()->MoveLand(POINT_LAND, pos, 7.0f); diff --git a/src/server/shared/DataStores/DBCStructure.h b/src/server/shared/DataStores/DBCStructure.h index 163ee4842..932334de8 100644 --- a/src/server/shared/DataStores/DBCStructure.h +++ b/src/server/shared/DataStores/DBCStructure.h @@ -2004,6 +2004,7 @@ struct VehicleSeatEntry VEHICLE_SEAT_FLAG_B_USABLE_FORCED_3 | VEHICLE_SEAT_FLAG_B_USABLE_FORCED_4))); } [[nodiscard]] bool IsEjectable() const { return m_flagsB & VEHICLE_SEAT_FLAG_B_EJECTABLE; } + [[nodiscard]] bool CanControl() const { return (m_flags & VEHICLE_SEAT_FLAG_CAN_CONTROL) != 0; } }; struct WMOAreaTableEntry