Core:
- Redesigned stealth and invisibility handling - Implemented the handling of multiple stealth types - Implemented fake inebriation - The message deliverer no longer sends packets from a non-visible source - The server won't send that much garbage which just takes bandwith - It won't be possible to use cheats to detect invisible objects - Removed a lot of checks for the Z-coord - Fixes visibility problems happening while flying - Limited the grid activation range of creatures to use less resources - Implemented Shroud of Death - Implemented increased visibility range for active objects - Removed visibility check at spellhit (only sanctuary effects should prevent it) (And a lot of other changes...) Closes issue 4208 Closes issue 3049 Closes issue 2097 Closes issue 2198 Closes issue 2384 Closes issue 2197 Closes issue 2319 --HG-- branch : trunk
This commit is contained in:
@@ -1223,7 +1223,10 @@ WorldObject::WorldObject(): WorldLocation(),
|
||||
m_isWorldObject(false), m_name(""), m_isActive(false), m_zoneScript(NULL),
|
||||
m_transport(NULL), m_currMap(NULL), m_InstanceId(0),
|
||||
m_phaseMask(PHASEMASK_NORMAL), m_notifyflags(0), m_executed_notifies(0)
|
||||
{}
|
||||
{
|
||||
m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST);
|
||||
m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE);
|
||||
}
|
||||
|
||||
void WorldObject::SetWorldObject(bool on)
|
||||
{
|
||||
@@ -1608,6 +1611,239 @@ void WorldObject::MonsterWhisper(const char* text, uint64 receiver, bool IsBossW
|
||||
player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
bool WorldObject::isValid() const
|
||||
{
|
||||
if (!IsInWorld())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float WorldObject::GetGridActivationRange() const
|
||||
{
|
||||
if (ToPlayer())
|
||||
return GetMap()->GetVisibilityRange();
|
||||
else if (ToCreature())
|
||||
return ToCreature()->m_SightDistance;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float WorldObject::GetVisibilityRange() const
|
||||
{
|
||||
if (isActiveObject() && !ToPlayer())
|
||||
return MAX_VISIBILITY_DISTANCE;
|
||||
else
|
||||
return GetMap()->GetVisibilityRange();
|
||||
}
|
||||
|
||||
float WorldObject::GetSightRange(const WorldObject* target) const
|
||||
{
|
||||
if (ToUnit())
|
||||
{
|
||||
if (ToPlayer())
|
||||
{
|
||||
if (target && target->isActiveObject())
|
||||
return MAX_VISIBILITY_DISTANCE;
|
||||
else
|
||||
return GetMap()->GetVisibilityRange();
|
||||
}
|
||||
else if (ToCreature())
|
||||
return ToCreature()->m_SightDistance;
|
||||
else
|
||||
return SIGHT_RANGE_UNIT;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool WorldObject::canSeeOrDetect(WorldObject const* obj, bool ignoreStealth, bool distanceCheck) const
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
|
||||
if (!obj->isValid())
|
||||
return false;
|
||||
|
||||
if (GetMap() != obj->GetMap())
|
||||
return false;
|
||||
|
||||
if (!InSamePhase(obj))
|
||||
return false;
|
||||
|
||||
if (obj->isAlwaysVisibleFor(this))
|
||||
return true;
|
||||
|
||||
if (canSeeAlways(obj))
|
||||
return true;
|
||||
|
||||
bool corpseCheck = false;
|
||||
bool corpseVisibility = false;
|
||||
if (distanceCheck)
|
||||
{
|
||||
if (const Player* thisPlayer = ToPlayer())
|
||||
{
|
||||
if (thisPlayer->isDead() && thisPlayer->GetHealth() > 0 && // Cheap way to check for ghost state
|
||||
!(obj->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GHOST) & m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GHOST) & GHOST_VISIBILITY_GHOST))
|
||||
{
|
||||
if (Corpse* corpse = thisPlayer->GetCorpse())
|
||||
{
|
||||
corpseCheck = true;
|
||||
if (corpse->IsWithinDist(thisPlayer, GetSightRange(obj), false))
|
||||
if (corpse->IsWithinDist(obj, GetSightRange(obj), false))
|
||||
corpseVisibility = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!corpseCheck && !IsWithinDist(obj, GetSightRange(obj), false))
|
||||
return false;
|
||||
}
|
||||
|
||||
// GM visibility off or hidden NPC
|
||||
if (!obj->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM))
|
||||
{
|
||||
// Stop checking other things for GMs
|
||||
if (m_serverSideVisibilityDetect.GetValue(SERVERSIDE_VISIBILITY_GM))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return m_serverSideVisibilityDetect.GetValue(SERVERSIDE_VISIBILITY_GM) >= obj->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM);
|
||||
|
||||
// Ghost players, Spirit Healers, and some other NPCs
|
||||
if (!corpseVisibility && !(obj->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GHOST) & m_serverSideVisibilityDetect.GetValue(SERVERSIDE_VISIBILITY_GHOST)))
|
||||
{
|
||||
// Alive players can see dead players in some cases, but other objects can't do that
|
||||
if (const Player* thisPlayer = ToPlayer())
|
||||
{
|
||||
if (const Player* objPlayer = obj->ToPlayer())
|
||||
{
|
||||
if (thisPlayer->GetTeam() != objPlayer->GetTeam() || !thisPlayer->IsGroupVisibleFor(objPlayer))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!obj->isVisibleForInState(this))
|
||||
return false;
|
||||
|
||||
if (!canDetect(obj, ignoreStealth))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldObject::canDetect(WorldObject const* obj, bool ignoreStealth) const
|
||||
{
|
||||
const WorldObject* seer = this;
|
||||
|
||||
// Pets don't have detection, they use the detection of their masters
|
||||
if (const Unit* thisUnit = ToUnit())
|
||||
if (Unit* controller = thisUnit->GetCharmerOrOwner())
|
||||
seer = controller;
|
||||
|
||||
if (obj->isAlwaysDetectableFor(seer))
|
||||
return true;
|
||||
|
||||
if (!seer->canDetectInvisibilityOf(obj))
|
||||
return false;
|
||||
|
||||
if (!ignoreStealth && !seer->canDetectStealthOf(obj))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldObject::canDetectInvisibilityOf(WorldObject const* obj) const
|
||||
{
|
||||
uint32 mask = obj->m_invisibility.GetFlags() & m_invisibilityDetect.GetFlags();
|
||||
|
||||
// Check for not detected types
|
||||
if (mask != obj->m_invisibility.GetFlags())
|
||||
return false;
|
||||
|
||||
// It isn't possible in invisibility to detect something that can't detect the invisible object
|
||||
// (it's at least true for spell: 66)
|
||||
// It seems like that only Units are affected by this check (couldn't see arena doors with preparation invisibility)
|
||||
if (obj->ToUnit())
|
||||
if ((m_invisibility.GetFlags() & obj->m_invisibilityDetect.GetFlags()) != m_invisibility.GetFlags())
|
||||
return false;
|
||||
|
||||
for (uint32 i = 0; i < TOTAL_INVISIBILITY_TYPES; ++i)
|
||||
{
|
||||
if (!(mask & (1 << i)))
|
||||
continue;
|
||||
|
||||
int32 objInvisibilityValue = obj->m_invisibility.GetValue(InvisibilityType(i));
|
||||
int32 ownInvisibilityDetectValue = m_invisibilityDetect.GetValue(InvisibilityType(i));
|
||||
|
||||
// Too low value to detect
|
||||
if (ownInvisibilityDetectValue < objInvisibilityValue)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldObject::canDetectStealthOf(WorldObject const* obj) const
|
||||
{
|
||||
// Combat reach is the minimal distance (both in front and behind),
|
||||
// and it is also used in the range calculation.
|
||||
// One stealth point increases the visibility range by 0.3 yard.
|
||||
|
||||
if (!obj->m_stealth.GetFlags())
|
||||
return true;
|
||||
|
||||
float distance = GetExactDist(obj);
|
||||
float combatReach = 0.0f;
|
||||
|
||||
if (isType(TYPEMASK_UNIT))
|
||||
combatReach = ((Unit*)this)->GetCombatReach();
|
||||
|
||||
if (distance < combatReach)
|
||||
return true;
|
||||
|
||||
if (!HasInArc(M_PI, obj))
|
||||
return false;
|
||||
|
||||
for (uint32 i = 0; i < TOTAL_STEALTH_TYPES; ++i)
|
||||
{
|
||||
if (!(obj->m_stealth.GetFlags() & (1 << i)))
|
||||
continue;
|
||||
|
||||
if (isType(TYPEMASK_UNIT))
|
||||
if (((Unit*)this)->HasAuraTypeWithMiscvalue(SPELL_AURA_DETECT_STEALTH, i))
|
||||
return true;
|
||||
|
||||
// Starting points
|
||||
int32 detectionValue = 30;
|
||||
|
||||
// Level difference: 5 point / level, starting from level 1.
|
||||
// There may be spells for this and the starting points too, but
|
||||
// not in the DBCs of the client.
|
||||
detectionValue += int32(getLevelForTarget(obj) - 1) * 5;
|
||||
|
||||
// Apply modifiers
|
||||
detectionValue += m_stealthDetect.GetValue(StealthType(i));
|
||||
detectionValue -= obj->m_stealth.GetValue(StealthType(i));
|
||||
|
||||
// Calculate max distance
|
||||
float visibilityRange = float(detectionValue) * 0.3f + combatReach;
|
||||
|
||||
if (visibilityRange > MAX_PLAYER_STEALTH_DETECT_RANGE)
|
||||
visibilityRange = MAX_PLAYER_STEALTH_DETECT_RANGE;
|
||||
|
||||
if (distance > visibilityRange)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorldObject::SendPlaySound(uint32 Sound, bool OnlySelf)
|
||||
{
|
||||
WorldPacket data(SMSG_PLAY_SOUND, 4);
|
||||
@@ -1754,12 +1990,6 @@ void Unit::BuildHeartBeatMsg(WorldPacket *data) const
|
||||
BuildMovementPacket(data);
|
||||
}
|
||||
|
||||
void WorldObject::SendMessageToSet(WorldPacket *data, bool /*fake*/)
|
||||
{
|
||||
Trinity::MessageDistDeliverer notifier(this, data, GetMap()->GetVisibilityDistance());
|
||||
VisitNearbyWorldObject(GetMap()->GetVisibilityDistance(), notifier);
|
||||
}
|
||||
|
||||
void WorldObject::SendMessageToSetInRange(WorldPacket *data, float dist, bool /*bToSelf*/)
|
||||
{
|
||||
Trinity::MessageDistDeliverer notifier(this, data, dist);
|
||||
@@ -1768,8 +1998,8 @@ void WorldObject::SendMessageToSetInRange(WorldPacket *data, float dist, bool /*
|
||||
|
||||
void WorldObject::SendMessageToSet(WorldPacket *data, Player const* skipped_rcvr)
|
||||
{
|
||||
Trinity::MessageDistDeliverer notifier(this, data, GetMap()->GetVisibilityDistance(), false, skipped_rcvr);
|
||||
VisitNearbyWorldObject(GetMap()->GetVisibilityDistance(), notifier);
|
||||
Trinity::MessageDistDeliverer notifier(this, data, GetVisibilityRange(), false, skipped_rcvr);
|
||||
VisitNearbyWorldObject(GetVisibilityRange(), notifier);
|
||||
}
|
||||
|
||||
void WorldObject::SendObjectDeSpawnAnim(uint64 guid)
|
||||
@@ -2434,9 +2664,9 @@ void WorldObject::DestroyForNearbyPlayers()
|
||||
return;
|
||||
|
||||
std::list<Player*> targets;
|
||||
Trinity::AnyPlayerInObjectRangeCheck check(this, GetMap()->GetVisibilityDistance());
|
||||
Trinity::AnyPlayerInObjectRangeCheck check(this, GetVisibilityRange());
|
||||
Trinity::PlayerListSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(this, targets, check);
|
||||
VisitNearbyWorldObject(GetMap()->GetVisibilityDistance(), searcher);
|
||||
VisitNearbyWorldObject(GetVisibilityRange(), searcher);
|
||||
for (std::list<Player*>::const_iterator iter = targets.begin(); iter != targets.end(); ++iter)
|
||||
{
|
||||
Player *plr = (*iter);
|
||||
@@ -2459,7 +2689,7 @@ void WorldObject::UpdateObjectVisibility(bool /*forced*/)
|
||||
{
|
||||
//updates object's visibility for nearby players
|
||||
Trinity::VisibleChangesNotifier notifier(*this);
|
||||
VisitNearbyWorldObject(GetMap()->GetVisibilityDistance(), notifier);
|
||||
VisitNearbyWorldObject(GetVisibilityRange(), notifier);
|
||||
}
|
||||
|
||||
struct WorldObjectChangeAccumulator
|
||||
@@ -2531,7 +2761,7 @@ void WorldObject::BuildUpdate(UpdateDataMapType& data_map)
|
||||
TypeContainerVisitor<WorldObjectChangeAccumulator, WorldTypeMapContainer > player_notifier(notifier);
|
||||
Map& map = *GetMap();
|
||||
//we must build packets for all visible players
|
||||
cell.Visit(p, player_notifier, map, *this, map.GetVisibilityDistance());
|
||||
cell.Visit(p, player_notifier, map, *this, GetVisibilityRange());
|
||||
|
||||
ClearUpdateMask(false);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
#define CONTACT_DISTANCE 0.5f
|
||||
#define INTERACTION_DISTANCE 5.0f
|
||||
#define ATTACK_DISTANCE 5.0f
|
||||
#define MAX_VISIBILITY_DISTANCE 333.0f // max distance for visible object show, limited in 333 yards
|
||||
#define MAX_VISIBILITY_DISTANCE 500.0f // max distance for visible objects
|
||||
#define SIGHT_RANGE_UNIT 50.0f
|
||||
#define DEFAULT_VISIBILITY_DISTANCE 90.0f // default visible distance, 90 yards on continents
|
||||
#define DEFAULT_VISIBILITY_INSTANCE 120.0f // default visible distance in instances, 120 yards
|
||||
#define DEFAULT_VISIBILITY_BGARENAS 180.0f // default visible distance in BG/Arenas, 180 yards
|
||||
@@ -542,6 +543,30 @@ class GridObject
|
||||
GridReference<T> m_gridRef;
|
||||
};
|
||||
|
||||
template <class T_VALUES, class T_FLAGS, class FLAG_TYPE, uint8 ARRAY_SIZE>
|
||||
class FlaggedValuesArray32
|
||||
{
|
||||
public:
|
||||
FlaggedValuesArray32()
|
||||
{
|
||||
memset(&m_values, 0x00, sizeof(T_VALUES) * ARRAY_SIZE);
|
||||
m_flags = 0;
|
||||
}
|
||||
|
||||
T_FLAGS GetFlags() const { return m_flags; }
|
||||
bool HasFlag(FLAG_TYPE flag) const { return m_flags & (1 << flag); }
|
||||
void AddFlag(FLAG_TYPE flag) { m_flags |= (1 << flag); }
|
||||
void DelFlag(FLAG_TYPE flag) { m_flags &= ~(1 << flag); }
|
||||
|
||||
T_VALUES GetValue(FLAG_TYPE flag) const { return m_values[flag]; }
|
||||
void SetValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] = value; }
|
||||
void AddValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] += value; }
|
||||
|
||||
private:
|
||||
T_VALUES m_values[ARRAY_SIZE];
|
||||
T_FLAGS m_flags;
|
||||
};
|
||||
|
||||
class WorldObject : public Object, public WorldLocation
|
||||
{
|
||||
protected:
|
||||
@@ -681,10 +706,12 @@ class WorldObject : public Object, public WorldLocation
|
||||
|
||||
virtual void CleanupsBeforeDelete(bool finalCleanup = true); // used in destructor or explicitly before mass creature delete to remove cross-references to already deleted units
|
||||
|
||||
virtual void SendMessageToSet(WorldPacket *data, bool self);
|
||||
virtual void SendMessageToSet(WorldPacket *data, bool self) { SendMessageToSetInRange(data, GetVisibilityRange(), self); }
|
||||
virtual void SendMessageToSetInRange(WorldPacket *data, float dist, bool self);
|
||||
virtual void SendMessageToSet(WorldPacket *data, Player const* skipped_rcvr);
|
||||
|
||||
virtual uint8 getLevelForTarget(WorldObject const* /*target*/) const { return 1; }
|
||||
|
||||
void MonsterSay(const char* text, uint32 language, uint64 TargetGuid);
|
||||
void MonsterYell(const char* text, uint32 language, uint64 TargetGuid);
|
||||
void MonsterTextEmote(const char* text, uint64 TargetGuid, bool IsBossEmote = false);
|
||||
@@ -704,11 +731,32 @@ class WorldObject : public Object, public WorldLocation
|
||||
virtual void SaveRespawnTime() {}
|
||||
void AddObjectToRemoveList();
|
||||
|
||||
// main visibility check function in normal case (ignore grey zone distance check)
|
||||
bool isVisibleFor(Player const* u) const { return isVisibleForInState(u,false); }
|
||||
virtual bool isValid() const;
|
||||
|
||||
// low level function for visibility change code, must be define in all main world object subclasses
|
||||
virtual bool isVisibleForInState(Player const* u, bool inVisibleList) const = 0;
|
||||
virtual bool isAlwaysVisibleFor(WorldObject const* seer) const { return false; }
|
||||
virtual bool canSeeAlways(WorldObject const* obj) const { return false; }
|
||||
bool canDetect(WorldObject const* obj, bool ignoreStealth) const;
|
||||
|
||||
virtual bool isVisibleForInState(WorldObject const* seer) const { return true; }
|
||||
|
||||
bool canDetectInvisibilityOf(WorldObject const* obj) const;
|
||||
bool canDetectStealthOf(WorldObject const* obj) const;
|
||||
|
||||
virtual bool isAlwaysDetectableFor(WorldObject const* seer) const { return false; }
|
||||
|
||||
float GetGridActivationRange() const;
|
||||
float GetVisibilityRange() const;
|
||||
float GetSightRange(const WorldObject* target = NULL) const;
|
||||
bool canSeeOrDetect(WorldObject const* obj, bool ignoreStealth = false, bool distanceCheck = false) const;
|
||||
|
||||
FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealth;
|
||||
FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealthDetect;
|
||||
|
||||
FlaggedValuesArray32<int32, uint32, InvisibilityType, TOTAL_INVISIBILITY_TYPES> m_invisibility;
|
||||
FlaggedValuesArray32<int32, uint32, InvisibilityType, TOTAL_INVISIBILITY_TYPES> m_invisibilityDetect;
|
||||
|
||||
FlaggedValuesArray32<int32, uint32, ServerSideVisibilityType, TOTAL_SERVERSIDE_VISIBILITY_TYPES> m_serverSideVisibility;
|
||||
FlaggedValuesArray32<int32, uint32, ServerSideVisibilityType, TOTAL_SERVERSIDE_VISIBILITY_TYPES> m_serverSideVisibilityDetect;
|
||||
|
||||
// Low Level Packets
|
||||
void SendPlaySound(uint32 Sound, bool OnlySelf);
|
||||
|
||||
Reference in New Issue
Block a user