diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 9c45722d4..b63514c40 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1110,7 +1110,31 @@ bool WorldObject::_IsWithinDist(WorldObject const* obj, float dist2compare, bool return distsq < maxdist * maxdist; } -bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const +Position WorldObject::GetHitSpherePointFor(Position const& dest) const +{ + G3D::Vector3 vThis(GetPositionX(), GetPositionY(), GetPositionZ()); + G3D::Vector3 vObj(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ()); + G3D::Vector3 contactPoint = vThis + (vObj - vThis).directionOrZero() * std::min(dest.GetExactDist(this), GetObjectSize()); + + return Position(contactPoint.x, contactPoint.y, contactPoint.z, GetAngle(contactPoint.x, contactPoint.y)); +} + +bool WorldObject::IsWithinLOS(float ox, float oy, float oz, LineOfSightChecks checks) const +{ + if (IsInWorld()) + { + float x, y, z; + if (GetTypeId() == TYPEID_PLAYER) + GetPosition(x, y, z); + else + GetHitSpherePointFor({ ox, oy, oz }, x, y, z); + + return GetMap()->isInLineOfSight(x, y, z + 2.0f, ox, oy, oz + 2.0f, GetPhaseMask(), checks); + } + return true; +} + +bool WorldObject::IsWithinLOSInMap(const WorldObject* obj, LineOfSightChecks checks) const { if (!IsInMap(obj)) return false; @@ -1121,36 +1145,7 @@ bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const else obj->GetHitSpherePointFor(GetPosition(), x, y, z); - return IsWithinLOS(x, y, z); -} - -bool WorldObject::IsWithinLOS(float ox, float oy, float oz) const -{ - /*float x, y, z; - GetPosition(x, y, z); - VMAP::IVMapManager* vMapManager = VMAP::VMapFactory::createOrGetVMapManager(); - return vMapManager->isInLineOfSight(GetMapId(), x, y, z+2.0f, ox, oy, oz+2.0f);*/ - if (IsInWorld()) - { - float x, y, z; - if (GetTypeId() == TYPEID_PLAYER) - GetPosition(x, y, z); - else - GetHitSpherePointFor({ ox, oy, oz }, x, y, z); - - return GetMap()->isInLineOfSight(x, y, z + 2.0f, ox, oy, oz + 2.0f, GetPhaseMask()); - } - - return true; -} - -Position WorldObject::GetHitSpherePointFor(Position const& dest) const -{ - G3D::Vector3 vThis(GetPositionX(), GetPositionY(), GetPositionZ()); - G3D::Vector3 vObj(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ()); - G3D::Vector3 contactPoint = vThis + (vObj - vThis).directionOrZero() * std::min(dest.GetExactDist(this), GetObjectSize()); - - return Position(contactPoint.x, contactPoint.y, contactPoint.z, GetAngle(contactPoint.x, contactPoint.y)); + return IsWithinLOS(x, y, z, checks); } void WorldObject::GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 0d779cd75..7530d2630 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -865,8 +865,8 @@ class WorldObject : public Object, public WorldLocation { return obj && IsInMap(obj) && InSamePhase(obj) && _IsWithinDist(obj, dist2compare, is3D); } - bool IsWithinLOS(float x, float y, float z) const; - bool IsWithinLOSInMap(const WorldObject* obj) const; + bool IsWithinLOS(float x, float y, float z, LineOfSightChecks checks = LINEOFSIGHT_ALL_CHECKS) const; + bool IsWithinLOSInMap(WorldObject const* obj, LineOfSightChecks checks = LINEOFSIGHT_ALL_CHECKS) const; Position GetHitSpherePointFor(Position const& dest) const; void GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const; bool GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D = true) const; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index efde5585d..107d978f5 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -2164,10 +2164,15 @@ float Map::GetWaterLevel(float x, float y) const return 0; } -bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const -{ - return VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2) - && _dynamicTree.isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask); +bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks) const +{ + if ((checks & LINEOFSIGHT_CHECK_VMAP) && !VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2)) + return false; + + if (sWorld->getBoolConfig(CONFIG_CHECK_GOBJECT_LOS) && (checks & LINEOFSIGHT_CHECK_GOBJECT) + && !_dynamicTree.isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask)) + return false; + return true; } bool Map::getObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 44c5c2ccf..a8cd46205 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -142,6 +142,14 @@ struct LiquidData float depth_level; }; +enum LineOfSightChecks +{ + LINEOFSIGHT_CHECK_VMAP = 0x1, // check static floor layout data + LINEOFSIGHT_CHECK_GOBJECT = 0x2, // check dynamic game object data + + LINEOFSIGHT_ALL_CHECKS = (LINEOFSIGHT_CHECK_VMAP | LINEOFSIGHT_CHECK_GOBJECT) +}; + class GridMap { uint32 _flags; @@ -448,7 +456,7 @@ class Map : public GridRefManager float GetWaterOrGroundLevel(float x, float y, float z, float* ground = NULL, bool swim = false, float maxSearchDist = 50.0f) const; float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; - bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const; + bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks) const; void Balance() { _dynamicTree.balance(); } void RemoveGameObjectModel(const GameObjectModel& model) { _dynamicTree.remove(model); } void InsertGameObjectModel(const GameObjectModel& model) { _dynamicTree.insert(model); } diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp index f3b09f40a..b1a1868d2 100644 --- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp @@ -145,7 +145,7 @@ void RandomMovementGenerator::_setRandomLocation(Creature* creature) return; } - if (!map->isInLineOfSight((*itr).x, (*itr).y, (*itr).z+2.f, (*itrNext).x, (*itrNext).y, (*itrNext).z+2.f, creature->GetPhaseMask())) + if (!map->isInLineOfSight((*itr).x, (*itr).y, (*itr).z+2.f, (*itrNext).x, (*itrNext).y, (*itrNext).z+2.f, creature->GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS)) { _validPointsVector[_currentPoint].erase(randomIter); _preComputedPaths.erase(pathIdx); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index acbbc8c04..cd286f3fb 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2191,7 +2191,7 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar if (Unit* unit = (*itr)->ToUnit()) { uint32 deficit = unit->GetMaxHealth() - unit->GetHealth(); - if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && target->IsWithinDist(unit, jumpRadius) && target->IsWithinLOSInMap(unit)) + if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && target->IsWithinDist(unit, jumpRadius) && target->IsWithinLOSInMap(unit, LINEOFSIGHT_ALL_CHECKS)) { foundItr = itr; maxHPDeficit = deficit; @@ -2206,10 +2206,10 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar { if (foundItr == tempTargets.end()) { - if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr)) + if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS)) foundItr = itr; } - else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr)) + else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS)) foundItr = itr; } } @@ -5518,7 +5518,7 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_NOT_INFRONT; if (m_caster->GetEntry() != WORLD_TRIGGER) // Ignore LOS for gameobjects casts (wrongly casted by a trigger) - if ((!m_caster->IsTotem() || !m_spellInfo->IsPositive()) && !m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !m_spellInfo->HasAttribute(SPELL_ATTR5_SKIP_CHECKCAST_LOS_CHECK) && !m_caster->IsWithinLOSInMap(target) && !(m_spellFlags & SPELL_FLAG_REDIRECTED)) + if ((!m_caster->IsTotem() || !m_spellInfo->IsPositive()) && !m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !m_spellInfo->HasAttribute(SPELL_ATTR5_SKIP_CHECKCAST_LOS_CHECK) && !m_caster->IsWithinLOSInMap(target, LINEOFSIGHT_ALL_CHECKS) && !(m_spellFlags & SPELL_FLAG_REDIRECTED)) return SPELL_FAILED_LINE_OF_SIGHT; } } @@ -5529,7 +5529,7 @@ SpellCastResult Spell::CheckCast(bool strict) float x, y, z; m_targets.GetDstPos()->GetPosition(x, y, z); - if ((!m_caster->IsTotem() || !m_spellInfo->IsPositive()) && !m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !m_spellInfo->HasAttribute(SPELL_ATTR5_SKIP_CHECKCAST_LOS_CHECK) && !m_caster->IsWithinLOS(x, y, z)) + if ((!m_caster->IsTotem() || !m_spellInfo->IsPositive()) && !m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !m_spellInfo->HasAttribute(SPELL_ATTR5_SKIP_CHECKCAST_LOS_CHECK) && !m_caster->IsWithinLOS(x, y, z, LINEOFSIGHT_ALL_CHECKS)) return SPELL_FAILED_LINE_OF_SIGHT; } @@ -5708,7 +5708,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (!target || !pet || pet->isDead() || target->isDead()) return SPELL_FAILED_BAD_TARGETS; - if (!pet->IsWithinLOSInMap(target)) + if (!pet->IsWithinLOSInMap(target, LINEOFSIGHT_ALL_CHECKS)) return SPELL_FAILED_LINE_OF_SIGHT; } break; @@ -7418,7 +7418,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const z = m_targets.GetDstPos()->GetPositionZ(); } - if ((!m_caster->IsTotem() || !m_spellInfo->IsPositive()) && !target->IsWithinLOS(x, y, z)) + if ((!m_caster->IsTotem() || !m_spellInfo->IsPositive()) && !target->IsWithinLOS(x, y, z, LINEOFSIGHT_ALL_CHECKS)) return false; return true; @@ -7430,7 +7430,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const { case SPELL_EFFECT_RESURRECT_NEW: // player far away, maybe his corpse near? - if (target != m_caster && !target->IsWithinLOSInMap(m_caster)) + if (target != m_caster && !target->IsWithinLOSInMap(m_caster, LINEOFSIGHT_ALL_CHECKS)) { if (!m_targets.GetCorpseTargetGUID()) return false; @@ -7442,7 +7442,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const if (target->GetGUID() != corpse->GetOwnerGUID()) return false; - if (!corpse->IsWithinLOSInMap(m_caster) && !(m_spellFlags & SPELL_FLAG_REDIRECTED)) + if (!corpse->IsWithinLOSInMap(m_caster, LINEOFSIGHT_ALL_CHECKS) && !(m_spellFlags & SPELL_FLAG_REDIRECTED)) return false; } break; @@ -7450,7 +7450,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const { if (!m_targets.GetCorpseTargetGUID()) { - if (target->IsWithinLOSInMap(m_caster) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE)) + if (target->IsWithinLOSInMap(m_caster, LINEOFSIGHT_ALL_CHECKS) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE)) return true; return false; @@ -7466,7 +7466,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const if (!corpse->HasFlag(CORPSE_FIELD_FLAGS, CORPSE_FLAG_LOOTABLE)) return false; - if (!corpse->IsWithinLOSInMap(m_caster)) + if (!corpse->IsWithinLOSInMap(m_caster, LINEOFSIGHT_ALL_CHECKS)) return false; } break; @@ -7503,7 +7503,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const z = m_targets.GetDstPos()->GetPositionZ(); } - if (!target->IsInMap(caster) || !target->IsWithinLOS(x, y, z)) + if (!target->IsInMap(caster) || !target->IsWithinLOS(x, y, z, LINEOFSIGHT_ALL_CHECKS)) return false; } break; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index ede71d2eb..d1a82ee70 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1297,7 +1297,10 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_ENABLE_CONTINENT_TRANSPORT_PRELOADING] = sConfigMgr->GetBoolDefault("IsPreloadedContinentTransport.Enabled", false); m_bool_configs[CONFIG_IP_BASED_ACTION_LOGGING] = sConfigMgr->GetBoolDefault("Allow.IP.Based.Action.Logging", false); - + + // Whether to use LoS from game objects + m_bool_configs[CONFIG_CHECK_GOBJECT_LOS] = sConfigMgr->GetBoolDefault("CheckGameObjectLoS", true); + m_bool_configs[CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA] = sConfigMgr->GetBoolDefault("Calculate.Creature.Zone.Area.Data", false); m_bool_configs[CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA] = sConfigMgr->GetBoolDefault("Calculate.Gameoject.Zone.Area.Data", false); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index c4ba65335..a6d37e25f 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -165,6 +165,7 @@ enum WorldBoolConfigs CONFIG_IP_BASED_ACTION_LOGGING, CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA, CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA, + CONFIG_CHECK_GOBJECT_LOS, BOOL_CONFIG_VALUE_COUNT }; diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 2f1a65593..bac6d9f8d 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -1047,8 +1047,15 @@ public: static bool HandleDebugLoSCommand(ChatHandler* handler, char const* /*args*/) { if (Unit* unit = handler->getSelectedUnit()) - handler->PSendSysMessage("Unit %s (GuidLow: %u) is %sin LoS", unit->GetName().c_str(), unit->GetGUIDLow(), handler->GetSession()->GetPlayer()->IsWithinLOSInMap(unit) ? "" : "not "); - return true; + { + Player* player = handler->GetSession()->GetPlayer(); + handler->PSendSysMessage("Checking LoS %s -> %s:", player->GetName().c_str(), unit->GetName().c_str()); + handler->PSendSysMessage(" VMAP LoS: %s", player->IsWithinLOSInMap(unit, LINEOFSIGHT_CHECK_VMAP) ? "clear" : "obstructed"); + handler->PSendSysMessage(" GObj LoS: %s", player->IsWithinLOSInMap(unit, LINEOFSIGHT_CHECK_GOBJECT) ? "clear" : "obstructed"); + handler->PSendSysMessage("%s is %sin line of sight of %s.", unit->GetName().c_str(), (player->IsWithinLOSInMap(unit) ? "" : "not "), player->GetName().c_str()); + return true; + } + return false; } static bool HandleDebugSetAuraStateCommand(ChatHandler* handler, char const* args) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp index 87a423a3b..f95c81087 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp @@ -1696,7 +1696,7 @@ class spell_blood_council_summon_shadow_resonance : public SpellScriptLoader { float destX = summoner->GetPositionX()+cos(angle + a*M_PI)*i*10.0f; float destY = summoner->GetPositionY()+sin(angle + a*M_PI)*i*10.0f; - if (summoner->GetMap()->isInLineOfSight(summoner->GetPositionX(), summoner->GetPositionY(), summoner->GetPositionZ()+10.0f, destX, destY, summoner->GetPositionZ()+10.0f, summoner->GetPhaseMask()) && destX > 4585.0f && destY > 2716.0f && destY < 2822.0f) + if (summoner->GetMap()->isInLineOfSight(summoner->GetPositionX(), summoner->GetPositionY(), summoner->GetPositionZ()+10.0f, destX, destY, summoner->GetPositionZ()+10.0f, summoner->GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS) && destX > 4585.0f && destY > 2716.0f && destY < 2822.0f) { float destZ = summoner->GetMap()->GetHeight(summoner->GetPhaseMask(), destX, destY, summoner->GetPositionZ()+10.0f); if (fabs(destZ-summoner->GetPositionZ()) < 10.0f) // valid z found diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp index 32618e6e2..ad4d6b30c 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp @@ -202,7 +202,7 @@ public: void MoveInLineOfSight(Unit* who) { if (!activated && who->GetTypeId() == TYPEID_PLAYER) - if (me->GetExactDist2d(who) <= 25.0f && me->GetMap()->isInLineOfSight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()+5.0f, who->GetPositionX(), who->GetPositionY(), who->GetPositionZ()+5.0f, 2)) + if (me->GetExactDist2d(who) <= 25.0f && me->GetMap()->isInLineOfSight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()+5.0f, who->GetPositionX(), who->GetPositionY(), who->GetPositionZ()+5.0f, 2, LINEOFSIGHT_ALL_CHECKS)) { activated = true; me->RemoveAura(64615); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 3f6c5164e..81815f97f 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -318,6 +318,15 @@ vmap.enableIndoorCheck = 1 DetectPosCollision = 1 +# +# CheckGameObjectLoS +# Description: Include dynamic game objects (doors, chests etc.) in line of sight checks. +# This increases CPU usage somewhat. +# Default: 1 - (Enabled) +# 0 - (Disabled, may break some boss encounters) + +CheckGameObjectLoS = 1 + # # TargetPosRecalculateRange # Description: Max distance from movement target point (+moving unit size) and targeted