208 lines
7.9 KiB
C#
208 lines
7.9 KiB
C#
using PkmnLib.Dynamic.Models;
|
|
using PkmnLib.Dynamic.Models.Choices;
|
|
using PkmnLib.Dynamic.ScriptHandling;
|
|
using PkmnLib.Dynamic.ScriptHandling.Registry;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
|
|
|
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|
|
|
/// <summary>
|
|
/// Tests for the <see cref="Endure"/> move script and its <see cref="EndureEffect"/>.
|
|
/// Gen VII Bulbapedia behavior: "For the rest of the turn, Endure allows the user to survive any attack
|
|
/// that would cause it to faint, leaving the user with 1 HP instead." Like other protection moves, "if it
|
|
/// or other protection moves are used consecutively, its success rate decreases."
|
|
/// </summary>
|
|
public class EndureTests
|
|
{
|
|
/// <summary>
|
|
/// Creates a fully mocked setup for driving <see cref="Endure"/>'s inherited
|
|
/// <see cref="ProtectionScript.OnSecondaryEffect"/>. The <c>target</c> of the secondary effect is the
|
|
/// Pokémon using Endure itself, as the move is self-targeted.
|
|
/// </summary>
|
|
private static (Endure script, IExecutingMove move, IPokemon target, IHitData hitData, IScriptSet volatileSet)
|
|
CreateProtectSetup(bool userMovesLast, float randomRoll)
|
|
{
|
|
var script = new Endure();
|
|
var move = Substitute.For<IExecutingMove>();
|
|
var target = Substitute.For<IPokemon>();
|
|
var hitData = Substitute.For<IHitData>();
|
|
move.GetHitData(target, 0).Returns(hitData);
|
|
|
|
// A queue with a remaining choice means another Pokémon still has to move after the user.
|
|
var queue = userMovesLast ? new BattleChoiceQueue([]) : new BattleChoiceQueue([Substitute.For<IMoveChoice>()]);
|
|
|
|
var random = Substitute.For<IBattleRandom>();
|
|
random.GetFloat().Returns(randomRoll);
|
|
|
|
var battle = Substitute.For<IBattle>();
|
|
battle.ChoiceQueue.Returns(queue);
|
|
battle.Random.Returns(random);
|
|
|
|
var battleData = Substitute.For<IPokemonBattleData>();
|
|
battleData.Battle.Returns(battle);
|
|
target.BattleData.Returns(battleData);
|
|
|
|
target.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
|
IScriptSet volatileSet = new ScriptSet(target);
|
|
target.Volatile.Returns(volatileSet);
|
|
|
|
return (script, move, target, hitData, volatileSet);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "For the rest of the turn, Endure allows the user to survive any attack that would
|
|
/// cause it to faint" — using the move attaches the <see cref="EndureEffect"/> to the user.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_FirstUse_AddsEndureEffect()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, hitData, volatileSet) = CreateProtectSetup(false, 0.5f);
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(volatileSet.Contains(ScriptUtils.ResolveName<EndureEffect>())).IsTrue();
|
|
hitData.DidNotReceive().Fail();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: Endure "always fails if the user is the last to act in the turn" — with no remaining
|
|
/// choices in the queue there is nothing left to protect against, and the move fails.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_UserMovesLast_FailsHit()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, hitData, volatileSet) = CreateProtectSetup(true, 0.0f);
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
hitData.Received(1).Fail();
|
|
await Assert.That(volatileSet.Contains(ScriptUtils.ResolveName<EndureEffect>())).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "If it or other protection moves are used consecutively, its success rate decreases."
|
|
/// On the second consecutive use the success chance is 1/3, so a roll above that fails.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_SecondConsecutiveUseWithHighRoll_FailsHit()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, hitData, volatileSet) = CreateProtectSetup(false, 0.5f);
|
|
volatileSet.Add(new ProtectionFailureScript { ProtectTurns = 1 });
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
hitData.Received(1).Fail();
|
|
await Assert.That(volatileSet.Contains(ScriptUtils.ResolveName<EndureEffect>())).IsFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "If it or other protection moves are used consecutively, its success rate decreases."
|
|
/// On the second consecutive use the success chance is 1/3, so a roll below that still succeeds.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnSecondaryEffect_SecondConsecutiveUseWithLowRoll_AddsEndureEffect()
|
|
{
|
|
// Arrange
|
|
var (script, move, target, hitData, volatileSet) = CreateProtectSetup(false, 0.2f);
|
|
volatileSet.Add(new ProtectionFailureScript { ProtectTurns = 1 });
|
|
|
|
// Act
|
|
script.OnSecondaryEffect(move, target, 0);
|
|
|
|
// Assert
|
|
await Assert.That(volatileSet.Contains(ScriptUtils.ResolveName<EndureEffect>())).IsTrue();
|
|
hitData.DidNotReceive().Fail();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: "Endure allows the user to survive any attack that would cause it to faint, leaving
|
|
/// the user with 1 HP instead." Damage exceeding the user's current HP is reduced to leave 1 HP.
|
|
/// </summary>
|
|
[Test, Arguments(100u, 150u, 99u), Arguments(50u, 51u, 49u), Arguments(2u, 9999u, 1u)]
|
|
public async Task ChangeIncomingDamage_LethalDamage_LeavesUserWithOneHp(uint currentHealth, uint incomingDamage,
|
|
uint expectedDamage)
|
|
{
|
|
// Arrange
|
|
var effect = new EndureEffect();
|
|
var pokemon = Substitute.For<IPokemon>();
|
|
pokemon.CurrentHealth.Returns(currentHealth);
|
|
var damage = incomingDamage;
|
|
|
|
// Act
|
|
effect.ChangeIncomingDamage(pokemon, DamageSource.MoveDamage, ref damage);
|
|
|
|
// Assert
|
|
await Assert.That(damage).IsEqualTo(expectedDamage);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: Endure lets the user "survive any attack that would cause it to faint" — a hit for
|
|
/// exactly the user's current HP would faint it, so it too must be reduced to leave 1 HP.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task ChangeIncomingDamage_DamageEqualToCurrentHp_LeavesUserWithOneHp()
|
|
{
|
|
// Arrange
|
|
var effect = new EndureEffect();
|
|
var pokemon = Substitute.For<IPokemon>();
|
|
pokemon.CurrentHealth.Returns(100u);
|
|
var damage = 100u;
|
|
|
|
// Act
|
|
effect.ChangeIncomingDamage(pokemon, DamageSource.MoveDamage, ref damage);
|
|
|
|
// Assert
|
|
await Assert.That(damage).IsEqualTo(99u);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: Endure only affects attacks "that would cause it to faint" — non-lethal damage passes
|
|
/// through unchanged.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task ChangeIncomingDamage_NonLethalDamage_DamageUnchanged()
|
|
{
|
|
// Arrange
|
|
var effect = new EndureEffect();
|
|
var pokemon = Substitute.For<IPokemon>();
|
|
pokemon.CurrentHealth.Returns(100u);
|
|
var damage = 50u;
|
|
|
|
// Act
|
|
effect.ChangeIncomingDamage(pokemon, DamageSource.MoveDamage, ref damage);
|
|
|
|
// Assert
|
|
await Assert.That(damage).IsEqualTo(50u);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bulbapedia: the protection lasts "for the rest of the turn" — the effect removes itself at the end
|
|
/// of the turn.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task OnEndTurn_EffectRemovesItself()
|
|
{
|
|
// Arrange
|
|
var owner = Substitute.For<IPokemon>();
|
|
owner.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
|
IScriptSet ownerVolatile = new ScriptSet(owner);
|
|
var effect = new EndureEffect();
|
|
ownerVolatile.Add(effect);
|
|
|
|
// Act
|
|
effect.OnEndTurn(owner, Substitute.For<IBattle>());
|
|
|
|
// Assert
|
|
await Assert.That(ownerVolatile.Contains(ScriptUtils.ResolveName<EndureEffect>())).IsFalse();
|
|
}
|
|
} |