mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
Revise Overworld8a application of PIDIV
Instantiating from template now follows group seed -> spawn 1 correlation, including alpha move. Differentiates static encounters that don't follow the ow8a correlation, scrambles EC to disassociate. Adds rand64 to get initial seeds Set correct level range to match slotSeed; not respecting the slot roll being valid, but whatever.
This commit is contained in:
parent
ad249dbb76
commit
ff2e890e69
10 changed files with 245 additions and 118 deletions
|
@ -1,6 +1,7 @@
|
|||
using static PKHeX.Core.EncounterUtil;
|
||||
using static PKHeX.Core.Shiny;
|
||||
using static PKHeX.Core.GameVersion;
|
||||
using static PKHeX.Core.EncounterStatic8aCorrelation;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
@ -15,13 +16,13 @@ internal static class Encounters8a
|
|||
internal static readonly EncounterStatic8a[] StaticLA =
|
||||
{
|
||||
// Gifts
|
||||
new(722,000,05,M,M) { Location = 006, Gift = true, Ball = (int)Ball.LAPoke }, // Rowlet
|
||||
new(155,000,05,M,M) { Location = 006, Gift = true, Ball = (int)Ball.LAPoke }, // Cyndaquil
|
||||
new(501,000,05,M,M) { Location = 006, Gift = true, Ball = (int)Ball.LAPoke }, // Oshawott
|
||||
new(037,001,40,M,M) { Location = 088, Gift = true, Ball = (int)Ball.LAPoke }, // Vulpix-1
|
||||
new(483,000,65,M,M) { Location = 109, FlawlessIVCount = 3, Gift = true, Ball = (int)Ball.LAOrigin }, // Dialga
|
||||
new(484,000,65,M,M) { Location = 109, FlawlessIVCount = 3, Gift = true, Ball = (int)Ball.LAOrigin }, // Palkia
|
||||
new(493,000,75,M,M) { Location = 109, FlawlessIVCount = 3, Gift = true, Ball = (int)Ball.LAPoke, Fateful = true }, // Arceus
|
||||
new(722,000,05,M,M) { Location = 006, Gift = true, Method = Fixed, Ball = (int)Ball.LAPoke }, // Rowlet
|
||||
new(155,000,05,M,M) { Location = 006, Gift = true, Method = Fixed, Ball = (int)Ball.LAPoke }, // Cyndaquil
|
||||
new(501,000,05,M,M) { Location = 006, Gift = true, Method = Fixed, Ball = (int)Ball.LAPoke }, // Oshawott
|
||||
new(037,001,40,M,M) { Location = 088, Gift = true, Method = Fixed, Ball = (int)Ball.LAPoke }, // Vulpix-1
|
||||
new(483,000,65,M,M) { Location = 109, FlawlessIVCount = 3, Gift = true, Method = Fixed, Ball = (int)Ball.LAOrigin }, // Dialga
|
||||
new(484,000,65,M,M) { Location = 109, FlawlessIVCount = 3, Gift = true, Method = Fixed, Ball = (int)Ball.LAOrigin }, // Palkia
|
||||
new(493,000,75,M,M) { Location = 109, FlawlessIVCount = 3, Gift = true, Method = Fixed, Ball = (int)Ball.LAPoke, Fateful = true }, // Arceus
|
||||
|
||||
// Static Encounters - Scripted Table Slots
|
||||
new(480,000,70,M,M) { Location = 111, FlawlessIVCount = 3 }, // Uxie
|
||||
|
@ -50,61 +51,66 @@ internal static class Encounters8a
|
|||
new(492,000,70,M,M) { Location = 026, FlawlessIVCount = 3, Fateful = true }, // Shaymin
|
||||
|
||||
// Unown Notes
|
||||
new(201,000,25,U) { Location = 040 }, // Unown A
|
||||
new(201,001,25,U) { Location = 056 }, // Unown B
|
||||
new(201,002,25,U) { Location = 081 }, // Unown C
|
||||
new(201,003,25,U) { Location = 008 }, // Unown D
|
||||
new(201,004,25,U) { Location = 022 }, // Unown E
|
||||
new(201,005,25,U) { Location = 010 }, // Unown F
|
||||
new(201,006,25,U) { Location = 017 }, // Unown G
|
||||
new(201,007,25,U) { Location = 006 }, // Unown H
|
||||
new(201,008,25,U) { Location = 023 }, // Unown I
|
||||
new(201,009,25,U) { Location = 072 }, // Unown J
|
||||
new(201,010,25,U) { Location = 043 }, // Unown K
|
||||
new(201,011,25,U) { Location = 086 }, // Unown L
|
||||
new(201,012,25,U) { Location = 037 }, // Unown M
|
||||
new(201,013,25,U) { Location = 009 }, // Unown N
|
||||
new(201,014,25,U) { Location = 102 }, // Unown O
|
||||
new(201,015,25,U) { Location = 075 }, // Unown P
|
||||
new(201,016,25,U) { Location = 058 }, // Unown Q
|
||||
new(201,017,25,U) { Location = 059 }, // Unown R
|
||||
new(201,018,25,U) { Location = 025 }, // Unown S
|
||||
new(201,019,25,U) { Location = 092 }, // Unown T
|
||||
new(201,020,25,U) { Location = 011 }, // Unown U
|
||||
new(201,021,25,U) { Location = 038 }, // Unown V
|
||||
new(201,022,25,U) { Location = 006 }, // Unown W
|
||||
new(201,023,25,U) { Location = 021 }, // Unown X
|
||||
new(201,024,25,U) { Location = 097 }, // Unown Y
|
||||
new(201,025,25,U) { Location = 051 }, // Unown Z
|
||||
new(201,026,25,U) { Location = 142 }, // Unown ! at Snowfall Hot Spring
|
||||
new(201,027,25,U) { Location = 006 }, // Unown ?
|
||||
new(201,000,25,U) { Location = 040, Method = Fixed }, // Unown A
|
||||
new(201,001,25,U) { Location = 056, Method = Fixed }, // Unown B
|
||||
new(201,002,25,U) { Location = 081, Method = Fixed }, // Unown C
|
||||
new(201,003,25,U) { Location = 008, Method = Fixed }, // Unown D
|
||||
new(201,004,25,U) { Location = 022, Method = Fixed }, // Unown E
|
||||
new(201,005,25,U) { Location = 010, Method = Fixed }, // Unown F
|
||||
new(201,006,25,U) { Location = 017, Method = Fixed }, // Unown G
|
||||
new(201,007,25,U) { Location = 006, Method = Fixed }, // Unown H
|
||||
new(201,008,25,U) { Location = 023, Method = Fixed }, // Unown I
|
||||
new(201,009,25,U) { Location = 072, Method = Fixed }, // Unown J
|
||||
new(201,010,25,U) { Location = 043, Method = Fixed }, // Unown K
|
||||
new(201,011,25,U) { Location = 086, Method = Fixed }, // Unown L
|
||||
new(201,012,25,U) { Location = 037, Method = Fixed }, // Unown M
|
||||
new(201,013,25,U) { Location = 009, Method = Fixed }, // Unown N
|
||||
new(201,014,25,U) { Location = 102, Method = Fixed }, // Unown O
|
||||
new(201,015,25,U) { Location = 075, Method = Fixed }, // Unown P
|
||||
new(201,016,25,U) { Location = 058, Method = Fixed }, // Unown Q
|
||||
new(201,017,25,U) { Location = 059, Method = Fixed }, // Unown R
|
||||
new(201,018,25,U) { Location = 025, Method = Fixed }, // Unown S
|
||||
new(201,019,25,U) { Location = 092, Method = Fixed }, // Unown T
|
||||
new(201,020,25,U) { Location = 011, Method = Fixed }, // Unown U
|
||||
new(201,021,25,U) { Location = 038, Method = Fixed }, // Unown V
|
||||
new(201,022,25,U) { Location = 006, Method = Fixed }, // Unown W
|
||||
new(201,023,25,U) { Location = 021, Method = Fixed }, // Unown X
|
||||
new(201,024,25,U) { Location = 097, Method = Fixed }, // Unown Y
|
||||
new(201,025,25,U) { Location = 051, Method = Fixed }, // Unown Z
|
||||
new(201,026,25,U) { Location = 142, Method = Fixed }, // Unown ! at Snowfall Hot Spring
|
||||
new(201,027,25,U) { Location = 006, Method = Fixed }, // Unown ?
|
||||
|
||||
// Future updates will handle crossovers better.
|
||||
new(201,017,25,U) { Location = 009 }, // Unown R (Cobalt Coastlands)
|
||||
new(201,026,25,U) { Location = 099 }, // Unown ! (Arena’s Approach)
|
||||
new(201,026,25,U) { Location = 141 }, // Unown ! (Icepeak Arena)
|
||||
new(201,023,25,U) { Location = 007 }, // Unown X
|
||||
new(201,024,25,U) { Location = 097 }, // Unown Y
|
||||
new(201,006,25,U) { Location = 007 }, // Unown G
|
||||
new(201,017,25,U) { Location = 009, Method = Fixed }, // Unown R (Cobalt Coastlands)
|
||||
new(201,026,25,U) { Location = 099, Method = Fixed }, // Unown ! (Arena’s Approach)
|
||||
new(201,026,25,U) { Location = 141, Method = Fixed }, // Unown ! (Icepeak Arena)
|
||||
new(201,023,25,U) { Location = 007, Method = Fixed }, // Unown X
|
||||
new(201,024,25,U) { Location = 097, Method = Fixed }, // Unown Y
|
||||
new(201,006,25,U) { Location = 007, Method = Fixed }, // Unown G
|
||||
|
||||
new(642,000,70,M,M) { Location = 059, FlawlessIVCount = 3 }, // Thundurus (Lunker’s Lair)
|
||||
new(642,000,70,M,M) { Location = 129, FlawlessIVCount = 3 }, // Thundurus (Sand’s Reach)
|
||||
new(488,000,70,M,M) { Location = 010, FlawlessIVCount = 3 }, // Cresselia (Coronet Highlands)
|
||||
new(491,000,70,M,M) { Location = 074, FlawlessIVCount = 3, Fateful = true }, // Darkrai (Lonely Spring)
|
||||
|
||||
// Static Encounters
|
||||
new(046,000,50,M,M) { Location = 019 }, // paras01: Paras
|
||||
new(390,000,12,M,M) { Location = 007 }, // hikozaru_01: Chimchar
|
||||
new(434,000,20,M,M) { Location = 008 }, // skunpuu01: Stunky
|
||||
new(441,000,34,M,M) { Location = 129 }, // perap01: Chatot
|
||||
new(450,000,34,M,M) { Location = 036, Gender = 0 }, // kabaldon01: Hippowdon
|
||||
new(459,000,50,M,M) { Location = 101, Gender = 1 }, // yukikaburi01: Snover
|
||||
new(046,000,50,M,M) { Location = 019, Method = Fixed }, // paras01: Paras
|
||||
new(390,000,12,M,M) { Location = 007, Method = Fixed }, // hikozaru_01: Chimchar
|
||||
new(434,000,20,M,M) { Location = 008, Method = Fixed }, // skunpuu01: Stunky
|
||||
new(441,000,34,M,M) { Location = 129, Method = Fixed }, // perap01: Chatot
|
||||
new(450,000,34,M,M) { Location = 036, Method = Fixed, Gender = 0 }, // kabaldon01: Hippowdon
|
||||
new(459,000,50,M,M) { Location = 101, Method = Fixed, Gender = 1 }, // yukikaburi01: Snover
|
||||
|
||||
new(483,000,65,M,M) { Location = 109, FlawlessIVCount = 3 }, // dialga01: Dialga
|
||||
new(484,000,65,M,M) { Location = 109, FlawlessIVCount = 3 }, // palkia01: Palkia
|
||||
new(486,000,70,M,M) { Location = 095, FlawlessIVCount = 3 }, // regigigas01: Regigigas
|
||||
new(487,001,70,M,M) { Location = 067, FlawlessIVCount = 3 }, // giratina02: Giratina-1
|
||||
new(483,000,65,M,M) { Location = 109, Method = Fixed, FlawlessIVCount = 3 }, // dialga01: Dialga
|
||||
new(484,000,65,M,M) { Location = 109, Method = Fixed, FlawlessIVCount = 3 }, // palkia01: Palkia
|
||||
new(486,000,70,M,M) { Location = 095, Method = Fixed, FlawlessIVCount = 3 }, // regigigas01: Regigigas
|
||||
new(487,001,70,M,M) { Location = 067, Method = Fixed, FlawlessIVCount = 3 }, // giratina02: Giratina-1
|
||||
|
||||
new(362,000,64,A,A) { Location = 011, IsAlpha = true, Moves = new[] {442,059,556,242}, Mastery = new[] {true,true,true, true } }, // onigohri01: Glalie
|
||||
new(402,000,12,A,A) { Location = 007, IsAlpha = true, Gender = 0, Moves = new[] {206,071,033,332}, Mastery = new[] {true,true,false,false} }, // mev002: Kricketune
|
||||
new(416,000,60,A,A) { Location = 022, IsAlpha = true, Gender = 1, FlawlessIVCount = 3, Moves = new[] {188,403,408,405}, Mastery = new[] {true,true,true ,true } }, // beequen01: Vespiquen
|
||||
new(571,001,58,M,M) { Location = 111, IsAlpha = true, Moves = new[] {555,421,841,417}, Mastery = new[] {true,true,true ,true } }, // zoroark01: Zoroark-1
|
||||
new(706,001,58,M,M) { Location = 104, IsAlpha = true, Moves = new[] {231,406,842,056}, Mastery = new[] {true,true,true ,true } }, // numelgon01: Goodra-1
|
||||
new(904,000,58,M,M) { Location = 105, IsAlpha = true, Moves = new[] {301,398,401,038}, Mastery = new[] {true,true,true ,false} }, // harysen01: Overqwil
|
||||
new(362,000,64,A,A) { Location = 011, Method = Fixed, IsAlpha = true, Moves = new[] {442,059,556,242}, Mastery = new[] {true,true,true, true } }, // onigohri01: Glalie
|
||||
new(402,000,12,A,A) { Location = 007, Method = Fixed, IsAlpha = true, Gender = 0, Moves = new[] {206,071,033,332}, Mastery = new[] {true,true,false,false} }, // mev002: Kricketune
|
||||
new(416,000,60,A,A) { Location = 022, Method = Fixed, IsAlpha = true, Gender = 1, FlawlessIVCount = 3, Moves = new[] {188,403,408,405}, Mastery = new[] {true,true,true ,true } }, // beequen01: Vespiquen
|
||||
new(571,001,58,M,M) { Location = 111, Method = Fixed, IsAlpha = true, Moves = new[] {555,421,841,417}, Mastery = new[] {true,true,true ,true } }, // zoroark01: Zoroark-1
|
||||
new(706,001,58,M,M) { Location = 104, Method = Fixed, IsAlpha = true, Moves = new[] {231,406,842,056}, Mastery = new[] {true,true,true ,true } }, // numelgon01: Goodra-1
|
||||
new(904,000,58,M,M) { Location = 105, Method = Fixed, IsAlpha = true, Moves = new[] {301,398,401,038}, Mastery = new[] {true,true,true ,false} }, // harysen01: Overqwil
|
||||
};
|
||||
}
|
||||
|
|
|
@ -23,25 +23,25 @@ public sealed record EncounterSlot8a : EncounterSlot, IAlpha
|
|||
Gender = gender;
|
||||
}
|
||||
|
||||
public bool HasAlphaMove => IsAlpha && Type is not SlotType.Landmark;
|
||||
|
||||
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
|
||||
{
|
||||
base.ApplyDetails(sav, criteria, pk);
|
||||
if (Gender != Gender.Random)
|
||||
pk.Gender = (int)Gender;
|
||||
|
||||
var para = GetParams(criteria);
|
||||
Overworld8aRNG.ApplyDetails(pk, criteria, para);
|
||||
var para = GetParams();
|
||||
var (_, slotSeed) = Overworld8aRNG.ApplyDetails(pk, criteria, para, HasAlphaMove);
|
||||
if (LevelMin != LevelMax)
|
||||
pk.CurrentLevel = pk.Met_Level = Overworld8aRNG.GetRandomLevel(slotSeed, LevelMin, LevelMax);
|
||||
|
||||
if (IsAlpha)
|
||||
{
|
||||
if (pk is IAlpha a)
|
||||
a.IsAlpha = true;
|
||||
if (Type is not SlotType.Landmark && pk is PA8 pa)
|
||||
{
|
||||
var extra = pa.AlphaMove = pa.GetRandomAlphaMove();
|
||||
pa.SetMasteryFlagMove(extra);
|
||||
pk.PushMove(extra);
|
||||
}
|
||||
if (pk is PA8 { AlphaMove: not 0 } pa)
|
||||
pk.PushMove(pa.AlphaMove);
|
||||
}
|
||||
|
||||
if (pk is PA8 pa8)
|
||||
|
@ -115,7 +115,7 @@ public sealed record EncounterSlot8a : EncounterSlot, IAlpha
|
|||
return EncounterMatchRating.Match;
|
||||
}
|
||||
|
||||
private OverworldParam8a GetParams(EncounterCriteria criteria)
|
||||
private OverworldParam8a GetParams()
|
||||
{
|
||||
var pt = PersonalTable.LA;
|
||||
var entry = pt.GetFormEntry(Species, Form);
|
||||
|
@ -125,8 +125,18 @@ public sealed record EncounterSlot8a : EncounterSlot, IAlpha
|
|||
IsAlpha = IsAlpha,
|
||||
FlawlessIVs = FlawlessIVCount,
|
||||
Shiny = Shiny,
|
||||
RollCount = criteria.Shiny.IsShiny() ? Type is SlotType.Swarm ? (byte)32 : (byte)7 : (byte)1,
|
||||
RollCount = GetRollCount(Type),
|
||||
GenderRatio = gender,
|
||||
};
|
||||
}
|
||||
|
||||
// hardcoded 7 to assume max dex progress + shiny charm.
|
||||
private const int MaxRollCount = 7;
|
||||
|
||||
private static byte GetRollCount(SlotType type) => (byte)(MaxRollCount + type switch
|
||||
{
|
||||
SlotType.OverworldMMO => 10,
|
||||
SlotType.OverworldMass => 25,
|
||||
_ => 0,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Ve
|
|||
public byte HeightScalar { get; }
|
||||
public byte WeightScalar { get; }
|
||||
public bool IsAlpha { get; set; }
|
||||
public EncounterStatic8aCorrelation Method { get; init; }
|
||||
|
||||
public bool HasFixedHeight => HeightScalar != NoScalar;
|
||||
public bool HasFixedWeight => WeightScalar != NoScalar;
|
||||
|
@ -50,21 +51,25 @@ public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Ve
|
|||
|
||||
if (pk is PA8 pa)
|
||||
{
|
||||
if (pa.AlphaMove != 0)
|
||||
pk.PushMove(pa.AlphaMove);
|
||||
pa.SetMasteryFlags();
|
||||
pa.HeightScalarCopy = pa.HeightScalar;
|
||||
if (IsAlpha)
|
||||
{
|
||||
var extra = pa.AlphaMove = pa.GetRandomAlphaMove();
|
||||
pa.SetMasteryFlagMove(extra);
|
||||
pk.PushMove(extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
|
||||
{
|
||||
var para = GetParams();
|
||||
Overworld8aRNG.ApplyDetails(pk, criteria, para);
|
||||
var (_, slotSeed) = Overworld8aRNG.ApplyDetails(pk, criteria, para, IsAlpha);
|
||||
// We don't override LevelMin, so just handle the two species cases.
|
||||
if (Species == (int)Core.Species.Zorua)
|
||||
pk.CurrentLevel = pk.Met_Level = Overworld8aRNG.GetRandomLevel(slotSeed, 26, 28);
|
||||
else if (Species == (int)Core.Species.Phione)
|
||||
pk.CurrentLevel = pk.Met_Level = Overworld8aRNG.GetRandomLevel(slotSeed, 33, 36);
|
||||
|
||||
if (Method == EncounterStatic8aCorrelation.Fixed)
|
||||
pk.EncryptionConstant = Util.Rand32();
|
||||
}
|
||||
|
||||
protected override void ApplyDetailsBall(PKM pk) => pk.Ball = Gift ? Ball : (int)Core.Ball.LAPoke;
|
||||
|
@ -159,3 +164,9 @@ public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Ve
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum EncounterStatic8aCorrelation : byte
|
||||
{
|
||||
WildGroup,
|
||||
Fixed,
|
||||
}
|
||||
|
|
|
@ -8,21 +8,6 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public static class Overworld8aRNG
|
||||
{
|
||||
public static uint AdaptPID(PKM pk, Shiny shiny, uint pid)
|
||||
{
|
||||
if (shiny == Shiny.Never)
|
||||
{
|
||||
if (GetIsShiny(pk.TID, pk.SID, pid))
|
||||
pid ^= 0x1000_0000;
|
||||
}
|
||||
else if (shiny != Shiny.Random)
|
||||
{
|
||||
if (!GetIsShiny(pk.TID, pk.SID, pid))
|
||||
pid = GetShinyPID(pk.TID, pk.SID, pid, 0);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
private static uint GetShinyPID(int tid, int sid, uint pid, int type)
|
||||
{
|
||||
return (uint)(((tid ^ sid ^ (pid & 0xFFFF) ^ type) << 16) | (pid & 0xFFFF));
|
||||
|
@ -41,30 +26,84 @@ public static class Overworld8aRNG
|
|||
|
||||
private const int UNSET = -1;
|
||||
|
||||
public static void ApplyDetails(PKM pk, EncounterCriteria criteria, in OverworldParam8a para)
|
||||
public static (ulong GroupSeed, ulong SlotSeed) ApplyDetails(PKM pk, EncounterCriteria criteria, in OverworldParam8a para, bool giveAlphaMove)
|
||||
{
|
||||
int ctr = 0;
|
||||
const int maxAttempts = 50_000;
|
||||
var rnd = Util.Rand;
|
||||
var fakeRand = new Xoroshiro128Plus(Util.Rand.Rand64());
|
||||
|
||||
Xoroshiro128Plus groupRand;
|
||||
ulong groupSeed;
|
||||
ulong slotSeed;
|
||||
do
|
||||
{
|
||||
ulong s0 = Util.Rand32(rnd) | (ulong)Util.Rand32(rnd) << 32;
|
||||
ulong s1 = Util.Rand32(rnd) | (ulong)Util.Rand32(rnd) << 32;
|
||||
var rand = new Xoroshiro128Plus(s0, s1);
|
||||
if (TryApplyFromSeed(pk, criteria, para, rand))
|
||||
return;
|
||||
groupSeed = fakeRand.Next();
|
||||
groupRand = new Xoroshiro128Plus(groupSeed);
|
||||
slotSeed = groupRand.Next();
|
||||
var slotRand = new Xoroshiro128Plus(slotSeed);
|
||||
_ = slotRand.Next();
|
||||
var entitySeed = slotRand.Next();
|
||||
var result = TryApplyFromSeed(pk, criteria, para, entitySeed);
|
||||
if (result)
|
||||
break;
|
||||
} while (++ctr != maxAttempts);
|
||||
|
||||
// Failed, fall back to Unrestricted and just put whatever.
|
||||
if (ctr >= maxAttempts)
|
||||
{
|
||||
ulong s0 = Util.Rand32(rnd) | (ulong)Util.Rand32(rnd) << 32;
|
||||
ulong s1 = Util.Rand32(rnd) | (ulong)Util.Rand32(rnd) << 32;
|
||||
var rand = new Xoroshiro128Plus(s0, s1);
|
||||
TryApplyFromSeed(pk, EncounterCriteria.Unrestricted, para, rand);
|
||||
groupSeed = fakeRand.Next();
|
||||
groupRand = new Xoroshiro128Plus(groupSeed);
|
||||
var slotRand = new Xoroshiro128Plus(slotSeed);
|
||||
_ = slotRand.Next();
|
||||
var entitySeed = slotRand.Next();
|
||||
TryApplyFromSeed(pk, EncounterCriteria.Unrestricted, para, entitySeed);
|
||||
}
|
||||
|
||||
if (giveAlphaMove)
|
||||
ApplyRandomAlphaMove(pk, groupRand.Next());
|
||||
|
||||
return (groupSeed, slotSeed);
|
||||
}
|
||||
|
||||
public static bool TryApplyFromSeed(PKM pk, EncounterCriteria criteria, in OverworldParam8a para, Xoroshiro128Plus rand)
|
||||
public static (ulong EntitySeed, ulong SlotRand) ApplyDetails(PKM pk, in OverworldParam8a para, bool giveAlphaMove, ref Xoroshiro128Plus groupRand)
|
||||
{
|
||||
var slotSeed = groupRand.Next();
|
||||
var alphaSeed = groupRand.Next();
|
||||
|
||||
var slotRand = new Xoroshiro128Plus(slotSeed);
|
||||
var slotRoll = slotRand.Next();
|
||||
var entitySeed = slotRand.Next();
|
||||
TryApplyFromSeed(pk, EncounterCriteria.Unrestricted, para, entitySeed);
|
||||
if (giveAlphaMove)
|
||||
ApplyRandomAlphaMove(pk, alphaSeed);
|
||||
|
||||
return (entitySeed, slotRoll);
|
||||
}
|
||||
|
||||
private static void ApplyRandomAlphaMove(PKM pk, ulong seed)
|
||||
{
|
||||
var pi = (PersonalInfoLA)PersonalTable.LA.GetFormEntry(pk.Species, pk.Form);
|
||||
var count = pi.GetMoveShopCount();
|
||||
if (count == 0 || pk is not PA8 pa8)
|
||||
return;
|
||||
|
||||
var index = GetRandomAlphaMoveIndex(seed, count);
|
||||
var alphaIndex = pi.GetMoveShopIndex(index);
|
||||
var alphaMove = Legal.MoveShop8_LA[alphaIndex];
|
||||
|
||||
pa8.SetMasteryFlagMove(pa8.AlphaMove = alphaMove);
|
||||
}
|
||||
|
||||
private static int GetRandomAlphaMoveIndex(ulong alphaSeed, int count)
|
||||
{
|
||||
var alphaRand = new Xoroshiro128Plus(alphaSeed);
|
||||
return (int)alphaRand.NextInt((uint)count);
|
||||
}
|
||||
|
||||
public static bool TryApplyFromSeed(PKM pk, EncounterCriteria criteria, in OverworldParam8a para, ulong seed)
|
||||
{
|
||||
var rand = new Xoroshiro128Plus(seed);
|
||||
|
||||
// Encryption Constant
|
||||
pk.EncryptionConstant = (uint)rand.NextInt();
|
||||
|
||||
|
@ -281,6 +320,16 @@ public static class Overworld8aRNG
|
|||
pid ^= 0x1000_0000;
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetRandomLevel(ulong slotSeed, byte LevelMin, byte LevelMax)
|
||||
{
|
||||
var delta = LevelMax - LevelMin;
|
||||
var xoro = new Xoroshiro128Plus(slotSeed);
|
||||
xoro.Next();
|
||||
xoro.Next(); // slot, entitySeed
|
||||
var amp = (int)xoro.NextInt((ulong)delta + 1);
|
||||
return LevelMin + amp;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly record struct OverworldParam8a(bool IsAlpha, byte GenderRatio, byte FlawlessIVs, byte RollCount, Shiny Shiny = Shiny.Random);
|
||||
|
|
|
@ -831,14 +831,6 @@ public sealed class PA8 : PKM, ISanityChecksum,
|
|||
return (byte)Math.Min(255, unsigned);
|
||||
}
|
||||
|
||||
public ushort GetRandomAlphaMove()
|
||||
{
|
||||
var index = MoveShopPermitFlags.IndexOf(true);
|
||||
if (index == -1)
|
||||
return 0;
|
||||
return MoveShopPermitIndexes[index];
|
||||
}
|
||||
|
||||
public void SetMasteryFlags()
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
|
|
|
@ -110,4 +110,32 @@ public sealed class PersonalInfoLA : PersonalInfo
|
|||
public int DexIndexLocal3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x66)); set => WriteUInt16LittleEndian(Data.AsSpan(0x66), (ushort)value); }
|
||||
public int DexIndexLocal4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x68)); set => WriteUInt16LittleEndian(Data.AsSpan(0x68), (ushort)value); }
|
||||
public int DexIndexLocal5 { get => ReadUInt16LittleEndian(Data.AsSpan(0x6A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), (ushort)value); }
|
||||
|
||||
public int GetMoveShopCount()
|
||||
{
|
||||
// Return a count of true indexes from Tutors
|
||||
var arr = SpecialTutors[0];
|
||||
int count = 0;
|
||||
foreach (var index in arr)
|
||||
{
|
||||
if (index)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public int GetMoveShopIndex(int randIndexFromCount)
|
||||
{
|
||||
// Return a count of true indexes from Tutors
|
||||
var arr = SpecialTutors[0];
|
||||
for (var i = 0; i < arr.Length; i++)
|
||||
{
|
||||
var index = arr[i];
|
||||
if (!index)
|
||||
continue;
|
||||
if (randIndexFromCount-- == 0)
|
||||
return i;
|
||||
}
|
||||
throw new ArgumentOutOfRangeException(nameof(randIndexFromCount));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ public readonly ref struct AreaSpawnerSet8a
|
|||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var spawner = this[i];
|
||||
if (spawner.Meta.Spawner_01 == key)
|
||||
if (spawner.Meta.SpawnerHash == key)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
|
|
|
@ -13,13 +13,13 @@ public readonly ref struct SpawnerMeta8a
|
|||
|
||||
public SpawnerMeta8a(Span<byte> data) => Data = data;
|
||||
|
||||
public ulong QuantitySeed { get => ReadUInt64LittleEndian(Data); set => WriteUInt64LittleEndian(Data , value); }
|
||||
public ulong CountSeed { get => ReadUInt64LittleEndian(Data); set => WriteUInt64LittleEndian(Data , value); }
|
||||
|
||||
/// <summary> Seed that regenerates seeds for the entries as a group, regenerating multiple or single entries. </summary>
|
||||
public ulong GroupSeed { get => ReadUInt64LittleEndian(Data[0x08..]); set => WriteUInt64LittleEndian(Data[0x08..], value); }
|
||||
|
||||
// flatbuffer PlacementSpawner8a.Field_01 to match
|
||||
public ulong Spawner_01 { get => ReadUInt64LittleEndian(Data[0x10..]); set => WriteUInt64LittleEndian(Data[0x10..], value); }
|
||||
public ulong SpawnerHash { get => ReadUInt64LittleEndian(Data[0x10..]); set => WriteUInt64LittleEndian(Data[0x10..], value); }
|
||||
|
||||
public int Count { get => ReadInt32LittleEndian (Data[0x18..]); set => WriteInt32LittleEndian (Data[0x18..], value); }
|
||||
public int Flags { get => ReadInt32LittleEndian (Data[0x1C..]); set => WriteInt32LittleEndian (Data[0x1C..], value); }
|
||||
|
@ -58,15 +58,15 @@ public readonly ref struct SpawnerMeta8a
|
|||
/// <param name="min">Minimum spawn count</param>
|
||||
/// <param name="max">Maximum spawn count</param>
|
||||
/// <returns>Count for the cycle.</returns>
|
||||
/// <remarks>Does not advance the <see cref="QuantitySeed"/> if the input <see cref="min"/> and <see cref="max"/> are equivalent.</remarks>
|
||||
/// <remarks>Does not advance the <see cref="CountSeed"/> if the input <see cref="min"/> and <see cref="max"/> are equivalent.</remarks>
|
||||
public int GetNextQuantity(int min, int max)
|
||||
{
|
||||
if (min == max)
|
||||
return min;
|
||||
var delta = max - min;
|
||||
var rand = new Xoroshiro128Plus(QuantitySeed);
|
||||
var rand = new Xoroshiro128Plus(CountSeed);
|
||||
var result = (int)rand.NextInt((uint)delta + 1);
|
||||
QuantitySeed = rand.Next();
|
||||
CountSeed = rand.Next();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ namespace PKHeX.Core
|
|||
});
|
||||
|
||||
public static uint Rand32() => Rand32(Rand);
|
||||
public static uint Rand32(Random rnd) => (uint)rnd.Next(1 << 30) << 2 | (uint)rnd.Next(1 << 2);
|
||||
public static uint Rand32(this Random rnd) => (uint)rnd.Next(1 << 30) << 2 | (uint)rnd.Next(1 << 2);
|
||||
public static ulong Rand64(this Random rnd) => rnd.Rand32() | (ulong)rnd.Rand32() << 32;
|
||||
|
||||
/// <summary>
|
||||
/// Shuffles the order of items within a collection of items.
|
||||
|
|
|
@ -11,7 +11,6 @@ public static class Wild8aRNGTests
|
|||
PA8 test = new() { Species = (int)Species.Zorua, Form = 1 };
|
||||
const ulong s0 = 0xDF440DA44EEC4FFB;
|
||||
|
||||
var rand = new Xoroshiro128Plus(s0);
|
||||
var param = new OverworldParam8a
|
||||
{
|
||||
FlawlessIVs = 0, IsAlpha = false,
|
||||
|
@ -19,7 +18,7 @@ public static class Wild8aRNGTests
|
|||
GenderRatio = 0x7F,
|
||||
};
|
||||
|
||||
var result = Overworld8aRNG.TryApplyFromSeed(test, EncounterCriteria.Unrestricted, param, rand);
|
||||
var result = Overworld8aRNG.TryApplyFromSeed(test, EncounterCriteria.Unrestricted, param, s0);
|
||||
result.Should().BeTrue();
|
||||
|
||||
test.IV_HP.Should().Be(10);
|
||||
|
@ -34,4 +33,35 @@ public static class Wild8aRNGTests
|
|||
var verify = Overworld8aRNG.Verify(test, s0, param);
|
||||
verify.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void TestMagby()
|
||||
{
|
||||
const ulong s0 = 0xE12DDECBDFC64AA1ul;
|
||||
PA8 test = new() { Species = (int)Species.Magby };
|
||||
|
||||
var param = new OverworldParam8a
|
||||
{
|
||||
FlawlessIVs = 3,
|
||||
IsAlpha = true,
|
||||
Shiny = Shiny.Random,
|
||||
RollCount = 17,
|
||||
GenderRatio = 0x7F,
|
||||
};
|
||||
|
||||
var xoro = new Xoroshiro128Plus(s0);
|
||||
var result = Overworld8aRNG.ApplyDetails(test, param, true, ref xoro);
|
||||
|
||||
test.IV_HP.Should().Be(31);
|
||||
test.IV_ATK.Should().Be(31);
|
||||
test.IV_DEF.Should().Be(7);
|
||||
test.IV_SPA.Should().Be(31);
|
||||
test.IV_SPD.Should().Be(20);
|
||||
test.IV_SPE.Should().Be(10);
|
||||
|
||||
test.AlphaMove.Should().Be((ushort)Move.Flamethrower);
|
||||
|
||||
var verify = Overworld8aRNG.Verify(test, result.EntitySeed, param);
|
||||
verify.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue