2022-06-26 06:08:28 +00:00
|
|
|
using System;
|
2018-06-24 05:00:01 +00:00
|
|
|
using static PKHeX.Core.LegalityCheckStrings;
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
namespace PKHeX.Core;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Verifies the transfer data for a <see cref="PKM"/> that has been irreversibly transferred forward.
|
|
|
|
/// </summary>
|
|
|
|
public sealed class TransferVerifier : Verifier
|
2018-06-24 05:00:01 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
protected override CheckIdentifier Identifier => CheckIdentifier.Encounter;
|
|
|
|
|
|
|
|
public override void Verify(LegalityAnalysis data)
|
2018-06-24 05:00:01 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
throw new Exception("Don't call via this.");
|
|
|
|
}
|
2018-07-27 02:34:27 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
public void VerifyTransferLegalityG12(LegalityAnalysis data)
|
|
|
|
{
|
|
|
|
VerifyVCOTGender(data);
|
|
|
|
VerifyVCNatureEXP(data);
|
|
|
|
VerifyVCShinyXorIfShiny(data);
|
|
|
|
VerifyVCGeolocation(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void VerifyVCOTGender(LegalityAnalysis data)
|
|
|
|
{
|
|
|
|
var pk = data.Entity;
|
|
|
|
if (pk.OT_Gender == 1 && pk.Version != (int)GameVersion.C)
|
|
|
|
data.AddLine(GetInvalid(LG2OTGender));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void VerifyVCNatureEXP(LegalityAnalysis data)
|
|
|
|
{
|
|
|
|
var pk = data.Entity;
|
|
|
|
var met = pk.Met_Level;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
if (met == 100) // check for precise match, can't receive EXP after transfer.
|
2019-04-04 03:13:30 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
var nature = Experience.GetNatureVC(pk.EXP);
|
|
|
|
if (nature != pk.Nature)
|
|
|
|
data.AddLine(GetInvalid(LTransferNature));
|
|
|
|
return;
|
2019-04-04 03:13:30 +00:00
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
if (met <= 2) // Not enough EXP to have every nature -- check for exclusions!
|
2020-12-02 00:55:10 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
var pi = pk.PersonalInfo;
|
|
|
|
var growth = pi.EXPGrowth;
|
|
|
|
var nature = pk.Nature;
|
|
|
|
bool valid = VerifyVCNature(growth, nature);
|
|
|
|
if (!valid)
|
|
|
|
data.AddLine(GetInvalid(LTransferNature));
|
2020-12-02 00:55:10 +00:00
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
}
|
2020-12-02 00:55:10 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
private static bool VerifyVCNature(int growth, int nature) => growth switch
|
|
|
|
{
|
|
|
|
// exp % 25 with a limited amount of EXP does not allow for every nature
|
|
|
|
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
|
|
|
|
_ => true,
|
|
|
|
};
|
|
|
|
|
|
|
|
private static void VerifyVCShinyXorIfShiny(LegalityAnalysis data)
|
|
|
|
{
|
|
|
|
// 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!
|
|
|
|
var xor = data.Entity.ShinyXor;
|
|
|
|
if (xor is <= 15 and not 0)
|
|
|
|
data.AddLine(Get(LEncStaticPIDShiny, ParseSettings.Gen7TransferStarPID, CheckIdentifier.PID));
|
|
|
|
}
|
2019-04-04 03:13:30 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
private static void VerifyVCGeolocation(LegalityAnalysis data)
|
|
|
|
{
|
|
|
|
if (data.Entity is not PK7 pk7)
|
|
|
|
return;
|
2019-04-04 03:13:30 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
// 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));
|
|
|
|
}
|
2019-04-04 03:13:30 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
public void VerifyTransferLegalityG3(LegalityAnalysis data)
|
|
|
|
{
|
|
|
|
var pk = data.Entity;
|
|
|
|
if (pk.Format == 4) // Pal Park (3->4)
|
2020-09-26 01:04:44 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
if (pk.Met_Location != Locations.Transfer3)
|
|
|
|
data.AddLine(GetInvalid(LEggLocationPalPark));
|
2020-09-26 01:04:44 +00:00
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
else // Transporter (4->5)
|
2020-12-30 23:30:50 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
if (pk.Met_Location != Locations.Transfer4)
|
|
|
|
data.AddLine(GetInvalid(LTransferEggLocationTransporter));
|
2020-12-30 23:30:50 +00:00
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
}
|
2020-09-26 01:04:44 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
public void VerifyTransferLegalityG4(LegalityAnalysis data)
|
|
|
|
{
|
|
|
|
var pk = data.Entity;
|
|
|
|
int loc = pk.Met_Location;
|
|
|
|
if (loc == Locations.Transfer4)
|
|
|
|
return;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
// Crown met location must be present if transferred via lock capsule
|
|
|
|
switch (pk.Species)
|
2018-06-24 05:00:01 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
case (int)Species.Celebi:
|
|
|
|
if (loc is not (Locations.Transfer4_CelebiUnused or Locations.Transfer4_CelebiUsed))
|
|
|
|
data.AddLine(GetInvalid(LTransferMet));
|
|
|
|
break;
|
|
|
|
case (int)Species.Raikou or (int)Species.Entei or (int)Species.Suicune:
|
|
|
|
if (loc is not (Locations.Transfer4_CrownUnused or Locations.Transfer4_CrownUsed))
|
|
|
|
data.AddLine(GetInvalid(LTransferMet));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
data.AddLine(GetInvalid(LTransferEggLocationTransporter));
|
|
|
|
break;
|
2018-06-24 05:00:01 +00:00
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
public void VerifyTransferLegalityG8(LegalityAnalysis data)
|
|
|
|
{
|
|
|
|
var pk = data.Entity;
|
|
|
|
var enc = data.EncounterMatch;
|
|
|
|
bool native = enc.Generation == 8 && pk.IsNative;
|
2023-07-08 21:51:45 +00:00
|
|
|
if (native && pk is PK8 pk8 && LocationsHOME.IsLocationSWSH(pk8.Met_Location))
|
|
|
|
native = false;
|
2022-06-18 18:04:24 +00:00
|
|
|
if (!native || IsHOMETrackerRequired(enc))
|
|
|
|
VerifyHOMETracker(data, pk);
|
2022-06-14 03:39:29 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
if (enc.Generation < 8)
|
|
|
|
{
|
|
|
|
VerifyHOMETransfer(data, pk);
|
|
|
|
// Check for impossible 7->8 transfers
|
|
|
|
if (enc is EncounterStatic7 { IsTotem: true } s)
|
2020-02-12 02:35:54 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
if (s.IsTotemNoTransfer)
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
|
|
|
else if (pk.Form != s.GetTotemBaseForm())
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
2020-11-12 05:22:13 +00:00
|
|
|
}
|
2022-02-05 01:35:15 +00:00
|
|
|
}
|
2020-11-12 05:22:13 +00:00
|
|
|
|
2023-02-08 06:30:33 +00:00
|
|
|
if (pk.Format >= 9)
|
|
|
|
{
|
|
|
|
VerifyTransferLegalityG9(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
// Starting in Generation 8, games have a selective amount of species/forms from prior games.
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
IPersonalTable pt = pk switch
|
2021-11-20 02:23:49 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
PA8 => PersonalTable.LA,
|
|
|
|
PB8 => PersonalTable.BDSP,
|
|
|
|
_ => PersonalTable.SWSH,
|
2022-06-14 03:39:29 +00:00
|
|
|
};
|
2022-06-18 18:04:24 +00:00
|
|
|
if (!pt.IsPresentInGame(pk.Species, pk.Form))
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
2022-11-25 01:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void VerifyTransferLegalityG9(LegalityAnalysis data)
|
|
|
|
{
|
|
|
|
var pk = data.Entity;
|
|
|
|
var pt = PersonalTable.SV;
|
|
|
|
if (!pt.IsPresentInGame(pk.Species, pk.Form))
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
2022-06-18 18:04:24 +00:00
|
|
|
}
|
2021-11-20 02:23:49 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
// Encounters that originate in HOME -> transfer to save data
|
|
|
|
private static bool IsHOMETrackerRequired(IEncounterTemplate enc) => enc switch
|
|
|
|
{
|
|
|
|
EncounterSlot8GO => true,
|
|
|
|
WC8 { IsHOMEGift: true } => true,
|
|
|
|
WB8 { IsHOMEGift: true } => true,
|
|
|
|
WA8 { IsHOMEGift: true } => true,
|
2023-06-04 01:19:16 +00:00
|
|
|
WC9 { IsHOMEGift: true } => true,
|
2022-06-18 18:04:24 +00:00
|
|
|
_ => enc.Generation < 8,
|
|
|
|
};
|
|
|
|
|
|
|
|
private void VerifyHOMETransfer(LegalityAnalysis data, PKM pk)
|
|
|
|
{
|
|
|
|
if (pk is not IScaledSize s)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (pk.LGPE || pk.GO)
|
|
|
|
return; // can have any size value
|
2023-06-04 01:19:16 +00:00
|
|
|
|
|
|
|
// HOME in 3.0.0 will actively re-roll (0,0) to non-zero values.
|
|
|
|
// Transfer between Gen8 can be done before HOME 3.0.0, so we can allow (0,0) or re-rolled.
|
|
|
|
if (pk.Context is not (EntityContext.Gen8 or EntityContext.Gen8a or EntityContext.Gen8b))
|
|
|
|
{
|
|
|
|
if (s is { HeightScalar: 0, WeightScalar: 0 } && !data.Info.EvoChainsAllGens.HasVisitedPLA)
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
}
|
2020-11-12 05:22:13 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
private void VerifyHOMETracker(LegalityAnalysis data, PKM pk)
|
|
|
|
{
|
|
|
|
// 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.
|
2023-06-04 01:19:16 +00:00
|
|
|
if (pk is IHomeTrack { HasTracker: false })
|
2020-11-12 05:22:13 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +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 pk to HOME to get assigned a valid Tracker
|
|
|
|
// - Don't make one up.
|
2019-09-25 06:28:05 +00:00
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
}
|
2019-09-25 06:28:05 +00:00
|
|
|
|
2023-08-12 23:01:16 +00:00
|
|
|
public void VerifyVCEncounter(PKM pk, IEncounterTemplate original, EncounterTransfer7 transfer, LegalityAnalysis data)
|
2022-06-18 18:04:24 +00:00
|
|
|
{
|
|
|
|
if (pk.Met_Location != transfer.Location)
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
data.AddLine(GetInvalid(LTransferMetLocation));
|
2022-05-31 04:43:52 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
var expecteEgg = pk is PB8 ? Locations.Default8bNone : transfer.EggLocation;
|
|
|
|
if (pk.Egg_Location != expecteEgg)
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
data.AddLine(GetInvalid(LEggLocationNone));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
// Flag Moves that cannot be transferred
|
2023-08-12 23:01:16 +00:00
|
|
|
if (original is EncounterStatic2 { DizzyPunchEgg: true}) // Dizzy Punch Gifts
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
FlagIncompatibleTransferMove(pk, data.Info.Moves, 146, 2); // can't have Dizzy Punch at all
|
2018-06-24 05:00:01 +00:00
|
|
|
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
bool checkShiny = pk.VC2 || (pk.VC1 && GBRestrictions.IsTimeCapsuleTransferred(pk, data.Info.Moves, original).WasTimeCapsuleTransferred());
|
2022-06-18 18:04:24 +00:00
|
|
|
if (!checkShiny)
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
return;
|
2018-07-27 02:34:27 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
if (pk.Gender == 1) // female
|
|
|
|
{
|
2023-01-22 04:02:33 +00:00
|
|
|
var enc = data.EncounterOriginal;
|
|
|
|
var pi = PersonalTable.USUM[enc.Species];
|
|
|
|
if (pi.Gender == 31 && pk.IsShiny) // impossible gender-shiny
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
data.AddLine(GetInvalid(LEncStaticPIDShiny, CheckIdentifier.PID));
|
2018-06-24 05:00:01 +00:00
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
else if (pk.Species == (int)Species.Unown)
|
2018-07-02 02:17:37 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
if (pk.Form is not (8 or 21) && pk.IsShiny) // impossibly form-shiny (not I or V)
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
data.AddLine(GetInvalid(LEncStaticPIDShiny, CheckIdentifier.PID));
|
2018-07-02 02:17:37 +00:00
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
private static void FlagIncompatibleTransferMove(PKM pk, Span<MoveResult> parse, ushort move, int gen)
|
2022-06-18 18:04:24 +00:00
|
|
|
{
|
2022-06-26 06:08:28 +00:00
|
|
|
int index = pk.GetMoveIndex(move);
|
2022-06-18 18:04:24 +00:00
|
|
|
if (index < 0)
|
|
|
|
return; // doesn't have move
|
|
|
|
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
if (parse[index].Generation == gen) // not obtained from a future gen
|
|
|
|
parse[index] = MoveResult.Unobtainable(0);
|
2022-06-18 18:04:24 +00:00
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
}
|