- 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);
}