Add more generator filtering criteria

Gen6+ slots now try to give shinies if possible & requested
Gen3/4 slots now try to respect IV requests if only a couple are requested
Removes PIDGenerator; no remaining uses (either all inlined or extracted to specialized generator classes). API usage wasn't recommended anyway, as it was incomplete and did not honor all correlations. We want to try to generate it right the first time.
This commit is contained in:
Kurt
2025-06-14 21:45:16 -05:00
parent a57914cf27
commit c274ff6f3a
22 changed files with 203 additions and 326 deletions

View File

@@ -85,7 +85,7 @@ private void SetPINGA(CK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
if (!IsEReader)
SetPINGA_Regular(pk, criteria, pi);
else
SetPINGA_EReader(pk);
SetPINGA_EReader(pk, criteria);
}
private void SetPINGA_Regular(CK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
@@ -98,7 +98,7 @@ private void SetPINGA_Regular(CK3 pk, EncounterCriteria criteria, PersonalInfo3
this.SetRandom(pk, EncounterCriteria.Unrestricted, pi, noShiny: false, seed);
}
private void SetPINGA_EReader(CK3 pk)
private void SetPINGA_EReader(CK3 pk, EncounterCriteria criteria)
{
// E-Reader have all IVs == 0
// Skip setting IVs.
@@ -113,15 +113,27 @@ private void SetPINGA_EReader(CK3 pk)
int ctr = 0;
const int max = 100_000;
var rnd = Util.Rand;
var gr = pk.PersonalInfo.Gender;
do
{
var seed = rnd.Rand32();
PIDGenerator.SetValuesFromSeedXDRNG_EReader(pk, seed);
if (pk.Nature != nature || pk.Gender != gender)
var D = XDRNG.Prev3(seed); // PID
var E = XDRNG.Next(D); // PID
var pid = (D & 0xFFFF0000) | (E >> 16);
if ((Nature)(pid % 25) != nature || EntityGender.GetFromPIDAndRatio(pid, gr) != gender)
continue;
if (criteria.Shiny.IsShiny() && !ShinyUtil.GetIsShiny(pk.ID32, pid, 8))
continue;
var result = LockFinder.IsAllShadowLockValid(this, seed, pk);
if (result)
break;
if (!result)
continue;
pk.PID = pid;
pk.RefreshAbility(0);
// IVs always 0 for E-Reader shadows.
}
while (++ctr <= max);
}

View File

@@ -98,15 +98,58 @@ private int GetTemplateLanguage(ITrainerInfo tr)
private void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
{
var gr = pi.Gender;
bool truncate = IsRoamingTruncatedIVs;
if (criteria.IsSpecifiedIVsAll() && !truncate)
if (IsRoamingTruncatedIVs)
{
SetRoamerPINGA(pk, criteria);
return;
}
if (criteria.IsSpecifiedIVsAll())
{
if (TrySetMethod1(pk, criteria, gr))
return;
}
SetMethod1(pk, criteria, gr, Util.Rand32());
if (truncate) // Never happens for eggs or dual-ability encounters.
pk.IV32 &= 0xFF;
}
private static bool SetRoamerPINGA(PK3 pk, EncounterCriteria criteria)
{
// For every possible 8-bit IV combination, check if it meets the criteria.
var id32 = pk.ID32;
for (uint i = 0; i <= byte.MaxValue; i++)
{
var iv32 = i;
// IVs can only ever be Hidden Power: Fighting. Don't bother checking if it matches.
if (!criteria.IsSatisfiedIVs(iv32))
continue;
// Satisfactory IVs; now check PID/nature/shininess.
var frame = iv32 << 16;
for (uint hi = 0; hi <= byte.MaxValue; hi++)
{
for (uint lo = 0; lo <= ushort.MaxValue; lo++)
{
var state = frame | (hi << 24) | lo;
var rand2 = LCRNG.Prev16(ref state);
var rand1 = LCRNG.Prev16(ref state);
var pid = (rand2 << 16) | rand1;
if (criteria.IsSpecifiedNature() && !criteria.IsSatisfiedNature((Nature)(pid % 25)))
continue;
bool shiny = ShinyUtil.GetIsShiny(id32, pid, 8);
if (criteria.Shiny.IsShiny() != shiny)
continue;
pk.PID = pid;
pk.IV32 = iv32;
pk.RefreshAbility(0);
return true;
}
}
}
// Should never happen, but just in case.
SetMethod1(pk, criteria, 255, Util.Rand32());
pk.IV32 &= 0xFF; // truncate to 8 bits (value storage fail)
return false;
}
private static bool TrySetMethod1(PK3 pk, EncounterCriteria criteria, byte gr)
@@ -143,6 +186,7 @@ private static bool TrySetMethod1(PK3 pk, EncounterCriteria criteria, byte gr)
private static void SetMethod1(PK3 pk, in EncounterCriteria criteria, byte gr, uint seed)
{
var id32 = pk.ID32;
bool filterIVs = criteria.IsSpecifiedIVs(2);
while (true)
{
var pid = ClassicEraRNG.GetSequentialPID(ref seed);
@@ -164,6 +208,8 @@ private static void SetMethod1(PK3 pk, in EncounterCriteria criteria, byte gr, u
var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed);
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
continue;
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
pk.PID = pid;
pk.IV32 |= iv32;

View File

@@ -208,7 +208,7 @@ private uint SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
continue;
pk.PID = pid;
pk.IV32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed);
pk.IV32 = ClassicEraRNG.GetSequentialIVs(ref seed);
pk.RefreshAbility((int)(pk.PID & 1));
return seed;
}
@@ -220,7 +220,7 @@ public bool GenerateSeed32(PKM pk, uint seed)
if (Method is Channel)
{
seed = ChannelJirachi.SkipToPIDIV(seed);
PIDGenerator.SetValuesFromSeedChannel(pk3, seed);
SetValuesFromSeedChannel(pk3, seed);
return true;
}
@@ -233,7 +233,7 @@ public bool GenerateSeed32(PKM pk, uint seed)
_ when Method is Method_2 => GetMethod2(ref seed),
_ => GetRegular(ref seed),
};
pk3.IV32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed);
pk3.IV32 = ClassicEraRNG.GetSequentialIVs(ref seed);
return true;
}
@@ -248,7 +248,7 @@ private static bool TrySetWishmkrShiny(PK3 pk, EncounterCriteria criteria)
if (filterNature && !criteria.IsSatisfiedNature((Nature)(pid % 25)))
continue; // try again
var iv32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed);
var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed);
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
continue; // try again
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
@@ -278,7 +278,7 @@ private static uint SetPINGAChannel(PK3 pk, EncounterCriteria criteria)
{
if (!ChannelJirachi.IsPossible(seed))
continue;
PIDGenerator.SetValuesFromSeedChannel(pk, seed);
SetValuesFromSeedChannel(pk, seed);
var pid = pk.EncryptionConstant;
if (criteria.IsSpecifiedNature() && !criteria.IsSatisfiedNature((Nature)(pid % 25)))
continue; // try again
@@ -288,24 +288,49 @@ private static uint SetPINGAChannel(PK3 pk, EncounterCriteria criteria)
}
}
bool filterIVs = criteria.IsSpecifiedIVs(2);
while (true)
{
uint seed = Util.Rand32();
seed = ChannelJirachi.SkipToPIDIV(seed);
PIDGenerator.SetValuesFromSeedChannel(pk, seed);
SetValuesFromSeedChannel(pk, seed);
var pid = pk.EncryptionConstant;
if (criteria.IsSpecifiedNature() && !criteria.IsSatisfiedNature((Nature)(pid % 25)))
continue; // try again
if (criteria.Shiny.IsShiny() != ShinyUtil.GetIsShiny(pk.ID32, pid, 8))
continue; // try again
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(pk.IV32))
var iv32 = pk.IV32;
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
continue; // try again
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
return seed;
}
}
public static void SetValuesFromSeedChannel(PK3 pk, uint seed)
{
const ushort TID16 = 40122;
var sid = XDRNG.Next16(ref seed);
pk.ID32 = (sid << 16) | TID16;
var pid1 = XDRNG.Next16(ref seed);
var pid2 = XDRNG.Next16(ref seed);
var pid = (pid1 << 16) | pid2;
if ((pid2 > 7 ? 0 : 1) != (pid1 ^ sid ^ TID16))
pid ^= 0x80000000;
pk.PID = pid;
pk.HeldItem = (ushort)((XDRNG.Next16(ref seed) >> 15) + 169u); // 0-Ganlon, 1-Salac
pk.Version = GameVersion.S + (byte)(XDRNG.Next16(ref seed) >> 15); // 0-Sapphire, 1-Ruby
pk.OriginalTrainerGender = (byte)(XDRNG.Next16(ref seed) >> 15);
var iv32 = XDRNG.GetSequentialIV32(seed);
pk.SetIVs(iv32);
}
private uint GetSaneSeed(uint seed) => Method switch
{
BACD_RBCD => Math.Clamp(seed, 3, 213), // BCD digit sum

View File

@@ -73,6 +73,7 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
private static void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
{
uint seed = Util.Rand32();
var filterIVs = criteria.IsSpecifiedIVs(2);
var gr = pi.Gender;
var idXor = pk.TID16; // no SID
while (true)
@@ -82,9 +83,14 @@ private static void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 p
continue; // try again
if (criteria.IsSpecifiedGender() && !criteria.IsSatisfiedGender(EntityGender.GetFromPIDAndRatio(pid, gr)))
continue;
var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed);
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
continue;
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
pk.PID = pid;
pk.IV32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed);
pk.IV32 = iv32;
pk.RefreshAbility((int)(pid & 1));
pk.OriginalTrainerGender = (byte)GetGender(LCRNG.Next16(ref seed));
return;

View File

@@ -74,6 +74,7 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
private static void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
{
uint seed = Util.Rand32();
var filterIVs = criteria.IsSpecifiedIVs(2);
var gr = pi.Gender;
var idXor = pk.TID16; // no SID
while (true)
@@ -84,9 +85,13 @@ private static void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 p
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
if (criteria.IsSpecifiedGender() && !criteria.IsSatisfiedGender(gender))
continue;
var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed);
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
continue;
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
pk.PID = pid;
pk.IV32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed);
pk.IV32 = iv32;
pk.RefreshAbility((int)(pid & 1));
return;
}

View File

@@ -71,14 +71,7 @@ private void SetPINGA(XK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
{
if (Species == (int)Core.Species.Eevee)
{
// Prefer IVs if requested, rather than Trainer Matching.
if (criteria.IsSpecifiedIVsAll() && MethodCXD.SetStarterFromIVs(pk, criteria))
return;
// Fall back to Trainer ID matching.
if (MethodCXD.SetStarterFromTrainerID(pk, criteria, pk.TID16, pk.SID16))
return;
// Fall back to generating a random PID.
MethodCXD.SetStarterRandom(pk, criteria, Util.Rand32());
SetStarterPINGA(pk, criteria);
return;
}
@@ -86,6 +79,19 @@ private void SetPINGA(XK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
return;
MethodCXD.SetRandom(pk, criteria, pi, noShiny: false, Util.Rand32());
}
private static void SetStarterPINGA(XK3 pk, EncounterCriteria criteria)
{
// Prefer IVs if requested, rather than Trainer Matching.
if (criteria.IsSpecifiedIVsAll() && MethodCXD.SetStarterFromIVs(pk, criteria))
return;
// Fall back to Trainer ID matching.
if (MethodCXD.SetStarterFromTrainerID(pk, criteria, pk.TID16, pk.SID16))
return;
// Fall back to generating a random PID.
MethodCXD.SetStarterRandom(pk, criteria, Util.Rand32());
}
#endregion
#region Matching

View File

@@ -159,6 +159,7 @@ private static bool TrySetMethod1(PK4 pk, in EncounterCriteria criteria, byte gr
private static void SetMethod1(PK4 pk, in EncounterCriteria criteria, byte gr, uint seed)
{
var id32 = pk.ID32;
var filterIVs = criteria.IsSpecifiedIVs(2);
while (true)
{
var pid = ClassicEraRNG.GetSequentialPID(ref seed);
@@ -180,6 +181,8 @@ private static void SetMethod1(PK4 pk, in EncounterCriteria criteria, byte gr, u
var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed);
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
continue;
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
pk.PID = pid;
pk.IV32 |= iv32;
@@ -227,6 +230,7 @@ private static bool TrySetChainShiny(PK4 pk, in EncounterCriteria criteria, byte
private static void SetChainShiny(PK4 pk, in EncounterCriteria criteria, byte gr, uint seed)
{
var id32 = pk.ID32;
var filterIVs = criteria.IsSpecifiedIVs(2);
while (true)
{
var pid = ClassicEraRNG.GetChainShinyPID(ref seed, id32);
@@ -244,6 +248,8 @@ private static void SetChainShiny(PK4 pk, in EncounterCriteria criteria, byte gr
var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed);
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
continue;
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
pk.PID = pid;
pk.IV32 = iv32;

View File

@@ -108,6 +108,11 @@ private void SetPINGA(PK6 pk, EncounterCriteria criteria, PersonalInfo6AO pi)
{
var rnd = Util.Rand;
pk.PID = rnd.Rand32();
if (criteria.Shiny.IsShiny())
pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15));
else if (criteria.Shiny == Shiny.Never && pk.IsShiny)
pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny
pk.EncryptionConstant = rnd.Rand32();
pk.Nature = criteria.GetNature();
pk.Gender = criteria.GetGender(pi);

View File

@@ -102,6 +102,11 @@ private void SetPINGA(PK6 pk, EncounterCriteria criteria, PersonalInfo6XY pi)
{
var rnd = Util.Rand;
pk.PID = rnd.Rand32();
if (criteria.Shiny.IsShiny())
pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15));
else if (criteria.Shiny == Shiny.Never && pk.IsShiny)
pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny
pk.EncryptionConstant = rnd.Rand32();
pk.Nature = criteria.GetNature();
pk.Gender = criteria.GetGender(pi);

View File

@@ -91,6 +91,11 @@ private void SetPINGA(PK7 pk, EncounterCriteria criteria, PersonalInfo7 pi)
{
var rnd = Util.Rand;
pk.PID = rnd.Rand32();
if (criteria.Shiny.IsShiny())
pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15));
else if (criteria.Shiny == Shiny.Never && pk.IsShiny)
pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny
pk.EncryptionConstant = rnd.Rand32();
pk.Nature = criteria.GetNature();
pk.Gender = criteria.GetGender(pi);

View File

@@ -64,6 +64,11 @@ private void SetPINGA(PB7 pk, EncounterCriteria criteria, PersonalInfo7GG pi)
{
var rnd = Util.Rand;
pk.PID = rnd.Rand32();
if (criteria.Shiny.IsShiny())
pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15));
else if (criteria.Shiny == Shiny.Never && pk.IsShiny)
pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny
pk.EncryptionConstant = rnd.Rand32();
pk.Nature = criteria.GetNature();
pk.Gender = criteria.GetGender(pi);

View File

@@ -104,6 +104,11 @@ private void SetPINGA(PK8 pk, EncounterCriteria criteria, PersonalInfo8SWSH pi)
var rand = Util.Rand;
pk.EncryptionConstant = rand.Rand32();
pk.PID = rand.Rand32();
if (criteria.Shiny.IsShiny())
pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rand.Next(1, 15));
else if (criteria.Shiny == Shiny.Never && pk.IsShiny)
pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny
pk.HeightScalar = PokeSizeUtil.GetRandomScalar(rand);
pk.WeightScalar = PokeSizeUtil.GetRandomScalar(rand);
criteria.SetRandomIVs(pk);

View File

@@ -97,6 +97,11 @@ private void SetPINGA(PB8 pk, EncounterCriteria criteria, PersonalInfo8BDSP pi)
{
var rnd = Util.Rand;
pk.PID = rnd.Rand32();
if (criteria.Shiny.IsShiny())
pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15));
else if (criteria.Shiny == Shiny.Never && pk.IsShiny)
pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny
pk.EncryptionConstant = rnd.Rand32();
criteria.SetRandomIVs(pk);
pk.Nature = pk.StatNature = criteria.GetNature();

View File

@@ -172,6 +172,11 @@ private void SetPINGA(PK9 pk, EncounterCriteria criteria, PersonalInfo9SV pi)
{
var rnd = Util.Rand;
pk.PID = rnd.Rand32();
if (criteria.Shiny.IsShiny())
pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15));
else if (criteria.Shiny == Shiny.Never && pk.IsShiny)
pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny
pk.EncryptionConstant = rnd.Rand32();
criteria.SetRandomIVs(pk);

View File

@@ -129,6 +129,11 @@ private void SetPINGA(PK9 pk, EncounterCriteria criteria, PersonalInfo9SV pi)
{
var rnd = Util.Rand;
pk.PID = rnd.Rand32();
if (criteria.Shiny.IsShiny())
pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15));
else if (criteria.Shiny == Shiny.Never && pk.IsShiny)
pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny
pk.EncryptionConstant = rnd.Rand32();
pk.Nature = pk.StatNature = criteria.GetNature(Nature);
pk.Gender = criteria.GetGender(Gender, pi);

View File

@@ -99,6 +99,7 @@ public static bool SetFromIVs<TEntity>(TEntity pk, in EncounterCriteria criteria
/// </summary>
public static bool SetStarterFromTrainerID(CK3 pk, in EncounterCriteria criteria, ushort tid, ushort sid)
{
var filterIVs = criteria.IsSpecifiedIVs(2);
var id32 = (uint)sid << 16 | tid;
var species = pk.Species;
// * => TID, SID, fakepid*2, [IVs, ability, PID]
@@ -131,6 +132,8 @@ public static bool SetStarterFromTrainerID(CK3 pk, in EncounterCriteria criteria
var iv32 = iv2 << 15 | iv1;
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
continue;
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
pk.PID = pid;
SetIVs(pk, iv1, iv2);
@@ -265,6 +268,7 @@ private static bool IsValidTrainerCombination(in EncounterCriteria criteria, uin
public static bool SetStarterFromTrainerID(XK3 pk, EncounterCriteria criteria, ushort tid, ushort sid)
{
// * => TID, SID, fakepid*2, [IVs, ability, PID]
var filterIVs = criteria.IsSpecifiedIVs(2);
Span<uint> all = stackalloc uint[XDRNG.MaxCountSeedsPID];
var count = XDRNG.GetSeeds(all, (uint)tid << 16, (uint)sid << 16);
var seeds = all[..count];
@@ -285,6 +289,8 @@ public static bool SetStarterFromTrainerID(XK3 pk, EncounterCriteria criteria, u
var iv32 = iv2 << 15 | iv1;
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
continue;
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
pk.PID = pid;
SetIVs(pk, iv1, iv2);

View File

@@ -15,6 +15,7 @@ public static void SetRandom<T>(this T enc, PK3 pk, PersonalInfo3 pi, EncounterC
var (min, max) = SlotMethodH.GetRange(enc.Type, enc.SlotNumber);
bool checkProc = MethodH.IsEncounterCheckApplicable(enc.Type);
bool checkLevel = criteria.IsSpecifiedLevelRange() && enc.IsLevelWithinRange(criteria);
bool filterIVs = criteria.IsSpecifiedIVs(2);
// Generate Method H correlated PID and IVs, no lead (keep things simple).
while (true)
@@ -55,6 +56,8 @@ public static void SetRandom<T>(this T enc, PK3 pk, PersonalInfo3 pi, EncounterC
var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed);
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
break; // try again
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
{
var level = (byte)MethodH.GetRandomLevel(enc, lv, LeadRequired.None);
@@ -78,6 +81,7 @@ public static void SetRandomUnown<T>(this T enc, PK3 pk, EncounterCriteria crite
var (min, max) = SlotMethodH.GetRangeGrass(enc.SlotNumber);
// Can't game the seed with % 100 increments as Unown's form calculation is based on the PID.
var filterIVs = criteria.IsSpecifiedIVs(2);
while (true)
{
var esv = LCRNG.Next16(ref seed) % 100;
@@ -103,6 +107,8 @@ public static void SetRandomUnown<T>(this T enc, PK3 pk, EncounterCriteria crite
var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed);
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
continue;
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
pk.PID = pid;
pk.IV32 = iv32;

View File

@@ -25,6 +25,7 @@ public static uint SetRandomJ<T>(this T enc, PK4 pk, PersonalInfo4 pi, Encounter
bool randLevel = MethodJ.IsLevelRand(enc);
bool checkProc = MethodJ.IsEncounterCheckApplicable(enc.Type);
bool checkLevel = criteria.IsSpecifiedLevelRange() && enc.IsLevelWithinRange(criteria);
bool filterIVs = criteria.IsSpecifiedIVs(2);
// Generate Method J correlated PID and IVs, no lead (keep things simple).
while (true)
@@ -64,6 +65,8 @@ public static uint SetRandomJ<T>(this T enc, PK4 pk, PersonalInfo4 pi, Encounter
var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed);
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
break; // try again
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
if (enc.Species is (ushort)Species.Unown)
{

View File

@@ -26,6 +26,7 @@ public static uint SetRandomK<T>(this T enc, PK4 pk, PersonalInfo4 pi, Encounter
var modulo = enc.Type.IsSafari() ? 10u : 100u;
bool checkProc = MethodK.IsEncounterCheckApplicable(enc.Type);
bool checkLevel = criteria.IsSpecifiedLevelRange() && enc.IsLevelWithinRange(criteria);
bool filterIVs = criteria.IsSpecifiedIVs(2);
// Generate Method K correlated PID and IVs, no lead (keep things simple).
while (true)
@@ -88,6 +89,8 @@ public static uint SetRandomK<T>(this T enc, PK4 pk, PersonalInfo4 pi, Encounter
var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed);
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
break; // try again
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
if (enc.Type is SlotType4.BugContest && !MethodK.IsAny31(iv32) && !MethodK.IsAny31(iv32 >> 16))
break; // try again

View File

@@ -325,8 +325,14 @@ public static bool GetRandomIVs(EncounterCriteria criteria, out uint iv32)
private static bool TryApply(ref uint seed, out uint iv32, in EncounterCriteria criteria)
{
// Act like a Non-Stroll encounter, generate IV rand() results immediately.
iv32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed);
return criteria.IsSatisfiedIVs(iv32);
var lo = LCRNG.Next15(ref seed);
var hi = LCRNG.Next15(ref seed);
iv32 = (hi << 15) | lo;
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
return false;
if (!criteria.IsSatisfiedIVs(iv32))
return false;
return true;
}
}

View File

@@ -1,291 +0,0 @@
using System;
using static PKHeX.Core.PIDType;
namespace PKHeX.Core;
/// <summary>
/// Contains a collection of methods that mutate the input Pokémon object, usually to obtain a <see cref="PIDType"/> correlation.
/// </summary>
public static class PIDGenerator
{
private static uint SetValuesFromSeedLCRNG(PKM pk, PIDType type, uint seed)
{
var a16 = LCRNG.Next16(ref seed);
var b16 = LCRNG.Next16(ref seed);
if (type is Method_3 or Method_3_Unown) // VBlank skip between PID rand() [RARE]
b16 = LCRNG.Next16(ref seed);
// Unown has switched order of PID halves, "BA**"
pk.PID = type.IsUnown() ? (a16 << 16) | b16 : (b16 << 16) | a16;
var c16 = LCRNG.Next15(ref seed);
if (type is Method_2 or Method_2_Unown) // VBlank skip after PID
c16 = LCRNG.Next15(ref seed);
var d16 = LCRNG.Next15(ref seed);
if (type is Method_4 or Method_4_Unown) // VBlank skip between IVs
d16 = LCRNG.Next15(ref seed);
uint iv32 = (d16 << 15) | c16;
// Roamers only store lowest 8 bits of IV data; zero out the other bits.
if (type == Method_1_Roamer)
iv32 &= 0xFF;
pk.SetIVs(iv32);
return d16;
}
private static uint SetValuesFromSeedBACD(PKM pk, PIDType type, uint seed)
{
uint idxor = pk.TID16 ^ (uint)pk.SID16;
pk.PID = GetPIDFromSeedBACD(ref seed, type, idxor);
SetIVsFromSeedSequentialLCRNG(ref seed, pk);
pk.RefreshAbility((int)(pk.PID & 1));
return seed;
}
private static uint GetPIDFromSeedBACD(ref uint seed, PIDType type, uint idXor) => type switch
{
BACD => CommonEvent3.GetRegular(ref seed),
BACD_S => CommonEvent3.GetForceShiny(ref seed, idXor),
BACD_AX => CommonEvent3.GetAntishiny(ref seed, idXor),
_ => CommonEvent3.GetRegularAntishiny(ref seed, idXor),
};
public static uint GetIVsFromSeedSequentialLCRNG(ref uint seed)
{
var c16 = LCRNG.Next15(ref seed);
var d16 = LCRNG.Next15(ref seed);
return (d16 << 15) | c16;
}
public static void SetIVsFromSeedSequentialLCRNG(ref uint seed, PKM pk)
{
var iv32 = GetIVsFromSeedSequentialLCRNG(ref seed);
pk.SetIVs(iv32);
}
private static uint SetValuesFromSeedXDRNG(PKM pk, uint seed)
{
var species = pk.Species;
switch (species)
{
case (int)Species.Umbreon or (int)Species.Eevee: // Colo Umbreon, XD Eevee
pk.TID16 = (ushort)XDRNG.Next16(ref seed);
pk.SID16 = (ushort)XDRNG.Next16(ref seed);
seed = XDRNG.Next2(seed); // fake PID
break;
case (int)Species.Espeon: // Colo Espeon
var tid = pk.TID16 = (ushort)XDRNG.Next16(ref seed);
var sid = pk.SID16 = (ushort)XDRNG.Next16(ref seed);
seed = XDRNG.Next5(seed); // skip fakePID, IVs, ability
MethodCXD.GetColoStarterPID(ref seed, tid, sid);
seed = XDRNG.Next2(seed); // fake PID
break;
}
var iv1 = XDRNG.Next15(ref seed); // IV1
var iv2 = XDRNG.Next15(ref seed); // IV2
var iv32 = (iv2 << 15) | iv1;
pk.SetIVs(iv32);
_ = XDRNG.Next16(ref seed); // Ability
if (species is (int)Species.Umbreon or (int)Species.Espeon)
{
// Reuse existing logic. Forced-male, never shiny.
pk.PID = MethodCXD.GetColoStarterPID(ref seed, pk.TID16, pk.SID16);
}
else
{
// Generate PID.
var d16 = XDRNG.Next16(ref seed); // PID
var e16 = XDRNG.Next16(ref seed); // PID
pk.PID = (d16 << 16) | e16;
}
return seed;
}
public static uint SetValuesFromSeedXDRNG_EReader(PKM pk, uint seed)
{
var D = XDRNG.Prev3(seed); // PID
var E = XDRNG.Next(D); // PID
pk.PID = (D & 0xFFFF0000) | (E >> 16);
return seed;
}
public static uint SetValuesFromSeedChannel(PKM pk, uint seed)
{
const ushort TID16 = 40122;
var sid = XDRNG.Next16(ref seed);
pk.ID32 = (sid << 16) | TID16;
var pid1 = XDRNG.Next16(ref seed);
var pid2 = XDRNG.Next16(ref seed);
var pid = (pid1 << 16) | pid2;
if ((pid2 > 7 ? 0 : 1) != (pid1 ^ sid ^ TID16))
pid ^= 0x80000000;
pk.PID = pid;
pk.HeldItem = (ushort)((XDRNG.Next16(ref seed) >> 15) + 169u); // 0-Ganlon, 1-Salac
pk.Version = GameVersion.S + (byte)(XDRNG.Next16(ref seed) >> 15); // 0-Sapphire, 1-Ruby
pk.OriginalTrainerGender = (byte)(XDRNG.Next16(ref seed) >> 15);
var iv32 = XDRNG.GetSequentialIV32(seed);
pk.SetIVs(iv32);
return seed;
}
public static void SetValuesFromSeed(PKM pk, PIDType type, uint seed)
{
var method = GetGeneratorMethod(type);
method(pk, seed);
}
private static Func<PKM, uint, uint> GetGeneratorMethod(PIDType t)
{
switch (t)
{
case Channel:
return SetValuesFromSeedChannel;
case CXD:
return SetValuesFromSeedXDRNG;
case Method_1 or Method_2 or Method_3 or Method_4:
case Method_1_Unown or Method_2_Unown or Method_3_Unown or Method_4_Unown:
case Method_1_Roamer:
return (pk, seed) => SetValuesFromSeedLCRNG(pk, t, seed);
case BACD:
case BACD_S:
case BACD_A:
case BACD_AX:
return (pk, seed) => SetValuesFromSeedBACD(pk, t, seed);
case PokeSpot:
return SetRandomPokeSpotPID;
case G5MGShiny:
return SetValuesFromSeedMG5Shiny;
case Pokewalker:
return (pk, seed) =>
{
var pid = pk.PID = PokewalkerRNG.GetPID(pk.TID16, pk.SID16, seed % 24, pk.Gender, pk.PersonalInfo.Gender);
pk.RefreshAbility((int)(pid & 1));
return seed;
};
// others: unimplemented
case CuteCharm:
break;
case ChainShiny:
return SetRandomChainShinyPID;
case G4MGAntiShiny:
break;
}
return (_, seed) => seed;
}
public static uint SetRandomChainShinyPID(PKM pk, uint seed)
{
pk.PID = ClassicEraRNG.GetChainShinyPID(ref seed, pk.ID32);
SetIVsFromSeedSequentialLCRNG(ref seed, pk);
return seed;
}
private static uint SetRandomPokeSpotPID(PKM pk, uint seed)
{
var D = XDRNG.Next(seed); // PID
var E = XDRNG.Next(D); // PID
pk.PID = (D & 0xFFFF0000) | (E >> 16);
return E;
}
public static uint GetMG5ShinyPID(uint gval, uint av, ushort TID16, ushort SID16)
{
uint pid = ((gval ^ TID16 ^ SID16) << 16) | gval;
if ((pid & 0x10000) != av << 16)
pid ^= 0x10000;
return pid;
}
public static uint SetValuesFromSeedMG5Shiny(PKM pk, uint seed)
{
var gv = seed >> 24;
var av = seed & 1; // arbitrary choice
pk.PID = GetMG5ShinyPID(gv, av, pk.TID16, pk.SID16);
pk.SetRandomIVs();
return seed; // meh
}
public static uint SetRandomWildPID4(PKM pk, Nature nature, int ability, byte gender, PIDType type)
{
pk.RefreshAbility(ability);
pk.Gender = gender;
var method = GetGeneratorMethod(type);
var rnd = Util.Rand;
while (true)
{
var seed = rnd.Rand32();
method(pk, seed);
if (!IsValidCriteria4(pk, nature, ability, gender))
continue;
return seed;
}
}
public static bool IsValidCriteria4(PKM pk, Nature nature, int ability, byte gender)
{
if (pk.GetSaneGender() != gender)
return false;
if (pk.Nature != nature)
return false;
if ((pk.EncryptionConstant & 1) != ability)
return false;
return true;
}
public static void SetRandomWildPID5(PKM pk, Nature nature, int ability, byte gender, PIDType specific = None)
{
var tidbit = (pk.TID16 ^ pk.SID16) & 1;
pk.RefreshAbility(ability);
pk.Gender = gender;
pk.Nature = nature;
if (ability == 2)
ability = 0;
var rnd = Util.Rand;
while (true)
{
uint seed = rnd.Rand32();
if (specific == G5MGShiny)
{
SetValuesFromSeedMG5Shiny(pk, seed);
seed = pk.PID;
}
else
{
var bitxor = (seed >> 31) ^ (seed & 1);
if (bitxor != tidbit)
seed ^= 1;
}
if (((seed >> 16) & 1) != ability)
continue;
pk.PID = seed;
if (pk.GetSaneGender() != gender)
continue;
return;
}
}
}

View File

@@ -339,18 +339,16 @@ private static void SetPINGA(PK4 pk4, PersonalInfo4 pi, EncounterCriteria criter
pk4.IV32 |= criteria.GetCombinedIVs();
return;
}
if (criteria.IsSpecifiedIVs())
{
criteria.SetRandomIVs(pk4);
return;
}
var seed = Util.Rand32(); // reseed, do not have method 1 correlation
bool filterIVs = criteria.IsSpecifiedIVs(2);
uint iv32;
while (true)
{
iv32 = ClassicEraRNG.GetSequentialIVs(ref seed);
if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32))
continue;
if (filterIVs && !criteria.IsSatisfiedIVs(iv32))
continue;
break;
}
pk4.IV32 |= iv32;