From 178f71c6dc0fe035c56db0d30d2cbd5b292bf830 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sat, 23 Jan 2021 16:40:53 -0800 Subject: [PATCH] chore(content): Cleans up abilities and some spell timers (#427) - [X] Exposes Index/Count on the timer. - [X] Cleans up abilities - [X] Combines some spell context/info objects with their timers to reduce allocations - [X] Fixes a bug where immolating weapon both finishes effect or stops in the wrong order due to a race condition. --- Projects/Server/Timer/Timer.cs | 12 +- .../Items/Weapons/Abilities/ArmorPierce.cs | 6 +- .../Items/Weapons/Abilities/BleedAttack.cs | 25 ++- .../Items/Weapons/Abilities/Block.cs | 32 +--- .../Items/Weapons/Abilities/DefenseMastery.cs | 17 +- .../Items/Weapons/Abilities/Disarm.cs | 14 +- .../Items/Weapons/Abilities/Dismount.cs | 7 +- .../Items/Weapons/Abilities/Disrobe.cs | 4 +- .../Items/Weapons/Abilities/DoubleShot.cs | 6 +- .../Items/Weapons/Abilities/DualWield.cs | 6 +- .../Items/Weapons/Abilities/Feint.cs | 6 +- .../Weapons/Abilities/FrenziedWhirlwind.cs | 8 +- .../Weapons/Abilities/InfectiousStrike.cs | 12 +- .../Items/Weapons/Abilities/MortalStrike.cs | 26 +-- .../Items/Weapons/Abilities/NerveStrike.cs | 6 +- .../Items/Weapons/Abilities/ParalyzingBlow.cs | 26 +-- .../Items/Weapons/Abilities/RidingSwipe.cs | 6 +- .../Items/Weapons/Abilities/TalonStrike.cs | 28 ++- .../Items/Weapons/Abilities/WeaponAbility.cs | 24 +-- .../Weapons/Abilities/WhirlwindAttack.cs | 7 +- Projects/UOContent/Spells/Base/Spell.cs | 4 +- .../UOContent/Spells/Bushido/CounterAttack.cs | 21 +-- .../Spells/Bushido/HonorableExecution.cs | 66 ++++--- Projects/UOContent/Spells/Fifth/Incognito.cs | 94 ++++------ .../Spells/Mysticism/SpellPlagueSpell.cs | 91 +++++---- .../UOContent/Spells/Necromancy/MindRot.cs | 7 +- .../UOContent/Spells/Ninjitsu/DeathStrike.cs | 172 +++++++++--------- .../UOContent/Spells/Ninjitsu/KiAttack.cs | 51 ++---- .../UOContent/Spells/Seventh/Polymorph.cs | 140 +++++++------- .../UOContent/Spells/Sixth/Invisibility.cs | 29 +-- .../Spells/Spellweaving/EssenceOfWind.cs | 55 +++--- .../Spells/Spellweaving/GiftOfRenewal.cs | 69 +++---- .../Spells/Spellweaving/ImmolatingWeapon.cs | 69 ++++--- 33 files changed, 469 insertions(+), 677 deletions(-) diff --git a/Projects/Server/Timer/Timer.cs b/Projects/Server/Timer/Timer.cs index 59c8a4116..487aaa266 100644 --- a/Projects/Server/Timer/Timer.cs +++ b/Projects/Server/Timer/Timer.cs @@ -24,9 +24,7 @@ namespace Server private static readonly Queue m_Queue = new(); private static int m_QueueCountAtSlice; - private readonly int m_Count; private long m_Delay; - private int m_Index; private long m_Interval; private List m_List; private long m_Next; @@ -44,7 +42,7 @@ namespace Server { m_Delay = (long)delay.TotalMilliseconds; m_Interval = (long)interval.TotalMilliseconds; - m_Count = count; + Count = count; if (!m_PrioritySet) { @@ -94,6 +92,10 @@ namespace Server set => m_Interval = (long)value.TotalMilliseconds; } + public int Index { get; private set; } + + public int Count { get; } + public bool Running { get => m_Running; @@ -365,7 +367,7 @@ namespace Server if (tce.m_IsAdd) { timer.m_Next = curTicks + timer.m_Delay; - timer.m_Index = 0; + timer.Index = 0; } if (newIndex >= 0) @@ -425,7 +427,7 @@ namespace Server m_Queue.Enqueue(t); } - if (t.m_Count != 0 && ++t.m_Index >= t.m_Count) + if (t.Count != 0 && ++t.Index >= t.Count) { t.Stop(); } diff --git a/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs b/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs index 8d4203a18..448de8b64 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ArmorPierce.cs @@ -15,10 +15,8 @@ namespace Server.Items { if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) { - from.SendLocalizedMessage( - 1063347, - "50" - ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + from.SendLocalizedMessage(1063347, "50"); return false; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/BleedAttack.cs b/Projects/UOContent/Items/Weapons/Abilities/BleedAttack.cs index 3861f5ce8..122ac2c1c 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/BleedAttack.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/BleedAttack.cs @@ -14,7 +14,7 @@ namespace Server.Items /// public class BleedAttack : WeaponAbility { - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary _table = new(); public override int BaseMana => 30; @@ -46,9 +46,9 @@ namespace Server.Items defender.NonlocalOverheadMessage( MessageType.Regular, 0x21, - 1060758, + 1060758, // ~1_NAME~ is bleeding profusely defender.Name - ); // ~1_NAME~ is bleeding profusely + ); } defender.PlaySound(0x133); @@ -57,15 +57,15 @@ namespace Server.Items BeginBleed(defender, attacker); } - public static bool IsBleeding(Mobile m) => m_Table.ContainsKey(m); + public static bool IsBleeding(Mobile m) => _table.ContainsKey(m); public static void BeginBleed(Mobile m, Mobile from) { - m_Table.TryGetValue(m, out var t); - t?.Stop(); + _table.TryGetValue(m, out var timer); + timer?.Stop(); - m_Table[m] = t = new InternalTimer(from, m); - t.Start(); + _table[m] = timer = new InternalTimer(from, m); + timer.Start(); } public static void DoBleed(Mobile m, Mobile from, int level) @@ -93,7 +93,7 @@ namespace Server.Items public static void EndBleed(Mobile m, bool message) { - if (!m_Table.Remove(m, out var t)) + if (!_table.Remove(m, out var t)) { return; } @@ -110,9 +110,8 @@ namespace Server.Items { private readonly Mobile m_From; private readonly Mobile m_Mobile; - private int m_Count; - public InternalTimer(Mobile from, Mobile m) : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0)) + public InternalTimer(Mobile from, Mobile m) : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0), 5) { m_From = from; m_Mobile = m; @@ -121,9 +120,9 @@ namespace Server.Items protected override void OnTick() { - DoBleed(m_Mobile, m_From, 5 - m_Count); + DoBleed(m_Mobile, m_From, 5 - Index); - if (++m_Count == 5) + if (Index == 4) { EndBleed(m_Mobile, true); } diff --git a/Projects/UOContent/Items/Weapons/Abilities/Block.cs b/Projects/UOContent/Items/Weapons/Abilities/Block.cs index d23e6c462..02a3b50cc 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Block.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Block.cs @@ -8,7 +8,7 @@ namespace Server.Items /// public class Block : WeaponAbility { - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary _table = new(); public override int BaseMana => 30; @@ -16,10 +16,8 @@ namespace Server.Items { if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) { - from.SendLocalizedMessage( - 1063347, - "50" - ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + from.SendLocalizedMessage(1063347, "50"); return false; } @@ -50,7 +48,7 @@ namespace Server.Items public static bool GetBonus(Mobile targ, ref int bonus) { - if (!m_Table.TryGetValue(targ, out var info)) + if (!_table.TryGetValue(targ, out var info)) { return false; } @@ -62,35 +60,25 @@ namespace Server.Items public static void BeginBlock(Mobile m, int bonus) { EndBlock(m); - m_Table[m] = new BlockInfo(m, bonus); + _table[m] = new InternalTimer(m, bonus); } public static void EndBlock(Mobile m) { - if (m_Table.Remove(m, out var info)) + if (_table.Remove(m, out var timer)) { - info.m_Timer?.Stop(); - } - } - - private class BlockInfo - { - public readonly int m_Bonus; - public readonly Timer m_Timer; - - public BlockInfo(Mobile target, int bonus) - { - m_Bonus = bonus; - m_Timer = new InternalTimer(target); + timer?.Stop(); } } private class InternalTimer : Timer { private readonly Mobile m_Mobile; + public readonly int m_Bonus; - public InternalTimer(Mobile m) : base(TimeSpan.FromSeconds(6.0)) + public InternalTimer(Mobile m, int bonus) : base(TimeSpan.FromSeconds(6.0)) { + m_Bonus = bonus; m_Mobile = m; Priority = TimerPriority.TwoFiftyMS; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs b/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs index 7fa706493..e38229d50 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/DefenseMastery.cs @@ -9,8 +9,7 @@ namespace Server.Items /// public class DefenseMastery : WeaponAbility { - private static readonly Dictionary - m_Table = new(); + private static readonly Dictionary _table = new(); public override int BaseMana => 30; @@ -18,10 +17,8 @@ namespace Server.Items { if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) { - from.SendLocalizedMessage( - 1063347, - "50" - ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + from.SendLocalizedMessage(1063347, "50"); return false; } @@ -46,7 +43,7 @@ namespace Server.Items ((Math.Max(attacker.Skills.Bushido.Value, attacker.Skills.Ninjitsu.Value) - 50.0) / 70.0)); - if (m_Table.TryGetValue(attacker, out var info)) + if (_table.TryGetValue(attacker, out var info)) { EndDefense(info); } @@ -57,14 +54,14 @@ namespace Server.Items info = new DefenseMasteryInfo(attacker, 80 - modifier, mod); info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(3.0), EndDefense, info); - m_Table[attacker] = info; + _table[attacker] = info; attacker.Delta(MobileDelta.WeaponDamage); } public static bool GetMalus(Mobile targ, ref int damageMalus) { - if (!m_Table.TryGetValue(targ, out var info)) + if (!_table.TryGetValue(targ, out var info)) { return false; } @@ -84,7 +81,7 @@ namespace Server.Items // No message is sent to the player. - m_Table.Remove(info.m_From); + _table.Remove(info.m_From); info.m_From.Delta(MobileDelta.WeaponDamage); } diff --git a/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs b/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs index d3b348df5..e8a6c9c1f 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs @@ -17,17 +17,17 @@ namespace Server.Items { if (!base.CheckSkills( from )) return false; - + if (!(from.Weapon is Fists)) return true; - + Skill skill = from.Skills.ArmsLore; - + if (skill?.Base >= 80.0) return true; - + from.SendLocalizedMessage( 1061812 ); // You lack the required skill in armslore to perform that attack! - + return false; }*/ @@ -43,7 +43,7 @@ namespace Server.Items public override void OnHit(Mobile attacker, Mobile defender, int damage) { - if (!Validate(attacker)) + if (!Validate(attacker) || !CheckMana(attacker, true)) { return; } @@ -67,7 +67,7 @@ namespace Server.Items { attacker.SendLocalizedMessage(1060849); // Your target is already unarmed! } - else if (CheckMana(attacker, true)) + else { attacker.SendLocalizedMessage(1060092); // You disarm their weapon! defender.SendLocalizedMessage(1060093); // Your weapon has been disarmed! diff --git a/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs b/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs index 3c828e5a9..d1dbf5811 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Dismount.cs @@ -34,7 +34,7 @@ namespace Server.Items public override void OnHit(Mobile attacker, Mobile defender, int damage) { - if (!Validate(attacker)) + if (!Validate(attacker) || !CheckMana(attacker, true)) { return; } @@ -60,11 +60,6 @@ namespace Server.Items return; } - if (!CheckMana(attacker, true)) - { - return; - } - if (Core.ML && attacker is LesserHiryu && Utility.RandomDouble() <= 0.8) { return; // Lesser Hiryu have an 80% chance of missing this attack diff --git a/Projects/UOContent/Items/Weapons/Abilities/Disrobe.cs b/Projects/UOContent/Items/Weapons/Abilities/Disrobe.cs index 911bf1f88..fc4286c78 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Disrobe.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Disrobe.cs @@ -13,7 +13,7 @@ namespace Server.Items public override void OnHit(Mobile attacker, Mobile defender, int damage) { - if (!Validate(attacker)) + if (!Validate(attacker) || !CheckMana(attacker, true)) { return; } @@ -32,7 +32,7 @@ namespace Server.Items { attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent. } - else if (CheckMana(attacker, true)) + else { // attacker.SendLocalizedMessage( 1060092 ); // You disarm their weapon! defender.SendLocalizedMessage(1062002); // You can no longer wear your ~1_ARMOR~ diff --git a/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs b/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs index 3c9c062e4..a97ad27f0 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/DoubleShot.cs @@ -11,10 +11,8 @@ namespace Server.Items { if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) { - from.SendLocalizedMessage( - 1063347, - "50" - ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + from.SendLocalizedMessage(1063347, "50"); return false; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs b/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs index 4203318f8..718ea8bd4 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/DualWield.cs @@ -16,10 +16,8 @@ namespace Server.Items { if (GetSkill(from, SkillName.Ninjitsu) < 50.0) { - from.SendLocalizedMessage( - 1063352, - "50" - ); // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack! + // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack! + from.SendLocalizedMessage(1063352, "50"); return false; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/Feint.cs b/Projects/UOContent/Items/Weapons/Abilities/Feint.cs index 93a30afe0..338b3ed84 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Feint.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Feint.cs @@ -16,10 +16,8 @@ namespace Server.Items { if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) { - from.SendLocalizedMessage( - 1063347, - "50" - ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + from.SendLocalizedMessage(1063347, "50"); return false; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs b/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs index 2ec584973..a15685d68 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs @@ -19,10 +19,8 @@ namespace Server.Items { if (GetSkill(from, SkillName.Ninjitsu) < 50.0 && GetSkill(from, SkillName.Bushido) < 50.0) { - from.SendLocalizedMessage( - 1063347, - "50" - ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + from.SendLocalizedMessage(1063347, "50"); return false; } @@ -31,7 +29,7 @@ namespace Server.Items public override void OnHit(Mobile attacker, Mobile defender, int damage) { - if (!Validate(attacker)) // Mana check after check that there are targets + if (!Validate(attacker) || !CheckMana(attacker, true)) { return; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs index 1b64f1d0a..ce155ce09 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs @@ -23,7 +23,7 @@ namespace Server.Items public override void OnHit(Mobile attacker, Mobile defender, int damage) { - if (!Validate(attacker)) + if (!Validate(attacker) || !CheckMana(attacker, true)) { return; } @@ -39,14 +39,8 @@ namespace Server.Items if (p == null || weapon.PoisonCharges <= 0) { - attacker.SendLocalizedMessage( - 1061141 - ); // Your weapon must have a dose of poison to perform an infectious strike! - return; - } - - if (!CheckMana(attacker, true)) - { + // Your weapon must have a dose of poison to perform an infectious strike! + attacker.SendLocalizedMessage(1061141); return; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs index babf26573..c8cfb2114 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/MortalStrike.cs @@ -13,7 +13,7 @@ namespace Server.Items public static readonly TimeSpan PlayerDuration = TimeSpan.FromSeconds(6.0); public static readonly TimeSpan NPCDuration = TimeSpan.FromSeconds(12.0); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary _table = new(); public override int BaseMana => 30; @@ -39,16 +39,16 @@ namespace Server.Items } } - public static bool IsWounded(Mobile m) => m_Table.ContainsKey(m); + public static bool IsWounded(Mobile m) => _table.ContainsKey(m); public static void BeginWound(Mobile m, TimeSpan duration) { - if (m_Table.TryGetValue(m, out var timer)) + if (_table.TryGetValue(m, out var timer)) { timer?.Stop(); } - m_Table[m] = timer = new InternalTimer(m, duration); + _table[m] = timer = Timer.DelayCall(duration, EndWound, m); timer.Start(); m.YellowHealthbar = true; @@ -56,7 +56,7 @@ namespace Server.Items public static void EndWound(Mobile m) { - if (m_Table.Remove(m, out var timer)) + if (_table.Remove(m, out var timer)) { timer.Stop(); } @@ -64,21 +64,5 @@ namespace Server.Items m.YellowHealthbar = false; m.SendLocalizedMessage(1060208); // You are no longer mortally wounded. } - - private class InternalTimer : Timer - { - private readonly Mobile m_Mobile; - - public InternalTimer(Mobile m, TimeSpan duration) : base(duration) - { - m_Mobile = m; - Priority = TimerPriority.TwoFiftyMS; - } - - protected override void OnTick() - { - EndWound(m_Mobile); - } - } } } diff --git a/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs index 6e9411009..cead5302b 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/NerveStrike.cs @@ -13,10 +13,8 @@ namespace Server.Items { if (GetSkill(from, SkillName.Bushido) < 50.0) { - from.SendLocalizedMessage( - 1070768, - "50" - ); // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! + // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! + from.SendLocalizedMessage(1070768, "50"); return false; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs b/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs index f82f56e3b..7c5f28e45 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ParalyzingBlow.cs @@ -13,7 +13,7 @@ namespace Server.Items public static readonly TimeSpan FreezeDelayDuration = TimeSpan.FromSeconds(8.0); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary _table = new(); public override int BaseMana => 30; @@ -80,41 +80,25 @@ namespace Server.Items BeginImmunity(defender, duration + FreezeDelayDuration); } - public static bool IsImmune(Mobile m) => m_Table.ContainsKey(m); + public static bool IsImmune(Mobile m) => _table.ContainsKey(m); public static void BeginImmunity(Mobile m, TimeSpan duration) { - if (m_Table.TryGetValue(m, out var timer)) + if (_table.TryGetValue(m, out var timer)) { timer?.Stop(); } - m_Table[m] = timer = new InternalTimer(m, duration); + _table[m] = timer = Timer.DelayCall(duration, EndImmunity, m); timer.Start(); } public static void EndImmunity(Mobile m) { - if (m_Table.Remove(m, out var timer)) + if (_table.Remove(m, out var timer)) { timer?.Stop(); } } - - private class InternalTimer : Timer - { - private readonly Mobile m_Mobile; - - public InternalTimer(Mobile m, TimeSpan duration) : base(duration) - { - m_Mobile = m; - Priority = TimerPriority.TwoFiftyMS; - } - - protected override void OnTick() - { - EndImmunity(m_Mobile); - } - } } } diff --git a/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs b/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs index 24b7d682a..bd880c6b9 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/RidingSwipe.cs @@ -18,10 +18,8 @@ namespace Server.Items { if (GetSkill(from, SkillName.Bushido) < 50.0) { - from.SendLocalizedMessage( - 1070768, - "50" - ); // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! + // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! + from.SendLocalizedMessage(1070768, "50"); return false; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs index 265f65835..51b32dad3 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/TalonStrike.cs @@ -8,7 +8,7 @@ namespace Server.Items /// public class TalonStrike : WeaponAbility { - private static readonly HashSet m_Table = new(); + private static readonly HashSet _defenders = new(); public override int BaseMana => 30; public override double DamageScalar => 1.2; @@ -17,10 +17,8 @@ namespace Server.Items { if (GetSkill(from, SkillName.Ninjitsu) < 50.0) { - from.SendLocalizedMessage( - 1063352, - "50" - ); // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack! + // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack! + from.SendLocalizedMessage(1063352, "50"); return false; } @@ -29,7 +27,7 @@ namespace Server.Items public override void OnHit(Mobile attacker, Mobile defender, int damage) { - if (m_Table.Contains(defender) || !Validate(attacker) || !CheckMana(attacker, true)) + if (_defenders.Contains(defender) || !Validate(attacker) || !CheckMana(attacker, true)) { return; } @@ -48,7 +46,7 @@ namespace Server.Items timer.Start(); - m_Table.Add(defender); + _defenders.Add(defender); } private class InternalTimer : Timer @@ -58,12 +56,12 @@ namespace Server.Items private double m_DamageRemaining; private double m_DamageToDo; - public InternalTimer(Mobile defender, int totalDamage) - : base( - TimeSpan.Zero, - TimeSpan.FromSeconds(0.25), - 12 - ) // 3 seconds at .25 seconds apart = 12. Confirm delay inbetween of .25 each. + // 3 seconds at 0.25 seconds apart = 12. Confirm delay in between of 0.25 each. + public InternalTimer(Mobile defender, int totalDamage) : base( + TimeSpan.Zero, + TimeSpan.FromSeconds(0.25), + 12 + ) { m_Defender = defender; m_DamageRemaining = totalDamage; @@ -77,7 +75,7 @@ namespace Server.Items if (!m_Defender.Alive || m_DamageRemaining <= 0) { Stop(); - m_Table.Remove(m_Defender); + _defenders.Remove(m_Defender); return; } @@ -101,7 +99,7 @@ namespace Server.Items if (!m_Defender.Alive || m_DamageRemaining <= 0) { Stop(); - m_Table.Remove(m_Defender); + _defenders.Remove(m_Defender); } } } diff --git a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs index 7c93af3d6..81d142653 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs @@ -185,10 +185,8 @@ namespace Server.Items if (Core.ML && reqTactics && from.Skills.Tactics.Base < reqSkill) { - from.SendLocalizedMessage( - 1079308, - reqSkill.ToString() - ); // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack + // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack + from.SendLocalizedMessage(1079308, reqSkill.ToString()); return false; } @@ -208,17 +206,13 @@ namespace Server.Items if (reqTactics) { - from.SendLocalizedMessage( - 1079308, - reqSkill.ToString() - ); // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack + // You need ~1_SKILL_REQUIREMENT~ weapon and tactics skill to perform that attack + from.SendLocalizedMessage(1079308, reqSkill.ToString()); } else { - from.SendLocalizedMessage( - 1060182, - reqSkill.ToString() - ); // You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack + // You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack + from.SendLocalizedMessage(1060182, reqSkill.ToString()); } return false; @@ -239,10 +233,8 @@ namespace Server.Items return true; } - from.SendLocalizedMessage( - 1060181, - mana.ToString() - ); // You need ~1_MANA_REQUIREMENT~ mana to perform that attack + // You need ~1_MANA_REQUIREMENT~ mana to perform that attack + from.SendLocalizedMessage(1060181, mana.ToString()); return false; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs b/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs index b17542bf8..316ae4c53 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs @@ -14,7 +14,7 @@ namespace Server.Items public override void OnHit(Mobile attacker, Mobile defender, int damage) { - if (!Validate(attacker)) + if (!Validate(attacker) || !CheckMana(attacker, true)) { return; } @@ -33,11 +33,6 @@ namespace Server.Items return; } - if (!CheckMana(attacker, true)) - { - return; - } - attacker.FixedEffect(0x3728, 10, 15); attacker.PlaySound(0x2A1); diff --git a/Projects/UOContent/Spells/Base/Spell.cs b/Projects/UOContent/Spells/Base/Spell.cs index 9f8700807..e4459ad1b 100644 --- a/Projects/UOContent/Spells/Base/Spell.cs +++ b/Projects/UOContent/Spells/Base/Spell.cs @@ -838,10 +838,9 @@ namespace Server.Spells public void Add(Mobile m, Timer t) { - if (m_Contexts.TryGetValue(m, out var oldTimer)) + if (m_Contexts.Remove(m, out var oldTimer)) { oldTimer.Stop(); - m_Contexts.Remove(m); } m_Contexts.Add(m, t); @@ -850,6 +849,7 @@ namespace Server.Spells public void Remove(Mobile m) { m_Contexts.Remove(m); + // TODO: Should we stop the timer? } } diff --git a/Projects/UOContent/Spells/Bushido/CounterAttack.cs b/Projects/UOContent/Spells/Bushido/CounterAttack.cs index e4069f398..a4db6ff37 100644 --- a/Projects/UOContent/Spells/Bushido/CounterAttack.cs +++ b/Projects/UOContent/Spells/Bushido/CounterAttack.cs @@ -78,9 +78,7 @@ namespace Server.Spells.Bushido m_Table.TryGetValue(m, out var timer); timer?.Stop(); - m_Table[m] = timer = new InternalTimer(m); - - timer.Start(); + m_Table[m] = Timer.DelayCall(TimeSpan.FromSeconds(30.0), EndCountering, m); } public static void StopCountering(Mobile m) @@ -93,21 +91,10 @@ namespace Server.Spells.Bushido OnEffectEnd(m, typeof(CounterAttack)); } - private class InternalTimer : Timer + private static void EndCountering(Mobile m) { - private readonly Mobile m_Mobile; - - public InternalTimer(Mobile m) : base(TimeSpan.FromSeconds(30.0)) - { - m_Mobile = m; - Priority = TimerPriority.TwoFiftyMS; - } - - protected override void OnTick() - { - StopCountering(m_Mobile); - m_Mobile.SendLocalizedMessage(1063119); // You return to your normal stance. - } + StopCountering(m); + m.SendLocalizedMessage(1063119); // You return to your normal stance. } } } diff --git a/Projects/UOContent/Spells/Bushido/HonorableExecution.cs b/Projects/UOContent/Spells/Bushido/HonorableExecution.cs index d7b9adfa2..e5ec28dbc 100644 --- a/Projects/UOContent/Spells/Bushido/HonorableExecution.cs +++ b/Projects/UOContent/Spells/Bushido/HonorableExecution.cs @@ -5,8 +5,7 @@ namespace Server.Spells.Bushido { public class HonorableExecution : SamuraiMove { - private static readonly Dictionary m_Table = - new(); + private static readonly Dictionary m_Table = new(); public override int BaseMana => 0; public override double RequiredSkill => 25.0; @@ -14,13 +13,9 @@ namespace Server.Spells.Bushido public override TextDefinition AbilityMessage => new(1063122); // You better kill your enemy with your next hit or you'll be rather sorry... - public override double GetDamageScalar(Mobile attacker, Mobile defender) - { - var bushido = attacker.Skills.Bushido.Value; - + public override double GetDamageScalar(Mobile attacker, Mobile defender) => // TODO: 20 -> Perfection - return 1.0 + bushido * 20 / 10000; - } + 1.0 + attacker.Skills.Bushido.Value * 20 / 10000; public override void OnHit(Mobile attacker, Mobile defender, int damage) { @@ -30,27 +25,20 @@ namespace Server.Spells.Bushido } ClearCurrentMove(attacker); - - if (m_Table.TryGetValue(attacker, out var info)) - { - info.Clear(); - info.m_Timer?.Stop(); - } + RemovePenalty(attacker); if (!defender.Alive) { attacker.FixedParticles(0x373A, 1, 17, 0x7E2, EffectLayer.Waist); var bushido = attacker.Skills.Bushido.Value; + bushido *= bushido; - attacker.Hits += 20 + (int)(bushido * bushido / 480.0); + attacker.Hits += 20 + (int)(bushido / 480.0); - var swingBonus = Math.Max((int)(bushido * bushido / 720.0), 1); + var swingBonus = Math.Max(1, (int)(bushido / 720.0)); - info = new HonorableExecutionInfo(attacker, swingBonus); - info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(20.0), RemovePenalty, info.m_Mobile); - - m_Table[attacker] = info; + m_Table[attacker] = new HonorableExecutionTimer(attacker, swingBonus); } else { @@ -70,12 +58,10 @@ namespace Server.Spells.Bushido mods.Add(new DefaultSkillMod(SkillName.MagicResist, true, -resSpells)); } - info = new HonorableExecutionInfo(attacker, mods); - info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(7.0), RemovePenalty, info.m_Mobile); - - m_Table[attacker] = info; + m_Table[attacker] = new HonorableExecutionTimer(attacker, mods); } + attacker.Delta(MobileDelta.WeaponDamage); CheckGain(attacker); } @@ -85,28 +71,30 @@ namespace Server.Spells.Bushido public static void RemovePenalty(Mobile target) { - if (!m_Table.Remove(target, out var info) || !info.m_Penalty) + if (m_Table.Remove(target, out var timer)) { - return; + timer.Clear(); } - - info.Clear(); - info.m_Timer?.Stop(); } - private class HonorableExecutionInfo + private class HonorableExecutionTimer : Timer { public readonly Mobile m_Mobile; public readonly List m_Mods; public readonly bool m_Penalty; public readonly int m_SwingBonus; - public Timer m_Timer; - public HonorableExecutionInfo(Mobile from, List mods) : this(from, 0, mods, mods != null) + public HonorableExecutionTimer(Mobile from, List mods) : this(TimeSpan.FromSeconds(7.0), from, 0, mods, mods != null) { } - public HonorableExecutionInfo(Mobile from, int swingBonus, List mods = null, bool penalty = false) + public HonorableExecutionTimer(Mobile from, int swingBonus) : this(TimeSpan.FromSeconds(20.0), from, swingBonus) + { + } + + public HonorableExecutionTimer( + TimeSpan duration, Mobile from, int swingBonus, List mods = null, bool penalty = false) + : base(duration) { m_Mobile = from; m_SwingBonus = swingBonus; @@ -116,6 +104,12 @@ namespace Server.Spells.Bushido Apply(); } + protected override void OnTick() + { + m_Mobile?.Delta(MobileDelta.WeaponDamage); + RemovePenalty(m_Mobile); + } + public void Apply() { if (m_Mods == null) @@ -140,6 +134,8 @@ namespace Server.Spells.Bushido public void Clear() { + Stop(); + if (m_Mods == null) { return; @@ -151,11 +147,11 @@ namespace Server.Spells.Bushido if (mod is ResistanceMod resistanceMod) { - m_Mobile.RemoveResistanceMod(resistanceMod); + m_Mobile?.RemoveResistanceMod(resistanceMod); } else if (mod is SkillMod skillMod) { - m_Mobile.RemoveSkillMod(skillMod); + m_Mobile?.RemoveSkillMod(skillMod); } } } diff --git a/Projects/UOContent/Spells/Fifth/Incognito.cs b/Projects/UOContent/Spells/Fifth/Incognito.cs index 9e7ecaaa8..ed67b9efa 100644 --- a/Projects/UOContent/Spells/Fifth/Incognito.cs +++ b/Projects/UOContent/Spells/Fifth/Incognito.cs @@ -19,7 +19,7 @@ namespace Server.Spells.Fifth Reagent.Nightshade ); - private static readonly Dictionary m_Timers = new(); + private static readonly Dictionary m_Table = new(); public IncognitoSpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info) { @@ -55,24 +55,34 @@ namespace Server.Spells.Fifth if (Sigil.ExistsOn(Caster)) { Caster.SendLocalizedMessage(1010445); // You cannot incognito if you have a sigil + return; } - else if (!Caster.CanBeginAction()) + + if (!Caster.CanBeginAction()) { Caster.SendLocalizedMessage(1005559); // This spell is already in effect. + return; } - else if (Caster.BodyMod == 183 || Caster.BodyMod == 184) + + if (Caster.BodyMod == 183 || Caster.BodyMod == 184) { Caster.SendLocalizedMessage(1042402); // You cannot use incognito while wearing body paint + return; } - else if (DisguiseTimers.IsDisguised(Caster)) + + if (DisguiseTimers.IsDisguised(Caster)) { Caster.SendLocalizedMessage(1061631); // You can't do that while disguised. + return; } - else if (!Caster.CanBeginAction() || Caster.IsBodyMod) + + if (!Caster.CanBeginAction() || Caster.IsBodyMod) { DoFizzle(); + return; } - else if (CheckSequence()) + + if (CheckSequence()) { if (Caster.BeginAction()) { @@ -98,19 +108,10 @@ namespace Server.Spells.Fifth StopTimer(Caster); - var timeVal = 6 * Caster.Skills.Magery.Fixed / 50 + 1; - - if (timeVal > 144) - { - timeVal = 144; - } + var timeVal = Math.Min(6 * Caster.Skills.Magery.Fixed / 50 + 1, 144); var length = TimeSpan.FromSeconds(timeVal); - - var t = new InternalTimer(Caster, length); - m_Timers[Caster] = t; - - t.Start(); + m_Table[Caster] = Timer.DelayCall(length, EndIncognito, Caster); BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.Incognito, 1075819, length, Caster)); } @@ -125,52 +126,31 @@ namespace Server.Spells.Fifth public static void StopTimer(Mobile m) { - if (!m_Timers.TryGetValue(m, out var t)) + if (m_Table.Remove(m, out var t)) + { + t.Stop(); + + } + + BuffInfo.RemoveBuff(m, BuffIcon.Incognito); + } + + private static void EndIncognito(Mobile m) + { + if (m.CanBeginAction()) { return; } - t.Stop(); - m_Timers.Remove(m); - BuffInfo.RemoveBuff(m, BuffIcon.Incognito); - } + (m as PlayerMobile)?.SetHairMods(-1, -1); - private class InternalTimer : Timer - { - private readonly Mobile m_Owner; + m.BodyMod = 0; + m.HueMod = -1; + m.NameMod = null; + m.EndAction(); - public InternalTimer(Mobile owner, TimeSpan length) : base(length) - { - m_Owner = owner; - - /* - int val = ((6 * owner.Skills.Magery.Fixed) / 50) + 1; - - if (val > 144) - val = 144; - - Delay = TimeSpan.FromSeconds( val ); - * */ - Priority = TimerPriority.OneSecond; - } - - protected override void OnTick() - { - if (m_Owner.CanBeginAction()) - { - return; - } - - (m_Owner as PlayerMobile)?.SetHairMods(-1, -1); - - m_Owner.BodyMod = 0; - m_Owner.HueMod = -1; - m_Owner.NameMod = null; - m_Owner.EndAction(); - - BaseArmor.ValidateMobile(m_Owner); - BaseClothing.ValidateMobile(m_Owner); - } + BaseArmor.ValidateMobile(m); + BaseClothing.ValidateMobile(m); } } } diff --git a/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs b/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs index 5b6d241aa..8daac52e0 100644 --- a/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs +++ b/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs @@ -17,8 +17,7 @@ namespace Server.Spells.Mysticism Reagent.SulfurousAsh ); - private static readonly Dictionary - m_Table = new(); + private static readonly Dictionary m_Table = new(); public SpellPlagueSpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info) @@ -67,16 +66,16 @@ namespace Server.Spells.Mysticism var damage = GetNewAosDamage(33, 1, 5, targeted); SpellHelper.Damage(this, targeted, damage, 0, 0, 0, 0, 0); - var context = new SpellPlagueContext(this, targeted); + var timer = new SpellPlagueTimer(this, targeted); - if (m_Table.TryGetValue(targeted, out var oldContext)) + if (m_Table.TryGetValue(targeted, out var oldtimer)) { - oldContext.SetNext(context); + oldtimer.SetNext(timer); } else { - m_Table[targeted] = context; - context.Start(); + m_Table[targeted] = timer; + timer.StartPlague(); } } @@ -114,94 +113,86 @@ namespace Server.Spells.Mysticism to.FixedParticles(0x3779, 1, 15, 0x251E, 0x43, 7, EffectLayer.Head, 0); } - private class SpellPlagueContext + private class SpellPlagueTimer : Timer { private readonly SpellPlagueSpell m_Owner; private readonly Mobile m_Target; private int m_Explosions; private DateTime m_LastExploded; - private SpellPlagueContext m_Next; - private Timer m_Timer; + private SpellPlagueTimer m_Next; - public SpellPlagueContext(SpellPlagueSpell owner, Mobile target) + public SpellPlagueTimer(SpellPlagueSpell owner, Mobile target) : base(TimeSpan.FromSeconds(8.0)) { m_Owner = owner; m_Target = target; } - public void SetNext(SpellPlagueContext context) + public void SetNext(SpellPlagueTimer timer) { if (m_Next == null) { - m_Next = context; + m_Next = timer; } else { - m_Next.SetNext(context); + m_Next.SetNext(timer); } } - public void Start() + public void StartPlague() { - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(8.0), EndPlague); - m_Timer.Start(); - BuffInfo.AddBuff( m_Target, new BuffInfo(BuffIcon.SpellPlague, 1031690, 1080167, TimeSpan.FromSeconds(8.5), m_Target) ); + + Start(); } public void OnDamage() { - if (DateTime.Now > m_LastExploded + TimeSpan.FromSeconds(2.0)) + if (DateTime.Now <= m_LastExploded + TimeSpan.FromSeconds(2.0)) { - var exploChance = 90 - m_Explosions * 30; + return; + } - var resist = m_Target.Skills.MagicResist.Value; + var exploChance = 90 - m_Explosions * 30; - if (resist >= 70) + var resist = m_Target.Skills.MagicResist.Value; + + if (resist >= 70) + { + exploChance -= (int)((resist - 70.0) * 3.0 / 10.0); + } + + if (exploChance > Utility.Random(100)) + { + m_Owner.VisualEffect(m_Target); + + var damage = m_Owner.GetNewAosDamage(15 + m_Explosions * 3, 1, 5, m_Target); + + m_Explosions++; + m_LastExploded = DateTime.Now; + + SpellHelper.Damage(m_Owner, m_Target, damage, 0, 0, 0, 0, 0, 100); + + if (m_Explosions >= 3) { - exploChance -= (int)((resist - 70.0) * 3.0 / 10.0); - } - - if (exploChance > Utility.Random(100)) - { - m_Owner.VisualEffect(m_Target); - - var damage = m_Owner.GetNewAosDamage(15 + m_Explosions * 3, 1, 5, m_Target); - - m_Explosions++; - m_LastExploded = DateTime.Now; - - SpellHelper.Damage(m_Owner, m_Target, damage, 0, 0, 0, 0, 0, 100); - - if (m_Explosions >= 3) - { - EndPlague(); - } + EndPlague(); } } } - private void EndPlague() + public void EndPlague(bool restart = true) { - EndPlague(true); - } - - public void EndPlague(bool restart) - { - m_Timer?.Stop(); - if (restart && m_Next != null) { m_Table[m_Target] = m_Next; - m_Next.Start(); + m_Next.StartPlague(); } else { m_Table.Remove(m_Target); - BuffInfo.RemoveBuff(m_Target, BuffIcon.SpellPlague); } } diff --git a/Projects/UOContent/Spells/Necromancy/MindRot.cs b/Projects/UOContent/Spells/Necromancy/MindRot.cs index 946cba8b0..9e2585965 100644 --- a/Projects/UOContent/Spells/Necromancy/MindRot.cs +++ b/Projects/UOContent/Spells/Necromancy/MindRot.cs @@ -73,14 +73,13 @@ namespace Server.Spells.Necromancy public static void ClearMindRotScalar(Mobile m) { - if (!m_Table.Remove(m, out var tmpB)) + if (m_Table.Remove(m, out var tmpB)) { - return; + tmpB.m_MRExpireTimer.Stop(); + m.SendLocalizedMessage(1060872); // Your mind feels normal again. } BuffInfo.RemoveBuff(m, BuffIcon.Mindrot); - tmpB.m_MRExpireTimer.Stop(); - m.SendLocalizedMessage(1060872); // Your mind feels normal again. } public static bool HasMindRotScalar(Mobile m) => m_Table.ContainsKey(m); diff --git a/Projects/UOContent/Spells/Ninjitsu/DeathStrike.cs b/Projects/UOContent/Spells/Ninjitsu/DeathStrike.cs index 653575d19..dab80c215 100644 --- a/Projects/UOContent/Spells/Ninjitsu/DeathStrike.cs +++ b/Projects/UOContent/Spells/Ninjitsu/DeathStrike.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Server.Items; using Server.SkillHandlers; @@ -7,7 +8,7 @@ namespace Server.Spells.Ninjitsu { public class DeathStrike : NinjaMove { - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); public override int BaseMana => 30; public override double RequiredSkill => 85.0; @@ -50,16 +51,16 @@ namespace Server.Spells.Ninjitsu var damageBonus = 0; - if (m_Table.Remove(defender, out var info)) + if (m_Table.Remove(defender, out var timer)) { defender.SendLocalizedMessage(1063092); // Your opponent lands another Death Strike! - if (info.m_Steps > 0) + if (timer.Steps > 0) { damageBonus = attacker.Skills.Ninjitsu.Fixed / 150; } - info.m_Timer?.Stop(); + timer.Stop(); } else { @@ -71,113 +72,108 @@ namespace Server.Spells.Ninjitsu defender.FixedParticles(0x374A, 1, 17, 0x26BC, EffectLayer.Waist); attacker.PlaySound(attacker.Female ? 0x50D : 0x50E); - info = new DeathStrikeInfo(defender, attacker, damageBonus, isRanged) - { - m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), ProcessDeathStrike, defender) - }; + var t = new DeathStrikeTimer(defender, attacker, damageBonus, isRanged); - m_Table[defender] = info; + m_Table[defender] = t; + + t.Start(); CheckGain(attacker); } public static void AddStep(Mobile m) { - if (m_Table.TryGetValue(m, out var info) && ++info.m_Steps >= 5) + if (m_Table.TryGetValue(m, out var timer) && ++timer.Steps >= 5) { - ProcessDeathStrike(m); + timer.ProcessDeathStrike(); } } - private static void ProcessDeathStrike(Mobile defender) - { - if (!m_Table.Remove(defender, out var info)) - { - return; - } - - int damage; - - var ninjitsu = info.m_Attacker.Skills.Ninjitsu.Value; - var stalkingBonus = Tracking.GetStalkingBonus(info.m_Attacker, info.m_Target); - - if (Core.ML) - { - var scalar = (info.m_Attacker.Skills.Hiding.Value + - info.m_Attacker.Skills.Stealth.Value) / 220; - - if (scalar > 1) - { - scalar = 1; - } - - // New formula doesn't apply DamageBonus anymore, caps must be, directly, 60/30. - if (info.m_Steps >= 5) - { - damage = (int)Math.Floor(Math.Min(60, ninjitsu / 3 * (0.3 + 0.7 * scalar) + stalkingBonus)); - } - else - { - damage = (int)Math.Floor(Math.Min(30, ninjitsu / 9 * (0.3 + 0.7 * scalar) + stalkingBonus)); - } - - if (info.m_isRanged) - { - damage /= 2; - } - } - else - { - var divisor = info.m_Steps >= 5 ? 30 : 80; - var baseDamage = ninjitsu / divisor * 10; - - var maxDamage = info.m_Steps >= 5 ? 62 : 22; - damage = Math.Clamp((int)(baseDamage + stalkingBonus), 0, maxDamage) + info.m_DamageBonus; - } - - if (Core.ML) - { - info.m_Target.Damage(damage, info.m_Attacker); // Damage is direct. - } - else - { - AOS.Damage( - info.m_Target, - info.m_Attacker, - damage, - true, - 100, - 0, - 0, - 0, - 0, - 0, - 0, - false, - false, - true - ); // Damage is physical. - } - - info.m_Timer?.Stop(); - } - - private class DeathStrikeInfo + private class DeathStrikeTimer : Timer { public readonly Mobile m_Attacker; public readonly int m_DamageBonus; public readonly bool m_isRanged; public readonly Mobile m_Target; - public int m_Steps; - public Timer m_Timer; + public int Steps { get; set; } - public DeathStrikeInfo(Mobile target, Mobile attacker, int damageBonus, bool isRanged) + internal DeathStrikeTimer(Mobile target, Mobile attacker, int damageBonus, bool isRanged) + : base(TimeSpan.FromSeconds(5.0)) { m_Target = target; m_Attacker = attacker; m_DamageBonus = damageBonus; m_isRanged = isRanged; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void OnTick() + { + ProcessDeathStrike(); + } + + public void ProcessDeathStrike() + { + int damage; + + var ninjitsu = m_Attacker.Skills.Ninjitsu.Value; + var stalkingBonus = Tracking.GetStalkingBonus(m_Attacker, m_Target); + + if (Core.ML) + { + var scalar = Math.Min(1, (m_Attacker.Skills.Hiding.Value + + m_Attacker.Skills.Stealth.Value) / 220); + + // New formula doesn't apply DamageBonus anymore, caps must be, directly, 60/30. + if (Steps >= 5) + { + damage = (int)Math.Floor(Math.Min(60, ninjitsu / 3 * (0.3 + 0.7 * scalar) + stalkingBonus)); + } + else + { + damage = (int)Math.Floor(Math.Min(30, ninjitsu / 9 * (0.3 + 0.7 * scalar) + stalkingBonus)); + } + + if (m_isRanged) + { + damage /= 2; + } + } + else + { + var divisor = Steps >= 5 ? 30 : 80; + var baseDamage = ninjitsu / divisor * 10; + + var maxDamage = Steps >= 5 ? 62 : 22; + damage = Math.Clamp((int)(baseDamage + stalkingBonus), 0, maxDamage) + m_DamageBonus; + } + + if (Core.ML) + { + m_Target.Damage(damage, m_Attacker); // Damage is direct. + } + else + { + AOS.Damage( + m_Target, + m_Attacker, + damage, + true, + 100, + 0, + 0, + 0, + 0, + 0, + 0, + false, + false, + true + ); // Damage is physical. + } + + Stop(); + } } } } diff --git a/Projects/UOContent/Spells/Ninjitsu/KiAttack.cs b/Projects/UOContent/Spells/Ninjitsu/KiAttack.cs index 6981e101c..c957c7ae0 100644 --- a/Projects/UOContent/Spells/Ninjitsu/KiAttack.cs +++ b/Projects/UOContent/Spells/Ninjitsu/KiAttack.cs @@ -6,7 +6,7 @@ namespace Server.Spells.Ninjitsu { public class KiAttack : NinjaMove { - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); public override int BaseMana => 25; public override double RequiredSkill => 80.0; @@ -21,10 +21,9 @@ namespace Server.Spells.Ninjitsu return; } - var info = new KiAttackInfo(from); - info.m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(2.0), EndKiAttack, info); - - m_Table[from] = info; + var t = new KiAttackTimer(from); + m_Table[from] = t; + t.Start(); } public override bool Validate(Mobile from) @@ -87,53 +86,43 @@ namespace Server.Spells.Ninjitsu public override void OnClearMove(Mobile from) { - if (m_Table.Remove(from, out var info)) + if (m_Table.Remove(from, out var t)) { - info.m_Timer.Stop(); + t.Stop(); } } public static double GetBonus(Mobile from) { - if (!m_Table.TryGetValue(from, out var info)) + if (!m_Table.TryGetValue(from, out var t)) { return 0; } - var xDelta = info.m_Location.X - from.X; - var yDelta = info.m_Location.Y - from.Y; + var xDelta = t.m_Location.X - from.X; + var yDelta = t.m_Location.Y - from.Y; - var bonus = Math.Sqrt(xDelta * xDelta + yDelta * yDelta); - - if (bonus > 20.0) - { - bonus = 20.0; - } - - return bonus; + return Math.Min(Math.Sqrt(xDelta * xDelta + yDelta * yDelta), 20.0); } - private static void EndKiAttack(KiAttackInfo info) - { - info.m_Timer?.Stop(); - - ClearCurrentMove(info.m_Mobile); - info.m_Mobile.SendLocalizedMessage(1063102); // You failed to complete your Ki Attack in time. - - m_Table.Remove(info.m_Mobile); - } - - private class KiAttackInfo + private class KiAttackTimer : Timer { public readonly Mobile m_Mobile; public Point3D m_Location; - public Timer m_Timer; - public KiAttackInfo(Mobile m) + public KiAttackTimer(Mobile m) : base(TimeSpan.FromSeconds(2.0)) { m_Mobile = m; m_Location = m.Location; } + + protected override void OnTick() + { + ClearCurrentMove(m_Mobile); + m_Mobile.SendLocalizedMessage(1063102); // You failed to complete your Ki Attack in time. + + m_Table.Remove(m_Mobile); + } } } } diff --git a/Projects/UOContent/Spells/Seventh/Polymorph.cs b/Projects/UOContent/Spells/Seventh/Polymorph.cs index 349945c79..70b16f03e 100644 --- a/Projects/UOContent/Spells/Seventh/Polymorph.cs +++ b/Projects/UOContent/Spells/Seventh/Polymorph.cs @@ -19,7 +19,7 @@ namespace Server.Spells.Seventh Reagent.MandrakeRoot ); - private static readonly Dictionary m_Timers = new(); + private static readonly Dictionary m_Table = new(); private readonly int m_NewBody; @@ -29,45 +29,47 @@ namespace Server.Spells.Seventh public override bool CheckCast() { - /*if (Caster.Mounted) + var caster = Caster; + + /*if (caster.Mounted) { - Caster.SendLocalizedMessage( 1042561 ); //Please dismount first. + caster.SendLocalizedMessage( 1042561 ); //Please dismount first. return false; } else */ - if (Sigil.ExistsOn(Caster)) + if (Sigil.ExistsOn(caster)) { - Caster.SendLocalizedMessage(1010521); // You cannot polymorph while you have a Town Sigil + caster.SendLocalizedMessage(1010521); // You cannot polymorph while you have a Town Sigil return false; } - if (TransformationSpellHelper.UnderTransformation(Caster)) + if (TransformationSpellHelper.UnderTransformation(caster)) { - Caster.SendLocalizedMessage(1061633); // You cannot polymorph while in that form. + caster.SendLocalizedMessage(1061633); // You cannot polymorph while in that form. return false; } - if (DisguiseTimers.IsDisguised(Caster)) + if (DisguiseTimers.IsDisguised(caster)) { - Caster.SendLocalizedMessage(502167); // You cannot polymorph while disguised. + caster.SendLocalizedMessage(502167); // You cannot polymorph while disguised. return false; } - if (Caster.BodyMod == 183 || Caster.BodyMod == 184) + if (caster.BodyMod == 183 || caster.BodyMod == 184) { - Caster.SendLocalizedMessage(1042512); // You cannot polymorph while wearing body paint + caster.SendLocalizedMessage(1042512); // You cannot polymorph while wearing body paint return false; } - if (!Caster.CanBeginAction()) + if (!caster.CanBeginAction()) { if (Core.ML) { - EndPolymorph(Caster); + EndPolymorph(caster); } else { - Caster.SendLocalizedMessage(1005559); // This spell is already in effect. + caster.SendLocalizedMessage(1005559); // This spell is already in effect. } return false; @@ -75,9 +77,9 @@ namespace Server.Spells.Seventh if (m_NewBody == 0) { - var gump = Core.SE ? (Gump)new NewPolymorphGump(Caster, Scroll) : new PolymorphGump(Caster, Scroll); + var gump = Core.SE ? (Gump)new NewPolymorphGump(caster, Scroll) : new PolymorphGump(caster, Scroll); - Caster.SendGump(gump); + caster.SendGump(gump); return false; } @@ -86,51 +88,67 @@ namespace Server.Spells.Seventh public override void OnCast() { - /*if (Caster.Mounted) + var caster = Caster; + + /*if (caster.Mounted) { - Caster.SendLocalizedMessage( 1042561 ); //Please dismount first. - } - else */ - if (Sigil.ExistsOn(Caster)) + caster.SendLocalizedMessage(1042561); // Please dismount first. + return; + }*/ + + if (Sigil.ExistsOn(caster)) { - Caster.SendLocalizedMessage(1010521); // You cannot polymorph while you have a Town Sigil + caster.SendLocalizedMessage(1010521); // You cannot polymorph while you have a Town Sigil + return; } - else if (!Caster.CanBeginAction()) + + if (!caster.CanBeginAction()) { if (Core.ML) { - EndPolymorph(Caster); + EndPolymorph(caster); } else { - Caster.SendLocalizedMessage(1005559); // This spell is already in effect. + caster.SendLocalizedMessage(1005559); // This spell is already in effect. } + + return; } - else if (TransformationSpellHelper.UnderTransformation(Caster)) + + if (TransformationSpellHelper.UnderTransformation(caster)) { - Caster.SendLocalizedMessage(1061633); // You cannot polymorph while in that form. + caster.SendLocalizedMessage(1061633); // You cannot polymorph while in that form. + return; } - else if (DisguiseTimers.IsDisguised(Caster)) + + if (DisguiseTimers.IsDisguised(caster)) { - Caster.SendLocalizedMessage(502167); // You cannot polymorph while disguised. + caster.SendLocalizedMessage(502167); // You cannot polymorph while disguised. + return; } - else if (Caster.BodyMod == 183 || Caster.BodyMod == 184) + + if (caster.BodyMod == 183 || caster.BodyMod == 184) { - Caster.SendLocalizedMessage(1042512); // You cannot polymorph while wearing body paint + caster.SendLocalizedMessage(1042512); // You cannot polymorph while wearing body paint + return; } - else if (!Caster.CanBeginAction() || Caster.IsBodyMod) + + if (!caster.CanBeginAction() || caster.IsBodyMod) { DoFizzle(); + return; } - else if (CheckSequence()) + + if (CheckSequence()) { - if (Caster.BeginAction()) + if (caster.BeginAction()) { if (m_NewBody != 0) { if (!((Body)m_NewBody).IsHuman) { - var mt = Caster.Mount; + var mt = caster.Mount; if (mt != null) { @@ -138,35 +156,33 @@ namespace Server.Spells.Seventh } } - Caster.BodyMod = m_NewBody; + caster.BodyMod = m_NewBody; if (m_NewBody == 400 || m_NewBody == 401) { - Caster.HueMod = Caster.Race.RandomSkinHue(); + caster.HueMod = caster.Race.RandomSkinHue(); } else { - Caster.HueMod = 0; + caster.HueMod = 0; } - BaseArmor.ValidateMobile(Caster); - BaseClothing.ValidateMobile(Caster); + BaseArmor.ValidateMobile(caster); + BaseClothing.ValidateMobile(caster); if (!Core.ML) { - StopTimer(Caster); + StopTimer(caster); - var timer = new InternalTimer(Caster); + var duration = Math.Max((int)caster.Skills.Magery.Value, 120); - m_Timers[Caster] = timer; - - timer.Start(); + m_Table[caster] = Timer.DelayCall(TimeSpan.FromSeconds(duration), EndPolymorph, caster); } } } else { - Caster.SendLocalizedMessage(1005559); // This spell is already in effect. + caster.SendLocalizedMessage(1005559); // This spell is already in effect. } } @@ -175,13 +191,10 @@ namespace Server.Spells.Seventh public static void StopTimer(Mobile m) { - if (!m_Timers.TryGetValue(m, out var timer)) + if (m_Table.Remove(m, out var timer)) { - return; + timer.Stop(); } - - timer?.Stop(); - m_Timers.Remove(m); } private static void EndPolymorph(Mobile m) @@ -198,30 +211,5 @@ namespace Server.Spells.Seventh BaseArmor.ValidateMobile(m); BaseClothing.ValidateMobile(m); } - - private class InternalTimer : Timer - { - private readonly Mobile m_Owner; - - public InternalTimer(Mobile owner) : base(TimeSpan.FromSeconds(0)) - { - m_Owner = owner; - - var val = (int)owner.Skills.Magery.Value; - - if (val > 120) - { - val = 120; - } - - Delay = TimeSpan.FromSeconds(val); - Priority = TimerPriority.OneSecond; - } - - protected override void OnTick() - { - EndPolymorph(m_Owner); - } - } } } diff --git a/Projects/UOContent/Spells/Sixth/Invisibility.cs b/Projects/UOContent/Spells/Sixth/Invisibility.cs index 2d1745902..b9dd8274e 100644 --- a/Projects/UOContent/Spells/Sixth/Invisibility.cs +++ b/Projects/UOContent/Spells/Sixth/Invisibility.cs @@ -62,19 +62,21 @@ namespace Server.Spells.Sixth var duration = TimeSpan.FromSeconds(1.2 * Caster.Skills.Magery.Fixed / 10); - Timer t = new InternalTimer(m, duration); - BuffInfo.RemoveBuff(m, BuffIcon.HidingAndOrStealth); BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Invisibility, 1075825, duration, m)); // Invisibility/Invisible - m_Table[m] = t; - - t.Start(); + m_Table[m] = Timer.DelayCall(duration, EndInvisiblity, m); } FinishSequence(); } + private static void EndInvisiblity(Mobile m) + { + m.RevealingAction(); + RemoveTimer(m); + } + public override bool CheckCast() { if (DuelContext.CheckSuddenDeath(Caster)) @@ -100,22 +102,5 @@ namespace Server.Spells.Sixth t.Stop(); } } - - private class InternalTimer : Timer - { - private readonly Mobile m_Mobile; - - public InternalTimer(Mobile m, TimeSpan duration) : base(duration) - { - Priority = TimerPriority.OneSecond; - m_Mobile = m; - } - - protected override void OnTick() - { - m_Mobile.RevealingAction(); - RemoveTimer(m_Mobile); - } - } } } diff --git a/Projects/UOContent/Spells/Spellweaving/EssenceOfWind.cs b/Projects/UOContent/Spells/Spellweaving/EssenceOfWind.cs index b22be1f91..0292dea1d 100644 --- a/Projects/UOContent/Spells/Spellweaving/EssenceOfWind.cs +++ b/Projects/UOContent/Spells/Spellweaving/EssenceOfWind.cs @@ -7,7 +7,7 @@ namespace Server.Spells.Spellweaving { private static readonly SpellInfo m_Info = new("Essence of Wind", "Anathrae", -1); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); public EssenceOfWindSpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info) { @@ -53,7 +53,10 @@ namespace Server.Spells.Spellweaving continue; } - m_Table[m] = new EssenceOfWindInfo(m, fcMalus, ssiMalus, duration); + var t = new EssenceOfWindTimer(m, fcMalus, ssiMalus, duration); + t.Start(); + + m_Table[m] = t; BuffInfo.AddBuff( m, @@ -73,58 +76,44 @@ namespace Server.Spells.Spellweaving FinishSequence(); } - public static int GetFCMalus(Mobile m) => m_Table.TryGetValue(m, out var info) ? info.FCMalus : 0; + public static int GetFCMalus(Mobile m) => m_Table.TryGetValue(m, out var timer) ? timer._fcMalus : 0; - public static int GetSSIMalus(Mobile m) => m_Table.TryGetValue(m, out var info) ? info.SSIMalus : 0; + public static int GetSSIMalus(Mobile m) => m_Table.TryGetValue(m, out var timer) ? timer._ssiMalus : 0; public static bool IsDebuffed(Mobile m) => m_Table.ContainsKey(m); public static void StopDebuffing(Mobile m, bool message) { - if (m_Table.TryGetValue(m, out var info)) + if (m_Table.TryGetValue(m, out var timer)) { - info.Timer.DoExpire(message); + timer.DoExpire(message); } } - private class EssenceOfWindInfo + private class EssenceOfWindTimer : Timer { - public EssenceOfWindInfo(Mobile defender, int fcMalus, int ssiMalus, TimeSpan duration) + private readonly Mobile _defender; + internal readonly int _fcMalus; + internal readonly int _ssiMalus; + + internal EssenceOfWindTimer(Mobile defender, int fcMalus, int ssiMalus, TimeSpan duration) : base(duration) { - Defender = defender; - FCMalus = fcMalus; - SSIMalus = ssiMalus; - - Timer = new ExpireTimer(Defender, duration); - Timer.Start(); + _defender = defender; + _fcMalus = fcMalus; + _ssiMalus = ssiMalus; } - public Mobile Defender { get; } - - public int FCMalus { get; } - - public int SSIMalus { get; } - - public ExpireTimer Timer { get; } - } - - private class ExpireTimer : Timer - { - private readonly Mobile m_Mobile; - - public ExpireTimer(Mobile m, TimeSpan delay) : base(delay) => m_Mobile = m; - protected override void OnTick() { - DoExpire(true); + DoExpire(); } - public void DoExpire(bool message) + internal void DoExpire(bool message = true) { Stop(); - m_Table.Remove(m_Mobile); + m_Table.Remove(_defender); - BuffInfo.RemoveBuff(m_Mobile, BuffIcon.EssenceOfWind); + BuffInfo.RemoveBuff(_defender, BuffIcon.EssenceOfWind); } } } diff --git a/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs b/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs index ed03a435d..e29f524f8 100644 --- a/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs +++ b/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs @@ -12,7 +12,7 @@ namespace Server.Spells.Spellweaving -1 ); - private static readonly Dictionary m_Table = new(); + private static readonly Dictionary m_Table = new(); public GiftOfRenewalSpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info) @@ -59,29 +59,19 @@ namespace Server.Spells.Spellweaving var skill = Caster.Skills.Spellweaving.Value; var hitsPerRound = 5 + (int)(skill / 24) + FocusLevel; - var duration = TimeSpan.FromSeconds(30 + FocusLevel * 10); + var duration = 30 + FocusLevel * 10; - var info = new GiftOfRenewalInfo(Caster, m, hitsPerRound); + var t = new GiftOfRenewalTimer(Caster, m, hitsPerRound, duration); - Timer.DelayCall( - duration, - () => - { - if (StopEffect(m)) - { - m.PlaySound(0x455); - m.SendLocalizedMessage(1075071); // The Gift of Renewal has faded. - } - } - ); + m_Table[m] = t; - m_Table[m] = info; + t.Start(); Caster.BeginAction(); BuffInfo.AddBuff( m, - new BuffInfo(BuffIcon.GiftOfRenewal, 1031602, 1075797, duration, m, hitsPerRound.ToString()) + new BuffInfo(BuffIcon.GiftOfRenewal, 1031602, 1075797, TimeSpan.FromSeconds(duration), m, hitsPerRound.ToString()) ); } } @@ -96,48 +86,43 @@ namespace Server.Spells.Spellweaving public static bool StopEffect(Mobile m) { - if (!m_Table.Remove(m, out var info)) - { - return false; - } - - info.m_Timer.Stop(); BuffInfo.RemoveBuff(m, BuffIcon.GiftOfRenewal); - Timer.DelayCall(TimeSpan.FromSeconds(60), info.m_Caster.EndAction); + if (m_Table.Remove(m, out var timer)) + { + timer.Stop(); + Timer.DelayCall(TimeSpan.FromSeconds(60), timer.m_Caster.EndAction); + return true; + } - return true; + return false; } - private class GiftOfRenewalInfo + private class GiftOfRenewalTimer : Timer { public readonly Mobile m_Caster; public readonly int m_HitsPerRound; public readonly Mobile m_Mobile; - public readonly InternalTimer m_Timer; - public GiftOfRenewalInfo(Mobile caster, Mobile mobile, int hitsPerRound) + internal GiftOfRenewalTimer(Mobile caster, Mobile mobile, int hitsPerRound, int duration) + : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0), duration / 2) { m_Caster = caster; m_Mobile = mobile; m_HitsPerRound = hitsPerRound; - - m_Timer = new InternalTimer(this); - m_Timer.Start(); } - } - - private class InternalTimer : Timer - { - private readonly GiftOfRenewalInfo m_GiftInfo; - - public InternalTimer(GiftOfRenewalInfo info) - : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.0)) => - m_GiftInfo = info; protected override void OnTick() { - var m = m_GiftInfo.m_Mobile; + if (Index + 1 == Count) + { + StopEffect(m_Mobile); + m_Mobile.PlaySound(0x455); + m_Mobile.SendLocalizedMessage(1075071); // The Gift of Renewal has faded. + return; + } + + var m = m_Mobile; if (!m_Table.ContainsKey(m)) { @@ -157,9 +142,9 @@ namespace Server.Spells.Spellweaving return; } - var toHeal = m_GiftInfo.m_HitsPerRound; + var toHeal = m_HitsPerRound; - SpellHelper.Heal(toHeal, m, m_GiftInfo.m_Caster); + SpellHelper.Heal(toHeal, m, m_Caster); m.FixedParticles(0x376A, 9, 32, 5005, EffectLayer.Waist); } } diff --git a/Projects/UOContent/Spells/Spellweaving/ImmolatingWeapon.cs b/Projects/UOContent/Spells/Spellweaving/ImmolatingWeapon.cs index 597ba87b7..77d560428 100644 --- a/Projects/UOContent/Spells/Spellweaving/ImmolatingWeapon.cs +++ b/Projects/UOContent/Spells/Spellweaving/ImmolatingWeapon.cs @@ -12,8 +12,7 @@ namespace Server.Spells.Spellweaving -1 ); - private static readonly Dictionary m_WeaponDamageTable = - new(); + private static readonly Dictionary m_Table = new(); public ImmolatingWeaponSpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info) @@ -54,9 +53,10 @@ namespace Server.Spells.Spellweaving var duration = 10 + (int)(skill / 24) + FocusLevel; var damage = 5 + (int)(skill / 24) + FocusLevel; - var stopTimer = Timer.DelayCall(TimeSpan.FromSeconds(duration), StopImmolating, weapon); + var t = new ImmolatingWeaponTimer(TimeSpan.FromSeconds(duration), damage, Caster, weapon); + m_Table[weapon] = t; + t.Start(); - m_WeaponDamageTable[weapon] = new ImmolatingWeaponEntry(damage, stopTimer, Caster); weapon.InvalidateProperties(); } } @@ -64,60 +64,53 @@ namespace Server.Spells.Spellweaving FinishSequence(); } - public static bool IsImmolating(BaseWeapon weapon) => m_WeaponDamageTable.ContainsKey(weapon); + public static bool IsImmolating(BaseWeapon weapon) => m_Table.ContainsKey(weapon); public static int GetImmolatingDamage(BaseWeapon weapon) => - m_WeaponDamageTable.TryGetValue(weapon, out var entry) ? entry.m_Damage : 0; + m_Table.TryGetValue(weapon, out var entry) ? entry._damage : 0; public static void DoEffect(BaseWeapon weapon, Mobile target) { - Timer.DelayCall(TimeSpan.FromSeconds(0.25), FinishEffect, new DelayedEffectEntry(weapon, target)); + if (m_Table.Remove(weapon, out var timer)) + { + timer.Stop(); + + Timer.DelayCall(TimeSpan.FromSeconds(0.25), FinishEffect, target, timer); + } } - private static void FinishEffect(DelayedEffectEntry effect) + private static void FinishEffect(Mobile target, ImmolatingWeaponTimer timer) { - if (m_WeaponDamageTable.TryGetValue(effect.m_Weapon, out var entry)) - { - AOS.Damage(effect.m_Target, entry.m_Caster, entry.m_Damage, 0, 100, 0, 0, 0); - } + AOS.Damage(target, timer._caster, timer._damage, 0, 100, 0, 0, 0); } public static void StopImmolating(BaseWeapon weapon) { - if (!m_WeaponDamageTable.Remove(weapon, out var entry)) + if (m_Table.Remove(weapon, out var timer)) { - return; - } + timer._caster?.PlaySound(0x27); + timer.Stop(); - entry.m_Caster?.PlaySound(0x27); - entry.m_Timer.Stop(); - - weapon.InvalidateProperties(); - } - - private class ImmolatingWeaponEntry - { - public readonly Mobile m_Caster; - public readonly int m_Damage; - public readonly Timer m_Timer; - - public ImmolatingWeaponEntry(int damage, Timer stopTimer, Mobile caster) - { - m_Damage = damage; - m_Timer = stopTimer; - m_Caster = caster; + weapon.InvalidateProperties(); } } - private class DelayedEffectEntry + private class ImmolatingWeaponTimer : Timer { - public readonly Mobile m_Target; - public readonly BaseWeapon m_Weapon; + public readonly Mobile _caster; + public readonly int _damage; + public readonly BaseWeapon _weapon; - public DelayedEffectEntry(BaseWeapon weapon, Mobile target) + public ImmolatingWeaponTimer(TimeSpan duration, int damage, Mobile caster, BaseWeapon weapon) : base(duration) { - m_Weapon = weapon; - m_Target = target; + _damage = damage; + _caster = caster; + _weapon = weapon; + } + + protected override void OnTick() + { + StopImmolating(_weapon); } } }