From 978bcfa56fcbcc712a02f6810b17b0ca99e9ed68 Mon Sep 17 00:00:00 2001 From: Kurt Date: Tue, 29 Aug 2023 22:22:23 -0700 Subject: [PATCH] Misc enc template tweaks Skip string fetch for Trade1 Set gender for Trade8 Add record to wrap Gen8 raid generate params, return & use bool for filtering results, fallback to unrestricted if none succeed Fix gender comparison for Entree5 slot Simplify gen2 ResetKey fetch (byte sum of Money/ID/OT(used) Closes #3970 by filtering gender inside the method like Encounter9RNG with an early return Adds IV filtering inside the method with an early return like Encounter9RNG --- .../Templates/Gen1/EncounterTrade1.cs | 6 +- .../Templates/Gen8/EncounterStatic8Nest.cs | 50 ++++------- .../Templates/Gen8/EncounterTrade8.cs | 1 + .../Legality/RNG/Methods/GenerateParam8.cs | 15 ++++ PKHeX.Core/Legality/RNG/RaidRNG.cs | 83 +++++++++++-------- PKHeX.Core/PKM/PK1.cs | 4 +- PKHeX.Core/PKM/Strings/StringConverter12.cs | 3 +- PKHeX.Core/PKM/Strings/StringConverter2KOR.cs | 2 +- PKHeX.Core/Saves/SAV2.cs | 20 ++--- .../Subforms/Save Editors/Gen5/SAV_Misc5.cs | 2 +- 10 files changed, 98 insertions(+), 88 deletions(-) create mode 100644 PKHeX.Core/Legality/RNG/Methods/GenerateParam8.cs diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen1/EncounterTrade1.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen1/EncounterTrade1.cs index 2dbc75d03..a0e958ce1 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen1/EncounterTrade1.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen1/EncounterTrade1.cs @@ -75,7 +75,7 @@ private bool IsNicknameValid(PKM pk) private static bool IsTrainerNameValid(PKM pk) { if (pk.Format <= 2) - return pk.OT_Name == StringConverter12.G1TradeOTStr; + return pk.OT_Trash is [StringConverter12.G1TradeOTCode, StringConverter12.G1TerminatorCode, _]; return pk.Language switch { 1 => GetIndex(pk.OT_Name, TrainerNames) == 1, @@ -89,7 +89,7 @@ private static int GetIndex(ReadOnlySpan name, IReadOnlyList arr) { for (int i = 0; i < arr.Count; i++) { - if (arr[i].AsSpan().SequenceEqual(name)) + if (name.SequenceEqual(arr[i])) return i; } @@ -124,12 +124,12 @@ public PK1 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) Catch_Rate = EncounterUtil1.GetWildCatchRate(Version, Species), DV16 = EncounterUtil1.GetRandomDVs(Util.Rand), - OT_Name = StringConverter12.G1TradeOTStr, Nickname = Nicknames[lang], TID16 = tr.TID16, Type1 = pi.Type1, Type2 = pi.Type2, }; + pk.OT_Trash[0] = StringConverter12.G1TradeOTCode; EncounterUtil1.SetEncounterMoves(pk, Version, level); diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterStatic8Nest.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterStatic8Nest.cs index 5e68483c4..c6855e4f4 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterStatic8Nest.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterStatic8Nest.cs @@ -90,32 +90,37 @@ private void SetPINGA(PK8 pk, EncounterCriteria criteria, PersonalInfo8SWSH pi) { bool requestShiny = criteria.Shiny.IsShiny(); bool checkShiny = requestShiny && Shiny != Shiny.Never; - var ratio = RemapGenderToParam(Gender, pi); - var abil = RemapAbilityToParam(Ability); Span iv = stackalloc int[6]; int ctr = 0; var rand = new Xoroshiro128Plus(Util.Rand.Rand64()); + var param = GetParam(pi); ulong seed; + const int max = 100_000; do { seed = rand.Next(); - ApplyDetailsTo(pk, seed, iv, abil, ratio); - - if (criteria.IV_ATK != 31 && pk.IV_ATK != criteria.IV_ATK) - continue; - if (criteria.IV_SPE != 31 && pk.IV_SPE != criteria.IV_SPE) + if (!TryApply(pk, seed, iv, param, criteria)) continue; if (checkShiny && pk.IsShiny != requestShiny) continue; break; - } while (ctr++ < 100_000); + } while (ctr++ < max); + + if (ctr == max) // fail + TryApply(pk, rand.Next(), iv, param, EncounterCriteria.Unrestricted); FinishCorrelation(pk, seed); if ((byte)criteria.Nature != pk.Nature && criteria.Nature.IsMint()) pk.StatNature = (byte)criteria.Nature; } + private GenerateParam8 GetParam(PersonalInfo8SWSH pi) + { + var ratio = RemapGenderToParam(Gender, pi); + return new GenerateParam8(Species, ratio, FlawlessIVCount, Ability, Shiny, Nature.Random, IVs); + } + protected virtual void FinishCorrelation(PK8 pk, ulong seed) { } #endregion @@ -239,28 +244,14 @@ protected bool IsMatchPartial(PKM pk) /// True if the seed is valid for the criteria. public bool Verify(PKM pk, ulong seed, bool forceNoShiny = false) { - var ratio = RemapGenderToParam(Gender, Info); - var abil = RemapAbilityToParam(Ability); - + var param = GetParam(PersonalTable.SWSH.GetFormEntry(Species, Form)); Span iv = stackalloc int[6]; - LoadIVs(iv); - return RaidRNG.Verify(pk, seed, iv, Species, FlawlessIVCount, abil, ratio, forceNoShiny: forceNoShiny); + return RaidRNG.Verify(pk, seed, iv, param, forceNoShiny: forceNoShiny); } - private void ApplyDetailsTo(PK8 pk, ulong seed, Span iv, byte abil, byte ratio) + private static bool TryApply(PK8 pk, ulong seed, Span iv, GenerateParam8 param, EncounterCriteria criteria) { - LoadIVs(iv); - RaidRNG.ApplyDetailsTo(pk, seed, iv, Species, FlawlessIVCount, abil, ratio); - } - - private void LoadIVs(Span span) - { - // Template stores with speed in middle (standard), convert for generator purpose. - var ivs = IVs; - if (ivs.IsSpecified) - ivs.CopyToSpeedLast(span); - else - span.Fill(-1); + return RaidRNG.TryApply(pk, seed, iv, param, criteria); } private static byte RemapGenderToParam(byte gender, PersonalInfo8SWSH pi) => gender switch @@ -271,13 +262,6 @@ private void LoadIVs(Span span) _ => pi.Gender, }; - private static byte RemapAbilityToParam(AbilityPermission a) => a switch - { - Any12H => 254, - Any12 => 255, - _ => a.GetSingleValue(), - }; - private bool IsMatchCorrelation(PKM pk) { if (pk.IsShiny) diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterTrade8.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterTrade8.cs index 754f7e8a0..e42470ae5 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterTrade8.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterTrade8.cs @@ -109,6 +109,7 @@ public PK8 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) Met_Level = Level, MetDate = EncounterDate.GetDateSwitch(), Ball = (byte)FixedBall, + Gender = Gender, ID32 = ID32, Version = (byte)version, diff --git a/PKHeX.Core/Legality/RNG/Methods/GenerateParam8.cs b/PKHeX.Core/Legality/RNG/Methods/GenerateParam8.cs new file mode 100644 index 000000000..1e9b37c35 --- /dev/null +++ b/PKHeX.Core/Legality/RNG/Methods/GenerateParam8.cs @@ -0,0 +1,15 @@ +namespace PKHeX.Core; + +/// +/// Parameters used to generate data for an encounter. +/// +/// Species to generate. +/// Gender ratio byte. +/// Count of IVs that are perfect. +/// Ability type to generate. +/// PID generation type. +/// Nature specification. +/// IV specification. +public readonly record struct GenerateParam8(ushort Species, byte GenderRatio, byte FlawlessIVs, + AbilityPermission Ability = AbilityPermission.Any12, Shiny Shiny = Shiny.Random, + Nature Nature = Nature.Random, IndividualValueSet IVs = default); diff --git a/PKHeX.Core/Legality/RNG/RaidRNG.cs b/PKHeX.Core/Legality/RNG/RaidRNG.cs index 89899fa2f..2f1599987 100644 --- a/PKHeX.Core/Legality/RNG/RaidRNG.cs +++ b/PKHeX.Core/Legality/RNG/RaidRNG.cs @@ -14,14 +14,10 @@ public static class RaidRNG /// Entity to verify against /// Seed that generated the entity /// Buffer of IVs (potentially with already specified values) - /// Species of the entity - /// Number of flawless IVs to generate - /// Ability to generate - /// Gender distribution to generate - /// Nature to generate + /// Parameters to generate with /// Force the entity to be non-shiny via special handling /// True if the seed matches the entity - public static bool Verify(PKM pk, ulong seed, Span ivs, ushort species, byte iv_count, byte ability_param, byte gender_ratio, sbyte nature_param = -1, bool forceNoShiny = false) + public static bool Verify(PKM pk, ulong seed, Span ivs, in GenerateParam8 param, bool forceNoShiny = false) { var rng = new Xoroshiro128Plus(seed); var ec = (uint)rng.NextInt(); @@ -49,7 +45,12 @@ public static bool Verify(PKM pk, ulong seed, Span ivs, ushort species, byt const int UNSET = -1; const int MAX = 31; - for (int i = ivs.Count(MAX); i < iv_count; i++) + if (param.IVs.IsSpecified) + param.IVs.CopyToSpeedLast(ivs); + else + ivs.Fill(UNSET); + + for (int i = ivs.Count(MAX); i < param.FlawlessIVs; i++) { int index = (int)rng.NextInt(6); while (ivs[index] != UNSET) @@ -76,11 +77,11 @@ public static bool Verify(PKM pk, ulong seed, Span ivs, ushort species, byt if (pk.IV_SPE != ivs[5]) return false; - int abil = ability_param switch + int abil = param.Ability switch { - 254 => (int)rng.NextInt(3), - 255 => (int)rng.NextInt(2), - _ => ability_param, + AbilityPermission.Any12H => (int)rng.NextInt(3), + AbilityPermission.Any12 => (int)rng.NextInt(2), + _ => param.Ability.GetSingleValue(), }; abil <<= 1; // 1/2/4 @@ -92,7 +93,7 @@ public static bool Verify(PKM pk, ulong seed, Span ivs, ushort species, byt } // else, for things that were made Hidden Ability, defer to Ability Checks (Ability Patch) - switch (gender_ratio) + switch (param.GenderRatio) { case PersonalInfo.RatioMagicGenderless: if (pk.Gender != 2) @@ -107,14 +108,14 @@ public static bool Verify(PKM pk, ulong seed, Span ivs, ushort species, byt return false; break; default: - var gender = (int)rng.NextInt(253) + 1 < gender_ratio ? 1 : 0; + var gender = (int)rng.NextInt(253) + 1 < param.GenderRatio ? 1 : 0; if (pk.Gender != gender && pk.Gender != 2) // allow Nincada(0/1)->Shedinja(2) return false; break; } - int nature = nature_param != -1 ? nature_param - : species == (int)Species.Toxtricity + int nature = param.Nature != Nature.Random ? (int)param.Nature + : param.Species == (int)Species.Toxtricity ? ToxtricityUtil.GetRandomNature(ref rng, pk.Form) : (byte)rng.NextInt(25); if (pk.Nature != nature) @@ -164,14 +165,10 @@ private static void ForceShinyState(bool isShiny, ref uint pid, uint tid) /// Entity to verify against /// Seed that generated the entity /// Buffer of IVs (potentially with already specified values) - /// Species of the entity - /// Number of flawless IVs to generate - /// Ability to generate - /// Gender distribution to generate - /// Nature to generate - /// Shiny state to generate + /// Parameters to generate with + /// Criteria to generate with /// True if the seed matches the entity - public static bool ApplyDetailsTo(PK8 pk, ulong seed, Span ivs, ushort species, byte iv_count, byte ability_param, byte gender_ratio, sbyte nature_param = -1, Shiny shiny = Shiny.Random) + public static bool TryApply(PK8 pk, ulong seed, Span ivs, in GenerateParam8 param, EncounterCriteria criteria) { var rng = new Xoroshiro128Plus(seed); pk.EncryptionConstant = (uint)rng.NextInt(); @@ -183,7 +180,7 @@ public static bool ApplyDetailsTo(PK8 pk, ulong seed, Span ivs, ushort spec pid = (uint)rng.NextInt(); var xor = GetShinyXor(pid, trID); isShiny = xor < 16; - if (isShiny && shiny == Shiny.Never) + if (isShiny && param.Shiny == Shiny.Never) { ForceShinyState(false, ref pid, trID); isShiny = false; @@ -205,12 +202,20 @@ public static bool ApplyDetailsTo(PK8 pk, ulong seed, Span ivs, ushort spec const int UNSET = -1; const int MAX = 31; - for (int i = ivs.Count(MAX); i < iv_count; i++) + if (param.IVs.IsSpecified) { - int index = (int)rng.NextInt(6); - while (ivs[index] != UNSET) - index = (int)rng.NextInt(6); - ivs[index] = MAX; + param.IVs.CopyToSpeedLast(ivs); + } + else + { + ivs.Fill(UNSET); + for (int i = ivs.Count(MAX); i < param.FlawlessIVs; i++) + { + int index; + do { index = (int)rng.NextInt(6); } + while (ivs[index] != UNSET); + ivs[index] = MAX; + } } for (int i = 0; i < 6; i++) @@ -219,6 +224,9 @@ public static bool ApplyDetailsTo(PK8 pk, ulong seed, Span ivs, ushort spec ivs[i] = (int)rng.NextInt(32); } + if (!param.IVs.IsSpecified && !criteria.IsIVsCompatible(ivs, 8)) + return false; + pk.IV_HP = ivs[0]; pk.IV_ATK = ivs[1]; pk.IV_DEF = ivs[2]; @@ -226,24 +234,27 @@ public static bool ApplyDetailsTo(PK8 pk, ulong seed, Span ivs, ushort spec pk.IV_SPD = ivs[4]; pk.IV_SPE = ivs[5]; - int abil = ability_param switch + int abil = param.Ability switch { - 254 => (int)rng.NextInt(3), - 255 => (int)rng.NextInt(2), - _ => ability_param, + AbilityPermission.Any12H => (int)rng.NextInt(3), + AbilityPermission.Any12 => (int)rng.NextInt(2), + _ => param.Ability.GetSingleValue(), }; pk.RefreshAbility(abil); - pk.Gender = gender_ratio switch + var gender = param.GenderRatio switch { PersonalInfo.RatioMagicGenderless => 2, PersonalInfo.RatioMagicFemale => 1, PersonalInfo.RatioMagicMale => 0, - _ => (int) rng.NextInt(253) + 1 < gender_ratio ? 1 : 0, + _ => (int) rng.NextInt(253) + 1 < param.GenderRatio ? 1 : 0, }; + if (criteria.Gender != FixedGenderUtil.GenderRandom && gender != criteria.Gender) + return false; + pk.Gender = gender; - int nature = nature_param != -1 ? nature_param - : species == (int)Species.Toxtricity + int nature = param.Nature != Nature.Random ? (byte)param.Nature + : param.Species == (int)Species.Toxtricity ? ToxtricityUtil.GetRandomNature(ref rng, pk.Form) : (byte)rng.NextInt(25); diff --git a/PKHeX.Core/PKM/PK1.cs b/PKHeX.Core/PKM/PK1.cs index eb47080fc..ce4eaac32 100644 --- a/PKHeX.Core/PKM/PK1.cs +++ b/PKHeX.Core/PKM/PK1.cs @@ -229,8 +229,8 @@ public PK7 ConvertToPK7() private string GetTransferTrainerName(int lang) { - if (OT_Trash[0] == StringConverter12.G1TradeOTCode) - return StringConverter12.G1TradeOTName[lang]; // In-game Trade + if (OT_Trash[0] == StringConverter12.G1TradeOTCode) // In-game Trade + return StringConverter12.G1TradeOTName[lang]; return StringConverter12Transporter.GetString(OT_Trash, Japanese); } } diff --git a/PKHeX.Core/PKM/Strings/StringConverter12.cs b/PKHeX.Core/PKM/Strings/StringConverter12.cs index 6b301787b..89706a2e9 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter12.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter12.cs @@ -39,7 +39,6 @@ private static bool AllCharsInDictionary(ReadOnlySpan data, IReadOnlyDicti public const char G1Terminator = '\0'; public const byte G1TradeOTCode = 0x5D; public const char G1TradeOT = '*'; - public const string G1TradeOTStr = "*"; public const byte G1SpaceCode = 0x7F; public static readonly IReadOnlyList G1TradeOTName = new [] @@ -111,7 +110,7 @@ public static string GetString(ReadOnlySpan data, bool jp) /// Character count loaded. public static int LoadString(ReadOnlySpan data, Span result, bool jp) { - if (data[0] == G1TradeOTCode) + if (data[0] == G1TradeOTCode) // In-game Trade { result[0] = G1TradeOT; return 1; diff --git a/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs b/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs index 604f56a9e..615083071 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs @@ -50,7 +50,7 @@ public static string GetString(ReadOnlySpan data) /// Character count loaded. public static int LoadString(ReadOnlySpan data, Span result) { - if (data[0] == G1TradeOTCode) + if (data[0] == G1TradeOTCode) // In-game Trade { result[0] = G1TradeOT; return 1; diff --git a/PKHeX.Core/Saves/SAV2.cs b/PKHeX.Core/Saves/SAV2.cs index ad90f40d6..fb8a03b09 100644 --- a/PKHeX.Core/Saves/SAV2.cs +++ b/PKHeX.Core/Saves/SAV2.cs @@ -720,16 +720,16 @@ public void SetEventFlag(int flagNumber, bool value) private ushort GetResetKey() { - var value = (TID16 >> 8) + (TID16 & 0xFF) + ((Money >> 16) & 0xFF) + ((Money >> 8) & 0xFF) + (Money & 0xFF); - var ot = Data.AsSpan(Offsets.Trainer1 + 2, 5); - var sum = 0; - foreach (var b in ot) - { - if (b == StringConverter12.G1TerminatorCode) - break; - sum += b; - } - return (ushort)(value + sum); + ushort result = 0; + foreach (var b in Data.AsSpan(Offsets.Money, 3)) + result += b; + var tr = Data.AsSpan(Offsets.Trainer1, 7); + var end = tr[2..].IndexOf(StringConverter12.G1TerminatorCode); + if (end >= 0) + tr = tr[..(end + 2)]; + foreach (var b in tr) + result += b; + return result; } /// diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs index 10dfe9de1..0e6fddabb 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen5/SAV_Misc5.cs @@ -620,7 +620,7 @@ private void B_RandForest_Click(object sender, EventArgs e) source.Remove(slot); s.Species = slot.Species; s.Form = slot.Form; - s.Gender = slot.Gender == -1 ? PersonalTable.B2W2[slot.Species].RandomGender() : slot.Gender; + s.Gender = slot.Gender == FixedGenderUtil.GenderRandom ? PersonalTable.B2W2[slot.Species].RandomGender() : slot.Gender; slot.Moves.CopyTo(moves); var count = moves.Length - moves.Count((ushort)0);