From 74d868242697e4693e7d9d549491af4f33f34c32 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Sat, 1 Jul 2023 09:09:57 -0400 Subject: [PATCH] Core/Creatures: Implement CREATURE_STATIC_FLAG_4_IGNORE_LOS_WHEN_CASTING_ON_ME Port From (https://github.com/TrinityCore/TrinityCore/commit/815e158a3ec5f5e173376ff4273913990e9bce10) --- .../Game/Collision/Management/VMapManager.cs | 14 ++--- Source/Game/Conditions/DisableManager.cs | 8 +-- Source/Game/Entities/Creature/Creature.cs | 2 + Source/Game/Spells/Spell.cs | 59 +++++++++++++------ 4 files changed, 54 insertions(+), 29 deletions(-) diff --git a/Source/Game/Collision/Management/VMapManager.cs b/Source/Game/Collision/Management/VMapManager.cs index aafc7c75f..46aa5f6d9 100644 --- a/Source/Game/Collision/Management/VMapManager.cs +++ b/Source/Game/Collision/Management/VMapManager.cs @@ -87,7 +87,7 @@ namespace Game.Collision public bool IsInLineOfSight(uint mapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) { - if (!IsLineOfSightCalcEnabled() || Global.DisableMgr.IsVMAPDisabledFor(mapId, (byte)DisableFlags.VmapLOS)) + if (!IsLineOfSightCalcEnabled() || Global.DisableMgr.IsVMAPDisabledFor(mapId, DisableFlags.VmapLOS)) return true; var instanceTree = iInstanceMapTrees.LookupByKey(mapId); @@ -104,7 +104,7 @@ namespace Game.Collision public bool GetObjectHitPos(uint mapId, float x1, float y1, float z1, float x2, float y2, float z2, out float rx, out float ry, out float rz, float modifyDist) { - if (IsLineOfSightCalcEnabled() && !Global.DisableMgr.IsVMAPDisabledFor(mapId, (byte)DisableFlags.VmapLOS)) + if (IsLineOfSightCalcEnabled() && !Global.DisableMgr.IsVMAPDisabledFor(mapId, DisableFlags.VmapLOS)) { var instanceTree = iInstanceMapTrees.LookupByKey(mapId); if (instanceTree != null) @@ -130,7 +130,7 @@ namespace Game.Collision public float GetHeight(uint mapId, float x, float y, float z, float maxSearchDist) { - if (IsHeightCalcEnabled() && !Global.DisableMgr.IsVMAPDisabledFor(mapId, (byte)DisableFlags.VmapHeight)) + if (IsHeightCalcEnabled() && !Global.DisableMgr.IsVMAPDisabledFor(mapId, DisableFlags.VmapHeight)) { var instanceTree = iInstanceMapTrees.LookupByKey(mapId); if (instanceTree != null) @@ -153,7 +153,7 @@ namespace Game.Collision adtId = 0; rootId = 0; groupId = 0; - if (!Global.DisableMgr.IsVMAPDisabledFor(mapId, (byte)DisableFlags.VmapAreaFlag)) + if (!Global.DisableMgr.IsVMAPDisabledFor(mapId, DisableFlags.VmapAreaFlag)) { var instanceTree = iInstanceMapTrees.LookupByKey(mapId); if (instanceTree != null) @@ -171,7 +171,7 @@ namespace Game.Collision public bool GetLiquidLevel(uint mapId, float x, float y, float z, uint reqLiquidType, ref float level, ref float floor, ref uint type, ref uint mogpFlags) { - if (!Global.DisableMgr.IsVMAPDisabledFor(mapId, (byte)DisableFlags.VmapLiquidStatus)) + if (!Global.DisableMgr.IsVMAPDisabledFor(mapId, DisableFlags.VmapLiquidStatus)) { var instanceTree = iInstanceMapTrees.LookupByKey(mapId); if (instanceTree != null) @@ -198,7 +198,7 @@ namespace Game.Collision { var data = new AreaAndLiquidData(); - if (Global.DisableMgr.IsVMAPDisabledFor(mapId, (byte)DisableFlags.VmapLiquidStatus)) + if (Global.DisableMgr.IsVMAPDisabledFor(mapId, DisableFlags.VmapLiquidStatus)) { data.floorZ = z; int adtId, rootId, groupId; @@ -221,7 +221,7 @@ namespace Game.Collision if (info.hitInstance.GetLiquidLevel(pos, info, ref liquidLevel)) data.liquidInfo = new(liquidType, liquidLevel); - if (!Global.DisableMgr.IsVMAPDisabledFor(mapId, (byte)DisableFlags.VmapLiquidStatus)) + if (!Global.DisableMgr.IsVMAPDisabledFor(mapId, DisableFlags.VmapLiquidStatus)) data.areaInfo = new(info.hitInstance.adtId, info.rootId, (int)info.hitModel.GetWmoID(), info.hitModel.GetMogpFlags()); } } diff --git a/Source/Game/Conditions/DisableManager.cs b/Source/Game/Conditions/DisableManager.cs index 8a83f0565..a5f6fd93e 100644 --- a/Source/Game/Conditions/DisableManager.cs +++ b/Source/Game/Conditions/DisableManager.cs @@ -270,7 +270,7 @@ namespace Game Log.outInfo(LogFilter.ServerLoading, "Checked {0} quest disables in {1} ms", m_DisableMap[DisableType.Quest].Count, Time.GetMSTimeDiffToNow(oldMSTime)); } - public bool IsDisabledFor(DisableType type, uint entry, WorldObject refe, ushort flags = 0) + public bool IsDisabledFor(DisableType type, uint entry, WorldObject refe, DisableFlags flags = 0) { Cypher.Assert(type < DisableType.Max); if (!m_DisableMap.ContainsKey(type) || m_DisableMap[type].Empty()) @@ -331,7 +331,7 @@ namespace Game } else if (spellFlags.HasFlag(DisableFlags.SpellDeprecatedSpell)) // call not from spellcast return true; - else if (flags.HasAnyFlag((byte)DisableFlags.SpellLOS)) + else if (flags.HasAnyFlag(DisableFlags.SpellLOS)) return spellFlags.HasFlag(DisableFlags.SpellLOS); break; @@ -373,13 +373,13 @@ namespace Game case DisableType.MMAP: return true; case DisableType.VMAP: - return flags.HasAnyFlag(data.flags); + return flags.HasAnyFlag((DisableFlags)data.flags); } return false; } - public bool IsVMAPDisabledFor(uint entry, byte flags) + public bool IsVMAPDisabledFor(uint entry, DisableFlags flags) { return IsDisabledFor(DisableType.VMAP, entry, null, flags); } diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 82e16bfb4..53a1baf36 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -3289,6 +3289,8 @@ namespace Game.Entities public bool CanMelee() { return !_staticFlags.HasFlag(CreatureStaticFlags.NoMelee); } public void SetCanMelee(bool canMelee) { _staticFlags.ApplyFlag(CreatureStaticFlags.NoMelee, !canMelee); } + + public bool CanIgnoreLineOfSightWhenCastingOnMe() { return _staticFlags.HasFlag(CreatureStaticFlags4.IgnoreLosWhenCastingOnMe); } public sbyte GetOriginalEquipmentId() { return m_originalEquipmentId; } public byte GetCurrentEquipmentId() { return m_equipmentId; } diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 4fe044577..4727e65ed 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -1771,7 +1771,7 @@ namespace Game.Spells if (unitTarget != null) { uint deficit = (uint)(unitTarget.GetMaxHealth() - unitTarget.GetHealth()); - if ((deficit > maxHPDeficit || found == null) && chainSource.IsWithinDist(unitTarget, jumpRadius) && chainSource.IsWithinLOSInMap(unitTarget, LineOfSightChecks.All, ModelIgnoreFlags.M2)) + if ((deficit > maxHPDeficit || found == null) && chainSource.IsWithinDist(unitTarget, jumpRadius) && IsWithinLOS(chainSource, unitTarget, false, ModelIgnoreFlags.M2)) { found = obj; maxHPDeficit = deficit; @@ -1786,10 +1786,10 @@ namespace Game.Spells { if (found == null) { - if (chainSource.IsWithinDist(obj, jumpRadius) && chainSource.IsWithinLOSInMap(obj, LineOfSightChecks.All, ModelIgnoreFlags.M2)) + if (chainSource.IsWithinDist(obj, jumpRadius) && IsWithinLOS(chainSource, obj, false, ModelIgnoreFlags.M2)) found = obj; } - else if (chainSource.GetDistanceOrder(obj, found) && chainSource.IsWithinLOSInMap(obj, LineOfSightChecks.All, ModelIgnoreFlags.M2)) + else if (chainSource.GetDistanceOrder(obj, found) && IsWithinLOS(chainSource, obj, false, ModelIgnoreFlags.M2)) found = obj; } } @@ -2604,11 +2604,11 @@ namespace Game.Spells Cast(true); else { - // commented out !m_spellInfo->StartRecoveryTime, it forces instant spells with global cooldown to be processed in spell::update + // commented out !m_spellInfo.StartRecoveryTime, it forces instant spells with global cooldown to be processed in spell::update // as a result a spell that passed CheckCast and should be processed instantly may suffer from this delayed process // the easiest bug to observe is LoS check in AddUnitTarget, even if spell passed the CheckCast LoS check the situation can change in spell::update // because target could be relocated in the meantime, making the spell fly to the air (no targets can be registered, so no effects processed, nothing in combat log) - bool willCastDirectly = m_casttime == 0 && /*!m_spellInfo->StartRecoveryTime && */ GetCurrentContainer() == CurrentSpellTypes.Generic; + bool willCastDirectly = m_casttime == 0 && /*!m_spellInfo.StartRecoveryTime && */ GetCurrentContainer() == CurrentSpellTypes.Generic; Unit unitCaster = m_caster.ToUnit(); if (unitCaster != null) @@ -4919,7 +4919,7 @@ namespace Game.Spells losTarget = dynObj; } - if (!m_spellInfo.HasAttribute(SpellAttr2.IgnoreLineOfSight) && !Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, (byte)DisableFlags.SpellLOS) && !unitTarget.IsWithinLOSInMap(losTarget, LineOfSightChecks.All, ModelIgnoreFlags.M2)) + if (!IsWithinLOS(losTarget, unitTarget, true, ModelIgnoreFlags.M2)) return SpellCastResult.LineOfSight; } } @@ -4927,13 +4927,8 @@ namespace Game.Spells // Check for line of sight for spells with dest if (m_targets.HasDst()) - { - float x, y, z; - m_targets.GetDstPos().GetPosition(out x, out y, out z); - - if (!m_spellInfo.HasAttribute(SpellAttr2.IgnoreLineOfSight) && !Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, (byte)DisableFlags.SpellLOS) && !m_caster.IsWithinLOS(x, y, z, LineOfSightChecks.All, ModelIgnoreFlags.M2)) + if (!IsWithinLOS(m_caster, m_targets.GetDstPos(), ModelIgnoreFlags.M2)) return SpellCastResult.LineOfSight; - } // check pet presence if (unitCaster != null) @@ -5218,7 +5213,7 @@ namespace Game.Spells return SpellCastResult.DontReport; // first we must check to see if the target is in LoS. A path can usually be built but LoS matters for charge spells - if (!target.IsWithinLOSInMap(unitCaster)) //Do full LoS/Path check. Don't exclude m2 + if (!IsWithinLOS(unitCaster, target, true, ModelIgnoreFlags.Nothing)) //Do full LoS/Path check. Don't exclude m2 return SpellCastResult.LineOfSight; float objSize = target.GetCombatReach(); @@ -7097,7 +7092,7 @@ namespace Game.Spells } // check for ignore LOS on the effect itself - if (m_spellInfo.HasAttribute(SpellAttr2.IgnoreLineOfSight) || Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, (byte)DisableFlags.SpellLOS)) + if (m_spellInfo.HasAttribute(SpellAttr2.IgnoreLineOfSight) || Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, DisableFlags.SpellLOS)) return true; // check if gameobject ignores LOS @@ -7107,7 +7102,7 @@ namespace Game.Spells return true; // if spell is triggered, need to check for LOS disable on the aura triggering it and inherit that behaviour - if (!m_spellInfo.HasAttribute(SpellAttr5.AlwaysLineOfSight) && IsTriggered() && m_triggeredByAuraSpell != null && (m_triggeredByAuraSpell.HasAttribute(SpellAttr2.IgnoreLineOfSight) || Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_triggeredByAuraSpell.Id, null, (byte)DisableFlags.SpellLOS))) + if (!m_spellInfo.HasAttribute(SpellAttr5.AlwaysLineOfSight) && IsTriggered() && m_triggeredByAuraSpell != null && (m_triggeredByAuraSpell.HasAttribute(SpellAttr2.IgnoreLineOfSight) || Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_triggeredByAuraSpell.Id, null, DisableFlags.SpellLOS))) return true; // @todo shit below shouldn't be here, but it's temporary @@ -7118,7 +7113,7 @@ namespace Game.Spells { if (m_targets.GetCorpseTargetGUID().IsEmpty()) { - if (target.IsWithinLOSInMap(m_caster, LineOfSightChecks.All, ModelIgnoreFlags.M2) && target.HasUnitFlag(UnitFlags.Skinnable)) + if (IsWithinLOS(m_caster, target, true, ModelIgnoreFlags.M2) && target.HasUnitFlag(UnitFlags.Skinnable)) return true; return false; @@ -7134,7 +7129,7 @@ namespace Game.Spells if (!corpse.HasCorpseDynamicFlag(CorpseDynFlags.Lootable)) return false; - if (!corpse.IsWithinLOSInMap(m_caster, LineOfSightChecks.All, ModelIgnoreFlags.M2)) + if (!IsWithinLOS(m_caster, corpse, true, ModelIgnoreFlags.M2)) return false; break; @@ -7149,7 +7144,8 @@ namespace Game.Spells caster = m_caster.GetMap().GetGameObject(m_originalCasterGUID); if (!caster) caster = m_caster; - if (target != m_caster && !target.IsWithinLOSInMap(caster, LineOfSightChecks.All, ModelIgnoreFlags.M2)) + + if (target != m_caster && !IsWithinLOS(caster, target, true, ModelIgnoreFlags.M2)) return false; } @@ -7716,6 +7712,33 @@ namespace Game.Spells } } + bool IsWithinLOS(WorldObject source, WorldObject target, bool targetAsSourceLocation, ModelIgnoreFlags ignoreFlags) + { + if (m_spellInfo.HasAttribute(SpellAttr2.IgnoreLineOfSight)) + return true; + + if (Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, DisableFlags.SpellLOS)) + return true; + + if (target.IsCreature() && target.ToCreature().CanIgnoreLineOfSightWhenCastingOnMe()) + return true; + + WorldObject src = targetAsSourceLocation ? target : source; + WorldObject dst = targetAsSourceLocation ? source : target; + return src.IsWithinLOSInMap(dst, LineOfSightChecks.All, ignoreFlags); + } + + bool IsWithinLOS(WorldObject source, Position target, ModelIgnoreFlags ignoreFlags) + { + if (m_spellInfo.HasAttribute(SpellAttr2.IgnoreLineOfSight)) + return true; + + if (Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, DisableFlags.SpellLOS)) + return true; + + return source.IsWithinLOS(target.GetPositionX(), target.GetPositionY(), target.GetPositionZ(), LineOfSightChecks.All, ignoreFlags); + } + bool CheckScriptEffectImplicitTargets(uint effIndex, uint effIndexToCheck) { // Skip if there are not any script