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); } }