From 47790c971401c33bbe618cc4aa6302c5f715f86b Mon Sep 17 00:00:00 2001 From: UltraNix <80540499+UltraNix@users.noreply.github.com> Date: Sun, 10 Apr 2022 14:24:35 +0200 Subject: [PATCH] =?UTF-8?q?fix(Core/Vmaps):=20Stop=20M2s=20from=20occludin?= =?UTF-8?q?g=20for=20spellcast=20LoS.=20Original=20autho=E2=80=A6=20(#1134?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Core/Vmaps: Stop M2s from occluding for spellcast LoS. Original authors: @Shauren & @HelloKitty Fixes #11293 * buildfix. Co-Authored-By: HelloKitty <5829095+HelloKitty@users.noreply.github.com> --- src/common/Collision/DynamicTree.cpp | 40 +++--- src/common/Collision/DynamicTree.h | 7 +- src/common/Collision/Management/IVMapMgr.h | 3 +- src/common/Collision/Management/VMapMgr2.cpp | 9 +- src/common/Collision/Management/VMapMgr2.h | 4 +- src/common/Collision/Maps/MapTree.cpp | 21 +-- src/common/Collision/Maps/MapTree.h | 5 +- .../Collision/Models/GameObjectModel.cpp | 8 +- src/common/Collision/Models/GameObjectModel.h | 3 +- .../Collision/Models/ModelIgnoreFlags.h | 37 +++++ src/common/Collision/Models/ModelInstance.cpp | 4 +- src/common/Collision/Models/ModelInstance.h | 3 +- src/common/Collision/Models/WorldModel.cpp | 13 +- src/common/Collision/Models/WorldModel.h | 4 +- .../game/Entities/GameObject/GameObject.h | 19 +++ src/server/game/Entities/Object/Object.cpp | 8 +- src/server/game/Entities/Object/Object.h | 5 +- src/server/game/Maps/Map.cpp | 27 +++- src/server/game/Maps/Map.h | 16 ++- .../MovementGenerators/PathGenerator.cpp | 10 +- .../RandomMovementGenerator.cpp | 3 +- src/server/game/Spells/Spell.cpp | 128 +++++++++++++++--- .../game/Spells/SpellInfoCorrections.cpp | 6 - src/server/scripts/Commands/cs_debug.cpp | 4 +- .../boss_blood_prince_council.cpp | 4 +- .../IcecrownCitadel/boss_sindragosa.cpp | 2 +- .../Northrend/Ulduar/Ulduar/ulduar.cpp | 3 +- 27 files changed, 295 insertions(+), 101 deletions(-) create mode 100644 src/common/Collision/Models/ModelIgnoreFlags.h diff --git a/src/common/Collision/DynamicTree.cpp b/src/common/Collision/DynamicTree.cpp index e7020c627..e7538a32a 100644 --- a/src/common/Collision/DynamicTree.cpp +++ b/src/common/Collision/DynamicTree.cpp @@ -19,6 +19,7 @@ #include "BoundingIntervalHierarchyWrapper.h" #include "GameObjectModel.h" #include "MapTree.h" +#include "ModelIgnoreFlags.h" #include "ModelInstance.h" #include "RegularGrid.h" #include "Timer.h" @@ -145,25 +146,33 @@ void DynamicMapTree::update(uint32 t_diff) struct DynamicTreeIntersectionCallback { - bool did_hit; - uint32 phase_mask; - DynamicTreeIntersectionCallback(uint32 phasemask) : did_hit(false), phase_mask(phasemask) { } + DynamicTreeIntersectionCallback(uint32 phasemask, VMAP::ModelIgnoreFlags ignoreFlags) : + _didHit(false), _phaseMask(phasemask), _ignoreFlags(ignoreFlags) { } + bool operator()(const G3D::Ray& r, const GameObjectModel& obj, float& distance, bool stopAtFirstHit) { - bool result = obj.intersectRay(r, distance, stopAtFirstHit, phase_mask); + bool result = obj.intersectRay(r, distance, stopAtFirstHit, _phaseMask, _ignoreFlags); if (result) { - did_hit = result; + _didHit = result; } return result; } - bool didHit() const { return did_hit;} + + [[nodiscard]] bool didHit() const + { + return _didHit; + } + +private: + bool _didHit; + uint32 _phaseMask; + VMAP::ModelIgnoreFlags _ignoreFlags; }; struct DynamicTreeAreaInfoCallback { - DynamicTreeAreaInfoCallback(uint32 phaseMask) - : _phaseMask(phaseMask) {} + DynamicTreeAreaInfoCallback(uint32 phaseMask) : _phaseMask(phaseMask) { } void operator()(G3D::Vector3 const& p, GameObjectModel const& obj) { @@ -176,7 +185,7 @@ struct DynamicTreeAreaInfoCallback } private: - uint32 _phaseMask; + uint32 _phaseMask; VMAP::AreaInfo _areaInfo; }; @@ -206,11 +215,10 @@ private: GameObjectModel const* _hitModel; }; -bool DynamicMapTree::GetIntersectionTime(const uint32 phasemask, const G3D::Ray& ray, - const G3D::Vector3& endPos, float& maxDist) const +bool DynamicMapTree::GetIntersectionTime(const uint32 phasemask, const G3D::Ray& ray, const G3D::Vector3& endPos, float& maxDist) const { float distance = maxDist; - DynamicTreeIntersectionCallback callback(phasemask); + DynamicTreeIntersectionCallback callback(phasemask, VMAP::ModelIgnoreFlags::Nothing); impl->intersectRay(ray, callback, distance, endPos, false); if (callback.didHit()) { @@ -265,7 +273,7 @@ bool DynamicMapTree::GetObjectHitPos(const uint32 phasemask, const G3D::Vector3& return result; } -bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const +bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, VMAP::ModelIgnoreFlags ignoreFlags) const { G3D::Vector3 v1(x1, y1, z1), v2(x2, y2, z2); @@ -277,17 +285,17 @@ bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, flo } G3D::Ray r(v1, (v2 - v1) / maxDist); - DynamicTreeIntersectionCallback callback(phasemask); + DynamicTreeIntersectionCallback callback(phasemask, ignoreFlags); impl->intersectRay(r, callback, maxDist, v2, true); - return !callback.did_hit; + return !callback.didHit(); } float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const { G3D::Vector3 v(x, y, z); G3D::Ray r(v, G3D::Vector3(0, 0, -1)); - DynamicTreeIntersectionCallback callback(phasemask); + DynamicTreeIntersectionCallback callback(phasemask, VMAP::ModelIgnoreFlags::Nothing); impl->intersectZAllignedRay(r, callback, maxSearchDist); if (callback.didHit()) diff --git a/src/common/Collision/DynamicTree.h b/src/common/Collision/DynamicTree.h index e31c3bce6..c9dd5281b 100644 --- a/src/common/Collision/DynamicTree.h +++ b/src/common/Collision/DynamicTree.h @@ -29,6 +29,7 @@ namespace G3D namespace VMAP { struct AreaAndLiquidData; + enum class ModelIgnoreFlags : uint32; } class GameObjectModel; @@ -42,11 +43,9 @@ public: DynamicMapTree(); ~DynamicMapTree(); - [[nodiscard]] bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, - float z2, uint32 phasemask) const; + [[nodiscard]] bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, VMAP::ModelIgnoreFlags ignoreFlags) const; - bool GetIntersectionTime(uint32 phasemask, const G3D::Ray& ray, - const G3D::Vector3& endPos, float& maxDist) const; + 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; diff --git a/src/common/Collision/Management/IVMapMgr.h b/src/common/Collision/Management/IVMapMgr.h index b1b516ee5..260188e7b 100644 --- a/src/common/Collision/Management/IVMapMgr.h +++ b/src/common/Collision/Management/IVMapMgr.h @@ -19,6 +19,7 @@ #define _IVMAPMANAGER_H #include "Define.h" +#include "ModelIgnoreFlags.h" #include "Optional.h" #include @@ -91,7 +92,7 @@ namespace VMAP virtual void unloadMap(unsigned int pMapId, int x, int y) = 0; virtual void unloadMap(unsigned int pMapId) = 0; - virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) = 0; + virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) = 0; virtual float getHeight(unsigned int pMapId, float x, float y, float z, float maxSearchDist) = 0; /** test if we hit an object. return true if we hit one. rx, ry, rz will hold the hit position or the dest position, if no intersection was found diff --git a/src/common/Collision/Management/VMapMgr2.cpp b/src/common/Collision/Management/VMapMgr2.cpp index 3fdbe5cab..46df0b330 100644 --- a/src/common/Collision/Management/VMapMgr2.cpp +++ b/src/common/Collision/Management/VMapMgr2.cpp @@ -171,7 +171,7 @@ namespace VMAP } } - bool VMapMgr2::isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2) + bool VMapMgr2::isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) { #if defined(ENABLE_VMAP_CHECKS) if (!isLineOfSightCalcEnabled() || IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LOS)) @@ -187,7 +187,7 @@ namespace VMAP Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2); if (pos1 != pos2) { - return instanceTree->second->isInLineOfSight(pos1, pos2); + return instanceTree->second->isInLineOfSight(pos1, pos2, ignoreFlags); } } @@ -337,7 +337,7 @@ namespace VMAP } } - WorldModel* VMapMgr2::acquireModelInstance(const std::string& basepath, const std::string& filename) + WorldModel* VMapMgr2::acquireModelInstance(const std::string& basepath, const std::string& filename, uint32 flags/* Only used when creating the model */) { //! Critical section, thread safe access to iLoadedModelFiles std::lock_guard lock(LoadedModelFilesLock); @@ -353,6 +353,9 @@ namespace VMAP return nullptr; } LOG_DEBUG("maps", "VMapMgr2: loading file '{}{}'", basepath, filename); + + worldmodel->Flags = flags; + model = iLoadedModelFiles.insert(std::pair(filename, ManagedModel())).first; model->second.setModel(worldmodel); } diff --git a/src/common/Collision/Management/VMapMgr2.h b/src/common/Collision/Management/VMapMgr2.h index 39110c1a8..4c37a95b4 100644 --- a/src/common/Collision/Management/VMapMgr2.h +++ b/src/common/Collision/Management/VMapMgr2.h @@ -107,7 +107,7 @@ namespace VMAP void unloadMap(unsigned int mapId, int x, int y) override; void unloadMap(unsigned int mapId) override; - bool isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2) override ; + bool isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) override ; /** fill the hit pos and return true, if an object was hit */ @@ -120,7 +120,7 @@ namespace VMAP 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); + WorldModel* acquireModelInstance(const std::string& basepath, const std::string& filename, uint32 flags); void releaseModelInstance(const std::string& filename); // what's the use of this? o.O diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp index 33ba1b0dc..2223c58d3 100644 --- a/src/common/Collision/Maps/MapTree.cpp +++ b/src/common/Collision/Maps/MapTree.cpp @@ -34,10 +34,10 @@ namespace VMAP class MapRayCallback { public: - MapRayCallback(ModelInstance* val): prims(val), hit(false) {} + MapRayCallback(ModelInstance* val, ModelIgnoreFlags ignoreFlags): prims(val), flags(ignoreFlags), hit(false) { } bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool StopAtFirstHit) { - bool result = prims[entry].intersectRay(ray, distance, StopAtFirstHit); + bool result = prims[entry].intersectRay(ray, distance, StopAtFirstHit, flags); if (result) { hit = true; @@ -47,6 +47,7 @@ namespace VMAP bool didHit() { return hit; } protected: ModelInstance* prims; + ModelIgnoreFlags flags; bool hit; }; @@ -143,10 +144,10 @@ namespace VMAP Else, pMaxDist is not modified and returns false; */ - bool StaticMapTree::GetIntersectionTime(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit) const + bool StaticMapTree::GetIntersectionTime(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit, ModelIgnoreFlags ignoreFlags) const { float distance = pMaxDist; - MapRayCallback intersectionCallBack(iTreeValues); + MapRayCallback intersectionCallBack(iTreeValues, ignoreFlags); iTree.intersectRay(pRay, intersectionCallBack, distance, StopAtFirstHit); if (intersectionCallBack.didHit()) { @@ -156,7 +157,7 @@ namespace VMAP } //========================================================= - bool StaticMapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2) const + bool StaticMapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2, ModelIgnoreFlags ignoreFlags) const { float maxDist = (pos2 - pos1).magnitude(); // return false if distance is over max float, in case of cheater teleporting to the end of the universe @@ -175,7 +176,7 @@ namespace VMAP // direction with length of 1 G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1) / maxDist); - if (GetIntersectionTime(ray, maxDist, true)) + if (GetIntersectionTime(ray, maxDist, true, ignoreFlags)) { return false; @@ -204,7 +205,7 @@ namespace VMAP Vector3 dir = (pPos2 - pPos1) / maxDist; // direction with length of 1 G3D::Ray ray(pPos1, dir); float dist = maxDist; - if (GetIntersectionTime(ray, dist, false)) + if (GetIntersectionTime(ray, dist, false, ModelIgnoreFlags::Nothing)) { pResultHitPos = pPos1 + dir * dist; if (pModifyDist < 0) @@ -240,7 +241,7 @@ namespace VMAP Vector3 dir = Vector3(0, 0, -1); G3D::Ray ray(pPos, dir); // direction with length of 1 float maxDist = maxSearchDist; - if (GetIntersectionTime(ray, maxDist, false)) + if (GetIntersectionTime(ray, maxDist, false, ModelIgnoreFlags::Nothing)) { height = pPos.z - maxDist; } @@ -328,7 +329,7 @@ namespace VMAP #endif if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn)) { - WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name); + WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags); //VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading {}", spawn.name); if (model) { @@ -404,7 +405,7 @@ namespace VMAP if (result) { // acquire model instance - WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name); + WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags); if (!model) { LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [{}, {}]", tileX, tileY); diff --git a/src/common/Collision/Maps/MapTree.h b/src/common/Collision/Maps/MapTree.h index 8bfc454ae..c7d6e5939 100644 --- a/src/common/Collision/Maps/MapTree.h +++ b/src/common/Collision/Maps/MapTree.h @@ -27,6 +27,7 @@ namespace VMAP class ModelInstance; class GroupModel; class VMapMgr2; + enum class ModelIgnoreFlags : uint32; enum class LoadResult : uint8; struct LocationInfo @@ -58,7 +59,7 @@ namespace VMAP std::string iBasePath; private: - bool GetIntersectionTime(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit) const; + bool GetIntersectionTime(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit, ModelIgnoreFlags ignoreFlags) const; //bool containsLoadedMapTile(unsigned int pTileIdent) const { return(iLoadedMapTiles.containsKey(pTileIdent)); } public: static std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY); @@ -69,7 +70,7 @@ namespace VMAP StaticMapTree(uint32 mapID, const std::string& basePath); ~StaticMapTree(); - [[nodiscard]] bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2) const; + [[nodiscard]] bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2, ModelIgnoreFlags ignoreFlags) const; bool GetObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist) const; [[nodiscard]] float getHeight(const G3D::Vector3& pPos, float maxSearchDist) const; bool GetAreaInfo(G3D::Vector3& pos, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const; diff --git a/src/common/Collision/Models/GameObjectModel.cpp b/src/common/Collision/Models/GameObjectModel.cpp index 23ef35ae1..eb4f58b8e 100644 --- a/src/common/Collision/Models/GameObjectModel.cpp +++ b/src/common/Collision/Models/GameObjectModel.cpp @@ -18,6 +18,7 @@ #include "GameObjectModel.h" #include "Log.h" #include "MapTree.h" +#include "ModelInstance.h" #include "Timer.h" #include "VMapDefinitions.h" #include "VMapFactory.h" @@ -123,7 +124,8 @@ bool GameObjectModel::initialize(std::unique_ptr model return false; } - iModel = VMAP::VMapFactory::createOrGetVMapMgr()->acquireModelInstance(dataPath + "vmaps/", it->second.name); + iModel = VMAP::VMapFactory::createOrGetVMapMgr()->acquireModelInstance(dataPath + "vmaps/", it->second.name, + it->second.isWmo ? VMAP::ModelFlags::MOD_WORLDSPAWN : VMAP::ModelFlags::MOD_M2); if (!iModel) { @@ -174,7 +176,7 @@ GameObjectModel* GameObjectModel::Create(std::unique_ptrIsSpawned()) { @@ -191,7 +193,7 @@ bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool Sto Vector3 p = iInvRot * (ray.origin() - iPos) * iInvScale; Ray modRay(p, iInvRot * ray.direction()); float distance = MaxDist * iInvScale; - bool hit = iModel->IntersectRay(modRay, distance, StopAtFirstHit); + bool hit = iModel->IntersectRay(modRay, distance, StopAtFirstHit, ignoreFlags); if (hit) { distance *= iScale; diff --git a/src/common/Collision/Models/GameObjectModel.h b/src/common/Collision/Models/GameObjectModel.h index bd03c25ff..d0f8e35f4 100644 --- a/src/common/Collision/Models/GameObjectModel.h +++ b/src/common/Collision/Models/GameObjectModel.h @@ -29,6 +29,7 @@ namespace VMAP class WorldModel; struct AreaInfo; struct LocationInfo; + enum class ModelIgnoreFlags : uint32; } class GameObject; @@ -68,7 +69,7 @@ public: [[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; + bool intersectRay(const G3D::Ray& Ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask, VMAP::ModelIgnoreFlags ignoreFlags) 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; diff --git a/src/common/Collision/Models/ModelIgnoreFlags.h b/src/common/Collision/Models/ModelIgnoreFlags.h new file mode 100644 index 000000000..f54da68ef --- /dev/null +++ b/src/common/Collision/Models/ModelIgnoreFlags.h @@ -0,0 +1,37 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef ModelIgnoreFlags_h__ +#define ModelIgnoreFlags_h__ + +#include "Define.h" + +namespace VMAP +{ + enum class ModelIgnoreFlags : uint32 + { + Nothing = 0x00, + M2 = 0x01 + }; + + inline ModelIgnoreFlags operator&(ModelIgnoreFlags left, ModelIgnoreFlags right) + { + return ModelIgnoreFlags(uint32(left) & uint32(right)); + } +} + +#endif // ModelIgnoreFlags_h__ diff --git a/src/common/Collision/Models/ModelInstance.cpp b/src/common/Collision/Models/ModelInstance.cpp index 4634b006c..4510461bd 100644 --- a/src/common/Collision/Models/ModelInstance.cpp +++ b/src/common/Collision/Models/ModelInstance.cpp @@ -30,7 +30,7 @@ namespace VMAP iInvScale = 1.f / iScale; } - bool ModelInstance::intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit) const + bool ModelInstance::intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit, ModelIgnoreFlags ignoreFlags) const { if (!iModel) { @@ -54,7 +54,7 @@ namespace VMAP Vector3 p = iInvRot * (pRay.origin() - iPos) * iInvScale; Ray modRay(p, iInvRot * pRay.direction()); float distance = pMaxDist * iInvScale; - bool hit = iModel->IntersectRay(modRay, distance, StopAtFirstHit); + bool hit = iModel->IntersectRay(modRay, distance, StopAtFirstHit, ignoreFlags); if (hit) { distance *= iScale; diff --git a/src/common/Collision/Models/ModelInstance.h b/src/common/Collision/Models/ModelInstance.h index 9e8ff652c..25987f3fd 100644 --- a/src/common/Collision/Models/ModelInstance.h +++ b/src/common/Collision/Models/ModelInstance.h @@ -29,6 +29,7 @@ namespace VMAP class WorldModel; struct AreaInfo; struct LocationInfo; + enum class ModelIgnoreFlags : uint32; enum ModelFlags { @@ -64,7 +65,7 @@ namespace VMAP ModelInstance() { } ModelInstance(const ModelSpawn& spawn, WorldModel* model); void setUnloaded() { iModel = nullptr; } - bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit) const; + bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit, ModelIgnoreFlags ignoreFlags) const; void intersectPoint(const G3D::Vector3& p, AreaInfo& info) const; bool GetLocationInfo(const G3D::Vector3& p, LocationInfo& info) const; bool GetLiquidLevel(const G3D::Vector3& p, LocationInfo& info, float& liqHeight) const; diff --git a/src/common/Collision/Models/WorldModel.cpp b/src/common/Collision/Models/WorldModel.cpp index ab1c585a1..985035415 100644 --- a/src/common/Collision/Models/WorldModel.cpp +++ b/src/common/Collision/Models/WorldModel.cpp @@ -17,6 +17,7 @@ #include "WorldModel.h" #include "MapTree.h" +#include "ModelIgnoreFlags.h" #include "ModelInstance.h" #include "VMapDefinitions.h" @@ -485,8 +486,18 @@ namespace VMAP bool hit; }; - bool WorldModel::IntersectRay(const G3D::Ray& ray, float& distance, bool stopAtFirstHit) const + bool WorldModel::IntersectRay(const G3D::Ray& ray, float& distance, bool stopAtFirstHit, ModelIgnoreFlags ignoreFlags) const { + // If the caller asked us to ignore certain objects we should check flags + if ((ignoreFlags & ModelIgnoreFlags::M2) != ModelIgnoreFlags::Nothing) + { + // M2 models are not taken into account for LoS calculation if caller requested their ignoring. + if (Flags & MOD_M2) + { + return false; + } + } + // small M2 workaround, maybe better make separate class with virtual intersection funcs // in any case, there's no need to use a bound tree if we only have one submodel if (groupModels.size() == 1) diff --git a/src/common/Collision/Models/WorldModel.h b/src/common/Collision/Models/WorldModel.h index a9268f885..b1a268318 100644 --- a/src/common/Collision/Models/WorldModel.h +++ b/src/common/Collision/Models/WorldModel.h @@ -30,6 +30,7 @@ namespace VMAP class TreeNode; struct AreaInfo; struct LocationInfo; + enum class ModelIgnoreFlags : uint32; class MeshTriangle { @@ -108,12 +109,13 @@ namespace VMAP //! pass group models to WorldModel and create BIH. Passed vector is swapped with old geometry! void setGroupModels(std::vector& models); void setRootWmoID(uint32 id) { RootWMOID = id; } - bool IntersectRay(const G3D::Ray& ray, float& distance, bool stopAtFirstHit) const; + bool IntersectRay(const G3D::Ray& ray, float& distance, bool stopAtFirstHit, ModelIgnoreFlags ignoreFlags) const; bool IntersectPoint(const G3D::Vector3& p, const G3D::Vector3& down, float& dist, AreaInfo& info) const; bool GetLocationInfo(const G3D::Vector3& p, const G3D::Vector3& down, float& dist, LocationInfo& info) const; bool writeFile(const std::string& filename); bool readFile(const std::string& filename); void GetGroupModels(std::vector& outGroupModels); + uint32 Flags; protected: uint32 RootWMOID{0}; std::vector groupModels; diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index cd96cb194..ad344bb9a 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -647,6 +647,25 @@ struct GameObjectTemplate { return IsForQuests; } + + [[nodiscard]] bool IsIgnoringLOSChecks() const + { + switch (type) + { + case GAMEOBJECT_TYPE_BUTTON: + return button.losOK == 0; + case GAMEOBJECT_TYPE_QUESTGIVER: + return questgiver.losOK == 0; + case GAMEOBJECT_TYPE_CHEST: + return chest.losOK == 0; + case GAMEOBJECT_TYPE_GOOBER: + return goober.losOK == 0; + case GAMEOBJECT_TYPE_FLAGSTAND: + return flagstand.losOK == 0; + default: + return false; + } + } }; // From `gameobject_template_addon` diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index d22df812a..74dc8cecd 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1320,7 +1320,7 @@ bool WorldObject::IsWithinDistInMap(WorldObject const* obj, float dist2compare, return obj && IsInMap(obj) && InSamePhase(obj) && _IsWithinDist(obj, dist2compare, is3D, useBoundingRadius); } -bool WorldObject::IsWithinLOS(float ox, float oy, float oz, LineOfSightChecks checks) const +bool WorldObject::IsWithinLOS(float ox, float oy, float oz, VMAP::ModelIgnoreFlags ignoreFlags, LineOfSightChecks checks) const { if (IsInWorld()) { @@ -1336,12 +1336,12 @@ bool WorldObject::IsWithinLOS(float ox, float oy, float oz, LineOfSightChecks ch GetHitSpherePointFor({ ox, oy, oz }, x, y, z); } - return GetMap()->isInLineOfSight(x, y, z, ox, oy, oz, GetPhaseMask(), checks); + return GetMap()->isInLineOfSight(x, y, z, ox, oy, oz, GetPhaseMask(), checks, ignoreFlags); } return true; } -bool WorldObject::IsWithinLOSInMap(WorldObject const* obj, LineOfSightChecks checks) const +bool WorldObject::IsWithinLOSInMap(WorldObject const* obj, VMAP::ModelIgnoreFlags ignoreFlags, LineOfSightChecks checks) const { if (!IsInMap(obj)) return false; @@ -1364,7 +1364,7 @@ bool WorldObject::IsWithinLOSInMap(WorldObject const* obj, LineOfSightChecks che else GetHitSpherePointFor({ obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ() + obj->GetCollisionHeight() }, x, y, z); - return GetMap()->isInLineOfSight(x, y, z, ox, oy, oz, GetPhaseMask(), checks); + return GetMap()->isInLineOfSight(x, y, z, ox, oy, oz, GetPhaseMask(), checks, ignoreFlags); } void WorldObject::GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 5c5a44ca4..0211ebad4 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -24,6 +24,7 @@ #include "GridDefines.h" #include "GridReference.h" #include "Map.h" +#include "ModelIgnoreFlags.h" #include "ObjectDefines.h" #include "ObjectGuid.h" #include "Optional.h" @@ -455,8 +456,8 @@ public: // use only if you will sure about placing both object at same map bool IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D = true, bool useBoundingRadius = true) const; bool IsWithinDistInMap(WorldObject const* obj, float dist2compare, bool is3D = true, bool useBoundingRadius = true) const; - [[nodiscard]] bool IsWithinLOS(float x, float y, float z, LineOfSightChecks checks = LINEOFSIGHT_ALL_CHECKS) const; - bool IsWithinLOSInMap(WorldObject const* obj, LineOfSightChecks checks = LINEOFSIGHT_ALL_CHECKS) const; + [[nodiscard]] bool IsWithinLOS(float x, float y, float z, VMAP::ModelIgnoreFlags ignoreFlags = VMAP::ModelIgnoreFlags::Nothing, LineOfSightChecks checks = LINEOFSIGHT_ALL_CHECKS) const; + [[nodiscard]] bool IsWithinLOSInMap(WorldObject const* obj, VMAP::ModelIgnoreFlags ignoreFlags = VMAP::ModelIgnoreFlags::Nothing, LineOfSightChecks checks = LINEOFSIGHT_ALL_CHECKS) const; [[nodiscard]] Position GetHitSpherePointFor(Position const& dest) const; void GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const; bool GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D = true) const; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index bd511806e..5bb676759 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -2023,7 +2023,7 @@ Transport* Map::GetTransportForPos(uint32 phase, float x, float y, float z, Worl if ((*itr)->IsInWorld() && (*itr)->GetExactDistSq(x, y, z) < 75.0f * 75.0f && (*itr)->m_model) { float dist = 30.0f; - bool hit = (*itr)->m_model->intersectRay(r, dist, false, phase); + bool hit = (*itr)->m_model->intersectRay(r, dist, false, phase, VMAP::ModelIgnoreFlags::Nothing); if (hit) return *itr; } @@ -2033,7 +2033,7 @@ Transport* Map::GetTransportForPos(uint32 phase, float x, float y, float z, Worl if (staticTrans->m_model) { float dist = 10.0f; - bool hit = staticTrans->m_model->intersectRay(r, dist, false, phase); + bool hit = staticTrans->m_model->intersectRay(r, dist, false, phase, VMAP::ModelIgnoreFlags::Nothing); if (hit) if (GetHeight(phase, x, y, z, true, 30.0f) < (v.z - dist + 1.0f)) return staticTrans->ToTransport(); @@ -2440,14 +2440,27 @@ float Map::GetWaterLevel(float x, float y) const return 0; } -bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks) const +bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const { - if ((checks & LINEOFSIGHT_CHECK_VMAP) && !VMAP::VMapFactory::createOrGetVMapMgr()->isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2)) + if ((checks & LINEOFSIGHT_CHECK_VMAP) && !VMAP::VMapFactory::createOrGetVMapMgr()->isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2, ignoreFlags)) + { return false; + } + + if (sWorld->getBoolConfig(CONFIG_CHECK_GOBJECT_LOS) && (checks & LINEOFSIGHT_CHECK_GOBJECT_ALL)) + { + ignoreFlags = VMAP::ModelIgnoreFlags::Nothing; + if (!(checks & LINEOFSIGHT_CHECK_GOBJECT_M2)) + { + ignoreFlags = VMAP::ModelIgnoreFlags::M2; + } + + if (!_dynamicTree.isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask, ignoreFlags)) + { + return false; + } + } - if (sWorld->getBoolConfig(CONFIG_CHECK_GOBJECT_LOS) && (checks & LINEOFSIGHT_CHECK_GOBJECT) - && !_dynamicTree.isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask)) - return false; return true; } diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index dbee10fc3..b957baa35 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -63,6 +63,11 @@ class PathGenerator; enum WeatherState : uint32; +namespace VMAP +{ + enum class ModelIgnoreFlags : uint32; +} + namespace Acore { struct ObjectUpdater; @@ -183,10 +188,13 @@ struct PositionFullTerrainStatus enum LineOfSightChecks { - LINEOFSIGHT_CHECK_VMAP = 0x1, // check static floor layout data - LINEOFSIGHT_CHECK_GOBJECT = 0x2, // check dynamic game object data + LINEOFSIGHT_CHECK_VMAP = 0x1, // check static floor layout data + LINEOFSIGHT_CHECK_GOBJECT_WMO = 0x2, // check dynamic game object data (wmo models) + LINEOFSIGHT_CHECK_GOBJECT_M2 = 0x4, // check dynamic game object data (m2 models) - LINEOFSIGHT_ALL_CHECKS = (LINEOFSIGHT_CHECK_VMAP | LINEOFSIGHT_CHECK_GOBJECT) + LINEOFSIGHT_CHECK_GOBJECT_ALL = LINEOFSIGHT_CHECK_GOBJECT_WMO | LINEOFSIGHT_CHECK_GOBJECT_M2, + + LINEOFSIGHT_ALL_CHECKS = LINEOFSIGHT_CHECK_VMAP | LINEOFSIGHT_CHECK_GOBJECT_ALL }; class GridMap @@ -539,7 +547,7 @@ public: float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = nullptr, bool swim = false, float collisionHeight = DEFAULT_COLLISION_HEIGHT) const; [[nodiscard]] float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; - [[nodiscard]] bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks) const; + [[nodiscard]] bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const; bool CanReachPositionAndGetValidCoords(WorldObject const* source, PathGenerator *path, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const; bool CanReachPositionAndGetValidCoords(WorldObject const* source, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const; bool CanReachPositionAndGetValidCoords(WorldObject const* source, float startX, float startY, float startZ, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const; diff --git a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp index a727aafb7..b07a0c9e4 100644 --- a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp @@ -1052,12 +1052,10 @@ void PathGenerator::ShortenPathUntilDist(G3D::Vector3 const& target, float dist) // check if the shortened path is still in LoS with the target and it is walkable _source->GetHitSpherePointFor({ _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z + collisionHeight }, x, y, z); - if (!_source->GetMap()->isInLineOfSight(x, y, z, _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z + collisionHeight, _source->GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS) - || (canCheckSlope - && !IsSwimmableSegment(_source->GetPositionX(), _source->GetPositionY(), _source->GetPositionZ(), _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z) - && !IsWalkableClimb(_source->GetPositionX(), _source->GetPositionY(), _source->GetPositionZ(), _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z) - ) - ) + if (!_source->GetMap()->isInLineOfSight(x, y, z, _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z + collisionHeight, + _source->GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing) || (canCheckSlope && + !IsSwimmableSegment(_source->GetPositionX(), _source->GetPositionY(), _source->GetPositionZ(), _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z) && + !IsWalkableClimb(_source->GetPositionX(), _source->GetPositionY(), _source->GetPositionZ(), _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z))) { // whenver we find a point that is not valid anymore, simply use last valid path _pathPoints.resize(i + 1); diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp index 02c920ae3..73ee0b5c3 100644 --- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp @@ -165,7 +165,8 @@ void RandomMovementGenerator::_setRandomLocation(Creature* creature) return; } - if (!map->isInLineOfSight((*itr).x, (*itr).y, (*itr).z + 2.f, (*itrNext).x, (*itrNext).y, (*itrNext).z + 2.f, creature->GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS)) + if (!map->isInLineOfSight((*itr).x, (*itr).y, (*itr).z + 2.f, (*itrNext).x, (*itrNext).y, (*itrNext).z + 2.f, creature->GetPhaseMask(), + LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing)) { _validPointsVector[_currentPoint].erase(randomIter); _preComputedPaths.erase(pathIdx); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 4e236d5ad..f67683d0a 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2049,7 +2049,7 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar if (Unit* unit = (*itr)->ToUnit()) { uint32 deficit = unit->GetMaxHealth() - unit->GetHealth(); - if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && target->IsWithinDist(unit, jumpRadius) && target->IsWithinLOSInMap(unit, LINEOFSIGHT_ALL_CHECKS)) + if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && target->IsWithinDist(unit, jumpRadius) && target->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::M2)) { foundItr = itr; maxHPDeficit = deficit; @@ -2064,10 +2064,10 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar { if (foundItr == tempTargets.end()) { - if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS)) + if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } - else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS)) + else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } } @@ -5617,9 +5617,38 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER) && !target->HasInArc(static_cast(M_PI), m_caster)) return SPELL_FAILED_NOT_INFRONT; - if (m_caster->GetEntry() != WORLD_TRIGGER) // Ignore LOS for gameobjects casts (wrongly casted by a trigger) - if ((!m_caster->IsTotem() || !m_spellInfo->IsPositive()) && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) && !m_spellInfo->HasAttribute(SPELL_ATTR5_ALWAYS_AOE_LINE_OF_SIGHT) && !m_caster->IsWithinLOSInMap(target, LINEOFSIGHT_ALL_CHECKS) && !(m_spellFlags & SPELL_FLAG_REDIRECTED)) + if ((!m_caster->IsTotem() || !m_spellInfo->IsPositive()) && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) && + !m_spellInfo->HasAttribute(SPELL_ATTR5_ALWAYS_AOE_LINE_OF_SIGHT) && !(m_spellFlags & SPELL_FLAG_REDIRECTED)) + { + WorldObject* losCenter = nullptr; + uint32 losChecks = LINEOFSIGHT_ALL_CHECKS; + if (m_originalCasterGUID.IsGameObject()) + { + losCenter = m_caster->GetMap()->GetGameObject(m_originalCasterGUID); + } + else if (m_caster->GetEntry() == WORLD_TRIGGER) + { + if (TempSummon* tempSummon = m_caster->ToTempSummon()) + { + losCenter = tempSummon->GetSummonerGameObject(); + } + } + + if (losCenter) + { + // If spell casted by gameobject then ignore M2 models + losChecks &= ~LINEOFSIGHT_CHECK_GOBJECT_M2; + } + else + { + losCenter = m_caster; + } + + if (!losCenter->IsWithinLOSInMap(target, VMAP::ModelIgnoreFlags::M2, LineOfSightChecks(losChecks))) + { return SPELL_FAILED_LINE_OF_SIGHT; + } + } } } @@ -5629,8 +5658,38 @@ SpellCastResult Spell::CheckCast(bool strict) float x, y, z; m_targets.GetDstPos()->GetPosition(x, y, z); - if ((!m_caster->IsTotem() || !m_spellInfo->IsPositive()) && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) && !m_spellInfo->HasAttribute(SPELL_ATTR5_ALWAYS_AOE_LINE_OF_SIGHT) && !m_caster->IsWithinLOS(x, y, z, LINEOFSIGHT_ALL_CHECKS)) - return SPELL_FAILED_LINE_OF_SIGHT; + if ((!m_caster->IsTotem() || !m_spellInfo->IsPositive()) && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) && + !m_spellInfo->HasAttribute(SPELL_ATTR5_ALWAYS_AOE_LINE_OF_SIGHT)) + { + WorldObject* losCenter = nullptr; + uint32 losChecks = LINEOFSIGHT_ALL_CHECKS; + if (m_originalCasterGUID.IsGameObject()) + { + losCenter = m_caster->GetMap()->GetGameObject(m_originalCasterGUID); + } + else if (m_caster->GetEntry() == WORLD_TRIGGER) + { + if (TempSummon* tempSummon = m_caster->ToTempSummon()) + { + losCenter = tempSummon->GetSummonerGameObject(); + } + } + + if (losCenter) + { + // If spell casted by gameobject then ignore M2 models + losChecks &= ~LINEOFSIGHT_CHECK_GOBJECT_M2; + } + else + { + losCenter = m_caster; + } + + if (!losCenter->IsWithinLOS(x, y, z, VMAP::ModelIgnoreFlags::M2, LineOfSightChecks((losChecks)))) + { + return SPELL_FAILED_LINE_OF_SIGHT; + } + } } // check pet presence @@ -7626,7 +7685,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const { case SPELL_EFFECT_RESURRECT_NEW: // player far away, maybe his corpse near? - if (target != m_caster && !target->IsWithinLOSInMap(m_caster, LINEOFSIGHT_ALL_CHECKS)) + if (target != m_caster && !target->IsWithinLOSInMap(m_caster)) { if (!m_targets.GetCorpseTargetGUID()) return false; @@ -7638,7 +7697,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const if (target->GetGUID() != corpse->GetOwnerGUID()) return false; - if (!corpse->IsWithinLOSInMap(m_caster, LINEOFSIGHT_ALL_CHECKS) && !(m_spellFlags & SPELL_FLAG_REDIRECTED)) + if (!corpse->IsWithinLOSInMap(m_caster) && !(m_spellFlags & SPELL_FLAG_REDIRECTED)) return false; } break; @@ -7646,7 +7705,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const { if (!m_targets.GetCorpseTargetGUID()) { - if (target->IsWithinLOSInMap(m_caster, LINEOFSIGHT_ALL_CHECKS) && target->HasUnitFlag(UNIT_FLAG_SKINNABLE)) + if (target->IsWithinLOSInMap(m_caster, VMAP::ModelIgnoreFlags::M2) && target->HasUnitFlag(UNIT_FLAG_SKINNABLE)) return true; return false; @@ -7662,7 +7721,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const if (!corpse->HasFlag(CORPSE_FIELD_FLAGS, CORPSE_FLAG_LOOTABLE)) return false; - if (!corpse->IsWithinLOSInMap(m_caster, LINEOFSIGHT_ALL_CHECKS)) + if (!corpse->IsWithinLOSInMap(m_caster, VMAP::ModelIgnoreFlags::M2)) return false; } break; @@ -7676,14 +7735,40 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const if (target->ToPlayer()->getLevel() >= sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL)) return false; break; - - default: // normal case - // Get GO cast coordinates if original caster -> GO - WorldObject* caster = nullptr; + default: // normal case + { + uint32 losChecks = LINEOFSIGHT_ALL_CHECKS; + GameObject* gobCaster = nullptr; if (m_originalCasterGUID.IsGameObject()) - caster = m_caster->GetMap()->GetGameObject(m_originalCasterGUID); - if (!caster) + { + gobCaster = m_caster->GetMap()->GetGameObject(m_originalCasterGUID); + } + else if (m_caster->GetEntry() == WORLD_TRIGGER) + { + if (TempSummon* tempSummon = m_caster->ToTempSummon()) + { + gobCaster = tempSummon->GetSummonerGameObject(); + } + } + + WorldObject* caster = nullptr; + if (gobCaster) + { + if (gobCaster->GetGOInfo()->IsIgnoringLOSChecks()) + { + return true; + } + + caster = gobCaster; + + // If spell casted by gameobject then ignore M2 models + losChecks &= ~LINEOFSIGHT_CHECK_GOBJECT_M2; + } + else + { caster = m_caster; + } + if (target != m_caster) { if (m_targets.HasDst()) @@ -7692,13 +7777,18 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const float y = m_targets.GetDstPos()->GetPositionY(); float z = m_targets.GetDstPos()->GetPositionZ(); - if (!target->IsWithinLOS(x, y, z, LINEOFSIGHT_ALL_CHECKS)) + if (!target->IsWithinLOS(x, y, z, VMAP::ModelIgnoreFlags::M2, LineOfSightChecks(losChecks))) + { return false; + } } - else if (!target->IsWithinLOSInMap(caster, LINEOFSIGHT_ALL_CHECKS)) + else if (!target->IsWithinLOSInMap(caster, VMAP::ModelIgnoreFlags::M2, LineOfSightChecks(losChecks))) + { return false; + } } break; + } } return true; diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 4c1a95971..5ee82985b 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -4229,12 +4229,6 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->AttributesEx |= SPELL_ATTR1_EXCLUDE_CASTER; }); - // Suppression Aura - ApplySpellFix({ 22247 }, [](SpellInfo* spellInfo) - { - spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; - }); - // Cosmetic - Lightning Beam Channel ApplySpellFix({ 45537 }, [](SpellInfo* spellInfo) { diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 9e159fbd8..dda74b0c8 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -1000,8 +1000,8 @@ public: { Player* player = handler->GetSession()->GetPlayer(); handler->PSendSysMessage("Checking LoS %s -> %s:", player->GetName().c_str(), unit->GetName().c_str()); - handler->PSendSysMessage(" VMAP LoS: %s", player->IsWithinLOSInMap(unit, LINEOFSIGHT_CHECK_VMAP) ? "clear" : "obstructed"); - handler->PSendSysMessage(" GObj LoS: %s", player->IsWithinLOSInMap(unit, LINEOFSIGHT_CHECK_GOBJECT) ? "clear" : "obstructed"); + handler->PSendSysMessage(" VMAP LoS: %s", player->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::Nothing, LINEOFSIGHT_CHECK_VMAP) ? "clear" : "obstructed"); + handler->PSendSysMessage(" GObj LoS: %s", player->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::Nothing, LINEOFSIGHT_CHECK_GOBJECT_ALL) ? "clear" : "obstructed"); handler->PSendSysMessage("%s is %sin line of sight of %s.", unit->GetName().c_str(), (player->IsWithinLOSInMap(unit) ? "" : "not "), player->GetName().c_str()); return true; } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp index 13e20f184..c6c79f0db 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp @@ -1763,7 +1763,9 @@ public: { float destX = summoner->GetPositionX() + cos(angle + a * M_PI) * i * 10.0f; float destY = summoner->GetPositionY() + std::sin(angle + a * M_PI) * i * 10.0f; - if (summoner->GetMap()->isInLineOfSight(summoner->GetPositionX(), summoner->GetPositionY(), summoner->GetPositionZ() + 10.0f, destX, destY, summoner->GetPositionZ() + 10.0f, summoner->GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS) && destX > 4585.0f && destY > 2716.0f && destY < 2822.0f) + if (summoner->GetMap()->isInLineOfSight(summoner->GetPositionX(), summoner->GetPositionY(), summoner->GetPositionZ() + 10.0f, destX, destY, + summoner->GetPositionZ() + 10.0f, summoner->GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing) && + destX > 4585.0f && destY > 2716.0f && destY < 2822.0f) { float destZ = summoner->GetMapHeight(summoner->GetPhaseMask(), destX, destY, summoner->GetPositionZ()); if (std::fabs(destZ - summoner->GetPositionZ()) < 10.0f) // valid z found diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp index 842462c55..17e11627f 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp @@ -1285,7 +1285,7 @@ public: float ox, oy, oz; _caster->GetPosition(ox, oy, oz); DynamicMapTree const& dTree = unit->GetMap()->GetDynamicMapTree(); - return !dTree.isInLineOfSight(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ() + 2.f, ox, oy, oz + 2.f, unit->GetPhaseMask()); + return !dTree.isInLineOfSight(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ() + 2.f, ox, oy, oz + 2.f, unit->GetPhaseMask(), VMAP::ModelIgnoreFlags::Nothing); } private: diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp index fe4900a3e..11f283e3a 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp @@ -125,7 +125,8 @@ public: void MoveInLineOfSight(Unit* who) override { if (!activated && who->GetTypeId() == TYPEID_PLAYER) - if (me->GetExactDist2d(who) <= 25.0f && me->GetMap()->isInLineOfSight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 5.0f, who->GetPositionX(), who->GetPositionY(), who->GetPositionZ() + 5.0f, 2, LINEOFSIGHT_ALL_CHECKS)) + if (me->GetExactDist2d(who) <= 25.0f && me->GetMap()->isInLineOfSight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 5.0f, + who->GetPositionX(), who->GetPositionY(), who->GetPositionZ() + 5.0f, 2, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing)) { activated = true; me->RemoveAura(64615);