From 0877e73ff2a84ee5dd029d14d8cfd61d9865c188 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Wed, 4 Sep 2019 13:13:59 -0400 Subject: [PATCH] Core/Spells: Implement SpellMisc.LaunchDelay Port From (https://github.com/TrinityCore/TrinityCore/commit/c924840ce7fa7289fe73fb15b0f022bc74618ee1) --- Source/Game/Entities/Unit/Unit.Combat.cs | 10 +++- Source/Game/Spells/Spell.cs | 69 ++++++++++++------------ Source/Game/Spells/SpellEffects.cs | 11 ++++ Source/Game/Spells/SpellInfo.cs | 7 +++ 4 files changed, 61 insertions(+), 36 deletions(-) diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index 161b7badc..5eaaf0238 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -723,10 +723,16 @@ namespace Game.Entities if (spellInfo.CheckExplicitTarget(this, magnet) == SpellCastResult.SpellCastOk && _IsValidAttackTarget(magnet, spellInfo)) { // @todo handle this charge drop by proc in cast phase on explicit target - if (spellInfo.Speed > 0.0f) + if (spellInfo.HasHitDelay()) { // Set up missile speed based delay - uint delay = (uint)Math.Floor(Math.Max(victim.GetDistance(this), 5.0f) / spellInfo.Speed * 1000.0f); + float hitDelay = spellInfo.LaunchDelay; + if (spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation)) + hitDelay += spellInfo.Speed; + else if (spellInfo.Speed > 0.0f) + hitDelay += Math.Max(victim.GetDistance(this), 5.0f) / spellInfo.Speed; + + uint delay = (uint)Math.Floor(hitDelay * 1000.0f); // Schedule charge drop eff.GetBase().DropChargeDelayed(delay, AuraRemoveMode.Expire); } diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 54beacc62..307108636 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -292,17 +292,18 @@ namespace Game.Spells { float speed = m_targets.GetSpeedXY(); if (speed > 0.0f) - return (ulong)Math.Floor(m_targets.GetDist2d() / speed * 1000.0f); + return (ulong)(Math.Floor((m_targets.GetDist2d() / speed + m_spellInfo.LaunchDelay) * 1000.0f)); } + else if (m_spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation)) + return (ulong)(Math.Floor((m_spellInfo.Speed + m_spellInfo.LaunchDelay) * 1000.0f)); else if (m_spellInfo.Speed > 0.0f) { // We should not subtract caster size from dist calculation (fixes execution time desync with animation on client, eg. Malleable Goo cast by PP) float dist = m_caster.GetExactDist(m_targets.GetDstPos()); - if (!m_spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation)) - return (ulong)Math.Floor(dist / m_spellInfo.Speed * 1000.0f); - else - return (ulong)(m_spellInfo.Speed * 1000.0f); + return (ulong)(Math.Floor((dist / m_spellInfo.Speed + m_spellInfo.LaunchDelay) * 1000.0f)); } + + return (ulong)(Math.Floor(m_spellInfo.LaunchDelay * 1000.0f)); } return 0; @@ -1554,19 +1555,20 @@ namespace Game.Spells // Spell have speed - need calculate incoming time // Incoming time is zero for self casts. At least I think so. - if (m_spellInfo.Speed > 0.0f && m_caster != target) + if (m_caster != target) { - // calculate spell incoming interval - // @todo this is a hack - float dist = m_caster.GetDistance(target.GetPositionX(), target.GetPositionY(), target.GetPositionZ()); + float hitDelay = m_spellInfo.LaunchDelay; + if (m_spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation)) + hitDelay += m_spellInfo.Speed; + else if (m_spellInfo.Speed > 0.0f) + { + // calculate spell incoming interval + /// @todo this is a hack + float dist = Math.Max(m_caster.GetDistance(target.GetPositionX(), target.GetPositionY(), target.GetPositionZ()), 5.0f); + hitDelay += dist / m_spellInfo.Speed; + } - if (dist < 5.0f) - dist = 5.0f; - - if (!(m_spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation))) - targetInfo.timeDelay = (ulong)Math.Floor((dist / m_spellInfo.Speed) * 1000.0f); - else - targetInfo.timeDelay = (ulong)(m_spellInfo.Speed * 1000.0f); + targetInfo.timeDelay = (ulong)Math.Floor(hitDelay * 1000.0f); } else targetInfo.timeDelay = 0L; @@ -1626,23 +1628,22 @@ namespace Game.Spells target.processed = false; // Effects not apply on target // Spell have speed - need calculate incoming time - if (m_spellInfo.Speed > 0.0f) + if (m_caster != go) { - // calculate spell incoming interval - float dist = m_caster.GetDistance(go.GetPositionX(), go.GetPositionY(), go.GetPositionZ()); - if (dist < 5.0f) - dist = 5.0f; + float hitDelay = m_spellInfo.LaunchDelay; + if (m_spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation)) + hitDelay += m_spellInfo.Speed; + else if (m_spellInfo.Speed > 0.0f) + { + // calculate spell incoming interval + float dist = Math.Max(m_caster.GetDistance(go.GetPositionX(), go.GetPositionY(), go.GetPositionZ()), 5.0f); + hitDelay += dist / m_spellInfo.Speed; + } - if (!m_spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation)) - target.timeDelay = (ulong)Math.Floor(dist / m_spellInfo.Speed * 1000.0f); - else - target.timeDelay = (ulong)(m_spellInfo.Speed * 1000.0f); - - if (m_delayMoment == 0 || m_delayMoment > target.timeDelay) - m_delayMoment = target.timeDelay; + target.timeDelay = (ulong)Math.Floor(hitDelay * 1000.0f); } else - target.timeDelay = 0L; + target.timeDelay = 0UL; // Add target to list m_UniqueGOTargetInfo.Add(target); @@ -1982,7 +1983,7 @@ namespace Game.Spells return SpellMissInfo.Evade; // For delayed spells immunity may be applied between missile launch and hit - check immunity for that case - if (m_spellInfo.Speed != 0.0f && unit.IsImmunedToSpell(m_spellInfo, m_caster)) + if (m_spellInfo.HasHitDelay() && unit.IsImmunedToSpell(m_spellInfo, m_caster)) return SpellMissInfo.Immune; // disable effects to which unit is immune @@ -2013,7 +2014,7 @@ namespace Game.Spells if (m_caster != unit) { // Recheck UNIT_FLAG_NON_ATTACKABLE for delayed spells - if (m_spellInfo.Speed > 0.0f && unit.HasUnitFlag(UnitFlags.NonAttackable) && unit.GetCharmerOrOwnerGUID() != m_caster.GetGUID()) + if (m_spellInfo.HasHitDelay() && unit.HasUnitFlag(UnitFlags.NonAttackable) && unit.GetCharmerOrOwnerGUID() != m_caster.GetGUID()) return SpellMissInfo.Evade; if (m_caster._IsValidAttackTarget(unit, m_spellInfo)) @@ -2023,7 +2024,7 @@ namespace Game.Spells // for delayed spells ignore negative spells (after duel end) for friendly targets // @todo this cause soul transfer bugged // 63881 - Malady of the Mind jump spell (Yogg-Saron) - if (m_spellInfo.Speed > 0.0f && unit.IsTypeId(TypeId.Player) && !m_spellInfo.IsPositive() && m_spellInfo.Id != 63881) + if (m_spellInfo.HasHitDelay() && unit.IsTypeId(TypeId.Player) && !m_spellInfo.IsPositive() && m_spellInfo.Id != 63881) return SpellMissInfo.Evade; // assisting case, healing and resurrection @@ -2738,7 +2739,7 @@ namespace Game.Spells SendSpellGo(); // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells - if ((m_spellInfo.Speed > 0.0f && !m_spellInfo.IsChanneled()) || m_spellInfo.HasAttribute(SpellAttr4.Unk4)) + if ((m_spellInfo.HasHitDelay() && !m_spellInfo.IsChanneled()) || m_spellInfo.HasAttribute(SpellAttr4.Unk4)) { // Remove used for cast item if need (it can be already NULL after TakeReagents call // in case delayed spell remove item at cast delay start @@ -6524,7 +6525,7 @@ namespace Game.Spells bool IsNeedSendToClient() { return m_SpellVisual != 0 || m_spellInfo.IsChanneled() || - m_spellInfo.HasAttribute(SpellAttr8.AuraSendAmount) || m_spellInfo.Speed > 0.0f || (m_triggeredByAuraSpell == null && !IsTriggered()); + m_spellInfo.HasAttribute(SpellAttr8.AuraSendAmount) || m_spellInfo.HasHitDelay() || (m_triggeredByAuraSpell == null && !IsTriggered()); } bool HaveTargetsForEffect(byte effect) diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index c2bdf3055..5a626a721 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -4114,10 +4114,21 @@ namespace Game.Spells if (m_preGeneratedPath.GetPathType() == PathType.Blank) { Position pos = unitTarget.GetFirstCollisionPosition(unitTarget.GetObjectSize(), unitTarget.GetRelativeAngle(m_caster.GetPosition())); + if (m_spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation)) + speed = pos.GetExactDist(m_caster) / speed; + m_caster.GetMotionMaster().MoveCharge(pos.posX, pos.posY, pos.posZ, speed, EventId.Charge, false, unitTarget, spellEffectExtraData); } else + { + if (m_spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation)) + { + Vector3 pos = m_preGeneratedPath.GetActualEndPosition(); + speed = new Position(pos.X, pos.Y, pos.Z).GetExactDist(m_caster) / speed; + } + m_caster.GetMotionMaster().MoveCharge(m_preGeneratedPath, speed, unitTarget, spellEffectExtraData); + } } if (effectHandleMode == SpellEffectHandleMode.HitTarget) diff --git a/Source/Game/Spells/SpellInfo.cs b/Source/Game/Spells/SpellInfo.cs index aca3bfd2d..66eff86d6 100644 --- a/Source/Game/Spells/SpellInfo.cs +++ b/Source/Game/Spells/SpellInfo.cs @@ -78,6 +78,7 @@ namespace Game.Spells RangeIndex = _misc.RangeIndex; RangeEntry = CliDB.SpellRangeStorage.LookupByKey(_misc.RangeIndex); Speed = _misc.Speed; + LaunchDelay = _misc.LaunchDelay; SchoolMask = (SpellSchoolMask)_misc.SchoolMask; AttributesCu = 0; @@ -636,6 +637,11 @@ namespace Game.Spells return !(HasAttribute(SpellAttr1.NoThreat) || HasAttribute(SpellAttr3.NoInitialAggro)); } + public bool HasHitDelay() + { + return Speed > 0.0f || LaunchDelay > 0.0f; + } + public WeaponAttackType GetAttackType() { WeaponAttackType result; @@ -3751,6 +3757,7 @@ namespace Game.Spells public uint RangeIndex { get; set; } public SpellRangeRecord RangeEntry { get; set; } public float Speed { get; set; } + public float LaunchDelay { get; set; } public uint StackAmount { get; set; } public uint[] Totem = new uint[SpellConst.MaxTotems]; public int[] Reagent = new int[SpellConst.MaxReagents];