243 lines
8.6 KiB
C#
243 lines
8.6 KiB
C#
using PkmnLib.Dynamic.Models;
|
|
using PkmnLib.Dynamic.ScriptHandling;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|
using PkmnLib.Static.Utils;
|
|
|
|
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|
|
|
/// <summary>
|
|
/// Tests for the <see cref="Drain"/> script, which implements Absorb (and the other HP-draining moves).
|
|
/// Behavior is verified against the Bulbapedia page for Absorb.
|
|
/// </summary>
|
|
public class DrainTests
|
|
{
|
|
/// <summary>
|
|
/// Creates a fully mocked test setup for Drain tests.
|
|
/// </summary>
|
|
private static (Drain drain, IExecutingMove move, IPokemon target, IPokemon user) CreateTestSetup(uint damage,
|
|
bool holdsBigRoot = false, Script[]? targetScripts = null)
|
|
{
|
|
var drain = new Drain();
|
|
var move = Substitute.For<IExecutingMove>();
|
|
var target = Substitute.For<IPokemon>();
|
|
var hitData = Substitute.For<IHitData>();
|
|
hitData.Damage.Returns(damage);
|
|
move.GetHitData(target, 0).Returns(hitData);
|
|
|
|
// RunScriptHook iterates the target's scripts; give the mock a real iterator so the
|
|
// hook pass runs (empty unless the test attaches scripts such as Liquid Ooze).
|
|
var containers = (targetScripts ?? []).Select(IEnumerable<ScriptContainer> (s) => new ScriptContainer(s))
|
|
.ToArray();
|
|
target.GetScripts().Returns(_ => new ScriptIterator(containers));
|
|
|
|
var user = Substitute.For<IPokemon>();
|
|
if (holdsBigRoot)
|
|
user.HasHeldItem("big_root").Returns(true);
|
|
move.User.Returns(user);
|
|
|
|
return (drain, move, target, user);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper to extract the heal amount from the user's received Heal calls.
|
|
/// </summary>
|
|
private static uint? GetHealAmount(IPokemon user)
|
|
{
|
|
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Heal");
|
|
return call != null ? (uint)call.GetArguments()[0]! : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper to extract the damage amount from the user's received Damage calls.
|
|
/// </summary>
|
|
private static uint? GetDamageAmount(IPokemon user)
|
|
{
|
|
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
|
return call != null ? (uint)call.GetArguments()[0]! : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper to extract the damage source from the user's received Damage calls.
|
|
/// </summary>
|
|
private static DamageSource? GetDamageSource(IPokemon user)
|
|
{
|
|
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
|
return call != null ? (DamageSource)call.GetArguments()[1]! : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Absorb inflicts damage, and up to 50% of the damage dealt to the target is restored to the
|
|
/// user as HP".
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_DamageDealt_UserHealsHalfOfDamage()
|
|
{
|
|
// Arrange
|
|
var (drain, move, target, user) = CreateTestSetup(100);
|
|
|
|
// Act
|
|
drain.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(GetHealAmount(user)!.Value).IsEqualTo(50u);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "up to 50% of the damage dealt to the target is restored to the user as HP".
|
|
/// Tests various damage values to ensure proper integer truncation of the halved amount.
|
|
/// </summary>
|
|
[Test, Arguments(200u, 100u), Arguments(51u, 25u), Arguments(99u, 49u), Arguments(3u, 1u)]
|
|
public async Task OnSecondaryEffect_DamageDealt_HealCalculation(uint damage, uint expectedHeal)
|
|
{
|
|
// Arrange
|
|
var (drain, move, target, user) = CreateTestSetup(damage);
|
|
|
|
// Act
|
|
drain.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(GetHealAmount(user)!.Value).IsEqualTo(expectedHeal);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "up to 50% of the damage dealt to the target is restored to the user as HP (but not less
|
|
/// than 1 HP)".
|
|
/// A move dealing 1 damage should still restore 1 HP.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_DamageOfOne_HealsAtLeastOneHp()
|
|
{
|
|
// Arrange
|
|
var (drain, move, target, user) = CreateTestSetup(1);
|
|
|
|
// Act
|
|
drain.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(GetHealAmount(user)!.Value).IsEqualTo(1u);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "If the user is holding a Big Root, the HP restored is increased by 30% (making the restored
|
|
/// HP 65% of the damage dealt)."
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_UserHoldingBigRoot_HealIncreasedByThirtyPercent()
|
|
{
|
|
// Arrange
|
|
var (drain, move, target, user) = CreateTestSetup(100, true);
|
|
|
|
// Act
|
|
drain.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert - 65% of 100 damage
|
|
await Assert.That(GetHealAmount(user)!.Value).IsEqualTo(65u);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "When used on a Pokémon with the Liquid Ooze Ability, the user will lose the amount of HP it
|
|
/// would have gained instead."
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_TargetHasLiquidOoze_UserLosesHpInsteadOfHealing()
|
|
{
|
|
// Arrange
|
|
var (drain, move, target, user) = CreateTestSetup(100, targetScripts: [new LiquidOoze()]);
|
|
|
|
// Act
|
|
drain.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert - user takes the 50 HP it would have gained, and is not healed
|
|
await Assert.That(GetDamageAmount(user)!.Value).IsEqualTo(50u);
|
|
await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Heal")).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "the user will lose the amount of HP it would have gained instead."
|
|
/// The HP loss caused by Liquid Ooze is indirect damage, not move damage.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_TargetHasLiquidOoze_UsesMiscDamageSource()
|
|
{
|
|
// Arrange
|
|
var (drain, move, target, user) = CreateTestSetup(100, targetScripts: [new LiquidOoze()]);
|
|
|
|
// Act
|
|
drain.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(GetDamageSource(user)!.Value).IsEqualTo(DamageSource.Misc);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "the user will lose the amount of HP it would have gained instead."
|
|
/// With a Big Root held, the amount it would have gained is 65% of the damage, so that is what is lost.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_LiquidOozeAndBigRoot_UserLosesBoostedAmount()
|
|
{
|
|
// Arrange
|
|
var (drain, move, target, user) = CreateTestSetup(100, true, [new LiquidOoze()]);
|
|
|
|
// Act
|
|
drain.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(GetDamageAmount(user)!.Value).IsEqualTo(65u);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Technical test: without initialization parameters the drain modifier defaults to 50%, matching Absorb.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnInitialize_NullParameters_DrainModifierDefaultsToHalf()
|
|
{
|
|
// Arrange
|
|
var drain = new Drain();
|
|
|
|
// Act
|
|
drain.OnInitialize(null);
|
|
|
|
// Assert
|
|
await Assert.That(drain.DrainModifier).IsEqualTo(0.5f);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Technical test: a "drain_modifier" parameter overrides the default drain fraction.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnInitialize_DrainModifierParameter_OverridesDefault()
|
|
{
|
|
// Arrange
|
|
var drain = new Drain();
|
|
var parameters = new Dictionary<StringKey, object?> { { "drain_modifier", 0.75f } };
|
|
|
|
// Act
|
|
drain.OnInitialize(parameters);
|
|
|
|
// Assert
|
|
await Assert.That(drain.DrainModifier).IsEqualTo(0.75f);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "up to 50% of the damage dealt to the target is restored to the user as HP".
|
|
/// Integration check: initializing the script with Absorb's actual effect parameters from the Gen7 data
|
|
/// results in a 50% drain.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_InitializedWithAbsorbData_HealsHalfOfDamage()
|
|
{
|
|
// Arrange
|
|
var library = LibraryHelpers.LoadLibrary();
|
|
await Assert.That(library.StaticLibrary.Moves.TryGet("absorb", out var absorb)).IsTrue();
|
|
var (drain, move, target, user) = CreateTestSetup(100);
|
|
|
|
// Act
|
|
drain.OnInitialize(absorb!.SecondaryEffect!.Parameters);
|
|
drain.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(GetHealAmount(user)!.Value).IsEqualTo(50u);
|
|
}
|
|
} |