- 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:
linencloth
2010-11-13 17:18:09 +01:00
parent 995408f0a9
commit bf888285aa
36 changed files with 780 additions and 791 deletions
+243 -13
View File
@@ -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);
}
+54 -6
View File
@@ -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);