2017-05-28 04:17:53 +00:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using static PKHeX . Core . LegalityCheckStrings ;
2020-11-28 02:45:06 +00:00
using static PKHeX . Core . ParseSettings ;
2017-05-28 04:17:53 +00:00
2019-02-22 04:44:31 +00:00
using static PKHeX . Core . MoveSource ;
using static PKHeX . Core . Severity ;
using static PKHeX . Core . CheckIdentifier ;
2017-05-28 04:17:53 +00:00
namespace PKHeX.Core
{
2017-10-24 06:12:58 +00:00
/// <summary>
/// Logic to verify the current <see cref="PKM.Moves"/>.
/// </summary>
2017-05-28 04:17:53 +00:00
public static class VerifyCurrentMoves
{
2019-09-10 07:21:51 +00:00
/// <summary>
/// Verifies the current moves of the <see cref="pkm"/> data based on the provided <see cref="info"/>.
/// </summary>
/// <param name="pkm">Data to check</param>
/// <param name="info">Encounter conditions and legality info</param>
/// <returns>Validity of the <see cref="PKM.Moves"/></returns>
2017-12-12 00:01:24 +00:00
public static CheckMoveResult [ ] VerifyMoves ( PKM pkm , LegalInfo info )
2017-05-28 04:17:53 +00:00
{
2020-05-31 19:12:07 +00:00
var currentMoves = pkm . Moves ;
var res = ParseMovesForEncounters ( pkm , info , currentMoves ) ;
2017-05-28 04:17:53 +00:00
// Duplicate Moves Check
2020-05-31 19:12:07 +00:00
VerifyNoEmptyDuplicates ( currentMoves , res ) ;
if ( currentMoves [ 0 ] = = 0 ) // Can't have an empty move slot for the first move.
2021-01-09 21:05:56 +00:00
res [ 0 ] = new CheckMoveResult ( res [ 0 ] , Invalid , LMoveSourceEmpty , CurrentMove ) ;
2017-05-28 04:17:53 +00:00
return res ;
}
2021-04-05 01:30:01 +00:00
private static CheckMoveResult [ ] ParseMovesForEncounters ( PKM pkm , LegalInfo info , int [ ] currentMoves )
2017-05-28 04:17:53 +00:00
{
2019-12-09 01:39:19 +00:00
if ( pkm . Species = = ( int ) Species . Smeargle ) // special handling for Smeargle
2020-05-31 19:12:07 +00:00
return ParseMovesForSmeargle ( pkm , currentMoves , info ) ; // Smeargle can have any moves except a few
2017-05-28 04:17:53 +00:00
// gather valid moves for encounter species
2021-07-23 04:25:15 +00:00
info . EncounterMoves = new ValidEncounterMoves ( pkm , info . EncounterMatch , info . EvoChainsAllGens ) ;
2017-05-28 04:17:53 +00:00
2017-12-12 00:01:24 +00:00
var res = info . Generation < 6
2020-05-31 19:12:07 +00:00
? ParseMovesPre3DS ( pkm , currentMoves , info )
: ParseMoves3DS ( pkm , currentMoves , info ) ;
2017-05-28 04:17:53 +00:00
if ( res . All ( x = > x . Valid ) )
return res ;
return res ;
}
2018-08-03 03:11:42 +00:00
2020-05-31 19:12:07 +00:00
private static CheckMoveResult [ ] ParseMovesForSmeargle ( PKM pkm , IReadOnlyList < int > currentMoves , LegalInfo info )
2017-05-28 04:17:53 +00:00
{
if ( ! pkm . IsEgg )
2020-05-31 19:12:07 +00:00
return ParseMovesSketch ( pkm , currentMoves ) ;
2017-05-28 04:17:53 +00:00
// can only know sketch as egg
2020-05-31 19:12:07 +00:00
var levelup = new int [ info . EvoChainsAllGens . Length ] [ ] ;
levelup [ pkm . Format ] = new [ ] { 166 } ;
2017-09-02 06:15:57 +00:00
info . EncounterMoves = new ValidEncounterMoves ( levelup ) ;
2021-08-20 20:49:20 +00:00
var source = new MoveParseSource { CurrentMoves = currentMoves } ;
2017-09-02 06:15:57 +00:00
return ParseMoves ( pkm , source , info ) ;
2017-06-18 01:37:19 +00:00
}
2018-08-03 03:11:42 +00:00
2020-05-31 19:12:07 +00:00
private static CheckMoveResult [ ] ParseMovesWasEggPreRelearn ( PKM pkm , IReadOnlyList < int > currentMoves , LegalInfo info , EncounterEgg e )
2017-05-28 04:17:53 +00:00
{
2018-06-09 03:36:52 +00:00
// Level up moves could not be inherited if Ditto is parent,
2019-04-17 06:07:47 +00:00
// that means genderless species and male only species (except Nidoran-M and Volbeat; they breed with Nidoran-F and Illumise) could not have level up moves as an egg
var pi = pkm . PersonalInfo ;
2021-09-16 01:43:18 +00:00
var AllowLevelUp = ! pi . Genderless & & ! ( pi . OnlyMale & & Breeding . MixedGenderBreeding . Contains ( e . Species ) ) ;
2017-09-02 06:15:57 +00:00
int BaseLevel = AllowLevelUp ? 100 : e . LevelMin ;
2020-06-21 00:44:05 +00:00
var LevelUp = MoveList . GetBaseEggMoves ( pkm , e . Species , e . Form , e . Version , BaseLevel ) ;
2017-11-18 06:19:23 +00:00
2021-09-16 01:43:18 +00:00
var TradebackPreevo = pkm . Format = = 2 & & e . Species > 151 ;
2018-05-12 15:13:39 +00:00
var NonTradebackLvlMoves = TradebackPreevo
2021-09-16 01:43:18 +00:00
? MoveList . GetExclusivePreEvolutionMoves ( pkm , e . Species , info . EvoChainsAllGens [ 2 ] , 2 , e . Version ) . Where ( m = > m > Legal . MaxMoveID_1 ) . ToArray ( )
2018-08-02 01:30:51 +00:00
: Array . Empty < int > ( ) ;
2017-11-18 06:19:23 +00:00
2021-01-01 23:01:22 +00:00
var Egg = MoveEgg . GetEggMoves ( pkm . PersonalInfo , e . Species , e . Form , e . Version , e . Generation ) ;
2017-10-17 00:24:19 +00:00
if ( info . Generation < 3 & & pkm . Format > = 7 & & pkm . VC1 )
2021-08-06 05:39:38 +00:00
Egg = Array . FindAll ( Egg , m = > m < = Legal . MaxMoveID_1 ) ;
2017-05-28 04:17:53 +00:00
2021-09-16 01:43:18 +00:00
var specialMoves = e . CanHaveVoltTackle ? new [ ] { ( int ) Move . VoltTackle } : Array . Empty < int > ( ) ; // Volt Tackle for bred Pichu line
2017-05-28 04:17:53 +00:00
2017-09-02 06:15:57 +00:00
var source = new MoveParseSource
{
2020-05-31 19:12:07 +00:00
CurrentMoves = currentMoves ,
2021-01-13 06:26:08 +00:00
SpecialSource = specialMoves ,
2017-09-02 06:15:57 +00:00
NonTradeBackLevelUpMoves = NonTradebackLvlMoves ,
EggLevelUpSource = LevelUp ,
EggMoveSource = Egg ,
} ;
return ParseMoves ( pkm , source , info ) ;
2017-05-28 04:17:53 +00:00
}
2018-08-03 03:11:42 +00:00
2020-05-31 19:12:07 +00:00
private static CheckMoveResult [ ] ParseMovesSketch ( PKM pkm , IReadOnlyList < int > currentMoves )
2017-05-28 04:17:53 +00:00
{
2018-08-03 03:11:42 +00:00
var res = new CheckMoveResult [ 4 ] ;
2017-05-28 04:17:53 +00:00
for ( int i = 0 ; i < 4 ; i + + )
2018-08-03 03:11:42 +00:00
{
2021-04-05 02:39:31 +00:00
var move = currentMoves [ i ] ;
2021-06-07 06:36:59 +00:00
res [ i ] = Legal . IsValidSketch ( move , pkm . Format )
? new CheckMoveResult ( Sketch , pkm . Format , CurrentMove )
: new CheckMoveResult ( Unknown , pkm . Format , Invalid , LMoveSourceInvalidSketch , CurrentMove ) ;
2018-08-03 03:11:42 +00:00
}
2017-05-28 04:17:53 +00:00
return res ;
}
2018-08-03 03:11:42 +00:00
2020-05-31 19:12:07 +00:00
private static CheckMoveResult [ ] ParseMoves3DS ( PKM pkm , IReadOnlyList < int > currentMoves , LegalInfo info )
2017-05-28 04:17:53 +00:00
{
2018-08-02 01:30:51 +00:00
info . EncounterMoves . Relearn = info . Generation > = 6 ? pkm . RelearnMoves : Array . Empty < int > ( ) ;
2020-05-31 19:12:07 +00:00
return info . EncounterMatch is IMoveset
? ParseMovesSpecialMoveset ( pkm , currentMoves , info )
: ParseMovesRelearn ( pkm , currentMoves , info ) ;
2017-05-28 04:17:53 +00:00
}
2018-08-03 03:11:42 +00:00
2021-04-05 01:30:01 +00:00
private static CheckMoveResult [ ] ParseMovesPre3DS ( PKM pkm , int [ ] currentMoves , LegalInfo info )
2017-05-28 04:17:53 +00:00
{
2020-05-31 19:12:07 +00:00
if ( info . EncounterMatch is EncounterEgg e )
2017-05-28 04:17:53 +00:00
{
2020-05-31 19:12:07 +00:00
return pkm . IsEgg
2021-04-17 20:13:03 +00:00
? VerifyPreRelearnEggBase ( currentMoves , e )
2020-05-31 19:12:07 +00:00
: ParseMovesWasEggPreRelearn ( pkm , currentMoves , info , e ) ;
2017-05-28 04:17:53 +00:00
}
2020-05-17 19:32:28 +00:00
2021-01-16 20:01:40 +00:00
// Not all games have a re-learner. Initial moves may not fill out all 4 slots.
2020-05-17 19:32:28 +00:00
int gen = info . EncounterMatch . Generation ;
2021-01-16 20:01:40 +00:00
if ( gen = = 1 | | ( gen = = 2 & & ! AllowGen2MoveReminder ( pkm ) ) )
2020-05-31 19:12:07 +00:00
return ParseMovesGenGB ( pkm , currentMoves , info ) ;
2017-05-28 04:17:53 +00:00
2020-05-31 19:12:07 +00:00
return ParseMovesSpecialMoveset ( pkm , currentMoves , info ) ;
2017-05-28 04:17:53 +00:00
}
2018-08-03 03:11:42 +00:00
2020-05-31 19:12:07 +00:00
private static CheckMoveResult [ ] ParseMovesGenGB ( PKM pkm , IReadOnlyList < int > currentMoves , LegalInfo info )
2017-05-28 04:17:53 +00:00
{
2019-02-22 05:54:41 +00:00
var res = new CheckMoveResult [ 4 ] ;
2021-01-16 20:01:40 +00:00
var enc = info . EncounterMatch ;
2022-02-05 01:35:15 +00:00
var evos = info . EvoChainsAllGens [ enc . Generation ] ;
var level = evos . Count > 0 ? evos [ ^ 1 ] . MinLevel : enc . LevelMin ;
2018-08-02 01:30:51 +00:00
var InitialMoves = Array . Empty < int > ( ) ;
2021-01-16 20:01:40 +00:00
var SpecialMoves = GetSpecialMoves ( enc ) ;
var games = enc . Generation = = 1 ? GBRestrictions . GetGen1Versions ( enc ) : GBRestrictions . GetGen2Versions ( enc , pkm . Korean ) ;
2018-06-19 02:57:32 +00:00
foreach ( var ver in games )
2017-05-28 04:17:53 +00:00
{
2021-12-05 02:37:47 +00:00
var VerInitialMoves = enc is IMoveset { Moves . Count : not 0 } x ? ( int [ ] ) x . Moves : MoveLevelUp . GetEncounterMoves ( enc . Species , 0 , level , ver ) ;
2018-06-19 02:57:32 +00:00
if ( VerInitialMoves . Intersect ( InitialMoves ) . Count ( ) = = VerInitialMoves . Length )
2017-05-28 04:17:53 +00:00
return res ;
2017-09-02 06:15:57 +00:00
var source = new MoveParseSource
{
2020-05-31 19:12:07 +00:00
CurrentMoves = currentMoves ,
2017-09-02 06:15:57 +00:00
SpecialSource = SpecialMoves ,
Base = VerInitialMoves ,
} ;
res = ParseMoves ( pkm , source , info ) ;
2021-08-20 22:59:54 +00:00
// Must have a minimum count of moves, depending on the tradeback state.
if ( pkm is PK1 pk1 )
{
int count = GBRestrictions . GetRequiredMoveCount ( pk1 , source . CurrentMoves , info , source . Base ) ;
if ( count = = 1 )
return res ;
2021-10-30 04:25:48 +00:00
// Reverse for loop and break instead of 0..count continue -- early-breaks for the vast majority of cases.
// We already flag for empty interstitial moveslots.
for ( int m = count - 1 ; m > = 0 ; m - - )
2021-08-20 22:59:54 +00:00
{
var move = source . CurrentMoves [ m ] ;
2021-10-30 04:25:48 +00:00
if ( move ! = 0 )
break ;
// There are ways to skip level up moves by leveling up more than once.
// https://bulbapedia.bulbagarden.net/wiki/List_of_glitches_(Generation_I)#Level-up_learnset_skipping
// Evolution canceling also leads to incorrect assumptions in the above used method, so just indicate them as fishy in that case.
// Not leveled up? Not possible to be missing the move slot.
var severity = enc . LevelMin = = pkm . CurrentLevel ? Invalid : Fishy ;
res [ m ] = new CheckMoveResult ( None , pkm . Format , severity , LMoveSourceEmpty , CurrentMove ) ;
2021-08-20 22:59:54 +00:00
}
}
2017-05-28 04:17:53 +00:00
if ( res . All ( r = > r . Valid ) )
return res ;
InitialMoves = VerInitialMoves ;
}
return res ;
}
2018-08-03 03:11:42 +00:00
2020-05-31 19:12:07 +00:00
private static CheckMoveResult [ ] ParseMovesSpecialMoveset ( PKM pkm , IReadOnlyList < int > currentMoves , LegalInfo info )
2017-05-28 04:17:53 +00:00
{
2017-09-02 06:15:57 +00:00
var source = new MoveParseSource
{
2020-05-31 19:12:07 +00:00
CurrentMoves = currentMoves ,
2017-09-02 06:15:57 +00:00
SpecialSource = GetSpecialMoves ( info . EncounterMatch ) ,
} ;
return ParseMoves ( pkm , source , info ) ;
2017-05-28 04:17:53 +00:00
}
2018-08-03 03:11:42 +00:00
2021-07-14 00:33:23 +00:00
private static IReadOnlyList < int > GetSpecialMoves ( IEncounterTemplate enc )
2017-07-26 00:35:07 +00:00
{
2021-01-11 02:15:33 +00:00
if ( enc is IMoveset mg )
2018-09-02 02:55:08 +00:00
return mg . Moves ;
2018-08-02 01:30:51 +00:00
return Array . Empty < int > ( ) ;
2017-07-26 00:35:07 +00:00
}
2018-08-03 03:11:42 +00:00
2020-05-31 19:12:07 +00:00
private static CheckMoveResult [ ] ParseMovesRelearn ( PKM pkm , IReadOnlyList < int > currentMoves , LegalInfo info )
2017-05-28 04:17:53 +00:00
{
2017-09-02 06:15:57 +00:00
var source = new MoveParseSource
{
2020-05-31 19:12:07 +00:00
CurrentMoves = currentMoves ,
2017-09-02 06:15:57 +00:00
SpecialSource = GetSpecialMoves ( info . EncounterMatch ) ,
} ;
2017-05-28 04:17:53 +00:00
2017-09-04 20:48:10 +00:00
if ( info . EncounterMatch is EncounterEgg e )
2021-01-01 23:01:22 +00:00
source . EggMoveSource = MoveEgg . GetEggMoves ( pkm . PersonalInfo , e . Species , e . Form , e . Version , e . Generation ) ;
2017-10-17 00:24:19 +00:00
2019-02-22 05:54:41 +00:00
var res = ParseMoves ( pkm , source , info ) ;
var relearn = pkm . RelearnMoves ;
2017-05-28 04:17:53 +00:00
for ( int i = 0 ; i < 4 ; i + + )
2018-08-03 03:11:42 +00:00
{
2020-10-17 20:40:12 +00:00
if ( ( pkm . IsEgg | | res [ i ] . IsRelearn ) & & ! relearn . Contains ( currentMoves [ i ] ) )
2019-02-22 04:44:31 +00:00
res [ i ] = new CheckMoveResult ( res [ i ] , Invalid , string . Format ( LMoveRelearnFMiss_0 , res [ i ] . Comment ) , res [ i ] . Identifier ) ;
2018-08-03 03:11:42 +00:00
}
2017-05-28 04:17:53 +00:00
return res ;
}
2018-08-03 03:11:42 +00:00
2017-09-02 06:15:57 +00:00
private static CheckMoveResult [ ] ParseMoves ( PKM pkm , MoveParseSource source , LegalInfo info )
2017-05-28 04:17:53 +00:00
{
2019-02-22 05:54:41 +00:00
var res = new CheckMoveResult [ 4 ] ;
2019-02-07 07:28:02 +00:00
bool AllParsed ( ) = > res . All ( z = > z ! = null ) ;
2017-05-28 04:17:53 +00:00
2020-06-19 23:51:15 +00:00
// Special considerations!
2021-08-06 05:39:38 +00:00
const int NoMinGeneration = 0 ;
int minGeneration = NoMinGeneration ;
2020-12-29 08:58:08 +00:00
if ( pkm is IBattleVersion { BattleVersion : not 0 } v )
2020-06-19 23:51:15 +00:00
{
2021-08-06 05:39:38 +00:00
minGeneration = ( ( GameVersion ) v . BattleVersion ) . GetGeneration ( ) ;
2020-10-04 19:08:16 +00:00
source . ResetSources ( ) ;
2020-06-19 23:51:15 +00:00
}
2017-09-02 06:15:57 +00:00
// Check empty moves and relearn moves before generation specific moves
2017-05-28 04:17:53 +00:00
for ( int m = 0 ; m < 4 ; m + + )
{
2021-08-06 05:39:38 +00:00
var move = source . CurrentMoves [ m ] ;
if ( move = = 0 )
2021-08-20 22:59:54 +00:00
res [ m ] = new CheckMoveResult ( None , pkm . Format , Valid , LMoveSourceEmpty , CurrentMove ) ;
2021-08-06 05:39:38 +00:00
else if ( minGeneration = = NoMinGeneration & & info . EncounterMoves . Relearn . Contains ( move ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( Relearn , info . Generation , Valid , LMoveSourceRelearn , CurrentMove ) ;
2017-05-28 04:17:53 +00:00
}
2017-09-02 06:15:57 +00:00
if ( AllParsed ( ) )
2017-05-28 04:17:53 +00:00
return res ;
2017-09-02 06:15:57 +00:00
// Encapsulate arguments to simplify method calls
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 moveInfo = new LearnInfo ( pkm , source ) ;
2017-05-28 04:17:53 +00:00
// Check moves going backwards, marking the move valid in the most current generation when it can be learned
2021-04-17 20:09:09 +00:00
int [ ] generations = GenerationTraversal . GetVisitedGenerationOrder ( pkm , info . EncounterOriginal . Generation ) ;
2017-07-18 23:21:31 +00:00
if ( pkm . Format < = 2 )
2021-08-06 05:39:38 +00:00
generations = Array . FindAll ( generations , z = > z < info . EncounterMoves . LevelUpMoves . Length ) ;
if ( minGeneration ! = NoMinGeneration )
generations = Array . FindAll ( generations , z = > z > = minGeneration ) ;
2017-09-02 06:15:57 +00:00
2021-08-06 05:39:38 +00:00
if ( generations . Length ! = 0 )
2017-05-28 04:17:53 +00:00
{
2021-08-06 05:39:38 +00:00
int lastgen = generations [ ^ 1 ] ;
foreach ( var gen in generations )
{
ParseMovesByGeneration ( pkm , res , gen , info , moveInfo , lastgen ) ;
if ( AllParsed ( ) )
return res ;
}
2017-09-02 06:15:57 +00:00
}
2017-05-28 04:17:53 +00:00
2019-12-09 01:39:19 +00:00
if ( pkm . Species = = ( int ) Species . Shedinja & & info . Generation < = 4 )
2020-04-12 18:28:03 +00:00
ParseShedinjaEvolveMoves ( pkm , res , source . CurrentMoves , info . EvoChainsAllGens ) ;
2017-05-28 04:17:53 +00:00
2021-01-04 00:53:13 +00:00
// ReSharper disable once ConstantNullCoalescingCondition
2017-09-02 06:15:57 +00:00
for ( int m = 0 ; m < 4 ; m + + )
2021-01-09 21:05:56 +00:00
res [ m ] ? ? = new CheckMoveResult ( Unknown , info . Generation , Invalid , LMoveSourceInvalid , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
return res ;
}
2018-08-03 03:11:42 +00:00
2017-09-02 06:15:57 +00:00
private static void ParseMovesByGeneration ( PKM pkm , CheckMoveResult [ ] res , int gen , LegalInfo info , LearnInfo learnInfo , int last )
{
2017-12-15 00:16:47 +00:00
GetHMCompatibility ( pkm , res , gen , learnInfo . Source . CurrentMoves , out bool [ ] HMLearned , out bool KnowDefogWhirlpool ) ;
2017-09-02 06:15:57 +00:00
ParseMovesByGeneration ( pkm , res , gen , info , learnInfo ) ;
2017-05-28 04:17:53 +00:00
2017-09-02 06:15:57 +00:00
if ( gen = = last )
2020-10-04 19:21:05 +00:00
ParseMovesByGenerationLast ( pkm , res , learnInfo , info . EncounterMatch ) ;
2017-06-07 03:10:05 +00:00
2017-09-02 06:15:57 +00:00
switch ( gen )
{
2020-12-25 18:58:33 +00:00
case 1 or 2 :
2017-09-02 06:15:57 +00:00
ParseMovesByGeneration12 ( pkm , res , learnInfo . Source . CurrentMoves , gen , info , learnInfo ) ;
break ;
2019-02-22 05:54:41 +00:00
2020-12-25 18:58:33 +00:00
case 3 or 4 :
2019-02-22 05:54:41 +00:00
if ( pkm . Format > gen )
FlagIncompatibleTransferHMs45 ( res , learnInfo . Source . CurrentMoves , gen , HMLearned , KnowDefogWhirlpool ) ;
break ;
2017-09-02 06:15:57 +00:00
}
}
2018-08-03 03:11:42 +00:00
2017-12-15 00:16:47 +00:00
private static void ParseMovesByGeneration ( PKM pkm , IList < CheckMoveResult > res , int gen , LegalInfo info , LearnInfo learnInfo )
2017-09-02 06:15:57 +00:00
{
var moves = learnInfo . Source . CurrentMoves ;
bool native = gen = = pkm . Format ;
for ( int m = 0 ; m < 4 ; m + + )
{
if ( IsCheckValid ( res [ m ] ) ) // already validated with another generation
continue ;
2017-09-20 23:19:50 +00:00
int move = moves [ m ] ;
if ( move = = 0 )
2017-09-02 06:15:57 +00:00
continue ;
2017-05-28 04:17:53 +00:00
2018-04-01 22:04:53 +00:00
if ( gen < = 2 )
{
if ( gen = = 2 & & ! native & & move > Legal . MaxMoveID_1 & & pkm . VC1 )
2018-04-01 22:51:55 +00:00
{
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( Unknown , gen , Invalid , LMoveSourceInvalid , CurrentMove ) ;
2018-04-01 22:51:55 +00:00
continue ;
}
if ( gen = = 2 & & learnInfo . Source . EggMoveSource . Contains ( move ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( EggMove , gen , Valid , LMoveSourceEgg , CurrentMove ) ;
2018-04-01 22:04:53 +00:00
else if ( learnInfo . Source . Base . Contains ( move ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( Initial , gen , Valid , native ? LMoveSourceDefault : string . Format ( LMoveFDefault_0 , gen ) , CurrentMove ) ;
2018-04-01 22:04:53 +00:00
}
2018-04-01 22:51:55 +00:00
if ( info . EncounterMoves . LevelUpMoves [ gen ] . Contains ( move ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( LevelUp , gen , Valid , native ? LMoveSourceLevelUp : string . Format ( LMoveFLevelUp_0 , gen ) , CurrentMove ) ;
2017-09-20 23:19:50 +00:00
else if ( info . EncounterMoves . TMHMMoves [ gen ] . Contains ( move ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( TMHM , gen , Valid , native ? LMoveSourceTMHM : string . Format ( LMoveFTMHM_0 , gen ) , CurrentMove ) ;
2017-09-20 23:19:50 +00:00
else if ( info . EncounterMoves . TutorMoves [ gen ] . Contains ( move ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( Tutor , gen , Valid , native ? LMoveSourceTutor : string . Format ( LMoveFTutor_0 , gen ) , CurrentMove ) ;
2017-09-20 23:19:50 +00:00
else if ( gen = = info . Generation & & learnInfo . Source . SpecialSource . Contains ( move ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( Special , gen , Valid , LMoveSourceSpecial , CurrentMove ) ;
2020-02-17 03:47:57 +00:00
else if ( gen > = 8 & & MoveEgg . GetIsSharedEggMove ( pkm , gen , move ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( Shared , gen , Valid , native ? LMoveSourceShared : string . Format ( LMoveSourceSharedF , gen ) , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
2019-09-10 07:21:51 +00:00
if ( gen > = 3 | | ! IsCheckValid ( res [ m ] ) )
2017-09-02 06:15:57 +00:00
continue ;
2017-05-28 04:17:53 +00:00
2019-03-17 03:07:30 +00:00
// Gen1/Gen2 only below
if ( gen = = 2 & & learnInfo . Source . NonTradeBackLevelUpMoves . Contains ( m ) )
2019-09-10 07:21:51 +00:00
{
2017-09-02 06:15:57 +00:00
learnInfo . Gen2PreevoMoves . Add ( m ) ;
2019-09-10 07:21:51 +00:00
}
2019-03-17 03:07:30 +00:00
else if ( gen = = 1 )
2017-05-28 04:17:53 +00:00
{
2017-09-02 06:15:57 +00:00
learnInfo . Gen1Moves . Add ( m ) ;
2017-12-05 04:16:54 +00:00
if ( learnInfo . Gen2PreevoMoves . Count ! = 0 )
2017-09-02 06:15:57 +00:00
learnInfo . MixedGen12NonTradeback = true ;
2017-05-28 04:17:53 +00:00
}
2017-09-02 06:15:57 +00:00
}
}
2018-08-03 03:11:42 +00:00
2020-05-31 19:12:07 +00:00
private static void ParseMovesByGeneration12 ( PKM pkm , CheckMoveResult [ ] res , IReadOnlyList < int > currentMoves , int gen , LegalInfo info , LearnInfo learnInfo )
2017-09-02 06:15:57 +00:00
{
// Mark the gen 1 exclusive moves as illegal because the pokemon also have Non tradeback egg moves.
if ( learnInfo . MixedGen12NonTradeback )
{
foreach ( int m in learnInfo . Gen1Moves )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , LG1MoveExclusive , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
foreach ( int m in learnInfo . Gen2PreevoMoves )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , LG1TradebackPreEvoMove , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
}
2017-06-07 03:10:05 +00:00
2021-08-20 22:59:54 +00:00
if ( gen = = 1 & & pkm . Format = = 1 & & ! AllowGen1Tradeback )
2017-09-02 06:15:57 +00:00
{
2020-05-31 19:12:07 +00:00
ParseRedYellowIncompatibleMoves ( pkm , res , currentMoves ) ;
ParseEvolutionsIncompatibleMoves ( pkm , res , currentMoves , info . EncounterMoves . TMHMMoves [ 1 ] ) ;
2017-09-02 06:15:57 +00:00
}
}
2018-08-03 03:11:42 +00:00
2021-07-14 00:33:23 +00:00
private static void ParseMovesByGenerationLast ( PKM pkm , CheckMoveResult [ ] res , LearnInfo learnInfo , IEncounterTemplate enc )
2017-09-02 06:15:57 +00:00
{
2020-10-04 19:21:05 +00:00
int gen = enc . Generation ;
2017-09-02 06:15:57 +00:00
ParseEggMovesInherited ( pkm , res , gen , learnInfo ) ;
ParseEggMoves ( pkm , res , gen , learnInfo ) ;
2020-08-05 05:56:55 +00:00
ParseEggMovesRemaining ( pkm , res , learnInfo , enc ) ;
2017-09-02 06:15:57 +00:00
}
2018-08-03 03:11:42 +00:00
2017-09-02 06:15:57 +00:00
private static void ParseEggMovesInherited ( PKM pkm , CheckMoveResult [ ] res , int gen , LearnInfo learnInfo )
{
var moves = learnInfo . Source . CurrentMoves ;
// Check higher-level moves after all the moves but just before egg moves to differentiate it from normal level up moves
// Also check if the base egg moves is a non tradeback move
for ( int m = 0 ; m < 4 ; m + + )
{
2020-10-04 19:21:05 +00:00
var r = res [ m ] ;
if ( IsCheckValid ( r ) ) // already validated
{
if ( gen = = 2 & & r . Generation ! = 1 )
continue ;
}
int move = moves [ m ] ;
if ( move = = 0 )
2017-09-02 06:15:57 +00:00
continue ;
2020-10-04 19:21:05 +00:00
if ( ! learnInfo . Source . EggLevelUpSource . Contains ( move ) ) // Check if contains level-up egg moves from parents
2017-09-02 06:15:57 +00:00
continue ;
2020-10-04 19:21:05 +00:00
if ( learnInfo . IsGen2Pkm & & learnInfo . Gen1Moves . Count ! = 0 & & move > Legal . MaxMoveID_1 )
2017-09-02 06:15:57 +00:00
{
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( InheritLevelUp , gen , Invalid , LG1MoveTradeback , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
learnInfo . MixedGen12NonTradeback = true ;
2017-06-07 03:10:05 +00:00
}
2017-09-02 06:15:57 +00:00
else
2018-08-03 03:11:42 +00:00
{
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( InheritLevelUp , gen , Valid , LMoveEggLevelUp , CurrentMove ) ;
2018-08-03 03:11:42 +00:00
}
2017-09-02 06:15:57 +00:00
learnInfo . LevelUpEggMoves . Add ( m ) ;
2020-10-04 19:21:05 +00:00
if ( gen = = 2 & & learnInfo . Gen1Moves . Contains ( m ) )
learnInfo . Gen1Moves . Remove ( m ) ;
2017-09-02 06:15:57 +00:00
}
}
2018-08-03 03:11:42 +00:00
2017-09-02 06:15:57 +00:00
private static void ParseEggMoves ( PKM pkm , CheckMoveResult [ ] res , int gen , LearnInfo learnInfo )
{
var moves = learnInfo . Source . CurrentMoves ;
// Check egg moves after all the generations and all the moves, every move that can't be learned in another source should have preference
// the moves that can only be learned from egg moves should in the future check if the move combinations can be breed in gens 2 to 5
for ( int m = 0 ; m < 4 ; m + + )
{
if ( IsCheckValid ( res [ m ] ) )
continue ;
2019-02-22 05:54:41 +00:00
int move = moves [ m ] ;
if ( move = = 0 )
2017-09-02 06:15:57 +00:00
continue ;
2017-05-28 04:17:53 +00:00
2019-02-22 05:54:41 +00:00
bool wasEggMove = learnInfo . Source . EggMoveSource . Contains ( move ) ;
2018-07-02 04:34:17 +00:00
if ( wasEggMove )
2017-05-28 04:17:53 +00:00
{
2017-09-02 06:15:57 +00:00
// To learn exclusive generation 1 moves the pokemon was tradeback, but it can't be trade to generation 1
// without removing moves above MaxMoveID_1, egg moves above MaxMoveID_1 and gen 1 moves are incompatible
2019-02-22 05:54:41 +00:00
if ( learnInfo . IsGen2Pkm & & learnInfo . Gen1Moves . Count ! = 0 & & move > Legal . MaxMoveID_1 )
2017-09-02 06:15:57 +00:00
{
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( EggMove , gen , Invalid , LG1MoveTradeback , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
learnInfo . MixedGen12NonTradeback = true ;
}
else
2018-08-03 03:11:42 +00:00
{
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( EggMove , gen , Valid , LMoveSourceEgg , CurrentMove ) ;
2018-08-03 03:11:42 +00:00
}
2017-05-28 04:17:53 +00:00
2017-09-02 06:15:57 +00:00
learnInfo . EggMovesLearned . Add ( m ) ;
2017-05-28 04:17:53 +00:00
}
2019-02-22 05:54:41 +00:00
if ( ! learnInfo . Source . EggEventSource . Contains ( move ) )
2017-09-02 06:15:57 +00:00
continue ;
2017-05-28 04:17:53 +00:00
2018-07-02 04:34:17 +00:00
if ( ! wasEggMove )
2017-05-28 04:17:53 +00:00
{
2019-02-22 05:54:41 +00:00
if ( learnInfo . IsGen2Pkm & & learnInfo . Gen1Moves . Count ! = 0 & & move > Legal . MaxMoveID_1 )
2017-09-02 06:15:57 +00:00
{
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( SpecialEgg , gen , Invalid , LG1MoveTradeback , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
learnInfo . MixedGen12NonTradeback = true ;
}
else
2018-08-03 03:11:42 +00:00
{
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( SpecialEgg , gen , Valid , LMoveSourceEggEvent , CurrentMove ) ;
2018-08-03 03:11:42 +00:00
}
2017-05-28 04:17:53 +00:00
}
2017-09-02 06:15:57 +00:00
learnInfo . EventEggMoves . Add ( m ) ;
2017-05-28 04:17:53 +00:00
}
2017-09-02 06:15:57 +00:00
}
2018-08-03 03:11:42 +00:00
2021-07-14 00:33:23 +00:00
private static void ParseEggMovesRemaining ( PKM pkm , CheckMoveResult [ ] res , LearnInfo learnInfo , IEncounterTemplate enc )
2017-09-02 06:15:57 +00:00
{
// A pokemon could have normal egg moves and regular egg moves
// Only if all regular egg moves are event egg moves or all event egg moves are regular egg moves
2021-08-06 05:39:38 +00:00
var RegularEggMovesLearned = learnInfo . EggMovesLearned . FindAll ( learnInfo . LevelUpEggMoves . Contains ) ;
2017-12-05 04:16:54 +00:00
if ( RegularEggMovesLearned . Count ! = 0 & & learnInfo . EventEggMoves . Count ! = 0 )
2017-05-28 04:17:53 +00:00
{
2017-09-02 06:15:57 +00:00
// Moves that are egg moves or event egg moves but not both
2021-04-19 01:18:09 +00:00
var IncompatibleEggMoves = RegularEggMovesLearned . Except ( learnInfo . EventEggMoves ) . Union ( learnInfo . EventEggMoves . Except ( RegularEggMovesLearned ) ) ;
2017-09-02 06:15:57 +00:00
foreach ( int m in IncompatibleEggMoves )
{
2021-04-19 01:18:09 +00:00
bool isEvent = learnInfo . EventEggMoves . Contains ( m ) ;
if ( isEvent )
{
if ( ! learnInfo . EggMovesLearned . Contains ( m ) )
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , LMoveEggIncompatibleEvent , CurrentMove ) ;
}
else
{
if ( learnInfo . EggMovesLearned . Contains ( m ) )
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , LMoveEggIncompatible , CurrentMove ) ;
else if ( learnInfo . LevelUpEggMoves . Contains ( m ) )
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , LMoveEventEggLevelUp , CurrentMove ) ;
}
2017-09-02 06:15:57 +00:00
}
2017-05-28 04:17:53 +00:00
}
2020-12-29 08:58:08 +00:00
else if ( enc is not EncounterEgg )
2017-05-28 04:17:53 +00:00
{
2019-03-17 03:07:30 +00:00
// Event eggs cannot inherit moves from parents; they are not bred.
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
var gift = enc is EncounterStatic { Gift : true } ; // otherwise, EncounterInvalid
2017-09-02 06:15:57 +00:00
foreach ( int m in RegularEggMovesLearned )
{
if ( learnInfo . EggMovesLearned . Contains ( m ) )
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , gift ? LMoveEggMoveGift : LMoveEggInvalidEvent , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
else if ( learnInfo . LevelUpEggMoves . Contains ( m ) )
Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately (#3222)
* Track a PKM's Box,Slot,StorageFlags,Identifier metadata separately
Don't store within the object, track the slot origin data separately.
Batch editing now pre-filters if using Box/Slot/Identifier logic; split up mods/filters as they're starting to get pretty hefty.
- Requesting a Box Data report now shows all slots in the save file (party, misc)
- Can now exclude backup saves from database search via toggle (separate from settings preventing load entirely)
- Replace some linq usages with direct code
* Remove WasLink virtual in PKM
Inline any logic, since we now have encounter objects to indicate matching, rather than the proto-legality logic checking properties of a PKM.
* Use Fateful to directly check gen5 mysterygift origins
No other encounter types in gen5 apply Fateful
* Simplify double ball comparison
Used to be separate for deferral cases, now no longer needed to be separate.
* Grab move/relearn reference and update locally
Fix relearn move identifier
* Inline defog HM transfer preference check
HasMove is faster than getting moves & checking contains. Skips allocation by setting values directly.
* Extract more met location metadata checks: WasBredEgg
* Replace Console.Write* with Debug.Write*
There's no console output UI, so don't include them in release builds.
* Inline WasGiftEgg, WasEvent, and WasEventEgg logic
Adios legality tags that aren't entirely correct for the specific format. Just put the computations in EncounterFinder.
2021-06-23 03:23:48 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , gift ? LMoveEggInvalidEventLevelUpGift : LMoveEggInvalidEventLevelUp , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
}
2017-05-28 04:17:53 +00:00
}
}
2018-08-03 03:11:42 +00:00
2020-05-31 19:12:07 +00:00
private static void ParseRedYellowIncompatibleMoves ( PKM pkm , IList < CheckMoveResult > res , IReadOnlyList < int > currentMoves )
2018-07-02 04:34:17 +00:00
{
2020-05-31 19:12:07 +00:00
var incompatible = GetIncompatibleRBYMoves ( pkm , currentMoves ) ;
2018-07-02 04:34:17 +00:00
if ( incompatible . Count = = 0 )
return ;
for ( int m = 0 ; m < 4 ; m + + )
2018-08-03 03:11:42 +00:00
{
2020-05-31 19:12:07 +00:00
if ( incompatible . Contains ( currentMoves [ m ] ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , LG1MoveLearnSameLevel , CurrentMove ) ;
2018-08-03 03:11:42 +00:00
}
2018-07-02 04:34:17 +00:00
}
2020-05-31 19:12:07 +00:00
private static IList < int > GetIncompatibleRBYMoves ( PKM pkm , IReadOnlyList < int > currentMoves )
2017-05-28 04:17:53 +00:00
{
2019-03-17 03:07:30 +00:00
// Check moves that are learned at the same level in Red/Blue and Yellow, these are illegal because there is no Move Reminder in Gen1.
// There are only two incompatibilities for Gen1; there are no illegal combination in Gen2.
2017-09-02 06:15:57 +00:00
switch ( pkm . Species )
2017-05-28 04:17:53 +00:00
{
2018-07-02 04:34:17 +00:00
// Vaporeon in Yellow learns Mist and Haze at level 42, Mist can only be learned if it leveled up in the daycare
2017-09-02 06:15:57 +00:00
// Vaporeon in Red/Blue learns Acid Armor at level 42 and level 47 in Yellow
2021-01-16 20:01:40 +00:00
case ( int ) Species . Vaporeon when pkm . CurrentLevel < 47 & & currentMoves . Contains ( ( int ) Move . AcidArmor ) :
2018-10-07 22:36:32 +00:00
{
var incompatible = new List < int > ( 3 ) ;
2021-01-16 20:01:40 +00:00
if ( currentMoves . Contains ( ( int ) Move . Mist ) )
incompatible . Add ( ( int ) Move . Mist ) ;
if ( currentMoves . Contains ( ( int ) Move . Haze ) )
incompatible . Add ( ( int ) Move . Haze ) ;
2017-12-05 04:16:54 +00:00
if ( incompatible . Count ! = 0 )
2021-01-16 20:01:40 +00:00
incompatible . Add ( ( int ) Move . AcidArmor ) ;
2018-10-07 22:36:32 +00:00
return incompatible ;
}
2017-09-02 06:15:57 +00:00
// Flareon in Yellow learns Smog at level 42
// Flareon in Red Blue learns Leer at level 42 and level 47 in Yellow
2021-01-16 20:01:40 +00:00
case ( int ) Species . Flareon when pkm . CurrentLevel < 47 & & currentMoves . Contains ( ( int ) Move . Leer ) & & currentMoves . Contains ( ( int ) Move . Smog ) :
return new [ ] { ( int ) Move . Leer , ( int ) Move . Smog } ;
2017-09-02 06:15:57 +00:00
2019-03-17 03:07:30 +00:00
default : return Array . Empty < int > ( ) ;
}
2017-05-28 04:17:53 +00:00
}
2018-07-02 04:34:17 +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
private static void ParseEvolutionsIncompatibleMoves ( PKM pkm , IList < CheckMoveResult > res , IReadOnlyList < int > moves , IReadOnlyList < int > tmhm )
2017-05-28 04:17:53 +00:00
{
2018-10-10 04:07:13 +00:00
GBRestrictions . GetIncompatibleEvolutionMoves ( pkm , moves , tmhm ,
out var prevSpeciesID ,
out var incompatPrev ,
out var incompatCurr ) ;
2017-05-28 04:17:53 +00:00
2018-10-10 04:07:13 +00:00
if ( prevSpeciesID = = 0 )
return ;
2018-08-03 03:11:42 +00:00
2018-10-10 04:07:13 +00:00
var prev = SpeciesStrings [ prevSpeciesID ] ;
var curr = SpeciesStrings [ pkm . Species ] ;
for ( int m = 0 ; m < 4 ; m + + )
2018-10-07 22:36:32 +00:00
{
2018-10-10 04:07:13 +00:00
if ( incompatCurr . Contains ( moves [ m ] ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , string . Format ( LMoveEvoFLower , curr , prev ) , CurrentMove ) ;
2018-10-10 04:07:13 +00:00
if ( incompatPrev . Contains ( moves [ m ] ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , string . Format ( LMoveEvoFHigher , curr , prev ) , CurrentMove ) ;
2018-10-07 22:36:32 +00:00
}
}
2020-05-31 19:12:07 +00:00
private static void ParseShedinjaEvolveMoves ( PKM pkm , IList < CheckMoveResult > res , IReadOnlyList < int > currentMoves , IReadOnlyList < IReadOnlyList < EvoCriteria > > evos )
2017-05-28 04:17:53 +00:00
{
var ShedinjaEvoMovesLearned = new List < int > ( ) ;
2020-04-12 18:28:03 +00:00
var format = pkm . Format ;
for ( int gen = Math . Min ( format , 4 ) ; gen > = 3 ; gen - - )
2017-05-28 04:17:53 +00:00
{
2020-04-12 18:28:03 +00:00
if ( evos [ gen ] . Count ! = 2 )
continue ; // Was not evolved in this generation
2022-01-03 03:12:35 +00:00
if ( gen = = 4 & & pkm . Ball ! = 4 & & ! ( pkm . Ball = = ( int ) Ball . Sport & & pkm . HGSS ) )
2020-04-12 18:28:03 +00:00
continue ; // Was definitively evolved in Gen3
2020-01-12 22:34:29 +00:00
var maxLevel = pkm . CurrentLevel ;
2020-06-21 00:44:05 +00:00
var ninjaskMoves = MoveList . GetShedinjaEvolveMoves ( pkm , gen , maxLevel ) ;
2020-04-12 18:28:03 +00:00
bool native = gen = = format ;
2017-05-28 04:17:53 +00:00
for ( int m = 0 ; m < 4 ; m + + )
{
2017-12-15 00:16:47 +00:00
if ( IsCheckValid ( res [ m ] ) ) // already validated
2017-05-28 04:17:53 +00:00
continue ;
2020-05-31 19:12:07 +00:00
if ( ! ninjaskMoves . Contains ( currentMoves [ m ] ) )
2017-05-28 04:17:53 +00:00
continue ;
2018-10-07 22:36:32 +00:00
var msg = native ? LMoveNincadaEvo : string . Format ( LMoveNincadaEvoF_0 , gen ) ;
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( ShedinjaEvo , gen , Valid , msg , CurrentMove ) ;
2017-05-28 04:17:53 +00:00
ShedinjaEvoMovesLearned . Add ( m ) ;
}
}
2018-06-09 23:04:06 +00:00
if ( ShedinjaEvoMovesLearned . Count = = 0 )
return ;
2018-02-02 03:57:02 +00:00
if ( ShedinjaEvoMovesLearned . Count > 1 )
{
// Can't have more than one Ninjask exclusive move on Shedinja
foreach ( int m in ShedinjaEvoMovesLearned )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , LMoveNincada , CurrentMove ) ;
2017-05-28 04:17:53 +00:00
return ;
2018-02-02 03:57:02 +00:00
}
// Double check that the Ninjask move level isn't less than any Nincada move level
int move = ShedinjaEvoMovesLearned [ 0 ] ;
int g = res [ move ] . Generation ;
2020-06-21 00:44:05 +00:00
int levelJ = MoveList . GetShedinjaMoveLevel ( ( int ) Species . Ninjask , currentMoves [ move ] , g ) ;
2018-02-02 03:57:02 +00:00
for ( int m = 0 ; m < 4 ; m + + )
{
2018-03-18 22:30:19 +00:00
if ( m ! = move )
2018-02-02 03:57:02 +00:00
continue ;
2019-02-22 04:44:31 +00:00
if ( res [ m ] . Source ! = LevelUp )
2018-02-02 03:57:02 +00:00
continue ;
2020-06-21 00:44:05 +00:00
int levelS = MoveList . GetShedinjaMoveLevel ( ( int ) Species . Shedinja , currentMoves [ m ] , res [ m ] . Generation ) ;
2018-02-02 03:57:02 +00:00
if ( levelS > 0 )
continue ;
2017-05-28 04:17:53 +00:00
2020-06-21 00:44:05 +00:00
int levelN = MoveList . GetShedinjaMoveLevel ( ( int ) Species . Nincada , currentMoves [ m ] , res [ m ] . Generation ) ;
2018-02-02 03:57:02 +00:00
if ( levelN > levelJ )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , string . Format ( LMoveEvoFHigher , SpeciesStrings [ ( int ) Species . Nincada ] , SpeciesStrings [ ( int ) Species . Ninjask ] ) , CurrentMove ) ;
2018-02-02 03:57:02 +00:00
}
2017-05-28 04:17:53 +00:00
}
2018-08-03 03:11:42 +00:00
2017-12-15 00:16:47 +00:00
private static void GetHMCompatibility ( PKM pkm , IReadOnlyList < CheckResult > res , int gen , IReadOnlyList < int > moves , out bool [ ] HMLearned , out bool KnowDefogWhirlpool )
2017-09-02 06:15:57 +00:00
{
HMLearned = new bool [ 4 ] ;
// Check if pokemon knows HM moves from generation 3 and 4 but are not valid yet, that means it cant learn the HMs in future generations
if ( gen = = 4 & & pkm . Format > 4 )
{
2017-12-15 00:16:47 +00:00
IsHMSource ( HMLearned , Legal . HM_4_RemovePokeTransfer ) ;
2017-09-02 06:15:57 +00:00
KnowDefogWhirlpool = moves . Where ( ( m , i ) = > IsDefogWhirl ( m ) & & IsCheckInvalid ( res [ i ] ) ) . Count ( ) = = 2 ;
return ;
}
KnowDefogWhirlpool = false ;
if ( gen = = 3 & & pkm . Format > 3 )
2017-12-15 00:16:47 +00:00
IsHMSource ( HMLearned , Legal . HM_3 ) ;
2017-09-02 06:15:57 +00:00
2017-12-15 00:16:47 +00:00
void IsHMSource ( IList < bool > flags , ICollection < int > source )
2017-09-02 06:15:57 +00:00
{
for ( int i = 0 ; i < 4 ; i + + )
flags [ i ] = IsCheckInvalid ( res [ i ] ) & & source . Contains ( moves [ i ] ) ;
}
}
2018-08-03 03:11:42 +00:00
2021-01-26 04:58:56 +00:00
private static bool IsDefogWhirl ( int move ) = > move is ( int ) Move . Defog or ( int ) Move . Whirlpool ;
2020-09-06 18:24:54 +00:00
private static bool IsCheckInvalid ( CheckResult ? chk ) = > ! ( chk ? . Valid ? ? false ) ;
private static bool IsCheckValid ( CheckResult ? chk ) = > chk ? . Valid ? ? false ;
2018-08-03 03:11:42 +00:00
2022-03-06 21:04:56 +00:00
private static void FlagIncompatibleTransferHMs45 ( CheckMoveResult [ ] res , IReadOnlyList < int > currentMoves , int gen , ReadOnlySpan < bool > HMLearned , bool KnowDefogWhirlpool )
2017-09-02 06:15:57 +00:00
{
2018-06-09 03:36:52 +00:00
// After all the moves from the generations 3 and 4,
2017-09-02 06:15:57 +00:00
// including egg moves if is the origin generation because some hidden moves are also special egg moves in gen 3
2018-06-09 03:36:52 +00:00
// Check if the marked hidden moves that were invalid at the start are now marked as valid, that means
2017-09-02 06:15:57 +00:00
// the hidden move was learned in gen 3 or 4 but was not removed when transfer to 4 or 5
if ( KnowDefogWhirlpool )
{
2020-05-31 19:12:07 +00:00
int invalidCount = currentMoves . Where ( ( m , i ) = > IsDefogWhirl ( m ) & & IsCheckValid ( res [ i ] ) ) . Count ( ) ;
2017-09-02 06:15:57 +00:00
if ( invalidCount = = 2 ) // can't know both at the same time
2018-08-03 03:11:42 +00:00
{
2017-09-02 06:15:57 +00:00
for ( int i = 0 ; i < 4 ; i + + ) // flag both moves
2018-08-03 03:11:42 +00:00
{
2020-05-31 19:12:07 +00:00
if ( IsDefogWhirl ( currentMoves [ i ] ) )
2021-01-09 21:05:56 +00:00
res [ i ] = new CheckMoveResult ( res [ i ] , Invalid , LTransferMoveG4HM , CurrentMove ) ;
2018-08-03 03:11:42 +00:00
}
}
2017-09-02 06:15:57 +00:00
}
// Flag moves that are only legal when learned from a past-gen HM source
2022-03-06 21:04:56 +00:00
for ( int i = 0 ; i < HMLearned . Length ; i + + )
2018-08-03 03:11:42 +00:00
{
2017-09-02 06:15:57 +00:00
if ( HMLearned [ i ] & & IsCheckValid ( res [ i ] ) )
2021-01-09 21:05:56 +00:00
res [ i ] = new CheckMoveResult ( res [ i ] , Invalid , string . Format ( LTransferMoveHM , gen , gen + 1 ) , CurrentMove ) ;
2018-08-03 03:11:42 +00:00
}
2017-09-02 06:15:57 +00:00
}
2017-05-28 04:17:53 +00:00
2021-04-17 20:13:03 +00:00
private static CheckMoveResult [ ] VerifyPreRelearnEggBase ( int [ ] currentMoves , EncounterEgg e )
2017-09-04 20:48:10 +00:00
{
2021-04-05 01:30:01 +00:00
CheckMoveResult [ ] result = new CheckMoveResult [ 4 ] ;
2021-04-17 20:13:03 +00:00
_ = VerifyRelearnMoves . VerifyEggMoveset ( e , result , currentMoves , CurrentMove ) ;
2021-04-05 01:30:01 +00:00
return result ;
2017-09-04 20:48:10 +00:00
}
2017-05-28 04:17:53 +00:00
2022-03-06 21:04:56 +00:00
private static void VerifyNoEmptyDuplicates ( ReadOnlySpan < int > moves , CheckMoveResult [ ] res )
2017-05-28 04:17:53 +00:00
{
bool emptySlot = false ;
for ( int i = 0 ; i < 4 ; i + + )
{
2020-08-30 22:43:29 +00:00
var move = moves [ i ] ;
if ( move = = 0 )
{
2017-05-28 04:17:53 +00:00
emptySlot = true ;
2020-08-30 22:43:29 +00:00
continue ;
}
// If an empty slot was noted for a prior move, flag the empty slots.
if ( emptySlot )
{
FlagEmptySlotsBeforeIndex ( moves , res , i ) ;
emptySlot = false ;
continue ;
}
// Check for same move in next move slots
FlagDuplicateMovesAfterIndex ( moves , res , i , move ) ;
}
}
2022-03-06 21:04:56 +00:00
private static void FlagDuplicateMovesAfterIndex ( ReadOnlySpan < int > moves , CheckMoveResult [ ] res , int index , int move )
2020-08-30 22:43:29 +00:00
{
2021-07-23 02:08:56 +00:00
for ( int i = index + 1 ; i < 4 ; i + + )
2020-08-30 22:43:29 +00:00
{
2021-07-23 02:08:56 +00:00
if ( moves [ i ] ! = move )
2020-08-30 22:43:29 +00:00
continue ;
2021-07-23 02:08:56 +00:00
res [ index ] = new CheckMoveResult ( res [ index ] , Invalid , LMoveSourceDuplicate ) ;
2020-08-30 22:43:29 +00:00
return ;
}
}
2022-03-06 21:04:56 +00:00
private static void FlagEmptySlotsBeforeIndex ( ReadOnlySpan < int > moves , CheckMoveResult [ ] res , int index )
2020-08-30 22:43:29 +00:00
{
2021-07-23 02:08:56 +00:00
for ( int i = index - 1 ; i > = 0 ; i - - )
2020-08-30 22:43:29 +00:00
{
2021-07-23 02:08:56 +00:00
if ( moves [ i ] ! = 0 )
2020-08-30 22:43:29 +00:00
return ;
2021-07-23 02:08:56 +00:00
res [ i ] = new CheckMoveResult ( res [ i ] , Invalid , LMoveSourceEmpty ) ;
2017-05-28 04:17:53 +00:00
}
}
}
}