2018-03-11 02:03:09 +00:00
using System ;
2019-09-10 07:21:51 +00:00
using System.Collections.Generic ;
2018-03-11 02:03:09 +00:00
using System.Linq ;
namespace PKHeX.Core
{
/// <summary>
/// Contains extension logic for modifying <see cref="PKM"/> data.
/// </summary>
public static class CommonEdits
{
2019-02-24 21:57:10 +00:00
/// <summary>
/// Setting which enables/disables automatic manipulation of <see cref="PKM.Markings"/> when importing from a <see cref="ShowdownSet"/>.
/// </summary>
2018-03-13 00:40:09 +00:00
public static bool ShowdownSetIVMarkings { get ; set ; } = true ;
2019-09-10 07:21:51 +00:00
/// <summary>
/// Default <see cref="MarkingMethod"/> when applying markings.
/// </summary>
2019-10-26 19:33:58 +00:00
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
2019-09-10 07:21:51 +00:00
public static Func < PKM , Func < int , int , int > > MarkingMethod { get ; set ; } = FlagHighLow ;
2018-03-11 02:03:09 +00:00
/// <summary>
/// Sets the <see cref="PKM.Nickname"/> to the provided value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="nick"><see cref="PKM.Nickname"/> to set. If no nickname is provided, the <see cref="PKM.Nickname"/> is set to the default value for its current language and format.</param>
2019-02-12 05:49:05 +00:00
public static void SetNickname ( this PKM pk , string nick )
2018-03-11 02:03:09 +00:00
{
2019-02-12 05:49:05 +00:00
if ( string . IsNullOrWhiteSpace ( nick ) )
2018-03-11 02:03:09 +00:00
{
2019-02-12 05:49:05 +00:00
pk . ClearNickname ( ) ;
return ;
2018-03-11 02:03:09 +00:00
}
2019-02-12 05:49:05 +00:00
pk . IsNicknamed = true ;
pk . Nickname = nick ;
}
2019-02-24 21:57:10 +00:00
/// <summary>
/// Clears the <see cref="PKM.Nickname"/> to the default value.
/// </summary>
/// <param name="pk"></param>
2019-05-30 05:40:07 +00:00
public static string ClearNickname ( this PKM pk )
2019-02-12 05:49:05 +00:00
{
pk . IsNicknamed = false ;
2019-09-19 02:58:23 +00:00
string nick = SpeciesName . GetSpeciesNameGeneration ( pk . Species , pk . Language , pk . Format ) ;
2019-05-30 05:40:07 +00:00
pk . Nickname = nick ;
2019-10-26 19:42:33 +00:00
if ( pk is GBPKM pk12 )
2019-02-12 05:49:05 +00:00
pk12 . SetNotNicknamed ( ) ;
2019-05-30 05:40:07 +00:00
return nick ;
2018-03-11 02:03:09 +00:00
}
/// <summary>
/// Sets the <see cref="PKM.AltForm"/> value, with special consideration for <see cref="PKM.Format"/> values which derive the <see cref="PKM.AltForm"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="form">Desired <see cref="PKM.AltForm"/> value to set.</param>
public static void SetAltForm ( this PKM pk , int form )
{
switch ( pk . Format )
{
case 2 :
while ( pk . AltForm ! = form )
pk . SetRandomIVs ( ) ;
break ;
case 3 :
pk . SetPIDUnown3 ( form ) ;
break ;
default :
pk . AltForm = form ;
break ;
}
}
/// <summary>
/// Sets the <see cref="PKM.Ability"/> value by sanity checking the provided <see cref="PKM.Ability"/> against the possible pool of abilities.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="abil">Desired <see cref="PKM.Ability"/> to set.</param>
public static void SetAbility ( this PKM pk , int abil )
{
2018-04-26 01:45:31 +00:00
if ( abil < 0 )
return ;
2018-03-11 02:03:09 +00:00
var abilities = pk . PersonalInfo . Abilities ;
int abilIndex = Array . IndexOf ( abilities , abil ) ;
abilIndex = Math . Max ( 0 , abilIndex ) ;
2018-09-03 17:30:35 +00:00
pk . SetAbilityIndex ( abilIndex ) ;
}
2018-03-11 02:03:09 +00:00
2018-09-03 17:30:35 +00:00
/// <summary>
/// Sets the <see cref="PKM.Ability"/> value based on the provided ability index (0-2)
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="abilIndex">Desired <see cref="PKM.AbilityNumber"/> (shifted by 1) to set.</param>
public static void SetAbilityIndex ( this PKM pk , int abilIndex )
{
2018-03-11 02:03:09 +00:00
if ( pk is PK5 pk5 & & abilIndex = = 2 )
pk5 . HiddenAbility = true ;
else if ( pk . Format < = 5 )
2018-06-16 17:51:53 +00:00
pk . PID = PKX . GetRandomPID ( pk . Species , pk . Gender , pk . Version , pk . Nature , pk . AltForm , ( uint ) ( abilIndex * 0x10001 ) ) ;
2018-03-11 02:03:09 +00:00
pk . RefreshAbility ( abilIndex ) ;
}
/// <summary>
/// Sets a Random <see cref="PKM.EncryptionConstant"/> value. The <see cref="PKM.EncryptionConstant"/> is not updated if the value should match the <see cref="PKM.PID"/> instead.
/// </summary>
2018-09-03 17:30:35 +00:00
/// <remarks>Accounts for Wurmple evolutions.</remarks>
2018-03-11 02:03:09 +00:00
/// <param name="pk">Pokémon to modify.</param>
public static void SetRandomEC ( this PKM pk )
{
int gen = pk . GenNumber ;
2019-05-11 21:25:58 +00:00
if ( 2 < gen & & gen < 6 )
{
2018-03-11 02:03:09 +00:00
pk . EncryptionConstant = pk . PID ;
2019-05-11 21:25:58 +00:00
return ;
}
int wIndex = WurmpleUtil . GetWurmpleEvoGroup ( pk . Species ) ;
if ( wIndex ! = - 1 )
{
pk . EncryptionConstant = WurmpleUtil . GetWurmpleEC ( wIndex ) ;
return ;
}
pk . EncryptionConstant = Util . Rand32 ( ) ;
2018-03-11 02:03:09 +00:00
}
/// <summary>
/// Sets the <see cref="PKM.IsShiny"/> derived value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="shiny">Desired <see cref="PKM.IsShiny"/> state to set.</param>
/// <returns></returns>
2018-10-27 15:53:09 +00:00
public static bool SetIsShiny ( this PKM pk , bool shiny ) = > shiny ? SetShiny ( pk ) : pk . SetUnshiny ( ) ;
2018-03-11 02:03:09 +00:00
/// <summary>
/// Makes a <see cref="PKM"/> shiny.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
2019-11-21 04:38:05 +00:00
/// <param name="xor0">Square shiny</param>
2018-04-04 16:53:48 +00:00
/// <returns>Returns true if the <see cref="PKM"/> data was modified.</returns>
2019-11-21 04:38:05 +00:00
public static bool SetShiny ( PKM pk , bool xor0 = false )
2018-03-11 02:03:09 +00:00
{
if ( pk . IsShiny )
return false ;
2019-11-21 04:38:05 +00:00
while ( true )
{
pk . SetShiny ( ) ;
if ( pk . Format < = 7 )
return true ;
var xor = pk . ShinyXor ;
if ( xor0 ? xor = = 0 : xor ! = 0 )
return true ;
}
2018-03-11 02:03:09 +00:00
}
/// <summary>
/// Makes a <see cref="PKM"/> not-shiny.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
2018-04-04 16:53:48 +00:00
/// <returns>Returns true if the <see cref="PKM"/> data was modified.</returns>
2018-03-11 02:03:09 +00:00
public static bool SetUnshiny ( this PKM pk )
{
if ( ! pk . IsShiny )
return false ;
pk . SetPIDGender ( pk . Gender ) ;
return true ;
}
/// <summary>
/// Sets the <see cref="PKM.Nature"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Nature"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="nature">Desired <see cref="PKM.Nature"/> value to set.</param>
public static void SetNature ( this PKM pk , int nature )
{
2019-05-30 05:40:07 +00:00
var value = Math . Min ( ( int ) Nature . Quirky , Math . Max ( ( int ) Nature . Hardy , nature ) ) ;
2019-11-16 01:34:18 +00:00
if ( pk . Format > = 8 )
pk . StatNature = value ;
else if ( pk . Format < = 4 )
2019-05-30 05:40:07 +00:00
pk . SetPIDNature ( value ) ;
2018-03-11 02:03:09 +00:00
else
2019-05-30 05:40:07 +00:00
pk . Nature = value ;
2018-03-11 02:03:09 +00:00
}
/// <summary>
/// Sets the <see cref="PKM.Nature"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Nature"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="nature">Desired <see cref="PKM.Nature"/> value to set.</param>
public static void SetNature ( this PKM pk , Nature nature ) = > pk . SetNature ( ( int ) nature ) ;
/// <summary>
/// Sets the individual PP Up count values depending if a Move is present in the moveslot or not.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="Moves"><see cref="PKM.Moves"/> to use (if already known). Will fetch the current <see cref="PKM.Moves"/> if not provided.</param>
2019-09-10 07:21:51 +00:00
public static void SetMaximumPPUps ( this PKM pk , int [ ] Moves )
2018-03-11 02:03:09 +00:00
{
2018-03-19 23:21:29 +00:00
pk . Move1_PPUps = GetPPUpCount ( Moves [ 0 ] ) ;
pk . Move2_PPUps = GetPPUpCount ( Moves [ 1 ] ) ;
pk . Move3_PPUps = GetPPUpCount ( Moves [ 2 ] ) ;
pk . Move4_PPUps = GetPPUpCount ( Moves [ 3 ] ) ;
2018-03-11 02:03:09 +00:00
2018-03-19 23:21:29 +00:00
pk . SetMaximumPPCurrent ( Moves ) ;
2019-10-08 01:40:09 +00:00
static int GetPPUpCount ( int moveID ) = > moveID > 0 ? 3 : 0 ;
2018-03-11 02:03:09 +00:00
}
2019-09-10 07:21:51 +00:00
/// <summary>
/// Sets the individual PP Up count values depending if a Move is present in the moveslot or not.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetMaximumPPUps ( this PKM pk ) = > pk . SetMaximumPPUps ( pk . Moves ) ;
2018-03-20 15:35:41 +00:00
/// <summary>
/// Updates the <see cref="PKM.Moves"/> and updates the current PP counts.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="Moves"><see cref="PKM.Moves"/> to set. Will be resized if 4 entries are not present.</param>
/// <param name="maxPP">Option to maximize PP Ups</param>
public static void SetMoves ( this PKM pk , int [ ] Moves , bool maxPP = false )
{
2018-05-07 01:40:51 +00:00
if ( Moves . Any ( z = > z > pk . MaxMoveID ) )
Moves = Moves . Where ( z = > z < = pk . MaxMoveID ) . ToArray ( ) ;
2018-03-20 15:35:41 +00:00
if ( Moves . Length ! = 4 )
Array . Resize ( ref Moves , 4 ) ;
pk . Moves = Moves ;
if ( maxPP )
pk . SetMaximumPPUps ( Moves ) ;
else
pk . SetMaximumPPCurrent ( Moves ) ;
pk . FixMoves ( ) ;
}
2018-03-19 23:21:29 +00:00
/// <summary>
/// Updates the individual PP count values for each moveslot based on the maximum possible value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="Moves"><see cref="PKM.Moves"/> to use (if already known). Will fetch the current <see cref="PKM.Moves"/> if not provided.</param>
2019-09-10 07:21:51 +00:00
public static void SetMaximumPPCurrent ( this PKM pk , int [ ] Moves )
2018-03-19 23:21:29 +00:00
{
2019-09-10 07:21:51 +00:00
pk . Move1_PP = Moves . Length = = 0 ? 0 : pk . GetMovePP ( Moves [ 0 ] , pk . Move1_PPUps ) ;
2018-03-30 04:00:38 +00:00
pk . Move2_PP = Moves . Length < = 1 ? 0 : pk . GetMovePP ( Moves [ 1 ] , pk . Move2_PPUps ) ;
pk . Move3_PP = Moves . Length < = 2 ? 0 : pk . GetMovePP ( Moves [ 2 ] , pk . Move3_PPUps ) ;
pk . Move4_PP = Moves . Length < = 3 ? 0 : pk . GetMovePP ( Moves [ 3 ] , pk . Move4_PPUps ) ;
2018-03-19 23:21:29 +00:00
}
2019-09-10 07:21:51 +00:00
/// <summary>
/// Updates the individual PP count values for each moveslot based on the maximum possible value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetMaximumPPCurrent ( this PKM pk ) = > pk . SetMaximumPPCurrent ( pk . Moves ) ;
2018-03-11 02:03:09 +00:00
/// <summary>
/// Sets the <see cref="PKM.Gender"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Gender"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="gender">Desired <see cref="PKM.Gender"/> value to set.</param>
public static void SetGender ( this PKM pk , string gender )
{
2019-02-13 23:50:48 +00:00
int g = string . IsNullOrEmpty ( gender )
2018-05-11 03:22:32 +00:00
? pk . GetSaneGender ( )
: PKX . GetGenderFromString ( gender ) ;
pk . SetGender ( g ) ;
2018-03-11 02:03:09 +00:00
}
/// <summary>
/// Sets the <see cref="PKM.Gender"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Gender"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="gender">Desired <see cref="PKM.Gender"/> value to set.</param>
public static void SetGender ( this PKM pk , int gender )
{
gender = Math . Min ( 2 , Math . Max ( 0 , gender ) ) ;
if ( pk . Format < = 2 )
2018-06-16 20:27:50 +00:00
{
2018-03-11 02:03:09 +00:00
pk . SetATKIVGender ( gender ) ;
2018-06-16 20:27:50 +00:00
}
else if ( pk . Format < = 5 )
{
2018-03-11 02:03:09 +00:00
pk . SetPIDGender ( gender ) ;
2018-06-16 20:27:50 +00:00
pk . Gender = gender ;
}
2018-03-11 02:03:09 +00:00
else
2018-06-16 20:27:50 +00:00
{
2018-03-11 02:03:09 +00:00
pk . Gender = gender ;
2018-06-16 20:27:50 +00:00
}
2018-03-11 02:03:09 +00:00
}
/// <summary>
/// Fetches <see cref="PKM.RelearnMoves"/> based on the provided <see cref="LegalityAnalysis"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
2019-10-25 01:11:25 +00:00
/// <returns><see cref="PKM.RelearnMoves"/> best suited for the current <see cref="PKM"/> data.</returns>
public static IReadOnlyList < int > GetSuggestedRelearnMoves ( this PKM pk ) = > new LegalityAnalysis ( pk ) . GetSuggestedRelearnMoves ( ) ;
/// <summary>
/// Fetches <see cref="PKM.RelearnMoves"/> based on the provided <see cref="LegalityAnalysis"/>.
/// </summary>
2018-03-11 02:03:09 +00:00
/// <param name="legal"><see cref="LegalityAnalysis"/> which contains parsed information pertaining to legality.</param>
/// <returns><see cref="PKM.RelearnMoves"/> best suited for the current <see cref="PKM"/> data.</returns>
2019-10-25 01:11:25 +00:00
public static IReadOnlyList < int > GetSuggestedRelearnMoves ( this LegalityAnalysis legal )
2018-03-11 02:03:09 +00:00
{
2019-09-10 07:21:51 +00:00
var m = legal . GetSuggestedRelearn ( ) ;
2019-01-13 07:50:31 +00:00
if ( m . Any ( z = > z ! = 0 ) )
2018-03-11 02:03:09 +00:00
return m ;
2019-01-13 07:50:31 +00:00
var enc = legal . EncounterMatch ;
if ( enc is MysteryGift | | enc is EncounterEgg )
2018-03-11 02:03:09 +00:00
return m ;
var encounter = legal . GetSuggestedMetInfo ( ) ;
2019-11-16 01:34:18 +00:00
if ( encounter is IRelearn r & & r . Relearn . Length > 0 )
m = r . Relearn ;
2018-03-11 02:03:09 +00:00
return m ;
}
/// <summary>
/// Sanity checks the provided <see cref="PKM.Gender"/> value, and returns a sane value.
/// </summary>
/// <param name="pkm"></param>
/// <returns>Most-legal <see cref="PKM.Gender"/> value</returns>
2018-05-10 00:50:56 +00:00
public static int GetSaneGender ( this PKM pkm )
2018-03-11 02:03:09 +00:00
{
int gt = pkm . PersonalInfo . Gender ;
switch ( gt )
{
case 255 : return 2 ; // Genderless
case 254 : return 1 ; // Female-Only
case 0 : return 0 ; // Male-Only
}
2018-05-10 00:50:56 +00:00
if ( ! pkm . IsGenderValid ( ) )
return PKX . GetGenderFromPIDAndRatio ( pkm . PID , gt ) ;
return pkm . Gender ;
2018-03-11 02:03:09 +00:00
}
/// <summary>
/// Copies <see cref="ShowdownSet"/> details to the <see cref="PKM"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="Set"><see cref="ShowdownSet"/> details to copy from.</param>
public static void ApplySetDetails ( this PKM pk , ShowdownSet Set )
{
2018-05-07 01:40:51 +00:00
pk . Species = Math . Min ( pk . MaxSpeciesID , Set . Species ) ;
pk . SetMoves ( Set . Moves , true ) ;
2018-04-26 01:45:31 +00:00
pk . ApplyHeldItem ( Set . HeldItem , Set . Format ) ;
2018-03-11 02:03:09 +00:00
pk . CurrentLevel = Set . Level ;
pk . CurrentFriendship = Set . Friendship ;
pk . IVs = Set . IVs ;
pk . EVs = Set . EVs ;
pk . SetSuggestedHyperTrainingData ( Set . IVs ) ;
2018-03-13 00:40:09 +00:00
if ( ShowdownSetIVMarkings )
pk . SetMarkings ( ) ;
2018-03-11 02:03:09 +00:00
pk . SetNickname ( Set . Nickname ) ;
pk . SetAltForm ( Set . FormIndex ) ;
2018-05-12 04:48:58 +00:00
pk . SetGender ( Set . Gender ) ;
2018-03-20 00:32:45 +00:00
pk . SetMaximumPPUps ( Set . Moves ) ;
2018-03-11 02:03:09 +00:00
pk . SetAbility ( Set . Ability ) ;
pk . SetNature ( Set . Nature ) ;
pk . SetIsShiny ( Set . Shiny ) ;
pk . SetRandomEC ( ) ;
2018-11-11 04:21:36 +00:00
if ( pk is IAwakened a )
2018-11-20 02:26:46 +00:00
{
2018-11-11 04:21:36 +00:00
a . SetSuggestedAwakenedValues ( pk ) ;
2018-11-21 07:57:38 +00:00
if ( pk is PB7 b )
2018-11-20 02:26:46 +00:00
{
for ( int i = 0 ; i < 6 ; i + + )
pk . SetEV ( i , 0 ) ;
2018-11-21 07:57:38 +00:00
b . ResetCalculatedValues ( ) ;
2018-11-20 02:26:46 +00:00
}
}
2019-11-26 01:32:10 +00:00
2019-11-19 23:57:08 +00:00
if ( pk is IGigantamax c )
c . CanGigantamax = Set . CanGigantamax ;
2018-11-11 04:21:36 +00:00
2019-11-24 14:06:08 +00:00
pk . ClearRecordFlags ( ) ;
pk . SetRecordFlags ( Set . Moves ) ;
2018-03-11 02:03:09 +00:00
var legal = new LegalityAnalysis ( pk ) ;
2018-04-21 23:42:45 +00:00
if ( legal . Parsed & & legal . Info . Relearn . Any ( z = > ! z . Valid ) )
2019-10-25 01:11:25 +00:00
pk . SetRelearnMoves ( legal . GetSuggestedRelearnMoves ( ) ) ;
2019-11-26 01:32:10 +00:00
pk . ResetPartyStats ( ) ;
2018-04-22 00:47:32 +00:00
pk . RefreshChecksum ( ) ;
2018-03-11 02:03:09 +00:00
}
2018-04-26 01:45:31 +00:00
/// <summary>
2018-10-31 20:52:09 +00:00
/// Sets the <see cref="PKM.HeldItem"/> value depending on the current format and the provided item index & format.
2018-04-26 01:45:31 +00:00
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="item">Held Item to apply</param>
/// <param name="format">Format required for importing</param>
public static void ApplyHeldItem ( this PKM pk , int item , int format )
{
2019-02-12 05:49:05 +00:00
item = ItemConverter . GetFormatHeldItemID ( item , format , pk . Format ) ;
pk . HeldItem = ( ( uint ) item > pk . MaxItemID ) ? 0 : item ;
2018-04-26 01:45:31 +00:00
}
2018-03-11 02:03:09 +00:00
/// <summary>
/// Sets all Memory related data to the default value (zero).
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void ClearMemories ( this PKM pk )
{
pk . OT_Memory = pk . OT_Affection = pk . OT_Feeling = pk . OT_Intensity = pk . OT_TextVar =
pk . HT_Memory = pk . HT_Affection = pk . HT_Feeling = pk . HT_Intensity = pk . HT_TextVar = 0 ;
}
/// <summary>
/// Sets the <see cref="PKM.Markings"/> to indicate flawless (or near-flawless) <see cref="PKM.IVs"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="IVs"><see cref="PKM.IVs"/> to use (if already known). Will fetch the current <see cref="PKM.IVs"/> if not provided.</param>
2019-09-10 07:21:51 +00:00
public static void SetMarkings ( this PKM pk , int [ ] IVs )
2018-03-11 02:03:09 +00:00
{
if ( pk . Format < = 3 )
return ; // no markings (gen3 only has 4; can't mark stats intelligently
2018-07-02 21:37:21 +00:00
var markings = IVs . Select ( MarkingMethod ( pk ) ) . ToArray ( ) ;
pk . Markings = PKX . ReorderSpeedLast ( markings ) ;
2018-03-13 03:44:16 +00:00
}
2018-03-11 02:03:09 +00:00
2019-02-24 21:57:10 +00:00
/// <summary>
2019-09-10 07:21:51 +00:00
/// Sets the <see cref="PKM.Markings"/> to indicate flawless (or near-flawless) <see cref="PKM.IVs"/>.
2019-02-24 21:57:10 +00:00
/// </summary>
2019-09-10 07:21:51 +00:00
/// <param name="pk">Pokémon to modify.</param>
public static void SetMarkings ( this PKM pk )
{
if ( pk . Format < = 3 )
return ; // no markings (gen3 only has 4; can't mark stats intelligently
pk . SetMarkings ( pk . IVs ) ;
}
2018-07-29 20:27:48 +00:00
2018-03-13 03:44:16 +00:00
private static Func < int , int , int > FlagHighLow ( PKM pk )
{
if ( pk . Format < 7 )
return GetSimpleMarking ;
return GetComplexMarking ;
2018-03-11 02:03:09 +00:00
2019-10-08 01:40:09 +00:00
static int GetSimpleMarking ( int val , int _ ) = > val = = 31 ? 1 : 0 ;
static int GetComplexMarking ( int val , int _ )
2018-03-11 02:03:09 +00:00
{
if ( val = = 31 | | val = = 1 )
return 1 ;
if ( val = = 30 | | val = = 0 )
return 2 ;
return 0 ;
}
}
/// <summary>
/// Sets one of the <see cref="PKM.EVs"/> based on its index within the array.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to set to</param>
/// <param name="value">Value to set</param>
public static void SetEV ( this PKM pk , int index , int value )
{
switch ( index )
{
case 0 : pk . EV_HP = value ; break ;
case 1 : pk . EV_ATK = value ; break ;
case 2 : pk . EV_DEF = value ; break ;
case 3 : pk . EV_SPE = value ; break ;
case 4 : pk . EV_SPA = value ; break ;
case 5 : pk . EV_SPD = value ; break ;
default :
throw new ArgumentOutOfRangeException ( nameof ( index ) ) ;
}
}
2018-07-29 20:27:48 +00:00
2018-03-11 02:03:09 +00:00
/// <summary>
/// Sets one of the <see cref="PKM.IVs"/> based on its index within the array.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to set to</param>
/// <param name="value">Value to set</param>
public static void SetIV ( this PKM pk , int index , int value )
{
switch ( index )
{
case 0 : pk . IV_HP = value ; break ;
case 1 : pk . IV_ATK = value ; break ;
case 2 : pk . IV_DEF = value ; break ;
case 3 : pk . IV_SPE = value ; break ;
case 4 : pk . IV_SPA = value ; break ;
case 5 : pk . IV_SPD = value ; break ;
default :
throw new ArgumentOutOfRangeException ( nameof ( index ) ) ;
}
}
/// <summary>
/// Updates the <see cref="PKM.IV_ATK"/> for a Generation 1/2 format <see cref="PKM"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="gender">Desired <see cref="PKM.Gender"/>.</param>
public static void SetATKIVGender ( this PKM pk , int gender )
{
2018-08-31 01:09:52 +00:00
while ( pk . Gender ! = gender )
pk . IV_ATK = Util . Rand . Next ( pk . MaxIV + 1 ) ;
2018-03-11 02:03:09 +00:00
}
/// <summary>
/// Fetches the highest value the provided <see cref="PKM.EVs"/> index can be while considering others.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to fetch for</param>
/// <returns>Highest value the value can be.</returns>
public static int GetMaximumEV ( this PKM pk , int index )
{
if ( pk . Format < 3 )
return ushort . MaxValue ;
var EVs = pk . EVs ;
EVs [ index ] = 0 ;
var sum = EVs . Sum ( ) ;
int remaining = 510 - sum ;
2018-08-26 23:29:52 +00:00
return Math . Min ( Math . Max ( remaining , 0 ) , 252 ) ;
2018-03-11 02:03:09 +00:00
}
/// <summary>
/// Fetches the highest value the provided <see cref="PKM.IVs"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to fetch for</param>
/// <param name="Allow30">Causes the returned value to be dropped down -1 if the value is already at a maxmimum.</param>
/// <returns>Highest value the value can be.</returns>
public static int GetMaximumIV ( this PKM pk , int index , bool Allow30 = false )
{
2018-12-30 06:19:44 +00:00
if ( pk . GetIV ( index ) = = pk . MaxIV & & Allow30 )
2018-03-11 02:03:09 +00:00
return pk . MaxIV - 1 ;
return pk . MaxIV ;
}
/// <summary>
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hptype"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="hptype">Desired Hidden Power typing.</param>
public static void SetHiddenPower ( this PKM pk , int hptype )
{
var IVs = pk . IVs ;
2018-07-14 03:30:57 +00:00
HiddenPower . SetIVsForType ( hptype , pk . IVs , pk . Format ) ;
2018-03-11 02:03:09 +00:00
pk . IVs = IVs ;
}
/// <summary>
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hptype"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="hptype">Desired Hidden Power typing.</param>
public static void SetHiddenPower ( this PKM pk , MoveType hptype ) = > pk . SetHiddenPower ( ( int ) hptype ) ;
2018-03-26 02:05:49 +00:00
2019-11-16 07:12:28 +00:00
/// <summary>
/// Sets the Technical Record flags for the <see cref="pk"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="value">Value to set for the record.</param>
/// <param name="max">Max record to set.</param>
public static void SetRecordFlags ( this PKM pk , bool value , int max = 100 )
{
if ( ! ( pk is PK8 pk8 ) )
return ;
for ( int i = 0 ; i < max ; i + + )
pk8 . SetMoveRecordFlag ( i , value ) ;
}
/// <summary>
/// Clears the Technical Record flags for the <see cref="pk"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
2019-11-16 16:00:58 +00:00
public static void ClearRecordFlags ( this PKM pk ) = > pk . SetRecordFlags ( false , 112 ) ;
2019-11-16 07:12:28 +00:00
/// <summary>
/// Sets the Technical Record flags for the <see cref="pk"/> based on the current moves.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
2019-11-16 16:18:57 +00:00
/// <param name="moves">Moves to set flags for. If a move is not a Technical Record, it is skipped.</param>
public static void SetRecordFlags ( this PKM pk , IEnumerable < int > moves )
2019-11-16 07:12:28 +00:00
{
if ( ! ( pk is PK8 pk8 ) )
return ;
2019-11-25 20:02:30 +00:00
var permit = pk8 . PersonalInfo . TMHM ;
2019-11-16 07:12:28 +00:00
foreach ( var m in moves )
{
2019-12-04 02:24:28 +00:00
var index = Array . IndexOf ( Legal . TMHM_SWSH , m , 100 ) ;
2019-11-25 20:02:30 +00:00
if ( index < 100 )
continue ;
if ( permit [ index ] )
pk8 . SetMoveRecordFlag ( index - 100 , true ) ;
2019-11-16 07:12:28 +00:00
}
}
/// <summary>
2019-11-16 16:18:57 +00:00
/// Sets all the Technical Record flags for the <see cref="pk"/> if they are permitted to be learned in-game.
2019-11-16 07:12:28 +00:00
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetRecordFlags ( this PKM pk )
{
if ( ! ( pk is PK8 pk8 ) )
return ;
2019-11-16 16:18:57 +00:00
var permit = pk8 . PersonalInfo . TMHM ;
for ( int i = 100 ; i < permit . Length ; i + + )
2019-11-16 07:12:28 +00:00
{
2019-11-16 16:18:57 +00:00
if ( permit [ i ] )
2019-11-16 07:12:28 +00:00
pk8 . SetMoveRecordFlag ( i - 100 , true ) ;
}
}
2018-04-22 19:43:18 +00:00
/// <summary>
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
/// </summary>
/// <param name="pkm">PKM to apply hatch details to</param>
/// <param name="reHatch">Re-hatch already hatched <see cref="PKM"/> inputs</param>
public static void ForceHatchPKM ( this PKM pkm , bool reHatch = false )
{
if ( ! pkm . IsEgg & & ! reHatch )
return ;
pkm . IsEgg = false ;
2019-02-12 05:49:05 +00:00
pkm . ClearNickname ( ) ;
2018-04-22 19:43:18 +00:00
pkm . CurrentFriendship = pkm . PersonalInfo . BaseFriendship ;
2018-10-27 23:06:06 +00:00
if ( pkm . IsTradedEgg )
pkm . Egg_Location = pkm . Met_Location ;
2018-04-22 19:43:18 +00:00
var loc = EncounterSuggestion . GetSuggestedEggMetLocation ( pkm ) ;
if ( loc > = 0 )
pkm . Met_Location = loc ;
pkm . MetDate = DateTime . Today ;
if ( pkm . Gen6 )
pkm . SetHatchMemory6 ( ) ;
}
2018-12-30 06:19:44 +00:00
/// <summary>
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
/// </summary>
/// <param name="pkm">PKM to apply hatch details to</param>
/// <param name="origin">Game the egg originated from</param>
/// <param name="dest">Game the egg is currently present on</param>
public static void SetEggMetData ( this PKM pkm , GameVersion origin , GameVersion dest )
{
bool traded = origin = = dest ;
var today = pkm . MetDate = DateTime . Today ;
pkm . Egg_Location = EncounterSuggestion . GetSuggestedEncounterEggLocationEgg ( pkm , traded ) ;
pkm . EggMetDate = today ;
}
2018-05-02 02:24:47 +00:00
/// <summary>
/// Maximizes the <see cref="PKM.CurrentFriendship"/>. If the <see cref="PKM.IsEgg"/>, the hatch counter is set to 1.
/// </summary>
/// <param name="pkm">PKM to apply hatch details to</param>
public static void MaximizeFriendship ( this PKM pkm )
{
if ( pkm . IsEgg )
pkm . OT_Friendship = 1 ;
else
pkm . CurrentFriendship = byte . MaxValue ;
2018-11-21 20:31:05 +00:00
if ( pkm is PB7 pb )
pb . ResetCP ( ) ;
2018-05-02 02:24:47 +00:00
}
/// <summary>
/// Maximizes the <see cref="PKM.CurrentLevel"/>. If the <see cref="PKM.IsEgg"/>, the <see cref="PKM"/> is ignored.
/// </summary>
/// <param name="pkm">PKM to apply hatch details to</param>
public static void MaximizeLevel ( this PKM pkm )
{
if ( pkm . IsEgg )
return ;
pkm . CurrentLevel = 100 ;
2018-11-21 20:31:05 +00:00
if ( pkm is PB7 pb )
pb . ResetCP ( ) ;
2018-05-02 02:24:47 +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
/// <summary>
/// Gets a moveset for the provided <see cref="PKM"/> data.
/// </summary>
/// <param name="pkm">PKM to generate for</param>
/// <param name="random">Full movepool & shuffling</param>
/// <returns>4 moves</returns>
public static int [ ] GetMoveSet ( this PKM pkm , bool random = false )
{
var la = new LegalityAnalysis ( pkm ) ;
2019-10-25 01:11:25 +00:00
var moves = pkm . GetMoveSet ( la , random ) ;
if ( random )
return moves ;
var clone = pkm . Clone ( ) ;
clone . SetMoves ( moves ) ;
var newLa = new LegalityAnalysis ( pkm ) ;
// ReSharper disable once TailRecursiveCall
return newLa . Valid ? moves : GetMoveSet ( pkm , true ) ;
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
}
2018-05-20 21:29:13 +00:00
/// <summary>
/// Gets a moveset for the provided <see cref="PKM"/> data.
/// </summary>
/// <param name="pkm">PKM to generate for</param>
/// <param name="la">Precomputed optional</param>
2019-10-26 19:33:58 +00:00
/// <param name="random">Full movepool & shuffling</param>
2018-05-20 21:29:13 +00:00
/// <returns>4 moves</returns>
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
public static int [ ] GetMoveSet ( this PKM pkm , LegalityAnalysis la , bool random = false )
2018-05-20 21:29:13 +00:00
{
int [ ] m = la . GetSuggestedMoves ( tm : random , tutor : random , reminder : random ) ;
if ( m = = null )
2018-05-22 00:02:48 +00:00
return pkm . Moves ;
2018-06-23 06:33:42 +00:00
2019-10-26 19:33:58 +00:00
if ( ! m . All ( z = > la . AllSuggestedMovesAndRelearn ( ) . Contains ( z ) ) )
m = m . Intersect ( la . AllSuggestedMovesAndRelearn ( ) ) . ToArray ( ) ;
2018-06-23 06:33:42 +00:00
2018-05-20 21:29:13 +00:00
if ( random )
Util . Shuffle ( m ) ;
const int count = 4 ;
if ( m . Length > count )
2019-10-25 01:11:25 +00:00
return m . SliceEnd ( m . Length - count ) ;
2018-05-20 21:29:13 +00:00
Array . Resize ( ref m , count ) ;
return m ;
}
2018-05-27 17:24:28 +00:00
/// <summary>
/// Toggles the marking at a given index.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Marking index to toggle</param>
/// <param name="markings">Current marking values (optional)</param>
/// <returns>Current marking values</returns>
2019-09-10 07:21:51 +00:00
public static int [ ] ToggleMarking ( this PKM pk , int index , int [ ] markings )
2018-05-27 17:24:28 +00:00
{
switch ( pk . Format )
{
case 3 :
case 4 :
case 5 :
case 6 : // on/off
markings [ index ] ^ = 1 ; // toggle
pk . Markings = markings ;
break ;
case 7 : // 0 (none) | 1 (blue) | 2 (pink)
2019-11-16 01:34:18 +00:00
case 8 :
2019-09-10 07:21:51 +00:00
markings [ index ] = ( markings [ index ] + 1 ) % 3 ; // cycle 0->1->2->0...
2018-05-27 17:24:28 +00:00
pk . Markings = markings ;
break ;
}
return markings ;
}
2019-09-10 07:21:51 +00:00
/// <summary>
/// Toggles the marking at a given index.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Marking index to toggle</param>
/// <returns>Current marking values</returns>
public static int [ ] ToggleMarking ( this PKM pk , int index ) = > pk . ToggleMarking ( index , pk . Markings ) ;
2018-03-26 02:05:49 +00:00
/// <summary>
/// Sets the Memory details to a Hatched Egg's memories.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetHatchMemory6 ( this PKM pk )
{
pk . OT_Memory = 2 ;
pk . OT_Affection = 0 ;
2018-05-19 17:04:07 +00:00
pk . OT_Feeling = Memories . GetRandomFeeling ( pk . OT_Memory ) ;
2018-03-26 02:05:49 +00:00
pk . OT_Intensity = 1 ;
pk . OT_TextVar = pk . XY ? 43 : 27 ; // riverside road : battling spot
}
2018-03-31 07:43:41 +00:00
2018-05-20 21:29:13 +00:00
/// <summary>
/// Sets a random memory specific to <see cref="GameVersion.Gen6"/> locality.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
2018-03-31 07:43:41 +00:00
public static void SetRandomMemory6 ( this PKM pk )
{
// for lack of better randomization :)
pk . OT_Memory = 63 ;
pk . OT_Intensity = 6 ;
2018-05-19 17:04:07 +00:00
pk . OT_Feeling = Memories . GetRandomFeeling ( pk . OT_Memory ) ;
2018-03-31 07:43:41 +00:00
}
2018-06-01 02:49:47 +00:00
/// <summary>
/// Sets the <see cref="PKM.Nickname"/> to its default value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="la">Precomputed optional</param>
2019-09-10 07:21:51 +00:00
public static void SetDefaultNickname ( this PKM pk , LegalityAnalysis la )
2018-06-01 02:49:47 +00:00
{
if ( la . Parsed & & la . EncounterOriginal is EncounterTrade t & & t . HasNickname )
pk . SetNickname ( t . GetNickname ( pk . Language ) ) ;
else
2019-02-12 05:49:05 +00:00
pk . ClearNickname ( ) ;
2018-06-01 02:49:47 +00:00
}
2018-07-14 22:08:14 +00:00
2019-09-10 07:21:51 +00:00
/// <summary>
/// Sets the <see cref="PKM.Nickname"/> to its default value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetDefaultNickname ( this PKM pk ) = > pk . SetDefaultNickname ( new LegalityAnalysis ( pk ) ) ;
2018-07-14 22:08:14 +00:00
private static readonly string [ ] PotentialUnicode = { "★☆☆☆" , "★★☆☆" , "★★★☆" , "★★★★" } ;
private static readonly string [ ] PotentialNoUnicode = { "+" , "++" , "+++" , "++++" } ;
/// <summary>
/// Gets the Potential evaluation of the input <see cref="pk"/>.
/// </summary>
/// <param name="pk">Pokémon to analyze.</param>
/// <param name="unicode">Returned value is unicode or not</param>
/// <returns>Potential string</returns>
public static string GetPotentialString ( this PKM pk , bool unicode = true )
{
var arr = unicode ? PotentialUnicode : PotentialNoUnicode ;
return arr [ pk . PotentialRating ] ;
}
2019-04-30 00:36:29 +00:00
// Extensions
/// <summary>
/// Gets the Location Name for the <see cref="PKM"/>
/// </summary>
/// <param name="pk">PKM to fetch data for</param>
/// <param name="eggmet">Location requested is the egg obtained location, not met location.</param>
/// <returns>Location string</returns>
public static string GetLocationString ( this PKM pk , bool eggmet )
{
if ( pk . Format < 2 )
return string . Empty ;
int locval = eggmet ? pk . Egg_Location : pk . Met_Location ;
return GameInfo . GetLocationName ( eggmet , locval , pk . Format , pk . GenNumber , ( GameVersion ) pk . Version ) ;
}
2018-03-11 02:03:09 +00:00
}
}