Misc tweaks

Add rejections for shiny criteria for gen8+ generators

Unrelated: use recent c# lang feature for settings property field
This commit is contained in:
Kurt 2026-03-19 03:27:40 -05:00
parent ca6fbf024c
commit 3ad376be44
10 changed files with 53 additions and 19 deletions

View File

@ -126,6 +126,12 @@ public EncounterCriteria()
/// <returns>><see langword="true"/> if an Ability is specified; otherwise, <see langword="false"/>.</returns> /// <returns>><see langword="true"/> if an Ability is specified; otherwise, <see langword="false"/>.</returns>
public bool IsSpecifiedAbility() => Ability != Any12H; public bool IsSpecifiedAbility() => Ability != Any12H;
/// <summary>
/// Determines whether the shiny value is explicitly specified rather than set to random.
/// </summary>
/// <returns>><see langword="true"/> if a Shiny is specified; otherwise, <see langword="false"/>.</returns>
public bool IsSpecifiedShiny() => Shiny != Shiny.Random;
/// <summary> /// <summary>
/// Determines whether all IVs are specified in the criteria. /// Determines whether all IVs are specified in the criteria.
/// </summary> /// </summary>
@ -183,6 +189,20 @@ public int GetCountSpecifiedIVs() => Convert.ToInt32(IV_HP != RandomIV)
_ => throw new ArgumentOutOfRangeException(nameof(ability), ability, null), _ => throw new ArgumentOutOfRangeException(nameof(ability), ability, null),
}; };
/// <summary>
/// Determines whether the specified shiny properties satisfy the shiny criteria based on the current <see cref="Shiny"/> setting.
/// </summary>
/// <returns>><see langword="true"/> if the index satisfies the shiny criteria; otherwise, <see langword="false"/>.</returns>
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
};
/// <summary> /// <summary>
/// Determines whether the specified Nature satisfies the criteria. /// Determines whether the specified Nature satisfies the criteria.
/// </summary> /// </summary>

View File

@ -57,6 +57,10 @@ protected override void SetPINGA(PK8 pk, in EncounterCriteria criteria, Personal
{ {
Span<int> iv = stackalloc int[6]; Span<int> 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; int ctr = 0;
var rand = new Xoroshiro128Plus(Util.Rand.Rand64()); var rand = new Xoroshiro128Plus(Util.Rand.Rand64());
var param = GetParam(pi); var param = GetParam(pi);
@ -64,21 +68,22 @@ protected override void SetPINGA(PK8 pk, in EncounterCriteria criteria, Personal
const int max = 100_000; const int max = 100_000;
do do
{ {
if (TryApply(pk, seed = rand.Next(), iv, param, criteria)) if (TryApply(pk, seed = rand.Next(), iv, param, iterCriteria))
break; break;
} while (++ctr < max); } while (++ctr < max);
if (ctr == max) // fail 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 }; iterCriteria = EncounterCriteria.Unrestricted with { Shiny = ShinyMethod };
while (!TryApply(pk, seed = rand.Next(), iv, param, tmp)) { } while (!TryApply(pk, seed = rand.Next(), iv, param, iterCriteria)) { }
} }
} }
FinishCorrelation(pk, seed); FinishCorrelation(pk, seed);
if (criteria.Shiny.IsShiny()) if (isShinyRequested)
pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, ShinyXor); pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, ShinyXor);
} }

View File

@ -36,10 +36,10 @@ public static bool GenerateData(PA9 pk, in GenerateParam9a enc, in EncounterCrit
{ {
var rand = new Xoroshiro128Plus(seed); var rand = new Xoroshiro128Plus(seed);
pk.EncryptionConstant = (uint)rand.NextInt(uint.MaxValue); pk.EncryptionConstant = (uint)rand.NextInt(uint.MaxValue);
pk.PID = GetAdaptedPID(ref rand, pk, enc); var pid = GetAdaptedPID(ref rand, pk, enc);
if (enc.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(ShinyUtil.GetShinyXor(pid, pk.ID32), 16))
if (enc.Shiny is Shiny.Random && criteria.Shiny.IsShiny() != pk.IsShiny)
return false; return false;
pk.PID = pid;
Span<int> ivs = [UNSET, UNSET, UNSET, UNSET, UNSET, UNSET]; Span<int> ivs = [UNSET, UNSET, UNSET, UNSET, UNSET, UNSET];
if (enc.IVs.IsSpecified) if (enc.IVs.IsSpecified)

View File

@ -55,7 +55,8 @@ private static bool TryApplyFromSeed(PK8 pk, in EncounterCriteria criteria, Shin
if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar) if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false; return false;
} }
if (shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid; pk.PID = pid;
// IVs // IVs

View File

@ -160,6 +160,8 @@ public static bool TryApply(PK8 pk, ulong seed, Span<int> ivs, in GenerateParam8
var trID = (uint)rng.NextInt(); var trID = (uint)rng.NextInt();
var pid = (uint)rng.NextInt(); var pid = (uint)rng.NextInt();
// Battle
var xor = GetShinyXor(pid, trID); var xor = GetShinyXor(pid, trID);
bool isShiny = xor < 16; bool isShiny = xor < 16;
if (isShiny && param.Shiny == Shiny.Never) if (isShiny && param.Shiny == Shiny.Never)
@ -167,9 +169,8 @@ public static bool TryApply(PK8 pk, ulong seed, Span<int> ivs, in GenerateParam8
ForceShinyState(false, ref pid, trID, 0); ForceShinyState(false, ref pid, trID, 0);
isShiny = false; isShiny = false;
} }
if (param.Shiny is Shiny.Random && isShiny != criteria.Shiny.IsShiny())
return false;
// Captured
if (isShiny) if (isShiny)
{ {
if (!GetIsShiny6(pk.ID32, pid)) if (!GetIsShiny6(pk.ID32, pid))
@ -181,6 +182,8 @@ public static bool TryApply(PK8 pk, ulong seed, Span<int> ivs, in GenerateParam8
pid ^= 0x1000_0000; pid ^= 0x1000_0000;
} }
if (param.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid; pk.PID = pid;
const int UNSET = -1; const int UNSET = -1;

View File

@ -129,6 +129,8 @@ public static bool TryApplyFromSeed(PA8 pk, in EncounterCriteria criteria, in Ov
if (para.Shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar) if (para.Shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false; return false;
} }
if (para.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid; pk.PID = pid;
Span<int> ivs = [UNSET, UNSET, UNSET, UNSET, UNSET, UNSET]; Span<int> ivs = [UNSET, UNSET, UNSET, UNSET, UNSET, UNSET];

View File

@ -65,6 +65,8 @@ public static bool TryApplyFromSeed(PB8 pk, in EncounterCriteria criteria, Shiny
if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar) if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false; return false;
} }
if (shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid; pk.PID = pid;
// Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless. // Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless.

View File

@ -65,6 +65,8 @@ public static bool TryApplyFromSeed(PB8 pk, in EncounterCriteria criteria, Shiny
if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar) if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false; return false;
} }
if (shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid; pk.PID = pid;
// Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless. // Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless.

View File

@ -62,10 +62,10 @@ public static bool GenerateData(PK9 pk, in GenerateParam9 enc, in EncounterCrite
{ {
var rand = new Xoroshiro128Plus(seed); var rand = new Xoroshiro128Plus(seed);
pk.EncryptionConstant = (uint)rand.NextInt(uint.MaxValue); pk.EncryptionConstant = (uint)rand.NextInt(uint.MaxValue);
pk.PID = GetAdaptedPID(ref rand, pk, enc); var pid = GetAdaptedPID(ref rand, pk, enc);
if (enc.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
if (enc.Shiny is Shiny.Random && criteria.Shiny.IsShiny() != pk.IsShiny)
return false; return false;
pk.PID = pid;
const int UNSET = -1; const int UNSET = -1;
const int MAX = 31; const int MAX = 31;

View File

@ -39,15 +39,14 @@ public sealed class StartupSettings : IStartupSettings
public List<string> RecentlyLoaded { get; set; } = new(DefaultMaxRecent); public List<string> RecentlyLoaded { get; set; } = new(DefaultMaxRecent);
private const int DefaultMaxRecent = 10; private const int DefaultMaxRecent = 10;
private uint MaxRecentCount = DefaultMaxRecent;
[LocalizedDescription("Amount of recently loaded save files to remember.")] [LocalizedDescription("Amount of recently loaded save files to remember.")]
public uint RecentlyLoadedMaxCount public uint RecentlyLoadedMaxCount
{ {
get => MaxRecentCount; get;
// Sanity check to not let the user foot-gun themselves a slow recall time. // 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. // Don't let invalid values slip into the startup version.
@ -89,7 +88,7 @@ public void LoadSaveFile(string path)
{ {
var recent = RecentlyLoaded; var recent = RecentlyLoaded;
// Remove from list if already present. // 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.RemoveAt(recent.Count - 1);
recent.Insert(0, path); recent.Insert(0, path);
} }