Add ball deferral & met level leniency for gen3/4

Fixes some issues reported via discord
This commit is contained in:
Kurt 2024-04-26 01:33:19 -05:00
parent 4e56a2b756
commit 3358038172
6 changed files with 115 additions and 35 deletions

View file

@ -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)

View file

@ -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,17 +62,25 @@ 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)
{ {
yield return e; if (pk.Ball is (byte)Ball.Safari or (byte)Ball.Sport)
defer.Update(DeferralType.Ball, e);
else
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)

View file

@ -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);

View file

@ -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) };
}
}
} }