mirror of
https://github.com/araxiaonline/TrinityCore2.git
synced 2026-06-21 07:11:59 -04:00
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user