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; namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves; /// /// Tests for the move script and its volatile effect script /// . /// Gen VII Bulbapedia behavior: "Laser Focus causes the user's move to result in a critical hit until the /// end of the next turn, unless that move's target has Battle Armor, Shell Armor, or is under the effect /// of Lucky Chant." /// public class LaserFocusTests { /// /// Creates a real hosting the given effect, so that /// calls can be observed through . /// private static IScriptSet CreateHostedSet(Script effect) { var owner = Substitute.For(); owner.GetScripts().Returns(_ => new ScriptIterator(Array.Empty>())); IScriptSet set = new ScriptSet(owner); set.Add(effect); return set; } /// /// Bulbapedia: "Laser Focus causes the user's move to result in a critical hit" — using the move places /// the on the target (Laser Focus targets the user itself). /// [Test] public async Task OnSecondaryEffect_Used_AddsLaserFocusEffectToTarget() { // Arrange var script = new LaserFocus(); var move = Substitute.For(); var target = Substitute.For(); var targetVolatile = Substitute.For(); target.Volatile.Returns(targetVolatile); // Act script.OnSecondaryEffect(move, target, 0); // Assert targetVolatile.Received(1).Add(Arg.Any()); await Assert.That(targetVolatile.ReceivedCalls().Count(c => c.GetMethodInfo().Name == "Add")).IsEqualTo(1); } /// /// Bulbapedia: the user's move will "result in a critical hit" — the effect raises the critical stage /// to at least 3, which the Gen 7 damage calculator treats as a guaranteed critical hit. /// [Test] public async Task ChangeCriticalStage_EffectActive_GuaranteesCriticalHit() { // Arrange var effect = new LaserFocusEffect(); var move = Substitute.For(); var target = Substitute.For(); byte stage = 0; // Act effect.ChangeCriticalStage(move, target, 0, ref stage); // Assert await Assert.That(stage).IsGreaterThanOrEqualTo((byte)3); } /// /// Bulbapedia: the guarantee lasts "until the end of the next turn" — it is not consumed by a single /// critical-stage check, so e.g. every hit of a multi-hit move is a critical hit. /// [Test] public async Task ChangeCriticalStage_AfterFirstCheck_EffectStillActive() { // Arrange var effect = new LaserFocusEffect(); var set = CreateHostedSet(effect); var move = Substitute.For(); var target = Substitute.For(); byte stage = 0; // Act effect.ChangeCriticalStage(move, target, 0, ref stage); // Assert await Assert.That(set.Contains(ScriptUtils.ResolveName())).IsTrue(); } /// /// Bulbapedia: the guarantee lasts "until the end of the next turn" — at the end of the turn Laser /// Focus was used the effect is still active, so the move used on the following turn is a critical hit. /// [Test] public async Task OnEndTurn_EndOfTurnUsed_EffectStillActive() { // Arrange var effect = new LaserFocusEffect(); var set = CreateHostedSet(effect); // Act - the end of the turn Laser Focus was used if (effect is IScriptOnEndTurn onEndTurn) onEndTurn.OnEndTurn(Substitute.For(), Substitute.For()); // Assert await Assert.That(set.Contains(ScriptUtils.ResolveName())).IsTrue(); } /// /// Bulbapedia: the guarantee only lasts "until the end of the next turn" — if the user does not move, /// the effect expires at the end of the turn after it was used. /// [Test] public async Task OnEndTurn_EndOfNextTurn_EffectExpires() { // Arrange var effect = new LaserFocusEffect(); var set = CreateHostedSet(effect); // Act - end of the turn Laser Focus was used, and end of the next turn if (effect is IScriptOnEndTurn onEndTurn) { onEndTurn.OnEndTurn(Substitute.For(), Substitute.For()); onEndTurn.OnEndTurn(Substitute.For(), Substitute.For()); } // Assert await Assert.That(set.Contains(ScriptUtils.ResolveName())).IsFalse(); } }