diff --git a/Projects/UOContent.Tests/Tests/Items/SpellFocusingSashTests.cs b/Projects/UOContent.Tests/Tests/Items/SpellFocusingSashTests.cs new file mode 100644 index 000000000..24f27a2fb --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Items/SpellFocusingSashTests.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using Server; +using Server.Collections; +using Server.ContextMenus; +using Server.Items; +using Server.Spells; +using Server.Tests; +using Server.Text; +using Xunit; + +namespace UOContent.Tests; + +[Collection("Sequential UOContent Tests")] +public class SpellFocusingSashTests +{ + private const int SpellFocusingCliloc = 1150058; + private const int BrittleCliloc = 1116209; + private const int ManaIncreaseCliloc = 1060439; + private const int DefendChanceCliloc = 1060408; + private const int StrengthRequirementCliloc = 1061170; + private const int DurabilityCliloc = 1060639; + + [Fact] + public void SpellFocusingSash_HasArtifactStatsAndPropertyOrder() + { + var previousExpansion = Core.Expansion; + var sash = new SpellFocusingSash(); + + try + { + Core.Expansion = Expansion.SA; + var list = new RecordingPropertyList(); + sash.GetProperties(list); + + Assert.Equal(1.0, sash.DefaultWeight); + Assert.Equal(1, sash.Attributes.BonusMana); + Assert.Equal(5, sash.Attributes.DefendChance); + Assert.Equal(10, sash.StrRequirement); + Assert.Equal(255, sash.HitPoints); + Assert.Equal(255, sash.MaxHitPoints); + + var spellFocusingIndex = list.Entries.FindIndex(entry => entry.Number == SpellFocusingCliloc); + Assert.True(spellFocusingIndex > 0); + Assert.Equal(1072788, list.Entries[spellFocusingIndex - 1].Number); + Assert.True(spellFocusingIndex < list.Entries.FindIndex(entry => entry.Number == BrittleCliloc)); + Assert.Contains(list.Entries, entry => entry.Number == StrengthRequirementCliloc && entry.Argument == "10"); + Assert.Contains(list.Entries, entry => entry.Number == DurabilityCliloc && entry.Argument == "255\t255"); + Assert.True( + list.Entries.FindIndex(entry => entry.Number == DefendChanceCliloc) < + list.Entries.FindIndex(entry => entry.Number == ManaIncreaseCliloc) + ); + } + finally + { + Core.Expansion = previousExpansion; + sash.Delete(); + } + } + + [Fact] + public void SpellFocusingSash_EnabledStateSerializesAndContextMenuTogglesIt() + { + var previousExpansion = Core.Expansion; + var caster = CreateMobile(player: true); + var sash = new SpellFocusingSash(); + var deserialized = new SpellFocusingSash(); + + try + { + Core.Expansion = Expansion.SA; + caster.AddItem(sash); + + var menu = ContextMenuSystem.CreateContextMenu(caster, sash); + var entry = Assert.Single(menu.Entries, e => e.Number == 3006151); + entry.OnClick(caster, sash); + Assert.False(sash.Enabled); + + sash.Enabled = true; + var writer = new BufferWriter(true); + sash.Enabled = false; + sash.Serialize(writer); + var buffer = new byte[writer.Position]; + writer.Buffer.AsSpan(0, (int)writer.Position).CopyTo(buffer); + deserialized.Deserialize(new BufferReader(buffer)); + Assert.False(deserialized.Enabled); + } + finally + { + Core.Expansion = previousExpansion; + sash.Delete(); + deserialized.Delete(); + caster.Delete(); + } + } + + [Fact] + public void SpellFocusingSash_AppliesPvMSequenceAndResetsAfterPeak() + { + var previousExpansion = Core.Expansion; + var caster = CreateMobile(player: true); + var target = CreateMobile(player: false); + var sash = new SpellFocusingSash(); + var spell = new TestSpell(caster); + + try + { + Core.Expansion = Expansion.SA; + caster.AddItem(sash); + var expected = new[] { -30, -24, -18, -12, -6, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 }; + + foreach (var expectedOffset in expected) + { + Assert.True(SpellFocusingSash.TryGetDamageOffset(spell, caster, target, out var actual)); + Assert.Equal(expectedOffset, actual); + } + + Assert.True(SpellFocusingSash.TryGetDamageOffset(spell, caster, target, out var resetOffset)); + Assert.Equal(-30, resetOffset); + } + finally + { + Core.Expansion = previousExpansion; + sash.Delete(); + target.Delete(); + caster.Delete(); + } + } + + [Fact] + public void SpellFocusingSash_HoldsPvPCapForFiveSpellsAndResetsOnTargetChange() + { + var previousExpansion = Core.Expansion; + var caster = CreateMobile(player: true); + var target = CreateMobile(player: true); + var secondTarget = CreateMobile(player: true); + var sash = new SpellFocusingSash(); + var spell = new TestSpell(caster); + + try + { + Core.Expansion = Expansion.SA; + caster.AddItem(sash); + + for (var i = 0; i < 15; i++) + { + Assert.True(SpellFocusingSash.TryGetDamageOffset(spell, caster, target, out _)); + } + + for (var i = 0; i < 6; i++) + { + Assert.True(SpellFocusingSash.TryGetDamageOffset(spell, caster, target, out var offset)); + Assert.Equal(20, offset); + } + + Assert.True(SpellFocusingSash.TryGetDamageOffset(spell, caster, target, out var resetOffset)); + Assert.Equal(-30, resetOffset); + + Assert.True(SpellFocusingSash.TryGetDamageOffset(spell, caster, secondTarget, out var targetResetOffset)); + Assert.Equal(-30, targetResetOffset); + + sash.Enabled = false; + Assert.False(SpellFocusingSash.TryGetDamageOffset(spell, caster, target, out _)); + } + finally + { + Core.Expansion = previousExpansion; + sash.Delete(); + secondTarget.Delete(); + target.Delete(); + caster.Delete(); + } + } + + [Fact] + public void SpellFocusingSash_ExcludesNonQualifyingSpells() + { + var previousExpansion = Core.Expansion; + var caster = CreateMobile(player: true); + var target = CreateMobile(player: false); + var sash = new SpellFocusingSash(); + + try + { + Core.Expansion = Expansion.SA; + caster.AddItem(sash); + Assert.False(SpellFocusingSash.TryGetDamageOffset(new NonQualifyingSpell(caster), caster, target, out _)); + } + finally + { + Core.Expansion = previousExpansion; + sash.Delete(); + target.Delete(); + caster.Delete(); + } + } + + private static Mobile CreateMobile(bool player) + { + var mobile = new Mobile(World.NewMobile); + mobile.DefaultMobileInit(); + mobile.Player = player; + mobile.RawStr = 100; + mobile.Hits = mobile.HitsMax; + return mobile; + } + + private sealed class TestSpell : Spell + { + private static readonly SpellInfo TestInfo = new("Test Spell", "test"); + + public TestSpell(Mobile caster) : base(caster, null, TestInfo) + { + } + + public override bool SpellFocusingEligible => true; + public override TimeSpan CastDelayBase => TimeSpan.Zero; + public override void OnCast() + { + } + + public override int GetMana() => 0; + } + + private sealed class NonQualifyingSpell : Spell + { + private static readonly SpellInfo TestInfo = new("Non-Qualifying Spell", "no"); + + public NonQualifyingSpell(Mobile caster) : base(caster, null, TestInfo) + { + } + + public override TimeSpan CastDelayBase => TimeSpan.Zero; + public override void OnCast() + { + } + + public override int GetMana() => 0; + } + + private sealed record PropertyEntry(int Number, string Argument); + + private sealed class RecordingPropertyList : IPropertyList + { + private string _interpolated = string.Empty; + + public List Entries { get; } = []; + + public void Reset() + { + } + + public void Terminate() + { + } + + public void Add(int number) => Entries.Add(new PropertyEntry(number, string.Empty)); + public void Add(int number, string argument) => Entries.Add(new PropertyEntry(number, argument)); + public void Add(ReadOnlySpan argument) => Entries.Add(new PropertyEntry(0, argument.ToString())); + public void Add(int number, ReadOnlySpan argument) => Entries.Add(new PropertyEntry(number, argument.ToString())); + public void AddChunked(ReadOnlySpan text) => Entries.Add(new PropertyEntry(0, text.ToString())); + public OplTextBlock TextBlock() => new(this); + public void Add(int number, int value) => Entries.Add(new PropertyEntry(number, value.ToString())); + public void AddLocalized(int value) => Entries.Add(new PropertyEntry(0, value.ToString())); + public void AddLocalized(int number, int value) => Entries.Add(new PropertyEntry(number, value.ToString())); + public void Add(ref IPropertyList.InterpolatedStringHandler handler) => Entries.Add(new PropertyEntry(0, _interpolated)); + public void Add(int number, ref IPropertyList.InterpolatedStringHandler handler) => Entries.Add(new PropertyEntry(number, _interpolated)); + public void InitializeInterpolation(int literalLength, int formattedCount) => _interpolated = string.Empty; + public void AppendLiteral(string value) => _interpolated += value; + public void AppendFormatted(T value) => _interpolated += value; + public void AppendFormatted(T value, string format) => _interpolated += value is IFormattable formattable ? formattable.ToString(format, null) : value; + public void AppendFormatted(T value, int alignment) => _interpolated += value; + public void AppendFormatted(T value, int alignment, string format) => _interpolated += value is IFormattable formattable ? formattable.ToString(format, null) : value; + public void AppendFormatted(ReadOnlySpan value) => _interpolated += value.ToString(); + public void AppendFormatted(ReadOnlySpan value, int alignment, string format = null) => _interpolated += value.ToString(); + public void AppendFormatted(object value, int alignment = 0, string format = null) => _interpolated += value; + public void AppendFormatted(string value) => _interpolated += value; + public void AppendFormatted(string value, int alignment, string format = null) => _interpolated += value; + } +} diff --git a/Projects/UOContent/Items/Clothing/SpellFocusingSash.cs b/Projects/UOContent/Items/Clothing/SpellFocusingSash.cs new file mode 100644 index 000000000..76e06c8ff --- /dev/null +++ b/Projects/UOContent/Items/Clothing/SpellFocusingSash.cs @@ -0,0 +1,248 @@ +using ModernUO.CodeGeneratedEvents; +using ModernUO.Serialization; +using Server.Collections; +using Server.ContextMenus; +using Server.Engines.BuffIcons; +using Server.Mobiles; +using Server.Spells; + +namespace Server.Items; + +[Flippable(0x1541, 0x1542)] +[SerializationGenerator(0, false)] +public partial class SpellFocusingSash : BaseMiddleTorso +{ + private const int SpellFocusingCliloc = 1150058; + private const int BrittleCliloc = 1116209; + private const int ResetMessage = 1150117; + private const int TunedMessage = 1150118; + private const int PeakMessage = 1150116; + private const int BuffTitleCliloc = 1151391; + private const int BuffSecondaryCliloc = 1151392; + private const int SequenceLength = 21; + + private Mobile _spellCastTarget; + private int _spellCastCount; + private bool _enabled = true; + + [Constructible] + public SpellFocusingSash() : base(0x1541) + { + Attributes.BonusMana = 1; + Attributes.DefendChance = 5; + HitPoints = MaxHitPoints = 255; + } + + public override int LabelNumber => 1150059; + public override double DefaultWeight => 1.0; + public override int InitMinHits => 255; + public override int InitMaxHits => 255; + public override int AosStrReq => 10; + + [SerializableProperty(0, useField: nameof(_enabled))] + [CommandProperty(AccessLevel.GameMaster)] + public bool Enabled + { + get => _enabled; + set + { + if (_enabled == value) + { + return; + } + + _enabled = value; + ResetSequence(Parent as Mobile); + InvalidateProperties(); + this.MarkDirty(); + } + } + + public static void Configure() + { + EventSink.Logout += Clear; + } + + public override void AddNameProperties(IPropertyList list) + { + base.AddNameProperties(list); + + if (Core.SA) + { + list.Add(SpellFocusingCliloc); + list.Add(BrittleCliloc); + } + } + + public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) + { + base.GetContextMenuEntries(from, ref list); + + if (Core.SA && from == Parent && from.Alive) + { + list.Add(new ToggleSpellFocusingEntry(this)); + } + } + + public override void OnAdded(IEntity parent) + { + base.OnAdded(parent); + + if (parent is Mobile mobile) + { + ResetSequence(mobile); + } + } + + public override void OnRemoved(IEntity parent) + { + if (parent is Mobile mobile) + { + ResetSequence(mobile); + } + + base.OnRemoved(parent); + } + + public override void OnDelete() + { + ResetSequence(Parent as Mobile); + base.OnDelete(); + } + + public static bool TryGetDamageOffset(Spell spell, Mobile caster, Mobile target, out int offset) + { + offset = 0; + + if (!Core.SA || spell?.SpellFocusingEligible != true || caster?.Deleted != false) + { + return false; + } + + if (caster.FindItemOnLayer(Layer.MiddleTorso) is not SpellFocusingSash sash || !sash.Enabled) + { + return false; + } + + if (target?.Deleted != false || !target.Alive || !caster.Alive) + { + sash.ResetSequence(caster); + return false; + } + + return sash.TryGetDamageOffset(caster, target, out offset); + } + + [OnEvent(nameof(PlayerMobile.PlayerDeathEvent))] + [OnEvent(nameof(PlayerMobile.PlayerDeletedEvent))] + [OnEvent(nameof(BaseCreature.CreatureDeathEvent))] + [OnEvent(nameof(BaseCreature.CreatureDeletedEvent))] + public static void Clear(Mobile mobile) + { + if (mobile?.FindItemOnLayer(Layer.MiddleTorso) is SpellFocusingSash sash) + { + sash.ResetSequence(mobile); + } + } + + private bool TryGetDamageOffset(Mobile caster, Mobile target, out int offset) + { + offset = 0; + + if (_spellCastTarget?.Deleted != false || !_spellCastTarget.Alive) + { + ResetSequence(caster); + } + + if (_spellCastTarget != target) + { + if (_spellCastTarget != null) + { + caster.SendLocalizedMessage(ResetMessage); + } + + ResetSequence(caster); + _spellCastTarget = target; + } + + offset = GetDamageOffset(_spellCastCount, target.Player); + _spellCastCount++; + + if (offset == 0) + { + caster.SendLocalizedMessage(TunedMessage); + } + + if (_spellCastCount >= SequenceLength) + { + caster.SendLocalizedMessage(PeakMessage); + ResetSequence(caster); + } + else + { + RefreshBuffInfo(caster, offset); + } + + return true; + } + + private static int GetDamageOffset(int castCount, bool playerTarget) + { + if (castCount < 6) + { + return -30 + castCount * 6; + } + + var offset = (castCount - 5) * 2; + return playerTarget ? System.Math.Min(offset, 20) : System.Math.Min(offset, 30); + } + + private void ResetSequence(Mobile caster) + { + _spellCastTarget = null; + _spellCastCount = 0; + + if (caster is PlayerMobile player) + { + player.RemoveBuff(BuffIcon.SpellFocusingBuff); + player.RemoveBuff(BuffIcon.SpellFocusingDebuff); + } + } + + private void RefreshBuffInfo(Mobile caster, int offset) + { + if (caster is not PlayerMobile player || caster != Parent || !Enabled) + { + return; + } + + player.RemoveBuff(offset < 0 ? BuffIcon.SpellFocusingBuff : BuffIcon.SpellFocusingDebuff); + player.AddBuff( + new BuffInfo( + offset < 0 ? BuffIcon.SpellFocusingDebuff : BuffIcon.SpellFocusingBuff, + BuffTitleCliloc, + BuffSecondaryCliloc, + args: $"{_spellCastTarget?.Name ?? "None"}\t{offset}" + ) + ); + } + + private sealed class ToggleSpellFocusingEntry : ContextMenuEntry + { + private readonly SpellFocusingSash _sash; + + public ToggleSpellFocusingEntry(SpellFocusingSash sash) : base(sash.Enabled ? 3006151 : 3006150, 2) + { + _sash = sash; + } + + public override void OnClick(Mobile from, IEntity target) + { + if (target is SpellFocusingSash sash && sash == _sash && from == sash.Parent && from.Alive) + { + sash.Enabled = !sash.Enabled; + from.SendMessage(sash.Enabled ? "Spell Focusing enabled." : "Spell Focusing disabled."); + } + } + } +} diff --git a/Projects/UOContent/Misc/AOS.cs b/Projects/UOContent/Misc/AOS.cs index 7f98f4447..29c273d24 100644 --- a/Projects/UOContent/Misc/AOS.cs +++ b/Projects/UOContent/Misc/AOS.cs @@ -1359,6 +1359,11 @@ namespace Server public static bool IsBrittle(Item item) { + if (item is SpellFocusingSash) + { + return Core.SA; + } + if (!Core.HS) { return false; diff --git a/Projects/UOContent/Spells/Base/Spell.cs b/Projects/UOContent/Spells/Base/Spell.cs index 088ba0d4f..80a700c93 100644 --- a/Projects/UOContent/Spells/Base/Spell.cs +++ b/Projects/UOContent/Spells/Base/Spell.cs @@ -57,6 +57,8 @@ namespace Server.Spells public virtual bool DelayedDamage => false; + public virtual bool SpellFocusingEligible => false; + public static readonly Type[] AOSNoDelayedDamageStackingSelf = Core.AOS ? Array.Empty() : null; // Null means stacking is allowed while empty indicates no stacking with self diff --git a/Projects/UOContent/Spells/Base/SpellHelper.cs b/Projects/UOContent/Spells/Base/SpellHelper.cs index dc7e22f80..0fb1cc62c 100644 --- a/Projects/UOContent/Spells/Base/SpellHelper.cs +++ b/Projects/UOContent/Spells/Base/SpellHelper.cs @@ -958,6 +958,11 @@ namespace Server.Spells bcFrom?.AlterSpellDamageTo(target, ref damageGiven); bcTarget?.AlterSpellDamageFrom(from, ref damageGiven); + if (SpellFocusingSash.TryGetDamageOffset(spell, from, target, out var spellFocusingOffset)) + { + damageGiven = AOS.Scale(damageGiven, 100 + spellFocusingOffset); + } + target.Damage(damageGiven, from); bcFrom?.OnDamageSpell(target, damageGiven); @@ -1026,6 +1031,11 @@ namespace Server.Spells bcTarget?.AlterSpellDamageFrom(from, ref dmg); + if (SpellFocusingSash.TryGetDamageOffset(spell, from, target, out var spellFocusingOffset)) + { + dmg = AOS.Scale(dmg, 100 + spellFocusingOffset); + } + if (Feint.GetDamageReduction(from, target, out var feintReduction)) { // example: 35 damage * 50 / 100 = 17 damage @@ -1110,6 +1120,11 @@ namespace Server.Spells (m_From as BaseCreature)?.AlterSpellDamageTo(m_Target, ref m_Damage); (m_Target as BaseCreature)?.AlterSpellDamageFrom(m_From, ref m_Damage); + if (SpellFocusingSash.TryGetDamageOffset(m_Spell, m_From, m_Target, out var spellFocusingOffset)) + { + m_Damage = AOS.Scale(m_Damage, 100 + spellFocusingOffset); + } + m_Target.Damage(m_Damage); m_Spell?.RemoveDelayedDamageContext(m_Target); } diff --git a/Projects/UOContent/Spells/Fifth/MindBlast.cs b/Projects/UOContent/Spells/Fifth/MindBlast.cs index 38e0c5548..bf3302405 100644 --- a/Projects/UOContent/Spells/Fifth/MindBlast.cs +++ b/Projects/UOContent/Spells/Fifth/MindBlast.cs @@ -26,6 +26,8 @@ namespace Server.Spells.Fifth public override SpellCircle Circle => SpellCircle.Fifth; + public override bool SpellFocusingEligible => true; + public override bool DelayedDamage => !Core.AOS; public void Target(Mobile m) diff --git a/Projects/UOContent/Spells/First/MagicArrow.cs b/Projects/UOContent/Spells/First/MagicArrow.cs index d8425a3ac..1f98a8c11 100644 --- a/Projects/UOContent/Spells/First/MagicArrow.cs +++ b/Projects/UOContent/Spells/First/MagicArrow.cs @@ -20,6 +20,8 @@ namespace Server.Spells.First public override SpellCircle Circle => SpellCircle.First; + public override bool SpellFocusingEligible => true; + private static readonly Type[] _delayedDamageSpellFamilyStacking = Core.AOS ? [typeof(NetherBoltSpell)] : null; public override Type[] DelayedDamageSpellFamilyStacking => _delayedDamageSpellFamilyStacking; diff --git a/Projects/UOContent/Spells/Fourth/Lightning.cs b/Projects/UOContent/Spells/Fourth/Lightning.cs index 335580c33..4daf13e53 100644 --- a/Projects/UOContent/Spells/Fourth/Lightning.cs +++ b/Projects/UOContent/Spells/Fourth/Lightning.cs @@ -19,6 +19,8 @@ namespace Server.Spells.Fourth public override SpellCircle Circle => SpellCircle.Fourth; + public override bool SpellFocusingEligible => true; + public override bool DelayedDamage => false; public void Target(Mobile m) diff --git a/Projects/UOContent/Spells/Mysticism/BombardSpell.cs b/Projects/UOContent/Spells/Mysticism/BombardSpell.cs index baaea9c0c..defca9e02 100644 --- a/Projects/UOContent/Spells/Mysticism/BombardSpell.cs +++ b/Projects/UOContent/Spells/Mysticism/BombardSpell.cs @@ -20,6 +20,8 @@ public class BombardSpell : MysticSpell, ITargetingSpell } public override SpellCircle Circle => SpellCircle.Sixth; + + public override bool SpellFocusingEligible => true; public override bool DelayedDamage => true; public override Type[] DelayedDamageSpellFamilyStacking => AOSNoDelayedDamageStackingSelf; diff --git a/Projects/UOContent/Spells/Mysticism/EagleStrikeSpell.cs b/Projects/UOContent/Spells/Mysticism/EagleStrikeSpell.cs index 0e1d1af28..1d05219c6 100644 --- a/Projects/UOContent/Spells/Mysticism/EagleStrikeSpell.cs +++ b/Projects/UOContent/Spells/Mysticism/EagleStrikeSpell.cs @@ -22,6 +22,8 @@ public class EagleStrikeSpell : MysticSpell, ITargetingSpell public override SpellCircle Circle => SpellCircle.Third; + public override bool SpellFocusingEligible => true; + public void Target(Mobile m) { if (CheckHSequence(m)) diff --git a/Projects/UOContent/Spells/Mysticism/NetherBoltSpell.cs b/Projects/UOContent/Spells/Mysticism/NetherBoltSpell.cs index 1397e7d9e..e5f1c6f67 100644 --- a/Projects/UOContent/Spells/Mysticism/NetherBoltSpell.cs +++ b/Projects/UOContent/Spells/Mysticism/NetherBoltSpell.cs @@ -23,6 +23,8 @@ public class NetherBoltSpell : MysticSpell, ITargetingSpell public override SpellCircle Circle => SpellCircle.First; + public override bool SpellFocusingEligible => true; + public override bool DelayedDamage => true; public override Type[] DelayedDamageSpellFamilyStacking => _delayedDamageSpellFamilyStacking; diff --git a/Projects/UOContent/Spells/Necromancy/PainSpike.cs b/Projects/UOContent/Spells/Necromancy/PainSpike.cs index eca3bd275..6c5f39fd3 100644 --- a/Projects/UOContent/Spells/Necromancy/PainSpike.cs +++ b/Projects/UOContent/Spells/Necromancy/PainSpike.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Server.Engines.BuffIcons; +using Server.Items; using Server.Misc; using Server.Mobiles; using Server.Targeting; @@ -31,6 +32,8 @@ public class PainSpikeSpell : NecromancerSpell, ITargetingSpell public override bool DelayedDamage => false; + public override bool SpellFocusingEligible => true; + public static bool UnderEffect(Mobile m) => _table.ContainsKey(m); public void Target(Mobile m) @@ -76,6 +79,12 @@ public class PainSpikeSpell : NecromancerSpell, ITargetingSpell // TODO: Find a better way to do this StaminaSystem.DFA = DFAlgorithm.PainSpike; + + if (SpellFocusingSash.TryGetDamageOffset(this, Caster, m, out var spellFocusingOffset)) + { + damage = AOS.Scale((int)damage, 100 + spellFocusingOffset); + } + m.Damage((int)damage, Caster, ignoreEvilOmen: true); SpellHelper.DoLeech((int)damage, Caster, m); StaminaSystem.DFA = DFAlgorithm.Standard; diff --git a/Projects/UOContent/Spells/Second/Harm.cs b/Projects/UOContent/Spells/Second/Harm.cs index 46eaf9856..c7f5b7ec0 100644 --- a/Projects/UOContent/Spells/Second/Harm.cs +++ b/Projects/UOContent/Spells/Second/Harm.cs @@ -19,6 +19,8 @@ namespace Server.Spells.Second public override SpellCircle Circle => SpellCircle.Second; + public override bool SpellFocusingEligible => true; + public override bool DelayedDamage => false; public void Target(Mobile m) diff --git a/Projects/UOContent/Spells/Seventh/FlameStrike.cs b/Projects/UOContent/Spells/Seventh/FlameStrike.cs index 1e021a5fa..ab871120a 100644 --- a/Projects/UOContent/Spells/Seventh/FlameStrike.cs +++ b/Projects/UOContent/Spells/Seventh/FlameStrike.cs @@ -19,6 +19,8 @@ namespace Server.Spells.Seventh public override SpellCircle Circle => SpellCircle.Seventh; + public override bool SpellFocusingEligible => true; + public override bool DelayedDamage => true; public void Target(Mobile m) diff --git a/Projects/UOContent/Spells/Sixth/EnergyBolt.cs b/Projects/UOContent/Spells/Sixth/EnergyBolt.cs index d72aaf55f..5bca8d5c7 100644 --- a/Projects/UOContent/Spells/Sixth/EnergyBolt.cs +++ b/Projects/UOContent/Spells/Sixth/EnergyBolt.cs @@ -19,6 +19,8 @@ namespace Server.Spells.Sixth public override SpellCircle Circle => SpellCircle.Sixth; + public override bool SpellFocusingEligible => true; + public override bool DelayedDamage => true; public void Target(Mobile m) diff --git a/Projects/UOContent/Spells/Sixth/Explosion.cs b/Projects/UOContent/Spells/Sixth/Explosion.cs index 9e101f03e..b58bbdc51 100644 --- a/Projects/UOContent/Spells/Sixth/Explosion.cs +++ b/Projects/UOContent/Spells/Sixth/Explosion.cs @@ -20,6 +20,8 @@ namespace Server.Spells.Sixth public override SpellCircle Circle => SpellCircle.Sixth; + public override bool SpellFocusingEligible => true; + public override Type[] DelayedDamageSpellFamilyStacking => AOSNoDelayedDamageStackingSelf; public override bool DelayedDamage => false; diff --git a/Projects/UOContent/Spells/Spellweaving/WordOfDeath.cs b/Projects/UOContent/Spells/Spellweaving/WordOfDeath.cs index 04143c6cf..00dfb95f8 100644 --- a/Projects/UOContent/Spells/Spellweaving/WordOfDeath.cs +++ b/Projects/UOContent/Spells/Spellweaving/WordOfDeath.cs @@ -11,6 +11,8 @@ namespace Server.Spells.Spellweaving { } + public override bool SpellFocusingEligible => true; + public override TimeSpan CastDelayBase => TimeSpan.FromSeconds(3.5); public override double RequiredSkill => 80.0; diff --git a/Projects/UOContent/Spells/Third/Fireball.cs b/Projects/UOContent/Spells/Third/Fireball.cs index 84d8d5b82..2c17cf98d 100644 --- a/Projects/UOContent/Spells/Third/Fireball.cs +++ b/Projects/UOContent/Spells/Third/Fireball.cs @@ -18,6 +18,8 @@ namespace Server.Spells.Third public override SpellCircle Circle => SpellCircle.Third; + public override bool SpellFocusingEligible => true; + public override bool DelayedDamage => true; public void Target(Mobile m)