mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-27 22:40:22 +00:00
88830e0d00
Updates from net46->net7, dropping support for mono in favor of using the latest runtime (along with the performance/API improvements). Releases will be posted as 64bit only for now. Refactors a good amount of internal API methods to be more performant and more customizable for future updates & fixes. Adds functionality for Batch Editor commands to `>`, `<` and <=/>= TID/SID properties renamed to TID16/SID16 for clarity; other properties exposed for Gen7 / display variants. Main window has a new layout to account for DPI scaling (8 point grid) Fixed: Tatsugiri and Paldean Tauros now output Showdown form names as Showdown expects Changed: Gen9 species now interact based on the confirmed National Dex IDs (closes #3724) Fixed: Pokedex set all no longer clears species with unavailable non-base forms (closes #3720) Changed: Hyper Training suggestions now apply for level 50 in SV. (closes #3714) Fixed: B2/W2 hatched egg met locations exclusive to specific versions are now explicitly checked (closes #3691) Added: Properties for ribbon/mark count (closes #3659) Fixed: Traded SV eggs are now checked correctly (closes #3692)
175 lines
5.3 KiB
C#
175 lines
5.3 KiB
C#
using System;
|
|
|
|
namespace PKHeX.Core;
|
|
|
|
/// <summary>
|
|
/// Contains logic for the Generation 8b (BD/SP) wild and stationary spawns.
|
|
/// </summary>
|
|
public static class Wild8bRNG
|
|
{
|
|
private const int UNSET = -1;
|
|
|
|
public static void ApplyDetails(PKM pk, EncounterCriteria criteria,
|
|
Shiny shiny = Shiny.FixedValue,
|
|
int flawless = -1,
|
|
AbilityPermission ability = AbilityPermission.Any12,
|
|
int maxAttempts = 70_000)
|
|
{
|
|
if (shiny == Shiny.FixedValue)
|
|
shiny = criteria.Shiny is Shiny.Random or Shiny.Never ? Shiny.Never : criteria.Shiny;
|
|
if (flawless == -1)
|
|
flawless = 0;
|
|
|
|
int ctr = 0;
|
|
var rnd = Util.Rand;
|
|
do
|
|
{
|
|
ulong s0 = rnd.Rand64();
|
|
ulong s1 = rnd.Rand64();
|
|
var xors = new XorShift128(s0, s1);
|
|
if (TryApplyFromSeed(pk, criteria, shiny, flawless, xors, ability))
|
|
return;
|
|
} while (++ctr != maxAttempts);
|
|
|
|
{
|
|
ulong s0 = rnd.Rand64();
|
|
ulong s1 = rnd.Rand64();
|
|
var xors = new XorShift128(s0, s1);
|
|
TryApplyFromSeed(pk, EncounterCriteria.Unrestricted, shiny, flawless, xors, ability);
|
|
}
|
|
}
|
|
|
|
public static bool TryApplyFromSeed(PKM pk, EncounterCriteria criteria, Shiny shiny, int flawless, XorShift128 xors, AbilityPermission ability)
|
|
{
|
|
// Encryption Constant
|
|
pk.EncryptionConstant = xors.NextUInt();
|
|
|
|
// PID
|
|
var fakeTID = xors.NextUInt(); // fakeTID
|
|
var pid = xors.NextUInt();
|
|
pid = GetRevisedPID(fakeTID, pid, pk);
|
|
var xor = GetShinyXor(pk.ID32, pid);
|
|
var type = GetRareType(xor);
|
|
if (shiny == Shiny.Never)
|
|
{
|
|
if (type != Shiny.Never)
|
|
return false;
|
|
}
|
|
else if (shiny != Shiny.Random)
|
|
{
|
|
if (type == Shiny.Never)
|
|
return false;
|
|
|
|
if (shiny == Shiny.AlwaysSquare && type != Shiny.AlwaysSquare)
|
|
return false;
|
|
if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
|
|
return false;
|
|
}
|
|
pk.PID = pid;
|
|
|
|
// Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless.
|
|
Span<int> ivs = stackalloc[] { UNSET, UNSET, UNSET, UNSET, UNSET, UNSET };
|
|
const int MAX = 31;
|
|
var determined = 0;
|
|
while (determined < flawless)
|
|
{
|
|
var idx = (int)xors.NextUInt(6);
|
|
if (ivs[idx] != UNSET)
|
|
continue;
|
|
ivs[idx] = 31;
|
|
determined++;
|
|
}
|
|
|
|
for (var i = 0; i < ivs.Length; i++)
|
|
{
|
|
if (ivs[i] == UNSET)
|
|
ivs[i] = xors.NextInt(0, MAX + 1);
|
|
}
|
|
|
|
if (!criteria.IsIVsCompatible(ivs, 8))
|
|
return false;
|
|
|
|
pk.IV_HP = ivs[0];
|
|
pk.IV_ATK = ivs[1];
|
|
pk.IV_DEF = ivs[2];
|
|
pk.IV_SPA = ivs[3];
|
|
pk.IV_SPD = ivs[4];
|
|
pk.IV_SPE = ivs[5];
|
|
|
|
// Ability
|
|
var n = ability switch
|
|
{
|
|
AbilityPermission.Any12 => (int)xors.NextUInt(2),
|
|
AbilityPermission.Any12H => (int)xors.NextUInt(3),
|
|
_ => (int)ability >> 1,
|
|
};
|
|
pk.SetAbilityIndex(n);
|
|
|
|
// Gender (skip this if gender is fixed)
|
|
var genderRatio = PersonalTable.BDSP.GetFormEntry(pk.Species, pk.Form).Gender;
|
|
if (genderRatio == PersonalInfo.RatioMagicGenderless)
|
|
{
|
|
pk.Gender = 2;
|
|
}
|
|
else if (genderRatio == PersonalInfo.RatioMagicMale)
|
|
{
|
|
pk.Gender = 0;
|
|
}
|
|
else if (genderRatio == PersonalInfo.RatioMagicFemale)
|
|
{
|
|
pk.Gender = 1;
|
|
}
|
|
else
|
|
{
|
|
var next = (((int)xors.NextUInt(253) + 1 < genderRatio) ? 1 : 0);
|
|
if (criteria.Gender is 0 or 1 && next != criteria.Gender)
|
|
return false;
|
|
pk.Gender = next;
|
|
}
|
|
|
|
if (criteria.Nature is Nature.Random)
|
|
pk.Nature = (int)xors.NextUInt(25);
|
|
else // Skip nature, assuming Synchronize
|
|
pk.Nature = (int)criteria.Nature;
|
|
pk.StatNature = pk.Nature;
|
|
|
|
// Remainder
|
|
var scale = (IScaledSize)pk;
|
|
scale.HeightScalar = (byte)((int)xors.NextUInt(0x81) + (int)xors.NextUInt(0x80));
|
|
scale.WeightScalar = (byte)((int)xors.NextUInt(0x81) + (int)xors.NextUInt(0x80));
|
|
|
|
// Item, don't care
|
|
return true;
|
|
}
|
|
|
|
private static uint GetRevisedPID(uint fakeTID, uint pid, ITrainerID32 tr)
|
|
{
|
|
var xor = GetShinyXor(pid, fakeTID);
|
|
var newXor = GetShinyXor(pid, tr.ID32);
|
|
|
|
var fakeRare = GetRareType(xor);
|
|
var newRare = GetRareType(newXor);
|
|
|
|
if (fakeRare == newRare)
|
|
return pid;
|
|
|
|
var isShiny = xor < 16;
|
|
if (isShiny)
|
|
return pid ^ 0x1000_0000;
|
|
var low = pid & 0xFFFF;
|
|
return (((xor == 0 ? 0u : 1u) ^ tr.TID16 ^ tr.SID16 ^ low) << 16) | low; // force same shiny star type
|
|
}
|
|
|
|
private static Shiny GetRareType(uint xor) => xor switch
|
|
{
|
|
0 => Shiny.AlwaysSquare,
|
|
< 16 => Shiny.AlwaysStar,
|
|
_ => Shiny.Never,
|
|
};
|
|
|
|
private static uint GetShinyXor(uint pid, uint id32)
|
|
{
|
|
var xor = pid ^ id32;
|
|
return (xor ^ (xor >> 16)) & 0xFFFF;
|
|
}
|
|
}
|