ModernUO/Projects/UOContent.Tests/Tests/Items/Weapons/SplinteringWeaponPropertyTests.cs

474 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using Server;
using Server.Items;
using Server.Mobiles;
using Server.Text;
using Xunit;
namespace UOContent.Tests;
[Collection("Sequential UOContent Tests")]
public class SplinteringWeaponPropertyTests
{
private const int SplinteringWeaponCliloc = 1112857;
private static readonly Point3D TestLocation = new(6200, 540, 0);
[Fact]
public void ExtendedWeaponAttributes_StoresAndDupesSplinteringWeapon()
{
var weapon = new TestKatana();
var dupe = new TestKatana();
try
{
weapon.ExtendedWeaponAttributes.SplinteringWeapon = 30;
weapon.Dupe(dupe);
Assert.Equal(30, weapon.ExtendedWeaponAttributes.SplinteringWeapon);
Assert.Equal(30, dupe.ExtendedWeaponAttributes.SplinteringWeapon);
}
finally
{
weapon.Delete();
dupe.Delete();
}
}
[Fact]
public void ExtendedWeaponAttributes_GetProperties_GatesSplinteringWeaponTooltipToStygianAbyss()
{
var previousExpansion = Core.Expansion;
var weapon = new TestKatana();
try
{
weapon.ExtendedWeaponAttributes.SplinteringWeapon = 30;
Core.Expansion = Expansion.ML;
var preStygianAbyss = new RecordingPropertyList();
weapon.ExtendedWeaponAttributes.GetProperties(preStygianAbyss);
Assert.DoesNotContain(preStygianAbyss.Entries, entry => entry.Number == SplinteringWeaponCliloc);
Core.Expansion = Expansion.SA;
var stygianAbyss = new RecordingPropertyList();
weapon.ExtendedWeaponAttributes.GetProperties(stygianAbyss);
Assert.Contains(
stygianAbyss.Entries,
entry => entry.Number == SplinteringWeaponCliloc && entry.Argument == "30"
);
}
finally
{
Core.Expansion = previousExpansion;
weapon.Delete();
}
}
[Fact]
public void OnHit_PreStygianAbyss_DoesNotStartSplinteringContext()
{
var previousExpansion = Core.Expansion;
var weapon = EquipSplinteringWeapon(CreateMobile());
var attacker = (Mobile)weapon.Parent;
var defender = CreateMobile(location: new Point3D(6201, 540, 0));
try
{
Core.Expansion = Expansion.ML;
weapon.OnHit(attacker, defender);
Assert.Equal(0, SplinteringWeapon.ActiveContextCount);
Assert.False(SplinteringWeapon.IsForceWalking(defender));
}
finally
{
Core.Expansion = previousExpansion;
SplinteringWeapon.ClearAll();
weapon.Delete();
attacker.Delete();
defender.Delete();
}
}
[Fact]
public void OnHit_EligibleSuccessfulHit_StartsBleedForceWalkAndDamagesDurability()
{
var previousExpansion = Core.Expansion;
var weapon = EquipSplinteringWeapon(CreateMobile());
var attacker = (Mobile)weapon.Parent;
var defender = CreateMobile(location: new Point3D(6201, 540, 0));
try
{
Core.Expansion = Expansion.SA;
weapon.HitPoints = 50;
weapon.MaxHitPoints = 50;
var defenderHits = defender.Hits;
weapon.OnHit(attacker, defender);
Assert.True(weapon.HitPoints <= 40);
Assert.True(SplinteringWeapon.IsForceWalking(defender));
Assert.Equal(1, SplinteringWeapon.ActiveContextCount);
Assert.True(SplinteringWeapon.TickForTests(attacker, defender));
Assert.True(defender.Hits < defenderHits);
}
finally
{
Core.Expansion = previousExpansion;
SplinteringWeapon.ClearAll();
weapon.Delete();
attacker.Delete();
defender.Delete();
}
}
[Fact]
public void ForceWalk_EndsAfterFourSecondsWhileSplinteringBleedCanContinue()
{
var previousExpansion = Core.Expansion;
var weapon = EquipSplinteringWeapon(CreateMobile());
var attacker = (Mobile)weapon.Parent;
var defender = CreateMobile(location: new Point3D(6201, 540, 0));
try
{
Core.Expansion = Expansion.SA;
weapon.OnHit(attacker, defender);
Assert.True(SplinteringWeapon.IsForceWalking(defender));
Assert.True(SplinteringWeapon.EndForceWalkForTests(attacker, defender));
Assert.False(SplinteringWeapon.IsForceWalking(defender));
Assert.Equal(1, SplinteringWeapon.ActiveContextCount);
}
finally
{
Core.Expansion = previousExpansion;
SplinteringWeapon.ClearAll();
weapon.Delete();
attacker.Delete();
defender.Delete();
}
}
[Fact]
public void ForceWalk_BlocksRunningMovementUntilEffectEnds()
{
var previousExpansion = Core.Expansion;
var weapon = EquipSplinteringWeapon(CreateMobile());
var attacker = (Mobile)weapon.Parent;
var defender = CreateMobile(location: new Point3D(6201, 540, 0));
try
{
Core.Expansion = Expansion.SA;
weapon.OnHit(attacker, defender);
var run = MovementEventArgs.Create(defender, Direction.Running);
SplinteringWeapon.OnMovement(run);
Assert.True(run.Blocked);
run.Free();
var walk = MovementEventArgs.Create(defender, Direction.North);
SplinteringWeapon.OnMovement(walk);
Assert.False(walk.Blocked);
walk.Free();
Assert.True(SplinteringWeapon.EndForceWalkForTests(attacker, defender));
var afterEffect = MovementEventArgs.Create(defender, Direction.Running);
SplinteringWeapon.OnMovement(afterEffect);
Assert.False(afterEffect.Blocked);
afterEffect.Free();
}
finally
{
Core.Expansion = previousExpansion;
SplinteringWeapon.ClearAll();
weapon.Delete();
attacker.Delete();
defender.Delete();
}
}
[Fact]
public void LogoutClearsSplinteringEffectsAndPlayerImmunity()
{
var previousExpansion = Core.Expansion;
var weapon = new TestKatana();
var attacker = CreateMobile();
var defender = CreatePlayerMobile(location: new Point3D(6201, 540, 0));
try
{
Core.Expansion = Expansion.SA;
Assert.True(SplinteringWeapon.TryProcOnEligibleHit(attacker, defender, weapon, null, 100));
Assert.True(SplinteringWeapon.IsForceWalking(defender));
Assert.True(SplinteringWeapon.IsPlayerImmune(defender));
SplinteringWeapon.Clear(defender);
Assert.False(SplinteringWeapon.IsForceWalking(defender));
Assert.False(SplinteringWeapon.IsPlayerImmune(defender));
Assert.Equal(0, SplinteringWeapon.ActiveContextCount);
}
finally
{
Core.Expansion = previousExpansion;
SplinteringWeapon.ClearAll();
weapon.Delete();
attacker.Delete();
defender.Delete();
}
}
[Fact]
public void PlayerVictim_ReceivesFifteenSecondImmunityToRepeatedSplinteringEffects()
{
var previousExpansion = Core.Expansion;
var weapon = new TestKatana();
var attacker = CreateMobile();
var defender = CreatePlayerMobile(location: new Point3D(6201, 540, 0));
try
{
Core.Expansion = Expansion.SA;
Assert.True(SplinteringWeapon.TryProcOnEligibleHit(attacker, defender, weapon, null, 100));
Assert.True(SplinteringWeapon.IsPlayerImmune(defender));
Assert.True(SplinteringWeapon.EndForceWalkForTests(attacker, defender));
Assert.False(SplinteringWeapon.IsForceWalking(defender));
Assert.False(SplinteringWeapon.TryProcOnEligibleHit(attacker, defender, weapon, null, 100));
Assert.False(SplinteringWeapon.IsForceWalking(defender));
SplinteringWeapon.ExpirePlayerImmunityForTests(defender);
Assert.False(SplinteringWeapon.IsPlayerImmune(defender));
}
finally
{
Core.Expansion = previousExpansion;
SplinteringWeapon.ClearAll();
weapon.Delete();
attacker.Delete();
defender.Delete();
}
}
[Theory]
[InlineData(5)]
[InlineData(8)]
public void NamedExcludedWeaponAbilities_DoNotTriggerSplinteringWeapon(int abilityIndex)
{
var previousExpansion = Core.Expansion;
var weapon = new TestKatana();
var attacker = CreateMobile();
var defender = CreateMobile(location: new Point3D(6201, 540, 0));
try
{
Core.Expansion = Expansion.SA;
var triggered = SplinteringWeapon.TryProcOnEligibleHit(
attacker,
defender,
weapon,
WeaponAbility.Abilities[abilityIndex],
100
);
Assert.False(triggered);
Assert.False(SplinteringWeapon.IsForceWalking(defender));
Assert.Equal(0, SplinteringWeapon.ActiveContextCount);
}
finally
{
Core.Expansion = previousExpansion;
SplinteringWeapon.ClearAll();
weapon.Delete();
attacker.Delete();
defender.Delete();
}
}
[Fact]
public void NonExcludedWeaponAbility_CanTriggerSplinteringWeapon()
{
var previousExpansion = Core.Expansion;
var weapon = new TestKatana();
var attacker = CreateMobile();
var defender = CreateMobile(location: new Point3D(6201, 540, 0));
try
{
Core.Expansion = Expansion.SA;
var triggered = SplinteringWeapon.TryProcOnEligibleHit(
attacker,
defender,
weapon,
WeaponAbility.ArmorIgnore,
100
);
Assert.True(triggered);
Assert.True(SplinteringWeapon.IsForceWalking(defender));
}
finally
{
Core.Expansion = previousExpansion;
SplinteringWeapon.ClearAll();
weapon.Delete();
attacker.Delete();
defender.Delete();
}
}
[Fact]
public void SplinteringBleed_DoesNotReplaceRegularBleedAttackContext()
{
var previousExpansion = Core.Expansion;
var weapon = new TestKatana();
var attacker = CreateMobile();
var defender = CreateMobile(location: new Point3D(6201, 540, 0));
try
{
Core.Expansion = Expansion.SA;
BleedAttack.BeginBleed(defender, attacker);
Assert.True(BleedAttack.IsBleeding(defender));
var triggered = SplinteringWeapon.TryProcOnEligibleHit(attacker, defender, weapon, null, 100);
Assert.True(triggered);
Assert.True(BleedAttack.IsBleeding(defender));
Assert.True(SplinteringWeapon.IsForceWalking(defender));
}
finally
{
Core.Expansion = previousExpansion;
BleedAttack.EndBleed(defender, false);
SplinteringWeapon.ClearAll();
weapon.Delete();
attacker.Delete();
defender.Delete();
}
}
[Fact]
public void RunicAttributeGeneration_DoesNotRollSplinteringWeapon()
{
var previousExpansion = Core.Expansion;
var weapon = new TestKatana();
try
{
Core.Expansion = Expansion.SA;
BaseRunicTool.ApplyAttributesTo(weapon, false, 0, 25, 100, 100);
Assert.Equal(0, weapon.ExtendedWeaponAttributes.SplinteringWeapon);
}
finally
{
Core.Expansion = previousExpansion;
weapon.Delete();
}
}
private static TestKatana EquipSplinteringWeapon(Mobile wielder)
{
var weapon = new TestKatana();
weapon.ExtendedWeaponAttributes.SplinteringWeapon = 100;
wielder.AddItem(weapon);
return weapon;
}
private static Mobile CreateMobile(bool player = false, Point3D location = default)
{
var mobile = new Mobile(World.NewMobile)
{
Player = player
};
mobile.DefaultMobileInit();
InitializeHits(mobile, 500, 500);
mobile.MoveToWorld(location == default ? TestLocation : location, Map.Felucca);
return mobile;
}
private static PlayerMobile CreatePlayerMobile(Point3D location)
{
var mobile = new PlayerMobile(World.NewMobile);
mobile.DefaultMobileInit();
mobile.Player = true;
mobile.Hits = mobile.HitsMax;
mobile.MoveToWorld(location, Map.Felucca);
return mobile;
}
private static void InitializeHits(Mobile mobile, int hitsMax, int hits)
{
mobile.RawStr = Math.Max(1, (hitsMax - 50) * 2);
Assert.Equal(hitsMax, mobile.HitsMax);
mobile.Hits = hits;
}
private class TestKatana : Katana
{
public override int ComputeDamage(Mobile attacker, Mobile defender) => 100;
public override int AbsorbDamage(Mobile attacker, Mobile defender, int damage) => damage;
public override void AddBlood(Mobile attacker, Mobile defender, int damage)
{
}
}
private sealed class RecordingPropertyList : IPropertyList
{
private string _interpolated = string.Empty;
public List<(int Number, string Argument)> Entries { get; } = [];
public void Reset()
{
}
public void Terminate()
{
}
public void Add(int number) => Entries.Add((number, string.Empty));
public void Add(int number, string argument) => Entries.Add((number, argument));
public void Add(ReadOnlySpan<char> argument) => Entries.Add((0, argument.ToString()));
public void Add(int number, ReadOnlySpan<char> argument) => Entries.Add((number, argument.ToString()));
public void AddChunked(ReadOnlySpan<char> text) => Entries.Add((0, text.ToString()));
public OplTextBlock TextBlock() => new(this);
public void Add(int number, int value) => Entries.Add((number, value.ToString()));
public void AddLocalized(int value) => Entries.Add((0, value.ToString()));
public void AddLocalized(int number, int value) => Entries.Add((number, value.ToString()));
public void Add(ref IPropertyList.InterpolatedStringHandler handler) => Entries.Add((0, _interpolated));
public void Add(int number, ref IPropertyList.InterpolatedStringHandler handler) =>
Entries.Add((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;
}
}