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 ;
}
2020-05-31 19:12:07 +00:00
private static CheckMoveResult [ ] ParseMovesForEncounters ( PKM pkm , LegalInfo info , IReadOnlyList < 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
2018-10-07 02:44:50 +00:00
var restrict = new LevelUpRestriction ( pkm , info ) ;
2019-11-25 16:52:03 +00:00
info . EncounterMoves = new ValidEncounterMoves ( pkm , restrict , info . EncounterMatch ) ;
2017-05-28 04:17:53 +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
IReadOnlyList < int > defaultG1LevelMoves = Array . Empty < int > ( ) ;
IReadOnlyList < int > defaultG2LevelMoves = Array . Empty < int > ( ) ;
2017-05-28 04:17:53 +00:00
var defaultTradeback = pkm . TradebackStatus ;
2018-10-07 02:44:50 +00:00
bool gb = false ;
2020-05-17 19:32:28 +00:00
int gen = info . EncounterMatch . Generation ;
if ( gen < = 2 )
2017-06-07 23:15:13 +00:00
{
2018-10-07 02:44:50 +00:00
gb = true ;
defaultG1LevelMoves = info . EncounterMoves . LevelUpMoves [ 1 ] ;
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 ( pkm . InhabitedGeneration ( 2 ) )
defaultG2LevelMoves = info . EncounterMoves . LevelUpMoves [ 2 ] ;
2017-05-28 04:17:53 +00:00
// Generation 1 can have different minimum level in different encounter of the same species; update valid level moves
2020-05-17 19:32:28 +00:00
UpdateGen1LevelUpMoves ( pkm , info . EncounterMoves , restrict . MinimumLevelGen1 , gen , info ) ;
2017-09-02 06:45:47 +00:00
// The same for Generation 2; if move reminder from Stadium 2 is not allowed
2018-10-06 02:58:30 +00:00
if ( ! ParseSettings . AllowGen2MoveReminder ( pkm ) & & pkm . InhabitedGeneration ( 2 ) )
2020-05-17 19:32:28 +00:00
UpdateGen2LevelUpMoves ( pkm , info . EncounterMoves , restrict . MinimumLevelGen2 , gen , info ) ;
2017-06-07 23:15:13 +00:00
}
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 ;
2017-12-12 00:01:24 +00:00
// not valid
2018-10-07 02:44:50 +00:00
if ( gb ) // restore generation 1 and 2 moves
2017-06-07 23:15:13 +00:00
{
2017-06-18 01:37:19 +00:00
info . EncounterMoves . LevelUpMoves [ 1 ] = defaultG1LevelMoves ;
2017-06-07 23:15:13 +00:00
if ( pkm . InhabitedGeneration ( 2 ) )
2017-06-18 01:37:19 +00:00
info . EncounterMoves . LevelUpMoves [ 2 ] = defaultG2LevelMoves ;
2017-06-07 23:15:13 +00:00
}
2017-05-28 04:17:53 +00:00
pkm . TradebackStatus = defaultTradeback ;
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 ) ;
2020-05-31 19:12:07 +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 [ ] ParseMovesIsEggPreRelearn ( PKM pkm , IReadOnlyList < int > currentMoves , EncounterEgg e )
2017-05-28 04:17:53 +00:00
{
2020-05-31 19:12:07 +00:00
var infoset = new EggInfoSource ( pkm , e ) ;
return VerifyPreRelearnEggBase ( pkm , currentMoves , infoset ) ;
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 [ ] ParseMovesWasEggPreRelearn ( PKM pkm , IReadOnlyList < int > currentMoves , LegalInfo info , EncounterEgg e )
2017-05-28 04:17:53 +00:00
{
2017-08-01 00:09:16 +00:00
var EventEggMoves = GetSpecialMoves ( info . EncounterMatch ) ;
2020-01-19 00:46:38 +00:00
bool notEvent = EventEggMoves . Count = = 0 ;
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 ;
2020-12-08 04:54:55 +00:00
var AllowLevelUp = notEvent & & ! 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
2017-06-07 03:10:05 +00:00
var TradebackPreevo = pkm . Format = = 2 & & info . EncounterMatch . Species > 151 ;
2018-05-12 15:13:39 +00:00
var NonTradebackLvlMoves = TradebackPreevo
2020-06-21 00:44:05 +00:00
? MoveList . GetExclusivePreEvolutionMoves ( pkm , info . EncounterMatch . 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 )
Egg = Egg . Where ( m = > m < = Legal . MaxMoveID_1 ) . ToArray ( ) ;
2017-05-28 04:17:53 +00:00
2018-03-31 05:11:24 +00:00
bool volt = ( info . Generation > 3 | | e . Version = = GameVersion . E ) & & Legal . LightBall . Contains ( pkm . Species ) ;
2019-04-17 06:07:47 +00:00
var Special = volt & & notEvent ? new [ ] { 344 } : 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 ,
2017-09-02 06:15:57 +00:00
SpecialSource = Special ,
NonTradeBackLevelUpMoves = NonTradebackLvlMoves ,
EggLevelUpSource = LevelUp ,
EggMoveSource = Egg ,
EggEventSource = EventEggMoves ,
} ;
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
{
2020-05-31 19:12:07 +00:00
res [ i ] = Legal . InvalidSketch . Contains ( currentMoves [ i ] )
2021-01-09 21:05:56 +00:00
? new CheckMoveResult ( Unknown , pkm . Format , Invalid , LMoveSourceInvalidSketch , CurrentMove )
: new CheckMoveResult ( Sketch , pkm . Format , 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
2020-05-31 19:12:07 +00:00
private static CheckMoveResult [ ] ParseMovesPre3DS ( PKM pkm , IReadOnlyList < 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
? ParseMovesIsEggPreRelearn ( pkm , currentMoves , e )
: ParseMovesWasEggPreRelearn ( pkm , currentMoves , info , e ) ;
2017-05-28 04:17:53 +00:00
}
2020-05-17 19:32:28 +00:00
int gen = info . EncounterMatch . Generation ;
if ( gen < = 2 & & ( gen = = 1 | | ( gen = = 2 & & ! ParseSettings . AllowGen2MoveReminder ( pkm ) ) ) ) // fixed encounter moves without relearning
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 ] ;
2017-05-28 04:17:53 +00:00
var G1Encounter = info . EncounterMatch ;
2018-08-02 01:30:51 +00:00
var InitialMoves = Array . Empty < int > ( ) ;
2019-02-22 05:54:41 +00:00
var SpecialMoves = GetSpecialMoves ( info . EncounterMatch ) ;
2020-05-17 19:32:28 +00:00
var games = info . EncounterMatch . Generation = = 1 ? GBRestrictions . GetGen1Versions ( info ) : GBRestrictions . GetGen2Versions ( info , pkm . Korean ) ;
2018-06-19 02:57:32 +00:00
foreach ( var ver in games )
2017-05-28 04:17:53 +00:00
{
2018-06-19 02:57:32 +00:00
var VerInitialMoves = MoveLevelUp . GetEncounterMoves ( G1Encounter . Species , 0 , G1Encounter . LevelMin , ver ) ;
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 ) ;
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-01-11 02:15:33 +00:00
private static IReadOnlyList < int > GetSpecialMoves ( IEncounterable 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 ) ;
2020-12-22 01:12:39 +00:00
var required = pkm is not PK1 pk1 ? 1 : GBRestrictions . GetRequiredMoveCount ( pk1 , source . CurrentMoves , info , source . Base ) ;
2017-05-28 04:17:53 +00:00
2020-06-19 23:51:15 +00:00
// Special considerations!
int reset = 0 ;
2020-12-29 08:58:08 +00:00
if ( pkm is IBattleVersion { BattleVersion : not 0 } v )
2020-06-19 23:51:15 +00:00
{
reset = ( ( 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 + + )
{
2017-09-02 06:15:57 +00:00
if ( source . CurrentMoves [ m ] = = 0 )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( None , pkm . Format , m < required ? Fishy : Valid , LMoveSourceEmpty , CurrentMove ) ;
2020-06-19 23:51:15 +00:00
else if ( reset = = 0 & & info . EncounterMoves . Relearn . Contains ( source . CurrentMoves [ m ] ) )
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
2020-11-19 23:34:06 +00:00
int [ ] generations = GetGenMovesCheckOrder ( pkm , info . EncounterOriginal . Generation ) ;
2017-07-18 23:21:31 +00:00
if ( pkm . Format < = 2 )
generations = generations . Where ( z = > z < info . EncounterMoves . LevelUpMoves . Length ) . ToArray ( ) ;
2020-06-19 23:51:15 +00:00
if ( reset ! = 0 )
generations = generations . Where ( z = > z > = reset ) . ToArray ( ) ;
2017-09-02 06:15:57 +00:00
2017-09-20 16:04:30 +00:00
int lastgen = generations . LastOrDefault ( ) ;
2017-05-28 04:17:53 +00:00
foreach ( var gen in generations )
{
2017-09-02 06:15:57 +00:00
ParseMovesByGeneration ( pkm , res , gen , info , moveInfo , lastgen ) ;
if ( AllParsed ( ) )
return res ;
}
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
}
2017-05-28 04:17:53 +00:00
2017-09-02 06:15:57 +00:00
// Pokemon that evolved by leveling up while learning a specific move
// This pokemon could only have 3 moves from preevolutions that are not the move used to evolved
2019-02-22 05:54:41 +00:00
// including special and eggs moves before relearn generations
2020-11-02 21:11:51 +00:00
if ( EvolutionRestrictions . SpeciesEvolutionWithMove . Contains ( pkm . Species ) )
2018-09-15 05:37:47 +00:00
ParseEvolutionLevelupMove ( pkm , res , learnInfo . Source . CurrentMoves , info ) ;
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
}
2019-03-17 03:07:30 +00:00
if ( pkm . TradebackStatus = = TradebackType . Any & & info . Generation ! = gen )
2017-09-02 06:15:57 +00:00
pkm . TradebackStatus = TradebackType . WasTradeback ;
}
}
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
2017-09-02 06:15:57 +00:00
if ( gen = = 1 & & pkm . Format = = 1 & & pkm . Gen1_NotTradeback )
{
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
2020-10-04 19:21:05 +00:00
private static void ParseMovesByGenerationLast ( PKM pkm , CheckMoveResult [ ] res , LearnInfo learnInfo , IEncounterable 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
}
2020-10-04 19:21:05 +00:00
if ( gen < = 2 & & learnInfo . Gen1Moves . Count = = 0 )
pkm . TradebackStatus = TradebackType . Any ;
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 ) ;
2020-12-11 04:42:30 +00:00
if ( pkm . TradebackStatus = = TradebackType . Any & & pkm . Generation = = 1 )
2017-09-02 06:15:57 +00:00
pkm . TradebackStatus = TradebackType . WasTradeback ;
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
}
2020-12-11 04:42:30 +00:00
if ( pkm . TradebackStatus = = TradebackType . Any & & pkm . Generation = = 1 )
2017-09-02 06:15:57 +00:00
pkm . TradebackStatus = TradebackType . WasTradeback ;
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
2020-08-05 05:56:55 +00:00
private static void ParseEggMovesRemaining ( PKM pkm , CheckMoveResult [ ] res , LearnInfo learnInfo , IEncounterable 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
var RegularEggMovesLearned = learnInfo . EggMovesLearned . Union ( learnInfo . LevelUpEggMoves ) . ToList ( ) ;
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
var IncompatibleEggMoves = RegularEggMovesLearned . Except ( learnInfo . EventEggMoves ) . Union ( learnInfo . EventEggMoves . Except ( RegularEggMovesLearned ) ) . ToList ( ) ;
2017-12-05 04:16:54 +00:00
if ( IncompatibleEggMoves . Count = = 0 )
2017-09-02 06:15:57 +00:00
return ;
foreach ( int m in IncompatibleEggMoves )
{
if ( learnInfo . EventEggMoves . Contains ( m ) & & ! learnInfo . EggMovesLearned . Contains ( m ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , LMoveEggIncompatibleEvent , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
else if ( ! learnInfo . EventEggMoves . Contains ( m ) & & learnInfo . EggMovesLearned . Contains ( m ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , LMoveEggIncompatible , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
else if ( ! learnInfo . EventEggMoves . Contains ( m ) & & learnInfo . LevelUpEggMoves . Contains ( m ) )
2021-01-09 21:05:56 +00:00
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.
2017-09-02 06:15:57 +00:00
foreach ( int m in RegularEggMovesLearned )
{
if ( learnInfo . EggMovesLearned . Contains ( m ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , pkm . WasGiftEgg ? LMoveEggMoveGift : LMoveEggInvalidEvent , CurrentMove ) ;
2017-09-02 06:15:57 +00:00
else if ( learnInfo . LevelUpEggMoves . Contains ( m ) )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , pkm . WasGiftEgg ? 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
2020-05-31 19:12:07 +00:00
case ( int ) Species . Vaporeon when pkm . CurrentLevel < 47 & & currentMoves . Contains ( 151 ) :
2018-10-07 22:36:32 +00:00
{
var incompatible = new List < int > ( 3 ) ;
2020-05-31 19:12:07 +00:00
if ( currentMoves . Contains ( 54 ) )
2017-09-02 06:15:57 +00:00
incompatible . Add ( 54 ) ;
2020-05-31 19:12:07 +00:00
if ( currentMoves . Contains ( 114 ) )
2017-09-02 06:15:57 +00:00
incompatible . Add ( 114 ) ;
2017-12-05 04:16:54 +00:00
if ( incompatible . Count ! = 0 )
2017-09-02 06:15:57 +00:00
incompatible . Add ( 151 ) ;
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
2020-05-31 19:12:07 +00:00
case ( int ) Species . Flareon when pkm . CurrentLevel < 47 & & currentMoves . Contains ( 43 ) & & currentMoves . Contains ( 123 ) :
2018-10-07 22:36:32 +00:00
return new [ ] { 43 , 123 } ;
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
if ( gen = = 4 & & pkm . Ball ! = 4 )
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
2020-05-31 19:12:07 +00:00
private static void ParseEvolutionLevelupMove ( PKM pkm , IList < CheckMoveResult > res , IReadOnlyList < int > currentMoves , LegalInfo info )
2017-05-28 04:17:53 +00:00
{
// Ignore if there is an invalid move or an empty move, this validation is only for 4 non-empty moves that are all valid, but invalid as a 4 combination
// Ignore Mr. Mime and Sudowodoo from generations 1 to 3, they cant be evolved from Bonsly or Munchlax
// Ignore if encounter species is the evolution species, the pokemon was not evolved by the player
2018-09-15 23:13:17 +00:00
if ( info . EncounterMatch . Species = = pkm . Species )
return ;
2020-11-02 21:11:51 +00:00
if ( ! res . All ( r = > r ? . Valid ? ? false ) | | currentMoves . Any ( m = > m = = 0 ) | | ( EvolutionRestrictions . BabyEvolutionWithMove . Contains ( pkm . Species ) & & info . Generation < = 3 ) )
2017-05-28 04:17:53 +00:00
return ;
2020-06-21 00:44:05 +00:00
var ValidMoves = MoveList . GetValidPostEvolutionMoves ( pkm , pkm . Species , info . EvoChainsAllGens , GameVersion . Any ) ;
2018-08-03 03:11:42 +00:00
2017-09-02 06:45:47 +00:00
// Add the evolution moves to valid moves in case some of these moves could not be learned after evolving
2017-05-28 04:17:53 +00:00
switch ( pkm . Species )
{
2019-06-01 17:22:49 +00:00
case ( int ) Species . MrMime : // Mr. Mime (Mime Jr with Mimic)
case ( int ) Species . Sudowoodo : // Sudowoodo (Bonsly with Mimic)
2017-05-28 04:17:53 +00:00
ValidMoves . Add ( 102 ) ;
break ;
2019-06-01 17:22:49 +00:00
case ( int ) Species . Ambipom : // Ambipom (Aipom with Double Hit)
2017-05-28 04:17:53 +00:00
ValidMoves . Add ( 458 ) ;
break ;
2019-06-01 17:22:49 +00:00
case ( int ) Species . Lickilicky : // Lickilicky (Lickitung with Rollout)
2017-05-28 04:17:53 +00:00
ValidMoves . Add ( 205 ) ;
break ;
2019-06-01 17:22:49 +00:00
case ( int ) Species . Tangrowth : // Tangrowth (Tangela with Ancient Power)
case ( int ) Species . Yanmega : // Yanmega (Yanma with Ancient Power)
case ( int ) Species . Mamoswine : // Mamoswine (Piloswine with Ancient Power)
2017-05-28 04:17:53 +00:00
ValidMoves . Add ( 246 ) ;
break ;
2019-06-01 17:22:49 +00:00
case ( int ) Species . Sylveon : // Sylveon (Eevee with Fairy Move)
2020-11-02 21:11:51 +00:00
// Add every fairy moves without checking if Eevee learn it or not; pokemon moves are determined legal before this function
ValidMoves . AddRange ( EvolutionRestrictions . FairyMoves ) ;
2017-05-28 04:17:53 +00:00
break ;
2019-06-01 17:22:49 +00:00
case ( int ) Species . Tsareena : // Tsareena (Steenee with Stomp)
2017-05-28 04:17:53 +00:00
ValidMoves . Add ( 023 ) ;
break ;
}
2020-05-31 19:12:07 +00:00
if ( currentMoves . Any ( m = > ValidMoves . Contains ( m ) ) )
2017-05-28 04:17:53 +00:00
return ;
for ( int m = 0 ; m < 4 ; m + + )
2021-01-09 21:05:56 +00:00
res [ m ] = new CheckMoveResult ( res [ m ] , Invalid , string . Format ( LMoveEvoFCombination_0 , SpeciesStrings [ pkm . Species ] ) , CurrentMove ) ;
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
2017-09-02 06:15:57 +00:00
private static bool IsDefogWhirl ( int move ) = > move = = 250 | | move = = 432 ;
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
2020-05-31 19:12:07 +00:00
private static void FlagIncompatibleTransferHMs45 ( CheckMoveResult [ ] res , IReadOnlyList < int > currentMoves , int gen , IReadOnlyList < 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
2020-05-31 19:12:07 +00:00
for ( int i = 0 ; i < HMLearned . Count ; 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
/* Similar to verifyRelearnEgg but in pre relearn generation is the moves what should match the expected order but only if the pokemon is inside an egg */
2020-05-31 19:12:07 +00:00
private static CheckMoveResult [ ] VerifyPreRelearnEggBase ( PKM pkm , IReadOnlyList < int > currentMoves , EggInfoSource infoset )
2017-05-28 04:17:53 +00:00
{
2017-06-07 03:10:05 +00:00
CheckMoveResult [ ] res = new CheckMoveResult [ 4 ] ;
2020-12-11 04:42:30 +00:00
var gen = pkm . Generation ;
2017-05-28 04:17:53 +00:00
// Obtain level1 moves
2020-05-31 19:12:07 +00:00
var reqBase = GetRequiredBaseMoveCount ( currentMoves , infoset ) ;
2017-05-28 04:17:53 +00:00
2018-09-02 02:55:08 +00:00
var sb = new System . Text . StringBuilder ( ) ;
2017-05-28 04:17:53 +00:00
// Check if the required amount of Base Egg Moves are present.
2017-06-18 01:37:19 +00:00
for ( int i = 0 ; i < reqBase ; i + + )
2017-05-28 04:17:53 +00:00
{
2020-05-31 19:12:07 +00:00
if ( infoset . Base . Contains ( currentMoves [ i ] ) )
2017-05-28 04:17:53 +00:00
{
2021-01-09 21:05:56 +00:00
res [ i ] = new CheckMoveResult ( Initial , gen , Valid , LMoveRelearnEgg , CurrentMove ) ;
2017-06-18 01:37:19 +00:00
continue ;
2017-05-28 04:17:53 +00:00
}
2017-06-18 01:37:19 +00:00
// mark remaining base egg moves missing
for ( int z = i ; z < reqBase ; z + + )
2021-01-09 21:05:56 +00:00
res [ z ] = new CheckMoveResult ( Initial , gen , Invalid , LMoveRelearnEggMissing , CurrentMove ) ;
2017-05-28 04:17:53 +00:00
2017-06-18 01:37:19 +00:00
// provide the list of suggested base moves for the last required slot
2018-09-02 02:55:08 +00:00
sb . Append ( string . Join ( ", " , GetMoveNames ( infoset . Base ) ) ) ;
2017-06-18 01:37:19 +00:00
break ;
}
2018-09-02 02:55:08 +00:00
if ( sb . Length ! = 0 )
2018-10-07 02:44:50 +00:00
res [ reqBase > 0 ? reqBase - 1 : 0 ] . Comment = string . Format ( Environment . NewLine + LMoveFExpect_0 , sb ) ;
2017-05-28 04:17:53 +00:00
// Inherited moves appear after the required base moves.
2019-02-22 04:44:31 +00:00
var AllowInheritedSeverity = infoset . AllowInherited ? Valid : Invalid ;
2020-05-31 19:12:07 +00:00
for ( int i = reqBase ; i < 4 ; i + + )
2017-05-28 04:17:53 +00:00
{
2020-05-31 19:12:07 +00:00
if ( currentMoves [ i ] = = 0 ) // empty
2021-01-09 21:05:56 +00:00
res [ i ] = new CheckMoveResult ( None , gen , Valid , LMoveSourceEmpty , CurrentMove ) ;
2020-05-31 19:12:07 +00:00
else if ( infoset . Egg . Contains ( currentMoves [ i ] ) ) // inherited egg move
2021-01-09 21:05:56 +00:00
res [ i ] = new CheckMoveResult ( EggMove , gen , AllowInheritedSeverity , infoset . AllowInherited ? LMoveEggInherited : LMoveEggInvalidEvent , CurrentMove ) ;
2020-05-31 19:12:07 +00:00
else if ( infoset . LevelUp . Contains ( currentMoves [ i ] ) ) // inherited lvl moves
2021-01-09 21:05:56 +00:00
res [ i ] = new CheckMoveResult ( InheritLevelUp , gen , AllowInheritedSeverity , infoset . AllowInherited ? LMoveEggLevelUp : LMoveEggInvalidEventLevelUp , CurrentMove ) ;
2020-05-31 19:12:07 +00:00
else if ( infoset . TMHM . Contains ( currentMoves [ i ] ) ) // inherited TMHM moves
2021-01-09 21:05:56 +00:00
res [ i ] = new CheckMoveResult ( TMHM , gen , AllowInheritedSeverity , infoset . AllowInherited ? LMoveEggTMHM : LMoveEggInvalidEventTMHM , CurrentMove ) ;
2020-05-31 19:12:07 +00:00
else if ( infoset . Tutor . Contains ( currentMoves [ i ] ) ) // inherited tutor moves
2021-01-09 21:05:56 +00:00
res [ i ] = new CheckMoveResult ( Tutor , gen , AllowInheritedSeverity , infoset . AllowInherited ? LMoveEggInheritedTutor : LMoveEggInvalidEventTutor , CurrentMove ) ;
2017-05-28 04:17:53 +00:00
else // not inheritable, flag
2021-01-09 21:05:56 +00:00
res [ i ] = new CheckMoveResult ( Unknown , gen , Invalid , LMoveEggInvalid , CurrentMove ) ;
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 int GetRequiredBaseMoveCount ( IReadOnlyList < int > currentMoves , EggInfoSource infoset )
2017-09-04 20:48:10 +00:00
{
int baseCt = infoset . Base . Count ;
if ( baseCt > 4 ) baseCt = 4 ;
// Obtain Inherited moves
2020-05-31 19:12:07 +00:00
var inherited = currentMoves . Where ( m = > m ! = 0 & & infoset . IsInherited ( m ) ) . ToList ( ) ;
2017-09-04 20:48:10 +00:00
int inheritCt = inherited . Count ;
// Get required amount of base moves
2017-10-07 04:03:23 +00:00
int unique = infoset . Base . Union ( inherited ) . Count ( ) ;
2017-09-04 20:48:10 +00:00
int reqBase = inheritCt = = 4 | | baseCt + inheritCt > 4 ? 4 - inheritCt : baseCt ;
2020-05-31 19:12:07 +00:00
if ( currentMoves . Count ( m = > m ! = 0 ) < Math . Min ( 4 , infoset . Base . Count ) )
2017-09-04 20:48:10 +00:00
reqBase = Math . Min ( 4 , unique ) ;
return reqBase ;
}
2017-05-28 04:17:53 +00:00
2020-05-31 19:12:07 +00:00
private static void VerifyNoEmptyDuplicates ( IReadOnlyList < 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 ) ;
}
}
private static void FlagDuplicateMovesAfterIndex ( IReadOnlyList < int > moves , CheckMoveResult [ ] res , int i , int move )
{
for ( int j = i + 1 ; j < 4 ; j + + )
{
if ( moves [ j ] ! = move )
continue ;
res [ i ] = new CheckMoveResult ( res [ i ] , Invalid , LMoveSourceDuplicate ) ;
return ;
}
}
private static void FlagEmptySlotsBeforeIndex ( IReadOnlyList < int > moves , CheckMoveResult [ ] res , int i )
{
for ( int k = i - 1 ; k > = 0 ; k - - )
{
if ( moves [ k ] ! = 0 )
return ;
res [ k ] = new CheckMoveResult ( res [ k ] , Invalid , LMoveSourceEmpty ) ;
2017-05-28 04:17:53 +00:00
}
}
2018-08-03 03:11:42 +00:00
2018-06-09 15:04:40 +00:00
private static void UpdateGen1LevelUpMoves ( PKM pkm , ValidEncounterMoves EncounterMoves , int defaultLvlG1 , int generation , LegalInfo info )
2017-05-28 04:17:53 +00:00
{
2018-09-02 02:55:08 +00:00
if ( generation > = 3 )
return ;
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 lvlG1 = info . EncounterMatch . LevelMin + 1 ;
2018-09-02 02:55:08 +00:00
if ( lvlG1 = = defaultLvlG1 )
return ;
2020-12-22 05:24:16 +00:00
EncounterMoves . LevelUpMoves [ 1 ] = MoveList . GetValidMoves ( pkm , info . EvoChainsAllGens [ 1 ] , generation : 1 , minLvLG1 : lvlG1 , types : MoveSourceType . LevelUp ) . ToList ( ) ;
2017-06-07 23:15:13 +00:00
}
2018-08-03 03:11:42 +00:00
2018-06-09 15:04:40 +00:00
private static void UpdateGen2LevelUpMoves ( PKM pkm , ValidEncounterMoves EncounterMoves , int defaultLvlG2 , int generation , LegalInfo info )
2017-06-07 23:15:13 +00:00
{
2018-09-02 02:55:08 +00:00
if ( generation > = 3 )
return ;
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 lvlG2 = info . EncounterMatch . LevelMin + 1 ;
2018-09-02 02:55:08 +00:00
if ( lvlG2 = = defaultLvlG2 )
return ;
2020-12-22 05:24:16 +00:00
EncounterMoves . LevelUpMoves [ 2 ] = MoveList . GetValidMoves ( pkm , info . EvoChainsAllGens [ 2 ] , generation : 2 , minLvLG2 : defaultLvlG2 , types : MoveSourceType . LevelUp ) . ToList ( ) ;
2017-05-28 04:17:53 +00:00
}
2018-08-03 03:11:42 +00:00
2019-09-10 07:21:51 +00:00
/// <summary>
/// Gets the generation numbers in descending order for iterating over.
/// </summary>
2020-11-19 23:34:06 +00:00
public static int [ ] GetGenMovesCheckOrder ( PKM pkm , int origin )
2017-05-28 04:17:53 +00:00
{
2017-09-24 17:52:39 +00:00
if ( pkm . Format < 3 )
return GetGenMovesCheckOrderGB ( pkm , pkm . Format ) ;
if ( pkm . VC )
return GetGenMovesOrderVC ( pkm ) ;
2020-11-19 23:34:06 +00:00
return GetGenMovesOrder ( pkm . Format , origin ) ;
2017-09-24 17:52:39 +00:00
}
2018-08-03 03:11:42 +00:00
2017-09-24 17:52:39 +00:00
private static int [ ] GetGenMovesOrderVC ( PKM pkm )
{
// VC case: check transfer games in reverse order (8, 7..) then past games.
2018-03-28 02:45:22 +00:00
int [ ] xfer = GetGenMovesOrder ( pkm . Format , 7 ) ;
2020-12-11 04:42:30 +00:00
int [ ] past = GetGenMovesCheckOrderGB ( pkm , pkm . Generation ) ;
2017-09-24 17:52:39 +00:00
int end = xfer . Length ;
Array . Resize ( ref xfer , xfer . Length + past . Length ) ;
past . CopyTo ( xfer , end ) ;
return xfer ;
}
2018-08-03 03:11:42 +00:00
2018-09-02 02:55:08 +00:00
private static readonly int [ ] G2 = { 2 } ;
private static readonly int [ ] G12 = { 1 , 2 } ;
2018-12-28 04:24:24 +00:00
private static readonly int [ ] G21 = { 2 , 1 } ;
2018-09-15 05:37:47 +00:00
2017-09-24 17:52:39 +00:00
private static int [ ] GetGenMovesCheckOrderGB ( PKM pkm , int originalGeneration )
{
if ( originalGeneration = = 2 )
2018-12-28 04:24:24 +00:00
return pkm . Korean ? G2 : G21 ;
return G12 ; // RBY
2017-09-24 17:52:39 +00:00
}
2018-08-03 03:11:42 +00:00
2017-09-24 17:52:39 +00:00
private static int [ ] GetGenMovesOrder ( int start , int end )
{
2018-06-09 03:36:52 +00:00
if ( end < 0 )
2018-08-02 01:30:51 +00:00
return Array . Empty < int > ( ) ;
2017-11-16 02:13:04 +00:00
if ( start < = end )
return new [ ] { start } ;
2017-09-24 17:52:39 +00:00
var order = new int [ start - end + 1 ] ;
2017-05-28 04:17:53 +00:00
for ( int i = 0 ; i < order . Length ; i + + )
2017-09-24 17:52:39 +00:00
order [ i ] = start - i ;
2017-05-28 04:17:53 +00:00
return order ;
}
}
}