diff options
| author | linencloth <none@none> | 2010-11-13 17:18:09 +0100 |
|---|---|---|
| committer | linencloth <none@none> | 2010-11-13 17:18:09 +0100 |
| commit | bf888285aab32ae2571002f23dd217396b2f12d8 (patch) | |
| tree | e13695f4909b7df4f218057126919321b4374f58 /src/server/game/Entities | |
| parent | 995408f0a9e6512af53e7719799d332d487f84eb (diff) | |
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
Diffstat (limited to 'src/server/game/Entities')
| -rwxr-xr-x | src/server/game/Entities/Corpse/Corpse.cpp | 5 | ||||
| -rwxr-xr-x | src/server/game/Entities/Corpse/Corpse.h | 2 | ||||
| -rwxr-xr-x | src/server/game/Entities/Creature/Creature.cpp | 94 | ||||
| -rwxr-xr-x | src/server/game/Entities/Creature/Creature.h | 7 | ||||
| -rwxr-xr-x | src/server/game/Entities/DynamicObject/DynamicObject.cpp | 6 | ||||
| -rwxr-xr-x | src/server/game/Entities/DynamicObject/DynamicObject.h | 1 | ||||
| -rwxr-xr-x | src/server/game/Entities/GameObject/GameObject.cpp | 67 | ||||
| -rwxr-xr-x | src/server/game/Entities/GameObject/GameObject.h | 14 | ||||
| -rwxr-xr-x | src/server/game/Entities/Object/Object.cpp | 256 | ||||
| -rwxr-xr-x | src/server/game/Entities/Object/Object.h | 60 | ||||
| -rwxr-xr-x | src/server/game/Entities/Pet/Pet.cpp | 2 | ||||
| -rwxr-xr-x | src/server/game/Entities/Player/Player.cpp | 292 | ||||
| -rwxr-xr-x | src/server/game/Entities/Player/Player.h | 17 | ||||
| -rwxr-xr-x | src/server/game/Entities/Unit/Unit.cpp | 130 | ||||
| -rwxr-xr-x | src/server/game/Entities/Unit/Unit.h | 37 |
15 files changed, 457 insertions, 533 deletions
diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index e773bb2d1b3..29a2b83e32d 100755 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -210,11 +210,6 @@ bool Corpse::LoadFromDB(uint32 guid, Field *fields) return true; } -bool Corpse::isVisibleForInState(Player const* u, bool inVisibleList) const -{ - return IsInWorld() && u->IsInWorld() && IsWithinDistInMap(u->m_seer, World::GetMaxVisibleDistanceForObject() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false); -} - bool Corpse::IsExpired(time_t t) const { if (m_type == CORPSE_BONES) diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h index 0ea164a90a4..98253fb49ef 100755 --- a/src/server/game/Entities/Corpse/Corpse.h +++ b/src/server/game/Entities/Corpse/Corpse.h @@ -73,8 +73,6 @@ class Corpse : public WorldObject, public GridObject<Corpse> GridPair const& GetGrid() const { return m_grid; } void SetGrid(GridPair const& grid) { m_grid = grid; } - bool isVisibleForInState(Player const* u, bool inVisibleList) const; - Loot loot; // remove insignia ONLY at BG Player* lootRecipient; bool lootForBody; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index dce2c968a84..6631de8bdd4 100755 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -802,6 +802,18 @@ bool Creature::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, LastUsedScriptID = GetCreatureInfo()->ScriptID; } + // TODO: Replace with spell, handle from DB + if (isSpiritHealer()) + { + m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_GHOST); + m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_GHOST); + } + else if(isSpiritGuide()) + m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_GHOST | GHOST_VISIBILITY_ALIVE); + + if (Entry == VISUAL_WAYPOINT) + SetVisibility(VISIBILITY_OFF); + return bResult; } @@ -1385,50 +1397,15 @@ void Creature::DeleteFromDB() WorldDatabase.CommitTransaction(trans); } -bool Creature::canSeeOrDetect(Unit const* u, bool detect, bool /*inVisibleList*/, bool /*is3dDistance*/) const +bool Creature::isVisibleForInState(WorldObject const* seer) const { - // not in world - if (!IsInWorld() || !u->IsInWorld()) - return false; - - // all dead creatures/players not visible for any creatures - if (!u->isAlive() || !isAlive()) - return false; - - // Always can see self - if (u == this) - return true; - - // phased visibility (both must phased in same way) - if (!InSamePhase(u)) + if (!Unit::isVisibleForInState(seer)) return false; - // always seen by owner - if (GetGUID() == u->GetCharmerOrOwnerGUID()) + if (isAlive() || (m_isDeadByDefault && m_deathState == CORPSE) || m_corpseRemoveTime > time(NULL)) return true; - if (u->GetVisibility() == VISIBILITY_OFF) //GM - return false; - - // invisible aura - if ((m_invisibilityMask || u->m_invisibilityMask) && !canDetectInvisibilityOf(u)) - return false; - - // unit got in stealth in this moment and must ignore old detected state - //if (m_Visibility == VISIBILITY_GROUP_NO_DETECT) - // return false; - - // GM invisibility checks early, invisibility if any detectable, so if not stealth then visible - if (u->GetVisibility() == VISIBILITY_GROUP_STEALTH) - { - //do not know what is the use of this detect - if (!detect || !canDetectStealthOf(u, GetDistance(u))) - return false; - } - - // Now check is target visible with LoS - //return u->IsWithinLOS(GetPositionX(),GetPositionY(),GetPositionZ()); - return true; + return false; } bool Creature::canStartAttack(Unit const* who, bool force) const @@ -1784,43 +1761,6 @@ SpellEntry const *Creature::reachWithSpellCure(Unit *pVictim) return NULL; } -bool Creature::IsVisibleInGridForPlayer(Player const* pl) const -{ - // gamemaster in GM mode see all, including ghosts - if (pl->isGameMaster()) - return true; - - // Trigger shouldn't be visible for players - //if (isTrigger()) - // return false; - // Rat: this makes no sense, triggers are always sent to players, but with invisible model and can not be attacked or targeted! - - // Live player (or with not release body see live creatures or death creatures with corpse disappearing time > 0 - if (pl->isAlive() || pl->GetDeathTimer() > 0) - { - if (GetEntry() == VISUAL_WAYPOINT) - return false; - return (isAlive() || m_corpseRemoveTime > time(NULL) || (m_isDeadByDefault && m_deathState == CORPSE)); - } - - // Dead player see creatures near own corpse - Corpse *corpse = pl->GetCorpse(); - if (corpse) - { - // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level - if (corpse->IsWithinDistInMap(this,(20+25)*sWorld.getRate(RATE_CREATURE_AGGRO))) - return true; - } - - // Dead player see Spirit Healer or Spirit Guide - if (isSpiritService()) - return true; - - // and not see any other - return false; -} - - // select nearest hostile unit within the given distance (regardless of threat list). Unit* Creature::SelectNearestTarget(float dist) const { @@ -1832,7 +1772,7 @@ Unit* Creature::SelectNearestTarget(float dist) const Unit *target = NULL; { - if (dist == 0.0f || dist > MAX_VISIBILITY_DISTANCE) + if (dist == 0.0f) dist = MAX_VISIBILITY_DISTANCE; Trinity::NearestHostileUnitCheck u_check(this, dist); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index fcfa9edf3e0..28f6d46d998 100755 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -437,7 +437,7 @@ class Creature : public Unit, public GridObject<Creature> bool HasReactState(ReactStates state) const { return (m_reactState == state); } void InitializeReactState() { - if (isTotem() || isTrigger() || GetCreatureType() == CREATURE_TYPE_CRITTER) + if (isTotem() || isTrigger() || GetCreatureType() == CREATURE_TYPE_CRITTER || isSpiritService()) SetReactState(REACT_PASSIVE); else SetReactState(REACT_AGGRESSIVE); @@ -568,7 +568,6 @@ class Creature : public Unit, public GridObject<Creature> CreatureSpellCooldowns m_CreatureCategoryCooldowns; uint32 m_GlobalCooldown; - bool canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const; bool canStartAttack(Unit const* u, bool force) const; float GetAttackDistance(Unit const* pl) const; @@ -593,8 +592,6 @@ class Creature : public Unit, public GridObject<Creature> Cell const& GetCurrentCell() const { return m_currentCell; } void SetCurrentCell(Cell const& cell) { m_currentCell = cell; } - bool IsVisibleInGridForPlayer(Player const* pl) const; - void RemoveCorpse(bool setSpawnTime = true); bool isDeadByDefault() const { return m_isDeadByDefault; }; @@ -726,6 +723,8 @@ class Creature : public Unit, public GridObject<Creature> uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable uint32 guid_transport; + + bool isVisibleForInState(WorldObject const* seer) const; private: //WaypointMovementGenerator vars uint32 m_waypointID; diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp index bc0db5b8cc2..4f10b410b7a 100755 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -182,9 +182,3 @@ void DynamicObject::Delay(int32 delaytime) { SetDuration(GetDuration() - delaytime); } - -bool DynamicObject::isVisibleForInState(Player const* u, bool inVisibleList) const -{ - return IsInWorld() && u->IsInWorld() - && (IsWithinDistInMap(u->m_seer,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false)); -} diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.h b/src/server/game/Entities/DynamicObject/DynamicObject.h index 80bba2772fe..5b662f4c6de 100755 --- a/src/server/game/Entities/DynamicObject/DynamicObject.h +++ b/src/server/game/Entities/DynamicObject/DynamicObject.h @@ -44,7 +44,6 @@ class DynamicObject : public WorldObject, public GridObject<DynamicObject> uint64 GetCasterGUID() const { return GetUInt64Value(DYNAMICOBJECT_CASTER); } float GetRadius() const { return GetFloatValue(DYNAMICOBJECT_RADIUS); } Unit* GetCaster() const; - bool isVisibleForInState(Player const* u, bool inVisibleList) const; void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); } void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 33dd0422ce8..bb79c2e0ea4 100755 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -227,6 +227,20 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMa case GAMEOBJECT_TYPE_FISHINGNODE: SetGoAnimProgress(0); break; + case GAMEOBJECT_TYPE_TRAP: + if (GetGOInfo()->trap.stealthed) + { + m_stealth.AddFlag( STEALTH_TRAP); + m_stealth.AddValue(STEALTH_TRAP, 300); + } + + if (GetGOInfo()->trap.invisible) + { + m_invisibility.AddFlag( INVISIBILITY_TRAP); + m_invisibility.AddValue(INVISIBILITY_TRAP, 70); + } + + break; default: SetGoAnimProgress(animprogress); break; @@ -810,56 +824,27 @@ void GameObject::SaveRespawnTime() sObjectMgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime); } -bool GameObject::isVisibleForInState(Player const* u, bool inVisibleList) const +bool GameObject::isAlwaysVisibleFor(WorldObject const* seer) const { - // Not in world - if (!IsInWorld() || !u->IsInWorld()) - return false; - - // Transport always visible at this step implementation - if (IsTransport() && IsInMap(u)) + if (WorldObject::isAlwaysVisibleFor(seer)) return true; - // quick check visibility false cases for non-GM-mode - if (!u->isGameMaster()) - { - // despawned and then not visible for non-GM in GM-mode - if (!isSpawned()) - return false; - - // special invisibility cases - if (GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed) - { - Unit *owner = GetOwner(); - if (owner && u->IsHostileTo(owner) && !canDetectTrap(u, GetDistance(u))) - return false; - } - } - - // check distance - return IsWithinDistInMap(u->m_seer,World::GetMaxVisibleDistanceForObject() + - (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false); + if (IsTransport()) + return true; + + return false; } -bool GameObject::canDetectTrap(Player const* u, float distance) const +bool GameObject::isVisibleForInState(WorldObject const* seer) const { - if (u->hasUnitState(UNIT_STAT_STUNNED)) + if (!WorldObject::isVisibleForInState(seer)) return false; - if (distance < GetGOInfo()->size) //collision - return true; - if (!u->HasInArc(M_PI, this)) //behind - return false; - if (u->HasAuraType(SPELL_AURA_DETECT_STEALTH)) - return true; - //Visible distance is modified by -Level Diff (every level diff = 0.25f in visible distance) - float visibleDistance = (int32(u->getLevel()) - int32(GetOwner()->getLevel()))* 0.25f; - //GetModifier for trap (miscvalue 1) - //35y for aura 2836 - //WARNING: these values are guessed, may be not blizzlike - visibleDistance += u->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DETECT, 1)* 0.5f; + // Despawned + if (!isSpawned()) + return false; - return distance < visibleDistance; + return true; } void GameObject::Respawn() diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 4ed40c7d6e9..e8c37cb34cd 100755 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -136,7 +136,7 @@ struct GameObjectInfo uint32 serverOnly; //8 uint32 stealthed; //9 uint32 large; //10 - uint32 stealthAffected; //11 + uint32 invisible; //11 uint32 openTextID; //12 can be used to replace castBarCaption? uint32 closeTextID; //13 uint32 ignoreTotems; //14 @@ -733,8 +733,16 @@ class GameObject : public WorldObject, public GridObject<GameObject> void TriggeringLinkedGameObject(uint32 trapEntry, Unit* target); - bool isVisibleForInState(Player const* u, bool inVisibleList) const; - bool canDetectTrap(Player const* u, float distance) const; + bool isAlwaysVisibleFor(WorldObject const* seer) const; + bool isVisibleForInState(WorldObject const* seer) const; + + uint8 getLevelForTarget(WorldObject const* target) const + { + if (Unit* owner = GetOwner()) + return owner->getLevelForTarget(target); + + return 1; + } GameObject* LookupFishingHoleAround(float range); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 868443031ae..e7edea0b07f 100755 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -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); } diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index b05b98f67b7..ff68bd3e9db 100755 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -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; + + 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; - // 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; + 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); diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index df482c40143..bf570ce0693 100755 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -506,7 +506,7 @@ void Pet::Update(uint32 diff) { // unsummon pet that lost owner Player* owner = GetOwner(); - if (!owner || (!IsWithinDistInMap(owner, GetMap()->GetVisibilityDistance()) && !isPossessed()) || (isControlled() && !owner->GetPetGUID())) + if (!owner || (!IsWithinDistInMap(owner, GetMap()->GetVisibilityRange()) && !isPossessed()) || (isControlled() && !owner->GetPetGUID())) //if (!owner || (!IsWithinDistInMap(owner, GetMap()->GetVisibilityDistance()) && (owner->GetCharmGUID() && (owner->GetCharmGUID() != GetGUID()))) || (isControlled() && !owner->GetPetGUID())) { Remove(PET_SAVE_NOT_IN_SLOT, true); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 07e56dafe89..02d6c4cebb0 100755 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -485,8 +485,6 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa m_swingErrorMsg = 0; - m_DetectInvTimer = 1*IN_MILLISECONDS; - for (uint8 j = 0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j) { m_bgBattlegroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE; @@ -1216,16 +1214,19 @@ void Player::SetDrunkValue(uint16 newDrunkenValue, uint32 itemId) { uint32 oldDrunkenState = Player::GetDrunkenstateByValue(m_drunk); + if (!newDrunkenValue && !HasAuraType(SPELL_AURA_MOD_FAKE_INEBRIATE)) + m_invisibilityDetect.AddFlag(INVISIBILITY_DRUNK); + else + m_invisibilityDetect.DelFlag(INVISIBILITY_DRUNK); + + m_invisibilityDetect.AddValue(INVISIBILITY_DRUNK, int32(newDrunkenValue - m_drunk) / 256); + m_drunk = newDrunkenValue; SetUInt32Value(PLAYER_BYTES_3,(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFF0001) | (m_drunk & 0xFFFE)); uint32 newDrunkenState = Player::GetDrunkenstateByValue(m_drunk); - // special drunk invisibility detection - if (newDrunkenState >= DRUNKEN_DRUNK) - m_detectInvisibilityMask |= (1<<6); - else - m_detectInvisibilityMask &= ~(1<<6); + UpdateObjectVisibility(); if (newDrunkenState == oldDrunkenState) return; @@ -1477,18 +1478,6 @@ void Player::Update(uint32 p_time) //Handle Water/drowning HandleDrowning(p_time); - //Handle detect stealth players - if (m_DetectInvTimer > 0) - { - if (p_time >= m_DetectInvTimer) - { - HandleStealthedUnitsDetection(); - m_DetectInvTimer = 3000; - } - else - m_DetectInvTimer -= p_time; - } - // Played time if (now > m_Last_tick) { @@ -1526,7 +1515,7 @@ void Player::Update(uint32 p_time) SendUpdateToOutOfRangeGroupMembers(); Pet* pet = GetPet(); - if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && !pet->isPossessed()) + if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityRange()) && !pet->isPossessed()) //if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID()))) RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true); @@ -1907,7 +1896,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if (!(options & TELE_TO_NOT_UNSUMMON_PET)) { //same map, only remove pet if out of range for new position - if (pet && !pet->IsWithinDist3d(x,y,z, GetMap()->GetVisibilityDistance())) + if (pet && !pet->IsWithinDist3d(x,y,z, GetMap()->GetVisibilityRange())) UnsummonPetTemporaryIfAny(); } @@ -2508,6 +2497,7 @@ void Player::SetGameMaster(bool on) CombatStopWithPets(); SetPhaseMask(PHASEMASK_ANYWHERE,false); // see and visible in all phases + m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity()); } else { @@ -2537,6 +2527,7 @@ void Player::SetGameMaster(bool on) UpdateArea(m_areaUpdateId); getHostileRefManager().setOnlineOfflineState(true); + m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); } UpdateObjectVisibility(); @@ -2548,13 +2539,7 @@ void Player::SetGMVisible(bool on) { m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; //remove flag - // Reapply stealth/invisibility if active or show if not any - if (HasAuraType(SPELL_AURA_MOD_STEALTH)) - SetVisibility(VISIBILITY_GROUP_STEALTH); - //else if (HasAuraType(SPELL_AURA_MOD_INVISIBILITY)) - // SetVisibility(VISIBILITY_GROUP_INVISIBILITY); - else - SetVisibility(VISIBILITY_ON); + m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); } else { @@ -2563,11 +2548,11 @@ void Player::SetGMVisible(bool on) SetAcceptWhispers(false); SetGameMaster(true); - SetVisibility(VISIBILITY_OFF); + m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity()); } } -bool Player::IsGroupVisibleFor(Player* p) const +bool Player::IsGroupVisibleFor(Player const* p) const { switch(sWorld.getIntConfig(CONFIG_GROUP_VISIBILITY)) { @@ -6423,17 +6408,6 @@ void Player::SaveRecallPosition() m_recallO = GetOrientation(); } -void Player::SendMessageToSet(WorldPacket *data, bool self) -{ - if (self) - GetSession()->SendPacket(data); - - // we use World::GetMaxVisibleDistance() because i cannot see why not use a distance - // update: replaced by GetMap()->GetVisibilityDistance() - Trinity::MessageDistDeliverer notifier(this, data, GetMap()->GetVisibilityDistance()); - VisitNearbyWorldObject(GetMap()->GetVisibilityDistance(), notifier); -} - void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self) { if (self) @@ -6459,8 +6433,8 @@ void Player::SendMessageToSet(WorldPacket *data, Player const* skipped_rcvr) // we use World::GetMaxVisibleDistance() because i cannot see why not use a distance // update: replaced by GetMap()->GetVisibilityDistance() - 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 Player::SendDirectMessage(WorldPacket *data) @@ -19373,49 +19347,6 @@ void Player::SetRestBonus (float rest_bonus_new) SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus)); } -void Player::HandleStealthedUnitsDetection() -{ - std::list<Unit*> stealthedUnits; - Trinity::AnyStealthedCheck u_check; - Trinity::UnitListSearcher<Trinity::AnyStealthedCheck > searcher(this, stealthedUnits, u_check); - VisitNearbyObject(GetMap()->GetVisibilityDistance(), searcher); - - for (std::list<Unit*>::const_iterator i = stealthedUnits.begin(); i != stealthedUnits.end(); ++i) - { - if ((*i) == this) - continue; - - bool hasAtClient = HaveAtClient((*i)); - bool hasDetected = canSeeOrDetect(*i, true); - - if (hasDetected) - { - if (!hasAtClient) - { - (*i)->SendUpdateToPlayer(this); - m_clientGUIDs.insert((*i)->GetGUID()); - - #ifdef TRINITY_DEBUG - if ((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES) == 0) - sLog.outDebug("Object %u (Type: %u) is detected in stealth by player %u. Distance = %f",(*i)->GetGUIDLow(),(*i)->GetTypeId(),GetGUIDLow(),GetDistance(*i)); - #endif - - // target aura duration for caster show only if target exist at caster client - // send data at target visibility change (adding to client) - SendInitialVisiblePackets(*i); - } - } - else - { - if (hasAtClient) - { - (*i)->DestroyForPlayer(this); - m_clientGUIDs.erase((*i)->GetGUID()); - } - } - } -} - bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc /*= NULL*/, uint32 spellid /*= 0*/) { if (nodes.size() < 2) @@ -20540,177 +20471,44 @@ WorldLocation Player::GetStartPosition() const return WorldLocation(mapId, info->positionX, info->positionY, info->positionZ, 0); } -bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool is3dDistance) const +bool Player::isValid() const { - // Always can see self - if (m_mover == u || this == u) - return true; - - // Arena visibility before arena start - if (InArena() && GetBattleground()->GetStatus() == STATUS_WAIT_JOIN) - if (const Player* target = u->GetCharmerOrOwnerPlayerOrPlayerItself()) - return GetBGTeam() == target->GetBGTeam() && target->isGMVisible(); - - // phased visibility (both must phased in same way) - if (!InSamePhase(u)) + if (!Unit::isValid()) return false; - // player visible for other player if not logout and at same transport - // including case when player is out of world - bool at_same_transport = - GetTransport() && u->GetTypeId() == TYPEID_PLAYER - && !GetSession()->PlayerLogout() && !u->ToPlayer()->GetSession()->PlayerLogout() - && !GetSession()->PlayerLoading() && !u->ToPlayer()->GetSession()->PlayerLoading() - && GetTransport() == u->ToPlayer()->GetTransport(); - - // not in world - if (!at_same_transport && (!IsInWorld() || !u->IsInWorld())) - return false; - - // forbidden to seen (at GM respawn command) - //if (u->GetVisibility() == VISIBILITY_RESPAWN) - // return false; - - Map& _map = *u->GetMap(); - // Grid dead/alive checks - // non visible at grid for any stealth state - if (!u->IsVisibleInGridForPlayer(this)) + if (GetSession()->PlayerLogout() || GetSession()->PlayerLoading()) return false; - // always seen by owner - if (uint64 guid = u->GetCharmerOrOwnerGUID()) - if (GetGUID() == guid) - return true; - - if (uint64 guid = GetUInt64Value(PLAYER_FARSIGHT)) - if (u->GetGUID() == guid) - return true; - - // different visible distance checks - if (isInFlight()) // what see player in flight - { - if (!m_seer->IsWithinDistInMap(u, _map.GetVisibilityDistance() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), is3dDistance)) - return false; - } - else if (!u->isAlive()) // distance for show body - { - if (!m_seer->IsWithinDistInMap(u, _map.GetVisibilityDistance() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), is3dDistance)) - return false; - } - else if (u->GetTypeId() == TYPEID_PLAYER) // distance for show player - { - // Players far than max visible distance for player or not in our map are not visible too - if (!at_same_transport && !m_seer->IsWithinDistInMap(u, _map.GetVisibilityDistance() + (inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f), is3dDistance)) - return false; - } - else if (u->GetCharmerOrOwnerGUID()) // distance for show pet/charmed - { - // Pet/charmed far than max visible distance for player or not in our map are not visible too - if (!m_seer->IsWithinDistInMap(u, _map.GetVisibilityDistance() + (inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f), is3dDistance)) - return false; - } - else // distance for show creature - { - // Units farther than max visible distance for creature or not in our map are not visible too - if (!m_seer->IsWithinDistInMap(u - , u->isActiveObject() ? (MAX_VISIBILITY_DISTANCE - (inVisibleList ? 0.0f : World::GetVisibleUnitGreyDistance())) - : (_map.GetVisibilityDistance() + (inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)) - , is3dDistance)) - return false; - } - - if (Unit* owner = u->GetCharmerOrOwnerOrSelf()) - { - if (owner->GetVisibility() == VISIBILITY_OFF) - { - // GMs see any players, not higher GMs and all units - if (isGameMaster()) - { - if (owner->GetTypeId() == TYPEID_PLAYER) - return owner->ToPlayer()->GetSession()->GetSecurity() <= GetSession()->GetSecurity(); - else - return true; - } - return false; - } - } - - // GM's can see everyone with invisibilitymask with less or equal security level - if (m_mover->m_invisibilityMask || u->m_invisibilityMask) - { - if (isGameMaster()) - { - if (u->GetTypeId() == TYPEID_PLAYER) - return u->ToPlayer()->GetSession()->GetSecurity() <= GetSession()->GetSecurity(); - else - return true; - } - - // player see other player with stealth/invisibility only if he in same group or raid or same team (raid/team case dependent from conf setting) - if (!m_mover->canDetectInvisibilityOf(u)) - if (!(u->GetTypeId() == TYPEID_PLAYER && !IsHostileTo(u) && IsGroupVisibleFor(const_cast<Player*>(u->ToPlayer())))) - return false; - } - - // GM invisibility checks early, invisibility if any detectable, so if not stealth then visible - if (u->GetVisibility() == VISIBILITY_GROUP_STEALTH && !isGameMaster()) - { - // if player is dead then he can't detect anyone in any cases - //do not know what is the use of this detect - // stealth and detected and visible for some seconds - if (!isAlive()) - detect = false; - if (m_DetectInvTimer < 300 || !HaveAtClient(u)) - if (!(u->GetTypeId() == TYPEID_PLAYER && !IsHostileTo(u) && IsGroupVisibleFor(const_cast<Player*>(u->ToPlayer())))) - if (!detect || !m_mover->canDetectStealthOf(u, GetDistance(u))) - return false; - } - - // If use this server will be too laggy - // Now check is target visible with LoS - //return u->IsWithinLOS(GetPositionX(),GetPositionY(),GetPositionZ()); return true; } -bool Player::IsVisibleInGridForPlayer(Player const * pl) const +bool Player::canSeeAlways(WorldObject const* obj) const { - // gamemaster in GM mode see all, including ghosts - if (pl->isGameMaster() && GetSession()->GetSecurity() <= pl->GetSession()->GetSecurity()) + if (Unit::canSeeAlways(obj)) return true; - // It seems in battleground everyone sees everyone, except the enemy-faction ghosts - if (InBattleground()) - { - if (!(isAlive() || m_deathTimer > 0) && !IsFriendlyTo(pl)) - return false; + // Always can see self + if (m_mover == obj) return true; - } - // Live player see live player or dead player with not realized corpse - if (pl->isAlive() || pl->m_deathTimer > 0) - { - return isAlive() || m_deathTimer > 0; - } + if (uint64 guid = GetUInt64Value(PLAYER_FARSIGHT)) + if (obj->GetGUID() == guid) + return true; + + return false; +} - // Ghost see other friendly ghosts, that's for sure - if (!(isAlive() || m_deathTimer > 0) && IsFriendlyTo(pl)) +bool Player::isAlwaysDetectableFor(WorldObject const* seer) const +{ + if (Unit::isAlwaysDetectableFor(seer)) return true; - // Dead player see live players near own corpse - if (isAlive()) - { - Corpse *corpse = pl->GetCorpse(); - if (corpse) - { - // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level - if (corpse->IsWithinDistInMap(this,(20+25)*sWorld.getRate(RATE_CREATURE_AGGRO))) - return true; - } - } + if (const Player* seerPlayer = seer->ToPlayer()) + if (IsGroupVisibleFor(seerPlayer)) + return true; - // and not see any other - return false; -} + return false; + } bool Player::IsVisibleGloballyFor(Player* u) const { @@ -20780,7 +20578,7 @@ void Player::UpdateVisibilityOf(WorldObject* target) { if (HaveAtClient(target)) { - if (!target->isVisibleForInState(this, true)) + if (!canSeeOrDetect(target, false, true)) { if (target->GetTypeId() == TYPEID_UNIT) BeforeVisibilityDestroy<Creature>(target->ToCreature(),this); @@ -20796,7 +20594,7 @@ void Player::UpdateVisibilityOf(WorldObject* target) } else { - if (target->isVisibleForInState(this,false)) + if (canSeeOrDetect(target, false, true)) { //if (target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle) // UpdateVisibilityOf(((Unit*)target)->m_Vehicle); @@ -20856,7 +20654,7 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<Unit*>& vi { if (HaveAtClient(target)) { - if (!target->isVisibleForInState(this,true)) + if (!canSeeOrDetect(target, false, true)) { BeforeVisibilityDestroy<T>(target,this); @@ -20871,7 +20669,7 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<Unit*>& vi } else //if (visibleNow.size() < 30 || target->GetTypeId() == TYPEID_UNIT && target->ToCreature()->IsVehicle()) { - if (target->isVisibleForInState(this,false)) + if (canSeeOrDetect(target, false, true)) { //if (target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle) // UpdateVisibilityOf(((Unit*)target)->m_Vehicle, data, visibleNow); @@ -20902,7 +20700,7 @@ void Player::UpdateObjectVisibility(bool forced) Unit::UpdateObjectVisibility(true); // updates visibility of all objects around point of view for current player Trinity::VisibleNotifier notifier(*this); - m_seer->VisitNearbyObject(GetMap()->GetVisibilityDistance(), notifier); + m_seer->VisitNearbyObject(GetVisibilityRange(), notifier); notifier.SendToSelf(); // send gathered data } } @@ -20910,7 +20708,7 @@ void Player::UpdateObjectVisibility(bool forced) void Player::UpdateVisibilityForPlayer() { Trinity::VisibleNotifier notifier(*this); - m_seer->VisitNearbyObject(GetMap()->GetVisibilityDistance(), notifier); + m_seer->VisitNearbyObject(GetVisibilityRange(), notifier); notifier.SendToSelf(); // send gathered data } @@ -21052,6 +20850,8 @@ void Player::SetGroup(Group *group, int8 subgroup) m_group.link(group, this); m_group.setSubGroup((uint8)subgroup); } + + UpdateObjectVisibility(); } void Player::SendInitialPacketsBeforeAddToMap() diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index efed19af453..7e151769810 100755 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1706,7 +1706,7 @@ class Player : public Unit, public GridObject<Player> void DuelComplete(DuelCompleteType type); void SendDuelCountdown(uint32 counter); - bool IsGroupVisibleFor(Player* p) const; + bool IsGroupVisibleFor(Player const* p) const; bool IsInSameGroupWith(Player const* p) const; bool IsInSameRaidWith(Player const* p) const { return p == this || (GetGroup() != NULL && GetGroup() == p->GetGroup()); } void UninviteFromGroup(); @@ -1842,7 +1842,7 @@ class Player : public Unit, public GridObject<Player> bool SetPosition(const Position &pos, bool teleport = false) { return SetPosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); } void UpdateUnderwaterState(Map * m, float x, float y, float z); - void SendMessageToSet(WorldPacket *data, bool self);// overwrite Object::SendMessageToSet + void SendMessageToSet(WorldPacket *data, bool self) {SendMessageToSetInRange(data, GetVisibilityRange(), self); };// overwrite Object::SendMessageToSet void SendMessageToSetInRange(WorldPacket *data, float fist, bool self);// overwrite Object::SendMessageToSetInRange void SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only); void SendMessageToSet(WorldPacket *data, Player const* skipped_rcvr); @@ -2232,8 +2232,8 @@ class Player : public Unit, public GridObject<Player> bool HaveAtClient(WorldObject const* u) const { return u == this || m_clientGUIDs.find(u->GetGUID()) != m_clientGUIDs.end(); } - bool canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const; - bool IsVisibleInGridForPlayer(Player const* pl) const; + bool isValid() const; + bool IsVisibleGloballyFor(Player* pl) const; void SendInitialVisiblePackets(Unit* target); @@ -2245,9 +2245,6 @@ class Player : public Unit, public GridObject<Player> template<class T> void UpdateVisibilityOf(T* target, UpdateData& data, std::set<Unit*>& visibleNow); - // Stealth detection system - void HandleStealthedUnitsDetection(); - uint8 m_forced_speed_changes[MAX_MOVE_TYPE]; bool HasAtLoginFlag(AtLoginFlags f) const { return m_atLoginFlags & f; } @@ -2623,6 +2620,10 @@ class Player : public Unit, public GridObject<Player> DeclinedName *m_declinedname; Runes *m_runes; EquipmentSets m_EquipmentSets; + + bool canSeeAlways(WorldObject const* obj) const; + + bool isAlwaysDetectableFor(WorldObject const* seer) const; private: // internal common parts for CanStore/StoreItem functions uint8 _CanStoreItem_InSpecificSlot(uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool swap, Item *pSrcItem) const; @@ -2673,8 +2674,6 @@ class Player : public Unit, public GridObject<Player> bool m_bCanDelayTeleport; bool m_bHasDelayedTeleport; - uint32 m_DetectInvTimer; - // Temporary removed pet cache uint32 m_temporaryUnsummonedPetNumber; uint32 m_oldpetspell; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 47b35321847..84ff41a67dd 100755 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -137,11 +137,8 @@ m_vehicleKit(NULL), m_unitTypeMask(UNIT_MASK_NONE), m_HostileRefManager(this) m_ObjectSlot[0] = m_ObjectSlot[1] = m_ObjectSlot[2] = m_ObjectSlot[3] = 0; m_auraUpdateIterator = m_ownedAuras.end(); - m_Visibility = VISIBILITY_ON; m_interruptMask = 0; - m_detectInvisibilityMask = 0; - m_invisibilityMask = 0; m_transform = 0; m_ShapeShiftFormSpellId = 0; m_canModifyStats = false; @@ -193,6 +190,8 @@ m_vehicleKit(NULL), m_unitTypeMask(UNIT_MASK_NONE), m_HostileRefManager(this) m_cleanupDone = false; m_duringRemoveFromWorld = false; + + m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE); } Unit::~Unit() @@ -4428,6 +4427,15 @@ bool Unit::HasAuraType(AuraType auraType) const return (!m_modAuras[auraType].empty()); } +bool Unit::HasAuraTypeWithCaster(AuraType auratype, uint64 caster) const +{ + AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); + for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) + if (caster == (*i)->GetCasterGUID()) + return true; + return false; +} + bool Unit::HasAuraTypeWithMiscvalue(AuraType auratype, int32 miscvalue) const { AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); @@ -11997,14 +12005,6 @@ bool Unit::canAttack(Unit const* target, bool force) const if (!target->isAttackableByAOE() || target->hasUnitState(UNIT_STAT_DIED)) return false; - // shaman totem quests: spell 8898, shaman can detect elementals but elementals cannot see shaman - if (m_invisibilityMask || target->m_invisibilityMask) - if (!canDetectInvisibilityOf(target) && !target->canDetectInvisibilityOf(this)) - return false; - - if (target->GetVisibility() == VISIBILITY_GROUP_STEALTH && !canDetectStealthOf(target, GetDistance(target))) - return false; - if (m_vehicle) if (IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target)) return false; @@ -12116,98 +12116,36 @@ int32 Unit::ModifyPower(Powers power, int32 dVal) return gain; } -bool Unit::isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList, bool is3dDistance) const +bool Unit::isAlwaysVisibleFor(WorldObject const* seer) const { - if (!u || !IsInMap(u)) - return false; - - return u->canSeeOrDetect(this, detect, inVisibleList, is3dDistance); -} - -bool Unit::canSeeOrDetect(Unit const* /*u*/, bool /*detect*/, bool /*inVisibleList*/, bool /*is3dDistance*/) const -{ - return true; -} - -bool Unit::canDetectInvisibilityOf(Unit const* u) const -{ - if (m_invisibilityMask & u->m_invisibilityMask) // same group + if (WorldObject::isAlwaysVisibleFor(seer)) return true; - AuraEffectList const& auras = u->GetAuraEffectsByType(SPELL_AURA_MOD_STALKED); // Hunter mark - for (AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) - if ((*iter)->GetCasterGUID() == GetGUID()) - return true; - - if (uint32 mask = (m_detectInvisibilityMask & u->m_invisibilityMask)) - { - for (uint8 i = 0; i < 10; ++i) - { - if (((1 << i) & mask) == 0) - continue; - - // find invisibility level - uint32 invLevel = 0; - Unit::AuraEffectList const& iAuras = u->GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY); - for (Unit::AuraEffectList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr) - if (uint8((*itr)->GetMiscValue()) == i && int32(invLevel) < (*itr)->GetAmount()) - invLevel = (*itr)->GetAmount(); - // find invisibility detect level - uint32 detectLevel = 0; - if (i == 6 && GetTypeId() == TYPEID_PLAYER) // special drunk detection case - { - detectLevel = this->ToPlayer()->GetDrunkValue(); - } - else - { - Unit::AuraEffectList const& dAuras = GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION); - for (Unit::AuraEffectList::const_iterator itr = dAuras.begin(); itr != dAuras.end(); ++itr) - if (uint8((*itr)->GetMiscValue()) == i && int32(detectLevel) < (*itr)->GetAmount()) - detectLevel = (*itr)->GetAmount(); - } - - if (invLevel <= detectLevel) - return true; - } - } + // Always seen by owner + if (uint64 guid = GetCharmerOrOwnerGUID()) + if (seer->GetGUID() == guid) + return true; return false; } -bool Unit::canDetectStealthOf(Unit const* target, float distance) const +bool Unit::isAlwaysDetectableFor(WorldObject const* seer) const { - if (hasUnitState(UNIT_STAT_STUNNED)) - return false; - if (distance < 0.24f) //collision - return true; - if (!HasInArc(M_PI, target)) //behind - return false; - if (HasAuraType(SPELL_AURA_DETECT_STEALTH)) + if (WorldObject::isAlwaysDetectableFor(seer)) return true; - AuraEffectList const &auras = target->GetAuraEffectsByType(SPELL_AURA_MOD_STALKED); // Hunter mark - for (AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) - if ((*iter)->GetCasterGUID() == GetGUID()) - return true; - - //Visible distance based on stealth value (stealth rank 4 300MOD, 10.5 - 3 = 7.5) - float visibleDistance = 7.5f; - //Visible distance is modified by -Level Diff (every level diff = 1.0f in visible distance) - visibleDistance += float(getLevelForTarget(target)) - target->GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH)/5.0f; - //-Stealth Mod(positive like Master of Deception) and Stealth Detection(negative like paranoia) - //based on wowwiki every 5 mod we have 1 more level diff in calculation - visibleDistance += (float)(GetTotalAuraModifier(SPELL_AURA_MOD_DETECT) - target->GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH_LEVEL)) / 5.0f; - visibleDistance = visibleDistance > MAX_PLAYER_STEALTH_DETECT_RANGE ? MAX_PLAYER_STEALTH_DETECT_RANGE : visibleDistance; + if (HasAuraTypeWithCaster(SPELL_AURA_MOD_STALKED, seer->GetGUID())) + return true; - return distance < visibleDistance; + return false; } void Unit::SetVisibility(UnitVisibility x) { - m_Visibility = x; - - if (m_Visibility == VISIBILITY_GROUP_STEALTH) - DestroyForNearbyPlayers(); + if (x == VISIBILITY_OFF) + m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_GAMEMASTER); + else + m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); UpdateObjectVisibility(); } @@ -12669,7 +12607,7 @@ Unit* Creature::SelectVictim() { --aura; caster = (*aura)->GetCaster(); - if (caster && caster->IsInMap(this) && canAttack(caster) && caster->isInAccessiblePlaceFor(this->ToCreature())) + if (caster && canSeeOrDetect(caster, true) && canAttack(caster) && caster->isInAccessiblePlaceFor(this->ToCreature())) { target = caster; break; @@ -12742,15 +12680,18 @@ Unit* Creature::SelectVictim() return target; } - if (m_invisibilityMask) + + Unit::AuraEffectList const& iAuras = GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY); + if(!iAuras.empty()) { - Unit::AuraEffectList const& iAuras = GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY); for (Unit::AuraEffectList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr) + { if ((*itr)->GetBase()->IsPermanent()) { AI()->EnterEvadeMode(); break; } + } return NULL; } @@ -13093,11 +13034,6 @@ Creature* Unit::GetCreature(WorldObject& object, uint64 guid) return object.GetMap()->GetCreature(guid); } -bool Unit::isVisibleForInState(Player const* u, bool inVisibleList) const -{ - return u->canSeeOrDetect(this, false, inVisibleList, false); -} - uint32 Unit::GetCreatureType() const { if (GetTypeId() == TYPEID_PLAYER) @@ -16182,7 +16118,7 @@ void Unit::UpdateObjectVisibility(bool forced) WorldObject::UpdateObjectVisibility(true); // call MoveInLineOfSight for nearby creatures Trinity::AIRelocationNotifier notifier(*this); - VisitNearbyObject(GetMap()->GetVisibilityDistance(), notifier); + VisitNearbyObject(GetVisibilityRange(), notifier); } } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 5432568289a..b7f0cc36ba4 100755 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -542,12 +542,8 @@ enum DamageEffectType enum UnitVisibility { - VISIBILITY_OFF = 0, // absolute, not detectable, GM-like, can see all other - VISIBILITY_ON = 1, - VISIBILITY_GROUP_STEALTH = 2, // detect chance, seen and can see group members - //VISIBILITY_GROUP_INVISIBILITY = 3, // invisibility, can see and can be seen only another invisible unit or invisible detection unit, set only if not stealthed, and in checks not used (mask used instead) - //VISIBILITY_GROUP_NO_DETECT = 4, // state just at stealth apply for update Grid state. Don't remove, otherwise stealth spells will break - VISIBILITY_RESPAWN = 5 // special totally not detectable visibility for force delete object at respawn command + VISIBILITY_OFF = 0, + VISIBILITY_ON = 1 }; // Value masks for UNIT_FIELD_FLAGS @@ -1083,7 +1079,7 @@ enum PlayerTotemType // delay time next attack to prevent client attack animation problems #define ATTACK_DISPLAY_DELAY 200 -#define MAX_PLAYER_STEALTH_DETECT_RANGE 45.0f // max distance for detection targets by player +#define MAX_PLAYER_STEALTH_DETECT_RANGE 30.0f // max distance for detection targets by player struct SpellProcEventEntry; // used only privately @@ -1194,7 +1190,7 @@ class Unit : public WorldObject bool IsVehicle() const { return m_unitTypeMask & UNIT_MASK_VEHICLE; } uint8 getLevel() const { return uint8(GetUInt32Value(UNIT_FIELD_LEVEL)); } - virtual uint8 getLevelForTarget(Unit const* /*target*/) const { return getLevel(); } + uint8 getLevelForTarget(WorldObject const* /*target*/) const { return getLevel(); } void SetLevel(uint8 lvl); uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, 0); } uint32 getRaceMask() const { return 1 << (getRace()-1); } @@ -1640,6 +1636,7 @@ class Unit : public WorldObject uint32 GetAuraCount(uint32 spellId) const; bool HasAura(uint32 spellId, uint64 caster = 0, uint8 reqEffMask = 0) const; bool HasAuraType(AuraType auraType) const; + bool HasAuraTypeWithCaster(AuraType auratype, uint64 caster) const; bool HasAuraTypeWithMiscvalue(AuraType auratype, int32 miscvalue) const; bool HasAuraTypeWithAffectMask(AuraType auratype, SpellEntry const * affectedSpell) const; bool HasAuraTypeWithValue(AuraType auratype, int32 value) const; @@ -1717,8 +1714,6 @@ class Unit : public WorldObject uint32 m_addDmgOnce; uint64 m_SummonSlot[MAX_SUMMON_SLOT]; uint64 m_ObjectSlot[4]; - uint32 m_detectInvisibilityMask; - uint32 m_invisibilityMask; uint32 m_ShapeShiftFormSpellId; ShapeshiftForm m_form; @@ -1768,22 +1763,16 @@ class Unit : public WorldObject void SetFacingToObject(WorldObject* pObject); // Visibility system - UnitVisibility GetVisibility() const { return m_Visibility; } + UnitVisibility GetVisibility() const { return (m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM) > SEC_PLAYER) ? VISIBILITY_OFF : VISIBILITY_ON; } void SetVisibility(UnitVisibility x); // common function for visibility checks for player/creatures with detection code - virtual bool canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const = 0; - bool isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const; - bool canDetectInvisibilityOf(Unit const* u) const; - bool canDetectStealthOf(Unit const* u, float distance) const; + + bool isValid() const { return WorldObject::isValid(); } + void SetPhaseMask(uint32 newPhaseMask, bool update);// overwrite WorldObject::SetPhaseMask void UpdateObjectVisibility(bool forced = true); - // virtual functions for all world objects types - bool isVisibleForInState(Player const* u, bool inVisibleList) const; - // function for low level grid visibility checks in player/creature cases - virtual bool IsVisibleInGridForPlayer(Player const* pl) const = 0; - SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY]; // Threat related methods @@ -2074,6 +2063,12 @@ class Unit : public WorldObject uint32 m_unitTypeMask; + bool isAlwaysVisibleFor(WorldObject const* seer) const; + bool canSeeAlways(WorldObject const* obj) const { return WorldObject::canSeeAlways(obj); } + + bool isVisibleForInState(WorldObject const* seer) const { return WorldObject::isVisibleForInState(seer); }; + + bool isAlwaysDetectableFor(WorldObject const* seer) const; private: bool IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura * aura, SpellEntry const * procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const *& spellProcEvent); bool HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); @@ -2104,8 +2099,6 @@ class Unit : public WorldObject Spell* m_currentSpells[CURRENT_MAX_SPELL]; - UnitVisibility m_Visibility; - Diminishing m_Diminishing; // Manage all Units threatening us // ThreatManager m_ThreatManager; |
