mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-22 20:13:06 +00:00
Add ball deferral & met level leniency for gen3/4
Fixes some issues reported via discord
This commit is contained in:
parent
4e56a2b756
commit
3358038172
6 changed files with 115 additions and 35 deletions
|
@ -21,6 +21,29 @@ public sealed class EncounterGenerator3 : IEncounterGenerator
|
||||||
return GetEncounters(pk, chain, 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<IEncounterable> GetEncounters(PKM pk, EvoCriteria[] chain, LegalInfo info)
|
public IEnumerable<IEncounterable> GetEncounters(PKM pk, EvoCriteria[] chain, LegalInfo info)
|
||||||
{
|
{
|
||||||
if (chain.Length == 0)
|
if (chain.Length == 0)
|
||||||
|
@ -29,8 +52,7 @@ public sealed class EncounterGenerator3 : IEncounterGenerator
|
||||||
info.PIDIV = MethodFinder.Analyze(pk);
|
info.PIDIV = MethodFinder.Analyze(pk);
|
||||||
var game = pk.Version;
|
var game = pk.Version;
|
||||||
var iterator = new EncounterEnumerator3(pk, chain, game);
|
var iterator = new EncounterEnumerator3(pk, chain, game);
|
||||||
IEncounterable? deferType = null;
|
Deferral defer = default;
|
||||||
EncounterSlot3? deferSlot = null;
|
|
||||||
var leadQueue = new LeadEncounterQueue<EncounterSlot3>();
|
var leadQueue = new LeadEncounterQueue<EncounterSlot3>();
|
||||||
|
|
||||||
bool emerald = pk.E;
|
bool emerald = pk.E;
|
||||||
|
@ -43,7 +65,12 @@ public sealed class EncounterGenerator3 : IEncounterGenerator
|
||||||
var e = enc.Encounter;
|
var e = enc.Encounter;
|
||||||
if (!IsTypeCompatible(e, pk, info.PIDIV.Type))
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if (e is not EncounterSlot3 slot)
|
if (e is not EncounterSlot3 slot)
|
||||||
|
@ -76,7 +103,7 @@ public sealed class EncounterGenerator3 : IEncounterGenerator
|
||||||
var lead = LeadFinder.GetLeadInfo3(slot, info.PIDIV, evo, emerald, gender, pk.Format);
|
var lead = LeadFinder.GetLeadInfo3(slot, info.PIDIV, evo, emerald, gender, pk.Format);
|
||||||
if (!lead.IsValid())
|
if (!lead.IsValid())
|
||||||
{
|
{
|
||||||
deferSlot ??= slot;
|
defer.Update(DeferralType.SlotNumber, slot);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
leadQueue.Insert(lead, slot);
|
leadQueue.Insert(lead, slot);
|
||||||
|
@ -90,19 +117,22 @@ public sealed class EncounterGenerator3 : IEncounterGenerator
|
||||||
if (leadQueue.List.Count != 0)
|
if (leadQueue.List.Count != 0)
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
// Error will be flagged later if this is chosen.
|
// Errors will be flagged later for those not manually handled below.
|
||||||
if (deferSlot != null)
|
if (defer.Encounter is not { } lastResort)
|
||||||
{
|
yield break;
|
||||||
info.ManualFlag = EncounterYieldFlag.InvalidFrame;
|
if (defer.Type is DeferralType.PIDIV)
|
||||||
yield return deferSlot;
|
|
||||||
}
|
|
||||||
else if (deferType != null)
|
|
||||||
{
|
|
||||||
info.ManualFlag = EncounterYieldFlag.InvalidPIDIV;
|
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)
|
private static bool IsTypeCompatible(IEncounterTemplate enc, PKM pk, PIDType type)
|
||||||
{
|
{
|
||||||
if (enc is IRandomCorrelation r)
|
if (enc is IRandomCorrelation r)
|
||||||
|
|
|
@ -26,14 +26,35 @@ public sealed class EncounterGenerator4 : IEncounterGenerator
|
||||||
yield return enc;
|
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<IEncounterable> GetEncounters(PKM pk, EvoCriteria[] chain, LegalInfo info)
|
public IEnumerable<IEncounterable> GetEncounters(PKM pk, EvoCriteria[] chain, LegalInfo info)
|
||||||
{
|
{
|
||||||
info.PIDIV = MethodFinder.Analyze(pk);
|
info.PIDIV = MethodFinder.Analyze(pk);
|
||||||
var game = pk.Version;
|
var game = pk.Version;
|
||||||
var iterator = new EncounterEnumerator4(pk, chain, game);
|
var iterator = new EncounterEnumerator4(pk, chain, game);
|
||||||
EncounterSlot4? deferSlot = null;
|
Deferral defer = default;
|
||||||
IEncounterable? deferTile = null;
|
|
||||||
IEncounterable? deferType = null;
|
|
||||||
var leadQueue = new LeadEncounterQueue<EncounterSlot4>();
|
var leadQueue = new LeadEncounterQueue<EncounterSlot4>();
|
||||||
|
|
||||||
foreach (var enc in iterator)
|
foreach (var enc in iterator)
|
||||||
|
@ -41,16 +62,24 @@ public sealed class EncounterGenerator4 : IEncounterGenerator
|
||||||
var e = enc.Encounter;
|
var e = enc.Encounter;
|
||||||
if (!IsTileCompatible(e, pk))
|
if (!IsTileCompatible(e, pk))
|
||||||
{
|
{
|
||||||
deferTile ??= e;
|
defer.Update(DeferralType.Tile, e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!IsTypeCompatible(e, pk, info.PIDIV.Type))
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if (e is not EncounterSlot4 slot)
|
if (e is not EncounterSlot4 slot)
|
||||||
{
|
{
|
||||||
|
if (pk.Ball is (byte)Ball.Safari or (byte)Ball.Sport)
|
||||||
|
defer.Update(DeferralType.Ball, e);
|
||||||
|
else
|
||||||
yield return e;
|
yield return e;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +88,7 @@ public sealed class EncounterGenerator4 : IEncounterGenerator
|
||||||
var lead = LeadFinder.GetLeadInfo4(pk, slot, info.PIDIV, evo);
|
var lead = LeadFinder.GetLeadInfo4(pk, slot, info.PIDIV, evo);
|
||||||
if (!lead.IsValid())
|
if (!lead.IsValid())
|
||||||
{
|
{
|
||||||
deferSlot ??= slot;
|
defer.Update(DeferralType.SlotNumber, slot);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
leadQueue.Insert(lead, slot);
|
leadQueue.Insert(lead, slot);
|
||||||
|
@ -73,23 +102,23 @@ public sealed class EncounterGenerator4 : IEncounterGenerator
|
||||||
if (leadQueue.List.Count != 0)
|
if (leadQueue.List.Count != 0)
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
// Error will be flagged later if this is chosen.
|
// Errors will be flagged later for those not manually handled below.
|
||||||
if (deferTile != null)
|
if (defer.Encounter is not { } lastResort)
|
||||||
{
|
yield break;
|
||||||
yield return deferTile;
|
if (defer.Type is DeferralType.PIDIV)
|
||||||
}
|
|
||||||
else if (deferSlot != null)
|
|
||||||
{
|
|
||||||
info.ManualFlag = EncounterYieldFlag.InvalidFrame;
|
|
||||||
yield return deferSlot;
|
|
||||||
}
|
|
||||||
else if (deferType != null)
|
|
||||||
{
|
|
||||||
info.ManualFlag = EncounterYieldFlag.InvalidPIDIV;
|
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)
|
private static bool IsTileCompatible(IEncounterTemplate enc, PKM pk)
|
||||||
{
|
{
|
||||||
if (pk is not IGroundTile e)
|
if (pk is not IGroundTile e)
|
||||||
|
|
|
@ -91,6 +91,8 @@ public static class EvolutionChain
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
var chain = result[..count];
|
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();
|
return chain.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,6 +123,13 @@ public static class EvolutionChain
|
||||||
return count;
|
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<EvoCriteria> result, PKM pk, EvolutionOrigin enc, EntityContext context, ushort encSpecies, bool discard)
|
private static int DevolveFrom(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc, EntityContext context, ushort encSpecies, bool discard)
|
||||||
{
|
{
|
||||||
var group = EvolutionGroupUtil.GetGroup(context);
|
var group = EvolutionGroupUtil.GetGroup(context);
|
||||||
|
|
|
@ -163,4 +163,16 @@ internal static class EvolutionUtil
|
||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ConditionEncounterNoMet(Span<EvoCriteria> 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) };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue