diff options
author | Takenbacon <revoke1336@live.com> | 2025-09-07 04:02:03 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-09-07 08:02:03 -0300 |
commit | a28824df85040e68f35938c3e085f0f107c84ef5 (patch) | |
tree | 220177010140f8bb8710f2433da76f79e8ee5186 /src | |
parent | d55851c513df8a337e2c77719003324cc3c51b69 (diff) |
feat(Core/Visibility): Far visibility worldobjects (#22828)
Diffstat (limited to 'src')
22 files changed, 460 insertions, 155 deletions
diff --git a/src/common/Dynamic/TypeContainer.h b/src/common/Dynamic/TypeContainer.h index 0c338de5da..6240eb48df 100644 --- a/src/common/Dynamic/TypeContainer.h +++ b/src/common/Dynamic/TypeContainer.h @@ -26,6 +26,7 @@ #include "Dynamic/TypeList.h" #include "GridRefMgr.h" #include <unordered_map> +#include <vector> /* * @class ContainerMapList is a mulit-type container for map elements @@ -50,6 +51,24 @@ struct ContainerMapList<TypeList<H, T>> ContainerMapList<T> _TailElements; }; +template<class OBJECT> +struct ContainerVector +{ + std::vector<OBJECT*> _element; +}; + +template<> +struct ContainerVector<TypeNull> +{ +}; + +template<class H, class T> +struct ContainerVector<TypeList<H, T>> +{ + ContainerVector<H> _elements; + ContainerVector<T> _TailElements; +}; + template<class OBJECT, class KEY_TYPE> struct ContainerUnorderedMap { @@ -123,6 +142,33 @@ private: ContainerMapList<OBJECT_TYPES> i_elements; }; +template<class OBJECT_TYPES> +class TypeVectorContainer +{ +public: + template<class SPECIFIC_TYPE> [[nodiscard]] std::size_t Count() const { return Acore::Count(i_elements, (SPECIFIC_TYPE*)nullptr); } + + template<class SPECIFIC_TYPE> + bool Insert(SPECIFIC_TYPE* obj) + { + SPECIFIC_TYPE* t = Acore::Insert(i_elements, obj); + return (t != nullptr); + } + + template<class SPECIFIC_TYPE> + bool Remove(SPECIFIC_TYPE* obj) + { + SPECIFIC_TYPE* t = Acore::Remove(i_elements, obj); + return (t != nullptr); + } + + ContainerVector<OBJECT_TYPES>& GetElements() { return i_elements; } + [[nodiscard]] const ContainerVector<OBJECT_TYPES>& GetElements() const { return i_elements; } + +private: + ContainerVector<OBJECT_TYPES> i_elements; +}; + template<class OBJECT_TYPES, class KEY_TYPE> class TypeUnorderedMapContainer { diff --git a/src/common/Dynamic/TypeContainerFunctions.h b/src/common/Dynamic/TypeContainerFunctions.h index b517f4529a..61735d7d89 100644 --- a/src/common/Dynamic/TypeContainerFunctions.h +++ b/src/common/Dynamic/TypeContainerFunctions.h @@ -239,5 +239,101 @@ namespace Acore // SPECIFIC_TYPE* t = Remove(elements._elements, obj); // return ( t != nullptr ? t : Remove(elements._TailElements, obj)); //} + + /* ContainerVector Helpers */ + // count functions + template<class SPECIFIC_TYPE> + std::size_t Count(const ContainerVector<SPECIFIC_TYPE>& elements, SPECIFIC_TYPE* /*fake*/) + { + return elements._element.getSize(); + } + + template<class SPECIFIC_TYPE> + std::size_t Count(const ContainerVector<TypeNull>& /*elements*/, SPECIFIC_TYPE* /*fake*/) + { + return 0; + } + + template<class SPECIFIC_TYPE, class T> + std::size_t Count(const ContainerVector<T>& /*elements*/, SPECIFIC_TYPE* /*fake*/) + { + return 0; + } + + template<class SPECIFIC_TYPE, class T> + std::size_t Count(const ContainerVector<TypeList<SPECIFIC_TYPE, T>>& elements, SPECIFIC_TYPE* fake) + { + return Count(elements._elements, fake); + } + + template<class SPECIFIC_TYPE, class H, class T> + std::size_t Count(const ContainerVector<TypeList<H, T>>& elements, SPECIFIC_TYPE* fake) + { + return Count(elements._TailElements, fake); + } + + // non-const insert functions + template<class SPECIFIC_TYPE> + SPECIFIC_TYPE* Insert(ContainerVector<SPECIFIC_TYPE>& elements, SPECIFIC_TYPE* obj) + { + elements._element.push_back(obj); + return obj; + } + + template<class SPECIFIC_TYPE> + SPECIFIC_TYPE* Insert(ContainerVector<TypeNull>& /*elements*/, SPECIFIC_TYPE* /*obj*/) + { + return nullptr; + } + + // this is a missed + template<class SPECIFIC_TYPE, class T> + SPECIFIC_TYPE* Insert(ContainerVector<T>& /*elements*/, SPECIFIC_TYPE* /*obj*/) + { + return nullptr; // a missed + } + + // Recursion + template<class SPECIFIC_TYPE, class H, class T> + SPECIFIC_TYPE* Insert(ContainerVector<TypeList<H, T>>& elements, SPECIFIC_TYPE* obj) + { + SPECIFIC_TYPE* t = Insert(elements._elements, obj); + return (t != nullptr ? t : Insert(elements._TailElements, obj)); + } + + // non-const remove method + template<class SPECIFIC_TYPE> SPECIFIC_TYPE* Remove(ContainerVector<SPECIFIC_TYPE>& elements, SPECIFIC_TYPE *obj) + { + // Simple vector find/swap/pop, this container should be very lightly used + // so I don't suspect the linear search complexity to be an issue + auto itr = std::find(elements._element.begin(), elements._element.end(), obj); + if (itr != elements._element.end()) + { + // Swap the element to be removed with the last element + std::swap(*itr, elements._element.back()); + + // Remove the last element (which is now the element we wanted to remove) + elements._element.pop_back(); + } + return obj; + } + + template<class SPECIFIC_TYPE> SPECIFIC_TYPE* Remove(ContainerVector<TypeNull> &/*elements*/, SPECIFIC_TYPE * /*obj*/) + { + return nullptr; + } + + // this is a missed + template<class SPECIFIC_TYPE, class T> SPECIFIC_TYPE* Remove(ContainerVector<T> &/*elements*/, SPECIFIC_TYPE * /*obj*/) + { + return nullptr; // a missed + } + + template<class SPECIFIC_TYPE, class T, class H> SPECIFIC_TYPE* Remove(ContainerVector<TypeList<H, T> > &elements, SPECIFIC_TYPE *obj) + { + // The head element is bad + SPECIFIC_TYPE* t = Remove(elements._elements, obj); + return ( t != nullptr ? t : Remove(elements._TailElements, obj)); + } } #endif diff --git a/src/common/Dynamic/TypeContainerVisitor.h b/src/common/Dynamic/TypeContainerVisitor.h index 1553d918af..066f6f3085 100644 --- a/src/common/Dynamic/TypeContainerVisitor.h +++ b/src/common/Dynamic/TypeContainerVisitor.h @@ -56,6 +56,27 @@ template<class VISITOR, class OBJECT_TYPES> void VisitorHelper(VISITOR& v, TypeM VisitorHelper(v, c.GetElements()); } +// VectorContainer +template<class VISITOR> void VisitorHelper(VISITOR& /*v*/, ContainerVector<TypeNull>& /*c*/) {} + +template<class VISITOR, class T> void VisitorHelper(VISITOR& v, ContainerVector<T>& c) +{ + v.Visit(c._element); +} + +// recursion container map list +template<class VISITOR, class H, class T> void VisitorHelper(VISITOR& v, ContainerVector<TypeList<H, T>>& c) +{ + VisitorHelper(v, c._elements); + VisitorHelper(v, c._TailElements); +} + +// for TypeMapContainer +template<class VISITOR, class OBJECT_TYPES> void VisitorHelper(VISITOR& v, TypeVectorContainer<OBJECT_TYPES>& c) +{ + VisitorHelper(v, c.GetElements()); +} + // TypeUnorderedMapContainer template<class VISITOR, class KEY_TYPE> void VisitorHelper(VISITOR& /*v*/, ContainerUnorderedMap<TypeNull, KEY_TYPE>& /*c*/) { } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 1fa5a4177f..f258db1caf 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -431,15 +431,11 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u // Check if GameObject is Large if (goinfo->IsLargeGameObject()) - { SetVisibilityDistanceOverride(VisibilityDistanceType::Large); - } // Check if GameObject is Infinite if (goinfo->IsInfiniteGameObject()) - { SetVisibilityDistanceOverride(VisibilityDistanceType::Infinite); - } return true; } diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 3081013be7..2cd31a8fa8 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1037,7 +1037,7 @@ void MovementInfo::OutDebug() } WorldObject::WorldObject() : WorldLocation(), - LastUsedScriptID(0), m_name(""), m_isActive(false), m_visibilityDistanceOverride(), m_zoneScript(nullptr), + LastUsedScriptID(0), m_name(""), m_isActive(false), _visibilityDistanceOverrideType(VisibilityDistanceType::Normal), m_zoneScript(nullptr), _zoneId(0), _areaId(0), _floorZ(INVALID_HEIGHT), _outdoors(false), _liquidData(), _updatePositionData(false), m_transport(nullptr), m_currMap(nullptr), _heartbeatTimer(HEARTBEAT_INTERVAL), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), m_useCombinedPhases(true), m_notifyflags(0), m_executed_notifies(0), _objectVisibilityContainer(this) @@ -1082,15 +1082,36 @@ void WorldObject::setActive(bool on) map->AddObjectToPendingUpdateList(this); } +float WorldObject::GetVisibilityOverrideDistance() const +{ + ASSERT(_visibilityDistanceOverrideType < VisibilityDistanceType::Max); + return VisibilityDistances[AsUnderlyingType(_visibilityDistanceOverrideType)]; +} + void WorldObject::SetVisibilityDistanceOverride(VisibilityDistanceType type) { ASSERT(type < VisibilityDistanceType::Max); + + if (type == GetVisibilityOverrideType()) + return; + if (IsPlayer()) - { return; + + if (IsVisibilityOverridden()) + { + if (IsFarVisible()) + GetMap()->RemoveWorldObjectFromFarVisibleMap(this); + else if (IsZoneWideVisible()) + GetMap()->RemoveWorldObjectFromZoneWideVisibleMap(GetZoneId(), this); } - m_visibilityDistanceOverride = VisibilityDistances[AsUnderlyingType(type)]; + if (type == VisibilityDistanceType::Large || type == VisibilityDistanceType::Gigantic) + GetMap()->AddWorldObjectToFarVisibleMap(this); + else if (type == VisibilityDistanceType::Infinite) + GetMap()->AddWorldObjectToZoneWideVisibleMap(GetZoneId(), this); + + _visibilityDistanceOverrideType = type; } void WorldObject::CleanupsBeforeDelete(bool /*finalCleanup*/) @@ -1127,6 +1148,8 @@ void WorldObject::UpdatePositionData() void WorldObject::ProcessPositionDataChanged(PositionFullTerrainStatus const& data) { + uint32 const oldZoneId = _zoneId; + _zoneId = _areaId = data.areaId; if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(_areaId)) @@ -1136,6 +1159,17 @@ void WorldObject::ProcessPositionDataChanged(PositionFullTerrainStatus const& da _outdoors = data.outdoors; _floorZ = data.floorZ; _liquidData = data.liquidInfo; + + // Has zone ID changed? + if (oldZoneId != _zoneId) + { + // If so, check if we are far visibility overridden object and refresh maps if needed. + if (IsZoneWideVisible()) + { + GetMap()->RemoveWorldObjectFromZoneWideVisibleMap(oldZoneId, this); + GetMap()->AddWorldObjectToZoneWideVisibleMap(_zoneId, this); + } + } } void WorldObject::AddToWorld() @@ -1150,6 +1184,9 @@ void WorldObject::RemoveFromWorld() if (!IsInWorld()) return; + if (IsZoneWideVisible()) + GetMap()->RemoveWorldObjectFromZoneWideVisibleMap(GetZoneId(), this); + DestroyForVisiblePlayers(); GetObjectVisibilityContainer().CleanVisibilityReferences(); @@ -1612,26 +1649,16 @@ float WorldObject::GetGridActivationRange() const float WorldObject::GetVisibilityRange() const { - if (IsVisibilityOverridden() && IsCreature()) - { - return *m_visibilityDistanceOverride; - } + if (IsCreature() && IsVisibilityOverridden()) + return GetVisibilityOverrideDistance(); else if (IsGameObject()) { - { - if (IsInWintergrasp()) - { - return VISIBILITY_DIST_WINTERGRASP + VISIBILITY_INC_FOR_GOBJECTS; - } - else if (IsVisibilityOverridden()) - { - return *m_visibilityDistanceOverride; - } - else - { - return GetMap()->GetVisibilityRange() + VISIBILITY_INC_FOR_GOBJECTS; - } - } + if (IsInWintergrasp()) + return VISIBILITY_DIST_WINTERGRASP; + else if (IsVisibilityOverridden()) + return GetVisibilityOverrideDistance(); + else + return GetMap()->GetVisibilityRange(); } else return IsInWintergrasp() ? VISIBILITY_DIST_WINTERGRASP : GetMap()->GetVisibilityRange(); @@ -1645,28 +1672,18 @@ float WorldObject::GetSightRange(WorldObject const* target) const { if (target) { - if (target->IsVisibilityOverridden() && target->IsCreature()) - { - return *target->m_visibilityDistanceOverride; - } + if (target->IsCreature() && target->IsVisibilityOverridden()) + return target->GetVisibilityOverrideDistance(); else if (target->IsGameObject()) { if (IsInWintergrasp() && target->IsInWintergrasp()) - { - return VISIBILITY_DIST_WINTERGRASP + VISIBILITY_INC_FOR_GOBJECTS; - } + return VISIBILITY_DIST_WINTERGRASP; else if (target->IsVisibilityOverridden()) - { - return *target->m_visibilityDistanceOverride; - } + return target->GetVisibilityOverrideDistance(); else if (ToPlayer()->GetCinematicMgr()->IsOnCinematic()) - { return DEFAULT_VISIBILITY_INSTANCE; - } else - { - return GetMap()->GetVisibilityRange() + VISIBILITY_INC_FOR_GOBJECTS; - } + return GetMap()->GetVisibilityRange(); } return IsInWintergrasp() && target->IsInWintergrasp() ? VISIBILITY_DIST_WINTERGRASP : GetMap()->GetVisibilityRange(); @@ -1674,19 +1691,13 @@ float WorldObject::GetSightRange(WorldObject const* target) const return IsInWintergrasp() ? VISIBILITY_DIST_WINTERGRASP : GetMap()->GetVisibilityRange(); } else if (ToCreature()) - { return ToCreature()->m_SightDistance; - } else - { return SIGHT_RANGE_UNIT; - } } if (ToDynObject() && isActiveObject()) - { return GetMap()->GetVisibilityRange(); - } return 0.0f; } diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 1a2cdf384f..23784c9c63 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -358,9 +358,20 @@ template<class T> class GridObject { public: - [[nodiscard]] bool IsInGrid() const { return _gridRef.isValid(); } - void AddToGrid(GridRefMgr<T>& m) { ASSERT(!IsInGrid()); _gridRef.link(&m, (T*)this); } - void RemoveFromGrid() { ASSERT(IsInGrid()); _gridRef.unlink(); } + bool IsInGrid() const + { + return _gridRef.isValid(); + } + void AddToGrid(GridRefMgr<T>& m) + { + ASSERT(!IsInGrid()); + _gridRef.link(&m, (T*)this); + } + void RemoveFromGrid() + { + ASSERT(IsInGrid()); + _gridRef.unlink(); + } private: GridReference<T> _gridRef; }; @@ -654,8 +665,11 @@ public: [[nodiscard]] bool isActiveObject() const { return m_isActive; } void setActive(bool isActiveObject); - [[nodiscard]] bool IsFarVisible() const { return m_isFarVisible; } - [[nodiscard]] bool IsVisibilityOverridden() const { return m_visibilityDistanceOverride.has_value(); } + VisibilityDistanceType GetVisibilityOverrideType() const { return _visibilityDistanceOverrideType; } + bool IsVisibilityOverridden() const { return _visibilityDistanceOverrideType > VisibilityDistanceType::Normal; } + bool IsZoneWideVisible() const { return _visibilityDistanceOverrideType == VisibilityDistanceType::Infinite; } + bool IsFarVisible() const { return _visibilityDistanceOverrideType == VisibilityDistanceType::Large || _visibilityDistanceOverrideType == VisibilityDistanceType::Gigantic; } + float GetVisibilityOverrideDistance() const; void SetVisibilityDistanceOverride(VisibilityDistanceType type); [[nodiscard]] bool IsInWintergrasp() const @@ -719,8 +733,7 @@ public: protected: std::string m_name; bool m_isActive; - bool m_isFarVisible; - Optional<float> m_visibilityDistanceOverride; + VisibilityDistanceType _visibilityDistanceOverrideType; ZoneScript* m_zoneScript; virtual void ProcessPositionDataChanged(PositionFullTerrainStatus const& data); diff --git a/src/server/game/Entities/Object/ObjectDefines.h b/src/server/game/Entities/Object/ObjectDefines.h index bfae02203c..fb0daa500b 100644 --- a/src/server/game/Entities/Object/ObjectDefines.h +++ b/src/server/game/Entities/Object/ObjectDefines.h @@ -25,7 +25,6 @@ #define ATTACK_DISTANCE 5.0f #define VISIBILITY_COMPENSATION 15.0f // increase searchers #define INSPECT_DISTANCE 28.0f -#define VISIBILITY_INC_FOR_GOBJECTS 30.0f // pussywizard #define SPELL_SEARCHER_COMPENSATION 30.0f // increase searchers size in case we have large npc near cell border #define TRADE_DISTANCE 11.11f #define MAX_VISIBILITY_DISTANCE 250.0f // max distance for visible objects, experimental diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index ebb62dbe51..0700d1140e 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -16311,13 +16311,25 @@ float Player::GetSightRange(WorldObject const* target) const { float sightRange = WorldObject::GetSightRange(target); if (_farSightDistance) - { sightRange += *_farSightDistance; - } return sightRange; } +bool Player::IsWorldObjectOutOfSightRange(WorldObject const* target) const +{ + // Special handling for Infinite visibility override objects -> they are zone wide visible + if (target->GetVisibilityOverrideType() == VisibilityDistanceType::Infinite) + { + // Same zone, always visible + if (target->GetZoneId() == GetZoneId()) + return false; + } + + // Check if out of range + return !m_seer->IsWithinDist(target, GetSightRange(target), true); +} + std::string Player::GetPlayerName() { std::string name = GetName(); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index befde718fc..3204f42417 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2630,6 +2630,7 @@ public: [[nodiscard]] Optional<float> GetFarSightDistance() const; float GetSightRange(WorldObject const* target = nullptr) const override; + bool IsWorldObjectOutOfSightRange(WorldObject const* target) const; std::string GetPlayerName(); diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 8426e4c8c8..fd41f73aac 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -1594,21 +1594,12 @@ void Player::UpdateVisibilityForPlayer(bool mapChange) // After added to map seer must be a player - there is no possibility to // still have different seer (all charm auras must be already removed) if (mapChange && m_seer != this) - { m_seer = this; - } - - Acore::VisibleNotifier notifierNoLarge( - *this, mapChange, - false); // visit only objects which are not large; default distance - Cell::VisitObjects(m_seer, notifierNoLarge, - GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - notifierNoLarge.SendToSelf(); - Acore::VisibleNotifier notifierLarge( - *this, mapChange, true); // visit only large objects; maximum distance - Cell::VisitObjects(m_seer, notifierLarge, GetSightRange()); - notifierLarge.SendToSelf(); + Acore::VisibleNotifier notifier(*this, mapChange); + Cell::VisitObjects(m_seer, notifier, GetSightRange()); + Cell::VisitFarVisibleObjects(m_seer, notifier, VISIBILITY_DISTANCE_GIGANTIC); + notifier.SendToSelf(); if (mapChange) m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index a2a33e89dc..b5667566e1 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -20284,12 +20284,10 @@ void Unit::ExecuteDelayedUnitRelocationEvent() //active->m_last_notify_position.Relocate(active->GetPositionX(), active->GetPositionY(), active->GetPositionZ()); } - Acore::PlayerRelocationNotifier relocateNoLarge(*player, false); // visit only objects which are not large; default distance - Cell::VisitObjects(viewPoint, relocateNoLarge, player->GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - relocateNoLarge.SendToSelf(); - Acore::PlayerRelocationNotifier relocateLarge(*player, true); // visit only large objects; maximum distance - Cell::VisitObjects(viewPoint, relocateLarge, MAX_VISIBILITY_DISTANCE); - relocateLarge.SendToSelf(); + Acore::PlayerRelocationNotifier notifier(*player); + Cell::VisitObjects(viewPoint, notifier, player->GetSightRange()); + Cell::VisitFarVisibleObjects(viewPoint, notifier, VISIBILITY_DISTANCE_GIGANTIC); + notifier.SendToSelf(); } if (Player* player = this->ToPlayer()) @@ -20323,16 +20321,10 @@ void Unit::ExecuteDelayedUnitRelocationEvent() GetMap()->LoadGridsInRange(*player, MAX_VISIBILITY_DISTANCE); - Acore::PlayerRelocationNotifier relocateNoLarge(*player, false); // visit only objects which are not large; default distance - Cell::VisitObjects(viewPoint, relocateNoLarge, player->GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - relocateNoLarge.SendToSelf(); - - if (!player->GetFarSightDistance()) - { - Acore::PlayerRelocationNotifier relocateLarge(*player, true); // visit only large objects; maximum distance - Cell::VisitObjects(viewPoint, relocateLarge, MAX_VISIBILITY_DISTANCE); - relocateLarge.SendToSelf(); - } + Acore::PlayerRelocationNotifier notifier(*player); + Cell::VisitObjects(viewPoint, notifier, player->GetSightRange()); + Cell::VisitFarVisibleObjects(viewPoint, notifier, VISIBILITY_DISTANCE_GIGANTIC); + notifier.SendToSelf(); this->AddToNotify(NOTIFY_AI_RELOCATION); } @@ -20352,7 +20344,7 @@ void Unit::ExecuteDelayedUnitRelocationEvent() unit->m_last_notify_position.Relocate(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); Acore::CreatureRelocationNotifier relocate(*unit); - Cell::VisitObjects(unit, relocate, unit->GetVisibilityRange() + VISIBILITY_COMPENSATION); + Cell::VisitObjects(unit, relocate, unit->GetVisibilityRange()); this->AddToNotify(NOTIFY_AI_RELOCATION); } diff --git a/src/server/game/Grids/Cells/Cell.h b/src/server/game/Grids/Cells/Cell.h index 89676468d9..a54f7f4cd8 100644 --- a/src/server/game/Grids/Cells/Cell.h +++ b/src/server/game/Grids/Cells/Cell.h @@ -106,6 +106,8 @@ struct Cell template<class T> static void VisitObjects(WorldObject const* obj, T& visitor, float radius); template<class T> static void VisitObjects(float x, float y, Map* map, T& visitor, float radius); + template<class T> static void VisitFarVisibleObjects(WorldObject const* obj, T& visitor, float radius); + private: template<class T, class CONTAINER> void VisitCircle(TypeContainerVisitor<T, CONTAINER>&, Map&, CellCoord const&, CellCoord const&) const; }; diff --git a/src/server/game/Grids/Cells/CellImpl.h b/src/server/game/Grids/Cells/CellImpl.h index d3f97a09d2..997840367a 100644 --- a/src/server/game/Grids/Cells/CellImpl.h +++ b/src/server/game/Grids/Cells/CellImpl.h @@ -181,4 +181,14 @@ inline void Cell::VisitObjects(float x, float y, Map* map, T& visitor, float rad cell.Visit(p, gnotifier, *map, x, y, radius); } +template<class T> +inline void Cell::VisitFarVisibleObjects(WorldObject const* center_obj, T& visitor, float radius) +{ + CellCoord p(Acore::ComputeCellCoord(center_obj->GetPositionX(), center_obj->GetPositionY())); + Cell cell(p); + + TypeContainerVisitor<T, FarVisibleGridContainer> gnotifier(visitor); + cell.Visit(p, gnotifier, *center_obj->GetMap(), *center_obj, radius); +} + #endif diff --git a/src/server/game/Grids/GridCell.h b/src/server/game/Grids/GridCell.h index 62ff765457..65e3a0315b 100644 --- a/src/server/game/Grids/GridCell.h +++ b/src/server/game/Grids/GridCell.h @@ -33,16 +33,20 @@ #include "TypeContainer.h" #include "TypeContainerVisitor.h" +class WorldObject; + template < - class GRID_OBJECT_TYPES + class GRID_OBJECT_TYPES, + class FAR_VISIBLE_OBJECT_TYPES > class GridCell { public: ~GridCell() = default; - template<class SPECIFIC_OBJECT> void AddGridObject(SPECIFIC_OBJECT* obj) + template<class SPECIFIC_OBJECT> + void AddGridObject(SPECIFIC_OBJECT* obj) { _gridObjects.template insert<SPECIFIC_OBJECT>(obj); ASSERT(obj->IsInGrid()); @@ -50,12 +54,32 @@ public: // Visit grid objects template<class T> - void Visit(TypeContainerVisitor<T, TypeMapContainer<GRID_OBJECT_TYPES> >& visitor) + void Visit(TypeContainerVisitor<T, TypeMapContainer<GRID_OBJECT_TYPES>>& visitor) { visitor.Visit(_gridObjects); } + template<class SPECIFIC_OBJECT> + void AddFarVisibleObject(SPECIFIC_OBJECT* obj) + { + _farVisibleObjects.template Insert<SPECIFIC_OBJECT>(obj); + } + + template<class SPECIFIC_OBJECT> + void RemoveFarVisibleObject(SPECIFIC_OBJECT* obj) + { + _farVisibleObjects.template Remove<SPECIFIC_OBJECT>(obj); + } + + // Visit far objects + template<class T> + void Visit(TypeContainerVisitor<T, TypeVectorContainer<FAR_VISIBLE_OBJECT_TYPES>>& visitor) + { + visitor.Visit(_farVisibleObjects); + } + private: TypeMapContainer<GRID_OBJECT_TYPES> _gridObjects; + TypeVectorContainer<FAR_VISIBLE_OBJECT_TYPES> _farVisibleObjects; }; #endif diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h index 6918d2b1ae..5c1d09c63e 100644 --- a/src/server/game/Grids/GridDefines.h +++ b/src/server/game/Grids/GridDefines.h @@ -57,6 +57,9 @@ typedef TYPELIST_5(GameObject, Player, Creature, Corpse, DynamicObject) AllMapGr // List of object types stored on map level typedef TYPELIST_4(Creature, GameObject, DynamicObject, Corpse) AllMapStoredObjectTypes; +// List of object types that can have far visible range +typedef TYPELIST_2(Creature, GameObject) AllFarVisibleObjectTypes; + typedef GridRefMgr<Corpse> CorpseMapType; typedef GridRefMgr<Creature> CreatureMapType; typedef GridRefMgr<DynamicObject> DynamicObjectMapType; @@ -73,10 +76,11 @@ enum GridMapTypeMask GRID_MAP_TYPE_MASK_ALL = 0x1F }; -typedef GridCell<AllMapGridStoredObjectTypes> GridCellType; -typedef MapGrid<AllMapGridStoredObjectTypes> MapGridType; +typedef GridCell<AllMapGridStoredObjectTypes, AllFarVisibleObjectTypes> GridCellType; +typedef MapGrid<AllMapGridStoredObjectTypes, AllFarVisibleObjectTypes> MapGridType; typedef TypeMapContainer<AllMapGridStoredObjectTypes> GridTypeMapContainer; +typedef TypeVectorContainer<AllFarVisibleObjectTypes> FarVisibleGridContainer; typedef TypeUnorderedMapContainer<AllMapStoredObjectTypes, ObjectGuid> MapStoredObjectTypesContainer; template<uint32 LIMIT> diff --git a/src/server/game/Grids/MapGrid.h b/src/server/game/Grids/MapGrid.h index b6dda486c7..c80c3b4d3f 100644 --- a/src/server/game/Grids/MapGrid.h +++ b/src/server/game/Grids/MapGrid.h @@ -25,12 +25,13 @@ class GridTerrainData; template < - class GRID_OBJECT_TYPES + class GRID_OBJECT_TYPES, + class FAR_VISIBLE_OBJECT_TYPES > class MapGrid { public: - typedef GridCell<GRID_OBJECT_TYPES> GridCellType; + typedef GridCell<GRID_OBJECT_TYPES, FAR_VISIBLE_OBJECT_TYPES> GridCellType; MapGrid(uint16 const x, uint16 const y) : _x(x), _y(y), _objectDataLoaded(false), _terrainData(nullptr) { } @@ -54,9 +55,19 @@ public: GetOrCreateCell(x, y).RemoveGridObject(obj); } + template<class SPECIFIC_OBJECT> void AddFarVisibleObject(uint16 const x, uint16 const y, SPECIFIC_OBJECT* obj) + { + GetOrCreateCell(x, y).AddFarVisibleObject(obj); + } + + template<class SPECIFIC_OBJECT> void RemoveFarVisibleObject(uint16 const x, uint16 const y, SPECIFIC_OBJECT* obj) + { + GetOrCreateCell(x, y).RemoveFarVisibleObject(obj); + } + // Visit all cells template<class T, class TT> - void VisitAllCells(TypeContainerVisitor<T, TypeMapContainer<TT> >& visitor) + void VisitAllCells(TypeContainerVisitor<T, TT>& visitor) { for (auto& cellX : _cells) { @@ -72,7 +83,7 @@ public: // Visit single cell template<class T, class TT> - void VisitCell(uint16 const x, uint16 const y, TypeContainerVisitor<T, TypeMapContainer<TT> >& visitor) + void VisitCell(uint16 const x, uint16 const y, TypeContainerVisitor<T, TT>& visitor) { GridCellType* gridCell = GetCell(x, y); if (!gridCell) @@ -81,7 +92,7 @@ public: gridCell->Visit(visitor); } - void link(GridRefMgr<MapGrid<GRID_OBJECT_TYPES>>* pTo) + void link(GridRefMgr<MapGrid<GRID_OBJECT_TYPES, FAR_VISIBLE_OBJECT_TYPES>>* pTo) { _gridReference.link(pTo, this); } @@ -134,7 +145,7 @@ private: bool _objectDataLoaded; std::array<std::array<std::unique_ptr<GridCellType>, MAX_NUMBER_OF_CELLS>, MAX_NUMBER_OF_CELLS> _cells; // N * N array - GridReference<MapGrid<GRID_OBJECT_TYPES>> _gridReference; + GridReference<MapGrid<GRID_OBJECT_TYPES, FAR_VISIBLE_OBJECT_TYPES>> _gridReference; // Instances will share a copy of the parent maps terrainData std::shared_ptr<GridTerrainData> _terrainData; diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 5636349290..98fdbec76c 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -29,38 +29,31 @@ void VisibleNotifier::Visit(GameObjectMapType& m) for (GameObjectMapType::iterator iter = m.begin(); iter != m.end(); ++iter) { GameObject* go = iter->GetSource(); - if (i_largeOnly != go->IsVisibilityOverridden()) - continue; - i_player.UpdateVisibilityOf(go, i_data, i_visibleNow); } } void VisibleNotifier::SendToSelf() { - // at this moment i_clientGUIDs have guids that not iterate at grid level checks - // but exist one case when this possible and object not out of range: transports - if (Transport* transport = i_player.GetTransport()) + // Update far visible objects + ZoneWideVisibleWorldObjectsSet const* zoneWideVisibleObjects = i_player.GetMap()->GetZoneWideVisibleWorldObjectsForZone(i_player.GetZoneId()); + if (zoneWideVisibleObjects) { - for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr) + for (WorldObject* obj : *zoneWideVisibleObjects) { - if (i_largeOnly != (*itr)->IsVisibilityOverridden()) - continue; - - switch ((*itr)->GetTypeId()) + switch (obj->GetTypeId()) { - case TYPEID_GAMEOBJECT: - i_player.UpdateVisibilityOf((*itr)->ToGameObject(), i_data, i_visibleNow); - break; - case TYPEID_PLAYER: - i_player.UpdateVisibilityOf((*itr)->ToPlayer(), i_data, i_visibleNow); - (*itr)->ToPlayer()->UpdateVisibilityOf(&i_player); - break; - case TYPEID_UNIT: - i_player.UpdateVisibilityOf((*itr)->ToCreature(), i_data, i_visibleNow); - break; - default: - break; + case TYPEID_GAMEOBJECT: + i_player.UpdateVisibilityOf(obj->ToGameObject(), i_data, i_visibleNow); + break; + case TYPEID_UNIT: + i_player.UpdateVisibilityOf(obj->ToCreature(), i_data, i_visibleNow); + break; + case TYPEID_DYNAMICOBJECT: + i_player.UpdateVisibilityOf(obj->ToDynObject(), i_data, i_visibleNow); + break; + default: + break; } } } @@ -69,26 +62,7 @@ void VisibleNotifier::SendToSelf() for (VisibleWorldObjectsMap::iterator itr = visibleWorldObjects->begin(); itr != visibleWorldObjects->end();) { WorldObject* obj = itr->second; - if (i_largeOnly != obj->IsVisibilityOverridden()) - { - ++itr; - continue; - } - - // pussywizard: static transports are removed only in RemovePlayerFromMap and here if can no longer detect (eg. phase changed) - if (itr->first.IsTransport()) - { - if (GameObject* staticTrans = obj->ToGameObject()) - { - if (i_player.CanSeeOrDetect(staticTrans, false, true)) - { - ++itr; - continue; - } - } - } - - if (i_player.m_seer->IsWithinDist(obj, i_player.GetSightRange(obj), true)) + if (!i_player.IsWorldObjectOutOfSightRange(obj)) { ++itr; continue; @@ -111,12 +85,7 @@ void VisibleNotifier::SendToSelf() i_player.GetSession()->SendPacket(&packet); for (std::vector<Unit*>::const_iterator it = i_visibleNow.begin(); it != i_visibleNow.end(); ++it) - { - if (i_largeOnly != (*it)->IsVisibilityOverridden()) - continue; - i_player.GetInitialVisiblePackets(*it); - } } void VisibleChangesNotifier::Visit(PlayerMapType& m) diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 040c542447..d9556c77f6 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -45,16 +45,16 @@ namespace Acore Player& i_player; std::vector<Unit*>& i_visibleNow; bool i_gobjOnly; - bool i_largeOnly; UpdateData i_data; - VisibleNotifier(Player& player, bool gobjOnly, bool largeOnly) : - i_player(player), i_visibleNow(player.m_newVisible), i_gobjOnly(gobjOnly), i_largeOnly(largeOnly) + VisibleNotifier(Player& player, bool gobjOnly) : + i_player(player), i_visibleNow(player.m_newVisible), i_gobjOnly(gobjOnly) { i_visibleNow.clear(); } void Visit(GameObjectMapType&); + template<class T> void Visit(std::vector<T>& m); template<class T> void Visit(GridRefMgr<T>& m); void SendToSelf(void); }; @@ -72,8 +72,9 @@ namespace Acore struct PlayerRelocationNotifier : public VisibleNotifier { - PlayerRelocationNotifier(Player& player, bool largeOnly): VisibleNotifier(player, false, largeOnly) { } + PlayerRelocationNotifier(Player& player): VisibleNotifier(player, false) { } + template<class T> void Visit(std::vector<T>& m) { VisibleNotifier::Visit(m); } template<class T> void Visit(GridRefMgr<T>& m) { VisibleNotifier::Visit(m); } void Visit(PlayerMapType&); }; diff --git a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h index 592e584eaf..a24e873745 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h +++ b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h @@ -26,6 +26,13 @@ #include "WorldSession.h" template<class T> +inline void Acore::VisibleNotifier::Visit(std::vector<T>& m) +{ + for (typename std::vector<T>::iterator iter = m.begin(); iter != m.end(); ++iter) + i_player.UpdateVisibilityOf((*iter), i_data, i_visibleNow); +} + +template<class T> inline void Acore::VisibleNotifier::Visit(GridRefMgr<T>& m) { // Xinef: Update gameobjects only @@ -33,12 +40,7 @@ inline void Acore::VisibleNotifier::Visit(GridRefMgr<T>& m) return; for (typename GridRefMgr<T>::iterator iter = m.begin(); iter != m.end(); ++iter) - { - if (i_largeOnly != iter->GetSource()->IsVisibilityOverridden()) - continue; - i_player.UpdateVisibilityOf(iter->GetSource(), i_data, i_visibleNow); - } } // SEARCHERS & LIST SEARCHERS & WORKERS diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 8e2a0cacff..17ab70da0f 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -118,6 +118,8 @@ void Map::AddToGrid(Creature* obj, Cell const& cell) { MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY()); grid->AddGridObject(cell.CellX(), cell.CellY(), obj); + if (obj->IsFarVisible()) + grid->AddFarVisibleObject(cell.CellX(), cell.CellY(), obj); obj->SetCurrentCell(cell); } @@ -127,6 +129,8 @@ void Map::AddToGrid(GameObject* obj, Cell const& cell) { MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY()); grid->AddGridObject(cell.CellX(), cell.CellY(), obj); + if (obj->IsFarVisible()) + grid->AddFarVisibleObject(cell.CellX(), cell.CellY(), obj); obj->SetCurrentCell(cell); } @@ -604,6 +608,52 @@ void Map::RemoveObjectFromMapUpdateList(WorldObject* obj) _RemoveObjectFromUpdateList(obj); } +// Used in VisibilityDistanceType::Large and VisibilityDistanceType::Gigantic +void Map::AddWorldObjectToFarVisibleMap(WorldObject* obj) +{ + Cell curr_cell(obj->GetPositionX(), obj->GetPositionY()); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + + if (obj->IsCreature()) + grid->AddFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToCreature()); + else if (obj->IsGameObject()) + grid->AddFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToGameObject()); +} + +void Map::RemoveWorldObjectFromFarVisibleMap(WorldObject* obj) +{ + Cell curr_cell(obj->GetPositionX(), obj->GetPositionY()); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + if (obj->IsCreature()) + grid->RemoveFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToCreature()); + else if (obj->IsGameObject()) + grid->RemoveFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToGameObject()); +} + +// Used in VisibilityDistanceType::Infinite +void Map::AddWorldObjectToZoneWideVisibleMap(uint32 zoneId, WorldObject* obj) +{ + _zoneWideVisibleWorldObjectsMap[zoneId].insert(obj); +} + +void Map::RemoveWorldObjectFromZoneWideVisibleMap(uint32 zoneId, WorldObject* obj) +{ + ZoneWideVisibleWorldObjectsMap::iterator itr = _zoneWideVisibleWorldObjectsMap.find(zoneId); + if (itr == _zoneWideVisibleWorldObjectsMap.end()) + return; + + itr->second.erase(obj); +} + +ZoneWideVisibleWorldObjectsSet const* Map::GetZoneWideVisibleWorldObjectsForZone(uint32 zoneId) const +{ + ZoneWideVisibleWorldObjectsMap::const_iterator itr = _zoneWideVisibleWorldObjectsMap.find(zoneId); + if (itr == _zoneWideVisibleWorldObjectsMap.end()) + return nullptr; + + return &itr->second; +} + void Map::HandleDelayedVisibility() { if (i_objectsForDelayedVisibility.empty()) @@ -656,6 +706,8 @@ void Map::RemoveFromMap(T* obj, bool remove) obj->RemoveFromWorld(); obj->RemoveFromGrid(); + if (obj->IsFarVisible()) + RemoveWorldObjectFromFarVisibleMap(obj); obj->ResetMap(); @@ -852,6 +904,12 @@ void Map::MoveAllCreaturesInMoveList() Cell const& old_cell = c->GetCurrentCell(); Cell new_cell(c->GetPositionX(), c->GetPositionY()); + MapGridType* oldGrid = GetMapGrid(old_cell.GridX(), old_cell.GridY()); + if (c->IsFarVisible()) + { + oldGrid->RemoveFarVisibleObject(old_cell.CellX(), old_cell.CellY(), c); + AddWorldObjectToFarVisibleMap(c); + } c->RemoveFromGrid(); if (old_cell.DiffGrid(new_cell)) @@ -881,6 +939,12 @@ void Map::MoveAllGameObjectsInMoveList() Cell const& old_cell = go->GetCurrentCell(); Cell new_cell(go->GetPositionX(), go->GetPositionY()); + MapGridType* oldGrid = GetMapGrid(old_cell.GridX(), old_cell.GridY()); + if (go->IsFarVisible()) + { + oldGrid->RemoveFarVisibleObject(old_cell.CellX(), old_cell.CellY(), go); + AddWorldObjectToFarVisibleMap(go); + } go->RemoveFromGrid(); if (old_cell.DiffGrid(new_cell)) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 2e5b0ed0a6..c875e4eaa9 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -147,6 +147,8 @@ struct ZoneDynamicInfo typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType; typedef std::unordered_map<uint32 /*zoneId*/, ZoneDynamicInfo> ZoneDynamicInfoMap; typedef std::unordered_set<Transport*> TransportsContainer; +typedef std::unordered_set<WorldObject*> ZoneWideVisibleWorldObjectsSet; +typedef std::unordered_map<uint32 /*ZoneId*/, ZoneWideVisibleWorldObjectsSet> ZoneWideVisibleWorldObjectsMap; enum EncounterCreditType : uint8 { @@ -496,6 +498,12 @@ public: typedef std::vector<WorldObject*> UpdatableObjectList; typedef std::unordered_set<WorldObject*> PendingAddUpdatableObjectList; + void AddWorldObjectToFarVisibleMap(WorldObject* obj); + void RemoveWorldObjectFromFarVisibleMap(WorldObject* obj); + void AddWorldObjectToZoneWideVisibleMap(uint32 zoneId, WorldObject* obj); + void RemoveWorldObjectFromZoneWideVisibleMap(uint32 zoneId, WorldObject* obj); + ZoneWideVisibleWorldObjectsSet const* GetZoneWideVisibleWorldObjectsForZone(uint32 zoneId) const; + private: template<class T> void InitializeObject(T* obj); @@ -603,6 +611,7 @@ private: UpdatableObjectList _updatableObjectList; PendingAddUpdatableObjectList _pendingAddUpdatableObjectList; IntervalTimer _updatableObjectListRecheckTimer; + ZoneWideVisibleWorldObjectsMap _zoneWideVisibleWorldObjectsMap; }; enum InstanceResetMethod diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index a18e01e0bd..40bb89b93b 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -99,7 +99,8 @@ public: { "objectcount", HandleDebugObjectCountCommand, SEC_ADMINISTRATOR, Console::Yes}, { "dummy", HandleDebugDummyCommand, SEC_ADMINISTRATOR, Console::No }, { "mapdata", HandleDebugMapDataCommand, SEC_ADMINISTRATOR, Console::No }, - { "boundary", HandleDebugBoundaryCommand, SEC_ADMINISTRATOR, Console::No } + { "boundary", HandleDebugBoundaryCommand, SEC_ADMINISTRATOR, Console::No }, + { "visibilitydata", HandleDebugVisibilityDataCommand, SEC_ADMINISTRATOR, Console::No } }; static ChatCommandTable commandTable = { @@ -1403,6 +1404,36 @@ public: return true; } + + static bool HandleDebugVisibilityDataCommand(ChatHandler* handler) + { + Player* player = handler->GetPlayer(); + if (!player) + return false; + + std::array<uint32, NUM_CLIENT_OBJECT_TYPES> objectByTypeCount = {}; + + ObjectVisibilityContainer const& objectVisibilityContainer = player->GetObjectVisibilityContainer(); + for (auto const& kvPair : *objectVisibilityContainer.GetVisibleWorldObjectsMap()) + { + WorldObject const* obj = kvPair.second; + ++objectByTypeCount[obj->GetTypeId()]; + } + + uint32 zoneWideVisibleObjectsInZone = 0; + if (ZoneWideVisibleWorldObjectsSet const* farVisibleSet = player->GetMap()->GetZoneWideVisibleWorldObjectsForZone(player->GetZoneId())) + zoneWideVisibleObjectsInZone = farVisibleSet->size(); + + handler->PSendSysMessage("Visibility Range: {}", player->GetVisibilityRange()); + handler->PSendSysMessage("Visible Creatures: {}", objectByTypeCount[TYPEID_UNIT]); + handler->PSendSysMessage("Visible Players: {}", objectByTypeCount[TYPEID_PLAYER]); + handler->PSendSysMessage("Visible GameObjects: {}", objectByTypeCount[TYPEID_GAMEOBJECT]); + handler->PSendSysMessage("Visible DynamicObjects: {}", objectByTypeCount[TYPEID_DYNAMICOBJECT]); + handler->PSendSysMessage("Visible Corpses: {}", objectByTypeCount[TYPEID_CORPSE]); + handler->PSendSysMessage("Players we are visible to: {}", objectVisibilityContainer.GetVisiblePlayersMap().size()); + handler->PSendSysMessage("Zone wide visible objects in zone: {}", zoneWideVisibleObjectsInZone); + return true; + } }; void AddSC_debug_commandscript() |