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.Side;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
///
/// Tests for the script, which implements Assurance (and
/// the other moves whose power doubles when the target was already damaged this turn, such as Avalanche
/// and Revenge). Behavior is verified against the Bulbapedia page for Assurance:
/// "Assurance inflicts damage and will deal double damage if the target has already received damage on
/// the turn it is used."
///
public class DoublePowerIfTargetDamagedInTurnTests
{
///
/// Creates a fully mocked test setup where the target sits on side 0 whose volatile scripts are a
/// real .
///
private static (DoublePowerIfTargetDamagedInTurn script, IExecutingMove move, IPokemon target, ScriptSet sideScripts
) CreateTestSetup()
{
var script = new DoublePowerIfTargetDamagedInTurn();
var move = Substitute.For();
var side = Substitute.For();
var sideScripts = new ScriptSet(side);
side.VolatileScripts.Returns(sideScripts);
side.GetScripts().Returns(_ => new ScriptIterator(new List>()));
var battle = Substitute.For();
battle.Sides.Returns(new[] { side });
var user = Substitute.For();
var userBattleData = Substitute.For();
userBattleData.Battle.Returns(battle);
user.BattleData.Returns(userBattleData);
move.User.Returns(user);
var target = Substitute.For();
var targetBattleData = Substitute.For();
targetBattleData.Battle.Returns(battle);
targetBattleData.SideIndex.Returns((byte)0);
target.BattleData.Returns(targetBattleData);
return (script, move, target, sideScripts);
}
///
/// Bulbapedia: "will deal double damage if the target has already received damage on the turn it is
/// used." Before the turn starts, a tracking script is placed on the targeted side to record which
/// Pokémon take damage this turn.
///
[Test]
public async Task OnBeforeTurnStart_MoveChoice_AddsTrackingDataToTargetSide()
{
// Arrange
var (script, move, _, sideScripts) = CreateTestSetup();
var user = move.User;
var choice = Substitute.For();
choice.User.Returns(user);
choice.TargetSide.Returns((byte)0);
// Act
script.OnBeforeTurnStart(choice);
// Assert
await Assert.That(sideScripts.Contains(ScriptUtils.ResolveName()))
.IsTrue();
}
///
/// Technical test: a non-move turn choice (e.g. a switch) does not place the tracking script.
///
[Test]
public async Task OnBeforeTurnStart_NonMoveChoice_DoesNotAddTrackingData()
{
// Arrange
var (script, _, _, sideScripts) = CreateTestSetup();
var choice = Substitute.For();
// Act
script.OnBeforeTurnStart(choice);
// Assert
await Assert.That(sideScripts.Contains(ScriptUtils.ResolveName()))
.IsFalse();
}
///
/// Bulbapedia: "will deal double damage if the target has already received damage on the turn it is
/// used." When the tracking data recorded the target as hit this turn, the base power doubles.
///
[Test]
public async Task ChangeBasePower_TargetDamagedThisTurn_BasePowerDoubles()
{
// Arrange
var (script, move, target, sideScripts) = CreateTestSetup();
var data = new DoublePowerIfTargetDamagedInTurnData();
sideScripts.Add(data);
data.OnDamage(target, DamageSource.MoveDamage, 100, 80);
ushort basePower = 60;
// Act
script.ChangeBasePower(move, target, 0, ref basePower);
// Assert
await Assert.That(basePower).IsEqualTo((ushort)120);
}
///
/// Bulbapedia: the power only doubles "if the target has already received damage on the turn it is
/// used." An undamaged target takes regular base power.
///
[Test]
public async Task ChangeBasePower_TargetNotDamagedThisTurn_BasePowerUnchanged()
{
// Arrange
var (script, move, target, sideScripts) = CreateTestSetup();
sideScripts.Add(new DoublePowerIfTargetDamagedInTurnData());
ushort basePower = 60;
// Act
script.ChangeBasePower(move, target, 0, ref basePower);
// Assert
await Assert.That(basePower).IsEqualTo((ushort)60);
}
///
/// Bulbapedia: only damage to the target itself counts. Damage recorded for a different Pokémon on
/// the same side does not double the power against this target.
///
[Test]
public async Task ChangeBasePower_OtherPokemonDamagedThisTurn_BasePowerUnchanged()
{
// Arrange
var (script, move, target, sideScripts) = CreateTestSetup();
var data = new DoublePowerIfTargetDamagedInTurnData();
sideScripts.Add(data);
data.OnDamage(Substitute.For(), DamageSource.MoveDamage, 100, 80);
ushort basePower = 60;
// Act
script.ChangeBasePower(move, target, 0, ref basePower);
// Assert
await Assert.That(basePower).IsEqualTo((ushort)60);
}
///
/// Bulbapedia: "This includes self-inflicted and indirect damage, such as damage from a Life Orb,
/// recoil, crash damage, or hurting itself in confusion." Any damage source recorded by the tracking
/// data counts.
///
[Test, Arguments(DamageSource.Misc), Arguments(DamageSource.Confusion)]
public async Task ChangeBasePower_TargetTookIndirectDamage_BasePowerDoubles(DamageSource source)
{
// Arrange
var (script, move, target, sideScripts) = CreateTestSetup();
var data = new DoublePowerIfTargetDamagedInTurnData();
sideScripts.Add(data);
data.OnDamage(target, source, 100, 90);
ushort basePower = 60;
// Act
script.ChangeBasePower(move, target, 0, ref basePower);
// Assert
await Assert.That(basePower).IsEqualTo((ushort)120);
}
///
/// The damage tracking only spans a single turn: at the end of the turn the tracking data removes
/// itself, so damage from a previous turn cannot double the power.
///
[Test]
public async Task OnEndTurn_TurnEnds_TrackingDataRemovesItself()
{
// Arrange
var (_, _, _, sideScripts) = CreateTestSetup();
var data = new DoublePowerIfTargetDamagedInTurnData();
sideScripts.Add(data);
var battle = Substitute.For();
// Act
data.OnEndTurn(Substitute.For(), battle);
// Assert
await Assert.That(sideScripts.Contains(ScriptUtils.ResolveName()))
.IsFalse();
}
///
/// Technical test: without battle data on the user (outside of battle) the base power is unchanged
/// and nothing throws.
///
[Test]
public async Task ChangeBasePower_UserHasNoBattleData_BasePowerUnchanged()
{
// Arrange
var script = new DoublePowerIfTargetDamagedInTurn();
var move = Substitute.For();
var user = Substitute.For();
user.BattleData.Returns((IPokemonBattleData?)null);
move.User.Returns(user);
var target = Substitute.For();
ushort basePower = 60;
// Act
script.ChangeBasePower(move, target, 0, ref basePower);
// Assert
await Assert.That(basePower).IsEqualTo((ushort)60);
}
}