ModernUO/Projects/UOContent.Tests/Tests/Items/Weapons/LastParryChancePropertyTests.cs
2026-07-09 20:29:15 +02:00

276 lines
9.8 KiB
C#

using System;
using System.Collections.Generic;
using Server;
using Server.Items;
using Server.Misc;
using Server.Tests;
using Server.Text;
using Xunit;
namespace UOContent.Tests;
[Collection("Sequential UOContent Tests")]
public class LastParryChancePropertyTests
{
private const int LastParryChanceCliloc = 1158861;
[Fact]
public void ShieldSuccessfulParry_SetsRuntimeTooltipValueAndClearsOnRemoval()
{
var previousExpansion = Core.Expansion;
using var random = new PredictableRandom(0);
var defender = CreateDefender(parry: 120.0, bushido: 0.0, dex: 100);
var shield = new Buckler();
try
{
EnsureSkillChecksConfigured();
Core.Expansion = Expansion.EJ;
Assert.True(defender.EquipItem(shield));
Assert.True(BaseWeapon.CheckParry(defender));
Assert.Equal(35, shield.LastParryChance);
AssertShowsLastParryChance(shield, 35);
shield.Internalize();
Assert.Equal(0, shield.LastParryChance);
AssertDoesNotShowLastParryChance(shield);
}
finally
{
Core.Expansion = previousExpansion;
shield.Delete();
defender.Delete();
}
}
[Fact]
public void WeaponSuccessfulParry_SetsOneHandedAndTwoHandedRuntimeTooltipValues()
{
var previousExpansion = Core.Expansion;
using var random = new PredictableRandom(0);
var oneHandedDefender = CreateDefender(parry: 120.0, bushido: 120.0, dex: 100);
var twoHandedDefender = CreateDefender(parry: 120.0, bushido: 120.0, dex: 100);
var katana = new Katana();
var halberd = new Halberd();
try
{
EnsureSkillChecksConfigured();
Core.Expansion = Expansion.EJ;
Assert.True(oneHandedDefender.EquipItem(katana));
Assert.True(BaseWeapon.CheckParry(oneHandedDefender));
Assert.Equal(35, katana.LastParryChance);
AssertShowsLastParryChance(katana, 35);
Assert.True(twoHandedDefender.EquipItem(halberd));
Assert.True(BaseWeapon.CheckParry(twoHandedDefender));
Assert.Equal(40, halberd.LastParryChance);
AssertShowsLastParryChance(halberd, 40);
}
finally
{
Core.Expansion = previousExpansion;
katana.Delete();
halberd.Delete();
oneHandedDefender.Delete();
twoHandedDefender.Delete();
}
}
[Fact]
public void WeaponFallbackParry_DisplaysActualSuccessfulAosChanceBranch()
{
var previousExpansion = Core.Expansion;
using var random = new PredictableRandom(0);
var defender = CreateDefender(parry: 100.0, bushido: 0.0, dex: 100);
var katana = new Katana();
try
{
EnsureSkillChecksConfigured();
Core.Expansion = Expansion.EJ;
Assert.True(defender.EquipItem(katana));
Assert.True(BaseWeapon.CheckParry(defender));
Assert.Equal(17, katana.LastParryChance);
AssertShowsLastParryChance(katana, 17);
}
finally
{
Core.Expansion = previousExpansion;
katana.Delete();
defender.Delete();
}
}
[Fact]
public void TooltipDisplay_IsGatedToEndlessJourney()
{
var previousExpansion = Core.Expansion;
var weapon = new Katana { LastParryChance = 35 };
var shield = new Buckler { LastParryChance = 35 };
try
{
Core.Expansion = Expansion.TOL;
AssertDoesNotShowLastParryChance(weapon);
AssertDoesNotShowLastParryChance(shield);
Core.Expansion = Expansion.EJ;
AssertShowsLastParryChance(weapon, 35);
AssertShowsLastParryChance(shield, 35);
}
finally
{
Core.Expansion = previousExpansion;
weapon.Delete();
shield.Delete();
}
}
[Fact]
public void FistsAndRangedWeapons_DoNotReceiveOrDisplayLastParryChance()
{
var previousExpansion = Core.Expansion;
using var random = new PredictableRandom(0);
var defender = CreateDefender(parry: 120.0, bushido: 120.0, dex: 100);
var bow = new Bow();
var fists = new Fists { LastParryChance = 35 };
try
{
EnsureSkillChecksConfigured();
Core.Expansion = Expansion.EJ;
Assert.True(defender.EquipItem(bow));
Assert.False(BaseWeapon.CheckParry(defender));
Assert.Equal(0, bow.LastParryChance);
AssertDoesNotShowLastParryChance(bow);
AssertDoesNotShowLastParryChance(fists);
}
finally
{
Core.Expansion = previousExpansion;
bow.Delete();
fists.Delete();
defender.Delete();
}
}
[Fact]
public void RuntimeState_DoesNotUseRollablePropertyContainers()
{
var weapon = new Katana { LastParryChance = 35 };
var shield = new Buckler { LastParryChance = 35 };
try
{
Assert.True(weapon.Attributes.IsEmpty);
Assert.True(weapon.WeaponAttributes.IsEmpty);
Assert.True(weapon.ExtendedWeaponAttributes.IsEmpty);
Assert.True(weapon.NegativeAttributes.IsEmpty);
Assert.True(weapon.AosElementDamages.IsEmpty);
Assert.True(shield.Attributes.IsEmpty);
Assert.True(shield.ArmorAttributes.IsEmpty);
Assert.True(shield.NegativeAttributes.IsEmpty);
Assert.True(shield.AbsorptionAttributes.IsEmpty);
}
finally
{
weapon.Delete();
shield.Delete();
}
}
private static Mobile CreateDefender(double parry, double bushido, int dex)
{
var mobile = new Mobile { Player = true };
mobile.DefaultMobileInit();
mobile.InitStats(100, dex, 100);
mobile.SkillsCap = 7200;
mobile.Skills[SkillName.Parry].Cap = 120.0;
mobile.Skills[SkillName.Bushido].Cap = 120.0;
mobile.Skills[SkillName.Parry].Base = parry;
mobile.Skills[SkillName.Bushido].Base = bushido;
return mobile;
}
private static void EnsureSkillChecksConfigured()
{
if (AntiMacroSystem.Settings == null)
{
AntiMacroSystem.Configure();
}
SkillCheck.Configure();
SkillCheck.Initialize();
}
private static void AssertShowsLastParryChance(Item item, int expectedValue)
{
var properties = new RecordingPropertyList();
item.GetProperties(properties);
Assert.Contains(
properties.Entries,
entry => entry.Number == LastParryChanceCliloc && entry.Argument == expectedValue.ToString()
);
}
private static void AssertDoesNotShowLastParryChance(Item item)
{
var properties = new RecordingPropertyList();
item.GetProperties(properties);
Assert.DoesNotContain(properties.Entries, entry => entry.Number == LastParryChanceCliloc);
}
private sealed record PropertyEntry(int Number, string Argument);
private sealed class RecordingPropertyList : IPropertyList
{
private string _interpolated = string.Empty;
public List<PropertyEntry> 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<char> argument) => Entries.Add(new PropertyEntry(0, argument.ToString()));
public void Add(int number, ReadOnlySpan<char> argument) => Entries.Add(new PropertyEntry(number, argument.ToString()));
public void AddChunked(ReadOnlySpan<char> 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>(T value) => _interpolated += value;
public void AppendFormatted<T>(T value, string format) =>
_interpolated += value is IFormattable formattable ? formattable.ToString(format, null) : value;
public void AppendFormatted<T>(T value, int alignment) => _interpolated += value;
public void AppendFormatted<T>(T value, int alignment, string format) =>
_interpolated += value is IFormattable formattable ? formattable.ToString(format, null) : value;
public void AppendFormatted(ReadOnlySpan<char> value) => _interpolated += value.ToString();
public void AppendFormatted(ReadOnlySpan<char> 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;
}
}