diff --git a/PKHeX.Core/Legality/RNG/Frame/Frame.cs b/PKHeX.Core/Legality/RNG/Frame/Frame.cs index 57c054f67..cfeb13e1d 100644 --- a/PKHeX.Core/Legality/RNG/Frame/Frame.cs +++ b/PKHeX.Core/Legality/RNG/Frame/Frame.cs @@ -2,16 +2,29 @@ { public class Frame { + /// + /// Ending seed value for the frame (prior to nature call). + /// public readonly uint Seed; public readonly LeadRequired Lead; private readonly FrameType FrameType; private readonly RNG RNG; - public uint RandLevel { get; set; } - public uint RandESV { get; set; } + /// + /// Starting seed for the frame (to generate the frame). + /// public uint OriginSeed { get; set; } + /// + /// RNG Call Value for the Level Calc + /// + public uint RandLevel { get; set; } + /// + /// RNG Call Value for the Encounter Slot Calc + /// + public uint RandESV { get; set; } + public bool LevelSlotModified => Lead.IsLevelOrSlotModified(); public Frame(uint seed, FrameType type, RNG rng, LeadRequired lead) @@ -30,6 +43,13 @@ /// Slot number for this frame & lead value. public bool IsSlotCompatibile(EncounterSlot slot, PKM pkm) { + bool usesLevel = !slot.FixedLevel; + if (FrameType != FrameType.MethodH) + { + if (Lead.HasFlag(LeadRequired.UsesLevelCall) != usesLevel) + return false; + } + // Level is before Nature, but usually isn't varied. Check ESV calc first. int s = GetSlot(slot); if (s != slot.SlotNumber) diff --git a/PKHeX.Core/Legality/RNG/Frame/FrameFinder.cs b/PKHeX.Core/Legality/RNG/Frame/FrameFinder.cs index 3afb9348b..886d1dcb6 100644 --- a/PKHeX.Core/Legality/RNG/Frame/FrameFinder.cs +++ b/PKHeX.Core/Legality/RNG/Frame/FrameFinder.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace PKHeX.Core { @@ -13,10 +14,10 @@ namespace PKHeX.Core public static IEnumerable GetFrames(PIDIV pidiv, PKM pk) { if (pidiv.RNG == null) - yield break; + return Enumerable.Empty(); FrameGenerator info = new FrameGenerator(pidiv, pk); if (info.FrameType == FrameType.None) - yield break; + return Enumerable.Empty(); info.Nature = pk.EncryptionConstant % 25; @@ -30,8 +31,9 @@ namespace PKHeX.Core : FilterNatureSync(seeds, pidiv, info); var refined = RefineFrames(frames, info); - foreach (var z in refined) - yield return z; + if (pk.Gen4 && pidiv.Type == PIDType.CuteCharm) // only permit cute charm successful frames + return refined.Where(z => (z.Lead & ~LeadRequired.UsesLevelCall) == LeadRequired.CuteCharm); + return refined; } private static IEnumerable RefineFrames(IEnumerable frames, FrameGenerator info) @@ -105,6 +107,8 @@ namespace PKHeX.Core lead = cc ? LeadRequired.CuteCharm : LeadRequired.CuteCharmFail; yield return info.GetFrame(prev2, lead, p2, p1, prev3); } + if (f.Lead == LeadRequired.CuteCharm) + yield break; // Pressure, Hustle, Vital Spirit = Force Maximum Level from slot // -2 ESV @@ -149,10 +153,16 @@ namespace PKHeX.Core // Current Seed of the frame is the ESV. var rand = f.Seed >> 16; f.RandESV = rand; - f.RandLevel = rand; + f.RandLevel = rand; // unused f.OriginSeed = info.RNG.Prev(f.Seed); yield return f; + // Create a copy for level; shift ESV and origin back + var esv = f.OriginSeed; + var origin = info.RNG.Prev(f.OriginSeed); + var withLevel = info.GetFrame(f.Seed, f.Lead | LeadRequired.UsesLevelCall, esv, f.RandLevel, origin); + yield return withLevel; + if (f.Lead != LeadRequired.None) continue; @@ -161,20 +171,82 @@ namespace PKHeX.Core } foreach (var f in list) { - // Level Modifiers between ESV and Nature - var prev = info.RNG.Prev(f.Seed); - var p3 = info.RNG.Prev(prev); - var p16 = prev >> 16; - - yield return info.GetFrame(prev, LeadRequired.IntimidateKeenEye, p16, p3); - yield return info.GetFrame(prev, LeadRequired.PressureHustleSpirit, p16, p3); - - // Slot Modifiers before ESV - var force = (info.DPPt ? p16 >> 15 : p16 & 1) == 1; - var rand = f.Seed >> 16; - yield return info.GetFrame(prev, force ? LeadRequired.StaticMagnet : LeadRequired.StaticMagnetFail, rand, p3); + var leadframes = GenerateLeadSpecificFrames4(f, info); + foreach (var frame in leadframes) + yield return frame; } } + private static IEnumerable GenerateLeadSpecificFrames4(Frame f, FrameGenerator info) + { + LeadRequired lead; + var prev0 = f.Seed; // 0 + var prev1 = info.RNG.Prev(f.Seed); // -1 + var prev2 = info.RNG.Prev(prev1); // -2 + var prev3 = info.RNG.Prev(prev2); // -3 + + // Rand call raw values + var p0 = prev0 >> 16; + var p1 = prev1 >> 16; + var p2 = prev2 >> 16; + + // Cute Charm + // -2 ESV + // -1 Level (Optional) + // 0 CC Proc (Random() % 3 != 0) + // 1 Nature + if (info.Gendered) + { + bool cc = p0 % 3 != 0; + if (f.Lead == LeadRequired.CuteCharm) // 100% required for frame base + { + if (!cc) yield break; + yield return info.GetFrame(prev2, LeadRequired.CuteCharm, p1, p1, prev2); + yield return info.GetFrame(prev2, LeadRequired.CuteCharm | LeadRequired.UsesLevelCall, p2, p1, prev3); + yield break; + } + lead = cc ? LeadRequired.CuteCharm : LeadRequired.CuteCharmFail; + yield return info.GetFrame(prev2, lead, p1, p1, prev2); + yield return info.GetFrame(prev2, lead | LeadRequired.UsesLevelCall, p2, p1, prev3); + } + if (f.Lead == LeadRequired.CuteCharm) + yield break; + + // Pressure, Hustle, Vital Spirit = Force Maximum Level from slot + // -2 ESV + // -1 Level (Optional) + // 0 LevelMax proc (Random() & 1) + // 1 Nature + bool max = p0 % 2 == 1; + lead = max ? LeadRequired.PressureHustleSpirit : LeadRequired.PressureHustleSpiritFail; + yield return info.GetFrame(prev2, lead, p1, p1, prev2); + yield return info.GetFrame(prev2, lead | LeadRequired.UsesLevelCall, p2, p1, prev3); + + // Keen Eye, Intimidate + // -2 ESV + // -1 Level (Optional) + // 0 Level Adequate Check !(Random() % 2 == 1) rejects -- rand%2==1 is adequate + // 1 Nature + // Note: if this check fails, the encounter generation routine is aborted. + if (max) // same result as above, no need to recalculate + { + lead = LeadRequired.IntimidateKeenEye; + yield return info.GetFrame(prev2, lead, p1, p1, prev2); + yield return info.GetFrame(prev2, lead | LeadRequired.UsesLevelCall, p2, p1, prev3); + } + + // Static or Magnet Pull + // -2 SlotProc (Random % 2 == 0) + // -1 ESV (select slot) + // 0 Level (Optional) + // 1 Nature + var force1 = (info.DPPt ? p1 >> 15 : p1 & 1) == 1; + lead = force1 ? LeadRequired.StaticMagnet : LeadRequired.StaticMagnetFail; + yield return info.GetFrame(prev2, lead, p0, p0, prev3); + + var force2 = (info.DPPt ? p2 >> 15 : p2 & 1) == 1; + lead = (force2 ? LeadRequired.StaticMagnet : LeadRequired.StaticMagnetFail) | LeadRequired.UsesLevelCall; + yield return info.GetFrame(prev2, lead, p1, p0, prev3); + } /// /// Filters the input according to a Nature Lock frame generation pattern. diff --git a/PKHeX.Core/Legality/RNG/Frame/LeadRequired.cs b/PKHeX.Core/Legality/RNG/Frame/LeadRequired.cs index 562937e05..77e1ea9d7 100644 --- a/PKHeX.Core/Legality/RNG/Frame/LeadRequired.cs +++ b/PKHeX.Core/Legality/RNG/Frame/LeadRequired.cs @@ -17,7 +17,8 @@ namespace PKHeX.Core PressureHustleSpirit = 1 << 4, SuctionCups = 1 << 5, - NoLevelCall = 1 << 6, + // Compatibility Flags + UsesLevelCall = 1 << 6, Fail = 1 << 7, CuteCharmFail = CuteCharm | Fail, @@ -25,7 +26,7 @@ namespace PKHeX.Core StaticMagnetFail = StaticMagnet | Fail, PressureHustleSpiritFail = PressureHustleSpirit | Fail, - AllFlags = NoLevelCall | Fail, + AllFlags = UsesLevelCall | Fail, NoFlags = ~AllFlags, }