Handle gen4 manaphy egg gift pid checks correctly

ty lincoln for bringing this to my attention and explaining the possible actions & outcomes

game checks an un-updated ID32 (original recipient, different if traded hatcher), not "oops Ranger is no longer stored"
This commit is contained in:
Kurt 2023-12-01 22:33:08 -08:00
parent dcb72b0993
commit a71597f3a8
2 changed files with 36 additions and 27 deletions

View file

@ -334,22 +334,39 @@ public static class MethodFinder
private static bool GetMG4Match(Span<uint> seeds, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
{
uint mg4Rev = ARNG.Prev(pid);
var currentPSV = getPSV(pid);
pid = ARNG.Prev(pid);
var originalPSV = getPSV(pid);
// ARNG shiny value must be different from the original shiny
// if we have a multi-rerolled PID, each re-roll must be from the same shiny value
if (originalPSV == currentPSV)
return GetNonMatch(out pidiv);
var count = LCRNGReversal.GetSeeds(seeds, mg4Rev << 16, mg4Rev & 0xFFFF0000);
var mg4 = seeds[..count];
foreach (var seed in mg4)
// ARNG can happen at most 3 times (checked all 2^32 seeds)
for (int i = 0; i < 3; i++)
{
var B = LCRNG.Next2(seed);
var C = LCRNG.Next(B);
var D = LCRNG.Next(C);
if (!IVsMatch(C >> 16, D >> 16, IVs))
continue;
var count = LCRNGReversal.GetSeeds(seeds, pid << 16, pid & 0xFFFF0000);
var mg4 = seeds[..count];
foreach (var seed in mg4)
{
var C = LCRNG.Next3(seed);
var D = LCRNG.Next(C);
if (!IVsMatch(C >> 16, D >> 16, IVs))
continue;
pidiv = new PIDIV(G4MGAntiShiny, seed);
return true;
pidiv = new PIDIV(G4MGAntiShiny, seed);
return true;
}
// Continue checking for multi-rerolls
pid = ARNG.Prev(pid);
var prevPSV = getPSV(pid);
if (prevPSV != originalPSV)
break;
}
return GetNonMatch(out pidiv);
static uint getPSV(uint u32) => ((u32 >> 16) ^ (u32 & 0xFFFF)) >> 3;
}
private static bool GetG5MGShinyMatch(PKM pk, uint pid, out PIDIV pidiv)

View file

@ -322,25 +322,17 @@ public sealed class PGT : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I
private static bool IsG4ManaphyPIDValid(PIDType val, PKM pk)
{
// Unhatched: Can't trigger ARNG, so it must always be Method 1
if (pk.IsEgg)
{
if (pk.IsShiny)
return false;
if (val == PIDType.Method_1)
return true;
return val == PIDType.G4MGAntiShiny && IsAntiShinyARNG(pk);
}
return val == PIDType.Method_1;
// Hatching: Code checks if the TID/SID yield a shiny, and re-roll until not shiny.
// However, the TID/SID reference is stale (original OT, not hatching OT), so it's fallible.
// Hatched: Can't be shiny for an un-traded egg.
if (val == PIDType.Method_1)
return pk.WasTradedEgg || !pk.IsShiny; // can't be shiny on received game
return val == PIDType.G4MGAntiShiny && (pk.WasTradedEgg || IsAntiShinyARNG(pk));
return pk.WasTradedEgg || !pk.IsShiny;
static bool IsAntiShinyARNG(PKM pk)
{
var shinyPID = ARNG.Prev(pk.PID);
var tmp = pk.ID32 ^ shinyPID;
var xor = (ushort)(tmp ^ (tmp >> 16));
return xor < 8; // shiny proc
}
// Hatched when the egg was shiny: PID needs to be from the ARNG.
return val == PIDType.G4MGAntiShiny;
}
}