150 lines
5.1 KiB
C#
150 lines
5.1 KiB
C#
using PkmnLib.Dynamic.Models;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|
using PkmnLib.Static;
|
|
using PkmnLib.Static.Utils;
|
|
|
|
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|
|
|
/// <summary>
|
|
/// Tests for the <see cref="FloralHealing"/> move script.
|
|
/// Gen VII Bulbapedia behavior: Floral Healing restores up to half of the target's maximum HP, or up to
|
|
/// two thirds when Grassy Terrain is in effect.
|
|
/// </summary>
|
|
public class FloralHealingTests
|
|
{
|
|
private static (FloralHealing script, IExecutingMove move, IPokemon target) CreateTestSetup(uint maxHp,
|
|
string? terrainName = null, bool isFainted = false, bool hasBattleData = true)
|
|
{
|
|
var script = new FloralHealing();
|
|
var move = Substitute.For<IExecutingMove>();
|
|
|
|
var battle = Substitute.For<IBattle>();
|
|
battle.TerrainName.Returns(terrainName == null ? null : new StringKey?(new StringKey(terrainName)));
|
|
|
|
var target = Substitute.For<IPokemon>();
|
|
target.IsFainted.Returns(isFainted);
|
|
target.BoostedStats.Returns(new StatisticSet<uint>(maxHp, 0, 0, 0, 0, 0));
|
|
if (hasBattleData)
|
|
{
|
|
var battleData = Substitute.For<IPokemonBattleData>();
|
|
battleData.Battle.Returns(battle);
|
|
target.BattleData.Returns(battleData);
|
|
}
|
|
else
|
|
{
|
|
target.BattleData.Returns((IPokemonBattleData?)null);
|
|
}
|
|
|
|
return (script, move, target);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper to extract the heal amount from the target's received Heal calls.
|
|
/// </summary>
|
|
private static uint? GetHealAmount(IPokemon target)
|
|
{
|
|
var call = target.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Heal");
|
|
return call != null ? (uint)call.GetArguments()[0]! : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Floral Healing restores up to ½ of the target's maximum HP."
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_NoTerrain_HealsHalfOfMaximumHp()
|
|
{
|
|
// Arrange
|
|
var (script, move, target) = CreateTestSetup(100);
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(GetHealAmount(target)!.Value).IsEqualTo(50u);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Floral Healing restores up to ½ of the target's maximum HP."
|
|
/// Various maximum HP values verify the fractional amount is truncated to a whole number.
|
|
/// </summary>
|
|
[Test, Arguments(200u, 100u), Arguments(101u, 50u), Arguments(99u, 49u), Arguments(3u, 1u)]
|
|
public async Task OnSecondaryEffect_NoTerrain_HealCalculation(uint maxHp, uint expectedHeal)
|
|
{
|
|
// Arrange
|
|
var (script, move, target) = CreateTestSetup(maxHp);
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(GetHealAmount(target)!.Value).IsEqualTo(expectedHeal);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "If Grassy Terrain is in effect, Floral Healing will restore up to ⅔ of the target's
|
|
/// maximum HP."
|
|
/// </summary>
|
|
[Test, Arguments(99u, 66u), Arguments(100u, 66u), Arguments(3u, 2u)]
|
|
public async Task OnSecondaryEffect_GrassyTerrain_HealsTwoThirdsOfMaximumHp(uint maxHp, uint expectedHeal)
|
|
{
|
|
// Arrange
|
|
var (script, move, target) = CreateTestSetup(maxHp, "grassy_terrain");
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(GetHealAmount(target)!.Value).IsEqualTo(expectedHeal);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: only "If Grassy Terrain is in effect" is the restored amount increased to ⅔; any other
|
|
/// terrain keeps the regular ½ of maximum HP.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_OtherTerrain_HealsHalfOfMaximumHp()
|
|
{
|
|
// Arrange
|
|
var (script, move, target) = CreateTestSetup(100, "misty_terrain");
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(GetHealAmount(target)!.Value).IsEqualTo(50u);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: Floral Healing "restores" HP of the target; a fainted Pokémon can no longer be healed,
|
|
/// so no heal is applied when the target <see cref="IPokemon.IsFainted"/>.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_TargetFainted_DoesNotHeal()
|
|
{
|
|
// Arrange
|
|
var (script, move, target) = CreateTestSetup(100, isFainted: true);
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Heal")).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Technical test: when the target has no <see cref="IPokemon.BattleData"/> (it is not in a battle),
|
|
/// the script does nothing instead of throwing.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_NullBattleData_DoesNotHeal()
|
|
{
|
|
// Arrange
|
|
var (script, move, target) = CreateTestSetup(100, hasBattleData: false);
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Heal")).IsFalse();
|
|
}
|
|
} |