2018-06-24 05:00:01 +00:00
|
|
|
|
using System;
|
2018-06-30 22:01:16 +00:00
|
|
|
|
using System.Collections.Generic;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
using static PKHeX.Core.LegalityCheckStrings;
|
|
|
|
|
|
|
|
|
|
namespace PKHeX.Core
|
|
|
|
|
{
|
2018-07-02 02:17:37 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Verifies the <see cref="PKM.AltForm"/> value.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public sealed class FormVerifier : Verifier
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
|
|
|
|
protected override CheckIdentifier Identifier => CheckIdentifier.Form;
|
|
|
|
|
|
|
|
|
|
public override void Verify(LegalityAnalysis data)
|
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
if (pkm.Format < 4)
|
|
|
|
|
return; // no forms exist
|
2018-06-30 22:01:16 +00:00
|
|
|
|
var result = VerifyForm(data);
|
|
|
|
|
data.AddLine(result);
|
2020-01-11 00:33:40 +00:00
|
|
|
|
|
|
|
|
|
if (pkm is IFormArgument f)
|
|
|
|
|
data.AddLine(VerifyFormArgument(data, f));
|
2018-06-30 22:01:16 +00:00
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2018-09-01 21:11:12 +00:00
|
|
|
|
private CheckResult VALID => GetValid(LFormValid);
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2018-06-30 22:01:16 +00:00
|
|
|
|
private CheckResult VerifyForm(LegalityAnalysis data)
|
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
var PersonalInfo = data.PersonalInfo;
|
|
|
|
|
|
|
|
|
|
int count = PersonalInfo.FormeCount;
|
|
|
|
|
if (count <= 1 && pkm.AltForm == 0)
|
2018-06-30 22:01:16 +00:00
|
|
|
|
return VALID; // no forms to check
|
|
|
|
|
|
|
|
|
|
var EncounterMatch = data.EncounterMatch;
|
|
|
|
|
var Info = data.Info;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
|
|
|
|
if (!PersonalInfo.IsFormeWithinRange(pkm.AltForm) && !FormConverter.IsValidOutOfBoundsForme(pkm.Species, pkm.AltForm, Info.Generation))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(string.Format(LFormInvalidRange, count - 1, pkm.AltForm));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
|
|
|
|
if (EncounterMatch is EncounterSlot w && w.Type == SlotType.FriendSafari)
|
2018-07-27 02:34:27 +00:00
|
|
|
|
{
|
2018-06-24 05:00:01 +00:00
|
|
|
|
VerifyFormFriendSafari(data);
|
2018-07-27 02:34:27 +00:00
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
else if (EncounterMatch is EncounterEgg)
|
|
|
|
|
{
|
|
|
|
|
if (FormConverter.IsTotemForm(pkm.Species, pkm.AltForm))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LFormInvalidGame);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (pkm.Species)
|
|
|
|
|
{
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Pikachu when Info.Generation == 6: // Cosplay
|
2018-06-24 05:00:01 +00:00
|
|
|
|
bool isStatic = EncounterMatch is EncounterStatic;
|
|
|
|
|
if (isStatic != (pkm.AltForm != 0))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(isStatic ? LFormPikachuCosplayInvalid : LFormPikachuCosplay);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
break;
|
2018-06-30 22:01:16 +00:00
|
|
|
|
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Pikachu when Info.Generation == 7: // Cap
|
2018-06-24 05:00:01 +00:00
|
|
|
|
bool IsValidPikachuCap()
|
|
|
|
|
{
|
2019-10-08 01:40:09 +00:00
|
|
|
|
return EncounterMatch switch
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2019-10-08 01:40:09 +00:00
|
|
|
|
WC7 wc7 => (wc7.Form == pkm.AltForm),
|
|
|
|
|
EncounterStatic s => (s.Form == pkm.AltForm),
|
|
|
|
|
_ => (pkm.AltForm == 0)
|
|
|
|
|
};
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!IsValidPikachuCap())
|
|
|
|
|
{
|
|
|
|
|
bool gift = EncounterMatch is WC7 g && g.Form != pkm.AltForm;
|
2018-09-01 21:11:12 +00:00
|
|
|
|
var msg = gift ? LFormPikachuEventInvalid : LFormInvalidGame;
|
2018-06-30 22:01:16 +00:00
|
|
|
|
return GetInvalid(msg);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Unown when Info.Generation == 2 && pkm.AltForm >= 26:
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(string.Format(LFormInvalidRange, "Z", pkm.AltForm == 26 ? "!" : "?"));
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Giratina when pkm.AltForm == 1 ^ pkm.HeldItem == 112: // Giratina, Origin form only with Griseous Orb
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LFormItemInvalid);
|
2018-06-30 22:01:16 +00:00
|
|
|
|
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Arceus:
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2018-06-30 22:01:16 +00:00
|
|
|
|
int form = GetArceusFormFromHeldItem(pkm.HeldItem, pkm.Format);
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return form != pkm.AltForm ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Keldeo:
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
|
|
|
|
if (pkm.Gen5) // can mismatch in gen5 via BW tutor and transfer up
|
|
|
|
|
break;
|
|
|
|
|
int index = Array.IndexOf(pkm.Moves, 548); // Secret Sword
|
|
|
|
|
bool noSword = index < 0;
|
|
|
|
|
if (pkm.AltForm == 0 ^ noSword) // mismatch
|
2018-09-01 21:11:12 +00:00
|
|
|
|
Info.Moves[noSword ? 0 : index] = new CheckMoveResult(Info.Moves[noSword ? 0 : index], Severity.Invalid, LMoveKeldeoMismatch, CheckIdentifier.Move);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Genesect:
|
2018-06-24 05:00:01 +00:00
|
|
|
|
{
|
2018-06-30 22:01:16 +00:00
|
|
|
|
int form = GetGenesectFormFromHeldItem(pkm.HeldItem);
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return form != pkm.AltForm ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Greninja:
|
2018-06-30 22:01:16 +00:00
|
|
|
|
if (pkm.AltForm > 1) // Ash Battle Bond active
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LFormBattle);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
if (pkm.AltForm != 0 && !(EncounterMatch is MysteryGift)) // Formes are not breedable, MysteryGift already checked
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(string.Format(LFormInvalidRange, 0, pkm.AltForm));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
break;
|
2018-06-30 22:01:16 +00:00
|
|
|
|
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Scatterbug:
|
|
|
|
|
case (int)Species.Spewpa:
|
2018-06-24 05:00:01 +00:00
|
|
|
|
if (pkm.AltForm > 17) // Fancy & Pokéball
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LFormVivillonEventPre);
|
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
|
|
|
|
if (!Legal.CheckVivillonPattern(pkm.AltForm, (byte)pkm.Country, (byte)pkm.Region))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
data.AddLine(Get(LFormVivillonInvalid, Severity.Fishy));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
break;
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Vivillon:
|
2018-06-24 05:00:01 +00:00
|
|
|
|
if (pkm.AltForm > 17) // Fancy & Pokéball
|
|
|
|
|
{
|
|
|
|
|
if (!(EncounterMatch is MysteryGift))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LFormVivillonInvalid);
|
|
|
|
|
return GetValid(LFormVivillon);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
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
|
|
|
|
if (!Legal.CheckVivillonPattern(pkm.AltForm, (byte)pkm.Country, (byte)pkm.Region))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
data.AddLine(Get(LFormVivillonInvalid, Severity.Fishy));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Floette when pkm.AltForm == 5: // Floette Eternal Flower -- Never Released
|
2018-06-30 22:01:16 +00:00
|
|
|
|
if (!(EncounterMatch is MysteryGift))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LFormEternalInvalid);
|
|
|
|
|
return GetValid(LFormEternal);
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Meowstic when pkm.AltForm != pkm.Gender:
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LGenderInvalidNone);
|
2018-06-30 22:01:16 +00:00
|
|
|
|
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Silvally:
|
2018-06-30 22:01:16 +00:00
|
|
|
|
{
|
|
|
|
|
int form = GetSilvallyFormFromHeldItem(pkm.HeldItem);
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return form != pkm.AltForm ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
|
2018-06-30 22:01:16 +00:00
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Lillipup when Info.EncounterMatch.EggEncounter && pkm.AltForm == 1 && pkm.SM:
|
|
|
|
|
case (int)Species.Lycanroc when Info.EncounterMatch.EggEncounter && pkm.AltForm == 2 && pkm.SM:
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LFormInvalidGame);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
|
|
|
|
// Impossible Egg forms
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Rotom when pkm.IsEgg && pkm.AltForm != 0:
|
|
|
|
|
case (int)Species.Furfrou when pkm.IsEgg && pkm.AltForm != 0:
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LEggSpecies);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
|
|
|
|
// Party Only Forms
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Shaymin:
|
|
|
|
|
case (int)Species.Furfrou:
|
|
|
|
|
case (int)Species.Hoopa:
|
2018-06-24 05:00:01 +00:00
|
|
|
|
if (pkm.AltForm != 0 && pkm.Box > -1 && pkm.Format <= 6) // has form but stored in box
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LFormParty);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Battle only Forms with other legal forms allowed
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Zygarde when pkm.AltForm >= 4: // Zygarde Complete
|
|
|
|
|
case (int)Species.Minior when pkm.AltForm < 7: // Minior Shield
|
|
|
|
|
case (int)Species.Necrozma when pkm.AltForm == 3: // Ultra Necrozma
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LFormBattle);
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Necrozma when pkm.AltForm < 3: // Necrozma Fused forms & default
|
|
|
|
|
case (int)Species.Mimikyu when pkm.AltForm == 2: // Totem disguise Mimikyu
|
2018-06-30 22:01:16 +00:00
|
|
|
|
return VALID;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pkm.Format >= 7 && Info.Generation < 7 && pkm.AltForm != 0)
|
|
|
|
|
{
|
2018-06-30 22:01:16 +00:00
|
|
|
|
if (pkm.Species == 25 || Legal.AlolanOriginForms.Contains(pkm.Species) || Legal.AlolanVariantEvolutions12.Contains(data.EncounterOriginal.Species))
|
2018-09-01 21:11:12 +00:00
|
|
|
|
return GetInvalid(LFormInvalidGame);
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
2019-09-23 23:56:47 +00:00
|
|
|
|
if (pkm.Format >= 8 && Info.Generation < 8 && pkm.AltForm != 0)
|
|
|
|
|
{
|
|
|
|
|
if (pkm.Species == 25 || Legal.GalarOriginForms.Contains(pkm.Species) || Legal.GalarVariantFormEvolutions.Contains(data.EncounterOriginal.Species))
|
|
|
|
|
return GetInvalid(LFormInvalidGame);
|
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2018-07-01 17:49:11 +00:00
|
|
|
|
if (pkm.AltForm != 0 && BattleOnly.Contains(pkm.Species))
|
2019-11-16 01:34:18 +00:00
|
|
|
|
{
|
2019-11-19 06:48:03 +00:00
|
|
|
|
if (pkm.Species == (int) Species.Darmanitan && pkm.AltForm == 2 && pkm.Format >= 8)
|
|
|
|
|
{
|
|
|
|
|
// this one is OK, Galarian non-Zen
|
2020-01-06 02:46:30 +00:00
|
|
|
|
}
|
2019-11-19 06:48:03 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return GetInvalid(LFormBattle);
|
|
|
|
|
}
|
2019-11-16 01:34:18 +00:00
|
|
|
|
}
|
2018-06-30 22:01:16 +00:00
|
|
|
|
|
|
|
|
|
return VALID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int GetArceusFormFromHeldItem(int item, int format)
|
|
|
|
|
{
|
|
|
|
|
if (777 <= item && item <= 793)
|
2019-05-15 16:46:52 +00:00
|
|
|
|
return Array.IndexOf(Legal.Arceus_ZCrystal, (ushort)item) + 1;
|
2018-06-30 22:01:16 +00:00
|
|
|
|
|
|
|
|
|
int form = 0;
|
|
|
|
|
if ((298 <= item && item <= 313) || item == 644)
|
2019-05-15 16:46:52 +00:00
|
|
|
|
form = Array.IndexOf(Legal.Arceus_Plate, (ushort)item) + 1;
|
2018-06-30 22:01:16 +00:00
|
|
|
|
if (format == 4 && form >= 9)
|
|
|
|
|
return form + 1; // ??? type Form shifts everything by 1
|
|
|
|
|
return form;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int GetSilvallyFormFromHeldItem(int item)
|
|
|
|
|
{
|
|
|
|
|
if ((904 <= item && item <= 920) || item == 644)
|
|
|
|
|
return item - 903;
|
|
|
|
|
return 0;
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-30 22:01:16 +00:00
|
|
|
|
private static int GetGenesectFormFromHeldItem(int item)
|
|
|
|
|
{
|
|
|
|
|
if (116 <= item && item <= 119)
|
|
|
|
|
return item - 115;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
|
2019-11-19 06:48:03 +00:00
|
|
|
|
private static readonly HashSet<int> BattleOnly = GetBattleFormSet();
|
2018-07-27 02:34:27 +00:00
|
|
|
|
|
2019-11-19 06:48:03 +00:00
|
|
|
|
private static HashSet<int> GetBattleFormSet()
|
2018-07-01 17:49:11 +00:00
|
|
|
|
{
|
2019-11-19 06:48:03 +00:00
|
|
|
|
var hs = new HashSet<int>();
|
|
|
|
|
hs.UnionWith(Legal.BattleForms);
|
|
|
|
|
hs.UnionWith(Legal.BattleMegas);
|
|
|
|
|
hs.UnionWith(Legal.BattlePrimals);
|
|
|
|
|
return hs;
|
2018-07-01 17:49:11 +00:00
|
|
|
|
}
|
2018-06-30 22:01:16 +00:00
|
|
|
|
|
2019-11-19 06:48:03 +00:00
|
|
|
|
private static readonly HashSet<int> SafariFloette = new HashSet<int> { 0, 1, 3 }; // 0/1/3 - RBY
|
|
|
|
|
|
2018-06-24 05:00:01 +00:00
|
|
|
|
private void VerifyFormFriendSafari(LegalityAnalysis data)
|
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
switch (pkm.Species)
|
|
|
|
|
{
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Floette when !SafariFloette.Contains(pkm.AltForm): // Floette
|
|
|
|
|
case (int)Species.Florges when !SafariFloette.Contains(pkm.AltForm): // Florges
|
2018-09-01 21:11:12 +00:00
|
|
|
|
data.AddLine(GetInvalid(LFormSafariFlorgesColor));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
break;
|
|
|
|
|
case 710 when pkm.AltForm != 0: // Pumpkaboo
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Gourgeist when pkm.AltForm != 0: // Average
|
2018-09-01 21:11:12 +00:00
|
|
|
|
data.AddLine(GetInvalid(LFormSafariPumpkabooAverage));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
break;
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Gastrodon when pkm.AltForm != 0: // West
|
2018-09-01 21:11:12 +00:00
|
|
|
|
data.AddLine(GetInvalid(LFormSafariFlorgesColor));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
break;
|
2019-06-01 17:22:49 +00:00
|
|
|
|
case (int)Species.Sawsbuck when pkm.AltForm != 0: // Sawsbuck
|
2018-09-01 21:11:12 +00:00
|
|
|
|
data.AddLine(GetInvalid(LFormSafariSawsbuckSpring));
|
2018-06-24 05:00:01 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-11 00:33:40 +00:00
|
|
|
|
|
|
|
|
|
private CheckResult VerifyFormArgument(LegalityAnalysis data, IFormArgument f)
|
|
|
|
|
{
|
|
|
|
|
var pkm = data.pkm;
|
|
|
|
|
switch (data.pkm.Species)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
if (f.FormArgument != 0)
|
|
|
|
|
return GetInvalid(LFormArgumentNotAllowed);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case (int)Species.Furfrou:
|
|
|
|
|
{
|
|
|
|
|
if (f.FormArgument > 5)
|
|
|
|
|
return GetInvalid(LFormArgumentHigh);
|
|
|
|
|
if (f.FormArgument == 0 && pkm.AltForm != 0)
|
|
|
|
|
return GetInvalid(LFormArgumentNotAllowed);
|
|
|
|
|
if (f.FormArgument != 0 && pkm.AltForm == 0)
|
|
|
|
|
return GetInvalid(LFormArgumentNotAllowed);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case (int)Species.Hoopa:
|
|
|
|
|
{
|
|
|
|
|
if (f.FormArgument > 3)
|
|
|
|
|
return GetInvalid(LFormArgumentHigh);
|
|
|
|
|
if (f.FormArgument == 0 && pkm.AltForm != 0)
|
|
|
|
|
return GetInvalid(LFormArgumentNotAllowed);
|
|
|
|
|
if (f.FormArgument != 0 && pkm.AltForm == 0)
|
|
|
|
|
return GetInvalid(LFormArgumentNotAllowed);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-01-13 00:22:56 +00:00
|
|
|
|
case (int)Species.Yamask when pkm.AltForm == 1:
|
|
|
|
|
{
|
|
|
|
|
if (pkm.IsEgg)
|
|
|
|
|
{
|
|
|
|
|
if (f.FormArgument != 0)
|
|
|
|
|
return GetInvalid(LFormArgumentNotAllowed);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var hp_max = GetYamask1MaxHP(pkm);
|
|
|
|
|
if (f.FormArgument > hp_max)
|
|
|
|
|
return GetInvalid(LFormArgumentHigh);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-01-11 00:33:40 +00:00
|
|
|
|
case (int)Species.Runerigus:
|
|
|
|
|
{
|
|
|
|
|
if (data.EncounterMatch.Species == (int)Species.Runerigus)
|
|
|
|
|
{
|
|
|
|
|
if (f.FormArgument != 0)
|
|
|
|
|
return GetInvalid(LFormArgumentNotAllowed);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (f.FormArgument < 49)
|
|
|
|
|
return GetInvalid(LFormArgumentLow);
|
2020-01-13 00:22:56 +00:00
|
|
|
|
var hp_max = GetYamask1MaxHP(pkm);
|
|
|
|
|
if (f.FormArgument > hp_max)
|
2020-01-11 00:33:40 +00:00
|
|
|
|
return GetInvalid(LFormArgumentHigh);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case (int)Species.Alcremie:
|
|
|
|
|
{
|
|
|
|
|
if (data.EncounterMatch.Species == (int)Species.Alcremie)
|
|
|
|
|
{
|
|
|
|
|
if (f.FormArgument != 0)
|
|
|
|
|
return GetInvalid(LFormArgumentNotAllowed);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (f.FormArgument > (uint)AlcremieDecoration.Ribbon)
|
|
|
|
|
return GetInvalid(LFormArgumentHigh);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GetValid(LFormArgumentValid);
|
|
|
|
|
}
|
2020-01-13 00:22:56 +00:00
|
|
|
|
|
|
|
|
|
private static int GetYamask1MaxHP(PKM pkm)
|
|
|
|
|
{
|
|
|
|
|
var lvl = pkm.CurrentLevel; // assume it was evolved at the current level (no further level ups)
|
|
|
|
|
var iv_hp = pkm is IHyperTrain ht && ht.HT_HP ? 31 : pkm.IV_HP;
|
|
|
|
|
const int base_hp = 38;
|
|
|
|
|
const int ev_hp = 252; // account for full EVs then removed
|
|
|
|
|
|
|
|
|
|
// Manually calculate the stat of Galarian Yamask under the most favorable conditions
|
|
|
|
|
return ((iv_hp + (2 * base_hp) + (ev_hp / 4) + 100) * lvl / 100) + 10;
|
|
|
|
|
}
|
2018-06-24 05:00:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|