2018-05-18 05:43:07 +00:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Globalization ;
using System.Linq ;
using System.Reflection ;
using static PKHeX . Core . MessageStrings ;
namespace PKHeX.Core
{
public static class BatchEditing
{
public static readonly Type [ ] Types =
{
2018-11-14 03:10:43 +00:00
typeof ( PB7 ) ,
2018-05-18 05:43:07 +00:00
typeof ( PK7 ) , typeof ( PK6 ) , typeof ( PK5 ) , typeof ( PK4 ) , typeof ( BK4 ) ,
typeof ( PK3 ) , typeof ( XK3 ) , typeof ( CK3 ) ,
typeof ( PK2 ) , typeof ( PK1 ) ,
} ;
2018-05-19 02:19:15 +00:00
2018-05-18 05:43:07 +00:00
public static readonly string [ ] CustomProperties = { PROP_LEGAL } ;
public static readonly string [ ] [ ] Properties = GetPropArray ( ) ;
2018-06-14 01:52:09 +00:00
private static readonly Dictionary < string , PropertyInfo > [ ] Props = Types . Select ( z = > ReflectUtil . GetAllPropertyInfoPublic ( z )
2018-05-18 05:43:07 +00:00
. GroupBy ( p = > p . Name ) . Select ( g = > g . First ( ) ) . ToDictionary ( p = > p . Name ) )
. ToArray ( ) ;
private const string CONST_RAND = "$rand" ;
private const string CONST_SHINY = "$shiny" ;
private const string CONST_SUGGEST = "$suggest" ;
private const string CONST_BYTES = "$[]" ;
private const string PROP_LEGAL = "Legal" ;
2018-05-19 02:19:15 +00:00
private const string IdentifierContains = nameof ( PKM . Identifier ) + "Contains" ;
2018-05-18 05:43:07 +00:00
private static string [ ] [ ] GetPropArray ( )
{
var p = new string [ Types . Length ] [ ] ;
for ( int i = 0 ; i < p . Length ; i + + )
{
2018-06-14 01:52:09 +00:00
var pz = ReflectUtil . GetPropertiesPublic ( Types [ i ] ) ;
2018-05-18 05:43:07 +00:00
p [ i ] = pz . Concat ( CustomProperties ) . OrderBy ( a = > a ) . ToArray ( ) ;
}
// Properties for any PKM
2018-06-14 01:52:09 +00:00
var any = ReflectUtil . GetPropertiesPublic ( typeof ( PK1 ) ) . Union ( p . SelectMany ( a = > a ) ) . OrderBy ( a = > a ) . ToArray ( ) ;
2018-05-18 05:43:07 +00:00
// Properties shared by all PKM
var all = p . Aggregate ( new HashSet < string > ( p [ 0 ] ) , ( h , e ) = > { h . IntersectWith ( e ) ; return h ; } ) . OrderBy ( a = > a ) . ToArray ( ) ;
var p1 = new string [ Types . Length + 2 ] [ ] ;
Array . Copy ( p , 0 , p1 , 1 , p . Length ) ;
p1 [ 0 ] = any ;
p1 [ p1 . Length - 1 ] = all ;
return p1 ;
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Tries to fetch the <see cref="PKM"/> property from the cache of available properties.
/// </summary>
/// <param name="pkm">Pokémon to check</param>
/// <param name="name">Property Name to check</param>
/// <param name="pi">Property Info retrieved (if any).</param>
/// <returns>True if has property, false if does not.</returns>
public static bool TryGetHasProperty ( PKM pkm , string name , out PropertyInfo pi )
2018-05-18 05:43:07 +00:00
{
2018-05-19 02:19:15 +00:00
var props = Props [ Array . IndexOf ( Types , pkm . GetType ( ) ) ] ;
2018-05-18 05:43:07 +00:00
return props . TryGetValue ( name , out pi ) ;
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Gets the type of the <see cref="PKM"/> property using the saved cache of properties.
/// </summary>
/// <param name="propertyName">Property Name to fetch the type for</param>
/// <param name="typeIndex">Type index (within <see cref="Types"/>. Leave empty (0) for a nonspecific format.</param>
/// <returns>Short name of the property's type.</returns>
2018-05-18 05:43:07 +00:00
public static string GetPropertyType ( string propertyName , int typeIndex = 0 )
{
if ( CustomProperties . Contains ( propertyName ) )
return "Custom" ;
if ( typeIndex = = 0 ) // Any
{
foreach ( var p in Props )
2018-07-29 20:27:48 +00:00
{
2018-05-18 05:43:07 +00:00
if ( p . TryGetValue ( propertyName , out var pi ) )
return pi . PropertyType . Name ;
2018-07-29 20:27:48 +00:00
}
2018-05-18 05:43:07 +00:00
return null ;
}
2018-06-14 01:52:09 +00:00
int index = typeIndex - 1 > = Props . Length ? 0 : typeIndex - 1 ; // All vs Specific
2018-05-19 02:19:15 +00:00
var pr = Props [ index ] ;
2018-05-18 05:43:07 +00:00
if ( ! pr . TryGetValue ( propertyName , out var info ) )
return null ;
return info . PropertyType . Name ;
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Initializes the <see cref="StringInstruction"/> list with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
/// </summary>
/// <param name="il">Instructions to initialize.</param>
2018-05-18 05:43:07 +00:00
public static void ScreenStrings ( IEnumerable < StringInstruction > il )
{
foreach ( var i in il . Where ( i = > ! i . PropertyValue . All ( char . IsDigit ) ) )
{
string pv = i . PropertyValue ;
if ( pv . StartsWith ( "$" ) & & ! pv . StartsWith ( CONST_BYTES ) & & pv . Contains ( ',' ) )
i . SetRandRange ( pv ) ;
SetInstructionScreenedValue ( i ) ;
}
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Initializes the <see cref="StringInstruction"/> with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
/// </summary>
/// <param name="i">Instruction to initialize.</param>
2018-05-18 05:43:07 +00:00
private static void SetInstructionScreenedValue ( StringInstruction i )
{
switch ( i . PropertyName )
{
case nameof ( PKM . Species ) : i . SetScreenedValue ( GameInfo . Strings . specieslist ) ; return ;
case nameof ( PKM . HeldItem ) : i . SetScreenedValue ( GameInfo . Strings . itemlist ) ; return ;
case nameof ( PKM . Ability ) : i . SetScreenedValue ( GameInfo . Strings . abilitylist ) ; return ;
case nameof ( PKM . Nature ) : i . SetScreenedValue ( GameInfo . Strings . natures ) ; return ;
case nameof ( PKM . Ball ) : i . SetScreenedValue ( GameInfo . Strings . balllist ) ; return ;
case nameof ( PKM . Move1 ) :
case nameof ( PKM . Move2 ) :
case nameof ( PKM . Move3 ) :
case nameof ( PKM . Move4 ) :
case nameof ( PKM . RelearnMove1 ) :
case nameof ( PKM . RelearnMove2 ) :
case nameof ( PKM . RelearnMove3 ) :
case nameof ( PKM . RelearnMove4 ) :
i . SetScreenedValue ( GameInfo . Strings . movelist ) ; return ;
}
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Checks if the object is filtered by the provided <see cref="filters"/>.
/// </summary>
/// <param name="filters">Filters which must be satisfied.</param>
/// <param name="pkm">Object to check.</param>
/// <returns>True if <see cref="pkm"/> matches all filters.</returns>
2018-06-01 02:59:05 +00:00
public static bool IsFilterMatch ( IEnumerable < StringInstruction > filters , PKM pkm ) = > filters . All ( z = > IsFilterMatch ( z , pkm , Props [ Array . IndexOf ( Types , pkm . GetType ( ) ) ] ) ) ;
2018-05-19 02:19:15 +00:00
/// <summary>
/// Checks if the object is filtered by the provided <see cref="filters"/>.
/// </summary>
/// <param name="filters">Filters which must be satisfied.</param>
/// <param name="obj">Object to check.</param>
/// <returns>True if <see cref="obj"/> matches all filters.</returns>
2018-06-01 02:59:05 +00:00
public static bool IsFilterMatch ( IEnumerable < StringInstruction > filters , object obj )
2018-05-19 02:19:15 +00:00
{
foreach ( var cmd in filters )
{
if ( ! ReflectUtil . HasProperty ( obj , cmd . PropertyName , out var pi ) )
return false ;
try { if ( pi . IsValueEqual ( obj , cmd . PropertyValue ) = = cmd . Evaluator ) continue ; }
catch { Debug . WriteLine ( $"Unable to compare {cmd.PropertyName} to {cmd.PropertyValue}." ) ; }
return false ;
}
return true ;
}
/// <summary>
/// Tries to modify the <see cref="PKM"/>.
/// </summary>
/// <param name="pkm">Object to modify.</param>
/// <param name="filters">Filters which must be satisfied prior to any modifications being made.</param>
/// <param name="modifications">Modifications to perform on the <see cref="pkm"/>.</param>
/// <returns>Result of the attempted modification.</returns>
public static bool TryModify ( PKM pkm , IEnumerable < StringInstruction > filters , IEnumerable < StringInstruction > modifications )
2018-05-18 05:43:07 +00:00
{
2018-05-19 02:19:15 +00:00
var result = TryModifyPKM ( pkm , filters , modifications ) ;
return result = = ModifyResult . Modified ;
}
/// <summary>
/// Tries to modify the <see cref="PKMInfo"/>.
/// </summary>
/// <param name="pkm">Command Filter</param>
/// <param name="filters">Filters which must be satisfied prior to any modifications being made.</param>
/// <param name="modifications">Modifications to perform on the <see cref="pkm"/>.</param>
/// <returns>Result of the attempted modification.</returns>
internal static ModifyResult TryModifyPKM ( PKM pkm , IEnumerable < StringInstruction > filters , IEnumerable < StringInstruction > modifications )
{
if ( ! pkm . ChecksumValid | | pkm . Species = = 0 )
2018-05-18 05:43:07 +00:00
return ModifyResult . Invalid ;
2018-05-19 02:19:15 +00:00
PKMInfo info = new PKMInfo ( pkm ) ;
var pi = Props [ Array . IndexOf ( Types , pkm . GetType ( ) ) ] ;
foreach ( var cmd in filters )
2018-05-18 05:43:07 +00:00
{
try
{
2018-06-01 02:59:05 +00:00
if ( ! IsFilterMatch ( cmd , info , pi ) )
2018-05-19 02:19:15 +00:00
return ModifyResult . Filtered ;
}
catch ( Exception ex )
{
Debug . WriteLine ( MsgBEModifyFailCompare + " " + ex . Message , cmd . PropertyName , cmd . PropertyValue ) ;
return ModifyResult . Error ;
2018-05-18 05:43:07 +00:00
}
}
ModifyResult result = ModifyResult . Modified ;
2018-05-19 02:19:15 +00:00
foreach ( var cmd in modifications )
2018-05-18 05:43:07 +00:00
{
try
{
2018-05-19 02:19:15 +00:00
var tmp = SetPKMProperty ( cmd , info , pi ) ;
2018-06-14 01:52:09 +00:00
if ( tmp ! = ModifyResult . Modified )
2018-05-18 05:43:07 +00:00
result = tmp ;
}
catch ( Exception ex ) { Debug . WriteLine ( MsgBEModifyFail + " " + ex . Message , cmd . PropertyName , cmd . PropertyValue ) ; }
}
return result ;
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Sets the if the <see cref="PKMInfo"/> should be filtered due to the <see cref="StringInstruction"/> provided.
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="info">Pokémon to check.</param>
/// <param name="props">PropertyInfo cache (optional)</param>
/// <returns>True if filtered, else false.</returns>
private static ModifyResult SetPKMProperty ( StringInstruction cmd , PKMInfo info , IReadOnlyDictionary < string , PropertyInfo > props )
2018-05-18 05:43:07 +00:00
{
2018-05-19 02:19:15 +00:00
var pkm = info . pkm ;
2018-05-18 05:43:07 +00:00
if ( cmd . PropertyValue . StartsWith ( CONST_BYTES ) )
2018-05-19 02:19:15 +00:00
return SetByteArrayProperty ( pkm , cmd ) ;
2018-05-18 05:43:07 +00:00
if ( cmd . PropertyValue = = CONST_SUGGEST )
2018-05-19 02:19:15 +00:00
return SetSuggestedPKMProperty ( cmd . PropertyName , info ) ;
2018-05-20 21:29:13 +00:00
if ( cmd . PropertyValue = = CONST_RAND & & cmd . PropertyName = = nameof ( PKM . Moves ) )
return SetMoves ( pkm , pkm . GetMoveSet ( true , info . Legality ) ) ;
2018-05-18 05:43:07 +00:00
2018-05-19 02:19:15 +00:00
if ( SetComplexProperty ( pkm , cmd ) )
2018-05-18 05:43:07 +00:00
return ModifyResult . Modified ;
2018-05-19 02:19:15 +00:00
if ( ! props . TryGetValue ( cmd . PropertyName , out var pi ) )
return ModifyResult . Error ;
2018-06-14 01:52:09 +00:00
if ( ! pi . CanWrite )
return ModifyResult . Error ;
2018-05-18 05:43:07 +00:00
object val = cmd . Random ? ( object ) cmd . RandomValue : cmd . PropertyValue ;
2018-05-19 02:19:15 +00:00
ReflectUtil . SetValue ( pi , pkm , val ) ;
return ModifyResult . Modified ;
2018-05-18 05:43:07 +00:00
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Checks if the <see cref="PKMInfo"/> should be filtered due to the <see cref="StringInstruction"/> provided.
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="info">Pokémon to check.</param>
/// <param name="props">PropertyInfo cache (optional)</param>
2018-06-01 02:59:05 +00:00
/// <returns>True if filter matches, else false.</returns>
private static bool IsFilterMatch ( StringInstruction cmd , PKMInfo info , IReadOnlyDictionary < string , PropertyInfo > props )
2018-05-18 05:43:07 +00:00
{
2018-05-19 02:19:15 +00:00
if ( IsLegalFiltered ( cmd , ( ) = > info . Legal ) )
return true ;
return IsPropertyFiltered ( cmd , info . pkm , props ) ;
}
2018-05-18 05:43:07 +00:00
2018-05-19 02:19:15 +00:00
/// <summary>
/// Checks if the <see cref="PKM"/> should be filtered due to the <see cref="StringInstruction"/> provided.
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="pkm">Pokémon to check.</param>
/// <param name="props">PropertyInfo cache (optional)</param>
2018-06-01 02:59:05 +00:00
/// <returns>True if filter matches, else false.</returns>
private static bool IsFilterMatch ( StringInstruction cmd , PKM pkm , IReadOnlyDictionary < string , PropertyInfo > props )
2018-05-19 02:19:15 +00:00
{
if ( IsLegalFiltered ( cmd , ( ) = > new LegalityAnalysis ( pkm ) . Valid ) )
return true ;
return IsPropertyFiltered ( cmd , pkm , props ) ;
}
2018-05-21 02:26:36 +00:00
/// <summary>
/// Checks if the <see cref="PKM"/> should be filtered due to the <see cref="StringInstruction"/> provided.
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="pkm">Pokémon to check.</param>
/// <param name="props">PropertyInfo cache</param>
/// <returns>True if filtered, else false.</returns>
private static bool IsPropertyFiltered ( StringInstruction cmd , PKM pkm , IReadOnlyDictionary < string , PropertyInfo > props )
{
if ( IsIdentifierFiltered ( cmd , pkm ) )
return true ;
if ( ! props . TryGetValue ( cmd . PropertyName , out var pi ) )
return false ;
2018-06-14 01:52:09 +00:00
if ( ! pi . CanRead )
return false ;
2018-05-27 19:27:44 +00:00
return pi . IsValueEqual ( pkm , cmd . PropertyValue ) = = cmd . Evaluator ;
2018-05-19 02:19:15 +00:00
}
/// <summary>
/// Checks if the <see cref="PKM"/> should be filtered due to its <see cref="PKM.Identifier"/> containing a value.
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="pkm">Pokémon to check.</param>
/// <returns>True if filtered, else false.</returns>
private static bool IsIdentifierFiltered ( StringInstruction cmd , PKM pkm )
{
if ( cmd . PropertyName ! = IdentifierContains )
return false ;
bool result = pkm . Identifier . Contains ( cmd . PropertyValue ) ;
2018-05-27 19:27:44 +00:00
return result = = cmd . Evaluator ;
2018-05-19 02:19:15 +00:00
}
/// <summary>
/// Checks if the <see cref="PKM"/> should be filtered due to its legality.
/// </summary>
/// <param name="cmd">Command Filter</param>
/// <param name="isLegal">Function to check if the <see cref="PKM"/> is legal.</param>
/// <returns>True if filtered, else false.</returns>
private static bool IsLegalFiltered ( StringInstruction cmd , Func < bool > isLegal )
{
if ( cmd . PropertyName ! = PROP_LEGAL )
return false ;
if ( ! bool . TryParse ( cmd . PropertyValue , out bool legal ) )
return true ;
2018-05-27 19:27:44 +00:00
return legal = = isLegal ( ) = = cmd . Evaluator ;
2018-05-18 05:43:07 +00:00
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Sets the <see cref="PKM"/> data with a suggested value based on its <see cref="LegalityAnalysis"/>.
/// </summary>
/// <param name="name">Property to modify.</param>
/// <param name="info">Cached info storing Legal data.</param>
private static ModifyResult SetSuggestedPKMProperty ( string name , PKMInfo info )
2018-05-18 05:43:07 +00:00
{
var PKM = info . pkm ;
2018-05-19 02:19:15 +00:00
switch ( name )
2018-05-18 05:43:07 +00:00
{
2018-06-06 04:31:42 +00:00
case nameof ( IHyperTrain . HyperTrainFlags ) :
2018-05-19 02:19:15 +00:00
PKM . SetSuggestedHyperTrainingData ( ) ;
return ModifyResult . Modified ;
2018-05-18 05:43:07 +00:00
case nameof ( PKM . RelearnMoves ) :
PKM . RelearnMoves = info . SuggestedRelearn ;
2018-05-19 02:19:15 +00:00
return ModifyResult . Modified ;
2018-05-18 05:43:07 +00:00
case nameof ( PKM . Met_Location ) :
var encounter = info . SuggestedEncounter ;
if ( encounter = = null )
2018-05-19 02:19:15 +00:00
return ModifyResult . Error ;
2018-05-18 05:43:07 +00:00
int level = encounter . Level ;
int location = encounter . Location ;
int minlvl = Legal . GetLowestLevel ( PKM , encounter . LevelMin ) ;
PKM . Met_Level = level ;
PKM . Met_Location = location ;
PKM . CurrentLevel = Math . Max ( minlvl , level ) ;
2018-05-19 02:19:15 +00:00
return ModifyResult . Modified ;
2018-05-18 05:43:07 +00:00
case nameof ( PKM . Moves ) :
2018-05-20 21:29:13 +00:00
return SetMoves ( PKM , PKM . GetMoveSet ( la : info . Legality ) ) ;
2018-05-18 05:43:07 +00:00
default :
2018-05-19 02:19:15 +00:00
return ModifyResult . Error ;
2018-05-18 05:43:07 +00:00
}
}
2018-05-20 21:29:13 +00:00
/// <summary>
/// Sets the provided moves in a random order.
/// </summary>
/// <param name="pkm">Pokémon to modify.</param>
/// <param name="moves">Moves to apply.</param>
private static ModifyResult SetMoves ( PKM pkm , int [ ] moves )
{
pkm . SetMoves ( moves ) ;
return ModifyResult . Modified ;
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Sets the <see cref="PKM"/> byte array propery to a specified value.
/// </summary>
/// <param name="pkm">Pokémon to modify.</param>
/// <param name="cmd">Modification</param>
private static ModifyResult SetByteArrayProperty ( PKM pkm , StringInstruction cmd )
2018-05-18 05:43:07 +00:00
{
switch ( cmd . PropertyName )
{
2018-05-19 02:19:15 +00:00
case nameof ( pkm . Nickname_Trash ) :
pkm . Nickname_Trash = string2arr ( cmd . PropertyValue ) ;
return ModifyResult . Modified ;
case nameof ( pkm . OT_Trash ) :
pkm . OT_Trash = string2arr ( cmd . PropertyValue ) ;
return ModifyResult . Modified ;
2018-05-18 05:43:07 +00:00
default :
2018-05-19 02:19:15 +00:00
return ModifyResult . Error ;
2018-05-18 05:43:07 +00:00
}
byte [ ] string2arr ( string str ) = > str . Substring ( CONST_BYTES . Length ) . Split ( ',' ) . Select ( z = > Convert . ToByte ( z . Trim ( ) , 16 ) ) . ToArray ( ) ;
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Sets the <see cref="PKM"/> property to a non-specific smart value.
/// </summary>
/// <param name="pkm">Pokémon to modify.</param>
/// <param name="cmd">Modification</param>
private static bool SetComplexProperty ( PKM pkm , StringInstruction cmd )
2018-05-18 05:43:07 +00:00
{
2018-05-19 02:19:15 +00:00
if ( cmd . PropertyName = = nameof ( pkm . MetDate ) )
pkm . MetDate = DateTime . ParseExact ( cmd . PropertyValue , "yyyyMMdd" , CultureInfo . InvariantCulture , DateTimeStyles . None ) ;
else if ( cmd . PropertyName = = nameof ( pkm . EggMetDate ) )
pkm . EggMetDate = DateTime . ParseExact ( cmd . PropertyValue , "yyyyMMdd" , CultureInfo . InvariantCulture , DateTimeStyles . None ) ;
else if ( cmd . PropertyName = = nameof ( pkm . EncryptionConstant ) & & cmd . PropertyValue = = CONST_RAND )
pkm . EncryptionConstant = Util . Rand32 ( ) ;
else if ( ( cmd . PropertyName = = nameof ( pkm . Ability ) | | cmd . PropertyName = = nameof ( pkm . AbilityNumber ) ) & & cmd . PropertyValue . StartsWith ( "$" ) )
pkm . RefreshAbility ( Convert . ToInt16 ( cmd . PropertyValue [ 1 ] ) - 0x30 ) ;
else if ( cmd . PropertyName = = nameof ( pkm . PID ) & & cmd . PropertyValue = = CONST_RAND )
pkm . SetPIDGender ( pkm . Gender ) ;
else if ( cmd . PropertyName = = nameof ( pkm . EncryptionConstant ) & & cmd . PropertyValue = = nameof ( pkm . PID ) )
pkm . EncryptionConstant = pkm . PID ;
else if ( cmd . PropertyName = = nameof ( pkm . PID ) & & cmd . PropertyValue = = CONST_SHINY )
2018-10-27 15:53:09 +00:00
pkm . SetShiny ( ) ;
2018-05-19 02:19:15 +00:00
else if ( cmd . PropertyName = = nameof ( pkm . Species ) & & cmd . PropertyValue = = "0" )
pkm . Data = new byte [ pkm . Data . Length ] ;
2018-05-18 05:43:07 +00:00
else if ( cmd . PropertyName . StartsWith ( "IV" ) & & cmd . PropertyValue = = CONST_RAND )
2018-05-19 02:19:15 +00:00
SetRandomIVs ( pkm , cmd ) ;
else if ( cmd . PropertyName = = nameof ( pkm . IsNicknamed ) & & string . Equals ( cmd . PropertyValue , "false" , StringComparison . OrdinalIgnoreCase ) )
2018-07-29 20:27:48 +00:00
pkm . SetDefaultNickname ( ) ;
2018-05-18 05:43:07 +00:00
else
return false ;
return true ;
}
2018-05-19 02:19:15 +00:00
/// <summary>
/// Sets the <see cref="PKM"/> IV(s) to a random value.
/// </summary>
/// <param name="pkm">Pokémon to modify.</param>
/// <param name="cmd">Modification</param>
private static void SetRandomIVs ( PKM pkm , StringInstruction cmd )
2018-05-18 05:43:07 +00:00
{
2018-05-19 02:19:15 +00:00
if ( cmd . PropertyName = = nameof ( pkm . IVs ) )
2018-05-18 05:43:07 +00:00
{
2018-05-19 02:19:15 +00:00
pkm . SetRandomIVs ( ) ;
2018-05-18 05:43:07 +00:00
return ;
}
2018-05-19 02:19:15 +00:00
if ( TryGetHasProperty ( pkm , cmd . PropertyName , out var pi ) )
2018-07-04 18:30:43 +00:00
ReflectUtil . SetValue ( pi , pkm , Util . Rand . Next ( pkm . MaxIV + 1 ) ) ;
2018-05-18 05:43:07 +00:00
}
}
}