2018-10-28 18:15:40 +00:00
|
|
|
|
using System.Collections.Generic;
|
2017-05-19 00:36:43 +00:00
|
|
|
|
|
|
|
|
|
namespace PKHeX.Core
|
|
|
|
|
{
|
|
|
|
|
public static class LockFinder
|
|
|
|
|
{
|
2018-10-30 05:49:04 +00:00
|
|
|
|
public static bool IsAllShadowLockValid(EncounterStaticShadow s, PIDIV pv, PKM pkm)
|
2018-02-04 08:06:07 +00:00
|
|
|
|
{
|
2020-05-08 17:07:21 +00:00
|
|
|
|
if (s.Version == GameVersion.XD && pkm.IsShiny)
|
|
|
|
|
return false; // no xd shiny shadow mons
|
2018-10-30 05:49:04 +00:00
|
|
|
|
var teams = s.Locks;
|
|
|
|
|
if (teams.Length == 0)
|
2020-05-08 17:07:21 +00:00
|
|
|
|
return true;
|
2018-10-22 01:55:02 +00:00
|
|
|
|
|
2020-03-12 02:18:58 +00:00
|
|
|
|
var tsv = s.Version == GameVersion.XD ? (pkm.TID ^ pkm.SID) >> 3 : -1; // no xd shiny shadow mons
|
2018-10-30 05:49:04 +00:00
|
|
|
|
return IsAllShadowLockValid(pv, teams, tsv);
|
2018-10-29 03:28:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-30 05:49:04 +00:00
|
|
|
|
public static bool IsAllShadowLockValid(PIDIV pv, IEnumerable<TeamLock> teams, int tsv = -1)
|
2018-10-29 03:28:56 +00:00
|
|
|
|
{
|
|
|
|
|
foreach (var t in teams)
|
|
|
|
|
{
|
2018-10-30 05:49:04 +00:00
|
|
|
|
var result = new TeamLockResult(t, pv.OriginSeed, tsv);
|
|
|
|
|
if (result.Valid)
|
2018-10-29 03:28:56 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-02 04:12:44 +00:00
|
|
|
|
// Colosseum/XD Starters
|
|
|
|
|
public static bool IsXDStarterValid(uint seed, int TID, int SID)
|
|
|
|
|
{
|
|
|
|
|
// pidiv reversed 2x yields SID, 3x yields TID. shift by 7 if another PKM is generated prior
|
|
|
|
|
var SIDf = RNG.XDRNG.Reverse(seed, 2);
|
|
|
|
|
var TIDf = RNG.XDRNG.Prev(SIDf);
|
|
|
|
|
return SIDf >> 16 == SID && TIDf >> 16 == TID;
|
|
|
|
|
}
|
2018-09-15 05:37:47 +00:00
|
|
|
|
|
2017-11-02 04:12:44 +00:00
|
|
|
|
public static bool IsColoStarterValid(int species, ref uint seed, int TID, int SID, uint pkPID, uint IV1, uint IV2)
|
|
|
|
|
{
|
|
|
|
|
// reverse the seed the bare minimum
|
|
|
|
|
int rev = 2;
|
2019-12-09 01:39:19 +00:00
|
|
|
|
if (species == (int)Species.Espeon)
|
2017-11-02 04:12:44 +00:00
|
|
|
|
rev += 7;
|
|
|
|
|
|
|
|
|
|
var rng = RNG.XDRNG;
|
|
|
|
|
var SIDf = rng.Reverse(seed, rev);
|
|
|
|
|
int ctr = 0;
|
2018-10-28 18:15:40 +00:00
|
|
|
|
uint temp;
|
2018-05-24 13:00:29 +00:00
|
|
|
|
while ((temp = rng.Prev(SIDf)) >> 16 != TID || SIDf >> 16 != SID)
|
2017-11-02 04:12:44 +00:00
|
|
|
|
{
|
2018-05-12 15:41:23 +00:00
|
|
|
|
SIDf = temp;
|
2017-11-02 04:12:44 +00:00
|
|
|
|
if (ctr > 32) // arbitrary
|
|
|
|
|
return false;
|
|
|
|
|
ctr++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var next = rng.Next(SIDf);
|
|
|
|
|
|
|
|
|
|
// generate Umbreon
|
|
|
|
|
var PIDIV = GenerateValidColoStarterPID(ref next, TID, SID);
|
2019-12-09 01:39:19 +00:00
|
|
|
|
if (species == (int)Species.Espeon) // need Espeon, which is immediately next
|
2017-11-02 04:12:44 +00:00
|
|
|
|
PIDIV = GenerateValidColoStarterPID(ref next, TID, SID);
|
|
|
|
|
|
|
|
|
|
if (!PIDIV.Equals(pkPID, IV1, IV2))
|
|
|
|
|
return false;
|
|
|
|
|
seed = rng.Reverse(SIDf, 2);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-27 06:18:25 +00:00
|
|
|
|
private readonly struct PIDIVGroup
|
2017-11-02 04:12:44 +00:00
|
|
|
|
{
|
2019-10-27 06:18:25 +00:00
|
|
|
|
private readonly uint PID;
|
|
|
|
|
private readonly uint IV1;
|
|
|
|
|
private readonly uint IV2;
|
|
|
|
|
|
|
|
|
|
public PIDIVGroup(uint pid, uint iv1, uint iv2)
|
|
|
|
|
{
|
|
|
|
|
PID = pid;
|
|
|
|
|
IV1 = iv1;
|
|
|
|
|
IV2 = iv2;
|
|
|
|
|
}
|
2017-11-02 04:12:44 +00:00
|
|
|
|
|
|
|
|
|
public bool Equals(uint pid, uint iv1, uint iv2) => PID == pid && IV1 == iv1 && IV2 == iv2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static PIDIVGroup GenerateValidColoStarterPID(ref uint uSeed, int TID, int SID)
|
|
|
|
|
{
|
|
|
|
|
var rng = RNG.XDRNG;
|
|
|
|
|
|
|
|
|
|
uSeed = rng.Advance(uSeed, 2); // skip fakePID
|
2019-10-27 06:18:25 +00:00
|
|
|
|
var IV1 = (uSeed >> 16) & 0x7FFF;
|
2017-11-02 04:12:44 +00:00
|
|
|
|
uSeed = rng.Next(uSeed);
|
2019-10-27 06:18:25 +00:00
|
|
|
|
var IV2 = (uSeed >> 16) & 0x7FFF;
|
2017-11-02 04:12:44 +00:00
|
|
|
|
uSeed = rng.Next(uSeed);
|
|
|
|
|
uSeed = rng.Advance(uSeed, 1); // skip ability call
|
2019-10-27 06:18:25 +00:00
|
|
|
|
var PID = GenerateStarterPID(ref uSeed, TID, SID);
|
2017-11-02 04:12:44 +00:00
|
|
|
|
|
|
|
|
|
uSeed = rng.Advance(uSeed, 2); // PID calls consumed
|
|
|
|
|
|
2019-10-27 06:18:25 +00:00
|
|
|
|
return new PIDIVGroup(PID, IV1, IV2);
|
2017-11-02 04:12:44 +00:00
|
|
|
|
}
|
2018-09-15 05:37:47 +00:00
|
|
|
|
|
2018-10-30 05:49:04 +00:00
|
|
|
|
private static bool IsShiny(int TID, int SID, uint PID) => (TID ^ SID ^ (PID >> 16) ^ (PID & 0xFFFF)) < 8;
|
|
|
|
|
|
2017-11-02 04:12:44 +00:00
|
|
|
|
private static uint GenerateStarterPID(ref uint uSeed, int TID, int SID)
|
|
|
|
|
{
|
|
|
|
|
uint PID;
|
2020-12-30 04:25:05 +00:00
|
|
|
|
const byte ratio = 0x1F; // 12.5% F (can't be female)
|
2017-11-02 04:12:44 +00:00
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
var next = RNG.XDRNG.Next(uSeed);
|
|
|
|
|
PID = (uSeed & 0xFFFF0000) | (next >> 16);
|
2020-12-30 04:25:05 +00:00
|
|
|
|
if ((PID & 0xFF) >= ratio && !IsShiny(TID, SID, PID))
|
2017-11-02 04:12:44 +00:00
|
|
|
|
break;
|
|
|
|
|
uSeed = RNG.XDRNG.Next(next);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PID;
|
|
|
|
|
}
|
2017-05-19 00:36:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|