This commit is contained in:
243
Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/DrainTests.cs
Normal file
243
Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/DrainTests.cs
Normal file
@@ -0,0 +1,243 @@
|
||||
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)).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)).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)).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)).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)).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)).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)).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)).IsEqualTo(50u);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user