mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-23 05:36:23 +00:00
feat(Core/Visibility): Visibility improvements part 1 (#22624)
This commit is contained in:
@@ -371,7 +371,7 @@ void Creature::RemoveFromWorld()
|
||||
|
||||
void Creature::DisappearAndDie()
|
||||
{
|
||||
DestroyForNearbyPlayers();
|
||||
DestroyForVisiblePlayers();
|
||||
//SetVisibility(VISIBILITY_OFF);
|
||||
//ObjectAccessor::UpdateObjectVisibility(this);
|
||||
if (IsAlive())
|
||||
@@ -408,7 +408,7 @@ void Creature::RemoveCorpse(bool setSpawnTime, bool skipVisibility)
|
||||
setDeathState(DeathState::Dead);
|
||||
RemoveAllAuras();
|
||||
if (!skipVisibility) // pussywizard
|
||||
DestroyForNearbyPlayers(); // pussywizard: previous UpdateObjectVisibility()
|
||||
DestroyForVisiblePlayers(); // pussywizard: previous UpdateObjectVisibility()
|
||||
loot.clear();
|
||||
uint32 respawnDelay = m_respawnDelay;
|
||||
if (IsAIEnabled)
|
||||
|
||||
@@ -893,7 +893,7 @@ void GameObject::Update(uint32 diff)
|
||||
if (!m_spawnedByDefault)
|
||||
{
|
||||
m_respawnTime = 0;
|
||||
DestroyForNearbyPlayers(); // xinef: old UpdateObjectVisibility();
|
||||
DestroyForVisiblePlayers(); // xinef: old UpdateObjectVisibility();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -904,7 +904,7 @@ void GameObject::Update(uint32 diff)
|
||||
if (GetMap()->IsDungeon())
|
||||
SaveRespawnTime();
|
||||
|
||||
DestroyForNearbyPlayers(); // xinef: old UpdateObjectVisibility();
|
||||
DestroyForVisiblePlayers(); // xinef: old UpdateObjectVisibility();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1150,7 +1150,7 @@ bool Item::IsBindedNotWith(Player const* player) const
|
||||
return true;
|
||||
}
|
||||
|
||||
void Item::BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&)
|
||||
void Item::BuildUpdate(UpdateDataMapType& data_map)
|
||||
{
|
||||
if (Player* owner = GetOwner())
|
||||
BuildFieldsUpdate(owner, data_map);
|
||||
|
||||
@@ -357,7 +357,7 @@ public:
|
||||
void ClearSoulboundTradeable(Player* currentOwner);
|
||||
bool CheckSoulboundTradeExpire();
|
||||
|
||||
void BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&) override;
|
||||
void BuildUpdate(UpdateDataMapType& data_map) override;
|
||||
void AddToObjectUpdate() override;
|
||||
void RemoveFromObjectUpdate() override;
|
||||
|
||||
|
||||
@@ -1050,7 +1050,8 @@ void MovementInfo::OutDebug()
|
||||
WorldObject::WorldObject(bool isWorldObject) : WorldLocation(),
|
||||
LastUsedScriptID(0), m_name(""), m_isActive(false), m_visibilityDistanceOverride(), m_isWorldObject(isWorldObject), 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)
|
||||
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)
|
||||
{
|
||||
m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST);
|
||||
m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE);
|
||||
@@ -1177,7 +1178,9 @@ void WorldObject::RemoveFromWorld()
|
||||
if (!IsInWorld())
|
||||
return;
|
||||
|
||||
DestroyForNearbyPlayers();
|
||||
DestroyForVisiblePlayers();
|
||||
|
||||
GetObjectVisibilityContainer().CleanVisibilityReferences();
|
||||
|
||||
Object::RemoveFromWorld();
|
||||
}
|
||||
@@ -2073,19 +2076,19 @@ void Unit::BuildHeartBeatMsg(WorldPacket* data) const
|
||||
void WorldObject::SendMessageToSet(WorldPacket const* data, bool self) const
|
||||
{
|
||||
if (IsInWorld())
|
||||
SendMessageToSetInRange(data, GetVisibilityRange(), self);
|
||||
SendMessageToSetInRange(data, 0.0f, self);
|
||||
}
|
||||
|
||||
void WorldObject::SendMessageToSetInRange(WorldPacket const* data, float dist, bool /*self*/) const
|
||||
{
|
||||
Acore::MessageDistDeliverer notifier(this, data, dist);
|
||||
Cell::VisitWorldObjects(this, notifier, dist);
|
||||
notifier.Visit(GetObjectVisibilityContainer().GetVisiblePlayersMap());
|
||||
}
|
||||
|
||||
void WorldObject::SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const
|
||||
{
|
||||
Acore::MessageDistDeliverer notifier(this, data, GetVisibilityRange(), false, skipped_rcvr);
|
||||
Cell::VisitWorldObjects(this, notifier, GetVisibilityRange());
|
||||
Acore::MessageDistDeliverer notifier(this, data, 0.0f, false, skipped_rcvr);
|
||||
notifier.Visit(GetObjectVisibilityContainer().GetVisiblePlayersMap());
|
||||
}
|
||||
|
||||
void WorldObject::SendObjectDeSpawnAnim(ObjectGuid guid)
|
||||
@@ -2906,72 +2909,49 @@ void WorldObject::PlayDirectSound(uint32 sound_id, Player* target /*= nullptr*/)
|
||||
|
||||
void WorldObject::PlayRadiusSound(uint32 sound_id, float radius)
|
||||
{
|
||||
std::list<Player*> targets;
|
||||
std::vector<Player*> targets;
|
||||
Acore::AnyPlayerInObjectRangeCheck check(this, radius, false);
|
||||
Acore::PlayerListSearcher<Acore::AnyPlayerInObjectRangeCheck> searcher(this, targets, check);
|
||||
Cell::VisitWorldObjects(this, searcher, radius);
|
||||
|
||||
for (Player* player : targets)
|
||||
{
|
||||
if (player)
|
||||
{
|
||||
player->SendDirectMessage(WorldPackets::Misc::Playsound(sound_id).Write());
|
||||
}
|
||||
}
|
||||
player->SendDirectMessage(WorldPackets::Misc::Playsound(sound_id).Write());
|
||||
}
|
||||
|
||||
void WorldObject::PlayDirectMusic(uint32 music_id, Player* target /*= nullptr*/)
|
||||
{
|
||||
if (target)
|
||||
{
|
||||
target->SendDirectMessage(WorldPackets::Misc::PlayMusic(music_id).Write());
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessageToSet(WorldPackets::Misc::PlayMusic(music_id).Write(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldObject::PlayRadiusMusic(uint32 music_id, float radius)
|
||||
{
|
||||
std::list<Player*> targets;
|
||||
std::vector<Player*> targets;
|
||||
Acore::AnyPlayerInObjectRangeCheck check(this, radius, false);
|
||||
Acore::PlayerListSearcher<Acore::AnyPlayerInObjectRangeCheck> searcher(this, targets, check);
|
||||
Cell::VisitWorldObjects(this, searcher, radius);
|
||||
|
||||
for (Player* player : targets)
|
||||
{
|
||||
if (player)
|
||||
{
|
||||
player->SendDirectMessage(WorldPackets::Misc::PlayMusic(music_id).Write());
|
||||
}
|
||||
}
|
||||
player->SendDirectMessage(WorldPackets::Misc::PlayMusic(music_id).Write());
|
||||
}
|
||||
|
||||
void WorldObject::DestroyForNearbyPlayers()
|
||||
// Removes us from visibility for all players who are currently able to see us
|
||||
void WorldObject::DestroyForVisiblePlayers()
|
||||
{
|
||||
if (!IsInWorld())
|
||||
return;
|
||||
|
||||
std::list<Player*> targets;
|
||||
Acore::AnyPlayerInObjectRangeCheck check(this, GetVisibilityRange() + VISIBILITY_COMPENSATION, false);
|
||||
Acore::PlayerListSearcherWithSharedVision<Acore::AnyPlayerInObjectRangeCheck> searcher(this, targets, check);
|
||||
Cell::VisitWorldObjects(this, searcher, GetVisibilityRange());
|
||||
for (std::list<Player*>::const_iterator iter = targets.begin(); iter != targets.end(); ++iter)
|
||||
VisiblePlayersMap& visiblePlayerMap = GetObjectVisibilityContainer().GetVisiblePlayersMap();
|
||||
for (VisiblePlayersMap::iterator itr = visiblePlayerMap.begin(); itr != visiblePlayerMap.end();)
|
||||
{
|
||||
Player* player = (*iter);
|
||||
|
||||
if (player == this)
|
||||
continue;
|
||||
|
||||
if (!player->HaveAtClient(this))
|
||||
continue;
|
||||
|
||||
if (IsUnit() && ((Unit*)this)->GetCharmerGUID() == player->GetGUID()) /// @todo: this is for puppet
|
||||
continue;
|
||||
Player* player = itr->second;
|
||||
|
||||
DestroyForPlayer(player);
|
||||
player->m_clientGUIDs.erase(GetGUID());
|
||||
|
||||
// Clean up visibility references now
|
||||
itr = GetObjectVisibilityContainer().UnlinkVisibilityFromWorldObject(player, itr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3008,84 +2988,17 @@ void WorldObject::AddToNotify(uint16 f)
|
||||
}
|
||||
}
|
||||
|
||||
struct WorldObjectChangeAccumulator
|
||||
void WorldObject::BuildUpdate(UpdateDataMapType& data_map)
|
||||
{
|
||||
UpdateDataMapType& i_updateDatas;
|
||||
UpdatePlayerSet& i_playerSet;
|
||||
WorldObject& i_object;
|
||||
WorldObjectChangeAccumulator(WorldObject& obj, UpdateDataMapType& d, UpdatePlayerSet& p) : i_updateDatas(d), i_playerSet(p), i_object(obj)
|
||||
// Build update for self
|
||||
if (IsPlayer())
|
||||
BuildFieldsUpdate(ToPlayer(), data_map);
|
||||
|
||||
// Build update for visible players
|
||||
DoForAllVisiblePlayers([this, &data_map](Player* player)
|
||||
{
|
||||
i_playerSet.clear();
|
||||
}
|
||||
void Visit(PlayerMapType& m)
|
||||
{
|
||||
Player* source = nullptr;
|
||||
for (PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
|
||||
{
|
||||
source = iter->GetSource();
|
||||
|
||||
BuildPacket(source);
|
||||
|
||||
if (source->HasSharedVision())
|
||||
{
|
||||
SharedVisionList::const_iterator it = source->GetSharedVisionList().begin();
|
||||
for (; it != source->GetSharedVisionList().end(); ++it)
|
||||
BuildPacket(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Visit(CreatureMapType& m)
|
||||
{
|
||||
Creature* source = nullptr;
|
||||
for (CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
|
||||
{
|
||||
source = iter->GetSource();
|
||||
if (source->HasSharedVision())
|
||||
{
|
||||
SharedVisionList::const_iterator it = source->GetSharedVisionList().begin();
|
||||
for (; it != source->GetSharedVisionList().end(); ++it)
|
||||
BuildPacket(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Visit(DynamicObjectMapType& m)
|
||||
{
|
||||
DynamicObject* source = nullptr;
|
||||
for (DynamicObjectMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
|
||||
{
|
||||
source = iter->GetSource();
|
||||
ObjectGuid guid = source->GetCasterGUID();
|
||||
|
||||
if (guid)
|
||||
{
|
||||
//Caster may be nullptr if DynObj is in removelist
|
||||
if (Player* caster = ObjectAccessor::FindPlayer(guid))
|
||||
if (caster->GetGuidValue(PLAYER_FARSIGHT) == source->GetGUID())
|
||||
BuildPacket(caster);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildPacket(Player* player)
|
||||
{
|
||||
// Only send update once to a player
|
||||
if (i_playerSet.find(player->GetGUID()) == i_playerSet.end() && player->HaveAtClient(&i_object))
|
||||
{
|
||||
i_object.BuildFieldsUpdate(player, i_updateDatas);
|
||||
i_playerSet.insert(player->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
template<class SKIP> void Visit(GridRefMgr<SKIP>&) {}
|
||||
};
|
||||
|
||||
void WorldObject::BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet& player_set)
|
||||
{
|
||||
WorldObjectChangeAccumulator notifier(*this, data_map, player_set);
|
||||
//we must build packets for all visible players
|
||||
Cell::VisitWorldObjects(this, notifier, GetVisibilityRange());
|
||||
BuildFieldsUpdate(player, data_map);
|
||||
});
|
||||
|
||||
ClearUpdateMask(false);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "Position.h"
|
||||
#include "UpdateData.h"
|
||||
#include "UpdateMask.h"
|
||||
#include "ObjectVisibilityContainer.h"
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
@@ -96,7 +97,6 @@ class MotionTransport;
|
||||
struct PositionFullTerrainStatus;
|
||||
|
||||
typedef std::unordered_map<Player*, UpdateData> UpdateDataMapType;
|
||||
typedef GuidUnorderedSet UpdatePlayerSet;
|
||||
|
||||
static constexpr Milliseconds HEARTBEAT_INTERVAL = 5s + 200ms;
|
||||
|
||||
@@ -189,7 +189,7 @@ public:
|
||||
|
||||
[[nodiscard]] virtual bool hasQuest(uint32 /* quest_id */) const { return false; }
|
||||
[[nodiscard]] virtual bool hasInvolvedQuest(uint32 /* quest_id */) const { return false; }
|
||||
virtual void BuildUpdate(UpdateDataMapType&, UpdatePlayerSet&) {}
|
||||
virtual void BuildUpdate(UpdateDataMapType&) {}
|
||||
void BuildFieldsUpdate(Player*, UpdateDataMapType&);
|
||||
|
||||
void SetFieldNotifyFlag(uint16 flag) { _fieldNotifyFlags |= flag; }
|
||||
@@ -559,6 +559,29 @@ public:
|
||||
void PlayDirectMusic(uint32 music_id, Player* target = nullptr);
|
||||
void PlayRadiusMusic(uint32 music_id, float radius);
|
||||
|
||||
// Warning: Possible iterator invalidation in uses that may modify visibility map
|
||||
template<typename Worker>
|
||||
void DoForAllVisiblePlayers(Worker&& worker)
|
||||
{
|
||||
for (auto const& kvPair : GetObjectVisibilityContainer().GetVisiblePlayersMap())
|
||||
worker(kvPair.second);
|
||||
}
|
||||
|
||||
// Warning: Possible iterator invalidation in uses that may modify visibility map
|
||||
template<typename Worker>
|
||||
void DoForAllVisibleWorldObjects(Worker&& worker)
|
||||
{
|
||||
// Not a player, no access to this map
|
||||
VisibleWorldObjectsMap const* visibleWorldObjectsMap = GetObjectVisibilityContainer().GetVisibleWorldObjectsMap();
|
||||
if (!visibleWorldObjectsMap)
|
||||
return;
|
||||
|
||||
for (auto const& kvPair : *visibleWorldObjectsMap)
|
||||
worker(kvPair.second);
|
||||
}
|
||||
|
||||
void DestroyForVisiblePlayers();
|
||||
|
||||
void SendObjectDeSpawnAnim(ObjectGuid guid);
|
||||
|
||||
virtual void SaveRespawnTime() {}
|
||||
@@ -609,10 +632,9 @@ public:
|
||||
void GetCreatureListWithEntryInGrid(std::list<Creature*>& creatureList, std::vector<uint32> const& entries, float maxSearchRange) const;
|
||||
void GetDeadCreatureListInGrid(std::list<Creature*>& lList, float maxSearchRange, bool alive = false) const;
|
||||
|
||||
void DestroyForNearbyPlayers();
|
||||
virtual void UpdateObjectVisibility(bool forced = true, bool fromUpdate = false);
|
||||
virtual void UpdateObjectVisibilityOnCreate() { UpdateObjectVisibility(true); }
|
||||
void BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet& player_set) override;
|
||||
void BuildUpdate(UpdateDataMapType& data_map) override;
|
||||
void GetCreaturesWithEntryInRange(std::list<Creature*>& creatureList, float radius, uint32 entry);
|
||||
|
||||
void SetPositionDataUpdate();
|
||||
@@ -688,6 +710,9 @@ public:
|
||||
|
||||
std::string GetDebugInfo() const override;
|
||||
|
||||
ObjectVisibilityContainer& GetObjectVisibilityContainer() { return _objectVisibilityContainer; }
|
||||
ObjectVisibilityContainer const& GetObjectVisibilityContainer() const { return _objectVisibilityContainer; }
|
||||
|
||||
// Event handler
|
||||
ElunaEventProcessor* elunaEvents;
|
||||
EventProcessor m_Events;
|
||||
@@ -745,6 +770,8 @@ private:
|
||||
bool CanDetectStealthOf(WorldObject const* obj, bool checkAlert = false) const;
|
||||
|
||||
GuidUnorderedSet _allowedLooters;
|
||||
|
||||
ObjectVisibilityContainer _objectVisibilityContainer;
|
||||
};
|
||||
|
||||
namespace Acore
|
||||
|
||||
114
src/server/game/Entities/Object/ObjectVisibilityContainer.cpp
Normal file
114
src/server/game/Entities/Object/ObjectVisibilityContainer.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ObjectVisibilityContainer.h"
|
||||
#include "Object.h"
|
||||
#include "Player.h"
|
||||
|
||||
/*
|
||||
* Some implementation notes:
|
||||
* Non-player worldobjects do not have any concept of 'visibility', thus,
|
||||
* the most important and mainly used map is 'VisibleWorldObjectsMap'
|
||||
* which is only accessible for player objects. The 'VisiblePlayersMap'
|
||||
* map is simply for managing the references so we can use direct pointers.
|
||||
*/
|
||||
|
||||
ObjectVisibilityContainer::ObjectVisibilityContainer(WorldObject* selfObject) :
|
||||
_selfObject(selfObject)
|
||||
{
|
||||
}
|
||||
|
||||
ObjectVisibilityContainer::~ObjectVisibilityContainer()
|
||||
{
|
||||
ASSERT(_visiblePlayersMap.empty());
|
||||
if (_visibleWorldObjectsMap)
|
||||
ASSERT((*_visibleWorldObjectsMap).empty());
|
||||
}
|
||||
|
||||
void ObjectVisibilityContainer::InitForPlayer()
|
||||
{
|
||||
_visibleWorldObjectsMap = std::make_unique<VisibleWorldObjectsMap>();
|
||||
}
|
||||
|
||||
void ObjectVisibilityContainer::CleanVisibilityReferences()
|
||||
{
|
||||
for (auto const& kvPair : _visiblePlayersMap)
|
||||
kvPair.second->GetObjectVisibilityContainer().DirectRemoveVisibilityReference(_selfObject->GetGUID());
|
||||
|
||||
if (_visibleWorldObjectsMap)
|
||||
{
|
||||
for (auto const& kvPair : *_visibleWorldObjectsMap)
|
||||
kvPair.second->GetObjectVisibilityContainer().DirectRemoveVisiblePlayerReference(_selfObject->GetGUID());
|
||||
|
||||
(*_visibleWorldObjectsMap).clear();
|
||||
}
|
||||
|
||||
_visiblePlayersMap.clear();
|
||||
}
|
||||
|
||||
void ObjectVisibilityContainer::LinkWorldObjectVisibility(WorldObject* worldObject)
|
||||
{
|
||||
// Do not link self
|
||||
if (worldObject == _selfObject)
|
||||
return;
|
||||
|
||||
// Only players can link visibility
|
||||
if (!_visibleWorldObjectsMap)
|
||||
return;
|
||||
|
||||
(*_visibleWorldObjectsMap).insert(std::make_pair(worldObject->GetGUID(), worldObject));
|
||||
worldObject->GetObjectVisibilityContainer().DirectInsertVisiblePlayerReference(_selfObject->ToPlayer());
|
||||
}
|
||||
|
||||
void ObjectVisibilityContainer::UnlinkWorldObjectVisibility(WorldObject* worldObject)
|
||||
{
|
||||
// Only players can unlink visibility
|
||||
if (!_visibleWorldObjectsMap)
|
||||
return;
|
||||
|
||||
worldObject->GetObjectVisibilityContainer().DirectRemoveVisiblePlayerReference(_selfObject->GetGUID());
|
||||
(*_visibleWorldObjectsMap).erase(worldObject->GetGUID());
|
||||
}
|
||||
|
||||
VisibleWorldObjectsMap::iterator ObjectVisibilityContainer::UnlinkVisibilityFromPlayer(WorldObject* worldObject, VisibleWorldObjectsMap::iterator itr)
|
||||
{
|
||||
ASSERT(_visibleWorldObjectsMap); // Ensure we aren't for some reason calling this as a non-player object
|
||||
worldObject->GetObjectVisibilityContainer().DirectRemoveVisiblePlayerReference(_selfObject->GetGUID());
|
||||
return (*_visibleWorldObjectsMap).erase(itr);
|
||||
}
|
||||
|
||||
VisiblePlayersMap::iterator ObjectVisibilityContainer::UnlinkVisibilityFromWorldObject(Player* player, VisiblePlayersMap::iterator itr)
|
||||
{
|
||||
player->GetObjectVisibilityContainer().DirectRemoveVisibilityReference(_selfObject->GetGUID());
|
||||
return _visiblePlayersMap.erase(itr);
|
||||
}
|
||||
|
||||
void ObjectVisibilityContainer::DirectRemoveVisibilityReference(ObjectGuid guid)
|
||||
{
|
||||
ASSERT(_visibleWorldObjectsMap);
|
||||
(*_visibleWorldObjectsMap).erase(guid);
|
||||
}
|
||||
|
||||
void ObjectVisibilityContainer::DirectInsertVisiblePlayerReference(Player* player)
|
||||
{
|
||||
_visiblePlayersMap.insert(std::make_pair(player->GetGUID(), player));
|
||||
}
|
||||
|
||||
void ObjectVisibilityContainer::DirectRemoveVisiblePlayerReference(ObjectGuid guid)
|
||||
{
|
||||
_visiblePlayersMap.erase(guid);
|
||||
}
|
||||
101
src/server/game/Entities/Object/ObjectVisibilityContainer.h
Normal file
101
src/server/game/Entities/Object/ObjectVisibilityContainer.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _OBJECTVISIBILITYCONTAINER_H
|
||||
#define _OBJECTVISIBILITYCONTAINER_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
class Player;
|
||||
class WorldObject;
|
||||
|
||||
typedef std::unordered_map<ObjectGuid, WorldObject*> VisibleWorldObjectsMap;
|
||||
typedef std::unordered_map<ObjectGuid, Player*> VisiblePlayersMap;
|
||||
|
||||
// Class that manages the visibility containers of a worldobject
|
||||
class ObjectVisibilityContainer
|
||||
{
|
||||
public:
|
||||
ObjectVisibilityContainer(WorldObject* selfObject);
|
||||
~ObjectVisibilityContainer();
|
||||
|
||||
// Creates the _visibleWorldObjectsMap map if we are a player
|
||||
void InitForPlayer();
|
||||
|
||||
// Cleans up all visibility references from other worldobjects,
|
||||
// this is used before a worldobject is deleted to prevent any dangling references
|
||||
void CleanVisibilityReferences();
|
||||
|
||||
void LinkWorldObjectVisibility(WorldObject* worldObject);
|
||||
void UnlinkWorldObjectVisibility(WorldObject* worldObject);
|
||||
|
||||
// These helpers aren't ideal, but needed in a few spots for cleaning up references
|
||||
VisibleWorldObjectsMap::iterator UnlinkVisibilityFromPlayer(WorldObject* worldObject, VisibleWorldObjectsMap::iterator itr);
|
||||
VisiblePlayersMap::iterator UnlinkVisibilityFromWorldObject(Player* player, VisiblePlayersMap::iterator itr);
|
||||
|
||||
// Returns a list of all players who can see us
|
||||
VisiblePlayersMap& GetVisiblePlayersMap() { return _visiblePlayersMap; }
|
||||
VisiblePlayersMap const& GetVisiblePlayersMap() const { return _visiblePlayersMap; }
|
||||
|
||||
// Returns a list of all worldobjects who we can see
|
||||
// Warning: This is for player objects only, all other objects will return a nullptr
|
||||
VisibleWorldObjectsMap* GetVisibleWorldObjectsMap()
|
||||
{
|
||||
if (!_visibleWorldObjectsMap)
|
||||
return nullptr;
|
||||
|
||||
return _visibleWorldObjectsMap.get();
|
||||
}
|
||||
|
||||
VisibleWorldObjectsMap const* GetVisibleWorldObjectsMap() const
|
||||
{
|
||||
if (!_visibleWorldObjectsMap)
|
||||
return nullptr;
|
||||
|
||||
return _visibleWorldObjectsMap.get();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Directly removes visibility reference. This is to be ONLY used as
|
||||
// a more efficient method for cleaning up visibility references.
|
||||
// Warning: Improper use will leave dangling references and result in crashes.
|
||||
void DirectRemoveVisibilityReference(ObjectGuid guid);
|
||||
|
||||
// Directly inserts player visibility reference.
|
||||
// Warning: Improper use will leave dangling references and result in crashes.
|
||||
void DirectInsertVisiblePlayerReference(Player* player);
|
||||
|
||||
// Directly removes player visibility reference.
|
||||
// Warning: Improper use will leave dangling references and result in crashes.
|
||||
void DirectRemoveVisiblePlayerReference(ObjectGuid guid);
|
||||
|
||||
WorldObject* _selfObject;
|
||||
|
||||
// List of all worldobjects that are visible to us (including other players)
|
||||
// Only players contain this map, thus we will only allocate it as needed.
|
||||
std::unique_ptr<VisibleWorldObjectsMap> _visibleWorldObjectsMap;
|
||||
|
||||
// List of players who are currently able to see this worldobject.
|
||||
// All worldobjects will contain this map
|
||||
VisiblePlayersMap _visiblePlayersMap;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -412,6 +412,9 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this)
|
||||
m_isInstantFlightOn = true;
|
||||
|
||||
_wasOutdoor = true;
|
||||
|
||||
GetObjectVisibilityContainer().InitForPlayer();
|
||||
|
||||
sScriptMgr->OnConstructPlayer(this);
|
||||
}
|
||||
|
||||
@@ -5707,20 +5710,7 @@ void Player::SendMessageToSetInRange(WorldPacket const* data, float dist, bool s
|
||||
SendDirectMessage(data);
|
||||
|
||||
Acore::MessageDistDeliverer notifier(this, data, dist);
|
||||
Cell::VisitWorldObjects(this, notifier, dist);
|
||||
}
|
||||
|
||||
void Player::SendMessageToSetInRange(WorldPacket const* data, float dist, bool self, bool includeMargin, bool ownTeamOnly, bool required3dDist) const
|
||||
{
|
||||
if (self)
|
||||
SendDirectMessage(data);
|
||||
|
||||
dist += GetObjectSize();
|
||||
if (includeMargin)
|
||||
dist += VISIBILITY_COMPENSATION; // pussywizard: to ensure everyone receives all important packets
|
||||
|
||||
Acore::MessageDistDeliverer notifier(this, data, dist, ownTeamOnly, nullptr, required3dDist);
|
||||
Cell::VisitWorldObjects(this, notifier, dist);
|
||||
notifier.Visit(GetObjectVisibilityContainer().GetVisiblePlayersMap());
|
||||
}
|
||||
|
||||
void Player::SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const
|
||||
@@ -5728,8 +5718,8 @@ void Player::SendMessageToSet(WorldPacket const* data, Player const* skipped_rcv
|
||||
if (skipped_rcvr != this)
|
||||
SendDirectMessage(data);
|
||||
|
||||
Acore::MessageDistDeliverer notifier(this, data, GetVisibilityRange(), false, skipped_rcvr);
|
||||
Cell::VisitWorldObjects(this, notifier, GetVisibilityRange());
|
||||
Acore::MessageDistDeliverer notifier(this, data, 0.0f, false, skipped_rcvr);
|
||||
notifier.Visit(GetObjectVisibilityContainer().GetVisiblePlayersMap());
|
||||
}
|
||||
|
||||
void Player::SendDirectMessage(WorldPacket const* data) const
|
||||
@@ -7711,23 +7701,26 @@ bool Player::CheckAmmoCompatibility(ItemTemplate const* ammo_proto) const
|
||||
|
||||
void Player::SendQuestGiverStatusMultiple()
|
||||
{
|
||||
if (GetObjectVisibilityContainer().GetVisibleWorldObjectsMap()->empty())
|
||||
return;
|
||||
|
||||
uint32 count = 0;
|
||||
|
||||
WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4);
|
||||
data << uint32(count); // placeholder
|
||||
|
||||
for (GuidUnorderedSet::const_iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
|
||||
DoForAllVisibleWorldObjects([this, &data, &count](WorldObject* worldObject)
|
||||
{
|
||||
uint32 questStatus = DIALOG_STATUS_NONE;
|
||||
|
||||
if ((*itr).IsAnyTypeCreature())
|
||||
if (worldObject->IsCreature())
|
||||
{
|
||||
// need also pet quests case support
|
||||
Creature* questgiver = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr);
|
||||
Creature* questgiver = worldObject->ToCreature();
|
||||
if (!questgiver || questgiver->IsHostileTo(this))
|
||||
continue;
|
||||
return;
|
||||
if (!questgiver->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER))
|
||||
continue;
|
||||
return;
|
||||
|
||||
questStatus = GetQuestDialogStatus(questgiver);
|
||||
|
||||
@@ -7735,11 +7728,11 @@ void Player::SendQuestGiverStatusMultiple()
|
||||
data << uint8(questStatus);
|
||||
++count;
|
||||
}
|
||||
else if ((*itr).IsGameObject())
|
||||
else if (worldObject->IsGameObject())
|
||||
{
|
||||
GameObject* questgiver = GetMap()->GetGameObject(*itr);
|
||||
GameObject* questgiver = worldObject->ToGameObject();
|
||||
if (!questgiver || questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER)
|
||||
continue;
|
||||
return;
|
||||
|
||||
questStatus = GetQuestDialogStatus(questgiver);
|
||||
|
||||
@@ -7747,7 +7740,7 @@ void Player::SendQuestGiverStatusMultiple()
|
||||
data << uint8(questStatus);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
data.put<uint32>(0, count); // write real count
|
||||
GetSession()->SendPacket(&data);
|
||||
@@ -9383,7 +9376,12 @@ void Player::Say(std::string_view text, Language language, WorldObject const* /*
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_SAY, language, this, this, _text);
|
||||
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true, false, false, true);
|
||||
|
||||
SendDirectMessage(&data);
|
||||
|
||||
// Special handling for messages, do not use visibility map for stealthed units
|
||||
Acore::MessageDistDeliverer notifier(this, &data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), false, nullptr, true);
|
||||
Cell::VisitWorldObjects(this, notifier, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY));
|
||||
}
|
||||
|
||||
void Player::Say(uint32 textId, WorldObject const* target /*= nullptr*/)
|
||||
@@ -9404,7 +9402,12 @@ void Player::Yell(std::string_view text, Language language, WorldObject const* /
|
||||
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_YELL, language, this, this, _text);
|
||||
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true, false, false, true);
|
||||
|
||||
SendDirectMessage(&data);
|
||||
|
||||
// Special handling for messages, do not use visibility map for stealthed units
|
||||
Acore::MessageDistDeliverer notifier(this, &data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), false, nullptr, true);
|
||||
Cell::VisitWorldObjects(this, notifier, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL));
|
||||
}
|
||||
|
||||
void Player::Yell(uint32 textId, WorldObject const* target /*= nullptr*/)
|
||||
@@ -9426,7 +9429,11 @@ void Player::TextEmote(std::string_view text, WorldObject const* /*= nullptr*/,
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_EMOTE, LANG_UNIVERSAL, this, this, _text);
|
||||
|
||||
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true, false, !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE), true);
|
||||
SendDirectMessage(&data);
|
||||
|
||||
// Special handling for messages, do not use visibility map for stealthed units
|
||||
Acore::MessageDistDeliverer notifier(this, &data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE), nullptr, true);
|
||||
Cell::VisitWorldObjects(this, notifier, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE));
|
||||
}
|
||||
|
||||
void Player::TextEmote(uint32 textId, WorldObject const* target /*= nullptr*/, bool /*isBossEmote = false*/)
|
||||
@@ -10487,35 +10494,24 @@ void Player::ContinueTaxiFlight()
|
||||
|
||||
void Player::SendTaxiNodeStatusMultiple()
|
||||
{
|
||||
for (auto itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
|
||||
DoForAllVisibleWorldObjects([this](WorldObject* worldObject)
|
||||
{
|
||||
if (!itr->IsCreature())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Creature* creature = ObjectAccessor::GetCreature(*this, *itr);
|
||||
Creature* creature = worldObject->ToCreature();
|
||||
if (!creature || creature->IsHostileTo(this))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
|
||||
if (!creature->HasNpcFlag(UNIT_NPC_FLAG_FLIGHTMASTER))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
|
||||
uint32 nearestNode = sObjectMgr->GetNearestTaxiNode(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetMapId(), GetTeamId());
|
||||
if (!nearestNode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
|
||||
WorldPacket data(SMSG_TAXINODE_STATUS, 9);
|
||||
data << *itr;
|
||||
data << creature->GetGUID();
|
||||
data << uint8(m_taxi.IsTaximaskNodeKnown(nearestNode) ? 1 : 0);
|
||||
SendDirectMessage(&data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
|
||||
@@ -11393,31 +11389,22 @@ WorldLocation Player::GetStartPosition() const
|
||||
|
||||
bool Player::HaveAtClient(WorldObject const* u) const
|
||||
{
|
||||
if (u == this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Motion Transports are always present in player's client
|
||||
if (GameObject const* gameobject = u->ToGameObject())
|
||||
{
|
||||
if (gameobject->IsMotionTransport())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return m_clientGUIDs.find(u->GetGUID()) != m_clientGUIDs.end();
|
||||
return HaveAtClient(u->GetGUID());
|
||||
}
|
||||
|
||||
bool Player::HaveAtClient(ObjectGuid guid) const
|
||||
{
|
||||
if (guid == GetGUID())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_clientGUIDs.find(guid) != m_clientGUIDs.end();
|
||||
return GetObjectVisibilityContainer().GetVisibleWorldObjectsMap()->find(guid) != GetObjectVisibilityContainer().GetVisibleWorldObjectsMap()->end();
|
||||
}
|
||||
|
||||
bool Player::IsNeverVisible() const
|
||||
|
||||
@@ -2043,7 +2043,6 @@ public:
|
||||
|
||||
void SendMessageToSet(WorldPacket const* data, bool self) const override;
|
||||
void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self) const override;
|
||||
void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self, bool includeMargin, bool ownTeamOnly, bool required3dDist = false) const;
|
||||
void SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const override;
|
||||
void SendTeleportAckPacket();
|
||||
|
||||
@@ -2397,7 +2396,6 @@ public:
|
||||
void SetEntryPoint();
|
||||
|
||||
// currently visible objects at player client
|
||||
GuidUnorderedSet m_clientGUIDs;
|
||||
std::vector<Unit*> m_newVisible; // pussywizard
|
||||
|
||||
[[nodiscard]] bool HaveAtClient(WorldObject const* u) const;
|
||||
|
||||
@@ -1635,35 +1635,35 @@ void Player::UpdateObjectVisibility(bool forced, bool fromUpdate)
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, T* target,
|
||||
inline void UpdateVisibilityOf_helper(Player* player, T* target,
|
||||
std::vector<Unit*>& /*v*/)
|
||||
{
|
||||
s64.insert(target->GetGUID());
|
||||
player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, GameObject* target,
|
||||
inline void UpdateVisibilityOf_helper(Player* player, GameObject* target,
|
||||
std::vector<Unit*>& /*v*/)
|
||||
{
|
||||
// @HACK: This is to prevent objects like deeprun tram from disappearing
|
||||
// when player moves far from its spawn point while riding it
|
||||
if ((target->GetGOInfo()->type != GAMEOBJECT_TYPE_TRANSPORT))
|
||||
s64.insert(target->GetGUID());
|
||||
player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, Creature* target,
|
||||
inline void UpdateVisibilityOf_helper(Player* player, Creature* target,
|
||||
std::vector<Unit*>& v)
|
||||
{
|
||||
s64.insert(target->GetGUID());
|
||||
player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target);
|
||||
v.push_back(target);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, Player* target,
|
||||
inline void UpdateVisibilityOf_helper(Player* player, Player* target,
|
||||
std::vector<Unit*>& v)
|
||||
{
|
||||
s64.insert(target->GetGUID());
|
||||
player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target);
|
||||
v.push_back(target);
|
||||
}
|
||||
|
||||
@@ -1692,7 +1692,7 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data,
|
||||
BeforeVisibilityDestroy<T>(target, this);
|
||||
|
||||
target->BuildOutOfRangeUpdateBlock(&data);
|
||||
m_clientGUIDs.erase(target->GetGUID());
|
||||
GetObjectVisibilityContainer().UnlinkWorldObjectVisibility(target);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1700,7 +1700,7 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data,
|
||||
if (CanSeeOrDetect(target, false, true))
|
||||
{
|
||||
target->BuildCreateUpdateBlockForPlayer(&data, this);
|
||||
UpdateVisibilityOf_helper(m_clientGUIDs, target, visibleNow);
|
||||
UpdateVisibilityOf_helper(this, target, visibleNow);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1726,7 +1726,7 @@ void Player::UpdateVisibilityOf(WorldObject* target)
|
||||
BeforeVisibilityDestroy<Creature>(target->ToCreature(), this);
|
||||
|
||||
target->DestroyForPlayer(this);
|
||||
m_clientGUIDs.erase(target->GetGUID());
|
||||
GetObjectVisibilityContainer().UnlinkWorldObjectVisibility(target);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1734,7 +1734,7 @@ void Player::UpdateVisibilityOf(WorldObject* target)
|
||||
if (CanSeeOrDetect(target, false, true))
|
||||
{
|
||||
target->SendUpdateToPlayer(this);
|
||||
m_clientGUIDs.insert(target->GetGUID());
|
||||
GetObjectVisibilityContainer().LinkWorldObjectVisibility(target);
|
||||
|
||||
// target aura duration for caster show only if target exist at
|
||||
// caster client send data at target visibility change (adding to
|
||||
@@ -1747,69 +1747,67 @@ void Player::UpdateVisibilityOf(WorldObject* target)
|
||||
|
||||
void Player::UpdateTriggerVisibility()
|
||||
{
|
||||
if (m_clientGUIDs.empty())
|
||||
return;
|
||||
|
||||
if (!IsInWorld())
|
||||
return;
|
||||
|
||||
UpdateData udata;
|
||||
WorldPacket packet;
|
||||
for (GuidUnorderedSet::iterator itr = m_clientGUIDs.begin();
|
||||
itr != m_clientGUIDs.end(); ++itr)
|
||||
if (GetObjectVisibilityContainer().GetVisibleWorldObjectsMap()->empty())
|
||||
return;
|
||||
|
||||
UpdateData udata;
|
||||
DoForAllVisibleWorldObjects([this, &udata](WorldObject* worldObject)
|
||||
{
|
||||
if ((*itr).IsCreatureOrVehicle())
|
||||
if (worldObject->IsCreature())
|
||||
{
|
||||
Creature* creature = GetMap()->GetCreature(*itr);
|
||||
Creature* creature = worldObject->ToCreature();
|
||||
// Update fields of triggers, transformed units or unselectable
|
||||
// units (values dependent on GM state)
|
||||
if (!creature || (!creature->IsTrigger() &&
|
||||
!creature->HasTransformAura() &&
|
||||
!creature->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)))
|
||||
continue;
|
||||
!creature->HasTransformAura() &&
|
||||
!creature->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)))
|
||||
return;
|
||||
|
||||
creature->SetFieldNotifyFlag(UF_FLAG_PUBLIC);
|
||||
creature->BuildValuesUpdateBlockForPlayer(&udata, this);
|
||||
creature->RemoveFieldNotifyFlag(UF_FLAG_PUBLIC);
|
||||
}
|
||||
else if ((*itr).IsGameObject())
|
||||
else if (worldObject->IsGameObject())
|
||||
{
|
||||
GameObject* go = GetMap()->GetGameObject(*itr);
|
||||
GameObject* go = worldObject->ToGameObject();
|
||||
if (!go)
|
||||
continue;
|
||||
return;
|
||||
|
||||
go->SetFieldNotifyFlag(UF_FLAG_PUBLIC);
|
||||
go->BuildValuesUpdateBlockForPlayer(&udata, this);
|
||||
go->RemoveFieldNotifyFlag(UF_FLAG_PUBLIC);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!udata.HasData())
|
||||
return;
|
||||
|
||||
WorldPacket packet;
|
||||
udata.BuildPacket(packet);
|
||||
GetSession()->SendPacket(&packet);
|
||||
}
|
||||
|
||||
void Player::UpdateForQuestWorldObjects()
|
||||
{
|
||||
if (m_clientGUIDs.empty())
|
||||
if (GetObjectVisibilityContainer().GetVisibleWorldObjectsMap()->empty())
|
||||
return;
|
||||
|
||||
UpdateData udata;
|
||||
WorldPacket packet;
|
||||
for (GuidUnorderedSet::iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
|
||||
UpdateData udata;
|
||||
DoForAllVisibleWorldObjects([this, &udata](WorldObject* worldObject)
|
||||
{
|
||||
if ((*itr).IsGameObject())
|
||||
if (worldObject->IsGameObject())
|
||||
{
|
||||
if (GameObject* obj = ObjectAccessor::GetGameObject(*this, *itr))
|
||||
if (GameObject* obj = worldObject->ToGameObject())
|
||||
obj->BuildValuesUpdateBlockForPlayer(&udata, this);
|
||||
}
|
||||
else if ((*itr).IsCreatureOrVehicle())
|
||||
else if (worldObject->IsCreature())
|
||||
{
|
||||
Creature* obj = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr);
|
||||
Creature* obj = worldObject->ToCreature();
|
||||
if (!obj)
|
||||
continue;
|
||||
return;
|
||||
|
||||
// check if this unit requires quest specific flags
|
||||
if (obj->HasNpcFlag(UNIT_NPC_FLAG_SPELLCLICK))
|
||||
@@ -1833,12 +1831,14 @@ void Player::UpdateForQuestWorldObjects()
|
||||
}
|
||||
}
|
||||
else if (obj->HasNpcFlag(UNIT_NPC_FLAG_VENDOR_MASK | UNIT_NPC_FLAG_TRAINER))
|
||||
{
|
||||
obj->BuildValuesUpdateBlockForPlayer(&udata, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!udata.HasData())
|
||||
return;
|
||||
|
||||
WorldPacket packet;
|
||||
udata.BuildPacket(packet);
|
||||
GetSession()->SendPacket(&packet);
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ void MotionTransport::CleanupsBeforeDelete(bool finalCleanup /*= true*/)
|
||||
GameObject::CleanupsBeforeDelete(finalCleanup);
|
||||
}
|
||||
|
||||
void MotionTransport::BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&)
|
||||
void MotionTransport::BuildUpdate(UpdateDataMapType& data_map)
|
||||
{
|
||||
Map::PlayerList const& players = GetMap()->GetPlayers();
|
||||
if (players.IsEmpty())
|
||||
@@ -812,7 +812,7 @@ void StaticTransport::CleanupsBeforeDelete(bool finalCleanup /*= true*/)
|
||||
GameObject::CleanupsBeforeDelete(finalCleanup);
|
||||
}
|
||||
|
||||
void StaticTransport::BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&)
|
||||
void StaticTransport::BuildUpdate(UpdateDataMapType& data_map)
|
||||
{
|
||||
Map::PlayerList const& players = GetMap()->GetPlayers();
|
||||
if (players.IsEmpty())
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
|
||||
bool CreateMoTrans(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress);
|
||||
void CleanupsBeforeDelete(bool finalCleanup = true) override;
|
||||
void BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&) override;
|
||||
void BuildUpdate(UpdateDataMapType& data_map) override;
|
||||
|
||||
void Update(uint32 diff) override;
|
||||
void DelayedUpdate(uint32 diff);
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
|
||||
bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0) override;
|
||||
void CleanupsBeforeDelete(bool finalCleanup = true) override;
|
||||
void BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&) override;
|
||||
void BuildUpdate(UpdateDataMapType& data_map) override;
|
||||
|
||||
void Update(uint32 diff) override;
|
||||
void RelocateToProgress(uint32 progress);
|
||||
|
||||
@@ -32,7 +32,6 @@ void VisibleNotifier::Visit(GameObjectMapType& m)
|
||||
if (i_largeOnly != go->IsVisibilityOverridden())
|
||||
continue;
|
||||
|
||||
vis_guids.erase(go->GetGUID());
|
||||
i_player.UpdateVisibilityOf(go, i_data, i_visibleNow);
|
||||
}
|
||||
}
|
||||
@@ -42,56 +41,66 @@ 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())
|
||||
{
|
||||
for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr)
|
||||
{
|
||||
if (i_largeOnly != (*itr)->IsVisibilityOverridden())
|
||||
continue;
|
||||
|
||||
if (vis_guids.find((*itr)->GetGUID()) != vis_guids.end())
|
||||
switch ((*itr)->GetTypeId())
|
||||
{
|
||||
vis_guids.erase((*itr)->GetGUID());
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch ((*itr)->GetTypeId())
|
||||
VisibleWorldObjectsMap* visibleWorldObjects = i_player.GetObjectVisibilityContainer().GetVisibleWorldObjectsMap();
|
||||
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))
|
||||
{
|
||||
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;
|
||||
++itr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (GuidUnorderedSet::const_iterator it = vis_guids.begin(); it != vis_guids.end(); ++it)
|
||||
{
|
||||
if (WorldObject* obj = ObjectAccessor::GetWorldObject(i_player, *it))
|
||||
if (i_player.m_seer->IsWithinDist(obj, i_player.GetSightRange(obj), true))
|
||||
{
|
||||
if (i_largeOnly != obj->IsVisibilityOverridden())
|
||||
continue;
|
||||
++itr;
|
||||
continue;
|
||||
}
|
||||
|
||||
// pussywizard: static transports are removed only in RemovePlayerFromMap and here if can no longer detect (eg. phase changed)
|
||||
if ((*it).IsTransport())
|
||||
if (GameObject* staticTrans = i_player.GetMap()->GetGameObject(*it))
|
||||
if (i_player.CanSeeOrDetect(staticTrans, false, true))
|
||||
continue;
|
||||
i_data.AddOutOfRangeGUID(obj->GetGUID());
|
||||
|
||||
i_player.m_clientGUIDs.erase(*it);
|
||||
i_data.AddOutOfRangeGUID(*it);
|
||||
if (Player* objPlayer = obj->ToPlayer())
|
||||
objPlayer->UpdateVisibilityOf(&i_player);
|
||||
|
||||
if ((*it).IsPlayer())
|
||||
{
|
||||
Player* player = ObjectAccessor::FindPlayer(*it);
|
||||
if (player && player->IsInMap(&i_player))
|
||||
player->UpdateVisibilityOf(&i_player);
|
||||
}
|
||||
// Clean up references
|
||||
itr = i_player.GetObjectVisibilityContainer().UnlinkVisibilityFromPlayer(obj, itr);
|
||||
}
|
||||
|
||||
if (!i_data.HasData())
|
||||
@@ -170,7 +179,6 @@ void PlayerRelocationNotifier::Visit(PlayerMapType& m)
|
||||
for (PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
|
||||
{
|
||||
Player* player = iter->GetSource();
|
||||
vis_guids.erase(player->GetGUID());
|
||||
i_player.UpdateVisibilityOf(player, i_data, i_visibleNow);
|
||||
player->UpdateVisibilityOf(&i_player); // this notifier with different Visit(PlayerMapType&) than VisibleNotifier is needed to update visibility of self for other players when we move (eg. stealth detection changes)
|
||||
}
|
||||
@@ -208,6 +216,23 @@ void AIRelocationNotifier::Visit(CreatureMapType& m)
|
||||
}
|
||||
}
|
||||
|
||||
// Uses visibility map
|
||||
void MessageDistDeliverer::Visit(VisiblePlayersMap const& m)
|
||||
{
|
||||
for (auto const& kvPair : m)
|
||||
{
|
||||
Player const* target = kvPair.second;
|
||||
if (i_distSq != 0.0f && target->m_seer->GetExactDist2dSq(i_source) > i_distSq)
|
||||
continue;
|
||||
|
||||
// @todo: Might not need this check anymore
|
||||
if (skipped_receiver == target)
|
||||
continue;
|
||||
|
||||
target->SendDirectMessage(i_message);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageDistDeliverer::Visit(PlayerMapType& m)
|
||||
{
|
||||
for (PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
|
||||
|
||||
@@ -43,14 +43,13 @@ namespace Acore
|
||||
struct VisibleNotifier
|
||||
{
|
||||
Player& i_player;
|
||||
GuidUnorderedSet vis_guids;
|
||||
std::vector<Unit*>& i_visibleNow;
|
||||
bool i_gobjOnly;
|
||||
bool i_largeOnly;
|
||||
UpdateData i_data;
|
||||
|
||||
VisibleNotifier(Player& player, bool gobjOnly, bool largeOnly) :
|
||||
i_player(player), vis_guids(player.m_clientGUIDs), i_visibleNow(player.m_newVisible), i_gobjOnly(gobjOnly), i_largeOnly(largeOnly)
|
||||
i_player(player), i_visibleNow(player.m_newVisible), i_gobjOnly(gobjOnly), i_largeOnly(largeOnly)
|
||||
{
|
||||
i_visibleNow.clear();
|
||||
}
|
||||
@@ -111,6 +110,7 @@ namespace Acore
|
||||
, skipped_receiver(skipped), required3dDist(req3dDist)
|
||||
{
|
||||
}
|
||||
void Visit(VisiblePlayersMap const& m);
|
||||
void Visit(PlayerMapType& m);
|
||||
void Visit(CreatureMapType& m);
|
||||
void Visit(DynamicObjectMapType& m);
|
||||
|
||||
@@ -37,7 +37,6 @@ inline void Acore::VisibleNotifier::Visit(GridRefMgr<T>& m)
|
||||
if (i_largeOnly != iter->GetSource()->IsVisibilityOverridden())
|
||||
continue;
|
||||
|
||||
vis_guids.erase(iter->GetSource()->GetGUID());
|
||||
i_player.UpdateVisibilityOf(iter->GetSource(), i_data, i_visibleNow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1180,7 +1180,6 @@ void WorldSession::HandlePlayerLoginToCharInWorld(Player* pCurrChar)
|
||||
pCurrChar->GetMap()->SendInitTransports(pCurrChar);
|
||||
pCurrChar->GetMap()->SendInitSelf(pCurrChar);
|
||||
pCurrChar->GetMap()->SendZoneDynamicInfo(pCurrChar);
|
||||
pCurrChar->m_clientGUIDs.clear();
|
||||
pCurrChar->UpdateObjectVisibility(false);
|
||||
|
||||
pCurrChar->CleanupChannels();
|
||||
|
||||
@@ -289,7 +289,6 @@ bool Map::AddPlayerToMap(Player* player)
|
||||
SendInitSelf(player);
|
||||
SendZoneDynamicInfo(player);
|
||||
|
||||
player->m_clientGUIDs.clear();
|
||||
player->UpdateObjectVisibility(false);
|
||||
|
||||
if (player->IsAlive())
|
||||
@@ -676,13 +675,9 @@ void Map::RemovePlayerFromMap(Player* player, bool remove)
|
||||
player->UpdateZone(MAP_INVALID_ZONE, 0);
|
||||
player->getHostileRefMgr().deleteReferences(true); // pussywizard: multithreading crashfix
|
||||
|
||||
bool inWorld = player->IsInWorld();
|
||||
player->RemoveFromWorld();
|
||||
SendRemoveTransports(player);
|
||||
|
||||
if (!inWorld) // pussywizard: if was in world, RemoveFromWorld() called DestroyForNearbyPlayers()
|
||||
player->DestroyForNearbyPlayers(); // pussywizard: previous player->UpdateObjectVisibility(true)
|
||||
|
||||
if (player->IsInGrid())
|
||||
player->RemoveFromGrid();
|
||||
else
|
||||
@@ -702,15 +697,11 @@ void Map::AfterPlayerUnlinkFromMap()
|
||||
template<class T>
|
||||
void Map::RemoveFromMap(T* obj, bool remove)
|
||||
{
|
||||
bool inWorld = obj->IsInWorld() && obj->GetTypeId() >= TYPEID_UNIT && obj->GetTypeId() <= TYPEID_GAMEOBJECT;
|
||||
obj->RemoveFromWorld();
|
||||
|
||||
if (obj->isActiveObject())
|
||||
RemoveFromActive(obj);
|
||||
|
||||
if (!inWorld) // pussywizard: if was in world, RemoveFromWorld() called DestroyForNearbyPlayers()
|
||||
obj->DestroyForNearbyPlayers(); // pussywizard: previous player->UpdateObjectVisibility()
|
||||
|
||||
obj->RemoveFromGrid();
|
||||
|
||||
obj->ResetMap();
|
||||
@@ -1653,6 +1644,9 @@ void Map::SendInitTransports(Player* player)
|
||||
if (*itr != player->GetTransport())
|
||||
(*itr)->BuildCreateUpdateBlockForPlayer(&transData, player);
|
||||
|
||||
if (!transData.HasData())
|
||||
return;
|
||||
|
||||
WorldPacket packet;
|
||||
transData.BuildPacket(packet);
|
||||
player->GetSession()->SendPacket(&packet);
|
||||
@@ -1667,7 +1661,7 @@ void Map::SendRemoveTransports(Player* player)
|
||||
(*itr)->BuildOutOfRangeUpdateBlock(&transData);
|
||||
|
||||
// pussywizard: remove static transports from client
|
||||
for (GuidUnorderedSet::const_iterator it = player->m_clientGUIDs.begin(); it != player->m_clientGUIDs.end(); )
|
||||
/*for (GuidUnorderedSet::const_iterator it = player->m_clientGUIDs.begin(); it != player->m_clientGUIDs.end(); )
|
||||
{
|
||||
if ((*it).IsTransport())
|
||||
{
|
||||
@@ -1676,7 +1670,10 @@ void Map::SendRemoveTransports(Player* player)
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}*/
|
||||
|
||||
if (!transData.HasData())
|
||||
return;
|
||||
|
||||
WorldPacket packet;
|
||||
transData.BuildPacket(packet);
|
||||
@@ -1686,7 +1683,6 @@ void Map::SendRemoveTransports(Player* player)
|
||||
void Map::SendObjectUpdates()
|
||||
{
|
||||
UpdateDataMapType update_players;
|
||||
UpdatePlayerSet player_set;
|
||||
|
||||
while (!_updateObjects.empty())
|
||||
{
|
||||
@@ -1694,7 +1690,7 @@ void Map::SendObjectUpdates()
|
||||
ASSERT(obj->IsInWorld());
|
||||
|
||||
_updateObjects.erase(_updateObjects.begin());
|
||||
obj->BuildUpdate(update_players, player_set);
|
||||
obj->BuildUpdate(update_players);
|
||||
}
|
||||
|
||||
WorldPacket packet; // here we allocate a std::vector with a size of 0x10000
|
||||
@@ -2686,7 +2682,7 @@ void Map::RemoveCorpse(Corpse* corpse)
|
||||
ASSERT(corpse);
|
||||
GridCoord const gridCoord = Acore::ComputeGridCoord(corpse->GetPositionX(), corpse->GetPositionY());
|
||||
|
||||
corpse->DestroyForNearbyPlayers();
|
||||
corpse->DestroyForVisiblePlayers();
|
||||
if (corpse->IsInGrid())
|
||||
RemoveFromMap(corpse, false);
|
||||
else
|
||||
|
||||
@@ -104,7 +104,7 @@ public:
|
||||
c->UpdatePosition(630.88f, 131.39f, 140.8f, 3.02f, true);
|
||||
|
||||
c->StopMovingOnCurrentPos();
|
||||
c->DestroyForNearbyPlayers();
|
||||
c->DestroyForVisiblePlayers();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -792,7 +792,7 @@ public:
|
||||
{
|
||||
icehowl->UpdatePosition(513.19f, 139.48f, 395.22f, 3 * M_PI / 2, true);
|
||||
icehowl->StopMovingOnCurrentPos();
|
||||
icehowl->DestroyForNearbyPlayers();
|
||||
icehowl->DestroyForVisiblePlayers();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -959,7 +959,7 @@ public:
|
||||
{
|
||||
jaraxxus->UpdatePosition(613.83f, 139.5f, 395.22f, 3 * M_PI / 2, true);
|
||||
jaraxxus->StopMovingOnCurrentPos();
|
||||
jaraxxus->DestroyForNearbyPlayers();
|
||||
jaraxxus->DestroyForVisiblePlayers();
|
||||
}
|
||||
|
||||
if (Creature* c = instance->GetCreature(NPC_TirionGUID))
|
||||
|
||||
@@ -252,7 +252,7 @@ public:
|
||||
me->SummonCreature(summons[0][i], cords[0][0] + ((i % 2) ? 4.0f : -4.0f), cords[0][1] + (i < 2 ? 4.0f : -4.0f), cords[0][2], 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000);
|
||||
uint8 phase = GetPhaseByCurrentPosition();
|
||||
me->SetHomePosition(cords[phase + 1][0], cords[phase + 1][1], cords[phase + 1][2], cords[phase + 1][3]);
|
||||
me->DestroyForNearbyPlayers();
|
||||
me->DestroyForVisiblePlayers();
|
||||
LeaveCombat();
|
||||
me->CastSpell(me, SPELL_EVOCATION, true);
|
||||
releaseLockTimer = 1;
|
||||
@@ -264,7 +264,7 @@ public:
|
||||
me->SummonCreature(summons[1][i], cords[1][0] + ((i % 2) ? 4.0f : -4.0f), cords[1][1] + (i < 2 ? 4.0f : -4.0f), cords[1][2], 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000);
|
||||
uint8 phase = GetPhaseByCurrentPosition();
|
||||
me->SetHomePosition(cords[phase + 1][0], cords[phase + 1][1], cords[phase + 1][2], cords[phase + 1][3]);
|
||||
me->DestroyForNearbyPlayers();
|
||||
me->DestroyForVisiblePlayers();
|
||||
LeaveCombat();
|
||||
me->CastSpell(me, SPELL_EVOCATION, true);
|
||||
releaseLockTimer = 1;
|
||||
@@ -276,7 +276,7 @@ public:
|
||||
me->SummonCreature(summons[2][i], cords[2][0] + ((i % 2) ? 4.0f : -4.0f), cords[2][1] + (i < 2 ? 4.0f : -4.0f), cords[2][2], 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000);
|
||||
uint8 phase = GetPhaseByCurrentPosition();
|
||||
me->SetHomePosition(cords[phase + 1][0], cords[phase + 1][1], cords[phase + 1][2], cords[phase + 1][3]);
|
||||
me->DestroyForNearbyPlayers();
|
||||
me->DestroyForVisiblePlayers();
|
||||
LeaveCombat();
|
||||
me->CastSpell(me, SPELL_EVOCATION, true);
|
||||
releaseLockTimer = 1;
|
||||
|
||||
@@ -366,7 +366,7 @@ struct boss_ingvar_the_plunderer : public ScriptedAI
|
||||
case EVENT_AXE_PICKUP:
|
||||
if (Creature* c = ObjectAccessor::GetCreature(*me, ThrowGUID))
|
||||
{
|
||||
c->DestroyForNearbyPlayers();
|
||||
c->DestroyForVisiblePlayers();
|
||||
c->DespawnOrUnsummon();
|
||||
summons.DespawnAll();
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ public:
|
||||
c->loot.FillLoot(lootid, LootTemplates_Creature, c->GetLootRecipient(), false, false, c->GetLootMode(), c);
|
||||
if (c->GetLootMode())
|
||||
c->loot.generateMoneyLoot(c->GetCreatureTemplate()->mingold, c->GetCreatureTemplate()->maxgold);
|
||||
c->DestroyForNearbyPlayers();
|
||||
c->DestroyForVisiblePlayers();
|
||||
c->SetVisible(true);
|
||||
}
|
||||
break;
|
||||
@@ -244,7 +244,7 @@ public:
|
||||
c->loot.FillLoot(lootid, LootTemplates_Creature, c->GetLootRecipient(), false, false, c->GetLootMode(), c);
|
||||
if (c->GetLootMode())
|
||||
c->loot.generateMoneyLoot(c->GetCreatureTemplate()->mingold, c->GetCreatureTemplate()->maxgold);
|
||||
c->DestroyForNearbyPlayers();
|
||||
c->DestroyForVisiblePlayers();
|
||||
c->SetVisible(true);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -159,7 +159,7 @@ public:
|
||||
{
|
||||
me->UpdatePosition(me->GetPositionX(), me->GetPositionY(), h, me->GetOrientation(), true); // move to ground
|
||||
me->StopMovingOnCurrentPos();
|
||||
me->DestroyForNearbyPlayers();
|
||||
me->DestroyForVisiblePlayers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,10 +110,11 @@ public:
|
||||
{
|
||||
if (Player* target = ObjectAccessor::GetPlayer(_owner, _targetGUID))
|
||||
{
|
||||
target->m_clientGUIDs.insert(_owner.GetGUID());
|
||||
// @todo: wtf? this is wrong but I cba looking into it.
|
||||
target->GetObjectVisibilityContainer().LinkWorldObjectVisibility(&_owner);
|
||||
_owner.CastSpell(target, SPELL_ENVENOM, true);
|
||||
target->RemoveAurasDueToSpell(SPELL_DEADLY_POISON);
|
||||
target->m_clientGUIDs.erase(_owner.GetGUID());
|
||||
target->GetObjectVisibilityContainer().UnlinkWorldObjectVisibility(&_owner);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
if (_timer > 5000)
|
||||
{
|
||||
me->CastSpell(nullptr, 9056);
|
||||
me->DestroyForNearbyPlayers();
|
||||
me->DestroyForVisiblePlayers();
|
||||
_timer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user