2018-11-17 01:42:50 +00:00
|
|
|
|
using System;
|
2020-05-25 03:00:32 +00:00
|
|
|
|
using System.Collections.Generic;
|
2018-11-17 01:42:50 +00:00
|
|
|
|
using System.Linq;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
using static PKHeX.Core.LegalityCheckStrings;
|
2019-05-11 17:12:14 +00:00
|
|
|
|
using static PKHeX.Core.CheckIdentifier;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
|
|
|
|
namespace PKHeX.Core
|
|
|
|
|
{
|
2018-07-02 02:17:37 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Verifies miscellaneous data including <see cref="PKM.FatefulEncounter"/> and minor values.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public sealed class MiscVerifier : Verifier
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2019-05-11 17:12:14 +00:00
|
|
|
|
protected override CheckIdentifier Identifier => Misc;
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2018-06-24 05:00:01 +00:00
|
|
|
|
public override void Verify(LegalityAnalysis data)
|
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
if (pkm.IsEgg)
|
|
|
|
|
{
|
|
|
|
|
VerifyMiscEggCommon(data);
|
|
|
|
|
|
|
|
|
|
if (pkm is IContestStats s && s.HasContestStats())
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEggContest, Egg));
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
|
|
|
|
switch (pkm)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2019-01-01 05:01:54 +00:00
|
|
|
|
case PK5 pk5 when pk5.PokeStarFame != 0 && pk5.IsEgg:
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEggShinyPokeStar, Egg));
|
2019-01-01 05:01:54 +00:00
|
|
|
|
break;
|
2018-07-27 02:34:27 +00:00
|
|
|
|
case PK4 pk4 when pk4.ShinyLeaf != 0:
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEggShinyLeaf, Egg));
|
2018-07-27 02:34:27 +00:00
|
|
|
|
break;
|
|
|
|
|
case PK4 pk4 when pk4.PokéathlonStat != 0:
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEggPokeathlon, Egg));
|
2018-07-27 02:34:27 +00:00
|
|
|
|
break;
|
|
|
|
|
case PK3 _ when pkm.Language != 1: // All Eggs are Japanese and flagged specially for localized string
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(string.Format(LOTLanguage, LanguageID.Japanese, (LanguageID)pkm.Language), Egg));
|
2018-07-27 02:34:27 +00:00
|
|
|
|
break;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-26 22:28:01 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
VerifyMiscMovePP(data);
|
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2020-05-25 03:00:32 +00:00
|
|
|
|
switch (pkm)
|
|
|
|
|
{
|
|
|
|
|
case PK7 pk7 when pk7.ResortEventStatus >= 20:
|
|
|
|
|
data.AddLine(GetInvalid(LTransferBad));
|
|
|
|
|
break;
|
|
|
|
|
case PB7 pb7:
|
|
|
|
|
VerifyBelugaStats(data, pb7);
|
|
|
|
|
break;
|
|
|
|
|
case PK8 pk8:
|
|
|
|
|
VerifySWSHStats(data, pk8);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pkm.Format >= 6)
|
|
|
|
|
VerifyFullness(data, pkm);
|
2018-08-28 03:44:26 +00:00
|
|
|
|
|
2018-06-24 05:00:01 +00:00
|
|
|
|
VerifyMiscFatefulEncounter(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void VerifyMiscG1(LegalityAnalysis data)
|
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
if (pkm.IsEgg)
|
|
|
|
|
{
|
|
|
|
|
VerifyMiscEggCommon(data);
|
|
|
|
|
if (pkm.PKRS_Cured || pkm.PKRS_Infected)
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEggPokerus, Egg));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(pkm is PK1 pk1))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
VerifyMiscG1Types(data, pk1);
|
|
|
|
|
VerifyMiscG1CatchRate(data, pk1);
|
|
|
|
|
}
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2018-06-24 05:00:01 +00:00
|
|
|
|
private void VerifyMiscG1Types(LegalityAnalysis data, PK1 pk1)
|
|
|
|
|
{
|
|
|
|
|
var Type_A = pk1.Type_A;
|
|
|
|
|
var Type_B = pk1.Type_B;
|
2019-05-11 17:12:14 +00:00
|
|
|
|
if (pk1.Species == (int)Species.Porygon)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2018-07-01 17:49:11 +00:00
|
|
|
|
// Can have any type combination of any species by using Conversion.
|
2019-05-11 17:12:14 +00:00
|
|
|
|
if (!GBRestrictions.TypeIDExists(Type_A))
|
2018-07-27 02:34:27 +00:00
|
|
|
|
{
|
2018-09-01 21:11:12 +00:00
|
|
|
|
data.AddLine(GetInvalid(LG1TypePorygonFail1));
|
2018-07-27 02:34:27 +00:00
|
|
|
|
}
|
2019-05-11 17:12:14 +00:00
|
|
|
|
if (!GBRestrictions.TypeIDExists(Type_B))
|
2018-07-27 02:34:27 +00:00
|
|
|
|
{
|
2018-09-01 21:11:12 +00:00
|
|
|
|
data.AddLine(GetInvalid(LG1TypePorygonFail2));
|
2018-07-27 02:34:27 +00:00
|
|
|
|
}
|
2018-07-01 17:49:11 +00:00
|
|
|
|
else // Both match a type, ensure a gen1 species has this combo
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
|
|
|
|
var TypesAB_Match = PersonalTable.RB.IsValidTypeCombination(Type_A, Type_B);
|
2018-09-01 21:11:12 +00:00
|
|
|
|
var result = TypesAB_Match ? GetValid(LG1TypeMatchPorygon) : GetInvalid(LG1TypePorygonFail);
|
2018-07-01 17:49:11 +00:00
|
|
|
|
data.AddLine(result);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // Types must match species types
|
|
|
|
|
{
|
|
|
|
|
var Type_A_Match = Type_A == PersonalTable.RB[pk1.Species].Type1;
|
|
|
|
|
var Type_B_Match = Type_B == PersonalTable.RB[pk1.Species].Type2;
|
|
|
|
|
|
2018-09-01 21:11:12 +00:00
|
|
|
|
var first = Type_A_Match ? GetValid(LG1TypeMatch1) : GetInvalid(LG1Type1Fail);
|
|
|
|
|
var second = Type_B_Match ? GetValid(LG1TypeMatch2) : GetInvalid(LG1Type2Fail);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
data.AddLine(first);
|
|
|
|
|
data.AddLine(second);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2018-06-24 05:00:01 +00:00
|
|
|
|
private void VerifyMiscG1CatchRate(LegalityAnalysis data, PK1 pk1)
|
|
|
|
|
{
|
2018-07-17 23:54:23 +00:00
|
|
|
|
var e = data.EncounterMatch;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
var catch_rate = pk1.Catch_Rate;
|
2018-08-28 03:44:26 +00:00
|
|
|
|
var result = pk1.TradebackStatus == TradebackType.Gen1_NotTradeback
|
|
|
|
|
? GetWasNotTradeback()
|
|
|
|
|
: GetWasTradeback();
|
|
|
|
|
data.AddLine(result);
|
|
|
|
|
|
|
|
|
|
CheckResult GetWasTradeback()
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2018-08-28 03:44:26 +00:00
|
|
|
|
if (catch_rate == 0 || Legal.HeldItems_GSC.Contains((ushort)catch_rate))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetValid(LG1CatchRateMatchTradeback);
|
2018-08-28 03:44:26 +00:00
|
|
|
|
if (pk1.TradebackStatus == TradebackType.WasTradeback)
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LG1CatchRateItem);
|
2018-08-28 03:44:26 +00:00
|
|
|
|
|
|
|
|
|
return GetWasNotTradeback();
|
|
|
|
|
}
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2018-08-28 03:44:26 +00:00
|
|
|
|
CheckResult GetWasNotTradeback()
|
|
|
|
|
{
|
2020-04-12 20:20:40 +00:00
|
|
|
|
if ((e is EncounterStatic s && s.Version == GameVersion.Stadium) || e is EncounterTradeCatchRate)
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetValid(LG1CatchRateMatchPrevious); // Encounters detected by the catch rate, cant be invalid if match this encounters
|
2018-10-10 23:32:02 +00:00
|
|
|
|
if ((pk1.Species == 149 && catch_rate == PersonalTable.Y[149].CatchRate) || (GBRestrictions.Species_NotAvailable_CatchRate.Contains(pk1.Species) && catch_rate == PersonalTable.RB[pk1.Species].CatchRate))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LG1CatchRateEvo);
|
2018-08-28 03:44:26 +00:00
|
|
|
|
if (!data.Info.EvoChainsAllGens[1].Any(c => RateMatchesEncounter(c.Species)))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(pk1.Gen1_NotTradeback ? LG1CatchRateChain : LG1CatchRateNone);
|
|
|
|
|
return GetValid(LG1CatchRateMatchPrevious);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2018-07-17 23:54:23 +00:00
|
|
|
|
|
|
|
|
|
bool RateMatchesEncounter(int species)
|
|
|
|
|
{
|
|
|
|
|
if (catch_rate == PersonalTable.RB[species].CatchRate)
|
|
|
|
|
return true;
|
|
|
|
|
if (catch_rate == PersonalTable.Y[species].CatchRate)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2018-07-01 17:49:11 +00:00
|
|
|
|
private static void VerifyMiscFatefulEncounter(LegalityAnalysis data)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
var EncounterMatch = data.EncounterMatch;
|
|
|
|
|
switch (EncounterMatch)
|
|
|
|
|
{
|
|
|
|
|
case WC3 w when w.Fateful:
|
|
|
|
|
if (w.IsEgg)
|
|
|
|
|
{
|
|
|
|
|
// Eggs hatched in RS clear the obedience flag!
|
2020-03-20 00:05:16 +00:00
|
|
|
|
// Hatching in Gen3 doesn't change the origin version.
|
2018-06-24 05:00:01 +00:00
|
|
|
|
if (pkm.Format != 3)
|
|
|
|
|
return; // possible hatched in either game, don't bother checking
|
2020-03-20 00:05:16 +00:00
|
|
|
|
if (pkm.Met_Location <= 087) // hatched in RS or Emerald
|
|
|
|
|
return; // possible hatched in either game, don't bother checking
|
2018-06-24 05:00:01 +00:00
|
|
|
|
// else, ensure fateful is active (via below)
|
|
|
|
|
}
|
|
|
|
|
VerifyFatefulIngameActive(data);
|
|
|
|
|
VerifyWC3Shiny(data, w);
|
|
|
|
|
return;
|
|
|
|
|
case WC3 w:
|
|
|
|
|
if (w.Version == GameVersion.XD)
|
|
|
|
|
return; // Can have either state
|
|
|
|
|
VerifyWC3Shiny(data, w);
|
|
|
|
|
break;
|
|
|
|
|
case MysteryGift g when g.Format != 3: // WC3
|
2018-07-28 05:26:27 +00:00
|
|
|
|
VerifyReceivability(data, g);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
VerifyFatefulMysteryGift(data, g);
|
|
|
|
|
return;
|
|
|
|
|
case EncounterStatic s when s.Fateful: // ingame fateful
|
2019-05-11 17:12:14 +00:00
|
|
|
|
case EncounterSlot x when x.Version == GameVersion.XD: // ingame pokespot
|
2018-06-24 05:00:01 +00:00
|
|
|
|
case EncounterTrade t when t.Fateful:
|
|
|
|
|
VerifyFatefulIngameActive(data);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (pkm.FatefulEncounter)
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LFatefulInvalid, Fateful));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2019-12-26 22:28:01 +00:00
|
|
|
|
private static void VerifyMiscMovePP(LegalityAnalysis data)
|
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
if (pkm.Move1_PP > pkm.GetMovePP(pkm.Move1, pkm.Move1_PPUps))
|
|
|
|
|
data.AddLine(GetInvalid(string.Format(LMovePPTooHigh_0, 1), Move));
|
|
|
|
|
if (pkm.Move2_PP > pkm.GetMovePP(pkm.Move2, pkm.Move2_PPUps))
|
|
|
|
|
data.AddLine(GetInvalid(string.Format(LMovePPTooHigh_0, 2), Move));
|
|
|
|
|
if (pkm.Move3_PP > pkm.GetMovePP(pkm.Move3, pkm.Move3_PPUps))
|
|
|
|
|
data.AddLine(GetInvalid(string.Format(LMovePPTooHigh_0, 3), Move));
|
|
|
|
|
if (pkm.Move4_PP > pkm.GetMovePP(pkm.Move4, pkm.Move4_PPUps))
|
|
|
|
|
data.AddLine(GetInvalid(string.Format(LMovePPTooHigh_0, 4), Move));
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-01 17:49:11 +00:00
|
|
|
|
private static void VerifyMiscEggCommon(LegalityAnalysis data)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
if (pkm.Move1_PPUps > 0 || pkm.Move2_PPUps > 0 || pkm.Move3_PPUps > 0 || pkm.Move4_PPUps > 0)
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEggPPUp, Egg));
|
2018-07-27 02:34:27 +00:00
|
|
|
|
if (pkm.Move1_PP != pkm.GetMovePP(pkm.Move1, 0) || pkm.Move2_PP != pkm.GetMovePP(pkm.Move2, 0) || pkm.Move3_PP != pkm.GetMovePP(pkm.Move3, 0) || pkm.Move4_PP != pkm.GetMovePP(pkm.Move4, 0))
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEggPP, Egg));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2018-08-02 03:39:20 +00:00
|
|
|
|
var EncounterMatch = data.EncounterOriginal;
|
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
|
|
|
|
var HatchCycles = EncounterMatch is EncounterStatic s ? s.EggCycles : 0;
|
|
|
|
|
if (HatchCycles == 0) // no value set
|
2018-06-24 05:00:01 +00:00
|
|
|
|
HatchCycles = pkm.PersonalInfo.HatchCycles;
|
2020-05-24 17:46:08 +00:00
|
|
|
|
if (pkm.OT_Friendship > HatchCycles)
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEggHatchCycles, Egg));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
|
|
|
|
if (pkm.Format >= 6 && EncounterMatch is EncounterEgg && !pkm.Moves.SequenceEqual(pkm.RelearnMoves))
|
|
|
|
|
{
|
|
|
|
|
var moves = string.Join(", ", LegalityAnalysis.GetMoveNames(pkm.Moves));
|
2018-09-01 21:11:12 +00:00
|
|
|
|
var msg = string.Format(LMoveFExpect_0, moves);
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(msg, Egg));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2019-12-21 22:51:52 +00:00
|
|
|
|
|
|
|
|
|
if (pkm is PK8 pk8)
|
|
|
|
|
{
|
|
|
|
|
if (pk8.HasAnyMoveRecordFlag())
|
|
|
|
|
data.AddLine(GetInvalid(LEggRelearnFlags, Egg));
|
|
|
|
|
if (pk8.StatNature != pk8.Nature)
|
|
|
|
|
data.AddLine(GetInvalid(LEggNature, Egg));
|
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2018-07-01 17:49:11 +00:00
|
|
|
|
private static void VerifyFatefulMysteryGift(LegalityAnalysis data, MysteryGift g)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
if (g is PGF p && p.IsShiny)
|
|
|
|
|
{
|
|
|
|
|
var Info = data.Info;
|
|
|
|
|
Info.PIDIV = MethodFinder.Analyze(pkm);
|
2019-05-11 03:46:49 +00:00
|
|
|
|
if (Info.PIDIV.Type != PIDType.G5MGShiny && pkm.Egg_Location != Locations.LinkTrade5)
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LPIDTypeMismatch, PID));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-13 07:50:31 +00:00
|
|
|
|
var result = pkm.FatefulEncounter != pkm.WasLink
|
2019-05-11 17:12:14 +00:00
|
|
|
|
? GetValid(LFatefulMystery, Fateful)
|
|
|
|
|
: GetInvalid(LFatefulMysteryMissing, Fateful);
|
2018-07-01 17:49:11 +00:00
|
|
|
|
data.AddLine(result);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2018-07-28 05:26:27 +00:00
|
|
|
|
private static void VerifyReceivability(LegalityAnalysis data, MysteryGift g)
|
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
switch (g)
|
|
|
|
|
{
|
2018-10-27 16:44:47 +00:00
|
|
|
|
case WC6 wc6 when !wc6.CanBeReceivedByVersion(pkm.Version) && !pkm.WasTradedEgg:
|
|
|
|
|
case WC7 wc7 when !wc7.CanBeReceivedByVersion(pkm.Version) && !pkm.WasTradedEgg:
|
2019-11-16 01:34:18 +00:00
|
|
|
|
case WC8 wc8 when !wc8.CanBeReceivedByVersion(pkm.Version) && !pkm.WasTradedEgg:
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEncGiftVersionNotDistributed, GameOrigin));
|
2018-07-28 05:26:27 +00:00
|
|
|
|
return;
|
|
|
|
|
case WC6 wc6 when wc6.RestrictLanguage != 0 && wc6.Language != wc6.RestrictLanguage:
|
2019-09-19 02:58:23 +00:00
|
|
|
|
data.AddLine(GetInvalid(string.Format(LOTLanguage, wc6.RestrictLanguage, pkm.Language), CheckIdentifier.Language));
|
2018-07-28 05:26:27 +00:00
|
|
|
|
return;
|
|
|
|
|
case WC7 wc7 when wc7.RestrictLanguage != 0 && wc7.Language != wc7.RestrictLanguage:
|
2019-09-19 02:58:23 +00:00
|
|
|
|
data.AddLine(GetInvalid(string.Format(LOTLanguage, wc7.RestrictLanguage, pkm.Language), CheckIdentifier.Language));
|
2018-07-28 05:26:27 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-01 17:49:11 +00:00
|
|
|
|
private static void VerifyWC3Shiny(LegalityAnalysis data, WC3 g3)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
|
|
|
|
// check for shiny locked gifts
|
|
|
|
|
if (!g3.Shiny.IsValid(data.pkm))
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEncGiftShinyMismatch, Fateful));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2018-07-01 17:49:11 +00:00
|
|
|
|
private static void VerifyFatefulIngameActive(LegalityAnalysis data)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
2018-07-01 17:49:11 +00:00
|
|
|
|
var result = pkm.FatefulEncounter
|
2019-05-11 17:12:14 +00:00
|
|
|
|
? GetValid(LFateful, Fateful)
|
|
|
|
|
: GetInvalid(LFatefulMissing, Fateful);
|
2018-07-01 17:49:11 +00:00
|
|
|
|
data.AddLine(result);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-20 22:18:59 +00:00
|
|
|
|
public void VerifyVersionEvolution(LegalityAnalysis data)
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
if (pkm.Format < 7 || data.EncounterMatch.Species == pkm.Species)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// No point using the evolution tree. Just handle certain species.
|
|
|
|
|
switch (pkm.Species)
|
|
|
|
|
{
|
2019-11-16 01:34:18 +00:00
|
|
|
|
case (int)Species.Lycanroc when (pkm.AltForm == 0 && Moon()) || (pkm.AltForm == 1 && Sun()):
|
|
|
|
|
case (int)Species.Solgaleo when Moon():
|
|
|
|
|
case (int)Species.Lunala when Sun():
|
|
|
|
|
bool Sun() => (pkm.Version & 1) == 0;
|
|
|
|
|
bool Moon() => (pkm.Version & 1) == 1;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
if (pkm.IsUntraded)
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LEvoTradeRequired, Evolution));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-17 01:42:50 +00:00
|
|
|
|
|
2020-05-25 03:00:32 +00:00
|
|
|
|
private static void VerifyFullness(LegalityAnalysis data, PKM pkm)
|
|
|
|
|
{
|
2020-05-31 20:29:13 +00:00
|
|
|
|
if (pkm.IsEgg)
|
2020-05-25 03:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (pkm.Fullness != 0)
|
|
|
|
|
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, 0), Encounter));
|
|
|
|
|
if (pkm.Enjoyment != 0)
|
|
|
|
|
data.AddLine(GetInvalid(string.Format(LMemoryStatEnjoyment, 0), Encounter));
|
2020-05-31 20:29:13 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pkm.Format >= 8)
|
|
|
|
|
{
|
|
|
|
|
if (pkm.Enjoyment != 0)
|
|
|
|
|
data.AddLine(GetInvalid(string.Format(LMemoryStatEnjoyment, 0), Encounter));
|
2020-05-25 03:00:32 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pkm.Format != 6 || !pkm.IsUntraded || pkm.XY)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// OR/AS PK6
|
|
|
|
|
if (pkm.Fullness == 0)
|
|
|
|
|
return;
|
|
|
|
|
if (pkm.Species != data.EncounterMatch.Species)
|
|
|
|
|
return; // evolved
|
|
|
|
|
|
|
|
|
|
if (Unfeedable.Contains(pkm.Species))
|
|
|
|
|
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, 0), Encounter));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static readonly HashSet<int> Unfeedable = new HashSet<int>
|
|
|
|
|
{
|
|
|
|
|
(int)Species.Metapod,
|
|
|
|
|
(int)Species.Kakuna,
|
|
|
|
|
(int)Species.Pineco,
|
|
|
|
|
(int)Species.Silcoon,
|
|
|
|
|
(int)Species.Cascoon,
|
|
|
|
|
(int)Species.Shedinja,
|
|
|
|
|
(int)Species.Spewpa,
|
|
|
|
|
};
|
|
|
|
|
|
2018-11-17 01:42:50 +00:00
|
|
|
|
private static void VerifyBelugaStats(LegalityAnalysis data, PB7 pb7)
|
|
|
|
|
{
|
2019-01-23 05:08:48 +00:00
|
|
|
|
// ReSharper disable once CompareOfFloatsByEqualityOperator -- THESE MUST MATCH EXACTLY
|
|
|
|
|
if (!IsCloseEnough(pb7.HeightAbsolute, pb7.CalcHeightAbsolute))
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LStatIncorrectHeight, Encounter));
|
2019-01-23 05:08:48 +00:00
|
|
|
|
// ReSharper disable once CompareOfFloatsByEqualityOperator -- THESE MUST MATCH EXACTLY
|
|
|
|
|
if (!IsCloseEnough(pb7.WeightAbsolute, pb7.CalcWeightAbsolute))
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LStatIncorrectWeight, Encounter));
|
2018-11-17 02:17:01 +00:00
|
|
|
|
if (pb7.Stat_CP != pb7.CalcCP && !IsStarter(pb7))
|
2019-05-11 17:12:14 +00:00
|
|
|
|
data.AddLine(GetInvalid(LStatIncorrectCP, Encounter));
|
2018-12-06 02:38:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-23 05:08:48 +00:00
|
|
|
|
private static bool IsCloseEnough(float a, float b)
|
|
|
|
|
{
|
|
|
|
|
var ia = BitConverter.ToInt32(BitConverter.GetBytes(a), 0);
|
|
|
|
|
var ib = BitConverter.ToInt32(BitConverter.GetBytes(b), 0);
|
2019-02-08 00:57:17 +00:00
|
|
|
|
return Math.Abs(ia - ib) <= 7;
|
2019-01-23 05:08:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-09 01:39:19 +00:00
|
|
|
|
private static bool IsStarter(PKM pb7) => (pb7.Species == (int)Species.Pikachu && pb7.AltForm == 8) || (pb7.Species == (int)Species.Eevee && pb7.AltForm == 1);
|
2019-11-16 01:34:18 +00:00
|
|
|
|
|
|
|
|
|
private void VerifySWSHStats(LegalityAnalysis data, PK8 pk8)
|
|
|
|
|
{
|
|
|
|
|
if (pk8.Favorite)
|
|
|
|
|
data.AddLine(GetInvalid(LFavoriteMarkingUnavailable, Encounter));
|
|
|
|
|
|
2020-06-19 23:51:15 +00:00
|
|
|
|
var sn = pk8.StatNature;
|
|
|
|
|
if (sn != pk8.Nature)
|
|
|
|
|
{
|
|
|
|
|
// Only allow Serious nature (0); disallow all other neutral natures.
|
|
|
|
|
if (sn != 0 && (sn > 24 || sn % 6 == 0))
|
|
|
|
|
data.AddLine(GetInvalid(LStatNatureInvalid));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var bv = pk8.BattleVersion;
|
|
|
|
|
if (bv != 0)
|
|
|
|
|
{
|
|
|
|
|
if (bv != (int)GameVersion.SW && bv != (int)GameVersion.SH || pk8.SWSH)
|
|
|
|
|
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool originGMax = data.EncounterMatch is IGigantamax g && g.CanGigantamax;
|
|
|
|
|
if (originGMax != pk8.CanGigantamax)
|
|
|
|
|
{
|
|
|
|
|
if (!Legal.CanEatMaxSoup.Contains(pk8.Species))
|
|
|
|
|
data.AddLine(GetInvalid(LStatGigantamaxInvalid));
|
|
|
|
|
}
|
2019-11-16 01:34:18 +00:00
|
|
|
|
|
|
|
|
|
if (pk8.DynamaxLevel != 0)
|
|
|
|
|
{
|
2020-05-16 22:15:41 +00:00
|
|
|
|
if (!pk8.CanHaveDynamaxLevel(pk8) || pk8.DynamaxLevel > 10)
|
2019-11-16 01:34:18 +00:00
|
|
|
|
data.AddLine(GetInvalid(LStatDynamaxInvalid));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PersonalInfo? pi = null;
|
|
|
|
|
for (int i = 0; i < 100; i++)
|
|
|
|
|
{
|
|
|
|
|
if (!pk8.GetMoveRecordFlag(i))
|
|
|
|
|
continue;
|
|
|
|
|
if (!(pi ??= pk8.PersonalInfo).TMHM[i + 100])
|
|
|
|
|
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, LegalityAnalysis.MoveStrings[Legal.TMHM_SWSH[i + 100]])));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// weight/height scalars can be legally 0 (1:65536) so don't bother checking
|
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|