using PkmnLib.Dynamic.Models; using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Dynamic.ScriptHandling.Registry; using PkmnLib.Plugin.Gen7.Scripts.Moves; using PkmnLib.Plugin.Gen7.Scripts.Pokemon; using PkmnLib.Static; using PkmnLib.Static.Moves; namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves; /// /// Tests for the move script and the it applies. /// Gen VII Bulbapedia behavior: Charge doubles the power of the user's next Electric-type move, /// and "It also raises the user's Special Defense stat by one stage." /// public class ChargeTests { private static (Charge script, IExecutingMove move, IPokemon user, IScriptSet userVolatile) CreateTestSetup() { var script = new Charge(); var move = Substitute.For(); var user = Substitute.For(); // Use a real script set so the volatile script added by Charge can be inspected afterwards. var userVolatile = new ScriptSet(user); user.Volatile.Returns(userVolatile); user.GetScripts().Returns(_ => new ScriptIterator(new List>())); move.User.Returns(user); return (script, move, user, userVolatile); } /// /// Creates an executing move of the given type whose damage modifier can be changed by /// . /// private static (IExecutingMove move, IPokemon target) CreateExecutingMoveOfType(string typeName) { var library = LibraryHelpers.LoadLibrary(); var battle = Substitute.For(); battle.Library.Returns(library); var battleData = Substitute.For(); battleData.Battle.Returns(battle); var target = Substitute.For(); target.BattleData.Returns(battleData); library.StaticLibrary.Types.TryGetTypeIdentifier(typeName, out var typeIdentifier); var useMove = Substitute.For(); useMove.MoveType.Returns(typeIdentifier); var move = Substitute.For(); move.UseMove.Returns(useMove); return (move, target); } /// /// Bulbapedia: "It also raises the user's Special Defense stat by one stage." /// [Test] public async Task OnSecondaryEffect_Used_RaisesUserSpecialDefenseByOneStage() { // Arrange var (script, move, user, _) = CreateTestSetup(); // Act script.OnSecondaryEffect(move, Substitute.For(), 0); // Assert var boost = user.ReceivedCalls().SingleOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost"); await Assert.That(boost).IsNotNull(); await Assert.That((Statistic)boost!.GetArguments()[0]!).IsEqualTo(Statistic.SpecialDefense); await Assert.That((sbyte)boost.GetArguments()[1]!).IsEqualTo((sbyte)1); await Assert.That((bool)boost.GetArguments()[2]!).IsTrue(); } /// /// Bulbapedia: Charge doubles the power of the user's next Electric-type move. Using Charge attaches /// the volatile script to the user. /// [Test] public async Task OnSecondaryEffect_Used_AddsChargeEffectToUser() { // Arrange var (script, move, _, userVolatile) = CreateTestSetup(); // Act script.OnSecondaryEffect(move, Substitute.For(), 0); // Assert await Assert.That(userVolatile.Contains(ScriptUtils.ResolveName())).IsTrue(); } /// /// Bulbapedia: "If the user's next move is Electric-type", its power is doubled. /// [Test] public async Task ChargeEffect_ElectricMove_DoublesDamageModifier() { // Arrange var effect = new ChargeEffect(); var (move, target) = CreateExecutingMoveOfType("electric"); var modifier = 1.0f; // Act effect.ChangeDamageModifier(move, target, 0, ref modifier); // Assert await Assert.That(modifier).IsEqualTo(2.0f); } /// /// Bulbapedia: only Electric-type moves benefit — a non-Electric move is not boosted by Charge. /// [Test] public async Task ChargeEffect_NonElectricMove_DoesNotChangeDamageModifier() { // Arrange var effect = new ChargeEffect(); var (move, target) = CreateExecutingMoveOfType("water"); var modifier = 1.0f; // Act effect.ChangeDamageModifier(move, target, 0, ref modifier); // Assert await Assert.That(modifier).IsEqualTo(1.0f); } /// /// Bulbapedia: Charge boosts the user's *next* Electric-type move — the effect lasts through the end /// of the following turn and then wears off. The effect survives the end of the turn Charge was used /// on and removes itself at the end of the next turn. /// [Test] public async Task ChargeEffect_OnEndTurn_RemovesItselfAfterTheNextTurn() { // Arrange var (script, move, user, userVolatile) = CreateTestSetup(); script.OnSecondaryEffect(move, Substitute.For(), 0); await Assert.That(userVolatile.TryGet(out var effect)).IsTrue(); var battle = Substitute.For(); // Act & Assert - end of the turn of use: effect remains effect!.OnEndTurn(user, battle); await Assert.That(userVolatile.Contains(ScriptUtils.ResolveName())).IsTrue(); // Act & Assert - end of the following turn: effect is removed effect.OnEndTurn(user, battle); await Assert.That(userVolatile.Contains(ScriptUtils.ResolveName())).IsFalse(); } }