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>
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>
/// Determines whether all IVs are specified in the criteria.
/// </summary>
@ -183,6 +189,20 @@ public int GetCountSpecifiedIVs() => Convert.ToInt32(IV_HP != RandomIV)
_ => 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>
/// Determines whether the specified Nature satisfies the criteria.
/// </summary>

View File

@ -57,6 +57,10 @@ protected override void SetPINGA(PK8 pk, in EncounterCriteria criteria, Personal
{
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;
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);
}

View File

@ -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<int> ivs = [UNSET, UNSET, UNSET, UNSET, UNSET, UNSET];
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)
return false;
}
if (shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid;
// 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 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<int> 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<int> 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;

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)
return false;
}
if (para.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid;
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)
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.

View File

@ -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.

View File

@ -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;

View File

@ -39,15 +39,14 @@ public sealed class StartupSettings : IStartupSettings
public List<string> 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);
}