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);
}
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)
{
if (chain.Length == 0)
@ -29,8 +52,7 @@ public sealed class EncounterGenerator3 : IEncounterGenerator
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<EncounterSlot3>();
bool emerald = pk.E;
@ -43,7 +65,12 @@ public sealed class EncounterGenerator3 : IEncounterGenerator
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 sealed class EncounterGenerator3 : IEncounterGenerator
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 sealed class EncounterGenerator3 : IEncounterGenerator
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)

View file

@ -26,14 +26,35 @@ public sealed class EncounterGenerator4 : IEncounterGenerator
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)
{
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<EncounterSlot4>();
foreach (var enc in iterator)
@ -41,17 +62,25 @@ public sealed class EncounterGenerator4 : IEncounterGenerator
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 sealed class EncounterGenerator4 : IEncounterGenerator
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 sealed class EncounterGenerator4 : IEncounterGenerator
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)

View file

@ -91,6 +91,8 @@ public static class EvolutionChain
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 class EvolutionChain
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)
{
var group = EvolutionGroupUtil.GetGroup(context);

View file

@ -163,4 +163,16 @@ internal static class EvolutionUtil
}
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) };
}
}
}