diff --git a/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator3.cs b/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator3.cs index 854f9b1b8..3ea7d1667 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator3.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator3.cs @@ -21,6 +21,29 @@ public IEnumerable GetEncounters(PKM pk, LegalInfo info) return GetEncounters(pk, chain, info); } + private enum DeferralType + { + None, + PIDIV, + Tile, + Ball, + SlotNumber, + } + + private struct Deferral + { + public DeferralType Type; + public IEncounterable? Encounter; + + public void Update(DeferralType type, IEncounterable enc) + { + if (Type >= type) + return; + Type = type; + Encounter = enc; + } + } + public IEnumerable GetEncounters(PKM pk, EvoCriteria[] chain, LegalInfo info) { if (chain.Length == 0) @@ -29,8 +52,7 @@ public IEnumerable GetEncounters(PKM pk, EvoCriteria[] chain, Le info.PIDIV = MethodFinder.Analyze(pk); var game = pk.Version; var iterator = new EncounterEnumerator3(pk, chain, game); - IEncounterable? deferType = null; - EncounterSlot3? deferSlot = null; + Deferral defer = default; var leadQueue = new LeadEncounterQueue(); bool emerald = pk.E; @@ -43,7 +65,12 @@ public IEnumerable GetEncounters(PKM pk, EvoCriteria[] chain, Le var e = enc.Encounter; if (!IsTypeCompatible(e, pk, info.PIDIV.Type)) { - deferType ??= e; + defer.Update(DeferralType.PIDIV, e); + continue; + } + if (!IsBallCompatible(e, pk)) + { + defer.Update(DeferralType.Ball, e); continue; } if (e is not EncounterSlot3 slot) @@ -76,7 +103,7 @@ public IEnumerable GetEncounters(PKM pk, EvoCriteria[] chain, Le var lead = LeadFinder.GetLeadInfo3(slot, info.PIDIV, evo, emerald, gender, pk.Format); if (!lead.IsValid()) { - deferSlot ??= slot; + defer.Update(DeferralType.SlotNumber, slot); continue; } leadQueue.Insert(lead, slot); @@ -90,19 +117,22 @@ public IEnumerable GetEncounters(PKM pk, EvoCriteria[] chain, Le if (leadQueue.List.Count != 0) yield break; - // Error will be flagged later if this is chosen. - if (deferSlot != null) - { - info.ManualFlag = EncounterYieldFlag.InvalidFrame; - yield return deferSlot; - } - else if (deferType != null) - { + // Errors will be flagged later for those not manually handled below. + if (defer.Encounter is not { } lastResort) + yield break; + if (defer.Type is DeferralType.PIDIV) info.ManualFlag = EncounterYieldFlag.InvalidPIDIV; - yield return deferType; - } + else if (defer.Type is DeferralType.Tile) + info.ManualFlag = EncounterYieldFlag.InvalidFrame; + yield return lastResort; } + private static bool IsBallCompatible(IFixedBall e, PKM pk) => e.FixedBall switch + { + Ball.Safari when pk.Ball is (byte)Ball.Safari => true, + _ => pk.Ball is not (byte)Ball.Safari, + }; + private static bool IsTypeCompatible(IEncounterTemplate enc, PKM pk, PIDType type) { if (enc is IRandomCorrelation r) diff --git a/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator4.cs b/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator4.cs index dad2113f3..84879e13c 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator4.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator4.cs @@ -26,14 +26,35 @@ public IEnumerable GetPossible(PKM pk, EvoCriteria[] chain, Game yield return enc; } + private enum DeferralType + { + None, + PIDIV, + Tile, + Ball, + SlotNumber, + } + + private struct Deferral + { + public DeferralType Type; + public IEncounterable? Encounter; + + public void Update(DeferralType type, IEncounterable enc) + { + if (Type >= type) + return; + Type = type; + Encounter = enc; + } + } + public IEnumerable GetEncounters(PKM pk, EvoCriteria[] chain, LegalInfo info) { info.PIDIV = MethodFinder.Analyze(pk); var game = pk.Version; var iterator = new EncounterEnumerator4(pk, chain, game); - EncounterSlot4? deferSlot = null; - IEncounterable? deferTile = null; - IEncounterable? deferType = null; + Deferral defer = default; var leadQueue = new LeadEncounterQueue(); foreach (var enc in iterator) @@ -41,17 +62,25 @@ public IEnumerable GetEncounters(PKM pk, EvoCriteria[] chain, Le var e = enc.Encounter; if (!IsTileCompatible(e, pk)) { - deferTile ??= e; + defer.Update(DeferralType.Tile, e); continue; } if (!IsTypeCompatible(e, pk, info.PIDIV.Type)) { - deferType ??= e; + defer.Update(DeferralType.PIDIV, e); + continue; + } + if (!IsBallCompatible(e, pk)) + { + defer.Update(DeferralType.Ball, e); continue; } if (e is not EncounterSlot4 slot) { - yield return e; + if (pk.Ball is (byte)Ball.Safari or (byte)Ball.Sport) + defer.Update(DeferralType.Ball, e); + else + yield return e; continue; } @@ -59,7 +88,7 @@ public IEnumerable GetEncounters(PKM pk, EvoCriteria[] chain, Le var lead = LeadFinder.GetLeadInfo4(pk, slot, info.PIDIV, evo); if (!lead.IsValid()) { - deferSlot ??= slot; + defer.Update(DeferralType.SlotNumber, slot); continue; } leadQueue.Insert(lead, slot); @@ -73,23 +102,23 @@ public IEnumerable GetEncounters(PKM pk, EvoCriteria[] chain, Le if (leadQueue.List.Count != 0) yield break; - // Error will be flagged later if this is chosen. - if (deferTile != null) - { - yield return deferTile; - } - else if (deferSlot != null) - { - info.ManualFlag = EncounterYieldFlag.InvalidFrame; - yield return deferSlot; - } - else if (deferType != null) - { + // Errors will be flagged later for those not manually handled below. + if (defer.Encounter is not { } lastResort) + yield break; + if (defer.Type is DeferralType.PIDIV) info.ManualFlag = EncounterYieldFlag.InvalidPIDIV; - yield return deferType; - } + else if (defer.Type is DeferralType.Tile) + info.ManualFlag = EncounterYieldFlag.InvalidFrame; + yield return lastResort; } + private static bool IsBallCompatible(IFixedBall e, PKM pk) => e.FixedBall switch + { + Ball.Safari when pk.Ball is (byte)Ball.Safari => true, + Ball.Sport when pk.Ball is (byte)Ball.Sport => true, + _ => pk.Ball is not ((byte)Ball.Safari or (byte)Ball.Sport), + }; + private static bool IsTileCompatible(IEncounterTemplate enc, PKM pk) { if (pk is not IGroundTile e) diff --git a/PKHeX.Core/Legality/Evolutions/EvolutionChain.cs b/PKHeX.Core/Legality/Evolutions/EvolutionChain.cs index 38821591c..ab2921969 100644 --- a/PKHeX.Core/Legality/Evolutions/EvolutionChain.cs +++ b/PKHeX.Core/Legality/Evolutions/EvolutionChain.cs @@ -91,6 +91,8 @@ public static EvoCriteria[] GetOriginChain(PKM pk, EvolutionOrigin enc, ushort e return []; var chain = result[..count]; + if (IsMetLost(pk, enc)) // Original met level lost, need to be more permissive on evos. + EvolutionUtil.ConditionEncounterNoMet(chain); return chain.ToArray(); } @@ -121,6 +123,13 @@ public static int GetOriginChain(Span result, PKM pk, EvolutionOrig return count; } + private static bool IsMetLost(PKM pk, EvolutionOrigin enc) => enc.Generation switch + { + >= 5 => false, + <= 2 => pk is not ICaughtData2 { MetLevel: not 0 }, + _ => enc.Generation != pk.Format, + }; + private static int DevolveFrom(Span result, PKM pk, EvolutionOrigin enc, EntityContext context, ushort encSpecies, bool discard) { var group = EvolutionGroupUtil.GetGroup(context); diff --git a/PKHeX.Core/Legality/Evolutions/EvolutionGroup/EvolutionUtil.cs b/PKHeX.Core/Legality/Evolutions/EvolutionGroup/EvolutionUtil.cs index 842c772b8..ebbd35d4b 100644 --- a/PKHeX.Core/Legality/Evolutions/EvolutionGroup/EvolutionUtil.cs +++ b/PKHeX.Core/Legality/Evolutions/EvolutionGroup/EvolutionUtil.cs @@ -163,4 +163,16 @@ private static int CleanEvolve(Span result) } return i; } + + public static void ConditionEncounterNoMet(Span chain) + { + // Allow for under-leveled evolutions for purposes of finding an under-leveled evolved encounter. + // e.g. a level 5 Silcoon encounter slot (normally needs level 7). + for (int i = 0; i < chain.Length - 1; i++) + { + ref var evo = ref chain[i]; + if (evo.Method.IsLevelUpRequired()) + evo = evo with { LevelMin = (byte)(chain[i + 1].LevelMin + evo.LevelUpRequired) }; + } + } } diff --git a/Tests/PKHeX.Core.Tests/Legality/Legal/Generation 6/0061_-_Quaputzi_-_81DD3D8C0FFD safari gen3 level.pk6 b/Tests/PKHeX.Core.Tests/Legality/Legal/Generation 6/0061_-_Quaputzi_-_81DD3D8C0FFD safari gen3 level.pk6 new file mode 100644 index 000000000..d2d76bd5f Binary files /dev/null and b/Tests/PKHeX.Core.Tests/Legality/Legal/Generation 6/0061_-_Quaputzi_-_81DD3D8C0FFD safari gen3 level.pk6 differ diff --git a/Tests/PKHeX.Core.Tests/Legality/Legal/Generation 7 Transfer/0267_-_Beautifly_-_02EC2BFA04AE gen3 l5 silcoon.pk7 b/Tests/PKHeX.Core.Tests/Legality/Legal/Generation 7 Transfer/0267_-_Beautifly_-_02EC2BFA04AE gen3 l5 silcoon.pk7 new file mode 100644 index 000000000..1e0906e0b Binary files /dev/null and b/Tests/PKHeX.Core.Tests/Legality/Legal/Generation 7 Transfer/0267_-_Beautifly_-_02EC2BFA04AE gen3 l5 silcoon.pk7 differ