using PkmnLib.Dynamic.Models; using PkmnLib.Dynamic.Models.Choices; using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Plugin.Gen7.Scripts.MoveVolatile; using PkmnLib.Plugin.Gen7.Scripts.Side; using PkmnLib.Static.Utils; namespace PkmnLib.Plugin.Gen7.Tests.Scripts.MoveVolatile; /// /// Tests for the volatile script, which implements the combined move of /// Fire Pledge and Water Pledge, and the it creates. /// Bulbapedia (Fire Pledge): "If Fire Pledge and Water Pledge are used in the same turn, the ally moving /// second will use Water Pledge with a power of 150, and create a rainbow on the user's side of the field /// for four turns." /// public class FireWaterPledgeMoveTests { /// /// Creates a fully mocked test setup. The user's battle side gets a real as its /// volatile script set, so the rainbow created by the combined move can be inspected and driven. /// private static (FireWaterPledgeMove script, IExecutingMove move, IPokemon user, IBattleSide side, IScriptSet sideVolatile) CreateTestSetup() { var script = new FireWaterPledgeMove(); var move = Substitute.For(); var side = Substitute.For(); side.GetScripts().Returns(_ => new ScriptIterator(new List>())); var sideVolatile = new ScriptSet(side); side.VolatileScripts.Returns(sideVolatile); var battleData = Substitute.For(); battleData.BattleSide.Returns(side); var user = Substitute.For(); user.BattleData.Returns(battleData); move.User.Returns(user); return (script, move, user, side, sideVolatile); } /// /// Bulbapedia: "the ally moving second will use Water Pledge" when Fire Pledge and Water Pledge are /// combined. The marked choice's move is replaced with Water Pledge. /// [Test] public async Task ChangeMove_CombinedMove_ExecutesWaterPledge() { // Arrange var (script, _, _, _, _) = CreateTestSetup(); var choice = Substitute.For(); StringKey moveName = "fire_pledge"; // Act script.ChangeMove(choice, ref moveName); // Assert await Assert.That(moveName).IsEqualTo(new StringKey("water_pledge")); } /// /// Bulbapedia: "the ally moving second will use Water Pledge with a power of 150". /// [Test] public async Task ChangeBasePower_CombinedMove_PowerIs150() { // Arrange var (script, move, _, _, _) = CreateTestSetup(); var target = Substitute.For(); ushort basePower = 80; // Act script.ChangeBasePower(move, target, 0, ref basePower); // Assert await Assert.That(basePower).IsEqualTo((ushort)150); } /// /// Bulbapedia: the combined move will "create a rainbow on the user's side of the field for four turns." /// The is added to the user's side. /// [Test] public async Task OnSecondaryEffect_CombinedMoveHits_AddsRainbowToUsersSide() { // Arrange var (script, move, _, _, sideVolatile) = CreateTestSetup(); var target = Substitute.For(); // Act script.OnSecondaryEffect(move, target, 0); // Assert await Assert.That(sideVolatile.Get()).IsNotNull(); } /// /// Technical test: if the user has no , no rainbow is created and the /// script does not throw. /// [Test] public async Task OnSecondaryEffect_UserHasNoBattleData_DoesNothing() { // Arrange var (script, move, user, _, sideVolatile) = CreateTestSetup(); user.BattleData.Returns((IPokemonBattleData?)null); var target = Substitute.For(); // Act script.OnSecondaryEffect(move, target, 0); // Assert await Assert.That(sideVolatile.Count).IsEqualTo(0); } /// /// Bulbapedia: "The rainbow doubles the probability of additional effects taking place for moves used /// by that side of the field". /// [Test, Arguments(10f, 20f), Arguments(30f, 60f), Arguments(50f, 100f)] public async Task RainbowEffect_ChangeEffectChance_DoublesTheChance(float chance, float expected) { // Arrange var (script, move, _, _, sideVolatile) = CreateTestSetup(); var target = Substitute.For(); script.OnSecondaryEffect(move, target, 0); var rainbow = sideVolatile.Get()!; // Act rainbow.ChangeEffectChance(move, target, 0, ref chance); // Assert await Assert.That(chance).IsEqualTo(expected); } /// /// Bulbapedia: the rainbow is created "on the user's side of the field for four turns." After the /// fourth end-of-turn since its creation the rainbow must be gone. /// [Test] public async Task RainbowEffect_AfterFourEndOfTurns_RainbowHasFaded() { // Arrange var (script, move, _, side, sideVolatile) = CreateTestSetup(); var target = Substitute.For(); script.OnSecondaryEffect(move, target, 0); var rainbow = sideVolatile.Get()!; var battle = Substitute.For(); // Act - four end-of-turn ticks, counting the turn the rainbow was created for (var i = 0; i < 4; i++) rainbow.OnEndTurn(side, battle); // Assert await Assert.That(sideVolatile.Contains("rainbow_effect")).IsFalse(); } /// /// Technical test: the rainbow does not stay around forever; it removes itself from the side once its /// counter runs out (currently after the fifth end-of-turn). /// [Test] public async Task RainbowEffect_AfterFiveEndOfTurns_HasRemovedItself() { // Arrange var (script, move, _, side, sideVolatile) = CreateTestSetup(); var target = Substitute.For(); script.OnSecondaryEffect(move, target, 0); var rainbow = sideVolatile.Get()!; var battle = Substitute.For(); // Act for (var i = 0; i < 5; i++) rainbow.OnEndTurn(side, battle); // Assert await Assert.That(sideVolatile.Contains("rainbow_effect")).IsFalse(); } }