using PkmnLib.Dynamic.Libraries; using PkmnLib.Dynamic.Models; using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Plugin.Gen7.Scripts.Moves; using PkmnLib.Static; using PkmnLib.Static.Libraries; using PkmnLib.Static.Utils; namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves; /// /// Tests for the move script. /// Gen VII Bulbapedia behavior: "Hidden Power can be any type, other than Normal and Fairy, based on its user's /// IVs." and "From Generation VI onward, its power is fixed at 60." /// public class HiddenPowerTests { public record TestCaseData(IndividualValueStatisticSet Ivs, StringKey ExpectedType) { /// public override string ToString() => $"Hidden Power type is {ExpectedType} " + $"with IVs: HP {Ivs.Hp}, Atk {Ivs.Attack}, Def {Ivs.Defense}, SpA {Ivs.SpecialAttack}, SpD {Ivs.SpecialDefense}, Spe {Ivs.Speed}"; } /// /// Rows of the Bulbapedia type-determination formula: the type index is /// (a + 2b + 4c + 8d + 16e + 32f) × 15 / 63 rounded down, where a–f are the least significant bits of the /// HP, Attack, Defense, Speed, Special Attack, and Special Defense IVs, and index 0 is Fighting through /// index 15 being Dark. /// public static IEnumerable> HiddenPowerTestData() { // All-even IVs: formula value 0 -> the lowest possible type, Fighting. yield return () => new TestCaseData(new IndividualValueStatisticSet(0, 0, 0, 0, 0, 0), "fighting"); // Only the Speed IV is odd: formula value 8 -> 8 * 15 / 63 = 1, Flying. yield return () => new TestCaseData(new IndividualValueStatisticSet(0, 0, 0, 0, 0, 1), "flying"); // All-odd IVs: formula value 63 -> the highest possible type, Dark. yield return () => new TestCaseData(new IndividualValueStatisticSet(31, 31, 31, 31, 31, 31), "dark"); yield return () => new TestCaseData(new IndividualValueStatisticSet(25, 2, 12, 5, 8, 17), "bug"); yield return () => new TestCaseData(new IndividualValueStatisticSet(29, 19, 18, 22, 15, 28), "fire"); } /// /// Creates a type library holding all 18 Gen VII types, registered in the National Pokédex type order. /// private static TypeLibrary CreateTypeLibrary() { 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"); return typeLibrary; } /// /// Creates a mocked executing move whose user has the given IVs and a library containing all 18 types. /// private static (IExecutingMove executingMove, IBattlePokemon target) CreateTestSetup( IndividualValueStatisticSet ivs) { var executingMove = Substitute.For(); var user = Substitute.For(); var target = Substitute.For(); var dynamicLibrary = Substitute.For(); var staticLibrary = Substitute.For(); executingMove.User.Returns(user); user.IndividualValues.Returns(ivs); user.Library.Returns(dynamicLibrary); staticLibrary.Types.Returns(CreateTypeLibrary()); dynamicLibrary.StaticLibrary.Returns(staticLibrary); return (executingMove, target); } /// /// Bulbapedia: "Hidden Power can be any type, other than Normal and Fairy, based on its user's IVs." /// The type index is (a + 2b + 4c + 8d + 16e + 32f) × 15 / 63 rounded down, where a–f are the least /// significant bits of the HP, Attack, Defense, Speed, Special Attack, and Special Defense IVs. /// [Test, MethodDataSource(nameof(HiddenPowerTestData))] public async Task HiddenPower_ChangesType(TestCaseData test) { var (executingMove, target) = CreateTestSetup(test.Ivs); TypeIdentifier? moveType = new TypeIdentifier(1, "normal"); var hiddenPower = new HiddenPower(); hiddenPower.ChangeMoveType(executingMove, target, 0, ref moveType); await Assert.That(moveType!.Value.Name).IsEqualTo(test.ExpectedType); } /// /// Bulbapedia: "Hidden Power can be any type, other than Normal and Fairy, based on its user's IVs." /// The type only depends on the least significant bit of each of the six IVs, so checking all 64 parity /// combinations exhaustively proves the move can never be Normal or Fairy. /// [Test] public async Task ChangeMoveType_AllIvParityCombinations_TypeIsNeverNormalOrFairy() { for (byte combination = 0; combination < 64; combination++) { // Arrange - each bit of the combination becomes the least significant bit of one IV. var ivs = new IndividualValueStatisticSet((byte)(combination & 0x01), (byte)((combination >> 1) & 0x01), (byte)((combination >> 2) & 0x01), (byte)((combination >> 4) & 0x01), (byte)((combination >> 5) & 0x01), (byte)((combination >> 3) & 0x01)); var (executingMove, target) = CreateTestSetup(ivs); TypeIdentifier? moveType = new TypeIdentifier(1, "normal"); // Act var hiddenPower = new HiddenPower(); hiddenPower.ChangeMoveType(executingMove, target, 0, ref moveType); // Assert await Assert.That(moveType!.Value.Name).IsNotEqualTo(new StringKey("normal")); await Assert.That(moveType!.Value.Name).IsNotEqualTo(new StringKey("fairy")); } } /// /// Bulbapedia: "From Generation VI onward, its power is fixed at 60." The pre-Gen-VI variable power formula /// no longer applies, so the script must leave the base power of 60 from the move data untouched, regardless /// of the user's IVs. /// [Test, MethodDataSource(nameof(HiddenPowerTestData))] public async Task ChangeBasePower_AnyIvs_PowerRemainsFixedAtSixty(TestCaseData test) { var hiddenPower = new HiddenPower(); await Assert.That(hiddenPower).IsNotAssignableTo(); } }