2022-01-03 05:35:59 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2017-08-04 04:35:41 +00:00
|
|
|
|
using System.Diagnostics;
|
2017-04-29 23:22:32 +00:00
|
|
|
|
using System.Linq;
|
2017-08-29 15:32:22 +00:00
|
|
|
|
using System.Runtime.CompilerServices;
|
2021-01-28 00:52:04 +00:00
|
|
|
|
using static PKHeX.Core.PIDType;
|
2017-04-29 23:22:32 +00:00
|
|
|
|
|
|
|
|
|
namespace PKHeX.Core
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Class containing logic to obtain a PKM's PIDIV method.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static class MethodFinder
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Analyzes a <see cref="PKM"/> to find a matching PIDIV method.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="pk">Input <see cref="PKM"/>.</param>
|
|
|
|
|
/// <returns><see cref="PIDIV"/> object containing seed and method info.</returns>
|
|
|
|
|
public static PIDIV Analyze(PKM pk)
|
|
|
|
|
{
|
|
|
|
|
if (pk.Format < 3)
|
|
|
|
|
return AnalyzeGB(pk);
|
2017-05-04 06:14:34 +00:00
|
|
|
|
var pid = pk.EncryptionConstant;
|
2018-05-12 15:13:39 +00:00
|
|
|
|
|
2017-04-29 23:22:32 +00:00
|
|
|
|
var top = pid >> 16;
|
|
|
|
|
var bot = pid & 0xFFFF;
|
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
Span<uint> temp = stackalloc uint[6];
|
2017-04-29 23:22:32 +00:00
|
|
|
|
for (int i = 0; i < 6; i++)
|
2022-01-03 05:35:59 +00:00
|
|
|
|
temp[i] = (uint)pk.GetIV(i);
|
|
|
|
|
ReadOnlySpan<uint> IVs = temp;
|
2017-04-29 23:22:32 +00:00
|
|
|
|
|
2018-05-12 19:28:48 +00:00
|
|
|
|
if (GetLCRNGMatch(top, bot, IVs, out PIDIV pidiv))
|
2017-04-29 23:22:32 +00:00
|
|
|
|
return pidiv;
|
2019-12-09 01:39:19 +00:00
|
|
|
|
if (pk.Species == (int)Species.Unown && GetLCRNGUnownMatch(top, bot, IVs, out pidiv)) // frlg only
|
2017-05-01 15:37:23 +00:00
|
|
|
|
return pidiv;
|
2017-11-02 04:12:44 +00:00
|
|
|
|
if (GetColoStarterMatch(pk, top, bot, IVs, out pidiv))
|
|
|
|
|
return pidiv;
|
2020-07-26 16:49:33 +00:00
|
|
|
|
if (GetXDRNGMatch(pk, top, bot, IVs, out pidiv))
|
2017-04-29 23:22:32 +00:00
|
|
|
|
return pidiv;
|
|
|
|
|
|
|
|
|
|
// Special cases
|
2017-06-18 01:37:19 +00:00
|
|
|
|
if (GetLCRNGRoamerMatch(top, bot, IVs, out pidiv))
|
2017-05-02 06:41:19 +00:00
|
|
|
|
return pidiv;
|
2017-06-30 02:32:29 +00:00
|
|
|
|
if (GetChannelMatch(top, bot, IVs, out pidiv, pk))
|
2017-04-29 23:22:32 +00:00
|
|
|
|
return pidiv;
|
2017-06-18 01:37:19 +00:00
|
|
|
|
if (GetMG4Match(pid, IVs, out pidiv))
|
2017-04-29 23:22:32 +00:00
|
|
|
|
return pidiv;
|
2017-04-30 03:04:54 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
if (GetBACDMatch(pk, pid, IVs, out pidiv))
|
2017-04-30 03:04:54 +00:00
|
|
|
|
return pidiv;
|
2017-08-29 15:32:22 +00:00
|
|
|
|
if (GetModifiedPIDMatch(pk, pid, IVs, out pidiv))
|
2017-06-25 00:20:15 +00:00
|
|
|
|
return pidiv;
|
2017-04-29 23:22:32 +00:00
|
|
|
|
|
2021-01-28 00:52:04 +00:00
|
|
|
|
return PIDIV.None; // no match
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static bool GetModifiedPIDMatch(PKM pk, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
2017-08-29 15:32:22 +00:00
|
|
|
|
{
|
|
|
|
|
if (pk.IsShiny)
|
|
|
|
|
{
|
|
|
|
|
if (GetChainShinyMatch(pk, pid, IVs, out pidiv))
|
|
|
|
|
return true;
|
|
|
|
|
if (GetModified8BitMatch(pk, pid, out pidiv))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (pid <= 0xFF && GetCuteCharmMatch(pk, pid, out pidiv))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GetPokewalkerMatch(pk, pid, out pidiv);
|
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2017-08-29 15:32:22 +00:00
|
|
|
|
private static bool GetModified8BitMatch(PKM pk, uint pid, out PIDIV pidiv)
|
|
|
|
|
{
|
2017-11-18 06:19:23 +00:00
|
|
|
|
return pk.Gen4
|
2018-08-03 03:11:42 +00:00
|
|
|
|
? (pid <= 0xFF && GetCuteCharmMatch(pk, pid, out pidiv)) || GetG5MGShinyMatch(pk, pid, out pidiv)
|
|
|
|
|
: GetG5MGShinyMatch(pk, pid, out pidiv) || (pid <= 0xFF && GetCuteCharmMatch(pk, pid, out pidiv));
|
2017-08-29 15:32:22 +00:00
|
|
|
|
}
|
2017-04-29 23:22:32 +00:00
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static bool GetLCRNGMatch(uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
2017-06-18 01:37:19 +00:00
|
|
|
|
var reg = GetSeedsFromPID(RNG.LCRNG, top, bot);
|
|
|
|
|
var iv1 = GetIVChunk(IVs, 0);
|
|
|
|
|
var iv2 = GetIVChunk(IVs, 3);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
foreach (var seed in reg)
|
|
|
|
|
{
|
|
|
|
|
// A and B are already used by PID
|
|
|
|
|
var B = RNG.LCRNG.Advance(seed, 2);
|
|
|
|
|
|
|
|
|
|
// Method 1/2/4 can use 3 different RNG frames
|
|
|
|
|
var C = RNG.LCRNG.Next(B);
|
2017-05-02 06:41:19 +00:00
|
|
|
|
var ivC = C >> 16 & 0x7FFF;
|
|
|
|
|
if (iv1 == ivC)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
2017-05-02 06:41:19 +00:00
|
|
|
|
var D = RNG.LCRNG.Next(C);
|
|
|
|
|
var ivD = D >> 16 & 0x7FFF;
|
|
|
|
|
if (iv2 == ivD) // ABCD
|
|
|
|
|
{
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(Method_1, seed);
|
2017-05-02 06:41:19 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-04-29 23:22:32 +00:00
|
|
|
|
|
2017-05-02 06:41:19 +00:00
|
|
|
|
var E = RNG.LCRNG.Next(D);
|
|
|
|
|
var ivE = E >> 16 & 0x7FFF;
|
|
|
|
|
if (iv2 == ivE) // ABCE
|
|
|
|
|
{
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(Method_4, seed);
|
2017-05-02 06:41:19 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2017-05-02 06:41:19 +00:00
|
|
|
|
else
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
2017-05-02 06:41:19 +00:00
|
|
|
|
var D = RNG.LCRNG.Next(C);
|
|
|
|
|
var ivD = D >> 16 & 0x7FFF;
|
|
|
|
|
if (iv1 != ivD)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var E = RNG.LCRNG.Next(D);
|
|
|
|
|
var ivE = E >> 16 & 0x7FFF;
|
|
|
|
|
if (iv2 == ivE) // ABDE
|
|
|
|
|
{
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(Method_2, seed);
|
2017-05-02 06:41:19 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-20 02:15:06 +00:00
|
|
|
|
reg = GetSeedsFromPIDSkip(RNG.LCRNG, top, bot);
|
|
|
|
|
foreach (var seed in reg)
|
|
|
|
|
{
|
|
|
|
|
// A and B are already used by PID
|
|
|
|
|
var C = RNG.LCRNG.Advance(seed, 3);
|
|
|
|
|
|
|
|
|
|
// Method 3
|
|
|
|
|
var D = RNG.LCRNG.Next(C);
|
|
|
|
|
var ivD = D >> 16 & 0x7FFF;
|
|
|
|
|
if (iv1 != ivD)
|
|
|
|
|
continue;
|
|
|
|
|
var E = RNG.LCRNG.Next(D);
|
|
|
|
|
var ivE = E >> 16 & 0x7FFF;
|
|
|
|
|
if (iv2 != ivE)
|
|
|
|
|
continue;
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(Method_3, seed);
|
2019-06-20 02:15:06 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static bool GetLCRNGUnownMatch(uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
2017-05-01 15:37:23 +00:00
|
|
|
|
{
|
|
|
|
|
// this is an exact copy of LCRNG 1,2,4 matching, except the PID has its halves switched (BACD, BADE, BACE)
|
2017-06-18 01:37:19 +00:00
|
|
|
|
var reg = GetSeedsFromPID(RNG.LCRNG, bot, top); // reversed!
|
|
|
|
|
var iv1 = GetIVChunk(IVs, 0);
|
|
|
|
|
var iv2 = GetIVChunk(IVs, 3);
|
2017-05-01 15:37:23 +00:00
|
|
|
|
foreach (var seed in reg)
|
|
|
|
|
{
|
|
|
|
|
// A and B are already used by PID
|
|
|
|
|
var B = RNG.LCRNG.Advance(seed, 2);
|
|
|
|
|
|
|
|
|
|
// Method 1/2/4 can use 3 different RNG frames
|
|
|
|
|
var C = RNG.LCRNG.Next(B);
|
2017-05-02 06:41:19 +00:00
|
|
|
|
var ivC = C >> 16 & 0x7FFF;
|
|
|
|
|
if (iv1 == ivC)
|
2017-05-01 15:37:23 +00:00
|
|
|
|
{
|
2017-05-02 06:41:19 +00:00
|
|
|
|
var D = RNG.LCRNG.Next(C);
|
|
|
|
|
var ivD = D >> 16 & 0x7FFF;
|
|
|
|
|
if (iv2 == ivD) // BACD
|
|
|
|
|
{
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(Method_1_Unown, seed);
|
2017-05-02 06:41:19 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-05-01 15:37:23 +00:00
|
|
|
|
|
2017-05-02 06:41:19 +00:00
|
|
|
|
var E = RNG.LCRNG.Next(D);
|
|
|
|
|
var ivE = E >> 16 & 0x7FFF;
|
|
|
|
|
if (iv2 == ivE) // BACE
|
|
|
|
|
{
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(Method_4_Unown, seed);
|
2017-05-02 06:41:19 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-05-01 15:37:23 +00:00
|
|
|
|
}
|
2017-05-02 06:41:19 +00:00
|
|
|
|
else
|
2017-05-01 15:37:23 +00:00
|
|
|
|
{
|
2017-05-02 06:41:19 +00:00
|
|
|
|
var D = RNG.LCRNG.Next(C);
|
|
|
|
|
var ivD = D >> 16 & 0x7FFF;
|
|
|
|
|
if (iv1 != ivD)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var E = RNG.LCRNG.Next(D);
|
|
|
|
|
var ivE = E >> 16 & 0x7FFF;
|
2017-05-02 15:59:55 +00:00
|
|
|
|
if (iv2 == ivE) // BADE
|
2017-05-02 06:41:19 +00:00
|
|
|
|
{
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(Method_2_Unown, seed);
|
2017-05-02 06:41:19 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-05-01 15:37:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-20 02:15:06 +00:00
|
|
|
|
reg = GetSeedsFromPIDSkip(RNG.LCRNG, bot, top); // reversed!
|
|
|
|
|
foreach (var seed in reg)
|
|
|
|
|
{
|
|
|
|
|
// A and B are already used by PID
|
|
|
|
|
var C = RNG.LCRNG.Advance(seed, 3);
|
|
|
|
|
|
|
|
|
|
// Method 3
|
|
|
|
|
var D = RNG.LCRNG.Next(C);
|
|
|
|
|
var ivD = D >> 16 & 0x7FFF;
|
|
|
|
|
if (iv1 != ivD)
|
|
|
|
|
continue;
|
|
|
|
|
var E = RNG.LCRNG.Next(D);
|
|
|
|
|
var ivE = E >> 16 & 0x7FFF;
|
|
|
|
|
if (iv2 != ivE)
|
|
|
|
|
continue;
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(Method_3_Unown, seed);
|
2019-06-20 02:15:06 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-05-01 15:37:23 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static bool GetLCRNGRoamerMatch(uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
2017-05-02 06:41:19 +00:00
|
|
|
|
{
|
2018-10-13 15:02:55 +00:00
|
|
|
|
if (IVs[2] != 0 || IVs[3] != 0 || IVs[4] != 0 || IVs[5] != 0 || IVs[1] > 7)
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-06-18 01:37:19 +00:00
|
|
|
|
var iv1 = GetIVChunk(IVs, 0);
|
|
|
|
|
var reg = GetSeedsFromPID(RNG.LCRNG, top, bot);
|
2017-05-02 06:41:19 +00:00
|
|
|
|
foreach (var seed in reg)
|
|
|
|
|
{
|
2017-06-03 22:43:41 +00:00
|
|
|
|
// Only the first 8 bits are kept
|
|
|
|
|
var ivC = RNG.LCRNG.Advance(seed, 3) >> 16 & 0x00FF;
|
2017-05-02 06:41:19 +00:00
|
|
|
|
if (iv1 != ivC)
|
|
|
|
|
continue;
|
|
|
|
|
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(Method_1_Roamer, seed);
|
2017-05-02 06:41:19 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-05-02 06:41:19 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static bool GetXDRNGMatch(PKM pk, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
2017-08-05 08:51:29 +00:00
|
|
|
|
var xdc = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
foreach (var seed in xdc)
|
|
|
|
|
{
|
2017-05-28 05:18:04 +00:00
|
|
|
|
var B = RNG.XDRNG.Prev(seed);
|
|
|
|
|
var A = RNG.XDRNG.Prev(B);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
|
2019-01-06 09:26:18 +00:00
|
|
|
|
var hi = A >> 16;
|
|
|
|
|
var lo = B >> 16;
|
2020-07-26 16:49:33 +00:00
|
|
|
|
if (IVsMatch(hi, lo, IVs))
|
2017-12-04 21:18:24 +00:00
|
|
|
|
{
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(CXD, RNG.XDRNG.Prev(A));
|
2020-07-26 16:49:33 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check for anti-shiny against player TSV
|
2020-10-03 16:29:26 +00:00
|
|
|
|
var tsv = (uint)(pk.TID ^ pk.SID) >> 3;
|
|
|
|
|
var psv = (top ^ bot) >> 3;
|
|
|
|
|
if (psv == tsv) // already shiny, wouldn't be anti-shiny
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var p2 = seed;
|
|
|
|
|
var p1 = B;
|
|
|
|
|
psv = ((p2 ^ p1) >> 19);
|
|
|
|
|
if (psv != tsv) // prior PID must be shiny
|
2019-01-06 09:26:18 +00:00
|
|
|
|
continue;
|
2020-07-26 16:49:33 +00:00
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
B = RNG.XDRNG.Prev(A);
|
|
|
|
|
A = RNG.XDRNG.Prev(B);
|
|
|
|
|
hi = A >> 16;
|
|
|
|
|
lo = B >> 16;
|
2020-10-03 16:29:26 +00:00
|
|
|
|
if (IVsMatch(hi, lo, IVs))
|
|
|
|
|
{
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(CXDAnti, RNG.XDRNG.Prev(A));
|
2020-10-03 16:29:26 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-04-29 23:22:32 +00:00
|
|
|
|
|
2020-10-03 16:29:26 +00:00
|
|
|
|
p2 = RNG.XDRNG.Prev(p1);
|
|
|
|
|
p1 = RNG.XDRNG.Prev(p2);
|
|
|
|
|
psv = (p2 ^ p1) >> 19;
|
2020-07-26 16:49:33 +00:00
|
|
|
|
}
|
2020-10-03 16:29:26 +00:00
|
|
|
|
while (psv == tsv);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static bool GetChannelMatch(uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv, PKM pk)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
2017-06-30 02:32:29 +00:00
|
|
|
|
var ver = pk.Version;
|
2021-01-05 01:31:43 +00:00
|
|
|
|
if (ver is not ((int)GameVersion.R or (int)GameVersion.S))
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-06-30 02:32:29 +00:00
|
|
|
|
|
2017-07-16 02:59:38 +00:00
|
|
|
|
var undo = top ^ 0x8000;
|
|
|
|
|
if ((undo > 7 ? 0 : 1) != (bot ^ pk.SID ^ 40122))
|
|
|
|
|
top = undo;
|
2017-08-05 08:51:29 +00:00
|
|
|
|
var channel = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
foreach (var seed in channel)
|
|
|
|
|
{
|
2017-06-30 02:32:29 +00:00
|
|
|
|
var C = RNG.XDRNG.Advance(seed, 3); // held item
|
|
|
|
|
// no checks, held item can be swapped
|
|
|
|
|
|
|
|
|
|
var D = RNG.XDRNG.Next(C); // Version
|
|
|
|
|
if ((D >> 31) + 1 != ver) // (0-Sapphire, 1-Ruby)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var E = RNG.XDRNG.Next(D); // OT Gender
|
|
|
|
|
if (E >> 31 != pk.OT_Gender)
|
|
|
|
|
continue;
|
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
if (!RNG.XDRNG.GetSequentialIVsUInt32(E, IVs))
|
2017-04-29 23:22:32 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2017-06-30 02:32:29 +00:00
|
|
|
|
if (seed >> 16 != pk.SID)
|
|
|
|
|
continue;
|
|
|
|
|
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(Channel, RNG.XDRNG.Prev(seed));
|
2017-04-29 23:22:32 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static bool GetMG4Match(uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
|
|
|
|
uint mg4Rev = RNG.ARNG.Prev(pid);
|
2017-06-18 01:37:19 +00:00
|
|
|
|
var mg4 = GetSeedsFromPID(RNG.LCRNG, mg4Rev >> 16, mg4Rev & 0xFFFF);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
foreach (var seed in mg4)
|
|
|
|
|
{
|
|
|
|
|
var B = RNG.LCRNG.Advance(seed, 2);
|
|
|
|
|
var C = RNG.LCRNG.Next(B);
|
|
|
|
|
var D = RNG.LCRNG.Next(C);
|
2019-01-06 09:26:18 +00:00
|
|
|
|
if (!IVsMatch(C >> 16, D >> 16, IVs))
|
2017-04-29 23:22:32 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(G4MGAntiShiny, seed);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2017-08-29 15:32:22 +00:00
|
|
|
|
private static bool GetG5MGShinyMatch(PKM pk, uint pid, out PIDIV pidiv)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
2017-04-30 03:04:54 +00:00
|
|
|
|
var low = pid & 0xFFFF;
|
2017-04-29 23:22:32 +00:00
|
|
|
|
// generation 5 shiny PIDs
|
2017-04-30 03:04:54 +00:00
|
|
|
|
if (low <= 0xFF)
|
|
|
|
|
{
|
2017-09-17 21:43:28 +00:00
|
|
|
|
var av = (pid >> 16) & 1;
|
|
|
|
|
var genPID = PIDGenerator.GetMG5ShinyPID(low, av, pk.TID, pk.SID);
|
|
|
|
|
if (genPID == pid)
|
2017-04-30 03:04:54 +00:00
|
|
|
|
{
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = PIDIV.G5MGShiny;
|
2017-04-30 03:04:54 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
private static bool GetCuteCharmMatch(PKM pk, uint pid, out PIDIV pidiv)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
2017-08-29 15:32:22 +00:00
|
|
|
|
if (pid > 0xFF)
|
|
|
|
|
return GetNonMatch(out pidiv);
|
|
|
|
|
|
2021-08-22 07:06:02 +00:00
|
|
|
|
(int species, int genderValue) = GetCuteCharmGenderSpecies(pk, pid, pk.Species);
|
2017-10-13 06:18:43 +00:00
|
|
|
|
int getRatio() => PersonalTable.HGSS[species].Gender;
|
2017-04-29 23:22:32 +00:00
|
|
|
|
switch (genderValue)
|
|
|
|
|
{
|
|
|
|
|
case 2: break; // can't cute charm a genderless pkm
|
|
|
|
|
case 0: // male
|
2017-10-13 06:18:43 +00:00
|
|
|
|
var gr = getRatio();
|
2021-07-13 07:25:51 +00:00
|
|
|
|
if (gr >= PersonalInfo.RatioMagicFemale) // no modification for PID
|
2017-04-29 23:22:32 +00:00
|
|
|
|
break;
|
2018-08-03 03:11:42 +00:00
|
|
|
|
var rate = 25*((gr / 25) + 1); // buffered
|
2017-05-14 19:42:27 +00:00
|
|
|
|
var nature = pid % 25;
|
|
|
|
|
if (nature + rate != pid)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = PIDIV.CuteCharm;
|
2017-04-29 23:22:32 +00:00
|
|
|
|
return true;
|
|
|
|
|
case 1: // female
|
2017-04-30 06:18:36 +00:00
|
|
|
|
if (pid >= 25)
|
2019-05-11 07:59:07 +00:00
|
|
|
|
break; // nope, this isn't a valid nature
|
2021-07-13 07:25:51 +00:00
|
|
|
|
if (getRatio() >= PersonalInfo.RatioMagicFemale) // no modification for PID
|
2017-04-29 23:22:32 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = PIDIV.CuteCharm;
|
2017-04-29 23:22:32 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static bool GetChainShinyMatch(PKM pk, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
2017-04-30 03:04:54 +00:00
|
|
|
|
{
|
|
|
|
|
// 13 shiny bits
|
|
|
|
|
// PIDH & 7
|
|
|
|
|
// PIDL & 7
|
|
|
|
|
// IVs
|
2017-06-18 01:37:19 +00:00
|
|
|
|
var bot = GetIVChunk(IVs, 0);
|
|
|
|
|
var top = GetIVChunk(IVs, 3);
|
|
|
|
|
var reg = GetSeedsFromIVs(RNG.LCRNG, top, bot);
|
2017-04-30 03:04:54 +00:00
|
|
|
|
foreach (var seed in reg)
|
|
|
|
|
{
|
|
|
|
|
// check the individual bits
|
|
|
|
|
var s = seed;
|
|
|
|
|
int i = 15;
|
2018-05-12 19:28:48 +00:00
|
|
|
|
do
|
2017-04-30 03:04:54 +00:00
|
|
|
|
{
|
|
|
|
|
var bit = s >> 16 & 1;
|
|
|
|
|
if (bit != (pid >> i & 1))
|
|
|
|
|
break;
|
|
|
|
|
s = RNG.LCRNG.Prev(s);
|
|
|
|
|
}
|
2018-05-12 19:28:48 +00:00
|
|
|
|
while (--i != 2);
|
2017-04-30 03:04:54 +00:00
|
|
|
|
if (i != 2) // bit failed
|
2017-04-30 06:18:36 +00:00
|
|
|
|
continue;
|
2017-04-30 03:04:54 +00:00
|
|
|
|
// Shiny Bits of PID validated
|
|
|
|
|
var upper = s;
|
|
|
|
|
if ((upper >> 16 & 7) != (pid >> 16 & 7))
|
2017-04-30 06:18:36 +00:00
|
|
|
|
continue;
|
2017-04-30 03:04:54 +00:00
|
|
|
|
var lower = RNG.LCRNG.Prev(upper);
|
|
|
|
|
if ((lower >> 16 & 7) != (pid & 7))
|
2017-04-30 06:18:36 +00:00
|
|
|
|
continue;
|
2017-04-30 03:04:54 +00:00
|
|
|
|
|
2018-08-03 03:11:42 +00:00
|
|
|
|
var upid = (((pid & 0xFFFF) ^ pk.TID ^ pk.SID) & 0xFFF8) | ((upper >> 16) & 0x7);
|
2017-04-30 03:04:54 +00:00
|
|
|
|
if (upid != pid >> 16)
|
2017-04-30 06:18:36 +00:00
|
|
|
|
continue;
|
2017-04-30 03:04:54 +00:00
|
|
|
|
|
|
|
|
|
s = RNG.LCRNG.Reverse(lower, 2); // unroll one final time to get the origin seed
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(ChainShiny, s);
|
2017-04-30 03:04:54 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-04-30 03:04:54 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2021-09-06 07:35:40 +00:00
|
|
|
|
public static IEnumerable<uint> GetCuteCharmSeeds(PKM pk)
|
|
|
|
|
{
|
2022-01-03 05:35:59 +00:00
|
|
|
|
Span<uint> IVs = stackalloc uint[6];
|
2021-09-06 07:35:40 +00:00
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
|
|
|
IVs[i] = (uint)pk.GetIV(i);
|
|
|
|
|
var bot = GetIVChunk(IVs, 0);
|
|
|
|
|
var top = GetIVChunk(IVs, 3);
|
|
|
|
|
|
|
|
|
|
return GetSeedsFromIVs(RNG.LCRNG, top, bot);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static bool GetBACDMatch(PKM pk, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
2017-04-30 06:18:36 +00:00
|
|
|
|
{
|
2017-06-18 01:37:19 +00:00
|
|
|
|
var bot = GetIVChunk(IVs, 0);
|
|
|
|
|
var top = GetIVChunk(IVs, 3);
|
|
|
|
|
var reg = GetSeedsFromIVs(RNG.LCRNG, top, bot);
|
2021-01-28 00:52:04 +00:00
|
|
|
|
PIDType type = BACD_U;
|
2017-04-30 06:18:36 +00:00
|
|
|
|
foreach (var seed in reg)
|
|
|
|
|
{
|
|
|
|
|
var B = seed;
|
|
|
|
|
var A = RNG.LCRNG.Prev(B);
|
2017-09-12 07:27:16 +00:00
|
|
|
|
var low = B >> 16;
|
2017-04-30 06:18:36 +00:00
|
|
|
|
|
2018-08-03 03:11:42 +00:00
|
|
|
|
var PID = (A & 0xFFFF0000) | low;
|
2017-04-30 06:18:36 +00:00
|
|
|
|
if (PID != pid)
|
|
|
|
|
{
|
2017-09-12 07:27:16 +00:00
|
|
|
|
uint idxor = (uint)(pk.TID ^ pk.SID);
|
2018-08-03 03:11:42 +00:00
|
|
|
|
bool isShiny = (idxor ^ PID >> 16 ^ (PID & 0xFFFF)) < 8;
|
2017-04-30 06:18:36 +00:00
|
|
|
|
if (!isShiny)
|
2017-05-01 05:11:51 +00:00
|
|
|
|
{
|
2017-09-12 07:27:16 +00:00
|
|
|
|
if (!pk.IsShiny) // check for nyx antishiny
|
|
|
|
|
{
|
|
|
|
|
if (!IsBACD_U_AX(idxor, pid, low, A, ref type))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else // check for force shiny pkm
|
|
|
|
|
{
|
|
|
|
|
if (!IsBACD_U_S(idxor, pid, low, ref A, ref type))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2017-05-01 05:11:51 +00:00
|
|
|
|
}
|
2017-09-12 07:27:16 +00:00
|
|
|
|
else if (!IsBACD_U_AX(idxor, pid, low, A, ref type))
|
2017-06-03 07:12:05 +00:00
|
|
|
|
{
|
|
|
|
|
if ((PID + 8 & 0xFFFFFFF8) != pid)
|
|
|
|
|
continue;
|
2021-01-28 00:52:04 +00:00
|
|
|
|
type = BACD_U_A;
|
2017-06-03 07:12:05 +00:00
|
|
|
|
}
|
2017-04-30 06:18:36 +00:00
|
|
|
|
}
|
|
|
|
|
var s = RNG.LCRNG.Prev(A);
|
|
|
|
|
|
2017-05-02 06:41:19 +00:00
|
|
|
|
// Check for prior Restricted seed
|
2017-04-30 06:18:36 +00:00
|
|
|
|
var sn = s;
|
|
|
|
|
for (int i = 0; i < 3; i++, sn = RNG.LCRNG.Prev(sn))
|
|
|
|
|
{
|
|
|
|
|
if ((sn & 0xFFFF0000) != 0)
|
|
|
|
|
continue;
|
2017-09-12 07:27:16 +00:00
|
|
|
|
// shift from unrestricted enum val to restricted enum val
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(--type, sn);
|
2017-04-30 06:18:36 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-05-02 06:41:19 +00:00
|
|
|
|
// no restricted seed found, thus unrestricted
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(type, s);
|
2017-04-30 06:18:36 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-04-30 06:18:36 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2017-06-25 00:20:15 +00:00
|
|
|
|
private static bool GetPokewalkerMatch(PKM pk, uint oldpid, out PIDIV pidiv)
|
|
|
|
|
{
|
2017-08-29 06:40:04 +00:00
|
|
|
|
// check surface compatibility
|
|
|
|
|
var mid = oldpid & 0x00FFFF00;
|
|
|
|
|
if (mid != 0 && mid != 0x00FFFF00) // not expected bits
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-06-25 00:20:15 +00:00
|
|
|
|
var nature = oldpid % 25;
|
2017-08-29 06:40:04 +00:00
|
|
|
|
if (nature == 24) // impossible nature
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
2017-06-25 00:20:15 +00:00
|
|
|
|
|
2019-05-16 00:14:48 +00:00
|
|
|
|
var gender = pk.Gender;
|
|
|
|
|
uint pid = PIDGenerator.GetPokeWalkerPID(pk.TID, pk.SID, nature, gender, pk.PersonalInfo.Gender);
|
2018-04-30 01:26:36 +00:00
|
|
|
|
|
2017-08-29 15:32:22 +00:00
|
|
|
|
if (pid != oldpid)
|
2019-05-16 00:14:48 +00:00
|
|
|
|
{
|
|
|
|
|
if (!(gender == 0 && IsAzurillEdgeCaseM(pk, nature, oldpid)))
|
|
|
|
|
return GetNonMatch(out pidiv);
|
|
|
|
|
}
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = PIDIV.Pokewalker;
|
2017-08-29 15:32:22 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2019-05-16 00:14:48 +00:00
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
private static bool IsAzurillEdgeCaseM(PKM pk, uint nature, uint oldpid)
|
|
|
|
|
{
|
|
|
|
|
// check for Azurill evolution edge case... 75% F-M is now 50% F-M; was this a F->M bend?
|
2020-12-29 06:22:52 +00:00
|
|
|
|
int species = pk.Species;
|
2021-01-05 01:31:43 +00:00
|
|
|
|
if (species is not ((int)Species.Marill or (int)Species.Azumarill))
|
2019-05-16 00:14:48 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const int AzurillGenderRatio = 0xBF;
|
|
|
|
|
var gender = PKX.GetGenderFromPIDAndRatio(pk.PID, AzurillGenderRatio);
|
|
|
|
|
if (gender != 1)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
var pid = PIDGenerator.GetPokeWalkerPID(pk.TID, pk.SID, nature, 1, AzurillGenderRatio);
|
|
|
|
|
return pid == oldpid;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static bool GetColoStarterMatch(PKM pk, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
|
2017-11-02 04:12:44 +00:00
|
|
|
|
{
|
2021-01-05 01:31:43 +00:00
|
|
|
|
if (pk.Version != (int)GameVersion.CXD || pk.Species is not ((int)Species.Espeon or (int)Species.Umbreon))
|
2017-11-02 04:12:44 +00:00
|
|
|
|
return GetNonMatch(out pidiv);
|
|
|
|
|
|
|
|
|
|
var iv1 = GetIVChunk(IVs, 0);
|
|
|
|
|
var iv2 = GetIVChunk(IVs, 3);
|
|
|
|
|
var xdc = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
|
|
|
|
foreach (var seed in xdc)
|
|
|
|
|
{
|
|
|
|
|
uint origin = seed;
|
|
|
|
|
if (!LockFinder.IsColoStarterValid(pk.Species, ref origin, pk.TID, pk.SID, pk.PID, iv1, iv2))
|
|
|
|
|
continue;
|
|
|
|
|
|
2021-01-28 00:52:04 +00:00
|
|
|
|
pidiv = new PIDIV(CXD_ColoStarter, origin);
|
2017-11-02 04:12:44 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return GetNonMatch(out pidiv);
|
|
|
|
|
}
|
2017-06-25 00:20:15 +00:00
|
|
|
|
|
2017-08-29 15:32:22 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns false and no <see cref="PIDIV"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="pidiv">Null</param>
|
|
|
|
|
/// <returns>False</returns>
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
private static bool GetNonMatch(out PIDIV pidiv)
|
|
|
|
|
{
|
PKHeX.Core Nullable cleanup (#2401)
* Handle some nullable cases
Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data)
Make some classes have explicit constructors instead of { } initialization
* Handle bits more obviously without null
* Make SaveFile.BAK explicitly readonly again
* merge constructor methods to have readonly fields
* Inline some properties
* More nullable handling
* Rearrange box actions
define straightforward classes to not have any null properties
* Make extrabyte reference array immutable
* Move tooltip creation to designer
* Rearrange some logic to reduce nesting
* Cache generated fonts
* Split mystery gift album purpose
* Handle more tooltips
* Disallow null setters
* Don't capture RNG object, only type enum
* Unify learnset objects
Now have readonly properties which are never null
don't new() empty learnsets (>800 Learnset objects no longer created,
total of 2400 objects since we also new() a move & level array)
optimize g1/2 reader for early abort case
* Access rewrite
Initialize blocks in a separate object, and get via that object
removes a couple hundred "might be null" warnings since blocks are now readonly getters
some block references have been relocated, but interfaces should expose all that's needed
put HoF6 controls in a groupbox, and disable
* Readonly personal data
* IVs non nullable for mystery gift
* Explicitly initialize forced encounter moves
* Make shadow objects readonly & non-null
Put murkrow fix in binary data resource, instead of on startup
* Assign dex form fetch on constructor
Fixes legality parsing edge cases
also handle cxd parse for valid; exit before exception is thrown in FrameGenerator
* Remove unnecessary null checks
* Keep empty value until init
SetPouch sets the value to an actual one during load, but whatever
* Readonly team lock data
* Readonly locks
Put locked encounters at bottom (favor unlocked)
* Mail readonly data / offset
Rearrange some call flow and pass defaults
Add fake classes for SaveDataEditor mocking
Always party size, no need to check twice in stat editor
use a fake save file as initial data for savedata editor, and for
gamedata (wow i found a usage)
constrain eventwork editor to struct variable types (uint, int, etc),
thus preventing null assignment errors
2019-10-17 01:47:31 +00:00
|
|
|
|
pidiv = PIDIV.None;
|
2017-06-25 00:20:15 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2017-09-12 07:27:16 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks if the PID is a <see cref="PIDType.BACD_U_S"></see> match.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="idxor"><see cref="PKM.TID"/> ^ <see cref="PKM.SID"/></param>
|
|
|
|
|
/// <param name="pid">Full actual PID</param>
|
|
|
|
|
/// <param name="low">Low portion of PID (B)</param>
|
|
|
|
|
/// <param name="A">First RNG call</param>
|
|
|
|
|
/// <param name="type">PID Type is updated if successful</param>
|
|
|
|
|
/// <returns>True/False if the PID matches</returns>
|
|
|
|
|
/// <remarks>First RNG call is unrolled once if the PID is valid with this correlation</remarks>
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
private static bool IsBACD_U_S(uint idxor, uint pid, uint low, ref uint A, ref PIDType type)
|
|
|
|
|
{
|
|
|
|
|
// 0-Origin
|
|
|
|
|
// 1-PIDH
|
|
|
|
|
// 2-PIDL (ends up unused)
|
|
|
|
|
// 3-FORCEBITS
|
|
|
|
|
// PID = PIDH << 16 | (SID ^ TID ^ PIDH)
|
|
|
|
|
|
|
|
|
|
var X = RNG.LCRNG.Prev(A); // unroll once as there's 3 calls instead of 2
|
2018-08-03 03:11:42 +00:00
|
|
|
|
uint PID = (X & 0xFFFF0000) | (idxor ^ X >> 16);
|
2017-09-12 07:27:16 +00:00
|
|
|
|
PID &= 0xFFFFFFF8;
|
|
|
|
|
PID |= low & 0x7; // lowest 3 bits
|
|
|
|
|
|
|
|
|
|
if (PID != pid)
|
|
|
|
|
return false;
|
|
|
|
|
A = X; // keep the unrolled seed
|
2021-01-28 00:52:04 +00:00
|
|
|
|
type = BACD_U_S;
|
2017-09-12 07:27:16 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2017-09-12 07:27:16 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks if the PID is a <see cref="PIDType.BACD_U_AX"></see> match.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="idxor"><see cref="PKM.TID"/> ^ <see cref="PKM.SID"/></param>
|
|
|
|
|
/// <param name="pid">Full actual PID</param>
|
|
|
|
|
/// <param name="low">Low portion of PID (B)</param>
|
|
|
|
|
/// <param name="A">First RNG call</param>
|
|
|
|
|
/// <param name="type">PID Type is updated if successful</param>
|
|
|
|
|
/// <returns>True/False if the PID matches</returns>
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
|
private static bool IsBACD_U_AX(uint idxor, uint pid, uint low, uint A, ref PIDType type)
|
|
|
|
|
{
|
|
|
|
|
if ((pid & 0xFFFF) != low)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// 0-Origin
|
|
|
|
|
// 1-ushort rnd, do until >8
|
|
|
|
|
// 2-PIDL
|
|
|
|
|
|
|
|
|
|
uint rnd = A >> 16;
|
|
|
|
|
if (rnd < 8)
|
|
|
|
|
return false;
|
|
|
|
|
uint PID = ((rnd ^ idxor ^ low) << 16) | low;
|
|
|
|
|
if (PID != pid)
|
|
|
|
|
return false;
|
2021-01-28 00:52:04 +00:00
|
|
|
|
type = BACD_U_AX;
|
2017-09-12 07:27:16 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-04-29 23:22:32 +00:00
|
|
|
|
|
2018-08-03 03:11:42 +00:00
|
|
|
|
private static PIDIV AnalyzeGB(PKM _)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
2019-05-11 07:59:07 +00:00
|
|
|
|
// not implemented; correlation between IVs and RNG hasn't been converted to code.
|
PKHeX.Core Nullable cleanup (#2401)
* Handle some nullable cases
Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data)
Make some classes have explicit constructors instead of { } initialization
* Handle bits more obviously without null
* Make SaveFile.BAK explicitly readonly again
* merge constructor methods to have readonly fields
* Inline some properties
* More nullable handling
* Rearrange box actions
define straightforward classes to not have any null properties
* Make extrabyte reference array immutable
* Move tooltip creation to designer
* Rearrange some logic to reduce nesting
* Cache generated fonts
* Split mystery gift album purpose
* Handle more tooltips
* Disallow null setters
* Don't capture RNG object, only type enum
* Unify learnset objects
Now have readonly properties which are never null
don't new() empty learnsets (>800 Learnset objects no longer created,
total of 2400 objects since we also new() a move & level array)
optimize g1/2 reader for early abort case
* Access rewrite
Initialize blocks in a separate object, and get via that object
removes a couple hundred "might be null" warnings since blocks are now readonly getters
some block references have been relocated, but interfaces should expose all that's needed
put HoF6 controls in a groupbox, and disable
* Readonly personal data
* IVs non nullable for mystery gift
* Explicitly initialize forced encounter moves
* Make shadow objects readonly & non-null
Put murkrow fix in binary data resource, instead of on startup
* Assign dex form fetch on constructor
Fixes legality parsing edge cases
also handle cxd parse for valid; exit before exception is thrown in FrameGenerator
* Remove unnecessary null checks
* Keep empty value until init
SetPouch sets the value to an actual one during load, but whatever
* Readonly team lock data
* Readonly locks
Put locked encounters at bottom (favor unlocked)
* Mail readonly data / offset
Rearrange some call flow and pass defaults
Add fake classes for SaveDataEditor mocking
Always party size, no need to check twice in stat editor
use a fake save file as initial data for savedata editor, and for
gamedata (wow i found a usage)
constrain eventwork editor to struct variable types (uint, int, etc),
thus preventing null assignment errors
2019-10-17 01:47:31 +00:00
|
|
|
|
return PIDIV.None;
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
private static IEnumerable<uint> GetSeedsFromPID(RNG method, uint a, uint b)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
2017-08-04 04:35:41 +00:00
|
|
|
|
Debug.Assert(a >> 16 == 0);
|
|
|
|
|
Debug.Assert(b >> 16 == 0);
|
|
|
|
|
uint second = a << 16;
|
|
|
|
|
uint first = b << 16;
|
|
|
|
|
return method.RecoverLower16Bits(first, second);
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2019-06-20 02:15:06 +00:00
|
|
|
|
private static IEnumerable<uint> GetSeedsFromPIDSkip(RNG method, uint a, uint b)
|
|
|
|
|
{
|
|
|
|
|
Debug.Assert(a >> 16 == 0);
|
|
|
|
|
Debug.Assert(b >> 16 == 0);
|
|
|
|
|
uint third = a << 16;
|
|
|
|
|
uint first = b << 16;
|
|
|
|
|
return method.RecoverLower16BitsGap(first, third);
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
private static IEnumerable<uint> GetSeedsFromIVs(RNG method, uint a, uint b)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
2017-08-04 04:35:41 +00:00
|
|
|
|
Debug.Assert(a >> 15 == 0);
|
|
|
|
|
Debug.Assert(b >> 15 == 0);
|
|
|
|
|
uint second = a << 16;
|
|
|
|
|
uint first = b << 16;
|
|
|
|
|
var pairs = method.RecoverLower16Bits(first, second)
|
|
|
|
|
.Concat(method.RecoverLower16Bits(first, second ^ 0x80000000));
|
|
|
|
|
foreach (var z in pairs)
|
|
|
|
|
{
|
|
|
|
|
yield return z;
|
|
|
|
|
yield return z ^ 0x80000000; // sister bitflip
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2017-08-04 04:35:41 +00:00
|
|
|
|
public static IEnumerable<uint> GetSeedsFromIVsSkip(RNG method, uint rand1, uint rand3)
|
|
|
|
|
{
|
|
|
|
|
Debug.Assert(rand1 >> 15 == 0);
|
|
|
|
|
Debug.Assert(rand3 >> 15 == 0);
|
|
|
|
|
rand1 <<= 16;
|
|
|
|
|
rand3 <<= 16;
|
|
|
|
|
var seeds = method.RecoverLower16BitsGap(rand1, rand3)
|
|
|
|
|
.Concat(method.RecoverLower16BitsGap(rand1, rand3 ^ 0x80000000));
|
|
|
|
|
foreach (var z in seeds)
|
|
|
|
|
{
|
|
|
|
|
yield return z;
|
|
|
|
|
yield return z ^ 0x80000000; // sister bitflip
|
|
|
|
|
}
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2017-08-05 08:51:29 +00:00
|
|
|
|
public static IEnumerable<uint> GetSeedsFromPIDEuclid(RNG method, uint rand1, uint rand2)
|
|
|
|
|
{
|
|
|
|
|
return method.RecoverLower16BitsEuclid16(rand1 << 16, rand2 << 16);
|
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2017-08-05 08:51:29 +00:00
|
|
|
|
public static IEnumerable<uint> GetSeedsFromIVsEuclid(RNG method, uint rand1, uint rand2)
|
|
|
|
|
{
|
|
|
|
|
return method.RecoverLower16BitsEuclid15(rand1 << 16, rand2 << 16);
|
|
|
|
|
}
|
2017-04-29 23:22:32 +00:00
|
|
|
|
|
2019-01-06 09:26:18 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Generates IVs from 2 RNG calls using 15 bits of each to generate 6 IVs (5bits each).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="r1">First rand frame</param>
|
|
|
|
|
/// <param name="r2">Second rand frame</param>
|
|
|
|
|
/// <param name="IVs">IVs that should be the result</param>
|
|
|
|
|
/// <returns>IVs match random number IVs</returns>
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static bool IVsMatch(uint r1, uint r2, ReadOnlySpan<uint> IVs)
|
2019-01-06 09:26:18 +00:00
|
|
|
|
{
|
|
|
|
|
if (IVs[0] != (r1 & 31))
|
|
|
|
|
return false;
|
|
|
|
|
if (IVs[1] != (r1 >> 5 & 31))
|
|
|
|
|
return false;
|
|
|
|
|
if (IVs[2] != (r1 >> 10 & 31))
|
|
|
|
|
return false;
|
|
|
|
|
if (IVs[3] != (r2 & 31))
|
|
|
|
|
return false;
|
|
|
|
|
if (IVs[4] != (r2 >> 5 & 31))
|
|
|
|
|
return false;
|
|
|
|
|
if (IVs[5] != (r2 >> 10 & 31))
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-29 23:22:32 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Generates IVs from 2 RNG calls using 15 bits of each to generate 6 IVs (5bits each).
|
|
|
|
|
/// </summary>
|
2022-03-14 02:39:38 +00:00
|
|
|
|
/// <param name="result">Result storage</param>
|
2017-04-29 23:22:32 +00:00
|
|
|
|
/// <param name="r1">First rand frame</param>
|
|
|
|
|
/// <param name="r2">Second rand frame</param>
|
|
|
|
|
/// <returns>Array of 6 IVs</returns>
|
2022-03-14 02:39:38 +00:00
|
|
|
|
internal static void GetIVsInt32(Span<int> result, uint r1, uint r2)
|
2017-04-29 23:22:32 +00:00
|
|
|
|
{
|
2022-03-14 02:39:38 +00:00
|
|
|
|
result[5] = (int)r2 >> 10 & 31;
|
|
|
|
|
result[4] = (int)r2 >> 5 & 31;
|
|
|
|
|
result[3] = (int)r2 & 31;
|
|
|
|
|
result[2] = (int)r1 >> 10 & 31;
|
|
|
|
|
result[1] = (int)r1 >> 5 & 31;
|
|
|
|
|
result[0] = (int)r1 & 31;
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2022-01-03 05:35:59 +00:00
|
|
|
|
private static uint GetIVChunk(ReadOnlySpan<uint> IVs, int start)
|
2017-04-30 03:04:54 +00:00
|
|
|
|
{
|
|
|
|
|
uint val = 0;
|
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
|
val |= IVs[i+start] << (5*i);
|
|
|
|
|
return val;
|
|
|
|
|
}
|
2017-04-30 23:53:54 +00:00
|
|
|
|
|
2019-01-04 05:29:19 +00:00
|
|
|
|
public static IEnumerable<PIDIV> GetColoEReaderMatches(uint PID)
|
|
|
|
|
{
|
|
|
|
|
var top = PID >> 16;
|
|
|
|
|
var bot = (ushort)PID;
|
|
|
|
|
var xdc = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
|
|
|
|
foreach (var seed in xdc)
|
|
|
|
|
{
|
|
|
|
|
var B = RNG.XDRNG.Prev(seed);
|
|
|
|
|
var A = RNG.XDRNG.Prev(B);
|
|
|
|
|
|
|
|
|
|
var C = RNG.XDRNG.Advance(A, 7);
|
|
|
|
|
|
2021-01-28 00:52:04 +00:00
|
|
|
|
yield return new PIDIV(CXD, RNG.XDRNG.Prev(C));
|
2019-01-04 05:29:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
public static IEnumerable<PIDIV> GetPokeSpotSeeds(PKM pkm, int slot)
|
2017-04-30 23:53:54 +00:00
|
|
|
|
{
|
|
|
|
|
// Activate (rand % 3)
|
|
|
|
|
// Munchlax / Bonsly (10%/30%)
|
|
|
|
|
// Encounter Slot Value (ESV) = 50%/35%/15% rarity (0-49, 50-84, 85-99)
|
|
|
|
|
var pid = pkm.PID;
|
|
|
|
|
var top = pid >> 16;
|
|
|
|
|
var bot = pid & 0xFFFF;
|
2017-08-05 08:51:29 +00:00
|
|
|
|
var seeds = GetSeedsFromPIDEuclid(RNG.XDRNG, top, bot);
|
2017-04-30 23:53:54 +00:00
|
|
|
|
foreach (var seed in seeds)
|
|
|
|
|
{
|
|
|
|
|
// check for valid encounter slot info
|
2018-04-30 01:26:36 +00:00
|
|
|
|
if (!IsPokeSpotActivation(slot, seed, out uint s))
|
|
|
|
|
continue;
|
2021-01-28 00:52:04 +00:00
|
|
|
|
yield return new PIDIV(PokeSpot, s);
|
2017-04-30 23:53:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2018-04-30 01:26:36 +00:00
|
|
|
|
public static bool IsPokeSpotActivation(int slot, uint seed, out uint s)
|
|
|
|
|
{
|
|
|
|
|
s = seed;
|
|
|
|
|
var esv = (seed >> 16) % 100;
|
2018-06-06 02:08:31 +00:00
|
|
|
|
if (!IsPokeSpotSlotValid(slot, esv))
|
2018-04-30 01:26:36 +00:00
|
|
|
|
{
|
2018-06-06 02:08:31 +00:00
|
|
|
|
// todo
|
2018-04-30 01:26:36 +00:00
|
|
|
|
}
|
|
|
|
|
// check for valid activation
|
|
|
|
|
s = RNG.XDRNG.Prev(seed);
|
|
|
|
|
if ((s >> 16) % 3 != 0)
|
|
|
|
|
{
|
|
|
|
|
if ((s >> 16) % 100 < 10) // can't fail a munchlax/bonsly encounter check
|
2018-06-06 02:08:31 +00:00
|
|
|
|
{
|
|
|
|
|
// todo
|
|
|
|
|
}
|
2018-04-30 01:26:36 +00:00
|
|
|
|
s = RNG.XDRNG.Prev(s);
|
|
|
|
|
if ((s >> 16) % 3 != 0) // can't activate even if generous
|
2018-06-06 02:08:31 +00:00
|
|
|
|
{
|
|
|
|
|
// todo
|
|
|
|
|
}
|
2018-04-30 01:26:36 +00:00
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2021-01-02 01:08:49 +00:00
|
|
|
|
private static bool IsPokeSpotSlotValid(int slot, uint esv) => slot switch
|
2018-06-06 02:08:31 +00:00
|
|
|
|
{
|
2021-03-10 05:31:53 +00:00
|
|
|
|
0 => esv < 50 , // [0,50)
|
|
|
|
|
1 => esv - 50 < 35, // [50,85)
|
|
|
|
|
2 => esv >= 85, // [85,100)
|
2021-08-20 20:49:20 +00:00
|
|
|
|
_ => false,
|
2021-01-02 01:08:49 +00:00
|
|
|
|
};
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2021-08-01 05:38:04 +00:00
|
|
|
|
public static bool IsCompatible3(this PIDType val, IEncounterTemplate encounter, PKM pkm) => encounter switch
|
|
|
|
|
{
|
|
|
|
|
WC3 g => IsCompatible3Mystery(val, pkm, g),
|
|
|
|
|
EncounterStatic3 s => IsCompatible3Static(val, pkm, s),
|
|
|
|
|
EncounterSlot3 w => (w.Species == (int)Species.Unown ? MethodH_Unown : MethodH).Contains(val),
|
|
|
|
|
EncounterStaticShadow => val is CXD or CXDAnti,
|
|
|
|
|
EncounterSlot3PokeSpot => val == PokeSpot,
|
2021-08-20 20:49:20 +00:00
|
|
|
|
_ => val == None,
|
2021-08-01 05:38:04 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private static bool IsCompatible3Static(PIDType val, PKM pkm, EncounterStatic3 s) => pkm.Version switch
|
2017-06-03 07:12:05 +00:00
|
|
|
|
{
|
2021-08-01 05:38:04 +00:00
|
|
|
|
(int)GameVersion.CXD => val is CXD or CXD_ColoStarter or CXDAnti,
|
|
|
|
|
(int)GameVersion.E => val == Method_1, // no roamer glitch
|
|
|
|
|
(int)GameVersion.FR or (int) GameVersion.LG => s.Roaming ? val.IsRoamerPIDIV(pkm) : val == Method_1, // roamer glitch
|
|
|
|
|
_ => s.Roaming ? val.IsRoamerPIDIV(pkm) : MethodH14.Contains(val), // RS, roamer glitch && RSBox s/w emulation => method 4 available
|
|
|
|
|
};
|
|
|
|
|
|
2021-08-01 23:21:30 +00:00
|
|
|
|
private static bool IsCompatible3Mystery(PIDType val, PKM pkm, WC3 g) => val == g.Method || val switch
|
2021-08-01 05:38:04 +00:00
|
|
|
|
{
|
|
|
|
|
// forced shiny eggs, when hatched, can lose their detectable correlation.
|
2021-08-01 23:21:30 +00:00
|
|
|
|
None => (g.Method is BACD_R_S or BACD_U_S) && g.IsEgg && !pkm.IsEgg,
|
|
|
|
|
CXDAnti => g.Method == CXD && g.Shiny == Shiny.Never,
|
2021-08-20 20:49:20 +00:00
|
|
|
|
_ => false,
|
2021-08-01 23:21:30 +00:00
|
|
|
|
};
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2018-01-25 03:07:03 +00:00
|
|
|
|
private static bool IsRoamerPIDIV(this PIDType val, PKM pkm)
|
|
|
|
|
{
|
|
|
|
|
// Roamer PIDIV is always Method 1.
|
|
|
|
|
// M1 is checked before M1R. A M1R PIDIV can also be a M1 PIDIV, so check that collision.
|
2021-01-28 00:52:04 +00:00
|
|
|
|
if (Method_1_Roamer == val)
|
2018-01-25 03:07:03 +00:00
|
|
|
|
return true;
|
2021-01-28 00:52:04 +00:00
|
|
|
|
if (Method_1 != val)
|
2018-01-25 03:07:03 +00:00
|
|
|
|
return false;
|
2019-01-05 18:51:41 +00:00
|
|
|
|
|
|
|
|
|
// only 8 bits are stored instead of 32 -- 5 bits HP, 3 bits for ATK.
|
2021-01-28 00:52:04 +00:00
|
|
|
|
// return pkm.IV32 <= 0xFF;
|
|
|
|
|
return pkm.IV_DEF == 0 && pkm.IV_SPE == 0 && pkm.IV_SPA == 0 && pkm.IV_SPD == 0 && pkm.IV_ATK <= 7;
|
2018-01-25 03:07:03 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2021-08-01 23:21:30 +00:00
|
|
|
|
public static bool IsCompatible4(this PIDType val, IEncounterTemplate encounter, PKM pkm) => encounter switch
|
2017-06-03 07:12:05 +00:00
|
|
|
|
{
|
2021-08-01 23:21:30 +00:00
|
|
|
|
// Pokewalker can sometimes be confused with CuteCharm due to the PID creation routine. Double check if it is okay.
|
|
|
|
|
EncounterStatic4Pokewalker when val is CuteCharm => GetCuteCharmMatch(pkm, pkm.EncryptionConstant, out _) && IsCuteCharm4Valid(encounter, pkm),
|
|
|
|
|
EncounterStatic4Pokewalker => val == Pokewalker,
|
|
|
|
|
|
|
|
|
|
EncounterStatic4 {Species: (int)Species.Pichu} => val == Pokewalker,
|
|
|
|
|
EncounterStatic4 {Shiny: Shiny.Always} => val == ChainShiny,
|
|
|
|
|
EncounterStatic4 when val is CuteCharm => IsCuteCharm4Valid(encounter, pkm),
|
|
|
|
|
EncounterStatic4 => val == Method_1,
|
|
|
|
|
|
|
|
|
|
EncounterSlot4 w => val switch
|
2017-06-03 07:12:05 +00:00
|
|
|
|
{
|
2021-08-01 23:21:30 +00:00
|
|
|
|
// Chain shiny with Poké Radar is only possible in DPPt, in grass. Safari Zone does not allow using the Poké Radar
|
|
|
|
|
ChainShiny => pkm.IsShiny && !pkm.HGSS && (w.GroundTile & GroundTilePermission.Grass) != 0 && !Locations.IsSafariZoneLocation4(w.Location),
|
|
|
|
|
CuteCharm => IsCuteCharm4Valid(encounter, pkm),
|
2021-09-08 03:57:19 +00:00
|
|
|
|
_ => val == Method_1,
|
2021-08-01 23:21:30 +00:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
PGT => IsG4ManaphyPIDValid(val, pkm), // Manaphy is the only PGT in the database
|
|
|
|
|
PCD d when d.Gift.PK.PID != 1 => true, // Already matches PCD's fixed PID requirement
|
2021-08-20 20:49:20 +00:00
|
|
|
|
_ => val == None,
|
2021-08-01 23:21:30 +00:00
|
|
|
|
};
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2017-06-16 01:34:03 +00:00
|
|
|
|
private static bool IsG4ManaphyPIDValid(PIDType val, PKM pkm)
|
|
|
|
|
{
|
2017-07-06 00:47:48 +00:00
|
|
|
|
if (pkm.IsEgg)
|
|
|
|
|
{
|
|
|
|
|
if (pkm.IsShiny)
|
|
|
|
|
return false;
|
2021-01-28 00:52:04 +00:00
|
|
|
|
if (val == Method_1)
|
2017-07-06 00:47:48 +00:00
|
|
|
|
return true;
|
2021-01-28 00:52:04 +00:00
|
|
|
|
return val == G4MGAntiShiny && IsAntiShinyARNG();
|
2017-07-06 00:47:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-28 00:52:04 +00:00
|
|
|
|
if (val == Method_1)
|
2017-06-16 01:34:03 +00:00
|
|
|
|
return pkm.WasTradedEgg || !pkm.IsShiny; // can't be shiny on received game
|
2021-01-28 00:52:04 +00:00
|
|
|
|
return val == G4MGAntiShiny && (pkm.WasTradedEgg || IsAntiShinyARNG());
|
2017-07-06 00:47:48 +00:00
|
|
|
|
|
|
|
|
|
bool IsAntiShinyARNG()
|
|
|
|
|
{
|
|
|
|
|
var shinyPID = RNG.ARNG.Prev(pkm.PID);
|
|
|
|
|
return (pkm.TID ^ pkm.SID ^ (shinyPID & 0xFFFF) ^ (shinyPID >> 16)) < 8; // shiny proc
|
|
|
|
|
}
|
2017-06-16 01:34:03 +00:00
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2021-07-14 00:33:23 +00:00
|
|
|
|
private static bool IsCuteCharm4Valid(ISpeciesForm encounter, PKM pkm)
|
2017-10-13 06:18:43 +00:00
|
|
|
|
{
|
2020-12-25 20:30:26 +00:00
|
|
|
|
if (pkm.Species is (int)Species.Marill or (int)Species.Azumarill)
|
2018-08-03 03:11:42 +00:00
|
|
|
|
{
|
2017-10-13 06:18:43 +00:00
|
|
|
|
return !IsCuteCharmAzurillMale(pkm.PID) // recognized as not Azurill
|
2019-06-01 17:22:49 +00:00
|
|
|
|
|| encounter.Species == (int)Species.Azurill; // encounter must be male Azurill
|
2018-08-03 03:11:42 +00:00
|
|
|
|
}
|
2017-10-13 06:18:43 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2020-12-29 08:37:59 +00:00
|
|
|
|
private static bool IsCuteCharmAzurillMale(uint pid) => pid is >= 0xC8 and <= 0xE0;
|
2018-08-03 03:11:42 +00:00
|
|
|
|
|
2021-08-22 07:06:02 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// There are some edge cases when the gender ratio changes across evolutions.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static (int Species, int Gender) GetCuteCharmGenderSpecies(PKM pk, uint pid, int currentSpecies) => currentSpecies switch
|
|
|
|
|
{
|
|
|
|
|
// Nincada evo chain travels from M/F -> Genderless Shedinja
|
2021-12-27 02:22:41 +00:00
|
|
|
|
(int)Species.Shedinja => ((int)Species.Nincada, PKX.GetGenderFromPID((int)Species.Nincada, pid)),
|
2021-08-22 07:06:02 +00:00
|
|
|
|
|
|
|
|
|
// These evolved species cannot be encountered with cute charm.
|
|
|
|
|
// 100% fixed gender does not modify PID; override this with the encounter species for correct calculation.
|
|
|
|
|
// We can assume the re-mapped species's [gender ratio] is what was encountered.
|
|
|
|
|
(int)Species.Wormadam => ((int)Species.Burmy, 1),
|
|
|
|
|
(int)Species.Mothim => ((int)Species.Burmy, 0),
|
|
|
|
|
(int)Species.Vespiquen => ((int)Species.Combee, 1),
|
|
|
|
|
(int)Species.Gallade => ((int)Species.Kirlia, 0),
|
|
|
|
|
(int)Species.Froslass => ((int)Species.Snorunt, 1),
|
|
|
|
|
// Azurill & Marill/Azumarill collision
|
|
|
|
|
// Changed gender ratio (25% M -> 50% M) needs special treatment.
|
|
|
|
|
// Double check the encounter species with IsCuteCharm4Valid afterwards.
|
|
|
|
|
(int)Species.Marill or (int)Species.Azumarill when IsCuteCharmAzurillMale(pid) => ((int)Species.Azurill, 0),
|
|
|
|
|
|
|
|
|
|
// Future evolutions
|
|
|
|
|
(int)Species.Sylveon => ((int)Species.Eevee, pk.Gender),
|
|
|
|
|
|
|
|
|
|
_ => (currentSpecies, pk.Gender),
|
|
|
|
|
};
|
2017-06-03 07:12:05 +00:00
|
|
|
|
|
2021-01-28 00:52:04 +00:00
|
|
|
|
private static readonly PIDType[] MethodH = { Method_1, Method_2, Method_3, Method_4 };
|
|
|
|
|
private static readonly PIDType[] MethodH14 = { Method_1, Method_4 };
|
|
|
|
|
private static readonly PIDType[] MethodH_Unown = { Method_1_Unown, Method_2_Unown, Method_3_Unown, Method_4_Unown };
|
2017-04-29 23:22:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|