diff --git a/PKHeX.Core/Game/GameUtil.cs b/PKHeX.Core/Game/GameUtil.cs
index c824a3eb3..c3a4ec18c 100644
--- a/PKHeX.Core/Game/GameUtil.cs
+++ b/PKHeX.Core/Game/GameUtil.cs
@@ -172,6 +172,9 @@ public static bool Contains(this GameVersion g1, GameVersion g2)
return g1.ContainsFromLumped(g2);
}
+ ///
+ /// Checks if the version is the lump of the requested saved .
+ ///
public static bool ContainsFromLumped(this GameVersion lump, GameVersion version) => lump switch
{
RB => version is RD or BU or GN,
diff --git a/PKHeX.Core/MysteryGifts/PGT.cs b/PKHeX.Core/MysteryGifts/PGT.cs
index 8f117dba6..a389b43c0 100644
--- a/PKHeX.Core/MysteryGifts/PGT.cs
+++ b/PKHeX.Core/MysteryGifts/PGT.cs
@@ -95,7 +95,6 @@ private void EncryptPK()
public Accessory4 Accessory { get => (Accessory4)ItemSubID; set => ItemSubID = (int)value; }
public Backdrop4 Backdrop { get => (Backdrop4)ItemSubID; set => ItemSubID = (int)value; }
- public bool IsHatched => GiftType is Pokémon or PokémonMovie;
public override bool IsEgg { get => GiftType == PokémonEgg || IsManaphyEgg; set { if (value) { GiftType = PokémonEgg; PK.IsEgg = true; } } }
public bool IsManaphyEgg { get => GiftType == ManaphyEgg; set { if (value) GiftType = ManaphyEgg; } }
public override bool IsItem { get => GiftType == Item; set { if (value) GiftType = Item; } }
@@ -125,19 +124,27 @@ public override PK4 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (!IsEntity)
throw new ArgumentException(nameof(IsEntity));
- // template is already filled out, only minor mutations required
- PK4 pk4 = new((byte[])PK.Data.Clone()) { Sanity = 0 };
- if (!IsHatched && Detail == 0)
+ // Template is already filled out, only minor mutations required
+ byte[] clone = [.. PK.Data]; // disassociate from gift template
+ PK4 pk4 = new(clone) { Sanity = 0 }; // clear for the bad WORLD08 Lucario (0x0100)
+ var pi = pk4.PersonalInfo;
+
+ if (IsEgg)
{
+ // Only hits here for the Manaphy gift. No gift eggs (type 2) were officially distributed.
pk4.OriginalTrainerName = tr.OT;
pk4.TID16 = tr.TID16;
pk4.SID16 = tr.SID16;
pk4.OriginalTrainerGender = tr.Gender;
- pk4.Language = tr.Language;
- }
+ // Set Language and Version later for Manaphy, as we need to sanity check the input Trainer info.
+ // No need to worry about regular egg gifts, because again, none exist.
- var pi = pk4.PersonalInfo;
- pk4.OriginalTrainerFriendship = pk4.IsEgg ? pi.HatchCycles : pi.BaseFriendship;
+ pk4.OriginalTrainerFriendship = pi.HatchCycles;
+ }
+ else
+ {
+ pk4.OriginalTrainerFriendship = pi.BaseFriendship;
+ }
if (IsManaphyEgg)
{
@@ -226,7 +233,7 @@ private static bool TrySetManaphyFromIVs(PK4 pk4, EncounterCriteria criteria, IT
if (criteria.Shiny.IsShiny() != shiny)
continue;
- if (shiny || arng)
+ if (shiny || arng) // set even for ARNG to ensure was shiny for the original recipient
SetAsTradedManaphy(pk4, shiny);
pk4.PID = pid;
pk4.IV32 |= iv2 << 15 | iv1;
@@ -247,6 +254,8 @@ private static void SetAsTradedManaphy(PK4 pk4, bool shiny)
return;
}
+ // Force hatch, we want to retain the trainer IDs from the request rather than leave the egg unhatched.
+ pk4.IsEgg = false;
pk4.IsNicknamed = false;
pk4.Nickname = SpeciesName.GetSpeciesNameGeneration((ushort)Core.Species.Manaphy, pk4.Language, 4);
}
@@ -265,6 +274,7 @@ private void SetMetData(PK4 pk4, ITrainerInfo trainer, EncounterCriteria criteri
}
else
{
+ // Never reaches here because no egg PGTs were distributed besides Manaphy.
if (criteria.Shiny.IsShiny())
pk4.EggLocation = Locations.LinkTrade4;
else
@@ -289,8 +299,16 @@ private static void SetDefaultManaphyEggDetails(PK4 pk4, ITrainerInfo trainer)
pk4.Ability = (int)Core.Ability.Hydration;
pk4.FatefulEncounter = true;
pk4.Ball = (int)Core.Ball.Poke;
- pk4.Version = trainer.Version.IsValidSavedVersion() && GameVersion.Gen4.Contains(trainer.Version) ? trainer.Version : GameVersion.D;
- var lang = trainer.Language is < (int)LanguageID.Korean and not (int)LanguageID.Korean ? trainer.Language : (int)LanguageID.English;
+
+ // Manaphy is the only PGT gift egg! (and the only gift that needs a version to be set)
+ var version = trainer.Version;
+ if (!version.IsValidSavedVersion() || !GameVersion.Gen4.ContainsFromLumped(version))
+ version = GameVersion.D;
+ pk4.Version = version;
+
+ var lang = trainer.Language;
+ if ((uint)lang is >= (uint)LanguageID.Korean or (0 or (int)LanguageID.UNUSED_6))
+ lang = (int)LanguageID.English; // Don't allow Korean or unused languages
pk4.Language = lang;
pk4.IsEgg = true;
@@ -378,22 +396,39 @@ private void SetUnhatchedEggDetails(PK4 pk4)
pk4.EggMetDate = EncounterDate.GetDateNDS();
}
+ ///
+ /// Rough check to see if the matches the expected Ranger Manaphy details.
+ ///
public static bool IsRangerManaphy(PKM pk)
{
- if (pk.Language >= (int)LanguageID.Korean) // never korean
+ if (pk.Species is not (ushort)Core.Species.Manaphy)
+ return false;
+
+ // Never Korean or future language values; others (0, 6) will get flagged by other checks.
+ if (pk.Language >= (int)LanguageID.Korean)
return false;
var egg = pk.EggLocation;
- if (!pk.IsEgg) // Link Trade Egg or Ranger
+ if (!pk.IsEgg) // Link Trade Egg or Ranger must be present in the Egg Met field. Met location is hatch location or Transfer (Gen5+).
return egg is Locations.LinkTrade4 or Locations.Ranger4;
+
+ if (pk.Format != 4)
+ return false; // Can't be an Egg if it must be transferred.
+
+ // Logic below only applies to Eggs.
+ // While an egg, Egg Location must still be Ranger.
+ // If traded while an egg, the Met Location will be updated from 0 => Link Trade.
+ // When hatched, the Met Location will move to Egg Location, if non-zero (aka, Traded).
if (egg != Locations.Ranger4)
return false;
-
var met = pk.MetLocation;
return met is Locations.LinkTrade4 or 0;
}
- // Nothing is stored as a PGT besides Ranger Manaphy. Nothing should trigger these.
+ ///
+ /// Nothing should call these methods. Should be checked via the static method prior to yielding the match.
+ ///
+ /// objects will instead call their respective methods.
public override bool IsMatchExact(PKM pk, EvoCriteria evo) => false;
protected override bool IsMatchDeferred(PKM pk) => false;
protected override bool IsMatchPartial(PKM pk) => false;