mirror of
https://github.com/kwsch/PKHeX
synced 2025-02-16 13:28:35 +00:00
Change EncounterCriteria to use Level Range
This commit is contained in:
parent
52351bf0e7
commit
0b686d428b
13 changed files with 123 additions and 34 deletions
|
@ -6,7 +6,7 @@ namespace PKHeX.Core;
|
|||
/// <summary>
|
||||
/// Object that can be fed to a <see cref="IEncounterConvertible"/> converter to ensure that the resulting <see cref="PKM"/> meets rough specifications.
|
||||
/// </summary>
|
||||
public sealed record EncounterCriteria : IFixedNature, IFixedAbilityNumber, IShinyPotential
|
||||
public sealed record EncounterCriteria : IFixedNature, IFixedAbilityNumber, IShinyPotential, ILevelRange
|
||||
{
|
||||
/// <summary>
|
||||
/// Default criteria with no restrictions (random) for all fields.
|
||||
|
@ -36,10 +36,8 @@ public sealed record EncounterCriteria : IFixedNature, IFixedAbilityNumber, IShi
|
|||
public sbyte IV_SPD { get; init; } = RandomIV;
|
||||
public sbyte IV_SPE { get; init; } = RandomIV;
|
||||
|
||||
/// <summary>
|
||||
/// If the Encounter yields variable level ranges (e.g. RNG correlation), force the minimum level instead of yielding first match.
|
||||
/// </summary>
|
||||
public bool ForceMinLevelRange { get; set; }
|
||||
public byte LevelMin { get; set; }
|
||||
public byte LevelMax { get; set; }
|
||||
|
||||
public sbyte TeraType { get; init; } = -1;
|
||||
|
||||
|
@ -49,6 +47,7 @@ public sealed record EncounterCriteria : IFixedNature, IFixedAbilityNumber, IShi
|
|||
private const int RandomIV = -1;
|
||||
|
||||
public bool IsSpecifiedNature() => Nature != Nature.Random;
|
||||
public bool IsSpecifiedLevelRange() => LevelMin != 0;
|
||||
public bool IsSpecifiedTeraType() => TeraType != -1;
|
||||
|
||||
public bool IsSpecifiedIVs() => IV_HP != RandomIV
|
||||
|
@ -58,6 +57,8 @@ public sealed record EncounterCriteria : IFixedNature, IFixedAbilityNumber, IShi
|
|||
&& IV_SPD != RandomIV
|
||||
&& IV_SPE != RandomIV;
|
||||
|
||||
public bool IsLevelRangeSatisfied(byte level) => LevelMin <= level && level <= LevelMax;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the IVs are compatible with the encounter's defined IV restrictions.
|
||||
/// </summary>
|
||||
|
|
|
@ -134,15 +134,26 @@ public sealed record EncounterArea3 : IEncounterArea<EncounterSlot3>, IAreaLocat
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wild Encounter data <see cref="IEncounterTemplate"/> Type
|
||||
/// </summary>
|
||||
public enum SlotType3 : byte
|
||||
{
|
||||
/// <summary> Grass tiles </summary>
|
||||
Grass = 0,
|
||||
/// <summary> Water tiles </summary>
|
||||
Surf = 1,
|
||||
/// <summary> Fishing with Old Rod </summary>
|
||||
Old_Rod = 2,
|
||||
/// <summary> Fishing with Good Rod </summary>
|
||||
Good_Rod = 3,
|
||||
/// <summary> Fishing with Super Rod </summary>
|
||||
Super_Rod = 4,
|
||||
/// <summary> Using Rock Smash move </summary>
|
||||
Rock_Smash = 5,
|
||||
|
||||
/// <summary> Swarm in grass tiles </summary>
|
||||
SwarmGrass50 = 6,
|
||||
/// <summary> Swarm in water tiles </summary>
|
||||
SwarmFish50 = 7,
|
||||
}
|
||||
|
|
|
@ -128,11 +128,14 @@ public sealed record EncounterArea4 : IEncounterArea<EncounterSlot4>, IGroundTyp
|
|||
58, // 20 Floaroma Meadow
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Unown form is valid for the given <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
public static bool IsUnownFormValid(PKM pk, byte form)
|
||||
{
|
||||
return pk.HGSS
|
||||
? RuinsOfAlph4.IsUnownFormValid(pk, form)
|
||||
: SolaceonRuins4.IsUnownFormValid(pk, form);
|
||||
? RuinsOfAlph4.IsFormValid(pk, form)
|
||||
: SolaceonRuins4.IsFormValid(pk, form);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,28 +146,54 @@ public sealed record EncounterArea4 : IEncounterArea<EncounterSlot4>, IGroundTyp
|
|||
/// Different from <see cref="GroundTileAllowed"/>, this corresponds to the method that the <see cref="IEncounterTemplate"/> may be encountered.</remarks>
|
||||
public enum SlotType4 : byte
|
||||
{
|
||||
/// <summary> Grass tiles </summary>
|
||||
Grass = 0,
|
||||
/// <summary> Water tiles </summary>
|
||||
Surf = 1,
|
||||
/// <summary> Fishing with Old Rod </summary>
|
||||
Old_Rod = 2,
|
||||
/// <summary> Fishing with Good Rod </summary>
|
||||
Good_Rod = 3,
|
||||
/// <summary> Fishing with Super Rod </summary>
|
||||
Super_Rod = 4,
|
||||
/// <summary> Using Rock Smash move </summary>
|
||||
Rock_Smash = 5,
|
||||
|
||||
/// <summary> Using Headbutt on capable trees </summary>
|
||||
Headbutt = 6,
|
||||
/// <summary> Using Headbutt on special trees </summary>
|
||||
HeadbuttSpecial = 7,
|
||||
/// <summary> Grass tiles during a Bug Catching Contest </summary>
|
||||
BugContest = 8,
|
||||
/// <summary> Shaking Honey Trees </summary>
|
||||
HoneyTree = 9,
|
||||
|
||||
/// <summary> Grass tiles in the Safari Zone </summary>
|
||||
Safari_Grass = 10,
|
||||
/// <summary> Water tiles in the Safari Zone </summary>
|
||||
Safari_Surf = 11,
|
||||
/// <summary> Fishing with Old Rod in the Safari Zone </summary>
|
||||
Safari_Old_Rod = 12,
|
||||
/// <summary> Fishing with Good Rod in the Safari Zone </summary>
|
||||
Safari_Good_Rod = 13,
|
||||
/// <summary> Fishing with Super Rod in the Safari Zone </summary>
|
||||
Safari_Super_Rod = 14,
|
||||
}
|
||||
|
||||
public static class SlotType4Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="type"/> is an encounter within the Safari Zone.
|
||||
/// </summary>
|
||||
public static bool IsSafari(this SlotType4 type) => type >= SlotType4.Safari_Grass;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="type"/> has a level range that is random. For D/P/Pt; this is all types except Grass.
|
||||
/// </summary>
|
||||
public static bool IsLevelRandDPPt(this SlotType4 type) => type != SlotType4.Grass;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="type"/> has a level range that is random. For HG/SS; this is all types except Grass and Safari.
|
||||
/// </summary>
|
||||
public static bool IsLevelRandHGSS(this SlotType4 type) => type != SlotType4.Grass && !type.IsSafari();
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ public sealed record EncounterSlot8a(EncounterArea8a Parent, ushort Species, byt
|
|||
{
|
||||
// Give a random level according to the RNG correlation.
|
||||
var lvl = Overworld8aRNG.GetRandomLevel(slotSeed, LevelMin, LevelMax);
|
||||
if (criteria.ForceMinLevelRange && lvl != LevelMin)
|
||||
if (criteria.IsSpecifiedLevelRange() && !criteria.IsLevelRangeSatisfied(lvl))
|
||||
continue;
|
||||
pk.MetLevel = pk.CurrentLevel = lvl;
|
||||
}
|
||||
|
|
|
@ -120,7 +120,9 @@ public static class GenerateMethodH
|
|||
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
|
||||
if (!criteria.IsGenderSatisfied(gender))
|
||||
continue;
|
||||
var lead = MethodH.GetSeed(enc, seed, pk.E, gender, criteria.ForceMinLevelRange);
|
||||
var lead = criteria.IsSpecifiedLevelRange()
|
||||
? MethodH.GetSeed(enc, seed, pk.E, gender, criteria)
|
||||
: MethodH.GetSeed(enc, seed, pk.E, gender);
|
||||
if (!lead.IsValid()) // Verifies the slot, (min) level, and nature loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
|
@ -155,7 +157,9 @@ public static class GenerateMethodH
|
|||
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
|
||||
if (!criteria.IsGenderSatisfied(gender))
|
||||
continue;
|
||||
var lead = MethodH.GetSeed(enc, seed, pk.E, gender, criteria.ForceMinLevelRange);
|
||||
var lead = criteria.IsSpecifiedLevelRange()
|
||||
? MethodH.GetSeed(enc, seed, pk.E, gender, criteria)
|
||||
: MethodH.GetSeed(enc, seed, pk.E, gender);
|
||||
if (!lead.IsValid()) // Verifies the slot and nature loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
|
@ -203,7 +207,7 @@ public static class GenerateMethodH
|
|||
continue;
|
||||
seed = LCRNG.Prev(seed);
|
||||
}
|
||||
var lead = MethodH.GetSeed(enc, seed, false, 2, criteria.ForceMinLevelRange);
|
||||
var lead = MethodH.GetSeed(enc, seed, false, 2);
|
||||
if (!lead.IsValid()) // Verifies the slot and form loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
|
@ -229,7 +233,7 @@ public static class GenerateMethodH
|
|||
var form = EntityPID.GetUnownForm3(pid);
|
||||
if (form != enc.Form)
|
||||
continue;
|
||||
var lead = MethodH.GetSeed(enc, seed, false, 2, criteria.ForceMinLevelRange);
|
||||
var lead = MethodH.GetSeed(enc, seed, false, 2);
|
||||
if (!lead.IsValid()) // Verifies the slot and form loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
|
|
|
@ -33,9 +33,17 @@ public static class MethodH
|
|||
}
|
||||
|
||||
/// <remarks>Used when generating or ignoring level ranges.</remarks>
|
||||
/// <inheritdoc cref="GetSeed{TEnc,TEvo}"/>
|
||||
public static LeadSeed GetSeed<TEnc>(TEnc enc, uint seed, bool emerald, byte gender, bool minLevel = false)
|
||||
where TEnc : IEncounterSlot3 => GetSeed(enc, seed, enc, emerald, gender, minLevel ? (byte)3 : (byte)0);
|
||||
/// <inheritdoc cref="GetSeed{TEnc,TEvo}(TEnc, uint, TEvo, bool, byte, byte)"/>
|
||||
public static LeadSeed GetSeed<TEnc>(TEnc enc, uint seed, bool emerald, byte gender)
|
||||
where TEnc : IEncounterSlot3
|
||||
=> GetSeed(enc, seed, enc, emerald, gender, 0);
|
||||
|
||||
/// <remarks>Used when generating with specific level ranges.</remarks>
|
||||
/// <inheritdoc cref="GetSeed{TEnc,TEvo}(TEnc, uint, TEvo, bool, byte, byte)"/>
|
||||
public static LeadSeed GetSeed<TEnc, TEvo>(TEnc enc, uint seed, bool emerald, byte gender, TEvo evo)
|
||||
where TEnc : IEncounterSlot3
|
||||
where TEvo : ILevelRange
|
||||
=> GetSeed(enc, seed, evo, emerald, gender, 3);
|
||||
|
||||
// Summary of Random Determinations:
|
||||
// Nature: rand() % 25 == nature
|
||||
|
|
|
@ -102,7 +102,9 @@ public static class GenerateMethodJ
|
|||
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
|
||||
if (!criteria.IsGenderSatisfied(gender))
|
||||
continue;
|
||||
var lead = MethodJ.GetSeed(enc, seed, criteria.ForceMinLevelRange);
|
||||
var lead = criteria.IsSpecifiedLevelRange()
|
||||
? MethodJ.GetSeed(enc, seed, criteria)
|
||||
: MethodJ.GetSeed(enc, seed);
|
||||
if (!lead.IsValid()) // Verifies the slot, (min) level, and nature loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
|
|
|
@ -106,7 +106,9 @@ public static class GenerateMethodK
|
|||
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
|
||||
if (!criteria.IsGenderSatisfied(gender))
|
||||
continue;
|
||||
var lead = MethodK.GetSeed(enc, seed, criteria.ForceMinLevelRange);
|
||||
var lead = criteria.IsSpecifiedLevelRange()
|
||||
? MethodK.GetSeed(enc, seed, criteria)
|
||||
: MethodK.GetSeed(enc, seed);
|
||||
if (!lead.IsValid()) // Verifies the slot, (min) level, and nature loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
|
|
|
@ -30,9 +30,16 @@ public static class MethodJ
|
|||
}
|
||||
|
||||
/// <remarks>Used when generating or ignoring level ranges.</remarks>
|
||||
/// <inheritdoc cref="GetSeed{TEnc,TEvo}"/>
|
||||
public static LeadSeed GetSeed<TEnc>(TEnc enc, uint seed, bool minLevel = false) where TEnc : IEncounterSlot4
|
||||
=> GetSeed(enc, seed, enc, minLevel ? (byte)4 : (byte)0);
|
||||
/// <inheritdoc cref="GetSeed{TEnc,TEvo}(TEnc, uint, TEvo, byte)"/>
|
||||
public static LeadSeed GetSeed<TEnc>(TEnc enc, uint seed) where TEnc : IEncounterSlot4
|
||||
=> GetSeed(enc, seed, enc, 0);
|
||||
|
||||
/// <remarks>Used when generating with specific level ranges.</remarks>
|
||||
/// <inheritdoc cref="GetSeed{TEnc,TEvo}(TEnc, uint, TEvo, byte)"/>
|
||||
public static LeadSeed GetSeed<TEnc, TEvo>(TEnc enc, uint seed, TEvo evo)
|
||||
where TEnc : IEncounterSlot4
|
||||
where TEvo : ILevelRange
|
||||
=> GetSeed(enc, seed, evo, 4);
|
||||
|
||||
// Summary of Random Determinations:
|
||||
// For constant-value rand choices, the games avoid using modulo via:
|
||||
|
|
|
@ -23,9 +23,16 @@ public static class MethodK
|
|||
=> GetSeed(enc, seed, evo.LevelMin, evo.LevelMax, format);
|
||||
|
||||
/// <remarks>Used when generating or ignoring level ranges.</remarks>
|
||||
/// <inheritdoc cref="GetSeed{TEnc,TEvo}"/>
|
||||
public static LeadSeed GetSeed<TEnc>(TEnc enc, uint seed, bool minLevel = false) where TEnc : IEncounterSlot4
|
||||
=> GetSeed(enc, seed, enc, minLevel ? (byte)4 : (byte)0);
|
||||
/// <inheritdoc cref="GetSeed{TEnc,TEvo}(TEnc, uint, TEvo, byte)"/>
|
||||
public static LeadSeed GetSeed<TEnc>(TEnc enc, uint seed) where TEnc : IEncounterSlot4
|
||||
=> GetSeed(enc, seed, enc, 0);
|
||||
|
||||
/// <remarks>Used when generating with specific level ranges.</remarks>
|
||||
/// <inheritdoc cref="GetSeed{TEnc,TEvo}(TEnc, uint, TEvo, byte)"/>
|
||||
public static LeadSeed GetSeed<TEnc, TEvo>(TEnc enc, uint seed, TEvo evo)
|
||||
where TEnc : IEncounterSlot4
|
||||
where TEvo : ILevelRange
|
||||
=> GetSeed(enc, seed, evo, 4);
|
||||
|
||||
public static LeadSeed GetSeed<TEnc>(TEnc enc, uint seed, byte levelMin, byte levelMax, byte format = Format, int depth = 0)
|
||||
where TEnc : IEncounterSlot4
|
||||
|
|
|
@ -5,25 +5,35 @@ namespace PKHeX.Core;
|
|||
public static class RuinsOfAlph4
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the requested <see cref="form"/> is valid for the given seed.
|
||||
/// Checks if the requested <see cref="form"/> is valid for the given result.
|
||||
/// </summary>
|
||||
public static bool IsUnownFormValid(PKM pk, byte form)
|
||||
public static bool IsFormValid(PKM pk, byte form)
|
||||
{
|
||||
if (!MethodFinder.GetLCRNGMethod1Match(pk, out var seed))
|
||||
return true; // invalid anyway, don't care.
|
||||
|
||||
// ABCD|E(Item)|F(Form) determination
|
||||
var f = LCRNG.Next6(seed);
|
||||
return IsFormValid(form, f);
|
||||
return IsFormValid(seed, form);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the requested <see cref="form"/> is valid for the given seed.
|
||||
/// </summary>
|
||||
/// <param name="seed">Seed that originated the PID/IV.</param>
|
||||
/// <param name="form">Form to validate</param>
|
||||
/// <returns>True if the form is valid</returns>
|
||||
public static bool IsFormValid(uint seed, byte form)
|
||||
{
|
||||
// ABCD|E(Item)|F(Form) determination
|
||||
var f = LCRNG.Next6(seed);
|
||||
return IsFormValidFrame(f, form);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the requested <see cref="form"/> is valid for the given frame seed that calculates form.
|
||||
/// </summary>
|
||||
/// <param name="seed">RNG state that determines the form</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsFormValid(byte form, uint seed)
|
||||
/// <param name="form">Form to validate</param>
|
||||
/// <returns>True if the form is valid</returns>
|
||||
public static bool IsFormValidFrame(uint seed, byte form)
|
||||
{
|
||||
if (form >= 26) // Entrance
|
||||
return form == GetEntranceForm(seed);
|
||||
|
|
|
@ -7,7 +7,7 @@ public static class SolaceonRuins4
|
|||
/// <summary>
|
||||
/// Checks if the requested <see cref="form"/> is valid for the given seed.
|
||||
/// </summary>
|
||||
public static bool IsUnownFormValid(PKM pk, byte form)
|
||||
public static bool IsFormValid(PKM pk, byte form)
|
||||
{
|
||||
if (IsSingleFormRoomUnown(form))
|
||||
return true; // FRIEND: Specific rooms with only one form.
|
||||
|
@ -17,6 +17,7 @@ public static class SolaceonRuins4
|
|||
if (!MethodFinder.GetLCRNGMethod1Match(pk, out var seed))
|
||||
return true; // invalid anyway, don't care.
|
||||
|
||||
// ABCD|E(Item)|F(Form) determination
|
||||
var f = LCRNG.Next6(seed) >> 16;
|
||||
var expect = GetUnownForm(f, form);
|
||||
return expect == form;
|
||||
|
|
|
@ -121,10 +121,17 @@ public static class Encounter9RNG
|
|||
var nature = enc.Nature != Nature.Random ? enc.Nature : enc.Species == (int)Species.Toxtricity
|
||||
? ToxtricityUtil.GetRandomNature(ref rand, pk.Form)
|
||||
: (Nature)rand.NextInt(25);
|
||||
if (criteria.Nature != Nature.Random && nature != criteria.Nature)
|
||||
return false;
|
||||
pk.Nature = pk.StatNature = nature;
|
||||
|
||||
// Compromise on Nature -- some are fixed, some are random. If the request wants a specific nature, just mint it.
|
||||
var requestNature = criteria.Nature;
|
||||
if (criteria.Nature != Nature.Random && nature != requestNature)
|
||||
{
|
||||
if (!requestNature.IsMint())
|
||||
return false;
|
||||
pk.StatNature = requestNature;
|
||||
}
|
||||
|
||||
pk.HeightScalar = enc.Height != 0 ? enc.Height : (byte)(rand.NextInt(0x81) + rand.NextInt(0x80));
|
||||
pk.WeightScalar = enc.Weight != 0 ? enc.Weight : (byte)(rand.NextInt(0x81) + rand.NextInt(0x80));
|
||||
pk.Scale = enc.ScaleType.GetSizeValue(enc.Scale, ref rand);
|
||||
|
|
Loading…
Add table
Reference in a new issue