ModernUO/Projects/UOContent.Tests/Tests/Items/SpellFocusingPropertyTests.cs

414 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using Server;
using Server.Items;
using Server.Spells;
using Server.Text;
using Xunit;
namespace UOContent.Tests;
[Collection("Sequential UOContent Tests")]
public class SpellFocusingPropertyTests
{
private const int SpellFocusingCliloc = 1150058;
[Fact]
public void AosAttributes_StoresAndDisplaysSpellFocusing()
{
var previousExpansion = Core.Expansion;
var item = new Katana();
try
{
Core.Expansion = Expansion.AOS;
item.Attributes.SpellFocusing = 1;
Assert.Equal(1, item.Attributes.SpellFocusing);
var properties = new RecordingPropertyList();
item.GetProperties(properties);
Assert.Contains(properties.Numbers, number => number == SpellFocusingCliloc);
}
finally
{
Core.Expansion = previousExpansion;
item.Delete();
}
}
[Fact]
public void AosAttributes_DoesNotDisplaySpellFocusingBeforeAos()
{
var previousExpansion = Core.Expansion;
var item = new Katana();
try
{
item.Attributes.SpellFocusing = 1;
Core.Expansion = Expansion.UOR;
var properties = new RecordingPropertyList();
item.GetProperties(properties);
Assert.DoesNotContain(properties.Numbers, number => number == SpellFocusingCliloc);
}
finally
{
Core.Expansion = previousExpansion;
item.Delete();
}
}
[Fact]
public void SpellFocusing_UsesAnyEquippedItemWithTheProperty()
{
var previousExpansion = Core.Expansion;
var caster = new Mobile(World.NewMobile);
var target = new Mobile(World.NewMobile);
var item = new Katana();
var spell = new TestSpell(caster);
try
{
Core.Expansion = Expansion.AOS;
caster.DefaultMobileInit();
caster.Player = true;
target.DefaultMobileInit();
target.Player = false;
caster.AddItem(item);
item.Attributes.SpellFocusing = 1;
var expected = new[] { -30, -24, -18, -12, -6, 0, 2 };
foreach (var expectedOffset in expected)
{
Assert.True(SpellFocusing.TryGetDamageOffset(spell, caster, target, out var offset));
Assert.Equal(expectedOffset, offset);
}
target.Delete();
var replacement = new Mobile(World.NewMobile);
replacement.DefaultMobileInit();
replacement.Player = false;
try
{
Assert.True(SpellFocusing.TryGetDamageOffset(spell, caster, replacement, out var resetOffset));
Assert.Equal(-30, resetOffset);
}
finally
{
replacement.Delete();
}
}
finally
{
Core.Expansion = previousExpansion;
item.Delete();
target.Delete();
caster.Delete();
}
}
[Fact]
public void SpellFocusing_UsesThePvMSequenceAndResetsAfterThePeak()
{
var previousExpansion = Core.Expansion;
var caster = CreateMobile(player: true);
var target = CreateMobile(player: false);
var item = new Katana();
var spell = new TestSpell(caster);
try
{
Core.Expansion = Expansion.AOS;
caster.AddItem(item);
item.Attributes.SpellFocusing = 1;
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(SpellFocusing.TryGetDamageOffset(spell, caster, target, out var offset));
Assert.Equal(expectedOffset, offset);
}
Assert.True(SpellFocusing.TryGetDamageOffset(spell, caster, target, out var resetOffset));
Assert.Equal(-30, resetOffset);
}
finally
{
Core.Expansion = previousExpansion;
item.Delete();
target.Delete();
caster.Delete();
}
}
[Fact]
public void SpellFocusing_HoldsThePvPCapAndResetsOnTargetChange()
{
var previousExpansion = Core.Expansion;
var caster = CreateMobile(player: true);
var target = CreateMobile(player: true);
var secondTarget = CreateMobile(player: true);
var item = new Katana();
var spell = new TestSpell(caster);
try
{
Core.Expansion = Expansion.AOS;
caster.AddItem(item);
item.Attributes.SpellFocusing = 1;
for (var i = 0; i < 15; i++)
{
Assert.True(SpellFocusing.TryGetDamageOffset(spell, caster, target, out _));
}
for (var i = 0; i < 6; i++)
{
Assert.True(SpellFocusing.TryGetDamageOffset(spell, caster, target, out var offset));
Assert.Equal(20, offset);
}
Assert.True(SpellFocusing.TryGetDamageOffset(spell, caster, target, out var resetOffset));
Assert.Equal(-30, resetOffset);
Assert.True(SpellFocusing.TryGetDamageOffset(spell, caster, secondTarget, out var targetResetOffset));
Assert.Equal(-30, targetResetOffset);
}
finally
{
Core.Expansion = previousExpansion;
item.Delete();
secondTarget.Delete();
target.Delete();
caster.Delete();
}
}
[Fact]
public void SpellFocusing_AppliesTheModifierAtTheSpellDamageHook()
{
var previousExpansion = Core.Expansion;
var caster = CreateMobile(player: true);
var target = CreateMobile(player: false);
var item = new Katana();
var spell = new TestSpell(caster);
try
{
Core.Expansion = Expansion.AOS;
caster.AddItem(item);
item.Attributes.SpellFocusing = 1;
var startingHits = target.Hits;
SpellHelper.Damage(spell, target, 100, 100, 0, 0, 0, 0);
Assert.Equal(startingHits - 70, target.Hits);
}
finally
{
Core.Expansion = previousExpansion;
item.Delete();
target.Delete();
caster.Delete();
}
}
[Fact]
public void SpellFocusing_ClearsTheSequenceWhenTheCasterIsCleared()
{
var previousExpansion = Core.Expansion;
var caster = CreateMobile(player: true);
var target = CreateMobile(player: false);
var item = new Katana();
var spell = new TestSpell(caster);
try
{
Core.Expansion = Expansion.AOS;
caster.AddItem(item);
item.Attributes.SpellFocusing = 1;
Assert.True(SpellFocusing.TryGetDamageOffset(spell, caster, target, out var firstOffset));
Assert.Equal(-30, firstOffset);
Assert.True(SpellFocusing.TryGetDamageOffset(spell, caster, target, out var secondOffset));
Assert.Equal(-24, secondOffset);
SpellFocusing.Clear(caster);
Assert.True(SpellFocusing.TryGetDamageOffset(spell, caster, target, out var resetOffset));
Assert.Equal(-30, resetOffset);
}
finally
{
Core.Expansion = previousExpansion;
item.Delete();
target.Delete();
caster.Delete();
}
}
[Fact]
public void SpellFocusing_RequiresAnEligibleSpellAndTheAoSExpansion()
{
var previousExpansion = Core.Expansion;
var caster = CreateMobile(player: true);
var target = CreateMobile(player: false);
var item = new Katana();
var qualifyingSpell = new TestSpell(caster);
var nonQualifyingSpell = new NonQualifyingSpell(caster);
try
{
caster.AddItem(item);
item.Attributes.SpellFocusing = 1;
Core.Expansion = Expansion.UOR;
Assert.False(SpellFocusing.TryGetDamageOffset(qualifyingSpell, caster, target, out _));
Core.Expansion = Expansion.AOS;
Assert.False(SpellFocusing.TryGetDamageOffset(nonQualifyingSpell, caster, target, out _));
}
finally
{
Core.Expansion = previousExpansion;
item.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 class RecordingPropertyList : IPropertyList
{
public List<int> Numbers { get; } = [];
public void Reset()
{
}
public void Terminate()
{
}
public void Add(int number) => Numbers.Add(number);
public void Add(int number, string argument) => Numbers.Add(number);
public void Add(ReadOnlySpan<char> argument)
{
}
public void Add(int number, ReadOnlySpan<char> argument) => Numbers.Add(number);
public void AddChunked(ReadOnlySpan<char> text)
{
}
public OplTextBlock TextBlock() => new(this);
public void Add(int number, int value) => Numbers.Add(number);
public void AddLocalized(int value)
{
}
public void AddLocalized(int number, int value) => Numbers.Add(number);
public void Add(ref IPropertyList.InterpolatedStringHandler handler)
{
}
public void Add(int number, ref IPropertyList.InterpolatedStringHandler handler) => Numbers.Add(number);
public void InitializeInterpolation(int literalLength, int formattedCount)
{
}
public void AppendLiteral(string value)
{
}
public void AppendFormatted<T>(T value)
{
}
public void AppendFormatted<T>(T value, string format)
{
}
public void AppendFormatted<T>(T value, int alignment)
{
}
public void AppendFormatted<T>(T value, int alignment, string format)
{
}
public void AppendFormatted(ReadOnlySpan<char> value)
{
}
public void AppendFormatted(ReadOnlySpan<char> value, int alignment, string format = null)
{
}
public void AppendFormatted(object value, int alignment = 0, string format = null)
{
}
public void AppendFormatted(string value)
{
}
public void AppendFormatted(string value, int alignment, string format = null)
{
}
}
}