2018-06-24 05:00:01 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using static PKHeX.Core.LegalityCheckStrings;
|
|
|
|
|
|
|
|
|
|
namespace PKHeX.Core
|
|
|
|
|
{
|
2018-07-02 02:17:37 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Verifies the transfer data for a <see cref="PKM"/> that has been irreversably transferred forward.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public sealed class TransferVerifier : Verifier
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
|
|
|
|
protected override CheckIdentifier Identifier => CheckIdentifier.Encounter;
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2018-06-24 05:00:01 +00:00
|
|
|
|
public override void Verify(LegalityAnalysis data)
|
|
|
|
|
{
|
2019-10-26 19:33:58 +00:00
|
|
|
|
throw new Exception("Don't call via this.");
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-04 03:13:30 +00:00
|
|
|
|
public void VerifyTransferLegalityG12(LegalityAnalysis data)
|
|
|
|
|
{
|
2020-12-30 23:30:50 +00:00
|
|
|
|
VerifyVCOTGender(data);
|
|
|
|
|
VerifyVCNatureEXP(data);
|
|
|
|
|
VerifyVCShinyXorIfShiny(data);
|
|
|
|
|
VerifyVCGeolocation(data);
|
2019-04-04 03:13:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-30 23:30:50 +00:00
|
|
|
|
private void VerifyVCOTGender(LegalityAnalysis data)
|
2020-12-02 00:55:10 +00:00
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
if (pkm.OT_Gender == 1 && pkm.Version != (int)GameVersion.C)
|
|
|
|
|
data.AddLine(GetInvalid(LG2OTGender));
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-30 23:30:50 +00:00
|
|
|
|
private void VerifyVCNatureEXP(LegalityAnalysis data)
|
2019-04-04 03:13:30 +00:00
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
var met = pkm.Met_Level;
|
|
|
|
|
|
|
|
|
|
if (met == 100) // check for precise match, can't receive EXP after transfer.
|
|
|
|
|
{
|
|
|
|
|
var nature = Experience.GetNatureVC(pkm.EXP);
|
|
|
|
|
if (nature != pkm.Nature)
|
|
|
|
|
data.AddLine(GetInvalid(LTransferNature));
|
2019-05-21 00:05:53 +00:00
|
|
|
|
return;
|
2019-04-04 03:13:30 +00:00
|
|
|
|
}
|
2019-05-21 00:05:53 +00:00
|
|
|
|
if (met <= 2) // Not enough EXP to have every nature -- check for exclusions!
|
2019-04-04 03:13:30 +00:00
|
|
|
|
{
|
|
|
|
|
var pi = pkm.PersonalInfo;
|
|
|
|
|
var growth = pi.EXPGrowth;
|
|
|
|
|
var nature = pkm.Nature;
|
2019-05-21 00:05:53 +00:00
|
|
|
|
bool valid = VerifyVCNature(growth, nature);
|
|
|
|
|
if (!valid)
|
2019-04-04 03:13:30 +00:00
|
|
|
|
data.AddLine(GetInvalid(LTransferNature));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-02 00:25:48 +00:00
|
|
|
|
private static bool VerifyVCNature(int growth, int nature) => growth switch
|
2019-04-04 03:13:30 +00:00
|
|
|
|
{
|
|
|
|
|
// exp % 25 with a limited amount of EXP does not allow for every nature
|
2021-01-02 00:25:48 +00:00
|
|
|
|
0 => (0x01FFFF03 & (1 << nature)) != 0, // MediumFast -- Can't be Brave, Adamant, Naughty, Bold, Docile, or Relaxed
|
|
|
|
|
4 => (0x001FFFC0 & (1 << nature)) != 0, // Fast -- Can't be Gentle, Sassy, Careful, Quirky, Hardy, Lonely, Brave, Adamant, Naughty, or Bold
|
|
|
|
|
5 => (0x01FFFCFF & (1 << nature)) != 0, // Slow -- Can't be Impish or Lax
|
2021-08-20 20:49:20 +00:00
|
|
|
|
_ => true,
|
2021-01-02 00:25:48 +00:00
|
|
|
|
};
|
2019-04-04 03:13:30 +00:00
|
|
|
|
|
2020-12-30 23:30:50 +00:00
|
|
|
|
private static void VerifyVCShinyXorIfShiny(LegalityAnalysis data)
|
2020-09-26 01:04:44 +00:00
|
|
|
|
{
|
|
|
|
|
// Star, not square. Requires transferring a shiny and having the initially random PID to already be a Star shiny.
|
|
|
|
|
// (15:65536, ~1:4096) odds on a given shiny transfer!
|
2021-01-05 01:31:56 +00:00
|
|
|
|
var xor = data.pkm.ShinyXor;
|
|
|
|
|
if (xor is <= 15 and not 0)
|
|
|
|
|
data.AddLine(Get(LEncStaticPIDShiny, ParseSettings.Gen7TransferStarPID, CheckIdentifier.PID));
|
2020-09-26 01:04:44 +00:00
|
|
|
|
}
|
2020-12-31 18:36:09 +00:00
|
|
|
|
|
2020-12-30 23:30:50 +00:00
|
|
|
|
private static void VerifyVCGeolocation(LegalityAnalysis data)
|
|
|
|
|
{
|
|
|
|
|
if (data.pkm is not PK7 pk7)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// VC Games were region locked to the Console, meaning not all language games are available.
|
|
|
|
|
var within = Locale3DS.IsRegionLockedLanguageValidVC(pk7.ConsoleRegion, pk7.Language);
|
|
|
|
|
if (!within)
|
|
|
|
|
data.AddLine(GetInvalid(string.Format(LOTLanguage, $"!={(LanguageID)pk7.Language}", ((LanguageID)pk7.Language).ToString()), CheckIdentifier.Language));
|
|
|
|
|
}
|
2020-09-26 01:04:44 +00:00
|
|
|
|
|
2018-06-24 05:00:01 +00:00
|
|
|
|
public void VerifyTransferLegalityG3(LegalityAnalysis data)
|
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
2019-05-10 05:47:37 +00:00
|
|
|
|
if (pkm.Format == 4) // Pal Park (3->4)
|
|
|
|
|
{
|
2019-05-11 03:46:49 +00:00
|
|
|
|
if (pkm.Met_Location != Locations.Transfer3)
|
2019-05-10 05:47:37 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEggLocationPalPark));
|
|
|
|
|
}
|
|
|
|
|
else // Transporter (4->5)
|
|
|
|
|
{
|
2019-05-11 03:46:49 +00:00
|
|
|
|
if (pkm.Met_Location != Locations.Transfer4)
|
2019-05-10 05:47:37 +00:00
|
|
|
|
data.AddLine(GetInvalid(LTransferEggLocationTransporter));
|
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void VerifyTransferLegalityG4(LegalityAnalysis data)
|
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
int loc = pkm.Met_Location;
|
2019-05-11 03:46:49 +00:00
|
|
|
|
if (loc == Locations.Transfer4)
|
2018-07-02 02:17:37 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2019-05-10 05:47:37 +00:00
|
|
|
|
// Crown met location must be present if transferred via lock capsule
|
2018-07-02 02:17:37 +00:00
|
|
|
|
switch (pkm.Species)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Celebi:
|
2021-01-05 01:31:43 +00:00
|
|
|
|
if (loc is not (Locations.Transfer4_CelebiUnused or Locations.Transfer4_CelebiUsed))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
data.AddLine(GetInvalid(LTransferMet));
|
2018-07-02 02:17:37 +00:00
|
|
|
|
break;
|
2020-12-25 18:58:33 +00:00
|
|
|
|
case (int)Species.Raikou or (int)Species.Entei or (int)Species.Suicune:
|
2021-01-05 01:31:43 +00:00
|
|
|
|
if (loc is not (Locations.Transfer4_CrownUnused or Locations.Transfer4_CrownUsed))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
data.AddLine(GetInvalid(LTransferMet));
|
2018-07-02 02:17:37 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
2018-09-01 21:11:12 +00:00
|
|
|
|
data.AddLine(GetInvalid(LTransferEggLocationTransporter));
|
2018-07-02 02:17:37 +00:00
|
|
|
|
break;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-25 06:28:05 +00:00
|
|
|
|
public void VerifyTransferLegalityG8(LegalityAnalysis data)
|
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
2022-02-05 01:35:15 +00:00
|
|
|
|
if (pkm is PA8 pa8)
|
|
|
|
|
{
|
|
|
|
|
VerifyTransferLegalityG8a(data, pa8);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-11-20 02:23:49 +00:00
|
|
|
|
if (pkm is PB8 pb8)
|
|
|
|
|
{
|
2022-02-05 01:35:15 +00:00
|
|
|
|
VerifyTransferLegalityG8b(data, pb8);
|
2021-11-20 02:23:49 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PK8
|
2019-09-25 06:28:05 +00:00
|
|
|
|
int species = pkm.Species;
|
2020-12-11 04:42:30 +00:00
|
|
|
|
var pi = (PersonalInfoSWSH)PersonalTable.SWSH.GetFormEntry(species, pkm.Form);
|
2022-02-05 01:35:15 +00:00
|
|
|
|
if (!pi.IsPresentInGame || pkm.BDSP || pkm.LA) // Can't transfer
|
2020-02-12 02:35:54 +00:00
|
|
|
|
{
|
2019-09-25 06:28:05 +00:00
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
2020-11-12 05:27:33 +00:00
|
|
|
|
return;
|
2020-02-12 02:35:54 +00:00
|
|
|
|
}
|
2020-11-12 05:27:33 +00:00
|
|
|
|
|
|
|
|
|
var enc = data.EncounterMatch;
|
2020-12-22 06:33:48 +00:00
|
|
|
|
if (enc.Version == GameVersion.GO || enc is WC8 {IsHOMEGift: true})
|
2020-11-12 05:22:13 +00:00
|
|
|
|
{
|
|
|
|
|
VerifyHOMETracker(data, pkm);
|
|
|
|
|
}
|
2020-11-12 05:27:33 +00:00
|
|
|
|
else if (enc.Generation < 8 && pkm.Format >= 8)
|
2020-02-12 02:35:54 +00:00
|
|
|
|
{
|
2021-04-17 20:24:57 +00:00
|
|
|
|
if (enc is EncounterStatic7 {IsTotem: true} s)
|
2020-02-12 02:35:54 +00:00
|
|
|
|
{
|
2020-11-12 05:22:13 +00:00
|
|
|
|
if (Legal.Totem_NoTransfer.Contains(s.Species))
|
2020-02-12 02:35:54 +00:00
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
2020-12-11 04:42:30 +00:00
|
|
|
|
if (pkm.Form != FormInfo.GetTotemBaseForm(s.Species, s.Form))
|
2020-02-12 02:35:54 +00:00
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
|
|
|
|
}
|
2020-02-13 02:10:24 +00:00
|
|
|
|
|
2020-11-12 05:22:13 +00:00
|
|
|
|
VerifyHOMETransfer(data, pkm);
|
|
|
|
|
VerifyHOMETracker(data, pkm);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-05 01:35:15 +00:00
|
|
|
|
private void VerifyTransferLegalityG8a(LegalityAnalysis data, PA8 pk)
|
|
|
|
|
{
|
|
|
|
|
// Tracker value is set via Transfer across HOME.
|
|
|
|
|
// No HOME access yet.
|
|
|
|
|
if (pk is IHomeTrack { Tracker: not 0 })
|
|
|
|
|
data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
|
|
|
|
|
|
|
|
|
|
var pi = (PersonalInfoLA)PersonalTable.LA.GetFormEntry(pk.Species, pk.Form);
|
|
|
|
|
if (!pi.IsPresentInGame || !pk.LA) // Can't transfer
|
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
|
|
|
|
}
|
2020-11-12 05:22:13 +00:00
|
|
|
|
|
2021-11-20 02:23:49 +00:00
|
|
|
|
// bdsp logic
|
2022-02-05 01:35:15 +00:00
|
|
|
|
private void VerifyTransferLegalityG8b(LegalityAnalysis data, PB8 pk)
|
2021-11-20 02:23:49 +00:00
|
|
|
|
{
|
|
|
|
|
// Tracker value is set via Transfer across HOME.
|
|
|
|
|
// No HOME access yet.
|
|
|
|
|
if (pk is IHomeTrack { Tracker: not 0 })
|
|
|
|
|
data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
|
|
|
|
|
|
|
|
|
|
var pi = (PersonalInfoBDSP)PersonalTable.BDSP.GetFormEntry(pk.Species, pk.Form);
|
|
|
|
|
if (!pi.IsPresentInGame || !pk.BDSP) // Can't transfer
|
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-12 05:22:13 +00:00
|
|
|
|
private void VerifyHOMETransfer(LegalityAnalysis data, PKM pkm)
|
|
|
|
|
{
|
2020-12-22 01:12:39 +00:00
|
|
|
|
if (pkm is not IScaledSize s)
|
2020-11-12 05:22:13 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (pkm.LGPE || pkm.GO)
|
|
|
|
|
return; // can have any size value
|
|
|
|
|
if (s.HeightScalar != 0)
|
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
|
|
|
|
if (s.WeightScalar != 0)
|
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void VerifyHOMETracker(LegalityAnalysis data, PKM pkm)
|
|
|
|
|
{
|
|
|
|
|
// Tracker value is set via Transfer across HOME.
|
|
|
|
|
// Can't validate the actual values (we aren't the server), so we can only check against zero.
|
2020-12-22 06:33:48 +00:00
|
|
|
|
if (pkm is IHomeTrack {Tracker: 0})
|
2020-11-12 05:22:13 +00:00
|
|
|
|
{
|
|
|
|
|
data.AddLine(Get(LTransferTrackerMissing, ParseSettings.Gen8TransferTrackerNotPresent));
|
|
|
|
|
// To the reader: It seems like the best course of action for setting a tracker is:
|
|
|
|
|
// - Transfer a 0-Tracker pkm to HOME to get assigned a valid Tracker
|
|
|
|
|
// - Don't make one up.
|
2020-02-12 02:35:54 +00:00
|
|
|
|
}
|
2019-09-25 06:28:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-14 00:33:23 +00:00
|
|
|
|
public IEnumerable<CheckResult> VerifyVCEncounter(PKM pkm, IEncounterTemplate encounter, ILocation transfer, IList<CheckMoveResult> Moves)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
|
|
|
|
if (pkm.Met_Location != transfer.Location)
|
2018-09-01 21:11:12 +00:00
|
|
|
|
yield return GetInvalid(LTransferMetLocation);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
if (pkm.Egg_Location != transfer.EggLocation)
|
2018-09-01 21:11:12 +00:00
|
|
|
|
yield return GetInvalid(LEggLocationNone);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
|
|
|
|
// Flag Moves that cannot be transferred
|
2020-12-22 06:33:48 +00:00
|
|
|
|
if (encounter is EncounterStatic2Odd {Version: GameVersion.C, EggLocation: 256}) // Dizzy Punch Gifts
|
2018-07-02 02:17:37 +00:00
|
|
|
|
FlagIncompatibleTransferMove(pkm, Moves, 146, 2); // can't have Dizzy Punch at all
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2021-08-20 22:59:54 +00:00
|
|
|
|
bool checkShiny = pkm.VC2 || (pkm.VC1 && GBRestrictions.IsTimeCapsuleTransferred(pkm, Moves, encounter).WasTimeCapsuleTransferred());
|
2018-06-24 05:00:01 +00:00
|
|
|
|
if (!checkShiny)
|
|
|
|
|
yield break;
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2018-06-24 05:00:01 +00:00
|
|
|
|
if (pkm.Gender == 1) // female
|
|
|
|
|
{
|
|
|
|
|
if (pkm.PersonalInfo.Gender == 31 && pkm.IsShiny) // impossible gender-shiny
|
2018-09-01 21:11:12 +00:00
|
|
|
|
yield return GetInvalid(LEncStaticPIDShiny, CheckIdentifier.PID);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2019-06-01 17:22:49 +00:00
|
|
|
|
else if (pkm.Species == (int)Species.Unown)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2021-01-05 01:31:43 +00:00
|
|
|
|
if (pkm.Form is not (8 or 21) && pkm.IsShiny) // impossibly form-shiny (not I or V)
|
2018-09-01 21:11:12 +00:00
|
|
|
|
yield return GetInvalid(LEncStaticPIDShiny, CheckIdentifier.PID);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-02 02:17:37 +00:00
|
|
|
|
|
|
|
|
|
private static void FlagIncompatibleTransferMove(PKM pkm, IList<CheckMoveResult> Moves, int move, int gen)
|
|
|
|
|
{
|
|
|
|
|
int index = Array.IndexOf(pkm.Moves, move);
|
|
|
|
|
if (index < 0)
|
|
|
|
|
return; // doesn't have move
|
|
|
|
|
|
|
|
|
|
var chk = Moves[index];
|
|
|
|
|
if (chk.Generation == gen) // not obtained from a future gen
|
2021-01-09 21:05:56 +00:00
|
|
|
|
Moves[index] = new CheckMoveResult(chk.Source, chk.Generation, Severity.Invalid, LTransferMove, CheckIdentifier.CurrentMove);
|
2018-07-02 02:17:37 +00:00
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|