using PkmnLib.Dynamic.Libraries; using PkmnLib.Dynamic.Models; using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Plugin.Gen7.Scripts.Battle; using PkmnLib.Plugin.Gen7.Scripts.Moves; using PkmnLib.Static; using PkmnLib.Static.Libraries; using PkmnLib.Static.Species; using PkmnLib.Static.Utils; namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves; /// /// Tests for the move script. /// Gen VII Bulbapedia behavior: "The type of Multi-Attack depends on the type of memory held by the user, /// being Normal-type if there is no held memory." /// public class MultiAttackTests { public record TestCaseData(string? ItemName, string ExpectedTypeName) { public override string ToString() => $"MultiAttack {ItemName ?? "None"} => {ExpectedTypeName}"; } public static IEnumerable> ChangeMoveTypeData() { yield return () => new TestCaseData("bug_memory", "bug"); yield return () => new TestCaseData("dark_memory", "dark"); yield return () => new TestCaseData("dragon_memory", "dragon"); yield return () => new TestCaseData("electric_memory", "electric"); yield return () => new TestCaseData("fairy_memory", "fairy"); yield return () => new TestCaseData("fighting_memory", "fighting"); yield return () => new TestCaseData("fire_memory", "fire"); yield return () => new TestCaseData("flying_memory", "flying"); yield return () => new TestCaseData("ghost_memory", "ghost"); yield return () => new TestCaseData("grass_memory", "grass"); yield return () => new TestCaseData("ground_memory", "ground"); yield return () => new TestCaseData("ice_memory", "ice"); yield return () => new TestCaseData("poison_memory", "poison"); yield return () => new TestCaseData("psychic_memory", "psychic"); yield return () => new TestCaseData("rock_memory", "rock"); yield return () => new TestCaseData("steel_memory", "steel"); yield return () => new TestCaseData("water_memory", "water"); yield return () => new TestCaseData("something_random", "normal"); yield return () => new TestCaseData(null, "normal"); } [Test, MethodDataSource(nameof(ChangeMoveTypeData))] public async Task ChangeMoveType_UserHoldingMemoryItem_ChangeMoveType(TestCaseData test) { var typeLibrary = new TypeLibrary(); typeLibrary.RegisterType("normal"); typeLibrary.RegisterType("fighting"); typeLibrary.RegisterType("flying"); typeLibrary.RegisterType("poison"); typeLibrary.RegisterType("ground"); typeLibrary.RegisterType("rock"); typeLibrary.RegisterType("bug"); typeLibrary.RegisterType("ghost"); typeLibrary.RegisterType("steel"); typeLibrary.RegisterType("fire"); typeLibrary.RegisterType("water"); typeLibrary.RegisterType("grass"); typeLibrary.RegisterType("electric"); typeLibrary.RegisterType("psychic"); typeLibrary.RegisterType("ice"); typeLibrary.RegisterType("dragon"); typeLibrary.RegisterType("dark"); typeLibrary.RegisterType("fairy"); // Arrange var move = Substitute.For(); var target = Substitute.For(); var user = Substitute.For(); TypeIdentifier? typeIdentifier = new TypeIdentifier(1, "Normal"); var dynamicLibrary = Substitute.For(); var staticLibrary = Substitute.For(); var item = Substitute.For(); user.Library.Returns(dynamicLibrary); dynamicLibrary.StaticLibrary.Returns(staticLibrary); staticLibrary.Types.Returns(typeLibrary); if (test.ItemName != null) item.Name.Returns(new StringKey(test.ItemName)); move.User.Returns(user); if (test.ItemName != null) user.HeldItem.Returns(item); else user.HeldItem.Returns((IItem?)null); var multiAttack = new MultiAttack(); // Act multiAttack.ChangeMoveType(move, target, 0, ref typeIdentifier); // Assert await Assert.That(typeIdentifier!.Value.Name.ToString()).IsEqualTo(test.ExpectedTypeName); } /// /// Creates a mocked executing move whose user holds a fire_memory, with a type library that knows both /// "normal" and "fire". /// private static (IExecutingMove move, IBattlePokemon user) CreateMemoryHolderSetup() { var typeLibrary = new TypeLibrary(); typeLibrary.RegisterType("normal"); typeLibrary.RegisterType("fire"); var move = Substitute.For(); var user = Substitute.For(); var dynamicLibrary = Substitute.For(); var staticLibrary = Substitute.For(); var item = Substitute.For(); user.Library.Returns(dynamicLibrary); dynamicLibrary.StaticLibrary.Returns(staticLibrary); staticLibrary.Types.Returns(typeLibrary); item.Name.Returns(new StringKey("fire_memory")); user.HeldItem.Returns(item); move.User.Returns(user); return (move, user); } /// /// Bulbapedia: "If the user has Klutz, or if Magic Room is in effect, Multi-Attack's type will always be /// Normal regardless of the memory held." A Klutz user holding a fire_memory still uses a Normal-type /// Multi-Attack. /// [Test] public async Task ChangeMoveType_UserHasKlutz_MoveTypeStaysNormal() { // Arrange var (move, user) = CreateMemoryHolderSetup(); var klutz = Substitute.For(); klutz.Name.Returns(new StringKey("klutz")); user.ActiveAbility.Returns(klutz); TypeIdentifier? typeIdentifier = new TypeIdentifier(1, "normal"); var multiAttack = new MultiAttack(); // Act multiAttack.ChangeMoveType(move, Substitute.For(), 0, ref typeIdentifier); // Assert await Assert.That(typeIdentifier!.Value.Name.ToString()).IsEqualTo("normal"); } /// /// Bulbapedia: "If the user has Klutz, or if Magic Room is in effect, Multi-Attack's type will always be /// Normal regardless of the memory held." While the is on the battle, a held /// fire_memory does not change Multi-Attack's type. /// [Test] public async Task ChangeMoveType_MagicRoomInEffect_MoveTypeStaysNormal() { // Arrange var (move, _) = CreateMemoryHolderSetup(); var battle = Substitute.For(); battle.GetScripts().Returns(_ => new ScriptIterator(Array.Empty>())); IScriptSet battleVolatile = new ScriptSet(battle); battleVolatile.Add(new MagicRoomEffect()); battle.Volatile.Returns(battleVolatile); move.Battle.Returns(battle); TypeIdentifier? typeIdentifier = new TypeIdentifier(1, "normal"); var multiAttack = new MultiAttack(); // Act multiAttack.ChangeMoveType(move, Substitute.For(), 0, ref typeIdentifier); // Assert await Assert.That(typeIdentifier!.Value.Name.ToString()).IsEqualTo("normal"); } }