diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterCriteria.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterCriteria.cs
index 57387f800..6ca6b04a3 100644
--- a/PKHeX.Core/Legality/Encounters/Generator/EncounterCriteria.cs
+++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterCriteria.cs
@@ -126,6 +126,12 @@ public EncounterCriteria()
/// > if an Ability is specified; otherwise, .
public bool IsSpecifiedAbility() => Ability != Any12H;
+ ///
+ /// Determines whether the shiny value is explicitly specified rather than set to random.
+ ///
+ /// > if a Shiny is specified; otherwise, .
+ public bool IsSpecifiedShiny() => Shiny != Shiny.Random;
+
///
/// Determines whether all IVs are specified in the criteria.
///
@@ -183,6 +189,20 @@ public int GetCountSpecifiedIVs() => Convert.ToInt32(IV_HP != RandomIV)
_ => throw new ArgumentOutOfRangeException(nameof(ability), ability, null),
};
+ ///
+ /// Determines whether the specified shiny properties satisfy the shiny criteria based on the current setting.
+ ///
+ /// > if the index satisfies the shiny criteria; otherwise, .
+ public bool IsSatisfiedShiny(uint xor, uint cmp) => Shiny switch
+ {
+ Shiny.Random => true,
+ Shiny.Never => xor > cmp, // not shiny
+ Shiny.AlwaysSquare => xor == 0, // square shiny
+ Shiny.AlwaysStar => xor < cmp && xor != 0, // star shiny
+ Shiny.Always => xor < cmp, // shiny
+ _ => false, // shouldn't be set
+ };
+
///
/// Determines whether the specified Nature satisfies the criteria.
///
diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterStatic8U.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterStatic8U.cs
index 753546d8b..fd217886f 100644
--- a/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterStatic8U.cs
+++ b/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterStatic8U.cs
@@ -57,6 +57,10 @@ protected override void SetPINGA(PK8 pk, in EncounterCriteria criteria, Personal
{
Span iv = stackalloc int[6];
+ // Honor a shiny request only at the end; generate as never-shiny to avoid shiny PID rejection in main RNG method.
+ var isShinyRequested = criteria.Shiny.IsShiny();
+ var iterCriteria = criteria with { Shiny = ShinyMethod };
+
int ctr = 0;
var rand = new Xoroshiro128Plus(Util.Rand.Rand64());
var param = GetParam(pi);
@@ -64,21 +68,22 @@ protected override void SetPINGA(PK8 pk, in EncounterCriteria criteria, Personal
const int max = 100_000;
do
{
- if (TryApply(pk, seed = rand.Next(), iv, param, criteria))
+ if (TryApply(pk, seed = rand.Next(), iv, param, iterCriteria))
break;
} while (++ctr < max);
if (ctr == max) // fail
{
- if (!TryApply(pk, seed = rand.Next(), iv, param, criteria.WithoutIVs()))
+ iterCriteria = iterCriteria.WithoutIVs();
+ if (!TryApply(pk, seed = rand.Next(), iv, param, iterCriteria))
{
- var tmp = EncounterCriteria.Unrestricted with { Shiny = ShinyMethod };
- while (!TryApply(pk, seed = rand.Next(), iv, param, tmp)) { }
+ iterCriteria = EncounterCriteria.Unrestricted with { Shiny = ShinyMethod };
+ while (!TryApply(pk, seed = rand.Next(), iv, param, iterCriteria)) { }
}
}
FinishCorrelation(pk, seed);
- if (criteria.Shiny.IsShiny())
+ if (isShinyRequested)
pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, ShinyXor);
}
diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen9a/LumioseRNG.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen9a/LumioseRNG.cs
index 5a5055930..90a3737cf 100644
--- a/PKHeX.Core/Legality/Encounters/Templates/Gen9a/LumioseRNG.cs
+++ b/PKHeX.Core/Legality/Encounters/Templates/Gen9a/LumioseRNG.cs
@@ -36,10 +36,10 @@ public static bool GenerateData(PA9 pk, in GenerateParam9a enc, in EncounterCrit
{
var rand = new Xoroshiro128Plus(seed);
pk.EncryptionConstant = (uint)rand.NextInt(uint.MaxValue);
- pk.PID = GetAdaptedPID(ref rand, pk, enc);
-
- if (enc.Shiny is Shiny.Random && criteria.Shiny.IsShiny() != pk.IsShiny)
+ var pid = GetAdaptedPID(ref rand, pk, enc);
+ if (enc.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(ShinyUtil.GetShinyXor(pid, pk.ID32), 16))
return false;
+ pk.PID = pid;
Span ivs = [UNSET, UNSET, UNSET, UNSET, UNSET, UNSET];
if (enc.IVs.IsSpecified)
diff --git a/PKHeX.Core/Legality/RNG/Methods/Gen8/Overworld8RNG.cs b/PKHeX.Core/Legality/RNG/Methods/Gen8/Overworld8RNG.cs
index 6ffc86788..231b5567d 100644
--- a/PKHeX.Core/Legality/RNG/Methods/Gen8/Overworld8RNG.cs
+++ b/PKHeX.Core/Legality/RNG/Methods/Gen8/Overworld8RNG.cs
@@ -55,7 +55,8 @@ private static bool TryApplyFromSeed(PK8 pk, in EncounterCriteria criteria, Shin
if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false;
}
-
+ if (shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
+ return false;
pk.PID = pid;
// IVs
diff --git a/PKHeX.Core/Legality/RNG/Methods/Gen8/RaidRNG.cs b/PKHeX.Core/Legality/RNG/Methods/Gen8/RaidRNG.cs
index 083f72ee0..abdaab812 100644
--- a/PKHeX.Core/Legality/RNG/Methods/Gen8/RaidRNG.cs
+++ b/PKHeX.Core/Legality/RNG/Methods/Gen8/RaidRNG.cs
@@ -160,6 +160,8 @@ public static bool TryApply(PK8 pk, ulong seed, Span ivs, in GenerateParam8
var trID = (uint)rng.NextInt();
var pid = (uint)rng.NextInt();
+
+ // Battle
var xor = GetShinyXor(pid, trID);
bool isShiny = xor < 16;
if (isShiny && param.Shiny == Shiny.Never)
@@ -167,9 +169,8 @@ public static bool TryApply(PK8 pk, ulong seed, Span ivs, in GenerateParam8
ForceShinyState(false, ref pid, trID, 0);
isShiny = false;
}
- if (param.Shiny is Shiny.Random && isShiny != criteria.Shiny.IsShiny())
- return false;
+ // Captured
if (isShiny)
{
if (!GetIsShiny6(pk.ID32, pid))
@@ -181,6 +182,8 @@ public static bool TryApply(PK8 pk, ulong seed, Span ivs, in GenerateParam8
pid ^= 0x1000_0000;
}
+ if (param.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
+ return false;
pk.PID = pid;
const int UNSET = -1;
diff --git a/PKHeX.Core/Legality/RNG/Methods/Gen8a/Overworld8aRNG.cs b/PKHeX.Core/Legality/RNG/Methods/Gen8a/Overworld8aRNG.cs
index 25451a2e4..e11a7bc6c 100644
--- a/PKHeX.Core/Legality/RNG/Methods/Gen8a/Overworld8aRNG.cs
+++ b/PKHeX.Core/Legality/RNG/Methods/Gen8a/Overworld8aRNG.cs
@@ -129,6 +129,8 @@ public static bool TryApplyFromSeed(PA8 pk, in EncounterCriteria criteria, in Ov
if (para.Shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false;
}
+ if (para.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
+ return false;
pk.PID = pid;
Span ivs = [UNSET, UNSET, UNSET, UNSET, UNSET, UNSET];
diff --git a/PKHeX.Core/Legality/RNG/Methods/Gen8b/Roaming8bRNG.cs b/PKHeX.Core/Legality/RNG/Methods/Gen8b/Roaming8bRNG.cs
index c9407df88..94f9634e1 100644
--- a/PKHeX.Core/Legality/RNG/Methods/Gen8b/Roaming8bRNG.cs
+++ b/PKHeX.Core/Legality/RNG/Methods/Gen8b/Roaming8bRNG.cs
@@ -65,6 +65,8 @@ public static bool TryApplyFromSeed(PB8 pk, in EncounterCriteria criteria, Shiny
if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false;
}
+ if (shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
+ return false;
pk.PID = pid;
// Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless.
diff --git a/PKHeX.Core/Legality/RNG/Methods/Gen8b/Wild8bRNG.cs b/PKHeX.Core/Legality/RNG/Methods/Gen8b/Wild8bRNG.cs
index f98eb2f3b..fb669ab8a 100644
--- a/PKHeX.Core/Legality/RNG/Methods/Gen8b/Wild8bRNG.cs
+++ b/PKHeX.Core/Legality/RNG/Methods/Gen8b/Wild8bRNG.cs
@@ -65,6 +65,8 @@ public static bool TryApplyFromSeed(PB8 pk, in EncounterCriteria criteria, Shiny
if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false;
}
+ if (shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
+ return false;
pk.PID = pid;
// Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless.
diff --git a/PKHeX.Core/Legality/RNG/Methods/Gen9/Encounter9RNG.cs b/PKHeX.Core/Legality/RNG/Methods/Gen9/Encounter9RNG.cs
index 4bf85080a..7d9866428 100644
--- a/PKHeX.Core/Legality/RNG/Methods/Gen9/Encounter9RNG.cs
+++ b/PKHeX.Core/Legality/RNG/Methods/Gen9/Encounter9RNG.cs
@@ -62,10 +62,10 @@ public static bool GenerateData(PK9 pk, in GenerateParam9 enc, in EncounterCrite
{
var rand = new Xoroshiro128Plus(seed);
pk.EncryptionConstant = (uint)rand.NextInt(uint.MaxValue);
- pk.PID = GetAdaptedPID(ref rand, pk, enc);
-
- if (enc.Shiny is Shiny.Random && criteria.Shiny.IsShiny() != pk.IsShiny)
+ var pid = GetAdaptedPID(ref rand, pk, enc);
+ if (enc.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
+ pk.PID = pid;
const int UNSET = -1;
const int MAX = 31;
diff --git a/PKHeX.WinForms/Settings/StartupSettings.cs b/PKHeX.WinForms/Settings/StartupSettings.cs
index 8f59a4311..0d98eb505 100644
--- a/PKHeX.WinForms/Settings/StartupSettings.cs
+++ b/PKHeX.WinForms/Settings/StartupSettings.cs
@@ -39,15 +39,14 @@ public sealed class StartupSettings : IStartupSettings
public List RecentlyLoaded { get; set; } = new(DefaultMaxRecent);
private const int DefaultMaxRecent = 10;
- private uint MaxRecentCount = DefaultMaxRecent;
[LocalizedDescription("Amount of recently loaded save files to remember.")]
public uint RecentlyLoadedMaxCount
{
- get => MaxRecentCount;
+ get;
// Sanity check to not let the user foot-gun themselves a slow recall time.
- set => MaxRecentCount = Math.Clamp(value, 1, 1000);
- }
+ set => field = Math.Clamp(value, 1, 1000);
+ } = DefaultMaxRecent;
// Don't let invalid values slip into the startup version.
@@ -89,7 +88,7 @@ public void LoadSaveFile(string path)
{
var recent = RecentlyLoaded;
// Remove from list if already present.
- if (!recent.Remove(path) && recent.Count >= MaxRecentCount)
+ if (!recent.Remove(path) && recent.Count >= RecentlyLoadedMaxCount)
recent.RemoveAt(recent.Count - 1);
recent.Insert(0, path);
}