mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
File scoped namespaces (#3529)
[Language Reference](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/file-scoped-namespaces) Updates all the files, one less level of indentation. Some small changes were made to API surfaces, renaming `PKM pkm` -> `PKM pk`, and `LegalityAnalysis.pkm` -> `LegalityAnalysis.Entity`
This commit is contained in:
parent
78092e070d
commit
fc754b346b
1124 changed files with 138713 additions and 139627 deletions
45
.editorconfig
Normal file
45
.editorconfig
Normal file
|
@ -0,0 +1,45 @@
|
|||
root = true
|
||||
|
||||
# All Files
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Solution Files
|
||||
[*.sln]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# XML Project Files
|
||||
[*.csproj]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Code Files
|
||||
[*.cs]
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
end_of_line = crlf
|
||||
csharp_prefer_braces = when_multiline:warning
|
||||
dotnet_diagnostic.IDE0047.severity = none
|
||||
dotnet_diagnostic.IDE0048.severity = none
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggest
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggest
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggest
|
||||
dotnet_style_parentheses_in_other_operators = always_for_clarity:suggest
|
||||
|
||||
[*.{cs,vb}]
|
||||
#### Naming styles ####
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
|
@ -3,160 +3,159 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using static PKHeX.Core.Ball;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains logic to apply a new <see cref="Ball"/> value to a <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
public static class BallApplicator
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains logic to apply a new <see cref="Ball"/> value to a <see cref="PKM"/>.
|
||||
/// Gets all balls that are legal for the input <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
public static class BallApplicator
|
||||
/// <remarks>
|
||||
/// Requires checking the <see cref="LegalityAnalysis"/> for every <see cref="Ball"/> that is tried.
|
||||
/// </remarks>
|
||||
/// <param name="pk">Pokémon to retrieve a list of valid balls for.</param>
|
||||
/// <returns>Enumerable list of <see cref="Ball"/> values that the <see cref="PKM"/> is legal with.</returns>
|
||||
public static IEnumerable<Ball> GetLegalBalls(PKM pk)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all balls that are legal for the input <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Requires checking the <see cref="LegalityAnalysis"/> for every <see cref="Ball"/> that is tried.
|
||||
/// </remarks>
|
||||
/// <param name="pkm">Pokémon to retrieve a list of valid balls for.</param>
|
||||
/// <returns>Enumerable list of <see cref="Ball"/> values that the <see cref="PKM"/> is legal with.</returns>
|
||||
public static IEnumerable<Ball> GetLegalBalls(PKM pkm)
|
||||
var clone = pk.Clone();
|
||||
foreach (var b in BallList)
|
||||
{
|
||||
var clone = pkm.Clone();
|
||||
foreach (var b in BallList)
|
||||
{
|
||||
clone.Ball = (int)b;
|
||||
if (new LegalityAnalysis(clone).Valid)
|
||||
yield return b;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a random legal ball value if any exist.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Requires checking the <see cref="LegalityAnalysis"/> for every <see cref="Ball"/> that is tried.
|
||||
/// </remarks>
|
||||
/// <param name="pkm">Pokémon to modify.</param>
|
||||
public static int ApplyBallLegalRandom(PKM pkm)
|
||||
{
|
||||
var balls = GetBallListFromColor(pkm).ToArray();
|
||||
Util.Shuffle(balls.AsSpan());
|
||||
return ApplyFirstLegalBall(pkm, balls);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a legal ball value if any exist, ordered by color.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Requires checking the <see cref="LegalityAnalysis"/> for every <see cref="Ball"/> that is tried.
|
||||
/// </remarks>
|
||||
/// <param name="pkm">Pokémon to modify.</param>
|
||||
public static int ApplyBallLegalByColor(PKM pkm)
|
||||
{
|
||||
var balls = GetBallListFromColor(pkm);
|
||||
return ApplyFirstLegalBall(pkm, balls);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a random ball value in a cyclical manner.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Pokémon to modify.</param>
|
||||
public static int ApplyBallNext(PKM pkm)
|
||||
{
|
||||
var balls = GetBallList(pkm.Ball);
|
||||
var next = balls.First();
|
||||
return pkm.Ball = (int)next;
|
||||
}
|
||||
|
||||
private static int ApplyFirstLegalBall(PKM pkm, IEnumerable<Ball> balls)
|
||||
{
|
||||
foreach (var b in balls)
|
||||
{
|
||||
pkm.Ball = (int)b;
|
||||
if (new LegalityAnalysis(pkm).Valid)
|
||||
break;
|
||||
}
|
||||
return pkm.Ball;
|
||||
}
|
||||
|
||||
private static IEnumerable<Ball> GetBallList(int ball)
|
||||
{
|
||||
var balls = BallList;
|
||||
var currentBall = (Ball)ball;
|
||||
return GetCircularOnce(balls, currentBall);
|
||||
}
|
||||
|
||||
private static IEnumerable<Ball> GetBallListFromColor(PKM pkm)
|
||||
{
|
||||
// Gen1/2 don't store color in personal info
|
||||
var pi = pkm.Format >= 3 ? pkm.PersonalInfo : PersonalTable.USUM.GetFormEntry(pkm.Species, 0);
|
||||
var color = (PersonalColor)pi.Color;
|
||||
var balls = BallColors[color];
|
||||
var currentBall = (Ball)pkm.Ball;
|
||||
return GetCircularOnce(balls, currentBall);
|
||||
}
|
||||
|
||||
private static IEnumerable<T> GetCircularOnce<T>(T[] items, T current)
|
||||
{
|
||||
var currentIndex = Array.IndexOf(items, current);
|
||||
if (currentIndex < 0)
|
||||
currentIndex = items.Length - 2;
|
||||
for (int i = currentIndex + 1; i < items.Length; i++)
|
||||
yield return items[i];
|
||||
for (int i = 0; i <= currentIndex; i++)
|
||||
yield return items[i];
|
||||
}
|
||||
|
||||
private static readonly Ball[] BallList = (Ball[]) Enum.GetValues(typeof(Ball));
|
||||
|
||||
static BallApplicator()
|
||||
{
|
||||
var exclude = new[] {None, Poke};
|
||||
var end = new[] {Poke};
|
||||
var allBalls = BallList.Except(exclude).ToArray();
|
||||
var colors = (PersonalColor[])Enum.GetValues(typeof(PersonalColor));
|
||||
foreach (var c in colors)
|
||||
{
|
||||
var matchingColors = BallColors[c];
|
||||
var extra = allBalls.Except(matchingColors).ToArray();
|
||||
Util.Shuffle(extra.AsSpan());
|
||||
BallColors[c] = ArrayUtil.ConcatAll(matchingColors, extra, end);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority Match ball IDs that match the color ID in descending order
|
||||
/// </summary>
|
||||
private static readonly Dictionary<PersonalColor, Ball[]> BallColors = new()
|
||||
{
|
||||
[PersonalColor.Red] = new[] { Cherish, Repeat, Fast, Heal, Great, Dream, Lure },
|
||||
[PersonalColor.Blue] = new[] { Dive, Net, Great, Beast, Lure },
|
||||
[PersonalColor.Yellow] = new[] { Level, Ultra, Repeat, Quick, Moon },
|
||||
[PersonalColor.Green] = new[] { Safari, Friend, Nest, Dusk },
|
||||
[PersonalColor.Black] = new[] { Luxury, Heavy, Ultra, Moon, Net, Beast },
|
||||
|
||||
[PersonalColor.Brown] = new[] { Level, Heavy },
|
||||
[PersonalColor.Purple] = new[] { Master, Love, Dream, Heal },
|
||||
[PersonalColor.Gray] = new[] { Heavy, Premier, Luxury },
|
||||
[PersonalColor.White] = new[] { Premier, Timer, Luxury, Ultra },
|
||||
[PersonalColor.Pink] = new[] { Love, Dream, Heal },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Personal Data color IDs
|
||||
/// </summary>
|
||||
private enum PersonalColor : byte
|
||||
{
|
||||
Red,
|
||||
Blue,
|
||||
Yellow,
|
||||
Green,
|
||||
Black,
|
||||
|
||||
Brown,
|
||||
Purple,
|
||||
Gray,
|
||||
White,
|
||||
Pink,
|
||||
clone.Ball = (int)b;
|
||||
if (new LegalityAnalysis(clone).Valid)
|
||||
yield return b;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a random legal ball value if any exist.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Requires checking the <see cref="LegalityAnalysis"/> for every <see cref="Ball"/> that is tried.
|
||||
/// </remarks>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static int ApplyBallLegalRandom(PKM pk)
|
||||
{
|
||||
var balls = GetBallListFromColor(pk).ToArray();
|
||||
Util.Shuffle(balls.AsSpan());
|
||||
return ApplyFirstLegalBall(pk, balls);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a legal ball value if any exist, ordered by color.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Requires checking the <see cref="LegalityAnalysis"/> for every <see cref="Ball"/> that is tried.
|
||||
/// </remarks>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static int ApplyBallLegalByColor(PKM pk)
|
||||
{
|
||||
var balls = GetBallListFromColor(pk);
|
||||
return ApplyFirstLegalBall(pk, balls);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a random ball value in a cyclical manner.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static int ApplyBallNext(PKM pk)
|
||||
{
|
||||
var balls = GetBallList(pk.Ball);
|
||||
var next = balls.First();
|
||||
return pk.Ball = (int)next;
|
||||
}
|
||||
|
||||
private static int ApplyFirstLegalBall(PKM pk, IEnumerable<Ball> balls)
|
||||
{
|
||||
foreach (var b in balls)
|
||||
{
|
||||
pk.Ball = (int)b;
|
||||
if (new LegalityAnalysis(pk).Valid)
|
||||
break;
|
||||
}
|
||||
return pk.Ball;
|
||||
}
|
||||
|
||||
private static IEnumerable<Ball> GetBallList(int ball)
|
||||
{
|
||||
var balls = BallList;
|
||||
var currentBall = (Ball)ball;
|
||||
return GetCircularOnce(balls, currentBall);
|
||||
}
|
||||
|
||||
private static IEnumerable<Ball> GetBallListFromColor(PKM pk)
|
||||
{
|
||||
// Gen1/2 don't store color in personal info
|
||||
var pi = pk.Format >= 3 ? pk.PersonalInfo : PersonalTable.USUM.GetFormEntry(pk.Species, 0);
|
||||
var color = (PersonalColor)pi.Color;
|
||||
var balls = BallColors[color];
|
||||
var currentBall = (Ball)pk.Ball;
|
||||
return GetCircularOnce(balls, currentBall);
|
||||
}
|
||||
|
||||
private static IEnumerable<T> GetCircularOnce<T>(T[] items, T current)
|
||||
{
|
||||
var currentIndex = Array.IndexOf(items, current);
|
||||
if (currentIndex < 0)
|
||||
currentIndex = items.Length - 2;
|
||||
for (int i = currentIndex + 1; i < items.Length; i++)
|
||||
yield return items[i];
|
||||
for (int i = 0; i <= currentIndex; i++)
|
||||
yield return items[i];
|
||||
}
|
||||
|
||||
private static readonly Ball[] BallList = (Ball[]) Enum.GetValues(typeof(Ball));
|
||||
|
||||
static BallApplicator()
|
||||
{
|
||||
var exclude = new[] {None, Poke};
|
||||
var end = new[] {Poke};
|
||||
var allBalls = BallList.Except(exclude).ToArray();
|
||||
var colors = (PersonalColor[])Enum.GetValues(typeof(PersonalColor));
|
||||
foreach (var c in colors)
|
||||
{
|
||||
var matchingColors = BallColors[c];
|
||||
var extra = allBalls.Except(matchingColors).ToArray();
|
||||
Util.Shuffle(extra.AsSpan());
|
||||
BallColors[c] = ArrayUtil.ConcatAll(matchingColors, extra, end);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority Match ball IDs that match the color ID in descending order
|
||||
/// </summary>
|
||||
private static readonly Dictionary<PersonalColor, Ball[]> BallColors = new()
|
||||
{
|
||||
[PersonalColor.Red] = new[] { Cherish, Repeat, Fast, Heal, Great, Dream, Lure },
|
||||
[PersonalColor.Blue] = new[] { Dive, Net, Great, Beast, Lure },
|
||||
[PersonalColor.Yellow] = new[] { Level, Ultra, Repeat, Quick, Moon },
|
||||
[PersonalColor.Green] = new[] { Safari, Friend, Nest, Dusk },
|
||||
[PersonalColor.Black] = new[] { Luxury, Heavy, Ultra, Moon, Net, Beast },
|
||||
|
||||
[PersonalColor.Brown] = new[] { Level, Heavy },
|
||||
[PersonalColor.Purple] = new[] { Master, Love, Dream, Heal },
|
||||
[PersonalColor.Gray] = new[] { Heavy, Premier, Luxury },
|
||||
[PersonalColor.White] = new[] { Premier, Timer, Luxury, Ultra },
|
||||
[PersonalColor.Pink] = new[] { Love, Dream, Heal },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Personal Data color IDs
|
||||
/// </summary>
|
||||
private enum PersonalColor : byte
|
||||
{
|
||||
Red,
|
||||
Blue,
|
||||
Yellow,
|
||||
Green,
|
||||
Black,
|
||||
|
||||
Brown,
|
||||
Purple,
|
||||
Gray,
|
||||
White,
|
||||
Pink,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +1,38 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for applying a <see cref="PK1.Catch_Rate"/> value.
|
||||
/// </summary>
|
||||
public static class CatchRateApplicator
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for applying a <see cref="PK1.Catch_Rate"/> value.
|
||||
/// </summary>
|
||||
public static class CatchRateApplicator
|
||||
public static int GetSuggestedCatchRate(PK1 pk1, SaveFile sav)
|
||||
{
|
||||
public static int GetSuggestedCatchRate(PK1 pk1, SaveFile sav)
|
||||
var la = new LegalityAnalysis(pk1);
|
||||
return GetSuggestedCatchRate(pk1, sav, la);
|
||||
}
|
||||
|
||||
public static int GetSuggestedCatchRate(PK1 pk1, SaveFile sav, LegalityAnalysis la)
|
||||
{
|
||||
if (la.Valid)
|
||||
return pk1.Catch_Rate;
|
||||
|
||||
if (la.Info.Generation == 2)
|
||||
return 0;
|
||||
|
||||
var v = la.EncounterMatch;
|
||||
switch (v)
|
||||
{
|
||||
var la = new LegalityAnalysis(pk1);
|
||||
return GetSuggestedCatchRate(pk1, sav, la);
|
||||
}
|
||||
|
||||
public static int GetSuggestedCatchRate(PK1 pk1, SaveFile sav, LegalityAnalysis la)
|
||||
{
|
||||
if (la.Valid)
|
||||
return pk1.Catch_Rate;
|
||||
|
||||
if (la.Info.Generation == 2)
|
||||
return 0;
|
||||
|
||||
var v = la.EncounterMatch;
|
||||
switch (v)
|
||||
case EncounterTrade1 c:
|
||||
return c.GetInitialCatchRate();
|
||||
case EncounterStatic1E { Version: GameVersion.Stadium, Species: (int)Species.Psyduck}:
|
||||
return pk1.Japanese ? 167 : 168; // Amnesia Psyduck has different catch rates depending on language
|
||||
default:
|
||||
{
|
||||
case EncounterTrade1 c:
|
||||
return c.GetInitialCatchRate();
|
||||
case EncounterStatic1E { Version: GameVersion.Stadium, Species: (int)Species.Psyduck}:
|
||||
return pk1.Japanese ? 167 : 168; // Amnesia Psyduck has different catch rates depending on language
|
||||
default:
|
||||
{
|
||||
if (sav.Version.Contains(v.Version) || v.Version.Contains(sav.Version))
|
||||
return sav.Personal[v.Species].CatchRate;
|
||||
if (!GameVersion.RB.Contains(v.Version))
|
||||
return PersonalTable.Y[v.Species].CatchRate;
|
||||
return PersonalTable.RB[v.Species].CatchRate;
|
||||
}
|
||||
if (sav.Version.Contains(v.Version) || v.Version.Contains(sav.Version))
|
||||
return sav.Personal[v.Species].CatchRate;
|
||||
if (!GameVersion.RB.Contains(v.Version))
|
||||
return PersonalTable.Y[v.Species].CatchRate;
|
||||
return PersonalTable.RB[v.Species].CatchRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,76 +1,75 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public static class GenderApplicator
|
||||
{
|
||||
public static class GenderApplicator
|
||||
/// <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>
|
||||
/// <remarks>Has special logic for an unspecified gender.</remarks>
|
||||
public static void SetSaneGender(this PKM pk, int gender)
|
||||
{
|
||||
/// <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>
|
||||
/// <remarks>Has special logic for an unspecified gender.</remarks>
|
||||
public static void SetSaneGender(this PKM pk, int gender)
|
||||
int g = gender == -1 ? pk.GetSaneGender() : gender;
|
||||
pk.SetGender(g);
|
||||
}
|
||||
|
||||
/// <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.Gender == gender)
|
||||
return;
|
||||
|
||||
if (pk.Format <= 2)
|
||||
{
|
||||
int g = gender == -1 ? pk.GetSaneGender() : gender;
|
||||
pk.SetGender(g);
|
||||
pk.SetAttackIVFromGender(gender);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
else if (pk.Format <= 5)
|
||||
{
|
||||
gender = Math.Min(2, Math.Max(0, gender));
|
||||
if (pk.Gender == gender)
|
||||
return;
|
||||
|
||||
if (pk.Format <= 2)
|
||||
{
|
||||
pk.SetAttackIVFromGender(gender);
|
||||
}
|
||||
else if (pk.Format <= 5)
|
||||
{
|
||||
pk.SetPIDGender(gender);
|
||||
pk.Gender = gender;
|
||||
}
|
||||
else
|
||||
{
|
||||
pk.Gender = gender;
|
||||
}
|
||||
pk.SetPIDGender(gender);
|
||||
pk.Gender = gender;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sanity checks the provided <see cref="PKM.Gender"/> value, and returns a sane value.
|
||||
/// </summary>
|
||||
/// <param name="pk"></param>
|
||||
/// <returns>Most-legal <see cref="PKM.Gender"/> value</returns>
|
||||
public static int GetSaneGender(this PKM pk)
|
||||
else
|
||||
{
|
||||
int gt = pk.PersonalInfo.Gender;
|
||||
switch (gt)
|
||||
{
|
||||
case PersonalInfo.RatioMagicGenderless: return 2;
|
||||
case PersonalInfo.RatioMagicFemale: return 1;
|
||||
case PersonalInfo.RatioMagicMale: return 0;
|
||||
}
|
||||
if (!pk.IsGenderValid())
|
||||
return EntityGender.GetFromPIDAndRatio(pk.PID, gt);
|
||||
return pk.Gender;
|
||||
}
|
||||
|
||||
/// <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 SetAttackIVFromGender(this PKM pk, int gender)
|
||||
{
|
||||
var rnd = Util.Rand;
|
||||
while (pk.Gender != gender)
|
||||
pk.IV_ATK = rnd.Next(16);
|
||||
pk.Gender = gender;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sanity checks the provided <see cref="PKM.Gender"/> value, and returns a sane value.
|
||||
/// </summary>
|
||||
/// <param name="pk"></param>
|
||||
/// <returns>Most-legal <see cref="PKM.Gender"/> value</returns>
|
||||
public static int GetSaneGender(this PKM pk)
|
||||
{
|
||||
int gt = pk.PersonalInfo.Gender;
|
||||
switch (gt)
|
||||
{
|
||||
case PersonalInfo.RatioMagicGenderless: return 2;
|
||||
case PersonalInfo.RatioMagicFemale: return 1;
|
||||
case PersonalInfo.RatioMagicMale: return 0;
|
||||
}
|
||||
if (!pk.IsGenderValid())
|
||||
return EntityGender.GetFromPIDAndRatio(pk.PID, gt);
|
||||
return pk.Gender;
|
||||
}
|
||||
|
||||
/// <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 SetAttackIVFromGender(this PKM pk, int gender)
|
||||
{
|
||||
var rnd = Util.Rand;
|
||||
while (pk.Gender != gender)
|
||||
pk.IV_ATK = rnd.Next(16);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,26 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public static class HiddenPowerApplicator
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hiddenPowerType"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="hiddenPowerType">Desired Hidden Power typing.</param>
|
||||
public static void SetHiddenPower(this PKM pk, int hiddenPowerType)
|
||||
{
|
||||
Span<int> IVs = stackalloc int[6];
|
||||
pk.GetIVs(IVs);
|
||||
HiddenPower.SetIVsForType(hiddenPowerType, IVs, pk.Format);
|
||||
pk.SetIVs(IVs);
|
||||
}
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hiddenPowerType"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="hiddenPowerType">Desired Hidden Power typing.</param>
|
||||
public static void SetHiddenPower(this PKM pk, MoveType hiddenPowerType) => pk.SetHiddenPower((int)hiddenPowerType);
|
||||
public static class HiddenPowerApplicator
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hiddenPowerType"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="hiddenPowerType">Desired Hidden Power typing.</param>
|
||||
public static void SetHiddenPower(this PKM pk, int hiddenPowerType)
|
||||
{
|
||||
Span<int> IVs = stackalloc int[6];
|
||||
pk.GetIVs(IVs);
|
||||
HiddenPower.SetIVsForType(hiddenPowerType, IVs, pk.Format);
|
||||
pk.SetIVs(IVs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hiddenPowerType"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="hiddenPowerType">Desired Hidden Power typing.</param>
|
||||
public static void SetHiddenPower(this PKM pk, MoveType hiddenPowerType) => pk.SetHiddenPower((int)hiddenPowerType);
|
||||
}
|
||||
|
|
|
@ -1,69 +1,68 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for modifying the <see cref="PKM.MarkValue"/>.
|
||||
/// </summary>
|
||||
public static class MarkingApplicator
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for modifying the <see cref="PKM.MarkValue"/>.
|
||||
/// Default <see cref="MarkingMethod"/> when applying markings.
|
||||
/// </summary>
|
||||
public static class MarkingApplicator
|
||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
||||
public static Func<PKM, Func<int, int, int>> MarkingMethod { get; set; } = FlagHighLow;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM.MarkValue"/> to indicate flawless (or near-flawless) <see cref="PKM.IVs"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void SetMarkings(this PKM pk)
|
||||
{
|
||||
/// <summary>
|
||||
/// Default <see cref="MarkingMethod"/> when applying markings.
|
||||
/// </summary>
|
||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
||||
public static Func<PKM, Func<int, int, int>> MarkingMethod { get; set; } = FlagHighLow;
|
||||
if (pk.MarkingCount < 6)
|
||||
return; // insufficient marking indexes
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM.MarkValue"/> to indicate flawless (or near-flawless) <see cref="PKM.IVs"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void SetMarkings(this PKM pk)
|
||||
var method = MarkingMethod(pk);
|
||||
pk.SetMarking(0, method(pk.IV_HP , 0));
|
||||
pk.SetMarking(1, method(pk.IV_ATK, 1));
|
||||
pk.SetMarking(2, method(pk.IV_DEF, 2));
|
||||
pk.SetMarking(3, method(pk.IV_SPA, 3));
|
||||
pk.SetMarking(4, method(pk.IV_SPD, 4));
|
||||
pk.SetMarking(5, method(pk.IV_SPE, 5));
|
||||
}
|
||||
|
||||
/// <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 value</returns>
|
||||
public static int ToggleMarking(this PKM pk, int index)
|
||||
{
|
||||
var marking = pk.GetMarking(index);
|
||||
var revised = NextMarking(pk.Format, marking);
|
||||
pk.SetMarking(index, revised);
|
||||
return revised;
|
||||
}
|
||||
|
||||
private static int NextMarking(int format, int marking) => format switch
|
||||
{
|
||||
<= 6 => marking ^ 1, // toggle : 0 (off) | 1 (on)
|
||||
_ => (marking + 1) % 3, // cycle 0->1->2->0... : 0 (none) | 1 (blue) | 2 (pink)
|
||||
};
|
||||
|
||||
private static Func<int, int, int> FlagHighLow(PKM pk)
|
||||
{
|
||||
if (pk.Format < 7)
|
||||
return GetSimpleMarking;
|
||||
return GetComplexMarking;
|
||||
|
||||
static int GetSimpleMarking(int val, int _) => val == 31 ? 1 : 0;
|
||||
static int GetComplexMarking(int val, int _) => val switch
|
||||
{
|
||||
if (pk.MarkingCount < 6)
|
||||
return; // insufficient marking indexes
|
||||
|
||||
var method = MarkingMethod(pk);
|
||||
pk.SetMarking(0, method(pk.IV_HP , 0));
|
||||
pk.SetMarking(1, method(pk.IV_ATK, 1));
|
||||
pk.SetMarking(2, method(pk.IV_DEF, 2));
|
||||
pk.SetMarking(3, method(pk.IV_SPA, 3));
|
||||
pk.SetMarking(4, method(pk.IV_SPD, 4));
|
||||
pk.SetMarking(5, method(pk.IV_SPE, 5));
|
||||
}
|
||||
|
||||
/// <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 value</returns>
|
||||
public static int ToggleMarking(this PKM pk, int index)
|
||||
{
|
||||
var marking = pk.GetMarking(index);
|
||||
var revised = NextMarking(pk.Format, marking);
|
||||
pk.SetMarking(index, revised);
|
||||
return revised;
|
||||
}
|
||||
|
||||
private static int NextMarking(int format, int marking) => format switch
|
||||
{
|
||||
<= 6 => marking ^ 1, // toggle : 0 (off) | 1 (on)
|
||||
_ => (marking + 1) % 3, // cycle 0->1->2->0... : 0 (none) | 1 (blue) | 2 (pink)
|
||||
31 or 1 => 1,
|
||||
30 or 0 => 2,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
private static Func<int, int, int> FlagHighLow(PKM pk)
|
||||
{
|
||||
if (pk.Format < 7)
|
||||
return GetSimpleMarking;
|
||||
return GetComplexMarking;
|
||||
|
||||
static int GetSimpleMarking(int val, int _) => val == 31 ? 1 : 0;
|
||||
static int GetComplexMarking(int val, int _) => val switch
|
||||
{
|
||||
31 or 1 => 1,
|
||||
30 or 0 => 2,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +1,50 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for modifying the Memory parameters of a <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
public static class MemoryApplicator
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for modifying the Memory parameters of a <see cref="PKM"/>.
|
||||
/// Sets all Memory related data to the default value (zero).
|
||||
/// </summary>
|
||||
public static class MemoryApplicator
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void ClearMemories(this PKM pk)
|
||||
{
|
||||
/// <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)
|
||||
{
|
||||
if (pk is IAffection a)
|
||||
a.OT_Affection = a.HT_Affection = 0;
|
||||
if (pk is IMemoryOT o)
|
||||
o.ClearMemoriesOT();
|
||||
if (pk is IMemoryHT h)
|
||||
h.ClearMemoriesHT();
|
||||
}
|
||||
if (pk is IAffection a)
|
||||
a.OT_Affection = a.HT_Affection = 0;
|
||||
if (pk is IMemoryOT o)
|
||||
o.ClearMemoriesOT();
|
||||
if (pk is IMemoryHT h)
|
||||
h.ClearMemoriesHT();
|
||||
}
|
||||
|
||||
/// <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)
|
||||
/// <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)
|
||||
{
|
||||
if (pk is IMemoryOT o)
|
||||
{
|
||||
if (pk is IMemoryOT o)
|
||||
{
|
||||
o.OT_Memory = 2;
|
||||
o.OT_Feeling = MemoryContext6.GetRandomFeeling6(2);
|
||||
o.OT_Intensity = 1;
|
||||
o.OT_TextVar = pk.XY ? (ushort)43 : (ushort)27; // riverside road : battling spot
|
||||
}
|
||||
if (pk is IAffection a)
|
||||
a.OT_Affection = 0;
|
||||
o.OT_Memory = 2;
|
||||
o.OT_Feeling = MemoryContext6.GetRandomFeeling6(2);
|
||||
o.OT_Intensity = 1;
|
||||
o.OT_TextVar = pk.XY ? (ushort)43 : (ushort)27; // riverside road : battling spot
|
||||
}
|
||||
if (pk is IAffection a)
|
||||
a.OT_Affection = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a random memory specific to <see cref="GameVersion.Gen6"/> locality.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void SetRandomMemory6(this PK6 pk)
|
||||
{
|
||||
// for lack of better randomization :)
|
||||
pk.OT_Memory = 63;
|
||||
pk.OT_Intensity = 6;
|
||||
pk.OT_Feeling = MemoryContext6.GetRandomFeeling6(pk.OT_Memory);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets a random memory specific to <see cref="GameVersion.Gen6"/> locality.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void SetRandomMemory6(this PK6 pk)
|
||||
{
|
||||
// for lack of better randomization :)
|
||||
pk.OT_Memory = 63;
|
||||
pk.OT_Intensity = 6;
|
||||
pk.OT_Feeling = MemoryContext6.GetRandomFeeling6(pk.OT_Memory);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,79 +1,78 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for applying a moveset to a <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
public static class MoveApplicator
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for applying a moveset to a <see cref="PKM"/>.
|
||||
/// Sets the individual PP Up count values depending if a Move is present in the move's slot or not.
|
||||
/// </summary>
|
||||
public static class MoveApplicator
|
||||
/// <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>
|
||||
public static void SetMaximumPPUps(this PKM pk, int[] moves)
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the individual PP Up count values depending if a Move is present in the move's slot 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>
|
||||
public static void SetMaximumPPUps(this PKM pk, int[] moves)
|
||||
{
|
||||
pk.Move1_PPUps = GetPPUpCount(moves[0]);
|
||||
pk.Move2_PPUps = GetPPUpCount(moves[1]);
|
||||
pk.Move3_PPUps = GetPPUpCount(moves[2]);
|
||||
pk.Move4_PPUps = GetPPUpCount(moves[3]);
|
||||
pk.Move1_PPUps = GetPPUpCount(moves[0]);
|
||||
pk.Move2_PPUps = GetPPUpCount(moves[1]);
|
||||
pk.Move3_PPUps = GetPPUpCount(moves[2]);
|
||||
pk.Move4_PPUps = GetPPUpCount(moves[3]);
|
||||
|
||||
pk.SetMaximumPPCurrent(moves);
|
||||
static int GetPPUpCount(int moveID) => moveID > 0 ? 3 : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the individual PP Up count values depending if a Move is present in the move slot or not.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void SetMaximumPPUps(this PKM pk) => pk.SetMaximumPPUps(pk.Moves);
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (Array.FindIndex(moves, z => z > pk.MaxMoveID) != -1)
|
||||
moves = Array.FindAll(moves, z => z <= pk.MaxMoveID);
|
||||
if (moves.Length != 4)
|
||||
Array.Resize(ref moves, 4);
|
||||
|
||||
pk.Moves = moves;
|
||||
if (maxPP && Legal.IsPPUpAvailable(pk))
|
||||
pk.SetMaximumPPUps(moves);
|
||||
else
|
||||
pk.SetMaximumPPCurrent(moves);
|
||||
pk.FixMoves();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the individual PP count values for each move slot 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>
|
||||
public static void SetMaximumPPCurrent(this PKM pk, ReadOnlySpan<int> moves)
|
||||
{
|
||||
pk.Move1_PP = moves.Length == 0 ? 0 : pk.GetMovePP(moves[0], pk.Move1_PPUps);
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the individual PP count values for each move slot 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);
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the Move PP for the desired move.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="index">Move PP to refresh.</param>
|
||||
public static void SetSuggestedMovePP(this PKM pk, int index) => pk.HealPPIndex(index);
|
||||
pk.SetMaximumPPCurrent(moves);
|
||||
static int GetPPUpCount(int moveID) => moveID > 0 ? 3 : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the individual PP Up count values depending if a Move is present in the move slot or not.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void SetMaximumPPUps(this PKM pk) => pk.SetMaximumPPUps(pk.Moves);
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (Array.FindIndex(moves, z => z > pk.MaxMoveID) != -1)
|
||||
moves = Array.FindAll(moves, z => z <= pk.MaxMoveID);
|
||||
if (moves.Length != 4)
|
||||
Array.Resize(ref moves, 4);
|
||||
|
||||
pk.Moves = moves;
|
||||
if (maxPP && Legal.IsPPUpAvailable(pk))
|
||||
pk.SetMaximumPPUps(moves);
|
||||
else
|
||||
pk.SetMaximumPPCurrent(moves);
|
||||
pk.FixMoves();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the individual PP count values for each move slot 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>
|
||||
public static void SetMaximumPPCurrent(this PKM pk, ReadOnlySpan<int> moves)
|
||||
{
|
||||
pk.Move1_PP = moves.Length == 0 ? 0 : pk.GetMovePP(moves[0], pk.Move1_PPUps);
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the individual PP count values for each move slot 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);
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the Move PP for the desired move.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="index">Move PP to refresh.</param>
|
||||
public static void SetSuggestedMovePP(this PKM pk, int index) => pk.HealPPIndex(index);
|
||||
}
|
||||
|
|
|
@ -2,120 +2,119 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for getting valid movesets.
|
||||
/// </summary>
|
||||
public static class MoveSetApplicator
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for getting valid movesets.
|
||||
/// Gets a moveset for the provided <see cref="PKM"/> data.
|
||||
/// </summary>
|
||||
public static class MoveSetApplicator
|
||||
/// <param name="pk">PKM to generate for</param>
|
||||
/// <param name="random">Full movepool & shuffling</param>
|
||||
/// <returns>4 moves</returns>
|
||||
public static int[] GetMoveSet(this PKM pk, bool random = false)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a moveset for the provided <see cref="PKM"/> data.
|
||||
/// </summary>
|
||||
/// <param name="pk">PKM to generate for</param>
|
||||
/// <param name="random">Full movepool & shuffling</param>
|
||||
/// <returns>4 moves</returns>
|
||||
public static int[] GetMoveSet(this PKM pk, bool random = false)
|
||||
{
|
||||
var la = new LegalityAnalysis(pk);
|
||||
var moves = la.GetMoveSet(random);
|
||||
var la = new LegalityAnalysis(pk);
|
||||
var moves = la.GetMoveSet(random);
|
||||
|
||||
if (random)
|
||||
return moves;
|
||||
if (random)
|
||||
return moves;
|
||||
|
||||
var clone = pk.Clone();
|
||||
clone.SetMoves(moves);
|
||||
clone.SetMaximumPPCurrent(moves);
|
||||
var newLa = new LegalityAnalysis(clone);
|
||||
var clone = pk.Clone();
|
||||
clone.SetMoves(moves);
|
||||
clone.SetMaximumPPCurrent(moves);
|
||||
var newLa = new LegalityAnalysis(clone);
|
||||
|
||||
// ReSharper disable once TailRecursiveCall
|
||||
return newLa.Valid ? moves : GetMoveSet(pk, true);
|
||||
}
|
||||
// ReSharper disable once TailRecursiveCall
|
||||
return newLa.Valid ? moves : GetMoveSet(pk, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a moveset for the provided <see cref="PKM"/> data.
|
||||
/// </summary>
|
||||
/// <param name="la">Precomputed optional</param>
|
||||
/// <param name="random">Full movepool & shuffling</param>
|
||||
/// <returns>4 moves</returns>
|
||||
public static int[] GetMoveSet(this LegalityAnalysis la, bool random = false)
|
||||
{
|
||||
int[] m = la.GetSuggestedCurrentMoves(random ? MoveSourceType.All : MoveSourceType.Encounter);
|
||||
/// <summary>
|
||||
/// Gets a moveset for the provided <see cref="PKM"/> data.
|
||||
/// </summary>
|
||||
/// <param name="la">Precomputed optional</param>
|
||||
/// <param name="random">Full movepool & shuffling</param>
|
||||
/// <returns>4 moves</returns>
|
||||
public static int[] GetMoveSet(this LegalityAnalysis la, bool random = false)
|
||||
{
|
||||
int[] m = la.GetSuggestedCurrentMoves(random ? MoveSourceType.All : MoveSourceType.Encounter);
|
||||
|
||||
var learn = la.GetSuggestedMovesAndRelearn();
|
||||
if (!m.All(z => learn.Contains(z)))
|
||||
m = m.Intersect(learn).ToArray();
|
||||
var learn = la.GetSuggestedMovesAndRelearn();
|
||||
if (!m.All(z => learn.Contains(z)))
|
||||
m = m.Intersect(learn).ToArray();
|
||||
|
||||
if (random && !la.pkm.IsEgg)
|
||||
Util.Shuffle(m.AsSpan());
|
||||
if (random && !la.Entity.IsEgg)
|
||||
Util.Shuffle(m.AsSpan());
|
||||
|
||||
const int count = 4;
|
||||
if (m.Length > count)
|
||||
return m.SliceEnd(m.Length - count);
|
||||
Array.Resize(ref m, count);
|
||||
const int count = 4;
|
||||
if (m.Length > count)
|
||||
return m.SliceEnd(m.Length - count);
|
||||
Array.Resize(ref m, count);
|
||||
return m;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches <see cref="PKM.RelearnMoves"/> based on the provided <see cref="LegalityAnalysis"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="enc">Encounter the relearn moves should be suggested for. If not provided, will try to detect it via legality analysis. </param>
|
||||
/// <returns><see cref="PKM.RelearnMoves"/> best suited for the current <see cref="PKM"/> data.</returns>
|
||||
public static IReadOnlyList<int> GetSuggestedRelearnMoves(this PKM pk, IEncounterTemplate? enc = null) => GetSuggestedRelearnMoves(new LegalityAnalysis(pk), enc);
|
||||
|
||||
/// <summary>
|
||||
/// Fetches <see cref="PKM.RelearnMoves"/> based on the provided <see cref="LegalityAnalysis"/>.
|
||||
/// </summary>
|
||||
/// <param name="legal"><see cref="LegalityAnalysis"/> which contains parsed information pertaining to legality.</param>
|
||||
/// <param name="enc">Encounter the relearn moves should be suggested for. If not provided, will try to detect it via legality analysis. </param>
|
||||
/// <returns><see cref="PKM.RelearnMoves"/> best suited for the current <see cref="PKM"/> data.</returns>
|
||||
public static IReadOnlyList<int> GetSuggestedRelearnMoves(this LegalityAnalysis legal, IEncounterTemplate? enc = null)
|
||||
{
|
||||
enc ??= legal.EncounterOriginal;
|
||||
var m = legal.GetSuggestedRelearnMovesFromEncounter(enc);
|
||||
if (m.Any(z => z != 0))
|
||||
return m;
|
||||
|
||||
if (enc is MysteryGift or EncounterEgg)
|
||||
return m;
|
||||
|
||||
if (enc is EncounterSlot6AO {CanDexNav: true} dn)
|
||||
{
|
||||
var moves = legal.Info.Moves;
|
||||
for (int i = 0; i < moves.Length; i++)
|
||||
{
|
||||
if (!moves[i].ShouldBeInRelearnMoves())
|
||||
continue;
|
||||
|
||||
var move = legal.Entity.GetMove(i);
|
||||
if (dn.CanBeDexNavMove(move))
|
||||
return new[] { move, 0, 0, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches <see cref="PKM.RelearnMoves"/> based on the provided <see cref="LegalityAnalysis"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="enc">Encounter the relearn moves should be suggested for. If not provided, will try to detect it via legality analysis. </param>
|
||||
/// <returns><see cref="PKM.RelearnMoves"/> best suited for the current <see cref="PKM"/> data.</returns>
|
||||
public static IReadOnlyList<int> GetSuggestedRelearnMoves(this PKM pk, IEncounterTemplate? enc = null) => GetSuggestedRelearnMoves(new LegalityAnalysis(pk), enc);
|
||||
|
||||
/// <summary>
|
||||
/// Fetches <see cref="PKM.RelearnMoves"/> based on the provided <see cref="LegalityAnalysis"/>.
|
||||
/// </summary>
|
||||
/// <param name="legal"><see cref="LegalityAnalysis"/> which contains parsed information pertaining to legality.</param>
|
||||
/// <param name="enc">Encounter the relearn moves should be suggested for. If not provided, will try to detect it via legality analysis. </param>
|
||||
/// <returns><see cref="PKM.RelearnMoves"/> best suited for the current <see cref="PKM"/> data.</returns>
|
||||
public static IReadOnlyList<int> GetSuggestedRelearnMoves(this LegalityAnalysis legal, IEncounterTemplate? enc = null)
|
||||
if (enc is EncounterSlot8b { IsUnderground: true } ug)
|
||||
{
|
||||
enc ??= legal.EncounterOriginal;
|
||||
var m = legal.GetSuggestedRelearnMovesFromEncounter(enc);
|
||||
if (m.Any(z => z != 0))
|
||||
return m;
|
||||
|
||||
if (enc is MysteryGift or EncounterEgg)
|
||||
return m;
|
||||
|
||||
if (enc is EncounterSlot6AO {CanDexNav: true} dn)
|
||||
var moves = legal.Info.Moves;
|
||||
for (int i = 0; i < moves.Length; i++)
|
||||
{
|
||||
var moves = legal.Info.Moves;
|
||||
for (int i = 0; i < moves.Length; i++)
|
||||
{
|
||||
if (!moves[i].ShouldBeInRelearnMoves())
|
||||
continue;
|
||||
if (!moves[i].ShouldBeInRelearnMoves())
|
||||
continue;
|
||||
|
||||
var move = legal.pkm.GetMove(i);
|
||||
if (dn.CanBeDexNavMove(move))
|
||||
return new[] { move, 0, 0, 0 };
|
||||
}
|
||||
var move = legal.Entity.GetMove(i);
|
||||
if (ug.CanBeUndergroundMove(move))
|
||||
return new[] { move, 0, 0, 0 };
|
||||
}
|
||||
|
||||
if (enc is EncounterSlot8b { IsUnderground: true } ug)
|
||||
{
|
||||
var moves = legal.Info.Moves;
|
||||
for (int i = 0; i < moves.Length; i++)
|
||||
{
|
||||
if (!moves[i].ShouldBeInRelearnMoves())
|
||||
continue;
|
||||
|
||||
var move = legal.pkm.GetMove(i);
|
||||
if (ug.CanBeUndergroundMove(move))
|
||||
return new[] { move, 0, 0, 0 };
|
||||
}
|
||||
|
||||
if (ug.GetBaseEggMove(out int any))
|
||||
return new[] { any, 0, 0, 0 };
|
||||
}
|
||||
|
||||
var encounter = EncounterSuggestion.GetSuggestedMetInfo(legal.pkm);
|
||||
if (encounter is IRelearn {Relearn: {Count: > 0} r})
|
||||
return r;
|
||||
|
||||
return m;
|
||||
if (ug.GetBaseEggMove(out int any))
|
||||
return new[] { any, 0, 0, 0 };
|
||||
}
|
||||
|
||||
var encounter = EncounterSuggestion.GetSuggestedMetInfo(legal.Entity);
|
||||
if (encounter is IRelearn {Relearn: {Count: > 0} r})
|
||||
return r;
|
||||
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,241 +1,241 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for applying ribbons.
|
||||
/// </summary>
|
||||
public static class RibbonApplicator
|
||||
{
|
||||
private static List<string> GetAllRibbonNames(PKM pk) => RibbonInfo.GetRibbonInfo(pk).Select(z => z.Name).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Logic for applying ribbons.
|
||||
/// Gets a list of valid ribbons for the <see cref="pk"/>.
|
||||
/// </summary>
|
||||
public static class RibbonApplicator
|
||||
/// <param name="pk">Entity to fetch the list for.</param>
|
||||
/// <param name="allRibbons">All ribbon names.</param>
|
||||
/// <returns>List of all valid ribbon names.</returns>
|
||||
public static IReadOnlyList<string> GetValidRibbons(PKM pk, IList<string> allRibbons)
|
||||
{
|
||||
private static List<string> GetAllRibbonNames(PKM pkm) => RibbonInfo.GetRibbonInfo(pkm).Select(z => z.Name).ToList();
|
||||
var clone = pk.Clone();
|
||||
return SetAllValidRibbons(allRibbons, clone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of valid ribbons for the <see cref="pkm"/>.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Entity to fetch the list for.</param>
|
||||
/// <param name="allRibbons">All ribbon names.</param>
|
||||
/// <returns>List of all valid ribbon names.</returns>
|
||||
public static IReadOnlyList<string> GetValidRibbons(PKM pkm, IList<string> allRibbons)
|
||||
/// <summary>
|
||||
/// Gets a list of valid ribbons for the <see cref="pk"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Entity to fetch the list for.</param>
|
||||
/// <returns>List of all valid ribbon names.</returns>
|
||||
public static IReadOnlyList<string> GetValidRibbons(PKM pk)
|
||||
{
|
||||
var names = GetAllRibbonNames(pk);
|
||||
return GetValidRibbons(pk, names);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of valid ribbons for the <see cref="pk"/> that can be removed.
|
||||
/// </summary>
|
||||
/// <param name="pk">Entity to fetch the list for.</param>
|
||||
/// <param name="allRibbons">All ribbon names.</param>
|
||||
/// <returns>List of all removable ribbon names.</returns>
|
||||
public static IReadOnlyList<string> GetRemovableRibbons(PKM pk, IList<string> allRibbons)
|
||||
{
|
||||
var clone = pk.Clone();
|
||||
return RemoveAllValidRibbons(allRibbons, clone);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of valid ribbons for the <see cref="pk"/> that can be removed.
|
||||
/// </summary>
|
||||
/// <param name="pk">Entity to fetch the list for.</param>
|
||||
/// <returns>List of all removable ribbon names.</returns>
|
||||
public static IReadOnlyList<string> GetRemovableRibbons(PKM pk)
|
||||
{
|
||||
var names = GetAllRibbonNames(pk);
|
||||
return GetRemovableRibbons(pk, names);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all valid ribbons to the <see cref="pk"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Entity to set ribbons for.</param>
|
||||
/// <returns>True if any ribbons were applied.</returns>
|
||||
public static bool SetAllValidRibbons(PKM pk)
|
||||
{
|
||||
var ribNames = GetAllRibbonNames(pk);
|
||||
ribNames.RemoveAll(z => z.StartsWith("RibbonMark", StringComparison.Ordinal)); // until marking legality is handled
|
||||
return SetAllValidRibbons(pk, ribNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all valid ribbons to the <see cref="pk"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Entity to set ribbons for.</param>
|
||||
/// <param name="ribNames">Ribbon names to try setting.</param>
|
||||
/// <returns>True if any ribbons were applied.</returns>
|
||||
public static bool SetAllValidRibbons(PKM pk, List<string> ribNames)
|
||||
{
|
||||
var list = SetAllValidRibbons(ribNames, pk);
|
||||
return list.Count != 0;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> SetAllValidRibbons(IList<string> allRibbons, PKM pk)
|
||||
{
|
||||
var la = new LegalityAnalysis(pk);
|
||||
var valid = new List<string>();
|
||||
|
||||
while (TryApplyAllRibbons(pk, la, allRibbons, valid) != 0)
|
||||
{
|
||||
var pk = pkm.Clone();
|
||||
return SetAllValidRibbons(allRibbons, pk);
|
||||
// Repeat the operation until no more ribbons are set.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of valid ribbons for the <see cref="pkm"/>.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Entity to fetch the list for.</param>
|
||||
/// <returns>List of all valid ribbon names.</returns>
|
||||
public static IReadOnlyList<string> GetValidRibbons(PKM pkm)
|
||||
// Ribbon Deadlock
|
||||
if (pk is IRibbonSetCommon6 c6)
|
||||
InvertDeadlockContest(c6, la, true);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all valid ribbons to the <see cref="pk"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Entity to set ribbons for.</param>
|
||||
/// <returns>True if any ribbons were removed.</returns>
|
||||
public static bool RemoveAllValidRibbons(PKM pk)
|
||||
{
|
||||
var ribNames = GetAllRibbonNames(pk);
|
||||
return RemoveAllValidRibbons(pk, ribNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all valid ribbons to the <see cref="pk"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Entity to set ribbons for.</param>
|
||||
/// <param name="ribNames">Ribbon names to try setting.</param>
|
||||
/// <returns>True if any ribbons were removed.</returns>
|
||||
public static bool RemoveAllValidRibbons(PKM pk, List<string> ribNames)
|
||||
{
|
||||
var list = RemoveAllValidRibbons(ribNames, pk);
|
||||
return list.Count != 0;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> RemoveAllValidRibbons(IList<string> allRibbons, PKM pk)
|
||||
{
|
||||
var la = new LegalityAnalysis(pk);
|
||||
var valid = new List<string>();
|
||||
|
||||
// Ribbon Deadlock
|
||||
if (pk is IRibbonSetCommon6 c6)
|
||||
InvertDeadlockContest(c6, la, false);
|
||||
|
||||
while (TryRemoveAllRibbons(pk, la, allRibbons, valid) != 0)
|
||||
{
|
||||
var names = GetAllRibbonNames(pkm);
|
||||
return GetValidRibbons(pkm, names);
|
||||
// Repeat the operation until no more ribbons are set.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of valid ribbons for the <see cref="pkm"/> that can be removed.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Entity to fetch the list for.</param>
|
||||
/// <param name="allRibbons">All ribbon names.</param>
|
||||
/// <returns>List of all removable ribbon names.</returns>
|
||||
public static IReadOnlyList<string> GetRemovableRibbons(PKM pkm, IList<string> allRibbons)
|
||||
return valid;
|
||||
}
|
||||
|
||||
private static int TryApplyAllRibbons(PKM pk, LegalityAnalysis la, IList<string> allRibbons, ICollection<string> valid)
|
||||
{
|
||||
int applied = 0;
|
||||
for (int i = 0; i < allRibbons.Count;)
|
||||
{
|
||||
var pk = pkm.Clone();
|
||||
return RemoveAllValidRibbons(allRibbons, pk);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of valid ribbons for the <see cref="pkm"/> that can be removed.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Entity to fetch the list for.</param>
|
||||
/// <returns>List of all removable ribbon names.</returns>
|
||||
public static IReadOnlyList<string> GetRemovableRibbons(PKM pkm)
|
||||
{
|
||||
var names = GetAllRibbonNames(pkm);
|
||||
return GetRemovableRibbons(pkm, names);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all valid ribbons to the <see cref="pkm"/>.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Entity to set ribbons for.</param>
|
||||
/// <returns>True if any ribbons were applied.</returns>
|
||||
public static bool SetAllValidRibbons(PKM pkm)
|
||||
{
|
||||
var ribNames = GetAllRibbonNames(pkm);
|
||||
ribNames.RemoveAll(z => z.StartsWith("RibbonMark")); // until marking legality is handled
|
||||
return SetAllValidRibbons(pkm, ribNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all valid ribbons to the <see cref="pkm"/>.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Entity to set ribbons for.</param>
|
||||
/// <param name="ribNames">Ribbon names to try setting.</param>
|
||||
/// <returns>True if any ribbons were applied.</returns>
|
||||
public static bool SetAllValidRibbons(PKM pkm, List<string> ribNames)
|
||||
{
|
||||
var list = SetAllValidRibbons(ribNames, pkm);
|
||||
return list.Count != 0;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> SetAllValidRibbons(IList<string> allRibbons, PKM pk)
|
||||
{
|
||||
var la = new LegalityAnalysis(pk);
|
||||
var valid = new List<string>();
|
||||
|
||||
while (TryApplyAllRibbons(pk, la, allRibbons, valid) != 0)
|
||||
{
|
||||
// Repeat the operation until no more ribbons are set.
|
||||
}
|
||||
|
||||
// Ribbon Deadlock
|
||||
if (pk is IRibbonSetCommon6 c6)
|
||||
InvertDeadlockContest(c6, la, true);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all valid ribbons to the <see cref="pkm"/>.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Entity to set ribbons for.</param>
|
||||
/// <returns>True if any ribbons were removed.</returns>
|
||||
public static bool RemoveAllValidRibbons(PKM pkm)
|
||||
{
|
||||
var ribNames = GetAllRibbonNames(pkm);
|
||||
return RemoveAllValidRibbons(pkm, ribNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all valid ribbons to the <see cref="pkm"/>.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Entity to set ribbons for.</param>
|
||||
/// <param name="ribNames">Ribbon names to try setting.</param>
|
||||
/// <returns>True if any ribbons were removed.</returns>
|
||||
public static bool RemoveAllValidRibbons(PKM pkm, List<string> ribNames)
|
||||
{
|
||||
var list = RemoveAllValidRibbons(ribNames, pkm);
|
||||
return list.Count != 0;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> RemoveAllValidRibbons(IList<string> allRibbons, PKM pk)
|
||||
{
|
||||
var la = new LegalityAnalysis(pk);
|
||||
var valid = new List<string>();
|
||||
|
||||
// Ribbon Deadlock
|
||||
if (pk is IRibbonSetCommon6 c6)
|
||||
InvertDeadlockContest(c6, la, false);
|
||||
|
||||
while (TryRemoveAllRibbons(pk, la, allRibbons, valid) != 0)
|
||||
{
|
||||
// Repeat the operation until no more ribbons are set.
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
private static int TryApplyAllRibbons(PKM pk, LegalityAnalysis la, IList<string> allRibbons, ICollection<string> valid)
|
||||
{
|
||||
int applied = 0;
|
||||
for (int i = 0; i < allRibbons.Count;)
|
||||
{
|
||||
la.ResetParse();
|
||||
var rib = allRibbons[i];
|
||||
var success = TryApplyRibbon(pk, la, rib);
|
||||
if (success)
|
||||
{
|
||||
++applied;
|
||||
allRibbons.RemoveAt(i);
|
||||
valid.Add(rib);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveRibbon(pk, rib);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return applied;
|
||||
}
|
||||
|
||||
private static int TryRemoveAllRibbons(PKM pk, LegalityAnalysis la, IList<string> allRibbons, ICollection<string> valid)
|
||||
{
|
||||
int removed = 0;
|
||||
for (int i = 0; i < allRibbons.Count;)
|
||||
{
|
||||
la.ResetParse();
|
||||
var rib = allRibbons[i];
|
||||
var success = TryRemoveRibbon(pk, la, rib);
|
||||
if (success)
|
||||
{
|
||||
++removed;
|
||||
allRibbons.RemoveAt(i);
|
||||
valid.Add(rib);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRibbonValue(pk, rib, 1);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
private static void RemoveRibbon(PKM pk, string rib) => SetRibbonValue(pk, rib, 0);
|
||||
|
||||
private static bool TryRemoveRibbon(PKM pk, LegalityAnalysis la, string rib)
|
||||
{
|
||||
RemoveRibbon(pk, rib);
|
||||
return UpdateIsValid(la);
|
||||
}
|
||||
|
||||
private static bool TryApplyRibbon(PKM pk, LegalityAnalysis la, string rib)
|
||||
{
|
||||
SetRibbonValue(pk, rib, 1);
|
||||
return UpdateIsValid(la);
|
||||
}
|
||||
|
||||
private static bool UpdateIsValid(LegalityAnalysis la)
|
||||
{
|
||||
LegalityAnalyzers.Ribbon.Verify(la);
|
||||
return la.Results.All(z => z.Valid);
|
||||
}
|
||||
|
||||
private static void SetRibbonValue(PKM pk, string rib, int value)
|
||||
{
|
||||
switch (rib)
|
||||
{
|
||||
case nameof(PK7.RibbonCountMemoryBattle):
|
||||
ReflectUtil.SetValue(pk, rib, value * (pk.Gen4 ? 6 : 8));
|
||||
break;
|
||||
case nameof(PK7.RibbonCountMemoryContest):
|
||||
ReflectUtil.SetValue(pk, rib, value * (pk.Gen4 ? 20 : 40));
|
||||
break;
|
||||
default:
|
||||
if (rib.StartsWith("RibbonCountG3"))
|
||||
ReflectUtil.SetValue(pk, rib, value * 4);
|
||||
else
|
||||
ReflectUtil.SetValue(pk, rib, value != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void InvertDeadlockContest(IRibbonSetCommon6 c6, LegalityAnalysis la, bool desiredState)
|
||||
{
|
||||
// RibbonContestStar depends on having all contest ribbons, and having RibbonContestStar requires all.
|
||||
// Since the above logic sets individual ribbons, we must try setting this deadlock pair manually.
|
||||
if (c6.RibbonMasterToughness == desiredState || c6.RibbonContestStar == desiredState)
|
||||
return;
|
||||
|
||||
la.ResetParse();
|
||||
c6.RibbonMasterToughness = c6.RibbonContestStar = desiredState;
|
||||
bool result = UpdateIsValid(la);
|
||||
if (!result)
|
||||
c6.RibbonMasterToughness = c6.RibbonContestStar = !desiredState;
|
||||
var rib = allRibbons[i];
|
||||
var success = TryApplyRibbon(pk, la, rib);
|
||||
if (success)
|
||||
{
|
||||
++applied;
|
||||
allRibbons.RemoveAt(i);
|
||||
valid.Add(rib);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveRibbon(pk, rib);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return applied;
|
||||
}
|
||||
|
||||
private static int TryRemoveAllRibbons(PKM pk, LegalityAnalysis la, IList<string> allRibbons, ICollection<string> valid)
|
||||
{
|
||||
int removed = 0;
|
||||
for (int i = 0; i < allRibbons.Count;)
|
||||
{
|
||||
la.ResetParse();
|
||||
var rib = allRibbons[i];
|
||||
var success = TryRemoveRibbon(pk, la, rib);
|
||||
if (success)
|
||||
{
|
||||
++removed;
|
||||
allRibbons.RemoveAt(i);
|
||||
valid.Add(rib);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRibbonValue(pk, rib, 1);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
private static void RemoveRibbon(PKM pk, string rib) => SetRibbonValue(pk, rib, 0);
|
||||
|
||||
private static bool TryRemoveRibbon(PKM pk, LegalityAnalysis la, string rib)
|
||||
{
|
||||
RemoveRibbon(pk, rib);
|
||||
return UpdateIsValid(la);
|
||||
}
|
||||
|
||||
private static bool TryApplyRibbon(PKM pk, LegalityAnalysis la, string rib)
|
||||
{
|
||||
SetRibbonValue(pk, rib, 1);
|
||||
return UpdateIsValid(la);
|
||||
}
|
||||
|
||||
private static bool UpdateIsValid(LegalityAnalysis la)
|
||||
{
|
||||
LegalityAnalyzers.Ribbon.Verify(la);
|
||||
return la.Results.All(z => z.Valid);
|
||||
}
|
||||
|
||||
private static void SetRibbonValue(PKM pk, string rib, int value)
|
||||
{
|
||||
switch (rib)
|
||||
{
|
||||
case nameof(PK7.RibbonCountMemoryBattle):
|
||||
ReflectUtil.SetValue(pk, rib, value * (pk.Gen4 ? 6 : 8));
|
||||
break;
|
||||
case nameof(PK7.RibbonCountMemoryContest):
|
||||
ReflectUtil.SetValue(pk, rib, value * (pk.Gen4 ? 20 : 40));
|
||||
break;
|
||||
default:
|
||||
if (rib.StartsWith("RibbonCountG3", StringComparison.Ordinal))
|
||||
ReflectUtil.SetValue(pk, rib, value * 4);
|
||||
else
|
||||
ReflectUtil.SetValue(pk, rib, value != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void InvertDeadlockContest(IRibbonSetCommon6 c6, LegalityAnalysis la, bool desiredState)
|
||||
{
|
||||
// RibbonContestStar depends on having all contest ribbons, and having RibbonContestStar requires all.
|
||||
// Since the above logic sets individual ribbons, we must try setting this deadlock pair manually.
|
||||
if (c6.RibbonMasterToughness == desiredState || c6.RibbonContestStar == desiredState)
|
||||
return;
|
||||
|
||||
la.ResetParse();
|
||||
c6.RibbonMasterToughness = c6.RibbonContestStar = desiredState;
|
||||
bool result = UpdateIsValid(la);
|
||||
if (!result)
|
||||
c6.RibbonMasterToughness = c6.RibbonContestStar = !desiredState;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,65 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for modifying the Technical Record flags of a <see cref="PK8"/>.
|
||||
/// </summary>
|
||||
public static class TechnicalRecordApplicator
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for modifying the Technical Record flags of a <see cref="PK8"/>.
|
||||
/// Sets the Technical Record flags for the <see cref="pk"/>.
|
||||
/// </summary>
|
||||
public static class TechnicalRecordApplicator
|
||||
/// <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 ITechRecord8 pk, bool value, int max = 100)
|
||||
{
|
||||
/// <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 ITechRecord8 pk, bool value, int max = 100)
|
||||
for (int i = 0; i < max; i++)
|
||||
pk.SetMoveRecordFlag(i, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the Technical Record flags for the <see cref="pk"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void ClearRecordFlags(this ITechRecord8 pk) => pk.SetRecordFlags(false, 112);
|
||||
|
||||
/// <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>
|
||||
/// <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 ITechRecord8 pk, IEnumerable<int> moves)
|
||||
{
|
||||
var permit = pk.TechRecordPermitFlags;
|
||||
var moveIDs = pk.TechRecordPermitIndexes;
|
||||
if (permit.Length != moveIDs.Length)
|
||||
return;
|
||||
|
||||
foreach (var m in moves)
|
||||
{
|
||||
for (int i = 0; i < max; i++)
|
||||
pk.SetMoveRecordFlag(i, value);
|
||||
var index = moveIDs.IndexOf(m);
|
||||
if (index == -1)
|
||||
continue;
|
||||
if (permit[index])
|
||||
pk.SetMoveRecordFlag(index, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the Technical Record flags for the <see cref="pk"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void ClearRecordFlags(this ITechRecord8 pk) => pk.SetRecordFlags(false, 112);
|
||||
|
||||
/// <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>
|
||||
/// <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 ITechRecord8 pk, IEnumerable<int> moves)
|
||||
/// <summary>
|
||||
/// Sets all the Technical Record flags for the <see cref="pk"/> if they are permitted to be learned in-game.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void SetRecordFlags(this ITechRecord8 pk)
|
||||
{
|
||||
var permit = pk.TechRecordPermitFlags;
|
||||
for (int i = 0; i < permit.Length; i++)
|
||||
{
|
||||
var permit = pk.TechRecordPermitFlags;
|
||||
var moveIDs = pk.TechRecordPermitIndexes;
|
||||
if (permit.Length != moveIDs.Length)
|
||||
return;
|
||||
|
||||
foreach (var m in moves)
|
||||
{
|
||||
var index = moveIDs.IndexOf(m);
|
||||
if (index == -1)
|
||||
continue;
|
||||
if (permit[index])
|
||||
pk.SetMoveRecordFlag(index, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all the Technical Record flags for the <see cref="pk"/> if they are permitted to be learned in-game.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void SetRecordFlags(this ITechRecord8 pk)
|
||||
{
|
||||
var permit = pk.TechRecordPermitFlags;
|
||||
for (int i = 0; i < permit.Length; i++)
|
||||
{
|
||||
if (permit[i])
|
||||
pk.SetMoveRecordFlag(i, true);
|
||||
}
|
||||
if (permit[i])
|
||||
pk.SetMoveRecordFlag(i, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,504 +1,503 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using static PKHeX.Core.MessageStrings;
|
||||
using static PKHeX.Core.BatchModifications;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for editing many <see cref="PKM"/> with user provided <see cref="StringInstruction"/> list.
|
||||
/// </summary>
|
||||
public static class BatchEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for editing many <see cref="PKM"/> with user provided <see cref="StringInstruction"/> list.
|
||||
/// </summary>
|
||||
public static class BatchEditing
|
||||
public static readonly Type[] Types =
|
||||
{
|
||||
public static readonly Type[] Types =
|
||||
typeof (PK8), typeof (PA8), typeof (PB8),
|
||||
typeof (PB7),
|
||||
typeof (PK7), typeof (PK6), typeof (PK5), typeof (PK4), typeof(BK4),
|
||||
typeof (PK3), typeof (XK3), typeof (CK3),
|
||||
typeof (PK2), typeof (SK2), typeof (PK1),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Extra properties to show in the list of selectable properties (GUI)
|
||||
/// </summary>
|
||||
public static readonly List<string> CustomProperties = new()
|
||||
{
|
||||
PROP_LEGAL, PROP_TYPENAME, PROP_RIBBONS, PROP_CONTESTSTATS, PROP_MOVEMASTERY,
|
||||
IdentifierContains, nameof(ISlotInfo.Slot), nameof(SlotInfoBox.Box),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Property names, indexed by <see cref="Types"/>.
|
||||
/// </summary>
|
||||
public static string[][] Properties => GetProperties.Value;
|
||||
|
||||
private static readonly Lazy<string[][]> GetProperties = new(() => GetPropArray(Types, CustomProperties));
|
||||
|
||||
private static readonly Dictionary<string, PropertyInfo>[] Props = GetPropertyDictionaries(Types);
|
||||
|
||||
private static Dictionary<string, PropertyInfo>[] GetPropertyDictionaries(IReadOnlyList<Type> types)
|
||||
{
|
||||
var result = new Dictionary<string, PropertyInfo>[types.Count];
|
||||
for (int i = 0; i < types.Count; i++)
|
||||
result[i] = GetPropertyDictionary(types[i], ReflectUtil.GetAllPropertyInfoPublic);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Dictionary<string, PropertyInfo> GetPropertyDictionary(Type type, Func<Type, IEnumerable<PropertyInfo>> selector)
|
||||
{
|
||||
var dict = new Dictionary<string, PropertyInfo>();
|
||||
var props = selector(type);
|
||||
foreach (var p in props)
|
||||
{
|
||||
typeof (PK8), typeof (PA8), typeof (PB8),
|
||||
typeof (PB7),
|
||||
typeof (PK7), typeof (PK6), typeof (PK5), typeof (PK4), typeof(BK4),
|
||||
typeof (PK3), typeof (XK3), typeof (CK3),
|
||||
typeof (PK2), typeof (SK2), typeof (PK1),
|
||||
};
|
||||
if (!dict.ContainsKey(p.Name))
|
||||
dict.Add(p.Name, p);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extra properties to show in the list of selectable properties (GUI)
|
||||
/// </summary>
|
||||
public static readonly List<string> CustomProperties = new()
|
||||
internal const string CONST_RAND = "$rand";
|
||||
internal const string CONST_SHINY = "$shiny";
|
||||
internal const string CONST_SUGGEST = "$suggest";
|
||||
private const string CONST_BYTES = "$[]";
|
||||
private const char CONST_POINTER = '*';
|
||||
internal const char CONST_SPECIAL = '$';
|
||||
|
||||
internal const string PROP_LEGAL = "Legal";
|
||||
internal const string PROP_TYPENAME = "ObjectType";
|
||||
internal const string PROP_RIBBONS = "Ribbons";
|
||||
internal const string PROP_CONTESTSTATS = "ContestStats";
|
||||
internal const string PROP_MOVEMASTERY = "MoveMastery";
|
||||
internal const string IdentifierContains = nameof(IdentifierContains);
|
||||
|
||||
private static string[][] GetPropArray(IReadOnlyList<Type> types, IReadOnlyList<string> extra)
|
||||
{
|
||||
var result = new string[types.Count + 2][];
|
||||
var p = result.AsSpan(1, types.Count);
|
||||
|
||||
for (int i = 0; i < p.Length; i++)
|
||||
{
|
||||
PROP_LEGAL, PROP_TYPENAME, PROP_RIBBONS, PROP_CONTESTSTATS, PROP_MOVEMASTERY,
|
||||
IdentifierContains, nameof(ISlotInfo.Slot), nameof(SlotInfoBox.Box),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Property names, indexed by <see cref="Types"/>.
|
||||
/// </summary>
|
||||
public static string[][] Properties => GetProperties.Value;
|
||||
|
||||
private static readonly Lazy<string[][]> GetProperties = new(() => GetPropArray(Types, CustomProperties));
|
||||
|
||||
private static readonly Dictionary<string, PropertyInfo>[] Props = GetPropertyDictionaries(Types);
|
||||
|
||||
private static Dictionary<string, PropertyInfo>[] GetPropertyDictionaries(IReadOnlyList<Type> types)
|
||||
{
|
||||
var result = new Dictionary<string, PropertyInfo>[types.Count];
|
||||
for (int i = 0; i < types.Count; i++)
|
||||
result[i] = GetPropertyDictionary(types[i], ReflectUtil.GetAllPropertyInfoPublic);
|
||||
return result;
|
||||
var props = ReflectUtil.GetPropertiesPublic(types[i]);
|
||||
p[i] = props.Concat(extra).OrderBy(a => a).ToArray();
|
||||
}
|
||||
|
||||
private static Dictionary<string, PropertyInfo> GetPropertyDictionary(Type type, Func<Type, IEnumerable<PropertyInfo>> selector)
|
||||
// Properties for any PKM
|
||||
// Properties shared by all PKM
|
||||
var first = p[0];
|
||||
var any = new HashSet<string>(first);
|
||||
var all = new HashSet<string>(first);
|
||||
for (int i = 1; i < p.Length; i++)
|
||||
{
|
||||
var dict = new Dictionary<string, PropertyInfo>();
|
||||
var props = selector(type);
|
||||
foreach (var p in props)
|
||||
{
|
||||
if (!dict.ContainsKey(p.Name))
|
||||
dict.Add(p.Name, p);
|
||||
}
|
||||
return dict;
|
||||
any.UnionWith(p[i]);
|
||||
all.IntersectWith(p[i]);
|
||||
}
|
||||
|
||||
internal const string CONST_RAND = "$rand";
|
||||
internal const string CONST_SHINY = "$shiny";
|
||||
internal const string CONST_SUGGEST = "$suggest";
|
||||
private const string CONST_BYTES = "$[]";
|
||||
private const string CONST_POINTER = "*";
|
||||
result[0] = any.OrderBy(z => z).ToArray();
|
||||
result[^1] = all.OrderBy(z => z).ToArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
internal const string PROP_LEGAL = "Legal";
|
||||
internal const string PROP_TYPENAME = "ObjectType";
|
||||
internal const string PROP_RIBBONS = "Ribbons";
|
||||
internal const string PROP_CONTESTSTATS = "ContestStats";
|
||||
internal const string PROP_MOVEMASTERY = "MoveMastery";
|
||||
internal const string IdentifierContains = nameof(IdentifierContains);
|
||||
/// <summary>
|
||||
/// Tries to fetch the <see cref="PKM"/> property from the cache of available properties.
|
||||
/// </summary>
|
||||
/// <param name="pk">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 pk, string name, [NotNullWhen(true)] out PropertyInfo? pi)
|
||||
{
|
||||
var type = pk.GetType();
|
||||
return TryGetHasProperty(type, name, out pi);
|
||||
}
|
||||
|
||||
private static string[][] GetPropArray(IReadOnlyList<Type> types, IReadOnlyList<string> extra)
|
||||
/// <summary>
|
||||
/// Tries to fetch the <see cref="PKM"/> property from the cache of available properties.
|
||||
/// </summary>
|
||||
/// <param name="type">Type 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(Type type, string name, [NotNullWhen(true)] out PropertyInfo? pi)
|
||||
{
|
||||
var index = Array.IndexOf(Types, type);
|
||||
if (index < 0)
|
||||
{
|
||||
var result = new string[types.Count + 2][];
|
||||
var p = result.AsSpan(1, types.Count);
|
||||
|
||||
for (int i = 0; i < p.Length; i++)
|
||||
{
|
||||
var props = ReflectUtil.GetPropertiesPublic(types[i]);
|
||||
p[i] = props.Concat(extra).OrderBy(a => a).ToArray();
|
||||
}
|
||||
|
||||
// Properties for any PKM
|
||||
// Properties shared by all PKM
|
||||
var first = p[0];
|
||||
var any = new HashSet<string>(first);
|
||||
var all = new HashSet<string>(first);
|
||||
for (int i = 1; i < p.Length; i++)
|
||||
{
|
||||
any.UnionWith(p[i]);
|
||||
all.IntersectWith(p[i]);
|
||||
}
|
||||
|
||||
result[0] = any.OrderBy(z => z).ToArray();
|
||||
result[^1] = all.OrderBy(z => z).ToArray();
|
||||
return result;
|
||||
pi = null;
|
||||
return false;
|
||||
}
|
||||
var props = Props[index];
|
||||
return props.TryGetValue(name, out pi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to fetch the <see cref="PKM"/> property from the cache of available properties.
|
||||
/// </summary>
|
||||
/// <param name="pk">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 pk, string name, [NotNullWhen(true)] out PropertyInfo? pi)
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="PKM"/> types that implement the requested <see cref="property"/>.
|
||||
/// </summary>
|
||||
public static IEnumerable<string> GetTypesImplementing(string property)
|
||||
{
|
||||
for (int i = 0; i < Types.Length; i++)
|
||||
{
|
||||
var type = pk.GetType();
|
||||
return TryGetHasProperty(type, name, out pi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to fetch the <see cref="PKM"/> property from the cache of available properties.
|
||||
/// </summary>
|
||||
/// <param name="type">Type 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(Type type, string name, [NotNullWhen(true)] out PropertyInfo? pi)
|
||||
{
|
||||
var index = Array.IndexOf(Types, type);
|
||||
if (index < 0)
|
||||
{
|
||||
pi = null;
|
||||
return false;
|
||||
}
|
||||
var props = Props[index];
|
||||
return props.TryGetValue(name, out pi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="PKM"/> types that implement the requested <see cref="property"/>.
|
||||
/// </summary>
|
||||
public static IEnumerable<string> GetTypesImplementing(string property)
|
||||
{
|
||||
for (int i = 0; i < Types.Length; i++)
|
||||
{
|
||||
var type = Types[i];
|
||||
var props = Props[i];
|
||||
if (!props.TryGetValue(property, out var pi))
|
||||
continue;
|
||||
yield return $"{type.Name}: {pi.PropertyType.Name}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static string? GetPropertyType(string propertyName, int typeIndex = 0)
|
||||
{
|
||||
if (CustomProperties.Contains(propertyName))
|
||||
return "Custom";
|
||||
|
||||
if (typeIndex == 0) // Any
|
||||
{
|
||||
foreach (var p in Props)
|
||||
{
|
||||
if (p.TryGetValue(propertyName, out var pi))
|
||||
return pi.PropertyType.Name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = typeIndex - 1 >= Props.Length ? 0 : typeIndex - 1; // All vs Specific
|
||||
var pr = Props[index];
|
||||
if (!pr.TryGetValue(propertyName, out var info))
|
||||
return null;
|
||||
return info.PropertyType.Name;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
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) or nameof(PKM.Move2) or nameof(PKM.Move3) or nameof(PKM.Move4):
|
||||
case nameof(PKM.RelearnMove1) or nameof(PKM.RelearnMove2) or nameof(PKM.RelearnMove3) or nameof(PKM.RelearnMove4):
|
||||
i.SetScreenedValue(GameInfo.Strings.movelist); return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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="pk">Object to check.</param>
|
||||
/// <returns>True if <see cref="pk"/> matches all filters.</returns>
|
||||
public static bool IsFilterMatch(IEnumerable<StringInstruction> filters, PKM pk) => filters.All(z => IsFilterMatch(z, pk, Props[Array.IndexOf(Types, pk.GetType())]));
|
||||
|
||||
/// <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="pk">Object to check.</param>
|
||||
/// <returns>True if <see cref="pk"/> matches all filters.</returns>
|
||||
public static bool IsFilterMatchMeta(IEnumerable<StringInstruction> filters, SlotCache pk)
|
||||
{
|
||||
foreach (var i in filters)
|
||||
{
|
||||
foreach (var filter in BatchFilters.FilterMeta)
|
||||
{
|
||||
if (!filter.IsMatch(i.PropertyName))
|
||||
continue;
|
||||
|
||||
if (!filter.IsFiltered(pk, i))
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static bool IsFilterMatch(IEnumerable<StringInstruction> filters, object obj)
|
||||
{
|
||||
foreach (var cmd in filters)
|
||||
{
|
||||
if (cmd.PropertyName is PROP_TYPENAME)
|
||||
{
|
||||
if ((obj.GetType().Name == cmd.PropertyValue) != cmd.Evaluator)
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ReflectUtil.HasProperty(obj, cmd.PropertyName, out var pi))
|
||||
return false;
|
||||
try
|
||||
{
|
||||
if (pi.IsValueEqual(obj, cmd.PropertyValue) == cmd.Evaluator)
|
||||
continue;
|
||||
}
|
||||
// User provided inputs can mismatch the type's required value format, and fail to be compared.
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine($"Unable to compare {cmd.PropertyName} to {cmd.PropertyValue}.");
|
||||
Debug.WriteLine(e.Message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to modify the <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">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="pk"/>.</param>
|
||||
/// <returns>Result of the attempted modification.</returns>
|
||||
public static bool TryModify(PKM pk, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
|
||||
{
|
||||
var result = TryModifyPKM(pk, filters, modifications);
|
||||
return result == ModifyResult.Modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to modify the <see cref="BatchInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">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="pk"/>.</param>
|
||||
/// <returns>Result of the attempted modification.</returns>
|
||||
internal static ModifyResult TryModifyPKM(PKM pk, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
|
||||
{
|
||||
if (!pk.ChecksumValid || pk.Species == 0)
|
||||
return ModifyResult.Invalid;
|
||||
|
||||
var info = new BatchInfo(pk);
|
||||
var pi = Props[Array.IndexOf(Types, pk.GetType())];
|
||||
foreach (var cmd in filters)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsFilterMatch(cmd, info, pi))
|
||||
return ModifyResult.Filtered;
|
||||
}
|
||||
// Swallow any error because this can be malformed user input.
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(MsgBEModifyFailCompare + " " + ex.Message, cmd.PropertyName, cmd.PropertyValue);
|
||||
return ModifyResult.Error;
|
||||
}
|
||||
}
|
||||
|
||||
ModifyResult result = ModifyResult.Modified;
|
||||
foreach (var cmd in modifications)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tmp = SetPKMProperty(cmd, info, pi);
|
||||
if (tmp != ModifyResult.Modified)
|
||||
result = tmp;
|
||||
}
|
||||
// Swallow any error because this can be malformed user input.
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(MsgBEModifyFail + " " + ex.Message, cmd.PropertyName, cmd.PropertyValue);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the if the <see cref="BatchInfo"/> 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, BatchInfo info, IReadOnlyDictionary<string, PropertyInfo> props)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
if (cmd.PropertyValue.StartsWith(CONST_BYTES))
|
||||
return SetByteArrayProperty(pk, cmd);
|
||||
|
||||
if (cmd.PropertyValue.StartsWith(CONST_SUGGEST, true, CultureInfo.CurrentCulture))
|
||||
return SetSuggestedPKMProperty(cmd.PropertyName, info, cmd.PropertyValue);
|
||||
if (cmd.PropertyValue == CONST_RAND && cmd.PropertyName == nameof(PKM.Moves))
|
||||
return SetMoves(pk, info.Legality.GetMoveSet(true));
|
||||
|
||||
if (SetComplexProperty(pk, cmd))
|
||||
return ModifyResult.Modified;
|
||||
|
||||
if (!props.TryGetValue(cmd.PropertyName, out var pi))
|
||||
return ModifyResult.Error;
|
||||
|
||||
if (!pi.CanWrite)
|
||||
return ModifyResult.Error;
|
||||
|
||||
object val;
|
||||
if (cmd.Random)
|
||||
val = cmd.RandomValue;
|
||||
else if (cmd.PropertyValue.StartsWith(CONST_POINTER) && props.TryGetValue(cmd.PropertyValue[1..], out var opi))
|
||||
val = opi.GetValue(pk);
|
||||
else
|
||||
val = cmd.PropertyValue;
|
||||
|
||||
ReflectUtil.SetValue(pi, pk, val);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="BatchInfo"/> 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 filter matches, else false.</returns>
|
||||
private static bool IsFilterMatch(StringInstruction cmd, BatchInfo info, IReadOnlyDictionary<string, PropertyInfo> props)
|
||||
{
|
||||
var match = BatchFilters.FilterMods.Find(z => z.IsMatch(cmd.PropertyName));
|
||||
if (match != null)
|
||||
return match.IsFiltered(info, cmd);
|
||||
return IsPropertyFiltered(cmd, info.Entity, props);
|
||||
}
|
||||
|
||||
/// <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="pk">Pokémon to check.</param>
|
||||
/// <param name="props">PropertyInfo cache (optional)</param>
|
||||
/// <returns>True if filter matches, else false.</returns>
|
||||
private static bool IsFilterMatch(StringInstruction cmd, PKM pk, IReadOnlyDictionary<string, PropertyInfo> props)
|
||||
{
|
||||
var match = BatchFilters.FilterMods.Find(z => z.IsMatch(cmd.PropertyName));
|
||||
if (match != null)
|
||||
return match.IsFiltered(pk, cmd);
|
||||
return IsPropertyFiltered(cmd, pk, props);
|
||||
}
|
||||
|
||||
/// <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="pk">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 pk, IReadOnlyDictionary<string, PropertyInfo> props)
|
||||
{
|
||||
if (!props.TryGetValue(cmd.PropertyName, out var pi))
|
||||
return false;
|
||||
if (!pi.CanRead)
|
||||
return false;
|
||||
return pi.IsValueEqual(pk, cmd.PropertyValue) == cmd.Evaluator;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <param name="propValue">Suggestion string which starts with <see cref="CONST_SUGGEST"/></param>
|
||||
private static ModifyResult SetSuggestedPKMProperty(string name, BatchInfo info, string propValue)
|
||||
{
|
||||
var first = BatchMods.SuggestionMods.Find(z => z.IsMatch(name, propValue, info));
|
||||
if (first != null)
|
||||
return first.Modify(name, propValue, info);
|
||||
return ModifyResult.Error;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM"/> byte array property to a specified value.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="cmd">Modification</param>
|
||||
private static ModifyResult SetByteArrayProperty(PKM pk, StringInstruction cmd)
|
||||
{
|
||||
switch (cmd.PropertyName)
|
||||
{
|
||||
case nameof(PKM.Nickname_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.Nickname_Trash); return ModifyResult.Modified;
|
||||
case nameof(PKM.OT_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.OT_Trash); return ModifyResult.Modified;
|
||||
case nameof(PKM.HT_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.HT_Trash); return ModifyResult.Modified;
|
||||
default:
|
||||
return ModifyResult.Error;
|
||||
}
|
||||
static byte[] ConvertToBytes(string str)
|
||||
{
|
||||
var arr = str[CONST_BYTES.Length..].Split(',');
|
||||
return Array.ConvertAll(arr, z => Convert.ToByte(z.Trim(), 16));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM"/> property to a non-specific smart value.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="cmd">Modification</param>
|
||||
/// <returns>True if modified, false if no modifications done.</returns>
|
||||
private static bool SetComplexProperty(PKM pk, StringInstruction cmd)
|
||||
{
|
||||
if (cmd.PropertyName.StartsWith("IV") && cmd.PropertyValue == CONST_RAND)
|
||||
{
|
||||
SetRandomIVs(pk, cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
var match = BatchMods.ComplexMods.Find(z => z.IsMatch(cmd.PropertyName, cmd.PropertyValue));
|
||||
if (match == null)
|
||||
return false;
|
||||
|
||||
match.Modify(pk, cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM"/> IV(s) to a random value.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="cmd">Modification</param>
|
||||
private static void SetRandomIVs(PKM pk, StringInstruction cmd)
|
||||
{
|
||||
if (cmd.PropertyName == nameof(PKM.IVs))
|
||||
{
|
||||
pk.SetRandomIVs();
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryGetHasProperty(pk, cmd.PropertyName, out var pi))
|
||||
ReflectUtil.SetValue(pi, pk, Util.Rand.Next(pk.MaxIV + 1));
|
||||
var type = Types[i];
|
||||
var props = Props[i];
|
||||
if (!props.TryGetValue(property, out var pi))
|
||||
continue;
|
||||
yield return $"{type.Name}: {pi.PropertyType.Name}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static string? GetPropertyType(string propertyName, int typeIndex = 0)
|
||||
{
|
||||
if (CustomProperties.Contains(propertyName))
|
||||
return "Custom";
|
||||
|
||||
if (typeIndex == 0) // Any
|
||||
{
|
||||
foreach (var p in Props)
|
||||
{
|
||||
if (p.TryGetValue(propertyName, out var pi))
|
||||
return pi.PropertyType.Name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = typeIndex - 1 >= Props.Length ? 0 : typeIndex - 1; // All vs Specific
|
||||
var pr = Props[index];
|
||||
if (!pr.TryGetValue(propertyName, out var info))
|
||||
return null;
|
||||
return info.PropertyType.Name;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
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(CONST_SPECIAL) && !pv.StartsWith(CONST_BYTES, StringComparison.Ordinal) && pv.Contains(','))
|
||||
i.SetRandRange(pv);
|
||||
|
||||
SetInstructionScreenedValue(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
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) or nameof(PKM.Move2) or nameof(PKM.Move3) or nameof(PKM.Move4):
|
||||
case nameof(PKM.RelearnMove1) or nameof(PKM.RelearnMove2) or nameof(PKM.RelearnMove3) or nameof(PKM.RelearnMove4):
|
||||
i.SetScreenedValue(GameInfo.Strings.movelist); return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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="pk">Object to check.</param>
|
||||
/// <returns>True if <see cref="pk"/> matches all filters.</returns>
|
||||
public static bool IsFilterMatch(IEnumerable<StringInstruction> filters, PKM pk) => filters.All(z => IsFilterMatch(z, pk, Props[Array.IndexOf(Types, pk.GetType())]));
|
||||
|
||||
/// <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="pk">Object to check.</param>
|
||||
/// <returns>True if <see cref="pk"/> matches all filters.</returns>
|
||||
public static bool IsFilterMatchMeta(IEnumerable<StringInstruction> filters, SlotCache pk)
|
||||
{
|
||||
foreach (var i in filters)
|
||||
{
|
||||
foreach (var filter in BatchFilters.FilterMeta)
|
||||
{
|
||||
if (!filter.IsMatch(i.PropertyName))
|
||||
continue;
|
||||
|
||||
if (!filter.IsFiltered(pk, i))
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static bool IsFilterMatch(IEnumerable<StringInstruction> filters, object obj)
|
||||
{
|
||||
foreach (var cmd in filters)
|
||||
{
|
||||
if (cmd.PropertyName is PROP_TYPENAME)
|
||||
{
|
||||
if ((obj.GetType().Name == cmd.PropertyValue) != cmd.Evaluator)
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ReflectUtil.HasProperty(obj, cmd.PropertyName, out var pi))
|
||||
return false;
|
||||
try
|
||||
{
|
||||
if (pi.IsValueEqual(obj, cmd.PropertyValue) == cmd.Evaluator)
|
||||
continue;
|
||||
}
|
||||
// User provided inputs can mismatch the type's required value format, and fail to be compared.
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine($"Unable to compare {cmd.PropertyName} to {cmd.PropertyValue}.");
|
||||
Debug.WriteLine(e.Message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to modify the <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">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="pk"/>.</param>
|
||||
/// <returns>Result of the attempted modification.</returns>
|
||||
public static bool TryModify(PKM pk, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
|
||||
{
|
||||
var result = TryModifyPKM(pk, filters, modifications);
|
||||
return result == ModifyResult.Modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to modify the <see cref="BatchInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">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="pk"/>.</param>
|
||||
/// <returns>Result of the attempted modification.</returns>
|
||||
internal static ModifyResult TryModifyPKM(PKM pk, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
|
||||
{
|
||||
if (!pk.ChecksumValid || pk.Species == 0)
|
||||
return ModifyResult.Invalid;
|
||||
|
||||
var info = new BatchInfo(pk);
|
||||
var pi = Props[Array.IndexOf(Types, pk.GetType())];
|
||||
foreach (var cmd in filters)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsFilterMatch(cmd, info, pi))
|
||||
return ModifyResult.Filtered;
|
||||
}
|
||||
// Swallow any error because this can be malformed user input.
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(MsgBEModifyFailCompare + " " + ex.Message, cmd.PropertyName, cmd.PropertyValue);
|
||||
return ModifyResult.Error;
|
||||
}
|
||||
}
|
||||
|
||||
ModifyResult result = ModifyResult.Modified;
|
||||
foreach (var cmd in modifications)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tmp = SetPKMProperty(cmd, info, pi);
|
||||
if (tmp != ModifyResult.Modified)
|
||||
result = tmp;
|
||||
}
|
||||
// Swallow any error because this can be malformed user input.
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(MsgBEModifyFail + " " + ex.Message, cmd.PropertyName, cmd.PropertyValue);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the if the <see cref="BatchInfo"/> 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, BatchInfo info, IReadOnlyDictionary<string, PropertyInfo> props)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
if (cmd.PropertyValue.StartsWith(CONST_BYTES, StringComparison.Ordinal))
|
||||
return SetByteArrayProperty(pk, cmd);
|
||||
|
||||
if (cmd.PropertyValue.StartsWith(CONST_SUGGEST, StringComparison.OrdinalIgnoreCase))
|
||||
return SetSuggestedPKMProperty(cmd.PropertyName, info, cmd.PropertyValue);
|
||||
if (cmd.PropertyValue == CONST_RAND && cmd.PropertyName == nameof(PKM.Moves))
|
||||
return SetMoves(pk, info.Legality.GetMoveSet(true));
|
||||
|
||||
if (SetComplexProperty(pk, cmd))
|
||||
return ModifyResult.Modified;
|
||||
|
||||
if (!props.TryGetValue(cmd.PropertyName, out var pi))
|
||||
return ModifyResult.Error;
|
||||
|
||||
if (!pi.CanWrite)
|
||||
return ModifyResult.Error;
|
||||
|
||||
object val;
|
||||
if (cmd.Random)
|
||||
val = cmd.RandomValue;
|
||||
else if (cmd.PropertyValue.StartsWith(CONST_POINTER) && props.TryGetValue(cmd.PropertyValue[1..], out var opi))
|
||||
val = opi.GetValue(pk);
|
||||
else
|
||||
val = cmd.PropertyValue;
|
||||
|
||||
ReflectUtil.SetValue(pi, pk, val);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="BatchInfo"/> 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 filter matches, else false.</returns>
|
||||
private static bool IsFilterMatch(StringInstruction cmd, BatchInfo info, IReadOnlyDictionary<string, PropertyInfo> props)
|
||||
{
|
||||
var match = BatchFilters.FilterMods.Find(z => z.IsMatch(cmd.PropertyName));
|
||||
if (match != null)
|
||||
return match.IsFiltered(info, cmd);
|
||||
return IsPropertyFiltered(cmd, info.Entity, props);
|
||||
}
|
||||
|
||||
/// <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="pk">Pokémon to check.</param>
|
||||
/// <param name="props">PropertyInfo cache (optional)</param>
|
||||
/// <returns>True if filter matches, else false.</returns>
|
||||
private static bool IsFilterMatch(StringInstruction cmd, PKM pk, IReadOnlyDictionary<string, PropertyInfo> props)
|
||||
{
|
||||
var match = BatchFilters.FilterMods.Find(z => z.IsMatch(cmd.PropertyName));
|
||||
if (match != null)
|
||||
return match.IsFiltered(pk, cmd);
|
||||
return IsPropertyFiltered(cmd, pk, props);
|
||||
}
|
||||
|
||||
/// <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="pk">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 pk, IReadOnlyDictionary<string, PropertyInfo> props)
|
||||
{
|
||||
if (!props.TryGetValue(cmd.PropertyName, out var pi))
|
||||
return false;
|
||||
if (!pi.CanRead)
|
||||
return false;
|
||||
return pi.IsValueEqual(pk, cmd.PropertyValue) == cmd.Evaluator;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <param name="propValue">Suggestion string which starts with <see cref="CONST_SUGGEST"/></param>
|
||||
private static ModifyResult SetSuggestedPKMProperty(string name, BatchInfo info, string propValue)
|
||||
{
|
||||
var first = BatchMods.SuggestionMods.Find(z => z.IsMatch(name, propValue, info));
|
||||
if (first != null)
|
||||
return first.Modify(name, propValue, info);
|
||||
return ModifyResult.Error;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM"/> byte array property to a specified value.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="cmd">Modification</param>
|
||||
private static ModifyResult SetByteArrayProperty(PKM pk, StringInstruction cmd)
|
||||
{
|
||||
switch (cmd.PropertyName)
|
||||
{
|
||||
case nameof(PKM.Nickname_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.Nickname_Trash); return ModifyResult.Modified;
|
||||
case nameof(PKM.OT_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.OT_Trash); return ModifyResult.Modified;
|
||||
case nameof(PKM.HT_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.HT_Trash); return ModifyResult.Modified;
|
||||
default:
|
||||
return ModifyResult.Error;
|
||||
}
|
||||
static byte[] ConvertToBytes(string str)
|
||||
{
|
||||
var arr = str[CONST_BYTES.Length..].Split(',');
|
||||
return Array.ConvertAll(arr, z => Convert.ToByte(z.Trim(), 16));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM"/> property to a non-specific smart value.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="cmd">Modification</param>
|
||||
/// <returns>True if modified, false if no modifications done.</returns>
|
||||
private static bool SetComplexProperty(PKM pk, StringInstruction cmd)
|
||||
{
|
||||
if (cmd.PropertyName.StartsWith("IV", StringComparison.Ordinal) && cmd.PropertyValue == CONST_RAND)
|
||||
{
|
||||
SetRandomIVs(pk, cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
var match = BatchMods.ComplexMods.Find(z => z.IsMatch(cmd.PropertyName, cmd.PropertyValue));
|
||||
if (match == null)
|
||||
return false;
|
||||
|
||||
match.Modify(pk, cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM"/> IV(s) to a random value.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="cmd">Modification</param>
|
||||
private static void SetRandomIVs(PKM pk, StringInstruction cmd)
|
||||
{
|
||||
if (cmd.PropertyName == nameof(PKM.IVs))
|
||||
{
|
||||
pk.SetRandomIVs();
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryGetHasProperty(pk, cmd.PropertyName, out var pi))
|
||||
ReflectUtil.SetValue(pi, pk, Util.Rand.Next(pk.MaxIV + 1));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,83 +4,82 @@ using System.Diagnostics;
|
|||
using System.Linq;
|
||||
using static PKHeX.Core.MessageStrings;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Carries out a batch edit and contains information summarizing the results.
|
||||
/// </summary>
|
||||
public sealed class BatchEditor
|
||||
{
|
||||
private int Modified { get; set; }
|
||||
private int Iterated { get; set; }
|
||||
private int Failed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Carries out a batch edit and contains information summarizing the results.
|
||||
/// Tries to modify the <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
public sealed class BatchEditor
|
||||
/// <param name="pk">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="pk"/>.</param>
|
||||
/// <returns>Result of the attempted modification.</returns>
|
||||
public bool Process(PKM pk, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
|
||||
{
|
||||
private int Modified { get; set; }
|
||||
private int Iterated { get; set; }
|
||||
private int Failed { get; set; }
|
||||
|
||||
/// <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 bool Process(PKM pkm, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
|
||||
if (pk.Species <= 0)
|
||||
return false;
|
||||
if (!pk.Valid)
|
||||
{
|
||||
if (pkm.Species <= 0)
|
||||
return false;
|
||||
if (!pkm.Valid)
|
||||
{
|
||||
Iterated++;
|
||||
const string reason = "Not Valid.";
|
||||
Debug.WriteLine($"{MsgBEModifyFailBlocked} {reason}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = BatchEditing.TryModifyPKM(pkm, filters, modifications);
|
||||
if (result != ModifyResult.Invalid)
|
||||
Iterated++;
|
||||
if (result == ModifyResult.Error)
|
||||
Failed++;
|
||||
if (result != ModifyResult.Modified)
|
||||
return false;
|
||||
|
||||
pkm.RefreshChecksum();
|
||||
Modified++;
|
||||
return true;
|
||||
Iterated++;
|
||||
const string reason = "Not Valid.";
|
||||
Debug.WriteLine($"{MsgBEModifyFailBlocked} {reason}");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a message indicating the overall result of all modifications performed across multiple Batch Edit jobs.
|
||||
/// </summary>
|
||||
/// <param name="sets">Collection of modifications.</param>
|
||||
/// <returns>Friendly (multi-line) string indicating the result of the batch edits.</returns>
|
||||
public string GetEditorResults(ICollection<StringInstructionSet> sets)
|
||||
var result = BatchEditing.TryModifyPKM(pk, filters, modifications);
|
||||
if (result != ModifyResult.Invalid)
|
||||
Iterated++;
|
||||
if (result == ModifyResult.Error)
|
||||
Failed++;
|
||||
if (result != ModifyResult.Modified)
|
||||
return false;
|
||||
|
||||
pk.RefreshChecksum();
|
||||
Modified++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a message indicating the overall result of all modifications performed across multiple Batch Edit jobs.
|
||||
/// </summary>
|
||||
/// <param name="sets">Collection of modifications.</param>
|
||||
/// <returns>Friendly (multi-line) string indicating the result of the batch edits.</returns>
|
||||
public string GetEditorResults(ICollection<StringInstructionSet> sets)
|
||||
{
|
||||
if (sets.Count == 0)
|
||||
return MsgBEInstructionNone;
|
||||
int ctr = Modified / sets.Count;
|
||||
int len = Iterated / sets.Count;
|
||||
string maybe = sets.Count == 1 ? string.Empty : "~";
|
||||
string result = string.Format(MsgBEModifySuccess, maybe, ctr, len);
|
||||
if (Failed > 0)
|
||||
result += Environment.NewLine + maybe + string.Format(MsgBEModifyFailError, Failed);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static BatchEditor Execute(IList<string> lines, IEnumerable<PKM> data)
|
||||
{
|
||||
var editor = new BatchEditor();
|
||||
var sets = StringInstructionSet.GetBatchSets(lines).ToArray();
|
||||
foreach (var pk in data)
|
||||
{
|
||||
if (sets.Count == 0)
|
||||
return MsgBEInstructionNone;
|
||||
int ctr = Modified / sets.Count;
|
||||
int len = Iterated / sets.Count;
|
||||
string maybe = sets.Count == 1 ? string.Empty : "~";
|
||||
string result = string.Format(MsgBEModifySuccess, maybe, ctr, len);
|
||||
if (Failed > 0)
|
||||
result += Environment.NewLine + maybe + string.Format(MsgBEModifyFailError, Failed);
|
||||
return result;
|
||||
foreach (var set in sets)
|
||||
editor.Process(pk, set.Filters, set.Instructions);
|
||||
}
|
||||
|
||||
public static BatchEditor Execute(IList<string> lines, IEnumerable<PKM> data)
|
||||
{
|
||||
var editor = new BatchEditor();
|
||||
var sets = StringInstructionSet.GetBatchSets(lines).ToArray();
|
||||
foreach (var pk in data)
|
||||
{
|
||||
foreach (var set in sets)
|
||||
editor.Process(pk, set.Filters, set.Instructions);
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
public void AddSkipped()
|
||||
{
|
||||
++Iterated;
|
||||
}
|
||||
public void AddSkipped()
|
||||
{
|
||||
++Iterated;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +1,30 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using static PKHeX.Core.BatchEditing;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public static class BatchFilters
|
||||
{
|
||||
public static class BatchFilters
|
||||
public static readonly List<IComplexFilter> FilterMods = new()
|
||||
{
|
||||
public static readonly List<IComplexFilter> FilterMods = new()
|
||||
{
|
||||
new ComplexFilter(PROP_LEGAL,
|
||||
(pkm, cmd) => bool.TryParse(cmd.PropertyValue, out var b) && (b == new LegalityAnalysis(pkm).Valid) == cmd.Evaluator,
|
||||
(info, cmd) => bool.TryParse(cmd.PropertyValue, out var b) && (b == info.Legality.Valid) == cmd.Evaluator),
|
||||
new ComplexFilter(PROP_LEGAL,
|
||||
(pk, cmd) => bool.TryParse(cmd.PropertyValue, out var b) && (b == new LegalityAnalysis(pk).Valid) == cmd.Evaluator,
|
||||
(info, cmd) => bool.TryParse(cmd.PropertyValue, out var b) && (b == info.Legality.Valid) == cmd.Evaluator),
|
||||
|
||||
new ComplexFilter(PROP_TYPENAME,
|
||||
(pkm, cmd) => (pkm.GetType().Name == cmd.PropertyValue) == cmd.Evaluator,
|
||||
(info, cmd) => (info.Entity.GetType().Name == cmd.PropertyValue) == cmd.Evaluator),
|
||||
};
|
||||
new ComplexFilter(PROP_TYPENAME,
|
||||
(pk, cmd) => (pk.GetType().Name == cmd.PropertyValue) == cmd.Evaluator,
|
||||
(info, cmd) => (info.Entity.GetType().Name == cmd.PropertyValue) == cmd.Evaluator),
|
||||
};
|
||||
|
||||
public static readonly List<IComplexFilterMeta> FilterMeta = new()
|
||||
{
|
||||
new MetaFilter(IdentifierContains,
|
||||
(obj, cmd) => obj is SlotCache s && s.Identify().Contains(cmd.PropertyValue) == cmd.Evaluator),
|
||||
public static readonly List<IComplexFilterMeta> FilterMeta = new()
|
||||
{
|
||||
new MetaFilter(IdentifierContains,
|
||||
(obj, cmd) => obj is SlotCache s && s.Identify().Contains(cmd.PropertyValue) == cmd.Evaluator),
|
||||
|
||||
new MetaFilter(nameof(SlotInfoBox.Box),
|
||||
(obj, cmd) => obj is SlotCache { Source: SlotInfoBox b } && int.TryParse(cmd.PropertyValue, out var box) && b.Box + 1 == box),
|
||||
new MetaFilter(nameof(SlotInfoBox.Box),
|
||||
(obj, cmd) => obj is SlotCache { Source: SlotInfoBox b } && int.TryParse(cmd.PropertyValue, out var box) && b.Box + 1 == box),
|
||||
|
||||
new MetaFilter(nameof(ISlotInfo.Slot),
|
||||
(obj, cmd) => obj is SlotCache s && int.TryParse(cmd.PropertyValue, out var slot) && s.Source.Slot + 1 == slot),
|
||||
};
|
||||
}
|
||||
new MetaFilter(nameof(ISlotInfo.Slot),
|
||||
(obj, cmd) => obj is SlotCache s && int.TryParse(cmd.PropertyValue, out var slot) && s.Source.Slot + 1 == slot),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Information wrapper used for Batch Editing to apply suggested values.
|
||||
/// </summary>
|
||||
public sealed class BatchInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Information wrapper used for Batch Editing to apply suggested values.
|
||||
/// </summary>
|
||||
public sealed class BatchInfo
|
||||
{
|
||||
internal PKM Entity { get; }
|
||||
internal BatchInfo(PKM pk) => Entity = pk;
|
||||
internal PKM Entity { get; }
|
||||
internal BatchInfo(PKM pk) => Entity = pk;
|
||||
|
||||
private LegalityAnalysis? la;
|
||||
internal LegalityAnalysis Legality => la ??= new LegalityAnalysis(Entity);
|
||||
private LegalityAnalysis? la;
|
||||
internal LegalityAnalysis Legality => la ??= new LegalityAnalysis(Entity);
|
||||
|
||||
public bool Legal => Legality.Valid;
|
||||
internal IReadOnlyList<int> SuggestedRelearn => Legality.GetSuggestedRelearnMoves();
|
||||
}
|
||||
public bool Legal => Legality.Valid;
|
||||
internal IReadOnlyList<int> SuggestedRelearn => Legality.GetSuggestedRelearnMoves();
|
||||
}
|
||||
|
|
|
@ -3,95 +3,94 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using static PKHeX.Core.BatchEditing;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public static class BatchMods
|
||||
{
|
||||
public static class BatchMods
|
||||
public static readonly List<ISuggestModification> SuggestionMods = new()
|
||||
{
|
||||
public static readonly List<ISuggestModification> SuggestionMods = new()
|
||||
{
|
||||
// Interface Specific
|
||||
new TypeSuggestion<ICombatPower>(nameof(ICombatPower.Stat_CP), p => p.ResetCP()),
|
||||
new TypeSuggestion<IScaledSizeValue>(nameof(IScaledSizeValue.HeightAbsolute), p => p.ResetHeight()),
|
||||
new TypeSuggestion<IScaledSizeValue>(nameof(IScaledSizeValue.WeightAbsolute), p => p.ResetWeight()),
|
||||
new TypeSuggestion<IHyperTrain>(nameof(Extensions.HyperTrainClear), p => p.HyperTrainClear()),
|
||||
new TypeSuggestion<IGeoTrack>(nameof(Extensions.ClearGeoLocationData), p => p.ClearGeoLocationData()),
|
||||
new TypeSuggestion<IAwakened>(nameof(Extensions.AwakeningClear), p => p.AwakeningClear()),
|
||||
new TypeSuggestion<IAwakened>(nameof(Extensions.AwakeningMax), p => p.AwakeningMax()),
|
||||
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.ClearGanbaruValues), p => p.ClearGanbaruValues()),
|
||||
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.SetSuggestedGanbaruValues), p => p.SetSuggestedGanbaruValues((PKM)p)),
|
||||
// Interface Specific
|
||||
new TypeSuggestion<ICombatPower>(nameof(ICombatPower.Stat_CP), p => p.ResetCP()),
|
||||
new TypeSuggestion<IScaledSizeValue>(nameof(IScaledSizeValue.HeightAbsolute), p => p.ResetHeight()),
|
||||
new TypeSuggestion<IScaledSizeValue>(nameof(IScaledSizeValue.WeightAbsolute), p => p.ResetWeight()),
|
||||
new TypeSuggestion<IHyperTrain>(nameof(Extensions.HyperTrainClear), p => p.HyperTrainClear()),
|
||||
new TypeSuggestion<IGeoTrack>(nameof(Extensions.ClearGeoLocationData), p => p.ClearGeoLocationData()),
|
||||
new TypeSuggestion<IAwakened>(nameof(Extensions.AwakeningClear), p => p.AwakeningClear()),
|
||||
new TypeSuggestion<IAwakened>(nameof(Extensions.AwakeningMax), p => p.AwakeningMax()),
|
||||
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.ClearGanbaruValues), p => p.ClearGanbaruValues()),
|
||||
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.SetSuggestedGanbaruValues), p => p.SetSuggestedGanbaruValues((PKM)p)),
|
||||
|
||||
// Date Copy
|
||||
new TypeSuggestion<PKM>(nameof(PKM.EggMetDate), p => p.EggMetDate = p.MetDate),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.MetDate), p => p.MetDate = p.EggMetDate),
|
||||
// Date Copy
|
||||
new TypeSuggestion<PKM>(nameof(PKM.EggMetDate), p => p.EggMetDate = p.MetDate),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.MetDate), p => p.MetDate = p.EggMetDate),
|
||||
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Nature), p => p.Format >= 8, p => p.Nature = p.StatNature),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.StatNature), p => p.Format >= 8, p => p.StatNature = p.Nature),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Stats), p => p.ResetPartyStats()),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Ball), p => BallApplicator.ApplyBallLegalByColor(p)),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Heal), p => p.Heal()),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.HealPP), p => p.HealPP()),
|
||||
new TypeSuggestion<PKM>(nameof(IHyperTrain.HyperTrainFlags), p => p.SetSuggestedHyperTrainingData()),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Nature), p => p.Format >= 8, p => p.Nature = p.StatNature),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.StatNature), p => p.Format >= 8, p => p.StatNature = p.Nature),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Stats), p => p.ResetPartyStats()),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Ball), p => BallApplicator.ApplyBallLegalByColor(p)),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Heal), p => p.Heal()),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.HealPP), p => p.HealPP()),
|
||||
new TypeSuggestion<PKM>(nameof(IHyperTrain.HyperTrainFlags), p => p.SetSuggestedHyperTrainingData()),
|
||||
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Move1_PP), p => p.SetSuggestedMovePP(0)),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Move2_PP), p => p.SetSuggestedMovePP(1)),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Move3_PP), p => p.SetSuggestedMovePP(2)),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Move4_PP), p => p.SetSuggestedMovePP(3)),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Move1_PP), p => p.SetSuggestedMovePP(0)),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Move2_PP), p => p.SetSuggestedMovePP(1)),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Move3_PP), p => p.SetSuggestedMovePP(2)),
|
||||
new TypeSuggestion<PKM>(nameof(PKM.Move4_PP), p => p.SetSuggestedMovePP(3)),
|
||||
|
||||
new ComplexSuggestion(nameof(PKM.Moves), (_, _, info) => BatchModifications.SetMoves(info.Entity, info.Legality.GetMoveSet())),
|
||||
new ComplexSuggestion(nameof(PKM.EVs), (_, _, info) => BatchModifications.SetEVs(info.Entity)),
|
||||
new ComplexSuggestion(nameof(PKM.RelearnMoves), (_, value, info) => BatchModifications.SetSuggestedRelearnData(info, value)),
|
||||
new ComplexSuggestion(PROP_RIBBONS, (_, value, info) => BatchModifications.SetSuggestedRibbons(info, value)),
|
||||
new ComplexSuggestion(nameof(PKM.Met_Location), (_, _, info) => BatchModifications.SetSuggestedMetData(info)),
|
||||
new ComplexSuggestion(nameof(PKM.CurrentLevel), (_, _, info) => BatchModifications.SetMinimumCurrentLevel(info)),
|
||||
new ComplexSuggestion(PROP_CONTESTSTATS, p => p is IContestStatsMutable, (_, value, info) => BatchModifications.SetContestStats(info.Entity, info.Legality, value)),
|
||||
new ComplexSuggestion(PROP_MOVEMASTERY, (_, value, info) => BatchModifications.SetSuggestedMasteryData(info, value)),
|
||||
};
|
||||
new ComplexSuggestion(nameof(PKM.Moves), (_, _, info) => BatchModifications.SetMoves(info.Entity, info.Legality.GetMoveSet())),
|
||||
new ComplexSuggestion(nameof(PKM.EVs), (_, _, info) => BatchModifications.SetEVs(info.Entity)),
|
||||
new ComplexSuggestion(nameof(PKM.RelearnMoves), (_, value, info) => BatchModifications.SetSuggestedRelearnData(info, value)),
|
||||
new ComplexSuggestion(PROP_RIBBONS, (_, value, info) => BatchModifications.SetSuggestedRibbons(info, value)),
|
||||
new ComplexSuggestion(nameof(PKM.Met_Location), (_, _, info) => BatchModifications.SetSuggestedMetData(info)),
|
||||
new ComplexSuggestion(nameof(PKM.CurrentLevel), (_, _, info) => BatchModifications.SetMinimumCurrentLevel(info)),
|
||||
new ComplexSuggestion(PROP_CONTESTSTATS, p => p is IContestStatsMutable, (_, value, info) => BatchModifications.SetContestStats(info.Entity, info.Legality, value)),
|
||||
new ComplexSuggestion(PROP_MOVEMASTERY, (_, value, info) => BatchModifications.SetSuggestedMasteryData(info, value)),
|
||||
};
|
||||
|
||||
private static DateTime ParseDate(string val) => DateTime.ParseExact(val, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);
|
||||
private static DateTime ParseDate(string val) => DateTime.ParseExact(val, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);
|
||||
|
||||
public static readonly List<IComplexSet> ComplexMods = new()
|
||||
{
|
||||
// Date
|
||||
new ComplexSet(nameof(PKM.MetDate), (pk, cmd) => pk.MetDate = ParseDate(cmd.PropertyValue)),
|
||||
new ComplexSet(nameof(PKM.EggMetDate), (pk, cmd) => pk.EggMetDate = ParseDate(cmd.PropertyValue)),
|
||||
public static readonly List<IComplexSet> ComplexMods = new()
|
||||
{
|
||||
// Date
|
||||
new ComplexSet(nameof(PKM.MetDate), (pk, cmd) => pk.MetDate = ParseDate(cmd.PropertyValue)),
|
||||
new ComplexSet(nameof(PKM.EggMetDate), (pk, cmd) => pk.EggMetDate = ParseDate(cmd.PropertyValue)),
|
||||
|
||||
// Value Swap
|
||||
new ComplexSet(nameof(PKM.EncryptionConstant), value => value == nameof(PKM.PID), (pk, _) => pk.EncryptionConstant = pk.PID),
|
||||
new ComplexSet(nameof(PKM.PID), value => value == nameof(PKM.EncryptionConstant), (pk, _) => pk.PID = pk.EncryptionConstant),
|
||||
// Value Swap
|
||||
new ComplexSet(nameof(PKM.EncryptionConstant), value => value == nameof(PKM.PID), (pk, _) => pk.EncryptionConstant = pk.PID),
|
||||
new ComplexSet(nameof(PKM.PID), value => value == nameof(PKM.EncryptionConstant), (pk, _) => pk.PID = pk.EncryptionConstant),
|
||||
|
||||
// Realign to Derived Value
|
||||
new ComplexSet(nameof(PKM.Ability), value => value.StartsWith("$"), (pk, cmd) => pk.RefreshAbility(Convert.ToInt16(cmd.PropertyValue[1]) - 0x30)),
|
||||
new ComplexSet(nameof(PKM.AbilityNumber), value => value.StartsWith("$"), (pk, cmd) => pk.RefreshAbility(Convert.ToInt16(cmd.PropertyValue[1]) - 0x30)),
|
||||
// Realign to Derived Value
|
||||
new ComplexSet(nameof(PKM.Ability), value => value.StartsWith(CONST_SPECIAL), (pk, cmd) => pk.RefreshAbility(Convert.ToInt16(cmd.PropertyValue[1]) - 0x30)),
|
||||
new ComplexSet(nameof(PKM.AbilityNumber), value => value.StartsWith(CONST_SPECIAL), (pk, cmd) => pk.RefreshAbility(Convert.ToInt16(cmd.PropertyValue[1]) - 0x30)),
|
||||
|
||||
// Random
|
||||
new ComplexSet(nameof(PKM.EncryptionConstant), value => value == CONST_RAND, (pk, _) => pk.EncryptionConstant = Util.Rand32()),
|
||||
new ComplexSet(nameof(PKM.PID), value => value == CONST_RAND, (pk, _) => pk.PID = Util.Rand32()),
|
||||
new ComplexSet(nameof(PKM.Gender), value => value == CONST_RAND, (pk, _) => pk.SetPIDGender(pk.Gender)),
|
||||
new ComplexSet(nameof(PKM.EVs), value => value == CONST_RAND, (pk, _) => SetRandomEVs(pk)),
|
||||
// Random
|
||||
new ComplexSet(nameof(PKM.EncryptionConstant), value => value == CONST_RAND, (pk, _) => pk.EncryptionConstant = Util.Rand32()),
|
||||
new ComplexSet(nameof(PKM.PID), value => value == CONST_RAND, (pk, _) => pk.PID = Util.Rand32()),
|
||||
new ComplexSet(nameof(PKM.Gender), value => value == CONST_RAND, (pk, _) => pk.SetPIDGender(pk.Gender)),
|
||||
new ComplexSet(nameof(PKM.EVs), value => value == CONST_RAND, (pk, _) => SetRandomEVs(pk)),
|
||||
|
||||
// Shiny
|
||||
new ComplexSet(nameof(PKM.PID),
|
||||
value => value.StartsWith(CONST_SHINY, true, CultureInfo.CurrentCulture),
|
||||
(pk, cmd) => CommonEdits.SetShiny(pk, GetRequestedShinyState(cmd.PropertyValue))),
|
||||
// Shiny
|
||||
new ComplexSet(nameof(PKM.PID),
|
||||
value => value.StartsWith(CONST_SHINY, true, CultureInfo.CurrentCulture),
|
||||
(pk, cmd) => CommonEdits.SetShiny(pk, GetRequestedShinyState(cmd.PropertyValue))),
|
||||
|
||||
new ComplexSet(nameof(PKM.Species), value => value == "0", (pk, _) => Array.Clear(pk.Data, 0, pk.Data.Length)),
|
||||
new ComplexSet(nameof(PKM.IsNicknamed), value => string.Equals(value, "false", StringComparison.OrdinalIgnoreCase), (pk, _) => pk.SetDefaultNickname()),
|
||||
};
|
||||
new ComplexSet(nameof(PKM.Species), value => value == "0", (pk, _) => Array.Clear(pk.Data, 0, pk.Data.Length)),
|
||||
new ComplexSet(nameof(PKM.IsNicknamed), value => string.Equals(value, "false", StringComparison.OrdinalIgnoreCase), (pk, _) => pk.SetDefaultNickname()),
|
||||
};
|
||||
|
||||
private static void SetRandomEVs(PKM pk)
|
||||
{
|
||||
Span<int> evs = stackalloc int[6];
|
||||
EffortValues.SetRandom(evs, pk.Format);
|
||||
pk.SetEVs(evs);
|
||||
}
|
||||
|
||||
private static Shiny GetRequestedShinyState(string text)
|
||||
{
|
||||
if (text.EndsWith("0"))
|
||||
return Shiny.AlwaysSquare;
|
||||
if (text.EndsWith("1"))
|
||||
return Shiny.AlwaysStar;
|
||||
return Shiny.Random;
|
||||
}
|
||||
private static void SetRandomEVs(PKM pk)
|
||||
{
|
||||
Span<int> evs = stackalloc int[6];
|
||||
EffortValues.SetRandom(evs, pk.Format);
|
||||
pk.SetEVs(evs);
|
||||
}
|
||||
|
||||
private static Shiny GetRequestedShinyState(string text) => text.Length == 0 ? Shiny.Random : GetRequestedShinyState(text[^1]);
|
||||
|
||||
private static Shiny GetRequestedShinyState(char last) => last switch
|
||||
{
|
||||
'0' => Shiny.AlwaysSquare,
|
||||
'1' => Shiny.AlwaysStar,
|
||||
_ => Shiny.Random,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,26 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <inheritdoc cref="IComplexFilter"/>
|
||||
public sealed class ComplexFilter : IComplexFilter
|
||||
{
|
||||
/// <inheritdoc cref="IComplexFilter"/>
|
||||
public sealed class ComplexFilter : IComplexFilter
|
||||
private readonly string Property;
|
||||
private readonly Func<PKM, StringInstruction, bool> FilterPKM;
|
||||
private readonly Func<BatchInfo, StringInstruction, bool> FilterBulk;
|
||||
|
||||
public ComplexFilter(
|
||||
string property,
|
||||
Func<PKM, StringInstruction, bool> filterPkm,
|
||||
Func<BatchInfo, StringInstruction, bool> filterBulk)
|
||||
{
|
||||
private readonly string Property;
|
||||
private readonly Func<PKM, StringInstruction, bool> FilterPKM;
|
||||
private readonly Func<BatchInfo, StringInstruction, bool> FilterBulk;
|
||||
|
||||
public ComplexFilter(
|
||||
string property,
|
||||
Func<PKM, StringInstruction, bool> filterPkm,
|
||||
Func<BatchInfo, StringInstruction, bool> filterBulk)
|
||||
{
|
||||
Property = property;
|
||||
FilterPKM = filterPkm;
|
||||
FilterBulk = filterBulk;
|
||||
}
|
||||
|
||||
public bool IsMatch(string prop) => prop == Property;
|
||||
public bool IsFiltered(PKM pkm, StringInstruction cmd) => FilterPKM(pkm, cmd);
|
||||
public bool IsFiltered(BatchInfo info, StringInstruction cmd) => FilterBulk(info, cmd);
|
||||
Property = property;
|
||||
FilterPKM = filterPkm;
|
||||
FilterBulk = filterBulk;
|
||||
}
|
||||
|
||||
public bool IsMatch(string prop) => prop == Property;
|
||||
public bool IsFiltered(PKM pk, StringInstruction value) => FilterPKM(pk, value);
|
||||
public bool IsFiltered(BatchInfo info, StringInstruction value) => FilterBulk(info, value);
|
||||
}
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <inheritdoc cref="IComplexSet"/>
|
||||
public sealed class ComplexSet : IComplexSet
|
||||
{
|
||||
/// <inheritdoc cref="IComplexSet"/>
|
||||
public sealed class ComplexSet : IComplexSet
|
||||
public readonly string PropertyName;
|
||||
public readonly Func<string, bool> IsValueCompatible = _ => true;
|
||||
private readonly Action<PKM, StringInstruction> Action;
|
||||
|
||||
public ComplexSet(string propertyName, Action<PKM, StringInstruction> modify)
|
||||
{
|
||||
public readonly string PropertyName;
|
||||
public readonly Func<string, bool> IsValueCompatible = _ => true;
|
||||
private readonly Action<PKM, StringInstruction> Action;
|
||||
|
||||
public ComplexSet(string propertyName, Action<PKM, StringInstruction> modify)
|
||||
{
|
||||
PropertyName = propertyName;
|
||||
Action = modify;
|
||||
}
|
||||
|
||||
public ComplexSet(string propertyName, Func<string, bool> criteria, Action<PKM, StringInstruction> modify) : this(propertyName, modify) => IsValueCompatible = criteria;
|
||||
|
||||
public bool IsMatch(string name, string value) => name == PropertyName && IsValueCompatible(value);
|
||||
|
||||
public void Modify(PKM pk, StringInstruction instr) => Action(pk, instr);
|
||||
PropertyName = propertyName;
|
||||
Action = modify;
|
||||
}
|
||||
|
||||
public ComplexSet(string propertyName, Func<string, bool> criteria, Action<PKM, StringInstruction> modify) : this(propertyName, modify) => IsValueCompatible = criteria;
|
||||
|
||||
public bool IsMatch(string name, string value) => name == PropertyName && IsValueCompatible(value);
|
||||
|
||||
public void Modify(PKM pk, StringInstruction instr) => Action(pk, instr);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Complex filter of data based on a string value.
|
||||
/// </summary>
|
||||
public interface IComplexFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Complex filter of data based on a string value.
|
||||
/// </summary>
|
||||
public interface IComplexFilter
|
||||
{
|
||||
bool IsMatch(string prop);
|
||||
bool IsFiltered(PKM pkm, StringInstruction value);
|
||||
bool IsFiltered(BatchInfo info, StringInstruction value);
|
||||
}
|
||||
bool IsMatch(string prop);
|
||||
bool IsFiltered(PKM pk, StringInstruction value);
|
||||
bool IsFiltered(BatchInfo info, StringInstruction value);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Complex filter of data based on a string value.
|
||||
/// </summary>
|
||||
public interface IComplexFilterMeta
|
||||
{
|
||||
/// <summary>
|
||||
/// Complex filter of data based on a string value.
|
||||
/// </summary>
|
||||
public interface IComplexFilterMeta
|
||||
{
|
||||
bool IsMatch(string prop);
|
||||
bool IsFiltered(object cache, StringInstruction value);
|
||||
}
|
||||
bool IsMatch(string prop);
|
||||
bool IsFiltered(object cache, StringInstruction value);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Complex modification of data to a string value.
|
||||
/// </summary>
|
||||
public interface IComplexSet
|
||||
{
|
||||
/// <summary>
|
||||
/// Complex modification of data to a string value.
|
||||
/// </summary>
|
||||
public interface IComplexSet
|
||||
{
|
||||
bool IsMatch(string name, string value);
|
||||
void Modify(PKM pk, StringInstruction instr);
|
||||
}
|
||||
bool IsMatch(string name, string value);
|
||||
void Modify(PKM pk, StringInstruction instr);
|
||||
}
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <inheritdoc cref="IComplexFilter"/>
|
||||
public sealed class MetaFilter : IComplexFilterMeta
|
||||
{
|
||||
/// <inheritdoc cref="IComplexFilter"/>
|
||||
public sealed class MetaFilter : IComplexFilterMeta
|
||||
private readonly string Property;
|
||||
private readonly Func<object, StringInstruction, bool> FilterPKM;
|
||||
|
||||
public MetaFilter(
|
||||
string property,
|
||||
Func<object, StringInstruction, bool> filterPkm)
|
||||
{
|
||||
private readonly string Property;
|
||||
private readonly Func<object, StringInstruction, bool> FilterPKM;
|
||||
|
||||
public MetaFilter(
|
||||
string property,
|
||||
Func<object, StringInstruction, bool> filterPkm)
|
||||
{
|
||||
Property = property;
|
||||
FilterPKM = filterPkm;
|
||||
}
|
||||
|
||||
public bool IsMatch(string prop) => prop == Property;
|
||||
public bool IsFiltered(object pkm, StringInstruction cmd) => FilterPKM(pkm, cmd);
|
||||
Property = property;
|
||||
FilterPKM = filterPkm;
|
||||
}
|
||||
|
||||
public bool IsMatch(string prop) => prop == Property;
|
||||
public bool IsFiltered(object pk, StringInstruction value) => FilterPKM(pk, value);
|
||||
}
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Batch Editor Modification result for an individual <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
public enum ModifyResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Batch Editor Modification result for an individual <see cref="PKM"/>.
|
||||
/// The <see cref="PKM"/> has invalid data and is not a suitable candidate for modification.
|
||||
/// </summary>
|
||||
public enum ModifyResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="PKM"/> has invalid data and is not a suitable candidate for modification.
|
||||
/// </summary>
|
||||
Invalid,
|
||||
Invalid,
|
||||
|
||||
/// <summary>
|
||||
/// An error was occurred while iterating modifications for this <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
Error,
|
||||
/// <summary>
|
||||
/// An error was occurred while iterating modifications for this <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="PKM"/> was skipped due to a matching Filter.
|
||||
/// </summary>
|
||||
Filtered,
|
||||
/// <summary>
|
||||
/// The <see cref="PKM"/> was skipped due to a matching Filter.
|
||||
/// </summary>
|
||||
Filtered,
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="PKM"/> was modified.
|
||||
/// </summary>
|
||||
Modified,
|
||||
}
|
||||
/// <summary>
|
||||
/// The <see cref="PKM"/> was modified.
|
||||
/// </summary>
|
||||
Modified,
|
||||
}
|
||||
|
|
|
@ -2,123 +2,122 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Batch Editing instruction
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be a filter (skip), or a modification instruction (modify)
|
||||
/// </remarks>
|
||||
/// <see cref="Exclude"/>
|
||||
/// <see cref="Require"/>
|
||||
/// <see cref="Apply"/>
|
||||
public sealed class StringInstruction
|
||||
{
|
||||
public string PropertyName { get; }
|
||||
public string PropertyValue { get; private set; }
|
||||
|
||||
/// <summary> True if ==, false if != </summary>
|
||||
public bool Evaluator { get; private init; }
|
||||
|
||||
public StringInstruction(string name, string value)
|
||||
{
|
||||
PropertyName = name;
|
||||
PropertyValue = value;
|
||||
}
|
||||
|
||||
public void SetScreenedValue(string[] arr)
|
||||
{
|
||||
int index = Array.IndexOf(arr, PropertyValue);
|
||||
PropertyValue = index > -1 ? index.ToString() : PropertyValue;
|
||||
}
|
||||
|
||||
public static readonly IReadOnlyList<char> Prefixes = new[] { Apply, Require, Exclude };
|
||||
private const char Exclude = '!';
|
||||
private const char Require = '=';
|
||||
private const char Apply = '.';
|
||||
private const char SplitRange = ',';
|
||||
|
||||
/// <summary>
|
||||
/// Batch Editing instruction
|
||||
/// Character which divides a property and a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be a filter (skip), or a modification instruction (modify)
|
||||
/// Example:
|
||||
/// =Species=1
|
||||
/// The second = is the split.
|
||||
/// </remarks>
|
||||
/// <see cref="Exclude"/>
|
||||
/// <see cref="Require"/>
|
||||
/// <see cref="Apply"/>
|
||||
public sealed class StringInstruction
|
||||
public const char SplitInstruction = '=';
|
||||
|
||||
// Extra Functionality
|
||||
private int RandomMinimum, RandomMaximum;
|
||||
public bool Random { get; private set; }
|
||||
public int RandomValue => Util.Rand.Next(RandomMinimum, RandomMaximum + 1);
|
||||
|
||||
public void SetRandRange(string pv)
|
||||
{
|
||||
public string PropertyName { get; }
|
||||
public string PropertyValue { get; private set; }
|
||||
string str = pv[1..];
|
||||
var split = str.Split(SplitRange);
|
||||
int.TryParse(split[0], out RandomMinimum);
|
||||
int.TryParse(split[1], out RandomMaximum);
|
||||
|
||||
/// <summary> True if ==, false if != </summary>
|
||||
public bool Evaluator { get; private init; }
|
||||
|
||||
public StringInstruction(string name, string value)
|
||||
if (RandomMinimum == RandomMaximum)
|
||||
{
|
||||
PropertyName = name;
|
||||
PropertyValue = value;
|
||||
PropertyValue = RandomMinimum.ToString();
|
||||
Debug.WriteLine($"{PropertyName} randomization range Min/Max same?");
|
||||
}
|
||||
|
||||
public void SetScreenedValue(string[] arr)
|
||||
else
|
||||
{
|
||||
int index = Array.IndexOf(arr, PropertyValue);
|
||||
PropertyValue = index > -1 ? index.ToString() : PropertyValue;
|
||||
Random = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly IReadOnlyList<char> Prefixes = new[] { Apply, Require, Exclude };
|
||||
private const char Exclude = '!';
|
||||
private const char Require = '=';
|
||||
private const char Apply = '.';
|
||||
private const char SplitRange = ',';
|
||||
|
||||
/// <summary>
|
||||
/// Character which divides a property and a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example:
|
||||
/// =Species=1
|
||||
/// The second = is the split.
|
||||
/// </remarks>
|
||||
public const char SplitInstruction = '=';
|
||||
|
||||
// Extra Functionality
|
||||
private int RandomMinimum, RandomMaximum;
|
||||
public bool Random { get; private set; }
|
||||
public int RandomValue => Util.Rand.Next(RandomMinimum, RandomMaximum + 1);
|
||||
|
||||
public void SetRandRange(string pv)
|
||||
public static IEnumerable<StringInstruction> GetFilters(IEnumerable<string> lines)
|
||||
{
|
||||
foreach (var line in lines)
|
||||
{
|
||||
string str = pv[1..];
|
||||
var split = str.Split(SplitRange);
|
||||
int.TryParse(split[0], out RandomMinimum);
|
||||
int.TryParse(split[1], out RandomMaximum);
|
||||
if (line.Length is 0 || line[0] is not (Exclude or Require))
|
||||
continue;
|
||||
|
||||
if (RandomMinimum == RandomMaximum)
|
||||
{
|
||||
PropertyValue = RandomMinimum.ToString();
|
||||
Debug.WriteLine($"{PropertyName} randomization range Min/Max same?");
|
||||
}
|
||||
else
|
||||
{
|
||||
Random = true;
|
||||
}
|
||||
const int start = 1;
|
||||
var splitIndex = line.IndexOf(SplitInstruction, start);
|
||||
if (splitIndex == -1)
|
||||
continue;
|
||||
var noExtra = line.IndexOf(SplitInstruction, splitIndex + 1);
|
||||
if (noExtra != -1)
|
||||
continue;
|
||||
|
||||
var name = line.AsSpan(start, splitIndex - start);
|
||||
if (name.IsWhiteSpace())
|
||||
continue;
|
||||
|
||||
bool eval = line[0] == Require;
|
||||
var value = line[(splitIndex + 1)..];
|
||||
yield return new StringInstruction(name.ToString(), value) { Evaluator = eval };
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<StringInstruction> GetFilters(IEnumerable<string> lines)
|
||||
public static IEnumerable<StringInstruction> GetInstructions(IEnumerable<string> lines)
|
||||
{
|
||||
foreach (var line in lines)
|
||||
{
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.Length is 0 || line[0] is not (Exclude or Require))
|
||||
continue;
|
||||
if (line.Length is 0 || line[0] is not Apply)
|
||||
continue;
|
||||
|
||||
const int start = 1;
|
||||
var splitIndex = line.IndexOf(SplitInstruction, start);
|
||||
if (splitIndex == -1)
|
||||
continue;
|
||||
var noExtra = line.IndexOf(SplitInstruction, splitIndex + 1);
|
||||
if (noExtra != -1)
|
||||
continue;
|
||||
const int start = 1;
|
||||
var splitIndex = line.IndexOf(SplitInstruction, start);
|
||||
if (splitIndex == -1)
|
||||
continue;
|
||||
var noExtra = line.IndexOf(SplitInstruction, splitIndex + 1);
|
||||
if (noExtra != -1)
|
||||
continue;
|
||||
|
||||
var name = line.AsSpan(start, splitIndex - start);
|
||||
if (name.IsWhiteSpace())
|
||||
continue;
|
||||
var name = line.AsSpan(start, splitIndex - start);
|
||||
if (name.IsWhiteSpace())
|
||||
continue;
|
||||
|
||||
bool eval = line[0] == Require;
|
||||
var value = line[(splitIndex + 1)..];
|
||||
yield return new StringInstruction(name.ToString(), value) { Evaluator = eval };
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<StringInstruction> GetInstructions(IEnumerable<string> lines)
|
||||
{
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.Length is 0 || line[0] is not Apply)
|
||||
continue;
|
||||
|
||||
const int start = 1;
|
||||
var splitIndex = line.IndexOf(SplitInstruction, start);
|
||||
if (splitIndex == -1)
|
||||
continue;
|
||||
var noExtra = line.IndexOf(SplitInstruction, splitIndex + 1);
|
||||
if (noExtra != -1)
|
||||
continue;
|
||||
|
||||
var name = line.AsSpan(start, splitIndex - start);
|
||||
if (name.IsWhiteSpace())
|
||||
continue;
|
||||
|
||||
var value = line[(splitIndex + 1)..];
|
||||
yield return new StringInstruction(name.ToString(), value);
|
||||
}
|
||||
var value = line[(splitIndex + 1)..];
|
||||
yield return new StringInstruction(name.ToString(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Processes input of strings into a list of valid Filters and Instructions.
|
||||
/// </summary>
|
||||
public sealed class StringInstructionSet
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes input of strings into a list of valid Filters and Instructions.
|
||||
/// </summary>
|
||||
public sealed class StringInstructionSet
|
||||
public readonly IReadOnlyList<StringInstruction> Filters;
|
||||
public readonly IReadOnlyList<StringInstruction> Instructions;
|
||||
|
||||
private const string SetSeparator = ";";
|
||||
|
||||
public StringInstructionSet(IReadOnlyList<StringInstruction> filters, IReadOnlyList<StringInstruction> instructions)
|
||||
{
|
||||
public readonly IReadOnlyList<StringInstruction> Filters;
|
||||
public readonly IReadOnlyList<StringInstruction> Instructions;
|
||||
Filters = filters;
|
||||
Instructions = instructions;
|
||||
}
|
||||
|
||||
private const string SetSeparator = ";";
|
||||
public StringInstructionSet(ICollection<string> set)
|
||||
{
|
||||
Filters = StringInstruction.GetFilters(set).ToList();
|
||||
Instructions = StringInstruction.GetInstructions(set).ToList();
|
||||
}
|
||||
|
||||
public StringInstructionSet(IReadOnlyList<StringInstruction> filters, IReadOnlyList<StringInstruction> instructions)
|
||||
public static IEnumerable<StringInstructionSet> GetBatchSets(IList<string> lines)
|
||||
{
|
||||
int start = 0;
|
||||
while (start < lines.Count)
|
||||
{
|
||||
Filters = filters;
|
||||
Instructions = instructions;
|
||||
}
|
||||
|
||||
public StringInstructionSet(ICollection<string> set)
|
||||
{
|
||||
Filters = StringInstruction.GetFilters(set).ToList();
|
||||
Instructions = StringInstruction.GetInstructions(set).ToList();
|
||||
}
|
||||
|
||||
public static IEnumerable<StringInstructionSet> GetBatchSets(IList<string> lines)
|
||||
{
|
||||
int start = 0;
|
||||
while (start < lines.Count)
|
||||
{
|
||||
var list = lines.Skip(start).TakeWhile(_ => !lines[start++].StartsWith(SetSeparator)).ToList();
|
||||
yield return new StringInstructionSet(list);
|
||||
}
|
||||
var list = lines.Skip(start).TakeWhile(_ => !lines[start++].StartsWith(SetSeparator, StringComparison.Ordinal)).ToList();
|
||||
yield return new StringInstructionSet(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,119 +1,117 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Modifications using <see cref="BatchInfo"/> legality.
|
||||
/// </summary>
|
||||
internal static class BatchModifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Modifications using <see cref="BatchInfo"/> legality.
|
||||
/// </summary>
|
||||
internal static class BatchModifications
|
||||
private static bool IsAll(string p) => p.EndsWith("All", StringComparison.OrdinalIgnoreCase);
|
||||
private static bool IsNone(string p) => p.EndsWith("None", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public static ModifyResult SetSuggestedRelearnData(BatchInfo info, string propValue)
|
||||
{
|
||||
private static bool IsAll(string p) => p.EndsWith("All", true, CultureInfo.CurrentCulture);
|
||||
private static bool IsNone(string p) => p.EndsWith("None", true, CultureInfo.CurrentCulture);
|
||||
|
||||
public static ModifyResult SetSuggestedRelearnData(BatchInfo info, string propValue)
|
||||
var pk = info.Entity;
|
||||
if (pk is ITechRecord8 t)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
if (pk is ITechRecord8 t)
|
||||
{
|
||||
t.ClearRecordFlags();
|
||||
if (IsAll(propValue))
|
||||
t.SetRecordFlags(); // all
|
||||
else if (!IsNone(propValue))
|
||||
t.SetRecordFlags(pk.Moves); // whatever fit the current moves
|
||||
}
|
||||
|
||||
pk.SetRelearnMoves(info.SuggestedRelearn);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
|
||||
public static ModifyResult SetSuggestedMasteryData(BatchInfo info, string propValue)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
if (pk is not IMoveShop8Mastery t)
|
||||
return ModifyResult.Invalid;
|
||||
|
||||
t.ClearMoveShopFlags();
|
||||
if (IsNone(propValue))
|
||||
return ModifyResult.Modified;
|
||||
|
||||
var e = info.Legality.EncounterMatch;
|
||||
if (e is IMasteryInitialMoveShop8 enc)
|
||||
enc.SetInitialMastery(pk);
|
||||
t.ClearRecordFlags();
|
||||
if (IsAll(propValue))
|
||||
t.SetMoveShopFlagsAll(pk);
|
||||
else
|
||||
t.SetMoveShopFlags(pk);
|
||||
t.SetRecordFlags(); // all
|
||||
else if (!IsNone(propValue))
|
||||
t.SetRecordFlags(pk.Moves); // whatever fit the current moves
|
||||
}
|
||||
|
||||
pk.SetRelearnMoves(info.SuggestedRelearn);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
|
||||
public static ModifyResult SetSuggestedMasteryData(BatchInfo info, string propValue)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
if (pk is not IMoveShop8Mastery t)
|
||||
return ModifyResult.Invalid;
|
||||
|
||||
t.ClearMoveShopFlags();
|
||||
if (IsNone(propValue))
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
|
||||
public static ModifyResult SetSuggestedRibbons(BatchInfo info, string value)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
if (IsNone(value))
|
||||
RibbonApplicator.RemoveAllValidRibbons(pk);
|
||||
else // All
|
||||
RibbonApplicator.SetAllValidRibbons(pk);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
var e = info.Legality.EncounterMatch;
|
||||
if (e is IMasteryInitialMoveShop8 enc)
|
||||
enc.SetInitialMastery(pk);
|
||||
if (IsAll(propValue))
|
||||
t.SetMoveShopFlagsAll(pk);
|
||||
else
|
||||
t.SetMoveShopFlags(pk);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
|
||||
public static ModifyResult SetSuggestedMetData(BatchInfo info)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
var encounter = EncounterSuggestion.GetSuggestedMetInfo(pk);
|
||||
if (encounter == null)
|
||||
return ModifyResult.Error;
|
||||
public static ModifyResult SetSuggestedRibbons(BatchInfo info, string value)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
if (IsNone(value))
|
||||
RibbonApplicator.RemoveAllValidRibbons(pk);
|
||||
else // All
|
||||
RibbonApplicator.SetAllValidRibbons(pk);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
|
||||
int level = encounter.LevelMin;
|
||||
int location = encounter.Location;
|
||||
int minimumLevel = EncounterSuggestion.GetLowestLevel(pk, encounter.LevelMin);
|
||||
public static ModifyResult SetSuggestedMetData(BatchInfo info)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
var encounter = EncounterSuggestion.GetSuggestedMetInfo(pk);
|
||||
if (encounter == null)
|
||||
return ModifyResult.Error;
|
||||
|
||||
pk.Met_Level = level;
|
||||
pk.Met_Location = location;
|
||||
pk.CurrentLevel = Math.Max(minimumLevel, level);
|
||||
int level = encounter.LevelMin;
|
||||
int location = encounter.Location;
|
||||
int minimumLevel = EncounterSuggestion.GetLowestLevel(pk, encounter.LevelMin);
|
||||
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
pk.Met_Level = level;
|
||||
pk.Met_Location = location;
|
||||
pk.CurrentLevel = Math.Max(minimumLevel, level);
|
||||
|
||||
public static ModifyResult SetMinimumCurrentLevel(BatchInfo info)
|
||||
{
|
||||
var result = EncounterSuggestion.IterateMinimumCurrentLevel(info.Entity, info.Legal);
|
||||
return result ? ModifyResult.Modified : ModifyResult.Filtered;
|
||||
}
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the provided moves in a random order.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="moves">Moves to apply.</param>
|
||||
public static ModifyResult SetMoves(PKM pk, ReadOnlySpan<int> moves)
|
||||
{
|
||||
pk.SetMoves(moves);
|
||||
pk.HealPP();
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
public static ModifyResult SetMinimumCurrentLevel(BatchInfo info)
|
||||
{
|
||||
var result = EncounterSuggestion.IterateMinimumCurrentLevel(info.Entity, info.Legal);
|
||||
return result ? ModifyResult.Modified : ModifyResult.Filtered;
|
||||
}
|
||||
|
||||
public static ModifyResult SetEVs(PKM pk)
|
||||
{
|
||||
Span<int> evs = stackalloc int[6];
|
||||
EffortValues.SetMax(evs, pk);
|
||||
pk.SetEVs(evs);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the provided moves in a random order.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="moves">Moves to apply.</param>
|
||||
public static ModifyResult SetMoves(PKM pk, ReadOnlySpan<int> moves)
|
||||
{
|
||||
pk.SetMoves(moves);
|
||||
pk.HealPP();
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the contests stats as requested.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="la">Legality Information matched to.</param>
|
||||
/// <param name="option">Option to apply with</param>
|
||||
public static ModifyResult SetContestStats(PKM pk, LegalityAnalysis la, string option)
|
||||
{
|
||||
if (option.Length != 0 && option[BatchEditing.CONST_SUGGEST.Length..] is not "0")
|
||||
pk.SetMaxContestStats(la.EncounterMatch, la.Info.EvoChainsAllGens);
|
||||
else
|
||||
pk.SetSuggestedContestStats(la.EncounterMatch, la.Info.EvoChainsAllGens);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
public static ModifyResult SetEVs(PKM pk)
|
||||
{
|
||||
Span<int> evs = stackalloc int[6];
|
||||
EffortValues.SetMax(evs, pk);
|
||||
pk.SetEVs(evs);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the contests stats as requested.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="la">Legality Information matched to.</param>
|
||||
/// <param name="option">Option to apply with</param>
|
||||
public static ModifyResult SetContestStats(PKM pk, LegalityAnalysis la, string option)
|
||||
{
|
||||
if (option.Length != 0 && option[BatchEditing.CONST_SUGGEST.Length..] is not "0")
|
||||
pk.SetMaxContestStats(la.EncounterMatch, la.Info.EvoChainsAllGens);
|
||||
else
|
||||
pk.SetSuggestedContestStats(la.EncounterMatch, la.Info.EvoChainsAllGens);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,37 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <inheritdoc cref="ISuggestModification"/>
|
||||
public sealed class ComplexSuggestion : ISuggestModification
|
||||
{
|
||||
/// <inheritdoc cref="ISuggestModification"/>
|
||||
public sealed class ComplexSuggestion : ISuggestModification
|
||||
public readonly string Keyword;
|
||||
public readonly Func<PKM, bool> Criteria = _ => true;
|
||||
public readonly Func<string, string, BatchInfo, ModifyResult> Action;
|
||||
|
||||
public ComplexSuggestion(
|
||||
string keyword,
|
||||
Func<PKM, bool> criteria,
|
||||
Func<string, string, BatchInfo, ModifyResult> action) : this(keyword, action)
|
||||
{
|
||||
public readonly string Keyword;
|
||||
public readonly Func<PKM, bool> Criteria = _ => true;
|
||||
public readonly Func<string, string, BatchInfo, ModifyResult> Action;
|
||||
Criteria = criteria;
|
||||
}
|
||||
|
||||
public ComplexSuggestion(
|
||||
string keyword,
|
||||
Func<PKM, bool> criteria,
|
||||
Func<string, string, BatchInfo, ModifyResult> action) : this(keyword, action)
|
||||
{
|
||||
Criteria = criteria;
|
||||
}
|
||||
public ComplexSuggestion(
|
||||
string keyword,
|
||||
Func<string, string, BatchInfo, ModifyResult> action)
|
||||
{
|
||||
Keyword = keyword;
|
||||
Action = action;
|
||||
}
|
||||
|
||||
public ComplexSuggestion(
|
||||
string keyword,
|
||||
Func<string, string, BatchInfo, ModifyResult> action)
|
||||
{
|
||||
Keyword = keyword;
|
||||
Action = action;
|
||||
}
|
||||
public bool IsMatch(string name, string value, BatchInfo info)
|
||||
{
|
||||
return name == Keyword && Criteria(info.Entity);
|
||||
}
|
||||
|
||||
public bool IsMatch(string name, string value, BatchInfo info)
|
||||
{
|
||||
return name == Keyword && Criteria(info.Entity);
|
||||
}
|
||||
|
||||
public ModifyResult Modify(string name, string value, BatchInfo info)
|
||||
{
|
||||
return Action(name, value, info);
|
||||
}
|
||||
public ModifyResult Modify(string name, string value, BatchInfo info)
|
||||
{
|
||||
return Action(name, value, info);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Modifies a property to have a "correct" value based on derived legality.
|
||||
/// </summary>
|
||||
public interface ISuggestModification
|
||||
{
|
||||
/// <summary>
|
||||
/// Modifies a property to have a "correct" value based on derived legality.
|
||||
/// </summary>
|
||||
public interface ISuggestModification
|
||||
{
|
||||
public bool IsMatch(string name, string value, BatchInfo info);
|
||||
public ModifyResult Modify(string name, string value, BatchInfo info);
|
||||
}
|
||||
public bool IsMatch(string name, string value, BatchInfo info);
|
||||
public ModifyResult Modify(string name, string value, BatchInfo info);
|
||||
}
|
||||
|
|
|
@ -1,40 +1,39 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <inheritdoc cref="ISuggestModification"/>
|
||||
/// <typeparam name="T">Specific (or not) type</typeparam>
|
||||
public sealed class TypeSuggestion<T> : ISuggestModification
|
||||
{
|
||||
/// <inheritdoc cref="ISuggestModification"/>
|
||||
/// <typeparam name="T">Specific (or not) type</typeparam>
|
||||
public sealed class TypeSuggestion<T> : ISuggestModification
|
||||
public readonly string Keyword;
|
||||
public readonly Action<T, string> Action;
|
||||
public readonly Func<T, bool> Criteria = _ => true;
|
||||
|
||||
public TypeSuggestion(string keyword, Action<T> action)
|
||||
{
|
||||
public readonly string Keyword;
|
||||
public readonly Action<T, string> Action;
|
||||
public readonly Func<T, bool> Criteria = _ => true;
|
||||
Keyword = keyword;
|
||||
Action = (pk, _) => action(pk);
|
||||
}
|
||||
|
||||
public TypeSuggestion(string keyword, Action<T> action)
|
||||
{
|
||||
Keyword = keyword;
|
||||
Action = (pkm, _) => action(pkm);
|
||||
}
|
||||
public TypeSuggestion(string keyword, Func<T, bool> criteria, Action<T> action) : this(keyword, action)
|
||||
{
|
||||
Criteria = criteria;
|
||||
}
|
||||
|
||||
public TypeSuggestion(string keyword, Func<T, bool> criteria, Action<T> action) : this(keyword, action)
|
||||
{
|
||||
Criteria = criteria;
|
||||
}
|
||||
public bool IsMatch(string name, string value, BatchInfo info)
|
||||
{
|
||||
return name == Keyword && info.Entity is T;
|
||||
}
|
||||
|
||||
public bool IsMatch(string name, string value, BatchInfo info)
|
||||
{
|
||||
return name == Keyword && info.Entity is T;
|
||||
}
|
||||
|
||||
public ModifyResult Modify(string name, string value, BatchInfo info)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
if (pk is not T x)
|
||||
return ModifyResult.Invalid;
|
||||
if (!Criteria(x))
|
||||
return ModifyResult.Invalid;
|
||||
Action(x, value);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
public ModifyResult Modify(string name, string value, BatchInfo info)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
if (pk is not T x)
|
||||
return ModifyResult.Invalid;
|
||||
if (!Criteria(x))
|
||||
return ModifyResult.Invalid;
|
||||
Action(x, value);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,446 +1,444 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains extension logic for modifying <see cref="PKM"/> data.
|
||||
/// </summary>
|
||||
public static class CommonEdits
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extension logic for modifying <see cref="PKM"/> data.
|
||||
/// Setting which enables/disables automatic manipulation of <see cref="PKM.MarkValue"/> when importing from a <see cref="IBattleTemplate"/>.
|
||||
/// </summary>
|
||||
public static class CommonEdits
|
||||
public static bool ShowdownSetIVMarkings { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Setting which causes the <see cref="PKM.StatNature"/> to the <see cref="PKM.Nature"/> in Gen8+ formats.
|
||||
/// </summary>
|
||||
public static bool ShowdownSetBehaviorNature { get; set; }
|
||||
|
||||
/// <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>
|
||||
public static void SetNickname(this PKM pk, string nick)
|
||||
{
|
||||
/// <summary>
|
||||
/// Setting which enables/disables automatic manipulation of <see cref="PKM.MarkValue"/> when importing from a <see cref="IBattleTemplate"/>.
|
||||
/// </summary>
|
||||
public static bool ShowdownSetIVMarkings { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Setting which causes the <see cref="PKM.StatNature"/> to the <see cref="PKM.Nature"/> in Gen8+ formats.
|
||||
/// </summary>
|
||||
public static bool ShowdownSetBehaviorNature { get; set; }
|
||||
|
||||
/// <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>
|
||||
public static void SetNickname(this PKM pk, string nick)
|
||||
if (string.IsNullOrWhiteSpace(nick))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(nick))
|
||||
{
|
||||
pk.ClearNickname();
|
||||
return;
|
||||
}
|
||||
pk.IsNicknamed = true;
|
||||
pk.Nickname = nick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the <see cref="PKM.Nickname"/> to the default value.
|
||||
/// </summary>
|
||||
/// <param name="pk"></param>
|
||||
public static string ClearNickname(this PKM pk)
|
||||
{
|
||||
pk.IsNicknamed = false;
|
||||
string nick = SpeciesName.GetSpeciesNameGeneration(pk.Species, pk.Language, pk.Format);
|
||||
pk.Nickname = nick;
|
||||
if (pk is GBPKM pk12)
|
||||
pk12.SetNotNicknamed();
|
||||
return nick;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (abil < 0)
|
||||
return;
|
||||
var index = pk.PersonalInfo.GetAbilityIndex(abil);
|
||||
index = Math.Max(0, index);
|
||||
pk.SetAbilityIndex(index);
|
||||
}
|
||||
|
||||
/// <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="index">Desired <see cref="PKM.AbilityNumber"/> (shifted by 1) to set.</param>
|
||||
public static void SetAbilityIndex(this PKM pk, int index)
|
||||
{
|
||||
if (pk is PK5 pk5 && index == 2)
|
||||
pk5.HiddenAbility = true;
|
||||
else if (pk.Format <= 5)
|
||||
pk.PID = EntityPID.GetRandomPID(Util.Rand, pk.Species, pk.Gender, pk.Version, pk.Nature, pk.Form, (uint)(index * 0x10001));
|
||||
pk.RefreshAbility(index);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <remarks>Accounts for Wurmple evolutions.</remarks>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void SetRandomEC(this PKM pk)
|
||||
{
|
||||
int gen = pk.Generation;
|
||||
if (gen is 3 or 4 or 5)
|
||||
{
|
||||
pk.EncryptionConstant = pk.PID;
|
||||
return;
|
||||
}
|
||||
|
||||
int wIndex = WurmpleUtil.GetWurmpleEvoGroup(pk.Species);
|
||||
if (wIndex != -1)
|
||||
{
|
||||
pk.EncryptionConstant = WurmpleUtil.GetWurmpleEncryptionConstant(wIndex);
|
||||
return;
|
||||
}
|
||||
pk.EncryptionConstant = Util.Rand32();
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static bool SetIsShiny(this PKM pk, bool shiny) => shiny ? SetShiny(pk) : pk.SetUnshiny();
|
||||
|
||||
/// <summary>
|
||||
/// Makes a <see cref="PKM"/> shiny.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="type">Shiny type to force. Only use Always* or Random</param>
|
||||
/// <returns>Returns true if the <see cref="PKM"/> data was modified.</returns>
|
||||
public static bool SetShiny(PKM pk, Shiny type = Shiny.Random)
|
||||
{
|
||||
if (pk.IsShiny && type.IsValid(pk))
|
||||
return false;
|
||||
|
||||
if (type == Shiny.Random || pk.FatefulEncounter || pk.Version == (int)GameVersion.GO || pk.Format <= 2)
|
||||
{
|
||||
pk.SetShiny();
|
||||
return true;
|
||||
}
|
||||
|
||||
do { pk.SetShiny(); }
|
||||
while (!type.IsValid(pk));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a <see cref="PKM"/> not-shiny.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <returns>Returns true if the <see cref="PKM"/> data was modified.</returns>
|
||||
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)
|
||||
{
|
||||
var value = Math.Min((int)Nature.Quirky, Math.Max((int)Nature.Hardy, nature));
|
||||
var format = pk.Format;
|
||||
if (format >= 8)
|
||||
pk.StatNature = value;
|
||||
else if (format is 3 or 4)
|
||||
pk.SetPIDNature(value);
|
||||
else
|
||||
pk.Nature = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies <see cref="IBattleTemplate"/> details to the <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="Set"><see cref="IBattleTemplate"/> details to copy from.</param>
|
||||
public static void ApplySetDetails(this PKM pk, IBattleTemplate Set)
|
||||
{
|
||||
pk.Species = Math.Min(pk.MaxSpeciesID, Set.Species);
|
||||
pk.Form = Set.Form;
|
||||
pk.SetMoves(Set.Moves, true);
|
||||
pk.ApplyHeldItem(Set.HeldItem, Set.Format);
|
||||
pk.CurrentLevel = Set.Level;
|
||||
pk.CurrentFriendship = Set.Friendship;
|
||||
pk.SetIVs(Set.IVs);
|
||||
|
||||
if (pk is GBPKM gb)
|
||||
{
|
||||
// In Generation 1/2 Format sets, when IVs are not specified with a Hidden Power set, we might not have the hidden power type.
|
||||
// Under this scenario, just force the Hidden Power type.
|
||||
if (Set.Moves.Contains(237) && pk.HPType != Set.HiddenPowerType && Set.IVs.Any(z => z >= 30))
|
||||
pk.SetHiddenPower(Set.HiddenPowerType);
|
||||
|
||||
// In Generation 1/2 Format sets, when EVs are not specified at all, it implies maximum EVs instead!
|
||||
// Under this scenario, just apply maximum EVs (65535).
|
||||
if (Set.EVs.All(z => z == 0))
|
||||
gb.MaxEVs();
|
||||
else
|
||||
pk.EVs = Set.EVs;
|
||||
}
|
||||
else
|
||||
{
|
||||
pk.EVs = Set.EVs;
|
||||
}
|
||||
|
||||
// IVs have no side effects such as hidden power type in gen 8
|
||||
// therefore all specified IVs are deliberate and should not be Hyper Trained for pokemon met in gen 8
|
||||
if (!pk.Gen8)
|
||||
pk.SetSuggestedHyperTrainingData(Set.IVs);
|
||||
|
||||
if (ShowdownSetIVMarkings)
|
||||
pk.SetMarkings();
|
||||
|
||||
pk.SetNickname(Set.Nickname);
|
||||
pk.SetSaneGender(Set.Gender);
|
||||
|
||||
if (Legal.IsPPUpAvailable(pk))
|
||||
pk.SetMaximumPPUps(Set.Moves);
|
||||
|
||||
if (pk.Format >= 3)
|
||||
{
|
||||
pk.SetAbility(Set.Ability);
|
||||
pk.SetNature(Set.Nature);
|
||||
}
|
||||
|
||||
pk.SetIsShiny(Set.Shiny);
|
||||
pk.SetRandomEC();
|
||||
|
||||
if (pk is IAwakened a)
|
||||
{
|
||||
a.SetSuggestedAwakenedValues(pk);
|
||||
if (pk is PB7 b)
|
||||
{
|
||||
for (int i = 0; i < 6; i++)
|
||||
b.SetEV(i, 0);
|
||||
b.ResetCalculatedValues();
|
||||
}
|
||||
}
|
||||
if (pk is IGanbaru g)
|
||||
g.SetSuggestedGanbaruValues(pk);
|
||||
|
||||
if (pk is IGigantamax c)
|
||||
c.CanGigantamax = Set.CanGigantamax;
|
||||
if (pk is IDynamaxLevel d)
|
||||
d.DynamaxLevel = d.GetSuggestedDynamaxLevel(pk);
|
||||
|
||||
if (pk is ITechRecord8 t)
|
||||
{
|
||||
t.ClearRecordFlags();
|
||||
t.SetRecordFlags(Set.Moves);
|
||||
}
|
||||
if (pk is IMoveShop8Mastery s)
|
||||
s.SetMoveShopFlags(Set.Moves, pk);
|
||||
|
||||
if (ShowdownSetBehaviorNature && pk.Format >= 8)
|
||||
pk.Nature = pk.StatNature;
|
||||
|
||||
var legal = new LegalityAnalysis(pk);
|
||||
if (legal.Parsed && legal.Info.Relearn.Any(z => !z.Valid))
|
||||
pk.SetRelearnMoves(legal.GetSuggestedRelearnMoves());
|
||||
pk.ResetPartyStats();
|
||||
pk.RefreshChecksum();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM.HeldItem"/> value depending on the current format and the provided item index & format.
|
||||
/// </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)
|
||||
{
|
||||
item = ItemConverter.GetItemForFormat(item, format, pk.Format);
|
||||
pk.HeldItem = ((uint)item > pk.MaxItemID) ? 0 : item;
|
||||
}
|
||||
|
||||
/// <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 int SetEV(this PKM pk, int index, int value) => index switch
|
||||
{
|
||||
0 => pk.EV_HP = value,
|
||||
1 => pk.EV_ATK = value,
|
||||
2 => pk.EV_DEF = value,
|
||||
3 => pk.EV_SPE = value,
|
||||
4 => pk.EV_SPA = value,
|
||||
5 => pk.EV_SPD = value,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index)),
|
||||
};
|
||||
|
||||
/// <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 int SetIV(this PKM pk, int index, int value) => index switch
|
||||
{
|
||||
0 => pk.IV_HP = value,
|
||||
1 => pk.IV_ATK = value,
|
||||
2 => pk.IV_DEF = value,
|
||||
3 => pk.IV_SPE = value,
|
||||
4 => pk.IV_SPA = value,
|
||||
5 => pk.IV_SPD = value,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index)),
|
||||
};
|
||||
|
||||
/// <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 sum = pk.EVTotal - pk.GetEV(index);
|
||||
int remaining = 510 - sum;
|
||||
return Math.Min(Math.Max(remaining, 0), 252);
|
||||
}
|
||||
|
||||
/// <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 maximum.</param>
|
||||
/// <returns>Highest value the value can be.</returns>
|
||||
public static int GetMaximumIV(this PKM pk, int index, bool allow30 = false)
|
||||
{
|
||||
if (pk.GetIV(index) == pk.MaxIV && allow30)
|
||||
return pk.MaxIV - 1;
|
||||
return pk.MaxIV;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
|
||||
/// </summary>
|
||||
/// <param name="pk">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 pk, bool reHatch = false)
|
||||
{
|
||||
if (!pk.IsEgg && !reHatch)
|
||||
return;
|
||||
pk.IsEgg = false;
|
||||
pk.ClearNickname();
|
||||
pk.CurrentFriendship = pk.PersonalInfo.BaseFriendship;
|
||||
if (pk.IsTradedEgg)
|
||||
pk.Egg_Location = pk.Met_Location;
|
||||
var loc = EncounterSuggestion.GetSuggestedEggMetLocation(pk);
|
||||
if (loc >= 0)
|
||||
pk.Met_Location = loc;
|
||||
pk.MetDate = DateTime.Today;
|
||||
if (pk.Gen6)
|
||||
pk.SetHatchMemory6();
|
||||
return;
|
||||
}
|
||||
pk.IsNicknamed = true;
|
||||
pk.Nickname = nick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the <see cref="PKM.Nickname"/> to the default value.
|
||||
/// </summary>
|
||||
/// <param name="pk"></param>
|
||||
public static string ClearNickname(this PKM pk)
|
||||
{
|
||||
pk.IsNicknamed = false;
|
||||
string nick = SpeciesName.GetSpeciesNameGeneration(pk.Species, pk.Language, pk.Format);
|
||||
pk.Nickname = nick;
|
||||
if (pk is GBPKM pk12)
|
||||
pk12.SetNotNicknamed();
|
||||
return nick;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (abil < 0)
|
||||
return;
|
||||
var index = pk.PersonalInfo.GetAbilityIndex(abil);
|
||||
index = Math.Max(0, index);
|
||||
pk.SetAbilityIndex(index);
|
||||
}
|
||||
|
||||
/// <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="index">Desired <see cref="PKM.AbilityNumber"/> (shifted by 1) to set.</param>
|
||||
public static void SetAbilityIndex(this PKM pk, int index)
|
||||
{
|
||||
if (pk is PK5 pk5 && index == 2)
|
||||
pk5.HiddenAbility = true;
|
||||
else if (pk.Format <= 5)
|
||||
pk.PID = EntityPID.GetRandomPID(Util.Rand, pk.Species, pk.Gender, pk.Version, pk.Nature, pk.Form, (uint)(index * 0x10001));
|
||||
pk.RefreshAbility(index);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <remarks>Accounts for Wurmple evolutions.</remarks>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
public static void SetRandomEC(this PKM pk)
|
||||
{
|
||||
int gen = pk.Generation;
|
||||
if (gen is 3 or 4 or 5)
|
||||
{
|
||||
pk.EncryptionConstant = pk.PID;
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
|
||||
/// </summary>
|
||||
/// <param name="pk">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 pk, GameVersion origin, GameVersion dest)
|
||||
int wIndex = WurmpleUtil.GetWurmpleEvoGroup(pk.Species);
|
||||
if (wIndex != -1)
|
||||
{
|
||||
bool traded = origin != dest;
|
||||
var today = pk.MetDate = DateTime.Today;
|
||||
pk.Egg_Location = EncounterSuggestion.GetSuggestedEncounterEggLocationEgg(pk.Generation, origin, traded);
|
||||
pk.EggMetDate = today;
|
||||
pk.EncryptionConstant = WurmpleUtil.GetWurmpleEncryptionConstant(wIndex);
|
||||
return;
|
||||
}
|
||||
pk.EncryptionConstant = Util.Rand32();
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static bool SetIsShiny(this PKM pk, bool shiny) => shiny ? SetShiny(pk) : pk.SetUnshiny();
|
||||
|
||||
/// <summary>
|
||||
/// Makes a <see cref="PKM"/> shiny.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="type">Shiny type to force. Only use Always* or Random</param>
|
||||
/// <returns>Returns true if the <see cref="PKM"/> data was modified.</returns>
|
||||
public static bool SetShiny(PKM pk, Shiny type = Shiny.Random)
|
||||
{
|
||||
if (pk.IsShiny && type.IsValid(pk))
|
||||
return false;
|
||||
|
||||
if (type == Shiny.Random || pk.FatefulEncounter || pk.Version == (int)GameVersion.GO || pk.Format <= 2)
|
||||
{
|
||||
pk.SetShiny();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximizes the <see cref="PKM.CurrentFriendship"/>. If the <see cref="PKM.IsEgg"/>, the hatch counter is set to 1.
|
||||
/// </summary>
|
||||
/// <param name="pk">PKM to apply hatch details to</param>
|
||||
public static void MaximizeFriendship(this PKM pk)
|
||||
do { pk.SetShiny(); }
|
||||
while (!type.IsValid(pk));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a <see cref="PKM"/> not-shiny.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <returns>Returns true if the <see cref="PKM"/> data was modified.</returns>
|
||||
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)
|
||||
{
|
||||
var value = Math.Min((int)Nature.Quirky, Math.Max((int)Nature.Hardy, nature));
|
||||
var format = pk.Format;
|
||||
if (format >= 8)
|
||||
pk.StatNature = value;
|
||||
else if (format is 3 or 4)
|
||||
pk.SetPIDNature(value);
|
||||
else
|
||||
pk.Nature = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies <see cref="IBattleTemplate"/> details to the <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="Set"><see cref="IBattleTemplate"/> details to copy from.</param>
|
||||
public static void ApplySetDetails(this PKM pk, IBattleTemplate Set)
|
||||
{
|
||||
pk.Species = Math.Min(pk.MaxSpeciesID, Set.Species);
|
||||
pk.Form = Set.Form;
|
||||
pk.SetMoves(Set.Moves, true);
|
||||
pk.ApplyHeldItem(Set.HeldItem, Set.Format);
|
||||
pk.CurrentLevel = Set.Level;
|
||||
pk.CurrentFriendship = Set.Friendship;
|
||||
pk.SetIVs(Set.IVs);
|
||||
|
||||
if (pk is GBPKM gb)
|
||||
{
|
||||
if (pk.IsEgg)
|
||||
pk.OT_Friendship = 1;
|
||||
// In Generation 1/2 Format sets, when IVs are not specified with a Hidden Power set, we might not have the hidden power type.
|
||||
// Under this scenario, just force the Hidden Power type.
|
||||
if (Set.Moves.Contains(237) && pk.HPType != Set.HiddenPowerType && Set.IVs.Any(z => z >= 30))
|
||||
pk.SetHiddenPower(Set.HiddenPowerType);
|
||||
|
||||
// In Generation 1/2 Format sets, when EVs are not specified at all, it implies maximum EVs instead!
|
||||
// Under this scenario, just apply maximum EVs (65535).
|
||||
if (Set.EVs.All(z => z == 0))
|
||||
gb.MaxEVs();
|
||||
else
|
||||
pk.CurrentFriendship = byte.MaxValue;
|
||||
if (pk is ICombatPower pb)
|
||||
pb.ResetCP();
|
||||
pk.EVs = Set.EVs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximizes the <see cref="PKM.CurrentLevel"/>. If the <see cref="PKM.IsEgg"/>, the <see cref="PKM"/> is ignored.
|
||||
/// </summary>
|
||||
/// <param name="pk">PKM to apply hatch details to</param>
|
||||
public static void MaximizeLevel(this PKM pk)
|
||||
else
|
||||
{
|
||||
if (pk.IsEgg)
|
||||
return;
|
||||
pk.CurrentLevel = 100;
|
||||
if (pk is ICombatPower pb)
|
||||
pb.ResetCP();
|
||||
pk.EVs = Set.EVs;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static void SetDefaultNickname(this PKM pk, LegalityAnalysis la)
|
||||
// IVs have no side effects such as hidden power type in gen 8
|
||||
// therefore all specified IVs are deliberate and should not be Hyper Trained for pokemon met in gen 8
|
||||
if (!pk.Gen8)
|
||||
pk.SetSuggestedHyperTrainingData(Set.IVs);
|
||||
|
||||
if (ShowdownSetIVMarkings)
|
||||
pk.SetMarkings();
|
||||
|
||||
pk.SetNickname(Set.Nickname);
|
||||
pk.SetSaneGender(Set.Gender);
|
||||
|
||||
if (Legal.IsPPUpAvailable(pk))
|
||||
pk.SetMaximumPPUps(Set.Moves);
|
||||
|
||||
if (pk.Format >= 3)
|
||||
{
|
||||
if (la.Parsed && la.EncounterOriginal is EncounterTrade {HasNickname: true} t)
|
||||
pk.SetNickname(t.GetNickname(pk.Language));
|
||||
else
|
||||
pk.ClearNickname();
|
||||
pk.SetAbility(Set.Ability);
|
||||
pk.SetNature(Set.Nature);
|
||||
}
|
||||
|
||||
/// <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));
|
||||
pk.SetIsShiny(Set.Shiny);
|
||||
pk.SetRandomEC();
|
||||
|
||||
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)
|
||||
if (pk is IAwakened a)
|
||||
{
|
||||
var arr = unicode ? PotentialUnicode : PotentialNoUnicode;
|
||||
return arr[pk.PotentialRating];
|
||||
a.SetSuggestedAwakenedValues(pk);
|
||||
if (pk is PB7 b)
|
||||
{
|
||||
for (int i = 0; i < 6; i++)
|
||||
b.SetEV(i, 0);
|
||||
b.ResetCalculatedValues();
|
||||
}
|
||||
}
|
||||
if (pk is IGanbaru g)
|
||||
g.SetSuggestedGanbaruValues(pk);
|
||||
|
||||
// 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 is IGigantamax c)
|
||||
c.CanGigantamax = Set.CanGigantamax;
|
||||
if (pk is IDynamaxLevel d)
|
||||
d.DynamaxLevel = d.GetSuggestedDynamaxLevel(pk);
|
||||
|
||||
if (pk is ITechRecord8 t)
|
||||
{
|
||||
if (pk.Format < 2)
|
||||
return string.Empty;
|
||||
|
||||
int location = eggmet ? pk.Egg_Location : pk.Met_Location;
|
||||
return GameInfo.GetLocationName(eggmet, location, pk.Format, pk.Generation, (GameVersion)pk.Version);
|
||||
t.ClearRecordFlags();
|
||||
t.SetRecordFlags(Set.Moves);
|
||||
}
|
||||
if (pk is IMoveShop8Mastery s)
|
||||
s.SetMoveShopFlags(Set.Moves, pk);
|
||||
|
||||
if (ShowdownSetBehaviorNature && pk.Format >= 8)
|
||||
pk.Nature = pk.StatNature;
|
||||
|
||||
var legal = new LegalityAnalysis(pk);
|
||||
if (legal.Parsed && legal.Info.Relearn.Any(z => !z.Valid))
|
||||
pk.SetRelearnMoves(legal.GetSuggestedRelearnMoves());
|
||||
pk.ResetPartyStats();
|
||||
pk.RefreshChecksum();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="PKM.HeldItem"/> value depending on the current format and the provided item index & format.
|
||||
/// </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)
|
||||
{
|
||||
item = ItemConverter.GetItemForFormat(item, format, pk.Format);
|
||||
pk.HeldItem = ((uint)item > pk.MaxItemID) ? 0 : item;
|
||||
}
|
||||
|
||||
/// <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 int SetEV(this PKM pk, int index, int value) => index switch
|
||||
{
|
||||
0 => pk.EV_HP = value,
|
||||
1 => pk.EV_ATK = value,
|
||||
2 => pk.EV_DEF = value,
|
||||
3 => pk.EV_SPE = value,
|
||||
4 => pk.EV_SPA = value,
|
||||
5 => pk.EV_SPD = value,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index)),
|
||||
};
|
||||
|
||||
/// <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 int SetIV(this PKM pk, int index, int value) => index switch
|
||||
{
|
||||
0 => pk.IV_HP = value,
|
||||
1 => pk.IV_ATK = value,
|
||||
2 => pk.IV_DEF = value,
|
||||
3 => pk.IV_SPE = value,
|
||||
4 => pk.IV_SPA = value,
|
||||
5 => pk.IV_SPD = value,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index)),
|
||||
};
|
||||
|
||||
/// <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 sum = pk.EVTotal - pk.GetEV(index);
|
||||
int remaining = 510 - sum;
|
||||
return Math.Min(Math.Max(remaining, 0), 252);
|
||||
}
|
||||
|
||||
/// <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 maximum.</param>
|
||||
/// <returns>Highest value the value can be.</returns>
|
||||
public static int GetMaximumIV(this PKM pk, int index, bool allow30 = false)
|
||||
{
|
||||
if (pk.GetIV(index) == pk.MaxIV && allow30)
|
||||
return pk.MaxIV - 1;
|
||||
return pk.MaxIV;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
|
||||
/// </summary>
|
||||
/// <param name="pk">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 pk, bool reHatch = false)
|
||||
{
|
||||
if (!pk.IsEgg && !reHatch)
|
||||
return;
|
||||
pk.IsEgg = false;
|
||||
pk.ClearNickname();
|
||||
pk.CurrentFriendship = pk.PersonalInfo.BaseFriendship;
|
||||
if (pk.IsTradedEgg)
|
||||
pk.Egg_Location = pk.Met_Location;
|
||||
var loc = EncounterSuggestion.GetSuggestedEggMetLocation(pk);
|
||||
if (loc >= 0)
|
||||
pk.Met_Location = loc;
|
||||
pk.MetDate = DateTime.Today;
|
||||
if (pk.Gen6)
|
||||
pk.SetHatchMemory6();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
|
||||
/// </summary>
|
||||
/// <param name="pk">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 pk, GameVersion origin, GameVersion dest)
|
||||
{
|
||||
bool traded = origin != dest;
|
||||
var today = pk.MetDate = DateTime.Today;
|
||||
pk.Egg_Location = EncounterSuggestion.GetSuggestedEncounterEggLocationEgg(pk.Generation, origin, traded);
|
||||
pk.EggMetDate = today;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximizes the <see cref="PKM.CurrentFriendship"/>. If the <see cref="PKM.IsEgg"/>, the hatch counter is set to 1.
|
||||
/// </summary>
|
||||
/// <param name="pk">PKM to apply hatch details to</param>
|
||||
public static void MaximizeFriendship(this PKM pk)
|
||||
{
|
||||
if (pk.IsEgg)
|
||||
pk.OT_Friendship = 1;
|
||||
else
|
||||
pk.CurrentFriendship = byte.MaxValue;
|
||||
if (pk is ICombatPower pb)
|
||||
pb.ResetCP();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximizes the <see cref="PKM.CurrentLevel"/>. If the <see cref="PKM.IsEgg"/>, the <see cref="PKM"/> is ignored.
|
||||
/// </summary>
|
||||
/// <param name="pk">PKM to apply hatch details to</param>
|
||||
public static void MaximizeLevel(this PKM pk)
|
||||
{
|
||||
if (pk.IsEgg)
|
||||
return;
|
||||
pk.CurrentLevel = 100;
|
||||
if (pk is ICombatPower pb)
|
||||
pb.ResetCP();
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static void SetDefaultNickname(this PKM pk, LegalityAnalysis la)
|
||||
{
|
||||
if (la.Parsed && la.EncounterOriginal is EncounterTrade {HasNickname: true} t)
|
||||
pk.SetNickname(t.GetNickname(pk.Language));
|
||||
else
|
||||
pk.ClearNickname();
|
||||
}
|
||||
|
||||
/// <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));
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
// 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 location = eggmet ? pk.Egg_Location : pk.Met_Location;
|
||||
return GameInfo.GetLocationName(eggmet, location, pk.Format, pk.Generation, (GameVersion)pk.Version);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,11 +114,11 @@ public sealed class TrainerDatabase
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the trainer details of the <see cref="pkm"/> to the <see cref="Database"/>.
|
||||
/// Adds the trainer details of the <see cref="pk"/> to the <see cref="Database"/>.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Pokémon with Trainer details to add.</param>
|
||||
/// <param name="pk">Pokémon with Trainer details to add.</param>
|
||||
/// <remarks>A copy of the object will be made to prevent modifications, just in case.</remarks>
|
||||
public void RegisterCopy(PKM pkm) => Register(GetTrainerReference(pkm));
|
||||
public void RegisterCopy(PKM pk) => Register(GetTrainerReference(pk));
|
||||
|
||||
/// <summary>
|
||||
/// Adds the trainer details of the <see cref="info"/> to the <see cref="Database"/>.
|
||||
|
@ -127,16 +127,16 @@ public sealed class TrainerDatabase
|
|||
/// <remarks>A copy of the object will be made to prevent modifications, just in case.</remarks>
|
||||
public void RegisterCopy(ITrainerInfo info) => Register(new SimpleTrainerInfo(info));
|
||||
|
||||
private static ITrainerInfo GetTrainerReference(PKM pkm)
|
||||
private static ITrainerInfo GetTrainerReference(PKM pk)
|
||||
{
|
||||
var result = new SimpleTrainerInfo((GameVersion)pkm.Version)
|
||||
var result = new SimpleTrainerInfo((GameVersion)pk.Version)
|
||||
{
|
||||
TID = pkm.TID, SID = pkm.SID, OT = pkm.OT_Name, Gender = pkm.OT_Gender,
|
||||
Language = pkm.Language,
|
||||
Generation = pkm.Generation,
|
||||
TID = pk.TID, SID = pk.SID, OT = pk.OT_Name, Gender = pk.OT_Gender,
|
||||
Language = pk.Language,
|
||||
Generation = pk.Generation,
|
||||
};
|
||||
|
||||
if (pkm is IRegionOrigin r)
|
||||
if (pk is IRegionOrigin r)
|
||||
r.CopyRegionOrigin(result);
|
||||
else
|
||||
result.SetDefaultRegionOrigins();
|
||||
|
|
|
@ -1,217 +1,216 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for calculating a Hidden Power Type based on IVs and generation-format.
|
||||
/// </summary>
|
||||
public static class HiddenPower
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for calculating a Hidden Power Type based on IVs and generation-format.
|
||||
/// Gets the current Hidden Power Type of the input <see cref="IVs"/> for the requested format generation.
|
||||
/// </summary>
|
||||
public static class HiddenPower
|
||||
/// <param name="IVs">Current IVs</param>
|
||||
/// <returns>Hidden Power Type of the <see cref="IVs"/></returns>
|
||||
/// <param name="format">Generation format</param>
|
||||
public static int GetType(ReadOnlySpan<int> IVs, int format)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current Hidden Power Type of the input <see cref="IVs"/> for the requested format generation.
|
||||
/// </summary>
|
||||
/// <param name="IVs">Current IVs</param>
|
||||
/// <returns>Hidden Power Type of the <see cref="IVs"/></returns>
|
||||
/// <param name="format">Generation format</param>
|
||||
public static int GetType(ReadOnlySpan<int> IVs, int format)
|
||||
{
|
||||
if (format <= 2)
|
||||
return GetTypeGB(IVs);
|
||||
return GetType(IVs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current Hidden Power Type of the input <see cref="IVs"/> for Generations 3+
|
||||
/// </summary>
|
||||
/// <param name="IVs">Current IVs</param>
|
||||
/// <returns>Hidden Power Type of the <see cref="IVs"/></returns>
|
||||
public static int GetType(ReadOnlySpan<int> IVs)
|
||||
{
|
||||
int hp = 0;
|
||||
for (int i = 0; i < 6; i++)
|
||||
hp |= (IVs[i] & 1) << i;
|
||||
hp *= 0xF;
|
||||
hp /= 0x3F;
|
||||
return hp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current Hidden Power Type of the input <see cref="IVs"/> for Generations 1 & 2
|
||||
/// </summary>
|
||||
/// <param name="IVs">Current IVs</param>
|
||||
/// <returns>Hidden Power Type of the <see cref="IVs"/></returns>
|
||||
public static int GetTypeGB(ReadOnlySpan<int> IVs)
|
||||
{
|
||||
var atk = IVs[1];
|
||||
var def = IVs[2];
|
||||
return ((atk & 3) << 2) | (def & 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the provided <see cref="IVs"/> to have the requested <see cref="hiddenPowerType"/> for Generations 1 & 2
|
||||
/// </summary>
|
||||
/// <param name="hiddenPowerType">Hidden Power Type</param>
|
||||
/// <param name="IVs">Current IVs</param>
|
||||
/// <returns>True if the Hidden Power of the <see cref="IVs"/> is obtained, with or without modifications</returns>
|
||||
public static bool SetTypeGB(int hiddenPowerType, Span<int> IVs)
|
||||
{
|
||||
IVs[1] = (IVs[1] & ~3) | (hiddenPowerType >> 2);
|
||||
IVs[2] = (IVs[2] & ~3) | (hiddenPowerType & 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the provided <see cref="IVs"/> to have the requested <see cref="hiddenPowerType"/>.
|
||||
/// </summary>
|
||||
/// <param name="hiddenPowerType">Hidden Power Type</param>
|
||||
/// <param name="IVs">Current IVs (6 total)</param>
|
||||
/// <param name="format">Generation format</param>
|
||||
/// <returns>True if the Hidden Power of the <see cref="IVs"/> is obtained, with or without modifications</returns>
|
||||
public static bool SetIVsForType(int hiddenPowerType, Span<int> IVs, int format)
|
||||
{
|
||||
if (format <= 2)
|
||||
return SetTypeGB(hiddenPowerType, IVs);
|
||||
return SetIVsForType(hiddenPowerType, IVs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="IVs"/> to the requested <see cref="hpVal"/> for Generation 3+ game formats.
|
||||
/// </summary>
|
||||
/// <param name="hpVal">Hidden Power Type</param>
|
||||
/// <param name="IVs">Current IVs (6 total)</param>
|
||||
/// <returns>True if the Hidden Power of the <see cref="IVs"/> is obtained, with or without modifications</returns>
|
||||
public static bool SetIVsForType(int hpVal, Span<int> IVs)
|
||||
{
|
||||
int flawlessCount = IVs.Count(31);
|
||||
if (flawlessCount == 0)
|
||||
return false;
|
||||
|
||||
if (flawlessCount == IVs.Length)
|
||||
{
|
||||
SetIVs(hpVal, IVs); // Get IVs
|
||||
return true;
|
||||
}
|
||||
|
||||
int current = GetType(IVs);
|
||||
if (current == hpVal)
|
||||
return true; // no mods necessary
|
||||
|
||||
// Required HP type doesn't match IVs. Make currently-flawless IVs flawed.
|
||||
Span<int> scratch = stackalloc int[IVs.Length];
|
||||
Span<int> result = stackalloc int[IVs.Length];
|
||||
var success = GetSuggestedHiddenPowerIVs(hpVal, IVs, scratch, result);
|
||||
if (!success)
|
||||
return false; // can't force hidden power?
|
||||
|
||||
// set IVs back to array
|
||||
result.CopyTo(IVs);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Non-recursive https://en.wikipedia.org/wiki/Heap%27s_algorithm
|
||||
private static bool GetSuggestedHiddenPowerIVs(int hpVal, ReadOnlySpan<int> original, Span<int> ivs, Span<int> best)
|
||||
{
|
||||
const int max = 31;
|
||||
|
||||
// Get a list of indexes that can be mutated
|
||||
Span<int> indexes = stackalloc int[original.Length];
|
||||
int flaw = 0;
|
||||
for (int i = 0; i < original.Length; i++)
|
||||
{
|
||||
if (original[i] == max)
|
||||
indexes[flaw++] = i;
|
||||
}
|
||||
indexes = indexes[..flaw];
|
||||
Span<int> c = stackalloc int[indexes.Length];
|
||||
|
||||
int mutated = c.Length + 1; // result tracking
|
||||
for (int i = 1; i < c.Length;)
|
||||
{
|
||||
if (c[i] >= i) // Reset the state and simulate popping the stack by incrementing the pointer.
|
||||
{
|
||||
c[i++] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
var x = (i & 1) == 1 ? c[i] : 0;
|
||||
Swap(ref indexes[i], ref indexes[x]);
|
||||
|
||||
// Inlined continuance check
|
||||
original.CopyTo(ivs);
|
||||
var q = Math.Min(indexes.Length, mutated);
|
||||
for (var j = 0; j < q; j++)
|
||||
{
|
||||
ivs[indexes[j]] ^= 1;
|
||||
if (hpVal != GetType(ivs))
|
||||
continue;
|
||||
|
||||
var ct = j + 1;
|
||||
if (ct >= mutated)
|
||||
break; // any further flaws are always worse
|
||||
|
||||
mutated = ct;
|
||||
ivs.CopyTo(best);
|
||||
if (j == 0) // nothing will be better than only 1 flaw
|
||||
return true;
|
||||
break; // any further flaws are always worse
|
||||
}
|
||||
|
||||
c[i]++;
|
||||
i = 1;
|
||||
}
|
||||
|
||||
return mutated <= c.Length; // did we actually find a suitable result?
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void Swap<T>(ref T a, ref T b) => (a, b) = (b, a);
|
||||
|
||||
/// <summary>Calculate the Hidden Power Type of the entered IVs.</summary>
|
||||
/// <param name="type">Hidden Power Type</param>
|
||||
/// <param name="ivs">Individual Values (H/A/B/S/C/D)</param>
|
||||
/// <param name="format">Generation specific format</param>
|
||||
public static void SetIVs(int type, Span<int> ivs, int format = PKX.Generation)
|
||||
{
|
||||
if (format <= 2)
|
||||
{
|
||||
ivs[1] = (ivs[1] & ~3) | (type >> 2);
|
||||
ivs[2] = (ivs[2] & ~3) | (type & 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
var bits = DefaultLowBits[type];
|
||||
for (int i = 0; i < 6; i++)
|
||||
ivs[i] = (ivs[i] & 0x1E) + ((bits >> i) & 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hidden Power IV values (even or odd) to achieve a specified Hidden Power Type
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There are other IV combinations to achieve the same Hidden Power Type.
|
||||
/// These are just precomputed for fast modification.
|
||||
/// Individual Values (H/A/B/S/C/D)
|
||||
/// </remarks>
|
||||
public static readonly byte[] DefaultLowBits =
|
||||
{
|
||||
0b000011, // Fighting
|
||||
0b001000, // Flying
|
||||
0b001011, // Poison
|
||||
0b001111, // Ground
|
||||
0b010011, // Rock
|
||||
0b011001, // Bug
|
||||
0b011101, // Ghost
|
||||
0b011111, // Steel
|
||||
0b100101, // Fire
|
||||
0b101001, // Water
|
||||
0b101101, // Grass
|
||||
0b101111, // Electric
|
||||
0b110101, // Psychic
|
||||
0b111001, // Ice
|
||||
0b111101, // Dragon
|
||||
0b111111, // Dark
|
||||
};
|
||||
if (format <= 2)
|
||||
return GetTypeGB(IVs);
|
||||
return GetType(IVs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current Hidden Power Type of the input <see cref="IVs"/> for Generations 3+
|
||||
/// </summary>
|
||||
/// <param name="IVs">Current IVs</param>
|
||||
/// <returns>Hidden Power Type of the <see cref="IVs"/></returns>
|
||||
public static int GetType(ReadOnlySpan<int> IVs)
|
||||
{
|
||||
int hp = 0;
|
||||
for (int i = 0; i < 6; i++)
|
||||
hp |= (IVs[i] & 1) << i;
|
||||
hp *= 0xF;
|
||||
hp /= 0x3F;
|
||||
return hp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current Hidden Power Type of the input <see cref="IVs"/> for Generations 1 & 2
|
||||
/// </summary>
|
||||
/// <param name="IVs">Current IVs</param>
|
||||
/// <returns>Hidden Power Type of the <see cref="IVs"/></returns>
|
||||
public static int GetTypeGB(ReadOnlySpan<int> IVs)
|
||||
{
|
||||
var atk = IVs[1];
|
||||
var def = IVs[2];
|
||||
return ((atk & 3) << 2) | (def & 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the provided <see cref="IVs"/> to have the requested <see cref="hiddenPowerType"/> for Generations 1 & 2
|
||||
/// </summary>
|
||||
/// <param name="hiddenPowerType">Hidden Power Type</param>
|
||||
/// <param name="IVs">Current IVs</param>
|
||||
/// <returns>True if the Hidden Power of the <see cref="IVs"/> is obtained, with or without modifications</returns>
|
||||
public static bool SetTypeGB(int hiddenPowerType, Span<int> IVs)
|
||||
{
|
||||
IVs[1] = (IVs[1] & ~3) | (hiddenPowerType >> 2);
|
||||
IVs[2] = (IVs[2] & ~3) | (hiddenPowerType & 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the provided <see cref="IVs"/> to have the requested <see cref="hiddenPowerType"/>.
|
||||
/// </summary>
|
||||
/// <param name="hiddenPowerType">Hidden Power Type</param>
|
||||
/// <param name="IVs">Current IVs (6 total)</param>
|
||||
/// <param name="format">Generation format</param>
|
||||
/// <returns>True if the Hidden Power of the <see cref="IVs"/> is obtained, with or without modifications</returns>
|
||||
public static bool SetIVsForType(int hiddenPowerType, Span<int> IVs, int format)
|
||||
{
|
||||
if (format <= 2)
|
||||
return SetTypeGB(hiddenPowerType, IVs);
|
||||
return SetIVsForType(hiddenPowerType, IVs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="IVs"/> to the requested <see cref="hpVal"/> for Generation 3+ game formats.
|
||||
/// </summary>
|
||||
/// <param name="hpVal">Hidden Power Type</param>
|
||||
/// <param name="IVs">Current IVs (6 total)</param>
|
||||
/// <returns>True if the Hidden Power of the <see cref="IVs"/> is obtained, with or without modifications</returns>
|
||||
public static bool SetIVsForType(int hpVal, Span<int> IVs)
|
||||
{
|
||||
int flawlessCount = IVs.Count(31);
|
||||
if (flawlessCount == 0)
|
||||
return false;
|
||||
|
||||
if (flawlessCount == IVs.Length)
|
||||
{
|
||||
SetIVs(hpVal, IVs); // Get IVs
|
||||
return true;
|
||||
}
|
||||
|
||||
int current = GetType(IVs);
|
||||
if (current == hpVal)
|
||||
return true; // no mods necessary
|
||||
|
||||
// Required HP type doesn't match IVs. Make currently-flawless IVs flawed.
|
||||
Span<int> scratch = stackalloc int[IVs.Length];
|
||||
Span<int> result = stackalloc int[IVs.Length];
|
||||
var success = GetSuggestedHiddenPowerIVs(hpVal, IVs, scratch, result);
|
||||
if (!success)
|
||||
return false; // can't force hidden power?
|
||||
|
||||
// set IVs back to array
|
||||
result.CopyTo(IVs);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Non-recursive https://en.wikipedia.org/wiki/Heap%27s_algorithm
|
||||
private static bool GetSuggestedHiddenPowerIVs(int hpVal, ReadOnlySpan<int> original, Span<int> ivs, Span<int> best)
|
||||
{
|
||||
const int max = 31;
|
||||
|
||||
// Get a list of indexes that can be mutated
|
||||
Span<int> indexes = stackalloc int[original.Length];
|
||||
int flaw = 0;
|
||||
for (int i = 0; i < original.Length; i++)
|
||||
{
|
||||
if (original[i] == max)
|
||||
indexes[flaw++] = i;
|
||||
}
|
||||
indexes = indexes[..flaw];
|
||||
Span<int> c = stackalloc int[indexes.Length];
|
||||
|
||||
int mutated = c.Length + 1; // result tracking
|
||||
for (int i = 1; i < c.Length;)
|
||||
{
|
||||
if (c[i] >= i) // Reset the state and simulate popping the stack by incrementing the pointer.
|
||||
{
|
||||
c[i++] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
var x = (i & 1) == 1 ? c[i] : 0;
|
||||
Swap(ref indexes[i], ref indexes[x]);
|
||||
|
||||
// Inlined continuance check
|
||||
original.CopyTo(ivs);
|
||||
var q = Math.Min(indexes.Length, mutated);
|
||||
for (var j = 0; j < q; j++)
|
||||
{
|
||||
ivs[indexes[j]] ^= 1;
|
||||
if (hpVal != GetType(ivs))
|
||||
continue;
|
||||
|
||||
var ct = j + 1;
|
||||
if (ct >= mutated)
|
||||
break; // any further flaws are always worse
|
||||
|
||||
mutated = ct;
|
||||
ivs.CopyTo(best);
|
||||
if (j == 0) // nothing will be better than only 1 flaw
|
||||
return true;
|
||||
break; // any further flaws are always worse
|
||||
}
|
||||
|
||||
c[i]++;
|
||||
i = 1;
|
||||
}
|
||||
|
||||
return mutated <= c.Length; // did we actually find a suitable result?
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void Swap<T>(ref T a, ref T b) => (a, b) = (b, a);
|
||||
|
||||
/// <summary>Calculate the Hidden Power Type of the entered IVs.</summary>
|
||||
/// <param name="type">Hidden Power Type</param>
|
||||
/// <param name="ivs">Individual Values (H/A/B/S/C/D)</param>
|
||||
/// <param name="format">Generation specific format</param>
|
||||
public static void SetIVs(int type, Span<int> ivs, int format = PKX.Generation)
|
||||
{
|
||||
if (format <= 2)
|
||||
{
|
||||
ivs[1] = (ivs[1] & ~3) | (type >> 2);
|
||||
ivs[2] = (ivs[2] & ~3) | (type & 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
var bits = DefaultLowBits[type];
|
||||
for (int i = 0; i < 6; i++)
|
||||
ivs[i] = (ivs[i] & 0x1E) + ((bits >> i) & 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hidden Power IV values (even or odd) to achieve a specified Hidden Power Type
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There are other IV combinations to achieve the same Hidden Power Type.
|
||||
/// These are just precomputed for fast modification.
|
||||
/// Individual Values (H/A/B/S/C/D)
|
||||
/// </remarks>
|
||||
public static readonly byte[] DefaultLowBits =
|
||||
{
|
||||
0b000011, // Fighting
|
||||
0b001000, // Flying
|
||||
0b001011, // Poison
|
||||
0b001111, // Ground
|
||||
0b010011, // Rock
|
||||
0b011001, // Bug
|
||||
0b011101, // Ghost
|
||||
0b011111, // Steel
|
||||
0b100101, // Fire
|
||||
0b101001, // Water
|
||||
0b101101, // Grass
|
||||
0b101111, // Electric
|
||||
0b110101, // Psychic
|
||||
0b111001, // Ice
|
||||
0b111101, // Dragon
|
||||
0b111111, // Dark
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,73 +1,72 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Interface containing details relevant for battling.
|
||||
/// </summary>
|
||||
public interface IBattleTemplate : ISpeciesForm, IGigantamax, INature
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface containing details relevant for battling.
|
||||
/// <see cref="PKM.Format"/> of the Set entity it is specific to.
|
||||
/// </summary>
|
||||
public interface IBattleTemplate : ISpeciesForm, IGigantamax, INature
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="PKM.Format"/> of the Set entity it is specific to.
|
||||
/// </summary>
|
||||
int Format { get; }
|
||||
int Format { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.Nickname"/> of the Set entity.
|
||||
/// </summary>
|
||||
string Nickname { get; }
|
||||
/// <summary>
|
||||
/// <see cref="PKM.Nickname"/> of the Set entity.
|
||||
/// </summary>
|
||||
string Nickname { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.Gender"/> name of the Set entity.
|
||||
/// </summary>
|
||||
int Gender { get; }
|
||||
/// <summary>
|
||||
/// <see cref="PKM.Gender"/> name of the Set entity.
|
||||
/// </summary>
|
||||
int Gender { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.HeldItem"/> of the Set entity.
|
||||
/// </summary>
|
||||
int HeldItem { get; }
|
||||
/// <summary>
|
||||
/// <see cref="PKM.HeldItem"/> of the Set entity.
|
||||
/// </summary>
|
||||
int HeldItem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.Ability"/> of the Set entity.
|
||||
/// </summary>
|
||||
int Ability { get; }
|
||||
/// <summary>
|
||||
/// <see cref="PKM.Ability"/> of the Set entity.
|
||||
/// </summary>
|
||||
int Ability { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.CurrentLevel"/> of the Set entity.
|
||||
/// </summary>
|
||||
int Level { get; }
|
||||
/// <summary>
|
||||
/// <see cref="PKM.CurrentLevel"/> of the Set entity.
|
||||
/// </summary>
|
||||
int Level { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.CurrentLevel"/> of the Set entity.
|
||||
/// </summary>
|
||||
bool Shiny { get; }
|
||||
/// <summary>
|
||||
/// <see cref="PKM.CurrentLevel"/> of the Set entity.
|
||||
/// </summary>
|
||||
bool Shiny { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.CurrentFriendship"/> of the Set entity.
|
||||
/// </summary>
|
||||
int Friendship { get; }
|
||||
/// <summary>
|
||||
/// <see cref="PKM.CurrentFriendship"/> of the Set entity.
|
||||
/// </summary>
|
||||
int Friendship { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.Form"/> name of the Set entity, stored in PKHeX style (instead of Showdown's)
|
||||
/// </summary>
|
||||
string FormName { get; }
|
||||
/// <summary>
|
||||
/// <see cref="PKM.Form"/> name of the Set entity, stored in PKHeX style (instead of Showdown's)
|
||||
/// </summary>
|
||||
string FormName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.HPType"/> of the Set entity.
|
||||
/// </summary>
|
||||
int HiddenPowerType { get; }
|
||||
/// <summary>
|
||||
/// <see cref="PKM.HPType"/> of the Set entity.
|
||||
/// </summary>
|
||||
int HiddenPowerType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.EVs"/> of the Set entity.
|
||||
/// </summary>
|
||||
int[] EVs { get; }
|
||||
/// <summary>
|
||||
/// <see cref="PKM.EVs"/> of the Set entity.
|
||||
/// </summary>
|
||||
int[] EVs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.IVs"/> of the Set entity.
|
||||
/// </summary>
|
||||
int[] IVs { get; }
|
||||
/// <summary>
|
||||
/// <see cref="PKM.IVs"/> of the Set entity.
|
||||
/// </summary>
|
||||
int[] IVs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PKM.Moves"/> of the Set entity.
|
||||
/// </summary>
|
||||
int[] Moves { get; }
|
||||
}
|
||||
/// <summary>
|
||||
/// <see cref="PKM.Moves"/> of the Set entity.
|
||||
/// </summary>
|
||||
int[] Moves { get; }
|
||||
}
|
||||
|
|
|
@ -1,48 +1,47 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Simple interface representing a <see cref="PKM"/> viewer.
|
||||
/// </summary>
|
||||
public interface IPKMView
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple interface representing a <see cref="PKM"/> viewer.
|
||||
/// Fetches the currently loaded <see cref="PKM"/> data from the viewer.
|
||||
/// </summary>
|
||||
public interface IPKMView
|
||||
{
|
||||
/// <summary>
|
||||
/// Fetches the currently loaded <see cref="PKM"/> data from the viewer.
|
||||
/// </summary>
|
||||
PKM Data { get; }
|
||||
PKM Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the Viewer supports using Unicode characters or not.
|
||||
/// </summary>
|
||||
bool Unicode { get; }
|
||||
/// <summary>
|
||||
/// Indicates if the Viewer supports using Unicode characters or not.
|
||||
/// </summary>
|
||||
bool Unicode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the Viewer is providing extra flexibility or not.
|
||||
/// </summary>
|
||||
bool HaX { get; }
|
||||
/// <summary>
|
||||
/// Indicates if the Viewer is providing extra flexibility or not.
|
||||
/// </summary>
|
||||
bool HaX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the Viewer's controls are changing their values and should avoid triggering other updates.
|
||||
/// </summary>
|
||||
bool ChangingFields { get; set; }
|
||||
/// <summary>
|
||||
/// Indicates if the Viewer's controls are changing their values and should avoid triggering other updates.
|
||||
/// </summary>
|
||||
bool ChangingFields { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the currently loaded <see cref="PKM"/> data from the viewer by finishing any pending changes or auto-modifications.
|
||||
/// </summary>
|
||||
/// <param name="click">Cause the viewer to do extra actions to force validation of its children.</param>
|
||||
/// <returns>Prepared <see cref="PKM"/> data from the viewer.</returns>
|
||||
PKM PreparePKM(bool click = true);
|
||||
/// <summary>
|
||||
/// Fetches the currently loaded <see cref="PKM"/> data from the viewer by finishing any pending changes or auto-modifications.
|
||||
/// </summary>
|
||||
/// <param name="click">Cause the viewer to do extra actions to force validation of its children.</param>
|
||||
/// <returns>Prepared <see cref="PKM"/> data from the viewer.</returns>
|
||||
PKM PreparePKM(bool click = true);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the currently loaded <see cref="PKM"/> data is ready for exporting.
|
||||
/// </summary>
|
||||
bool EditsComplete { get; }
|
||||
/// <summary>
|
||||
/// Indicates if the currently loaded <see cref="PKM"/> data is ready for exporting.
|
||||
/// </summary>
|
||||
bool EditsComplete { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Loads a given <see cref="PKM"/> data to the viewer.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon data to load.</param>
|
||||
/// <param name="focus">Cause the viewer to give focus to itself.</param>
|
||||
/// <param name="skipConversionCheck">Cause the viewer to skip converting the data. Faster if it is known that the format is the same as the previous format.</param>
|
||||
void PopulateFields(PKM pk, bool focus = true, bool skipConversionCheck = false);
|
||||
}
|
||||
/// <summary>
|
||||
/// Loads a given <see cref="PKM"/> data to the viewer.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon data to load.</param>
|
||||
/// <param name="focus">Cause the viewer to give focus to itself.</param>
|
||||
/// <param name="skipConversionCheck">Cause the viewer to skip converting the data. Faster if it is known that the format is the same as the previous format.</param>
|
||||
void PopulateFields(PKM pk, bool focus = true, bool skipConversionCheck = false);
|
||||
}
|
||||
|
|
|
@ -1,41 +1,40 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Plugin interface used by an editor to notify third-party code providers.
|
||||
/// </summary>
|
||||
public interface IPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Plugin interface used by an editor to notify third-party code providers.
|
||||
/// Plugin Name.
|
||||
/// </summary>
|
||||
public interface IPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Plugin Name.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Plugin Loading Priority (lowest is initialized first).
|
||||
/// </summary>
|
||||
int Priority { get; }
|
||||
/// <summary>
|
||||
/// Plugin Loading Priority (lowest is initialized first).
|
||||
/// </summary>
|
||||
int Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Entry point for the parent to initialize the plugin with provided arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">Arguments containing objects useful for initializing the plugin.</param>
|
||||
void Initialize(params object[] args);
|
||||
/// <summary>
|
||||
/// Entry point for the parent to initialize the plugin with provided arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">Arguments containing objects useful for initializing the plugin.</param>
|
||||
void Initialize(params object[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the plugin that a save file was just loaded.
|
||||
/// </summary>
|
||||
void NotifySaveLoaded();
|
||||
/// <summary>
|
||||
/// Notifies the plugin that a save file was just loaded.
|
||||
/// </summary>
|
||||
void NotifySaveLoaded();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to load a file using the plugin.
|
||||
/// </summary>
|
||||
/// <param name="filePath">Path to file to be loaded.</param>
|
||||
/// <returns>True if the plugin has handled the file.</returns>
|
||||
bool TryLoadFile(string filePath);
|
||||
/// <summary>
|
||||
/// Attempts to load a file using the plugin.
|
||||
/// </summary>
|
||||
/// <param name="filePath">Path to file to be loaded.</param>
|
||||
/// <returns>True if the plugin has handled the file.</returns>
|
||||
bool TryLoadFile(string filePath);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <see cref="ISaveFileProvider"/> object which can provide a <see cref="SaveFile"/>.
|
||||
/// </summary>
|
||||
ISaveFileProvider SaveFileEditor { get; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Retrieves the <see cref="ISaveFileProvider"/> object which can provide a <see cref="SaveFile"/>.
|
||||
/// </summary>
|
||||
ISaveFileProvider SaveFileEditor { get; }
|
||||
}
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Simple interface representing a Save File viewer.
|
||||
/// </summary>
|
||||
public interface ISaveFileProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple interface representing a Save File viewer.
|
||||
/// Retrieves the save file the <see cref="ISaveFileProvider"/> has control over.
|
||||
/// </summary>
|
||||
public interface ISaveFileProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the save file the <see cref="ISaveFileProvider"/> has control over.
|
||||
/// </summary>
|
||||
SaveFile SAV { get; }
|
||||
SaveFile SAV { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the current box the <see cref="ISaveFileProvider"/> has control over.
|
||||
/// </summary>
|
||||
int CurrentBox { get; }
|
||||
/// <summary>
|
||||
/// Retrieves the current box the <see cref="ISaveFileProvider"/> has control over.
|
||||
/// </summary>
|
||||
int CurrentBox { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Triggers a refresh of any individual <see cref="PKM"/> view slots.
|
||||
/// </summary>
|
||||
void ReloadSlots();
|
||||
}
|
||||
/// <summary>
|
||||
/// Triggers a refresh of any individual <see cref="PKM"/> view slots.
|
||||
/// </summary>
|
||||
void ReloadSlots();
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public interface ISpriteBuilder<T>
|
||||
{
|
||||
public interface ISpriteBuilder<T>
|
||||
{
|
||||
T GetSprite(int species, int form, int gender, uint formarg, int heldItem, bool isEgg, bool isShiny,
|
||||
int generation = -1,
|
||||
bool isBoxBGRed = false,
|
||||
bool isAltShiny = false);
|
||||
T GetSprite(int species, int form, int gender, uint formarg, int heldItem, bool isEgg, bool isShiny,
|
||||
int generation = -1,
|
||||
bool isBoxBGRed = false,
|
||||
bool isAltShiny = false);
|
||||
|
||||
T GetSprite(T baseSprite, int species, int heldItem, bool isEgg, bool isShiny,
|
||||
int generation = -1,
|
||||
bool isBoxBGRed = false,
|
||||
bool isAltShiny = false);
|
||||
T GetSprite(T baseSprite, int species, int heldItem, bool isEgg, bool isShiny,
|
||||
int generation = -1,
|
||||
bool isBoxBGRed = false,
|
||||
bool isAltShiny = false);
|
||||
|
||||
void Initialize(SaveFile sav);
|
||||
}
|
||||
void Initialize(SaveFile sav);
|
||||
}
|
||||
|
|
|
@ -2,26 +2,25 @@
|
|||
using System.Linq;
|
||||
using static PKHeX.Core.MessageStrings;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Utility for editing a <see cref="PKM"/>
|
||||
/// </summary>
|
||||
public static class EntitySuggestionUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility for editing a <see cref="PKM"/>
|
||||
/// </summary>
|
||||
public static class EntitySuggestionUtil
|
||||
public static List<string> GetMetLocationSuggestionMessage(PKM pk, int level, int location, int minimumLevel)
|
||||
{
|
||||
public static List<string> GetMetLocationSuggestionMessage(PKM pkm, int level, int location, int minimumLevel)
|
||||
var suggestion = new List<string> { MsgPKMSuggestionStart };
|
||||
if (pk.Format >= 3)
|
||||
{
|
||||
var suggestion = new List<string> { MsgPKMSuggestionStart };
|
||||
if (pkm.Format >= 3)
|
||||
{
|
||||
var metList = GameInfo.GetLocationList((GameVersion)pkm.Version, pkm.Context, egg: false);
|
||||
var locationName = metList.First(loc => loc.Value == location).Text;
|
||||
suggestion.Add($"{MsgPKMSuggestionMetLocation} {locationName}");
|
||||
suggestion.Add($"{MsgPKMSuggestionMetLevel} {level}");
|
||||
}
|
||||
if (pkm.CurrentLevel < minimumLevel)
|
||||
suggestion.Add($"{MsgPKMSuggestionLevel} {minimumLevel}");
|
||||
return suggestion;
|
||||
var metList = GameInfo.GetLocationList((GameVersion)pk.Version, pk.Context, egg: false);
|
||||
var locationName = metList.First(loc => loc.Value == location).Text;
|
||||
suggestion.Add($"{MsgPKMSuggestionMetLocation} {locationName}");
|
||||
suggestion.Add($"{MsgPKMSuggestionMetLevel} {level}");
|
||||
}
|
||||
if (pk.CurrentLevel < minimumLevel)
|
||||
suggestion.Add($"{MsgPKMSuggestionLevel} {minimumLevel}");
|
||||
return suggestion;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,126 +1,125 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Bindable summary object that can fetch strings that summarize a <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
public class EntitySummary // do NOT seal, allow inheritance
|
||||
{
|
||||
/// <summary>
|
||||
/// Bindable summary object that can fetch strings that summarize a <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
public class EntitySummary // do NOT seal, allow inheritance
|
||||
private static readonly IReadOnlyList<string> GenderSymbols = GameInfo.GenderSymbolASCII;
|
||||
|
||||
private readonly GameStrings Strings;
|
||||
private readonly ushort[] Stats;
|
||||
protected readonly PKM pk; // protected for children generating extra properties
|
||||
|
||||
public virtual string Position => "???";
|
||||
public string Nickname => pk.Nickname;
|
||||
public string Species => Get(Strings.specieslist, pk.Species);
|
||||
public string Nature => Get(Strings.natures, pk.StatNature);
|
||||
public string Gender => Get(GenderSymbols, pk.Gender);
|
||||
public string ESV => pk.PSV.ToString("0000");
|
||||
public string HP_Type => Get(Strings.types, pk.HPType + 1);
|
||||
public string Ability => Get(Strings.abilitylist, pk.Ability);
|
||||
public string Move1 => Get(Strings.movelist, pk.Move1);
|
||||
public string Move2 => Get(Strings.movelist, pk.Move2);
|
||||
public string Move3 => Get(Strings.movelist, pk.Move3);
|
||||
public string Move4 => Get(Strings.movelist, pk.Move4);
|
||||
public string HeldItem => GetSpan(Strings.GetItemStrings(pk.Format), pk.HeldItem);
|
||||
public string HP => Stats[0].ToString();
|
||||
public string ATK => Stats[1].ToString();
|
||||
public string DEF => Stats[2].ToString();
|
||||
public string SPA => Stats[4].ToString();
|
||||
public string SPD => Stats[5].ToString();
|
||||
public string SPE => Stats[3].ToString();
|
||||
public string MetLoc => pk.GetLocationString(eggmet: false);
|
||||
public string EggLoc => pk.GetLocationString(eggmet: true);
|
||||
public string Ball => Get(Strings.balllist, pk.Ball);
|
||||
public string OT => pk.OT_Name;
|
||||
public string Version => Get(Strings.gamelist, pk.Version);
|
||||
public string OTLang => ((LanguageID)pk.Language).ToString();
|
||||
public string Legal { get { var la = new LegalityAnalysis(pk); return la.Parsed ? la.Valid.ToString() : "-"; } }
|
||||
|
||||
#region Extraneous
|
||||
public string EC => pk.EncryptionConstant.ToString("X8");
|
||||
public string PID => pk.PID.ToString("X8");
|
||||
public int HP_IV => pk.IV_HP;
|
||||
public int ATK_IV => pk.IV_ATK;
|
||||
public int DEF_IV => pk.IV_DEF;
|
||||
public int SPA_IV => pk.IV_SPA;
|
||||
public int SPD_IV => pk.IV_SPD;
|
||||
public int SPE_IV => pk.IV_SPE;
|
||||
public uint EXP => pk.EXP;
|
||||
public int Level => pk.CurrentLevel;
|
||||
public int HP_EV => pk.EV_HP;
|
||||
public int ATK_EV => pk.EV_ATK;
|
||||
public int DEF_EV => pk.EV_DEF;
|
||||
public int SPA_EV => pk.EV_SPA;
|
||||
public int SPD_EV => pk.EV_SPD;
|
||||
public int SPE_EV => pk.EV_SPE;
|
||||
public int Cool => pk is IContestStats s ? s.CNT_Cool : 0;
|
||||
public int Beauty => pk is IContestStats s ? s.CNT_Beauty : 0;
|
||||
public int Cute => pk is IContestStats s ? s.CNT_Cute : 0;
|
||||
public int Smart => pk is IContestStats s ? s.CNT_Smart : 0;
|
||||
public int Tough => pk is IContestStats s ? s.CNT_Tough : 0;
|
||||
public int Sheen => pk is IContestStats s ? s.CNT_Sheen : 0;
|
||||
public int Markings => pk.MarkValue;
|
||||
|
||||
public string NotOT => pk.Format > 5 ? pk.HT_Name : "N/A";
|
||||
|
||||
public int AbilityNum => pk.Format > 5 ? pk.AbilityNumber : -1;
|
||||
public int GenderFlag => pk.Gender;
|
||||
public int Form => pk.Form;
|
||||
public int PKRS_Strain => pk.PKRS_Strain;
|
||||
public int PKRS_Days => pk.PKRS_Days;
|
||||
public int MetLevel => pk.Met_Level;
|
||||
public int OT_Gender => pk.OT_Gender;
|
||||
|
||||
public bool FatefulFlag => pk.FatefulEncounter;
|
||||
public bool IsEgg => pk.IsEgg;
|
||||
public bool IsNicknamed => pk.IsNicknamed;
|
||||
public bool IsShiny => pk.IsShiny;
|
||||
|
||||
public int TID => pk.DisplayTID;
|
||||
public int SID => pk.DisplaySID;
|
||||
public int TSV => pk.TSV;
|
||||
public int Move1_PP => pk.Move1_PP;
|
||||
public int Move2_PP => pk.Move2_PP;
|
||||
public int Move3_PP => pk.Move3_PP;
|
||||
public int Move4_PP => pk.Move4_PP;
|
||||
public int Move1_PPUp => pk.Move1_PPUps;
|
||||
public int Move2_PPUp => pk.Move2_PPUps;
|
||||
public int Move3_PPUp => pk.Move3_PPUps;
|
||||
public int Move4_PPUp => pk.Move4_PPUps;
|
||||
public string Relearn1 => Get(Strings.movelist, pk.RelearnMove1);
|
||||
public string Relearn2 => Get(Strings.movelist, pk.RelearnMove2);
|
||||
public string Relearn3 => Get(Strings.movelist, pk.RelearnMove3);
|
||||
public string Relearn4 => Get(Strings.movelist, pk.RelearnMove4);
|
||||
public ushort Checksum => pk is ISanityChecksum s ? s.Checksum : Checksums.CRC16_CCITT(pk.Data.AsSpan(pk.SIZE_STORED));
|
||||
public int Friendship => pk.OT_Friendship;
|
||||
public int Egg_Year => pk.EggMetDate.GetValueOrDefault().Year;
|
||||
public int Egg_Month => pk.EggMetDate.GetValueOrDefault().Month;
|
||||
public int Egg_Day => pk.EggMetDate.GetValueOrDefault().Day;
|
||||
public int Met_Year => pk.MetDate.GetValueOrDefault().Year;
|
||||
public int Met_Month => pk.MetDate.GetValueOrDefault().Month;
|
||||
public int Met_Day => pk.MetDate.GetValueOrDefault().Day;
|
||||
|
||||
#endregion
|
||||
|
||||
protected EntitySummary(PKM p, GameStrings strings)
|
||||
{
|
||||
private static readonly IReadOnlyList<string> GenderSymbols = GameInfo.GenderSymbolASCII;
|
||||
|
||||
private readonly GameStrings Strings;
|
||||
private readonly ushort[] Stats;
|
||||
protected readonly PKM pkm; // protected for children generating extra properties
|
||||
|
||||
public virtual string Position => "???";
|
||||
public string Nickname => pkm.Nickname;
|
||||
public string Species => Get(Strings.specieslist, pkm.Species);
|
||||
public string Nature => Get(Strings.natures, pkm.StatNature);
|
||||
public string Gender => Get(GenderSymbols, pkm.Gender);
|
||||
public string ESV => pkm.PSV.ToString("0000");
|
||||
public string HP_Type => Get(Strings.types, pkm.HPType + 1);
|
||||
public string Ability => Get(Strings.abilitylist, pkm.Ability);
|
||||
public string Move1 => Get(Strings.movelist, pkm.Move1);
|
||||
public string Move2 => Get(Strings.movelist, pkm.Move2);
|
||||
public string Move3 => Get(Strings.movelist, pkm.Move3);
|
||||
public string Move4 => Get(Strings.movelist, pkm.Move4);
|
||||
public string HeldItem => GetSpan(Strings.GetItemStrings(pkm.Format), pkm.HeldItem);
|
||||
public string HP => Stats[0].ToString();
|
||||
public string ATK => Stats[1].ToString();
|
||||
public string DEF => Stats[2].ToString();
|
||||
public string SPA => Stats[4].ToString();
|
||||
public string SPD => Stats[5].ToString();
|
||||
public string SPE => Stats[3].ToString();
|
||||
public string MetLoc => pkm.GetLocationString(eggmet: false);
|
||||
public string EggLoc => pkm.GetLocationString(eggmet: true);
|
||||
public string Ball => Get(Strings.balllist, pkm.Ball);
|
||||
public string OT => pkm.OT_Name;
|
||||
public string Version => Get(Strings.gamelist, pkm.Version);
|
||||
public string OTLang => ((LanguageID)pkm.Language).ToString();
|
||||
public string Legal { get { var la = new LegalityAnalysis(pkm); return la.Parsed ? la.Valid.ToString() : "-"; } }
|
||||
|
||||
#region Extraneous
|
||||
public string EC => pkm.EncryptionConstant.ToString("X8");
|
||||
public string PID => pkm.PID.ToString("X8");
|
||||
public int HP_IV => pkm.IV_HP;
|
||||
public int ATK_IV => pkm.IV_ATK;
|
||||
public int DEF_IV => pkm.IV_DEF;
|
||||
public int SPA_IV => pkm.IV_SPA;
|
||||
public int SPD_IV => pkm.IV_SPD;
|
||||
public int SPE_IV => pkm.IV_SPE;
|
||||
public uint EXP => pkm.EXP;
|
||||
public int Level => pkm.CurrentLevel;
|
||||
public int HP_EV => pkm.EV_HP;
|
||||
public int ATK_EV => pkm.EV_ATK;
|
||||
public int DEF_EV => pkm.EV_DEF;
|
||||
public int SPA_EV => pkm.EV_SPA;
|
||||
public int SPD_EV => pkm.EV_SPD;
|
||||
public int SPE_EV => pkm.EV_SPE;
|
||||
public int Cool => pkm is IContestStats s ? s.CNT_Cool : 0;
|
||||
public int Beauty => pkm is IContestStats s ? s.CNT_Beauty : 0;
|
||||
public int Cute => pkm is IContestStats s ? s.CNT_Cute : 0;
|
||||
public int Smart => pkm is IContestStats s ? s.CNT_Smart : 0;
|
||||
public int Tough => pkm is IContestStats s ? s.CNT_Tough : 0;
|
||||
public int Sheen => pkm is IContestStats s ? s.CNT_Sheen : 0;
|
||||
public int Markings => pkm.MarkValue;
|
||||
|
||||
public string NotOT => pkm.Format > 5 ? pkm.HT_Name : "N/A";
|
||||
|
||||
public int AbilityNum => pkm.Format > 5 ? pkm.AbilityNumber : -1;
|
||||
public int GenderFlag => pkm.Gender;
|
||||
public int Form => pkm.Form;
|
||||
public int PKRS_Strain => pkm.PKRS_Strain;
|
||||
public int PKRS_Days => pkm.PKRS_Days;
|
||||
public int MetLevel => pkm.Met_Level;
|
||||
public int OT_Gender => pkm.OT_Gender;
|
||||
|
||||
public bool FatefulFlag => pkm.FatefulEncounter;
|
||||
public bool IsEgg => pkm.IsEgg;
|
||||
public bool IsNicknamed => pkm.IsNicknamed;
|
||||
public bool IsShiny => pkm.IsShiny;
|
||||
|
||||
public int TID => pkm.DisplayTID;
|
||||
public int SID => pkm.DisplaySID;
|
||||
public int TSV => pkm.TSV;
|
||||
public int Move1_PP => pkm.Move1_PP;
|
||||
public int Move2_PP => pkm.Move2_PP;
|
||||
public int Move3_PP => pkm.Move3_PP;
|
||||
public int Move4_PP => pkm.Move4_PP;
|
||||
public int Move1_PPUp => pkm.Move1_PPUps;
|
||||
public int Move2_PPUp => pkm.Move2_PPUps;
|
||||
public int Move3_PPUp => pkm.Move3_PPUps;
|
||||
public int Move4_PPUp => pkm.Move4_PPUps;
|
||||
public string Relearn1 => Get(Strings.movelist, pkm.RelearnMove1);
|
||||
public string Relearn2 => Get(Strings.movelist, pkm.RelearnMove2);
|
||||
public string Relearn3 => Get(Strings.movelist, pkm.RelearnMove3);
|
||||
public string Relearn4 => Get(Strings.movelist, pkm.RelearnMove4);
|
||||
public ushort Checksum => pkm is ISanityChecksum s ? s.Checksum : Checksums.CRC16_CCITT(pkm.Data.AsSpan(pkm.SIZE_STORED));
|
||||
public int Friendship => pkm.OT_Friendship;
|
||||
public int Egg_Year => pkm.EggMetDate.GetValueOrDefault().Year;
|
||||
public int Egg_Month => pkm.EggMetDate.GetValueOrDefault().Month;
|
||||
public int Egg_Day => pkm.EggMetDate.GetValueOrDefault().Day;
|
||||
public int Met_Year => pkm.MetDate.GetValueOrDefault().Year;
|
||||
public int Met_Month => pkm.MetDate.GetValueOrDefault().Month;
|
||||
public int Met_Day => pkm.MetDate.GetValueOrDefault().Day;
|
||||
|
||||
#endregion
|
||||
|
||||
protected EntitySummary(PKM p, GameStrings strings)
|
||||
{
|
||||
pkm = p;
|
||||
Strings = strings;
|
||||
Stats = pkm.GetStats(pkm.PersonalInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely fetches the string from the array.
|
||||
/// </summary>
|
||||
/// <param name="arr">Array of strings</param>
|
||||
/// <param name="val">Index to fetch</param>
|
||||
/// <returns>Null if array is null</returns>
|
||||
private static string Get(IReadOnlyList<string> arr, int val) => (uint)val < arr.Count ? arr[val] : string.Empty;
|
||||
private static string GetSpan(ReadOnlySpan<string> arr, int val) => (uint)val < arr.Length ? arr[val] : string.Empty;
|
||||
pk = p;
|
||||
Strings = strings;
|
||||
Stats = pk.GetStats(pk.PersonalInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely fetches the string from the array.
|
||||
/// </summary>
|
||||
/// <param name="arr">Array of strings</param>
|
||||
/// <param name="val">Index to fetch</param>
|
||||
/// <returns>Null if array is null</returns>
|
||||
private static string Get(IReadOnlyList<string> arr, int val) => (uint)val < arr.Count ? arr[val] : string.Empty;
|
||||
private static string GetSpan(ReadOnlySpan<string> arr, int val) => (uint)val < arr.Length ? arr[val] : string.Empty;
|
||||
}
|
||||
|
|
|
@ -1,78 +1,77 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for filling in template data for <see cref="PKM"/> objects.
|
||||
/// </summary>
|
||||
public static class EntityTemplates
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for filling in template data for <see cref="PKM"/> objects.
|
||||
/// Applies junk data to a <see cref="SaveFile.BlankPKM"/>, which is preferable to a completely empty entity.
|
||||
/// </summary>
|
||||
public static class EntityTemplates
|
||||
/// <param name="pk">Blank data</param>
|
||||
/// <param name="tr">Trainer info to apply</param>
|
||||
public static void TemplateFields(PKM pk, ITrainerInfo tr)
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies junk data to a <see cref="SaveFile.BlankPKM"/>, which is preferable to a completely empty entity.
|
||||
/// </summary>
|
||||
/// <param name="pk">Blank data</param>
|
||||
/// <param name="tr">Trainer info to apply</param>
|
||||
public static void TemplateFields(PKM pk, ITrainerInfo tr)
|
||||
pk.Move1 = (int)Move.Pound;
|
||||
pk.HealPP();
|
||||
pk.Ball = 4;
|
||||
pk.MetDate = DateTime.Today;
|
||||
|
||||
if (tr.Game >= 0)
|
||||
pk.Version = tr.Game;
|
||||
|
||||
pk.Species = GetTemplateSpecies(pk, tr);
|
||||
pk.Language = GetTemplateLanguage(tr);
|
||||
pk.Gender = pk.GetSaneGender();
|
||||
|
||||
pk.ClearNickname();
|
||||
|
||||
pk.OT_Name = tr.OT;
|
||||
pk.OT_Gender = tr.Gender;
|
||||
pk.TID = tr.TID;
|
||||
pk.SID = tr.SID;
|
||||
if (tr is IRegionOrigin o && pk is IRegionOrigin gt)
|
||||
{
|
||||
pk.Move1 = (int)Move.Pound;
|
||||
pk.HealPP();
|
||||
pk.Ball = 4;
|
||||
pk.MetDate = DateTime.Today;
|
||||
|
||||
if (tr.Game >= 0)
|
||||
pk.Version = tr.Game;
|
||||
|
||||
pk.Species = GetTemplateSpecies(pk, tr);
|
||||
pk.Language = GetTemplateLanguage(tr);
|
||||
pk.Gender = pk.GetSaneGender();
|
||||
|
||||
pk.ClearNickname();
|
||||
|
||||
pk.OT_Name = tr.OT;
|
||||
pk.OT_Gender = tr.Gender;
|
||||
pk.TID = tr.TID;
|
||||
pk.SID = tr.SID;
|
||||
if (tr is IRegionOrigin o && pk is IRegionOrigin gt)
|
||||
{
|
||||
gt.ConsoleRegion = o.ConsoleRegion;
|
||||
gt.Country = o.Country;
|
||||
gt.Region = o.Region;
|
||||
}
|
||||
|
||||
ApplyTrashBytes(pk, tr);
|
||||
pk.RefreshChecksum();
|
||||
gt.ConsoleRegion = o.ConsoleRegion;
|
||||
gt.Country = o.Country;
|
||||
gt.Region = o.Region;
|
||||
}
|
||||
|
||||
private static void ApplyTrashBytes(PKM pk, ITrainerInfo tr)
|
||||
{
|
||||
// Copy OT trash bytes for sensitive games (Gen1/2)
|
||||
if (pk is not GBPKM pk12)
|
||||
return;
|
||||
switch (tr)
|
||||
{
|
||||
case SAV1 s1:
|
||||
s1.OT_Trash.CopyTo(pk12.OT_Trash);
|
||||
break;
|
||||
case SAV2 s2:
|
||||
s2.OT_Trash.CopyTo(pk12.OT_Trash);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ApplyTrashBytes(pk, tr);
|
||||
pk.RefreshChecksum();
|
||||
}
|
||||
|
||||
private static int GetTemplateSpecies(PKM pk, ITrainerInfo tr)
|
||||
private static void ApplyTrashBytes(PKM pk, ITrainerInfo tr)
|
||||
{
|
||||
// Copy OT trash bytes for sensitive games (Gen1/2)
|
||||
if (pk is not GBPKM pk12)
|
||||
return;
|
||||
switch (tr)
|
||||
{
|
||||
int species = tr is IGameValueLimit s ? s.MaxSpeciesID : ((GameVersion)pk.Version).GetMaxSpeciesID();
|
||||
if (species <= 0)
|
||||
species = pk.MaxSpeciesID;
|
||||
return species;
|
||||
}
|
||||
|
||||
private static int GetTemplateLanguage(ITrainerInfo tr)
|
||||
{
|
||||
var lang = tr.Language;
|
||||
if (lang <= 0)
|
||||
lang = (int)LanguageID.English;
|
||||
return lang;
|
||||
case SAV1 s1:
|
||||
s1.OT_Trash.CopyTo(pk12.OT_Trash);
|
||||
break;
|
||||
case SAV2 s2:
|
||||
s2.OT_Trash.CopyTo(pk12.OT_Trash);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetTemplateSpecies(PKM pk, ITrainerInfo tr)
|
||||
{
|
||||
int species = tr is IGameValueLimit s ? s.MaxSpeciesID : ((GameVersion)pk.Version).GetMaxSpeciesID();
|
||||
if (species <= 0)
|
||||
species = pk.MaxSpeciesID;
|
||||
return species;
|
||||
}
|
||||
|
||||
private static int GetTemplateLanguage(ITrainerInfo tr)
|
||||
{
|
||||
var lang = tr.Language;
|
||||
if (lang <= 0)
|
||||
lang = (int)LanguageID.English;
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,111 +1,113 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// QR Message reading & writing logic
|
||||
/// </summary>
|
||||
public static class QRMessageUtil
|
||||
{
|
||||
private const string QR6PathBad = "null/#";
|
||||
private const string QR6Path = "http://lunarcookies.github.io/b1s1.html#";
|
||||
private const string QR6PathWC = "http://lunarcookies.github.io/wc.html#";
|
||||
private static string GetExploitURLPrefixPKM(int format) => format == 6 ? QR6Path : QR6PathBad;
|
||||
private static string GetExploitURLPrefixWC(int format) => format == 6 ? QR6PathWC : QR6PathBad;
|
||||
|
||||
/// <summary>
|
||||
/// QR Message reading & writing logic
|
||||
/// Gets the <see cref="PKM"/> data from the message that is encoded in a QR.
|
||||
/// </summary>
|
||||
public static class QRMessageUtil
|
||||
/// <param name="message">QR Message</param>
|
||||
/// <param name="context">Preferred <see cref="PKM.Context"/> to expect.</param>
|
||||
/// <returns>Decoded <see cref="PKM"/> object, null if invalid.</returns>
|
||||
public static PKM? GetPKM(string message, EntityContext context)
|
||||
{
|
||||
private const string QR6PathBad = "null/#";
|
||||
private const string QR6Path = "http://lunarcookies.github.io/b1s1.html#";
|
||||
private const string QR6PathWC = "http://lunarcookies.github.io/wc.html#";
|
||||
private static string GetExploitURLPrefixPKM(int format) => format == 6 ? QR6Path : QR6PathBad;
|
||||
private static string GetExploitURLPrefixWC(int format) => format == 6 ? QR6PathWC : QR6PathBad;
|
||||
var data = DecodeMessagePKM(message);
|
||||
if (data == null)
|
||||
return null;
|
||||
return EntityFormat.GetFromBytes(data, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="PKM"/> data from the message that is encoded in a QR.
|
||||
/// </summary>
|
||||
/// <param name="message">QR Message</param>
|
||||
/// <param name="context">Preferred <see cref="PKM.Context"/> to expect.</param>
|
||||
/// <returns>Decoded <see cref="PKM"/> object, null if invalid.</returns>
|
||||
public static PKM? GetPKM(string message, EntityContext context)
|
||||
/// <summary>
|
||||
/// Gets a QR Message from the input <see cref="PKM"/> data.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to encode</param>
|
||||
/// <returns>QR Message</returns>
|
||||
public static string GetMessage(PKM pk)
|
||||
{
|
||||
if (pk is PK7 pk7)
|
||||
{
|
||||
var data = DecodeMessagePKM(message);
|
||||
if (data == null)
|
||||
byte[] payload = QR7.GenerateQRData(pk7);
|
||||
return GetMessage(payload);
|
||||
}
|
||||
|
||||
var server = GetExploitURLPrefixPKM(pk.Format);
|
||||
var data = pk.EncryptedBoxData;
|
||||
return GetMessageBase64(data, server);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a QR Message from the input <see cref="byte"/> data.
|
||||
/// </summary>
|
||||
/// <param name="payload">Data to encode</param>
|
||||
/// <returns>QR Message</returns>
|
||||
public static string GetMessage(byte[] payload) => string.Concat(payload.Select(z => (char) z));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a QR Message from the input <see cref="MysteryGift"/> data.
|
||||
/// </summary>
|
||||
/// <param name="mg">Gift data to encode</param>
|
||||
/// <returns>QR Message</returns>
|
||||
public static string GetMessage(DataMysteryGift mg)
|
||||
{
|
||||
var server = GetExploitURLPrefixWC(mg.Generation);
|
||||
var data = mg.Write();
|
||||
return GetMessageBase64(data, server);
|
||||
}
|
||||
|
||||
public static string GetMessageBase64(byte[] data, string server)
|
||||
{
|
||||
string payload = Convert.ToBase64String(data);
|
||||
return server + payload;
|
||||
}
|
||||
|
||||
private static byte[]? DecodeMessagePKM(string message)
|
||||
{
|
||||
if (message.Length < 32) // arbitrary length check; everything should be greater than this
|
||||
return null;
|
||||
if (message.StartsWith(QR6PathBad, StringComparison.Ordinal)) // fake url
|
||||
return DecodeMessageDataBase64(message);
|
||||
if (message.StartsWith("http", StringComparison.Ordinal)) // inject url
|
||||
return DecodeMessageDataBase64(message);
|
||||
if (message.StartsWith("POKE", StringComparison.Ordinal) && message.Length > 0x30 + 0xE8) // G7 data
|
||||
return GetBytesFromMessage(message, 0x30, 0xE8);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static byte[]? DecodeMessageDataBase64(string url)
|
||||
{
|
||||
if (url.Length == 0 || url[^1] == '#')
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
int payloadBegin = url.IndexOf('#');
|
||||
if (payloadBegin < 0) // bad URL, need the payload separator
|
||||
return null;
|
||||
return EntityFormat.GetFromBytes(data, context);
|
||||
url = url[(payloadBegin + 1)..]; // Trim URL to right after #
|
||||
return Convert.FromBase64String(url);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a QR Message from the input <see cref="PKM"/> data.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Pokémon to encode</param>
|
||||
/// <returns>QR Message</returns>
|
||||
public static string GetMessage(PKM pkm)
|
||||
catch (FormatException)
|
||||
{
|
||||
if (pkm is PK7 pk7)
|
||||
{
|
||||
byte[] payload = QR7.GenerateQRData(pk7);
|
||||
return GetMessage(payload);
|
||||
}
|
||||
|
||||
var server = GetExploitURLPrefixPKM(pkm.Format);
|
||||
var data = pkm.EncryptedBoxData;
|
||||
return GetMessageBase64(data, server);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a QR Message from the input <see cref="byte"/> data.
|
||||
/// </summary>
|
||||
/// <param name="payload">Data to encode</param>
|
||||
/// <returns>QR Message</returns>
|
||||
public static string GetMessage(byte[] payload) => string.Concat(payload.Select(z => (char) z));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a QR Message from the input <see cref="MysteryGift"/> data.
|
||||
/// </summary>
|
||||
/// <param name="mg">Gift data to encode</param>
|
||||
/// <returns>QR Message</returns>
|
||||
public static string GetMessage(DataMysteryGift mg)
|
||||
{
|
||||
var server = GetExploitURLPrefixWC(mg.Generation);
|
||||
var data = mg.Write();
|
||||
return GetMessageBase64(data, server);
|
||||
}
|
||||
|
||||
public static string GetMessageBase64(byte[] data, string server)
|
||||
{
|
||||
string payload = Convert.ToBase64String(data);
|
||||
return server + payload;
|
||||
}
|
||||
|
||||
private static byte[]? DecodeMessagePKM(string message)
|
||||
{
|
||||
if (message.Length < 32) // arbitrary length check; everything should be greater than this
|
||||
return null;
|
||||
if (message.StartsWith(QR6PathBad)) // fake url
|
||||
return DecodeMessageDataBase64(message);
|
||||
if (message.StartsWith("http")) // inject url
|
||||
return DecodeMessageDataBase64(message);
|
||||
if (message.StartsWith("POKE") && message.Length > 0x30 + 0xE8) // G7 data
|
||||
return GetBytesFromMessage(message, 0x30, 0xE8);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static byte[]? DecodeMessageDataBase64(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
int payloadBegin = url.IndexOf('#');
|
||||
if (payloadBegin < 0) // bad URL, need the payload separator
|
||||
return null;
|
||||
url = url[(payloadBegin + 1)..]; // Trim URL to right after #
|
||||
return Convert.FromBase64String(url);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] GetBytesFromMessage(string seed, int skip, int take)
|
||||
{
|
||||
byte[] data = new byte[take];
|
||||
for (int i = 0; i < take; i++)
|
||||
data[i] = (byte)seed[i + skip];
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] GetBytesFromMessage(string seed, int skip, int take)
|
||||
{
|
||||
byte[] data = new byte[take];
|
||||
for (int i = 0; i < take; i++)
|
||||
data[i] = (byte)seed[i + skip];
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,123 +1,122 @@
|
|||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public sealed class QRPK7 : IEncounterInfo
|
||||
{
|
||||
public sealed class QRPK7 : IEncounterInfo
|
||||
public GameVersion Version => (GameVersion)CassetteVersion;
|
||||
public bool EggEncounter => false;
|
||||
public byte LevelMin => Level;
|
||||
public byte LevelMax => Level;
|
||||
public int Generation => Version.GetGeneration();
|
||||
public bool IsShiny => false;
|
||||
|
||||
private readonly byte[] Data;
|
||||
public const int SIZE = 0x30;
|
||||
public QRPK7(byte[] d) => Data = (byte[])d.Clone();
|
||||
|
||||
public uint EncryptionConstant => ReadUInt32LittleEndian(Data.AsSpan(0));
|
||||
public byte HT_Flags => Data[4];
|
||||
public int Unk_5 => Data[5];
|
||||
public int Unk_6 => Data[6];
|
||||
public int Unk_7 => Data[7];
|
||||
public int Move1_PPUps => Data[8];
|
||||
public int Move2_PPUps => Data[9];
|
||||
public int Move3_PPUps => Data[0xA];
|
||||
public int Move4_PPUps => Data[0xB];
|
||||
public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0xC)); set => WriteUInt32LittleEndian(Data.AsSpan(0xC), value); }
|
||||
public int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 00)) | (uint)((value > 31 ? 31 : value) << 00)); }
|
||||
public int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 05)) | (uint)((value > 31 ? 31 : value) << 05)); }
|
||||
public int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 10)) | (uint)((value > 31 ? 31 : value) << 10)); }
|
||||
public int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 15)) | (uint)((value > 31 ? 31 : value) << 15)); }
|
||||
public int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 20)) | (uint)((value > 31 ? 31 : value) << 20)); }
|
||||
public int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); }
|
||||
public uint PID => ReadUInt32LittleEndian(Data.AsSpan(0x10));
|
||||
public int Species => ReadUInt16LittleEndian(Data.AsSpan(0x14));
|
||||
public ushort HeldItem => ReadUInt16LittleEndian(Data.AsSpan(0x16));
|
||||
public ushort Move1 => ReadUInt16LittleEndian(Data.AsSpan(0x18));
|
||||
public ushort Move2 => ReadUInt16LittleEndian(Data.AsSpan(0x1A));
|
||||
public ushort Move3 => ReadUInt16LittleEndian(Data.AsSpan(0x1C));
|
||||
public ushort Move4 => ReadUInt16LittleEndian(Data.AsSpan(0x1E));
|
||||
public int Unk_20 => Data[0x20];
|
||||
public int AbilityIndex => Data[0x21];
|
||||
public int Nature => Data[0x22];
|
||||
public bool FatefulEncounter => (Data[0x23] & 1) == 1;
|
||||
public int Gender => (Data[0x23] >> 1) & 3;
|
||||
public int Form => Data[0x23] >> 3;
|
||||
public int EV_HP => Data[0x24];
|
||||
public int EV_ATK => Data[0x25];
|
||||
public int EV_DEF => Data[0x26];
|
||||
public int EV_SPE => Data[0x27];
|
||||
public int EV_SPA => Data[0x28];
|
||||
public int EV_SPD => Data[0x29];
|
||||
public int Unk_2A => Data[0x2A];
|
||||
public int Friendship => Data[0x2B];
|
||||
public int Ball => Data[0x2C];
|
||||
public byte Level => Data[0x2D];
|
||||
public int CassetteVersion => Data[0x2E];
|
||||
public int Language => Data[0x2F];
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Data"/> to a rough PKM.
|
||||
/// </summary>
|
||||
public PKM ConvertToPKM(ITrainerInfo tr) => ConvertToPKM(tr, EncounterCriteria.Unrestricted);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Data"/> to a rough PKM.
|
||||
/// </summary>
|
||||
public PKM ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
|
||||
{
|
||||
public GameVersion Version => (GameVersion)CassetteVersion;
|
||||
public bool EggEncounter => false;
|
||||
public byte LevelMin => Level;
|
||||
public byte LevelMax => Level;
|
||||
public int Generation => Version.GetGeneration();
|
||||
public bool IsShiny => false;
|
||||
|
||||
private readonly byte[] Data;
|
||||
public const int SIZE = 0x30;
|
||||
public QRPK7(byte[] d) => Data = (byte[])d.Clone();
|
||||
|
||||
public uint EncryptionConstant => ReadUInt32LittleEndian(Data.AsSpan(0));
|
||||
public byte HT_Flags => Data[4];
|
||||
public int Unk_5 => Data[5];
|
||||
public int Unk_6 => Data[6];
|
||||
public int Unk_7 => Data[7];
|
||||
public int Move1_PPUps => Data[8];
|
||||
public int Move2_PPUps => Data[9];
|
||||
public int Move3_PPUps => Data[0xA];
|
||||
public int Move4_PPUps => Data[0xB];
|
||||
public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0xC)); set => WriteUInt32LittleEndian(Data.AsSpan(0xC), value); }
|
||||
public int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 00)) | (uint)((value > 31 ? 31 : value) << 00)); }
|
||||
public int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 05)) | (uint)((value > 31 ? 31 : value) << 05)); }
|
||||
public int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 10)) | (uint)((value > 31 ? 31 : value) << 10)); }
|
||||
public int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 15)) | (uint)((value > 31 ? 31 : value) << 15)); }
|
||||
public int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 20)) | (uint)((value > 31 ? 31 : value) << 20)); }
|
||||
public int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); }
|
||||
public uint PID => ReadUInt32LittleEndian(Data.AsSpan(0x10));
|
||||
public int Species => ReadUInt16LittleEndian(Data.AsSpan(0x14));
|
||||
public ushort HeldItem => ReadUInt16LittleEndian(Data.AsSpan(0x16));
|
||||
public ushort Move1 => ReadUInt16LittleEndian(Data.AsSpan(0x18));
|
||||
public ushort Move2 => ReadUInt16LittleEndian(Data.AsSpan(0x1A));
|
||||
public ushort Move3 => ReadUInt16LittleEndian(Data.AsSpan(0x1C));
|
||||
public ushort Move4 => ReadUInt16LittleEndian(Data.AsSpan(0x1E));
|
||||
public int Unk_20 => Data[0x20];
|
||||
public int AbilityIndex => Data[0x21];
|
||||
public int Nature => Data[0x22];
|
||||
public bool FatefulEncounter => (Data[0x23] & 1) == 1;
|
||||
public int Gender => (Data[0x23] >> 1) & 3;
|
||||
public int Form => Data[0x23] >> 3;
|
||||
public int EV_HP => Data[0x24];
|
||||
public int EV_ATK => Data[0x25];
|
||||
public int EV_DEF => Data[0x26];
|
||||
public int EV_SPE => Data[0x27];
|
||||
public int EV_SPA => Data[0x28];
|
||||
public int EV_SPD => Data[0x29];
|
||||
public int Unk_2A => Data[0x2A];
|
||||
public int Friendship => Data[0x2B];
|
||||
public int Ball => Data[0x2C];
|
||||
public byte Level => Data[0x2D];
|
||||
public int CassetteVersion => Data[0x2E];
|
||||
public int Language => Data[0x2F];
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Data"/> to a rough PKM.
|
||||
/// </summary>
|
||||
public PKM ConvertToPKM(ITrainerInfo sav) => ConvertToPKM(sav, EncounterCriteria.Unrestricted);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Data"/> to a rough PKM.
|
||||
/// </summary>
|
||||
public PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria)
|
||||
var pk = new PK7
|
||||
{
|
||||
var pk = new PK7
|
||||
{
|
||||
EncryptionConstant = EncryptionConstant,
|
||||
PID = PID,
|
||||
Language = Language,
|
||||
Species = Species,
|
||||
Gender = Gender,
|
||||
Nature = Nature,
|
||||
FatefulEncounter = FatefulEncounter,
|
||||
Form = Form,
|
||||
HyperTrainFlags = HT_Flags,
|
||||
IV_HP = IV_HP,
|
||||
IV_ATK = IV_ATK,
|
||||
IV_DEF = IV_DEF,
|
||||
IV_SPA = IV_SPA,
|
||||
IV_SPD = IV_SPD,
|
||||
IV_SPE = IV_SPE,
|
||||
EV_HP = EV_HP,
|
||||
EV_ATK = EV_ATK,
|
||||
EV_DEF = EV_DEF,
|
||||
EV_SPA = EV_SPA,
|
||||
EV_SPD = EV_SPD,
|
||||
EV_SPE = EV_SPE,
|
||||
Move1 = Move1,
|
||||
Move2 = Move2,
|
||||
Move3 = Move3,
|
||||
Move4 = Move4,
|
||||
Move1_PPUps = Move1_PPUps,
|
||||
Move2_PPUps = Move2_PPUps,
|
||||
Move3_PPUps = Move3_PPUps,
|
||||
Move4_PPUps = Move4_PPUps,
|
||||
HeldItem = HeldItem,
|
||||
HT_Friendship = Friendship,
|
||||
OT_Friendship = Friendship,
|
||||
Ball = Ball,
|
||||
Version = CassetteVersion,
|
||||
EncryptionConstant = EncryptionConstant,
|
||||
PID = PID,
|
||||
Language = Language,
|
||||
Species = Species,
|
||||
Gender = Gender,
|
||||
Nature = Nature,
|
||||
FatefulEncounter = FatefulEncounter,
|
||||
Form = Form,
|
||||
HyperTrainFlags = HT_Flags,
|
||||
IV_HP = IV_HP,
|
||||
IV_ATK = IV_ATK,
|
||||
IV_DEF = IV_DEF,
|
||||
IV_SPA = IV_SPA,
|
||||
IV_SPD = IV_SPD,
|
||||
IV_SPE = IV_SPE,
|
||||
EV_HP = EV_HP,
|
||||
EV_ATK = EV_ATK,
|
||||
EV_DEF = EV_DEF,
|
||||
EV_SPA = EV_SPA,
|
||||
EV_SPD = EV_SPD,
|
||||
EV_SPE = EV_SPE,
|
||||
Move1 = Move1,
|
||||
Move2 = Move2,
|
||||
Move3 = Move3,
|
||||
Move4 = Move4,
|
||||
Move1_PPUps = Move1_PPUps,
|
||||
Move2_PPUps = Move2_PPUps,
|
||||
Move3_PPUps = Move3_PPUps,
|
||||
Move4_PPUps = Move4_PPUps,
|
||||
HeldItem = HeldItem,
|
||||
HT_Friendship = Friendship,
|
||||
OT_Friendship = Friendship,
|
||||
Ball = Ball,
|
||||
Version = CassetteVersion,
|
||||
|
||||
OT_Name = sav.OT,
|
||||
HT_Name = sav.OT,
|
||||
CurrentLevel = Level,
|
||||
Met_Level = Level,
|
||||
MetDate = DateTime.Now,
|
||||
};
|
||||
RecentTrainerCache.SetConsoleRegionData3DS(pk, sav);
|
||||
OT_Name = tr.OT,
|
||||
HT_Name = tr.OT,
|
||||
CurrentLevel = Level,
|
||||
Met_Level = Level,
|
||||
MetDate = DateTime.Now,
|
||||
};
|
||||
RecentTrainerCache.SetConsoleRegionData3DS(pk, tr);
|
||||
|
||||
pk.RefreshAbility(AbilityIndex >> 1);
|
||||
pk.ForcePartyData();
|
||||
pk.RefreshAbility(AbilityIndex >> 1);
|
||||
pk.ForcePartyData();
|
||||
|
||||
pk.RefreshChecksum();
|
||||
return pk;
|
||||
}
|
||||
pk.RefreshChecksum();
|
||||
return pk;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,60 +1,59 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public static class QRPKM
|
||||
{
|
||||
public static class QRPKM
|
||||
/// <summary>
|
||||
/// Summarizes the details of a <see cref="PKM"/> into multiple lines for display in an image.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to generate details for.</param>
|
||||
/// <returns>Lines representing a Header, Moves, and IVs/EVs</returns>
|
||||
public static string[] GetQRLines(this PKM pk)
|
||||
{
|
||||
/// <summary>
|
||||
/// Summarizes the details of a <see cref="PKM"/> into multiple lines for display in an image.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Pokémon to generate details for.</param>
|
||||
/// <returns>Lines representing a Header, Moves, and IVs/EVs</returns>
|
||||
public static string[] GetQRLines(this PKM pkm)
|
||||
var s = GameInfo.Strings;
|
||||
|
||||
var header = GetHeader(pk, s);
|
||||
string moves = string.Join(" / ", pk.Moves.Select(move => move < s.movelist.Length ? s.movelist[move] : "ERROR"));
|
||||
string IVs = $"IVs: {pk.IV_HP:00}/{pk.IV_ATK:00}/{pk.IV_DEF:00}/{pk.IV_SPA:00}/{pk.IV_SPD:00}/{pk.IV_SPE:00}";
|
||||
string EVs = $"EVs: {pk.EV_HP:00}/{pk.EV_ATK:00}/{pk.EV_DEF:00}/{pk.EV_SPA:00}/{pk.EV_SPD:00}/{pk.EV_SPE:00}";
|
||||
|
||||
return new[]
|
||||
{
|
||||
var s = GameInfo.Strings;
|
||||
string.Join(" ", header),
|
||||
moves,
|
||||
IVs + " " + EVs,
|
||||
};
|
||||
}
|
||||
|
||||
var header = GetHeader(pkm, s);
|
||||
string moves = string.Join(" / ", pkm.Moves.Select(move => move < s.movelist.Length ? s.movelist[move] : "ERROR"));
|
||||
string IVs = $"IVs: {pkm.IV_HP:00}/{pkm.IV_ATK:00}/{pkm.IV_DEF:00}/{pkm.IV_SPA:00}/{pkm.IV_SPD:00}/{pkm.IV_SPE:00}";
|
||||
string EVs = $"EVs: {pkm.EV_HP:00}/{pkm.EV_ATK:00}/{pkm.EV_DEF:00}/{pkm.EV_SPA:00}/{pkm.EV_SPD:00}/{pkm.EV_SPE:00}";
|
||||
private static IEnumerable<string> GetHeader(PKM pk, GameStrings s)
|
||||
{
|
||||
string filename = pk.Nickname;
|
||||
if ((uint) pk.Species < s.Species.Count)
|
||||
{
|
||||
var name = s.Species[pk.Species];
|
||||
if (pk.Nickname != name)
|
||||
filename += $" ({name})";
|
||||
}
|
||||
yield return filename;
|
||||
|
||||
return new[]
|
||||
{
|
||||
string.Join(" ", header),
|
||||
moves,
|
||||
IVs + " " + EVs,
|
||||
};
|
||||
if (pk.Format >= 3 && (uint)pk.Ability < s.Ability.Count)
|
||||
yield return $"[{s.Ability[pk.Ability]}]";
|
||||
|
||||
var level = pk.Stat_Level;
|
||||
if (level == 0)
|
||||
level = pk.CurrentLevel;
|
||||
yield return $"lv{level}";
|
||||
|
||||
if (pk.HeldItem > 0)
|
||||
{
|
||||
var items = s.GetItemStrings(pk.Format);
|
||||
if ((uint)pk.HeldItem < items.Length)
|
||||
yield return $" @ {items[pk.HeldItem]}";
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetHeader(PKM pkm, GameStrings s)
|
||||
{
|
||||
string filename = pkm.Nickname;
|
||||
if ((uint) pkm.Species < s.Species.Count)
|
||||
{
|
||||
var name = s.Species[pkm.Species];
|
||||
if (pkm.Nickname != name)
|
||||
filename += $" ({name})";
|
||||
}
|
||||
yield return filename;
|
||||
|
||||
if (pkm.Format >= 3 && (uint)pkm.Ability < s.Ability.Count)
|
||||
yield return $"[{s.Ability[pkm.Ability]}]";
|
||||
|
||||
var level = pkm.Stat_Level;
|
||||
if (level == 0)
|
||||
level = pkm.CurrentLevel;
|
||||
yield return $"lv{level}";
|
||||
|
||||
if (pkm.HeldItem > 0)
|
||||
{
|
||||
var items = s.GetItemStrings(pkm.Format);
|
||||
if ((uint)pkm.HeldItem < items.Length)
|
||||
yield return $" @ {items[pkm.HeldItem]}";
|
||||
}
|
||||
|
||||
if (pkm.Format >= 3 && (uint)pkm.Nature < s.Natures.Count)
|
||||
yield return s.natures[pkm.Nature];
|
||||
}
|
||||
if (pk.Format >= 3 && (uint)pk.Nature < s.Natures.Count)
|
||||
yield return s.natures[pk.Nature];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Option to load a save file automatically to an editing environment.
|
||||
/// </summary>
|
||||
public enum AutoLoadSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// Option to load a save file automatically to an editing environment.
|
||||
/// Doesn't auto load a save file, and instead uses a fake save file data.
|
||||
/// </summary>
|
||||
public enum AutoLoadSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// Doesn't auto load a save file, and instead uses a fake save file data.
|
||||
/// </summary>
|
||||
Disabled,
|
||||
Disabled,
|
||||
|
||||
/// <summary>
|
||||
/// Loads the most recently created Save File in the usual backup locations.
|
||||
/// </summary>
|
||||
RecentBackup,
|
||||
/// <summary>
|
||||
/// Loads the most recently created Save File in the usual backup locations.
|
||||
/// </summary>
|
||||
RecentBackup,
|
||||
|
||||
/// <summary>
|
||||
/// Loads the most recently opened Save File path.
|
||||
/// </summary>
|
||||
LastLoaded,
|
||||
}
|
||||
/// <summary>
|
||||
/// Loads the most recently opened Save File path.
|
||||
/// </summary>
|
||||
LastLoaded,
|
||||
}
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Settings used for starting up an editing environment.
|
||||
/// </summary>
|
||||
public interface IStartupSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings used for starting up an editing environment.
|
||||
/// Save File version to start the environment with if a preexisting save file has not been chosen.
|
||||
/// </summary>
|
||||
public interface IStartupSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Save File version to start the environment with if a preexisting save file has not been chosen.
|
||||
/// </summary>
|
||||
GameVersion DefaultSaveVersion { get; }
|
||||
GameVersion DefaultSaveVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Method to load the environment's initial save file.
|
||||
/// </summary>
|
||||
AutoLoadSetting AutoLoadSaveOnStartup { get; }
|
||||
/// <summary>
|
||||
/// Method to load the environment's initial save file.
|
||||
/// </summary>
|
||||
AutoLoadSetting AutoLoadSaveOnStartup { get; }
|
||||
|
||||
/// <summary>
|
||||
/// List of recently loaded save file paths.
|
||||
/// </summary>
|
||||
List<string> RecentlyLoaded { get; }
|
||||
}
|
||||
/// <summary>
|
||||
/// List of recently loaded save file paths.
|
||||
/// </summary>
|
||||
List<string> RecentlyLoaded { get; }
|
||||
}
|
||||
|
|
|
@ -3,126 +3,123 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic object that assembles parameters used for starting up an editing environment.
|
||||
/// </summary>
|
||||
public sealed class StartupArguments
|
||||
{
|
||||
public PKM? Entity { get; private set; }
|
||||
public SaveFile? SAV { get; private set; }
|
||||
|
||||
// ReSharper disable once UnassignedGetOnlyAutoProperty
|
||||
public Exception? Error { get; }
|
||||
// ReSharper disable once CollectionNeverQueried.Global
|
||||
public readonly List<object> Extra = new();
|
||||
|
||||
/// <summary>
|
||||
/// Logic object that assembles parameters used for starting up an editing environment.
|
||||
/// Step 1: Reads in command line arguments.
|
||||
/// </summary>
|
||||
public sealed class StartupArguments
|
||||
public void ReadArguments(IEnumerable<string> args)
|
||||
{
|
||||
public PKM? Entity { get; private set; }
|
||||
public SaveFile? SAV { get; private set; }
|
||||
|
||||
// ReSharper disable once UnassignedGetOnlyAutoProperty
|
||||
public Exception? Error { get; }
|
||||
// ReSharper disable once CollectionNeverQueried.Global
|
||||
public readonly List<object> Extra = new();
|
||||
|
||||
/// <summary>
|
||||
/// Step 1: Reads in command line arguments.
|
||||
/// </summary>
|
||||
public void ReadArguments(IEnumerable<string> args)
|
||||
foreach (string path in args)
|
||||
{
|
||||
foreach (string path in args)
|
||||
var other = FileUtil.GetSupportedFile(path, SAV);
|
||||
if (other is SaveFile s)
|
||||
{
|
||||
var other = FileUtil.GetSupportedFile(path, SAV);
|
||||
if (other is SaveFile s)
|
||||
{
|
||||
s.Metadata.SetExtraInfo(path);
|
||||
SAV = s;
|
||||
}
|
||||
else if (other is PKM pkm)
|
||||
{
|
||||
Entity = pkm;
|
||||
}
|
||||
else if (other is not null)
|
||||
{
|
||||
Extra.Add(other);
|
||||
}
|
||||
s.Metadata.SetExtraInfo(path);
|
||||
SAV = s;
|
||||
}
|
||||
else if (other is PKM pk)
|
||||
{
|
||||
Entity = pk;
|
||||
}
|
||||
else if (other is not null)
|
||||
{
|
||||
Extra.Add(other);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Step 2: Reads settings config.
|
||||
/// </summary>
|
||||
public void ReadSettings(IStartupSettings startup)
|
||||
{
|
||||
if (SAV is not null)
|
||||
return;
|
||||
|
||||
if (Entity is { } x)
|
||||
SAV = ReadSettingsDefinedPKM(startup, x) ?? GetBlank(x);
|
||||
else
|
||||
SAV = ReadSettingsAnyPKM(startup) ?? GetBlankSaveFile(startup.DefaultSaveVersion, SAV);
|
||||
}
|
||||
|
||||
// step 3
|
||||
public void ReadTemplateIfNoEntity(string path)
|
||||
{
|
||||
if (Entity is not null)
|
||||
return;
|
||||
|
||||
var sav = SAV;
|
||||
if (sav is null)
|
||||
throw new NullReferenceException(nameof(sav));
|
||||
|
||||
var pk = sav.LoadTemplate(path);
|
||||
var isBlank = pk.Data.SequenceEqual(sav.BlankPKM.Data);
|
||||
if (isBlank)
|
||||
EntityTemplates.TemplateFields(pk, sav);
|
||||
|
||||
Entity = pk;
|
||||
}
|
||||
|
||||
private static SaveFile? ReadSettingsDefinedPKM(IStartupSettings startup, PKM pkm) => startup.AutoLoadSaveOnStartup switch
|
||||
{
|
||||
AutoLoadSetting.RecentBackup => SaveFinder.DetectSaveFiles().FirstOrDefault(z => z.IsCompatiblePKM(pkm)),
|
||||
AutoLoadSetting.LastLoaded => GetMostRecentlyLoaded(startup.RecentlyLoaded).FirstOrDefault(z => z.IsCompatiblePKM(pkm)),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
private static SaveFile? ReadSettingsAnyPKM(IStartupSettings startup) => startup.AutoLoadSaveOnStartup switch
|
||||
{
|
||||
AutoLoadSetting.RecentBackup => SaveFinder.DetectSaveFiles().FirstOrDefault(),
|
||||
AutoLoadSetting.LastLoaded => GetMostRecentlyLoaded(startup.RecentlyLoaded).FirstOrDefault(),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
#region Utility
|
||||
private static SaveFile GetBlank(PKM pk)
|
||||
{
|
||||
var ctx = pk.Context;
|
||||
var ver = ctx.GetSingleGameVersion();
|
||||
if (pk is { Format: 1, Japanese: true })
|
||||
ver = GameVersion.BU;
|
||||
|
||||
return SaveUtil.GetBlankSAV(ver, pk.OT_Name, (LanguageID)pk.Language);
|
||||
}
|
||||
|
||||
private static SaveFile GetBlankSaveFile(GameVersion version, SaveFile? current)
|
||||
{
|
||||
var lang = SaveUtil.GetSafeLanguage(current);
|
||||
var tr = SaveUtil.GetSafeTrainerName(current, lang);
|
||||
var sav = SaveUtil.GetBlankSAV(version, tr, lang);
|
||||
if (sav.Version == GameVersion.Invalid) // will fail to load
|
||||
sav = SaveUtil.GetBlankSAV((GameVersion)GameInfo.VersionDataSource.Max(z => z.Value), tr, lang);
|
||||
return sav;
|
||||
}
|
||||
|
||||
private static IEnumerable<SaveFile> GetMostRecentlyLoaded(IEnumerable<string> paths)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
continue;
|
||||
|
||||
var sav = SaveUtil.GetVariantSAV(path);
|
||||
if (sav is null)
|
||||
continue;
|
||||
|
||||
yield return sav;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Step 2: Reads settings config.
|
||||
/// </summary>
|
||||
public void ReadSettings(IStartupSettings startup)
|
||||
{
|
||||
if (SAV is not null)
|
||||
return;
|
||||
|
||||
if (Entity is { } x)
|
||||
SAV = ReadSettingsDefinedPKM(startup, x) ?? GetBlank(x);
|
||||
else
|
||||
SAV = ReadSettingsAnyPKM(startup) ?? GetBlankSaveFile(startup.DefaultSaveVersion, SAV);
|
||||
}
|
||||
|
||||
// step 3
|
||||
public void ReadTemplateIfNoEntity(string path)
|
||||
{
|
||||
if (Entity is not null)
|
||||
return;
|
||||
if (SAV is not { } sav)
|
||||
return;
|
||||
|
||||
var pk = sav.LoadTemplate(path);
|
||||
var isBlank = pk.Data.SequenceEqual(sav.BlankPKM.Data);
|
||||
if (isBlank)
|
||||
EntityTemplates.TemplateFields(pk, sav);
|
||||
|
||||
Entity = pk;
|
||||
}
|
||||
|
||||
private static SaveFile? ReadSettingsDefinedPKM(IStartupSettings startup, PKM pk) => startup.AutoLoadSaveOnStartup switch
|
||||
{
|
||||
AutoLoadSetting.RecentBackup => SaveFinder.DetectSaveFiles().FirstOrDefault(z => z.IsCompatiblePKM(pk)),
|
||||
AutoLoadSetting.LastLoaded => GetMostRecentlyLoaded(startup.RecentlyLoaded).FirstOrDefault(z => z.IsCompatiblePKM(pk)),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
private static SaveFile? ReadSettingsAnyPKM(IStartupSettings startup) => startup.AutoLoadSaveOnStartup switch
|
||||
{
|
||||
AutoLoadSetting.RecentBackup => SaveFinder.DetectSaveFiles().FirstOrDefault(),
|
||||
AutoLoadSetting.LastLoaded => GetMostRecentlyLoaded(startup.RecentlyLoaded).FirstOrDefault(),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
#region Utility
|
||||
private static SaveFile GetBlank(PKM pk)
|
||||
{
|
||||
var ctx = pk.Context;
|
||||
var ver = ctx.GetSingleGameVersion();
|
||||
if (pk is { Format: 1, Japanese: true })
|
||||
ver = GameVersion.BU;
|
||||
|
||||
return SaveUtil.GetBlankSAV(ver, pk.OT_Name, (LanguageID)pk.Language);
|
||||
}
|
||||
|
||||
private static SaveFile GetBlankSaveFile(GameVersion version, SaveFile? current)
|
||||
{
|
||||
var lang = SaveUtil.GetSafeLanguage(current);
|
||||
var tr = SaveUtil.GetSafeTrainerName(current, lang);
|
||||
var sav = SaveUtil.GetBlankSAV(version, tr, lang);
|
||||
if (sav.Version == GameVersion.Invalid) // will fail to load
|
||||
sav = SaveUtil.GetBlankSAV((GameVersion)GameInfo.VersionDataSource.Max(z => z.Value), tr, lang);
|
||||
return sav;
|
||||
}
|
||||
|
||||
private static IEnumerable<SaveFile> GetMostRecentlyLoaded(IEnumerable<string> paths)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
continue;
|
||||
|
||||
var sav = SaveUtil.GetVariantSAV(path);
|
||||
if (sav is null)
|
||||
continue;
|
||||
|
||||
yield return sav;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -20,4 +20,4 @@ public sealed class BoxManipModify : BoxManipBase
|
|||
var (start, stop, _) = param;
|
||||
return sav.ModifyBoxes(Action, start, stop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,4 +25,4 @@ public sealed class BoxManipSortComplex : BoxManipBase
|
|||
var (start, stop, reverse) = param;
|
||||
return sav.SortBoxes(start, stop, Method, reverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,4 +46,4 @@ public enum BoxManipType
|
|||
ModifyRemoveNicknames,
|
||||
ModifyRemoveItem,
|
||||
ModifyHeal,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,64 +1,63 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public static class BoxManipUtil
|
||||
{
|
||||
public static class BoxManipUtil
|
||||
/// <summary>
|
||||
/// Grouped categories of different <see cref="IBoxManip"/>.
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyList<IBoxManip>[] ManipCategories =
|
||||
{
|
||||
/// <summary>
|
||||
/// Grouped categories of different <see cref="IBoxManip"/>.
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyList<IBoxManip>[] ManipCategories =
|
||||
{
|
||||
BoxManipDefaults.ClearCommon,
|
||||
BoxManipDefaults.SortCommon,
|
||||
BoxManipDefaults.SortAdvanced,
|
||||
BoxManipDefaults.ModifyCommon,
|
||||
};
|
||||
BoxManipDefaults.ClearCommon,
|
||||
BoxManipDefaults.SortCommon,
|
||||
BoxManipDefaults.SortAdvanced,
|
||||
BoxManipDefaults.ModifyCommon,
|
||||
};
|
||||
|
||||
public static readonly string[] ManipCategoryNames =
|
||||
{
|
||||
"Delete",
|
||||
"Sort",
|
||||
"SortAdvanced",
|
||||
"Modify",
|
||||
};
|
||||
public static readonly string[] ManipCategoryNames =
|
||||
{
|
||||
"Delete",
|
||||
"Sort",
|
||||
"SortAdvanced",
|
||||
"Modify",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IBoxManip"/> reference that carries out the action of the requested <see cref="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">Manipulation type.</param>
|
||||
/// <returns>Reference to <see cref="IBoxManip"/>.</returns>
|
||||
public static IBoxManip GetManip(this BoxManipType type) => ManipCategories.SelectMany(c => c).First(m => m.Type == type);
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IBoxManip"/> reference that carries out the action of the requested <see cref="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">Manipulation type.</param>
|
||||
/// <returns>Reference to <see cref="IBoxManip"/>.</returns>
|
||||
public static IBoxManip GetManip(this BoxManipType type) => ManipCategories.SelectMany(c => c).First(m => m.Type == type);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding name from <see cref="ManipCategoryNames"/> for the requested <see cref="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">Manipulation type.</param>
|
||||
/// <returns>Category Name</returns>
|
||||
public static string? GetManipCategoryName(this BoxManipType type)
|
||||
/// <summary>
|
||||
/// Gets the corresponding name from <see cref="ManipCategoryNames"/> for the requested <see cref="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">Manipulation type.</param>
|
||||
/// <returns>Category Name</returns>
|
||||
public static string? GetManipCategoryName(this BoxManipType type)
|
||||
{
|
||||
for (int i = 0; i < ManipCategories.Length; i++)
|
||||
{
|
||||
for (int i = 0; i < ManipCategories.Length; i++)
|
||||
{
|
||||
if (ManipCategories[i].Any(z => z.Type == type))
|
||||
return ManipCategoryNames[i];
|
||||
}
|
||||
return null;
|
||||
if (ManipCategories[i].Any(z => z.Type == type))
|
||||
return ManipCategoryNames[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding name from <see cref="ManipCategoryNames"/> for the requested <see cref="manip"/>.
|
||||
/// </summary>
|
||||
/// <param name="manip">Manipulation type.</param>
|
||||
/// <returns>Category Name</returns>
|
||||
public static string? GetManipCategoryName(this IBoxManip manip)
|
||||
/// <summary>
|
||||
/// Gets the corresponding name from <see cref="ManipCategoryNames"/> for the requested <see cref="manip"/>.
|
||||
/// </summary>
|
||||
/// <param name="manip">Manipulation type.</param>
|
||||
/// <returns>Category Name</returns>
|
||||
public static string? GetManipCategoryName(this IBoxManip manip)
|
||||
{
|
||||
for (int i = 0; i < ManipCategories.Length; i++)
|
||||
{
|
||||
for (int i = 0; i < ManipCategories.Length; i++)
|
||||
{
|
||||
if (ManipCategories[i].Any(z => z == manip))
|
||||
return ManipCategoryNames[i];
|
||||
}
|
||||
return null;
|
||||
if (ManipCategories[i].Any(z => z == manip))
|
||||
return ManipCategoryNames[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,73 +1,71 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Manipulates boxes of a <see cref="SaveFile"/>.
|
||||
/// </summary>
|
||||
public abstract class BoxManipulator
|
||||
{
|
||||
protected abstract SaveFile SAV { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Manipulates boxes of a <see cref="SaveFile"/>.
|
||||
/// Executes the provided <see cref="manip"/> with the provided parameters.
|
||||
/// </summary>
|
||||
public abstract class BoxManipulator
|
||||
/// <param name="manip">Manipulation to perform on the <see cref="SAV"/> box data.</param>
|
||||
/// <param name="box">Single box to modify; if <see cref="allBoxes"/> is set, this param is ignored.</param>
|
||||
/// <param name="allBoxes">Indicates if all boxes are to be manipulated, or just one box.</param>
|
||||
/// <param name="reverse">Manipulation action should be inverted (criteria) or reversed (sort).</param>
|
||||
/// <returns>True if operation succeeded, false if no changes made.</returns>
|
||||
public bool Execute(IBoxManip manip, int box, bool allBoxes, bool reverse = false)
|
||||
{
|
||||
protected abstract SaveFile SAV { get; }
|
||||
bool usable = manip.Usable.Invoke(SAV);
|
||||
if (!usable)
|
||||
return false;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the provided <see cref="manip"/> with the provided parameters.
|
||||
/// </summary>
|
||||
/// <param name="manip">Manipulation to perform on the <see cref="SAV"/> box data.</param>
|
||||
/// <param name="box">Single box to modify; if <see cref="allBoxes"/> is set, this param is ignored.</param>
|
||||
/// <param name="allBoxes">Indicates if all boxes are to be manipulated, or just one box.</param>
|
||||
/// <param name="reverse">Manipulation action should be inverted (criteria) or reversed (sort).</param>
|
||||
/// <returns>True if operation succeeded, false if no changes made.</returns>
|
||||
public bool Execute(IBoxManip manip, int box, bool allBoxes, bool reverse = false)
|
||||
{
|
||||
bool usable = manip.Usable.Invoke(SAV);
|
||||
if (!usable)
|
||||
return false;
|
||||
var start = allBoxes ? 0 : box;
|
||||
var stop = allBoxes ? SAV.BoxCount - 1 : box;
|
||||
var param = new BoxManipParam(start, stop, reverse);
|
||||
|
||||
var start = allBoxes ? 0 : box;
|
||||
var stop = allBoxes ? SAV.BoxCount - 1 : box;
|
||||
var param = new BoxManipParam(start, stop, reverse);
|
||||
var prompt = manip.GetPrompt(allBoxes);
|
||||
var fail = manip.GetFail(allBoxes);
|
||||
if (!CanManipulateRegion(param.Start, param.Stop, prompt, fail))
|
||||
return false;
|
||||
|
||||
var prompt = manip.GetPrompt(allBoxes);
|
||||
var fail = manip.GetFail(allBoxes);
|
||||
if (!CanManipulateRegion(param.Start, param.Stop, prompt, fail))
|
||||
return false;
|
||||
|
||||
var result = manip.Execute(SAV, param);
|
||||
if (result <= 0)
|
||||
return false;
|
||||
var success = manip.GetSuccess(allBoxes);
|
||||
FinishBoxManipulation(success, allBoxes, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the provided <see cref="type"/> with the provided parameters.
|
||||
/// </summary>
|
||||
/// <param name="type">Manipulation to perform on the <see cref="SAV"/> box data.</param>
|
||||
/// <param name="box">Single box to modify; if <see cref="allBoxes"/> is set, this param is ignored.</param>
|
||||
/// <param name="allBoxes">Indicates if all boxes are to be manipulated, or just one box.</param>
|
||||
/// <param name="reverse">Manipulation action should be inverted (criteria) or reversed (sort).</param>
|
||||
/// <returns>True if operation succeeded, false if no changes made.</returns>
|
||||
public bool Execute(BoxManipType type, int box, bool allBoxes, bool reverse = false)
|
||||
{
|
||||
var manip = type.GetManip();
|
||||
return Execute(manip, box, allBoxes, reverse);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sanity check for modifying the box data.
|
||||
/// </summary>
|
||||
/// <param name="start">Start box</param>
|
||||
/// <param name="end">End box</param>
|
||||
/// <param name="prompt">Message asking about the operation.</param>
|
||||
/// <param name="fail">Message indicating the operation cannot be performed.</param>
|
||||
/// <returns></returns>
|
||||
protected virtual bool CanManipulateRegion(int start, int end, string prompt, string fail) => !SAV.IsAnySlotLockedInBox(start, end);
|
||||
|
||||
/// <summary>
|
||||
/// Called if the <see cref="IBoxManip"/> operation modified any data.
|
||||
/// </summary>
|
||||
/// <param name="message">Optional message to show if applicable.</param>
|
||||
/// <param name="all">Indicates if all boxes were manipulated, or just one box.</param>
|
||||
/// <param name="count">Count of manipulated slots</param>
|
||||
protected abstract void FinishBoxManipulation(string message, bool all, int count);
|
||||
var result = manip.Execute(SAV, param);
|
||||
if (result <= 0)
|
||||
return false;
|
||||
var success = manip.GetSuccess(allBoxes);
|
||||
FinishBoxManipulation(success, allBoxes, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the provided <see cref="type"/> with the provided parameters.
|
||||
/// </summary>
|
||||
/// <param name="type">Manipulation to perform on the <see cref="SAV"/> box data.</param>
|
||||
/// <param name="box">Single box to modify; if <see cref="allBoxes"/> is set, this param is ignored.</param>
|
||||
/// <param name="allBoxes">Indicates if all boxes are to be manipulated, or just one box.</param>
|
||||
/// <param name="reverse">Manipulation action should be inverted (criteria) or reversed (sort).</param>
|
||||
/// <returns>True if operation succeeded, false if no changes made.</returns>
|
||||
public bool Execute(BoxManipType type, int box, bool allBoxes, bool reverse = false)
|
||||
{
|
||||
var manip = type.GetManip();
|
||||
return Execute(manip, box, allBoxes, reverse);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sanity check for modifying the box data.
|
||||
/// </summary>
|
||||
/// <param name="start">Start box</param>
|
||||
/// <param name="end">End box</param>
|
||||
/// <param name="prompt">Message asking about the operation.</param>
|
||||
/// <param name="fail">Message indicating the operation cannot be performed.</param>
|
||||
protected virtual bool CanManipulateRegion(int start, int end, string prompt, string fail) => !SAV.IsAnySlotLockedInBox(start, end);
|
||||
|
||||
/// <summary>
|
||||
/// Called if the <see cref="IBoxManip"/> operation modified any data.
|
||||
/// </summary>
|
||||
/// <param name="message">Optional message to show if applicable.</param>
|
||||
/// <param name="all">Indicates if all boxes were manipulated, or just one box.</param>
|
||||
/// <param name="count">Count of manipulated slots</param>
|
||||
protected abstract void FinishBoxManipulation(string message, bool all, int count);
|
||||
}
|
||||
|
|
|
@ -1,279 +1,278 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public sealed class EventLabelCollection
|
||||
{
|
||||
public sealed class EventLabelCollection
|
||||
public readonly IReadOnlyList<NamedEventWork> Work;
|
||||
public readonly IReadOnlyList<NamedEventValue> Flag;
|
||||
|
||||
public EventLabelCollection(string game, int maxFlag = int.MaxValue, int maxValue = int.MaxValue)
|
||||
{
|
||||
public readonly IReadOnlyList<NamedEventWork> Work;
|
||||
public readonly IReadOnlyList<NamedEventValue> Flag;
|
||||
|
||||
public EventLabelCollection(string game, int maxFlag = int.MaxValue, int maxValue = int.MaxValue)
|
||||
{
|
||||
var f = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "flags");
|
||||
var c = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "const");
|
||||
Flag = GetFlags(f, maxFlag);
|
||||
Work = GetValues(c, maxValue);
|
||||
}
|
||||
|
||||
private static List<NamedEventValue> GetFlags(IReadOnlyCollection<string> strings, int maxValue)
|
||||
{
|
||||
var result = new List<NamedEventValue>(strings.Count);
|
||||
var processed = new HashSet<int>();
|
||||
foreach (var s in strings)
|
||||
{
|
||||
var split = s.Split('\t');
|
||||
if (split.Length != 3)
|
||||
continue;
|
||||
|
||||
var index = TryParseHexDec(split[0]);
|
||||
if (index >= maxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
if (processed.Contains(index))
|
||||
throw new ArgumentException("Already have an entry for this!", nameof(index));
|
||||
|
||||
var type = GetEventType(split[1]);
|
||||
var desc = split[2];
|
||||
|
||||
var item = new NamedEventValue(desc, index, type);
|
||||
result.Add(item);
|
||||
processed.Add(index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static readonly NamedEventConst Custom = new("Custom", NamedEventConst.CustomMagicValue);
|
||||
private static readonly NamedEventConst[] Empty = {Custom};
|
||||
|
||||
private static IReadOnlyList<NamedEventWork> GetValues(IReadOnlyCollection<string> strings, int maxValue)
|
||||
{
|
||||
var result = new List<NamedEventWork>(strings.Count);
|
||||
var processed = new HashSet<int>();
|
||||
foreach (var s in strings)
|
||||
{
|
||||
var split = s.Split('\t');
|
||||
if (split.Length is not (3 or 4))
|
||||
continue;
|
||||
|
||||
var index = TryParseHexDecConst(split[0]);
|
||||
if (index >= maxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
if (processed.Contains(index))
|
||||
throw new ArgumentException("Already have an entry for this!", nameof(index));
|
||||
|
||||
var type = GetEventType(split[1]);
|
||||
var desc = split[2];
|
||||
var predefined = split.Length is 3 ? Empty : GetPredefinedArray(split[3]);
|
||||
var item = new NamedEventWork(desc, index, type, predefined);
|
||||
result.Add(item);
|
||||
processed.Add(index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<NamedEventConst> GetPredefinedArray(string combined)
|
||||
{
|
||||
var result = new List<NamedEventConst> {Custom};
|
||||
var split = combined.Split(',');
|
||||
foreach (var entry in split)
|
||||
{
|
||||
var subsplit = entry.Split(':');
|
||||
var name = subsplit[1];
|
||||
var value = Convert.ToUInt16(subsplit[0]);
|
||||
result.Add(new NamedEventConst(name, value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int TryParseHexDec(string flag)
|
||||
{
|
||||
if (!flag.StartsWith("0x"))
|
||||
return Convert.ToInt16(flag);
|
||||
flag = flag[2..];
|
||||
return Convert.ToInt16(flag, 16);
|
||||
}
|
||||
|
||||
private static int TryParseHexDecConst(string c)
|
||||
{
|
||||
if (!c.StartsWith("0x40"))
|
||||
return Convert.ToInt16(c);
|
||||
c = c[4..];
|
||||
return Convert.ToInt16(c, 16);
|
||||
}
|
||||
|
||||
private static NamedEventType GetEventType(string s) => s.Length == 0 ? 0 : GetEventType(s[0]);
|
||||
|
||||
private static NamedEventType GetEventType(char c) => c switch
|
||||
{
|
||||
'h' => NamedEventType.HiddenItem,
|
||||
'm' => NamedEventType.Misc,
|
||||
'f' => NamedEventType.FlyToggle,
|
||||
't' => NamedEventType.TrainerToggle,
|
||||
's' => NamedEventType.StoryProgress,
|
||||
|
||||
'a' => NamedEventType.Achievement,
|
||||
'+' => NamedEventType.Statistic,
|
||||
'*' => NamedEventType.UsefulFeature,
|
||||
'e' => NamedEventType.EncounterEvent,
|
||||
'g' => NamedEventType.GiftAvailable,
|
||||
'r' => NamedEventType.Rebattle,
|
||||
_ => NamedEventType.None,
|
||||
};
|
||||
var f = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "flags");
|
||||
var c = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "const");
|
||||
Flag = GetFlags(f, maxFlag);
|
||||
Work = GetValues(c, maxValue);
|
||||
}
|
||||
|
||||
public sealed class EventLabelCollectionSystem
|
||||
private static List<NamedEventValue> GetFlags(IReadOnlyCollection<string> strings, int maxValue)
|
||||
{
|
||||
public readonly IReadOnlyList<NamedEventWork> Work;
|
||||
public readonly IReadOnlyList<NamedEventValue> Flag;
|
||||
public readonly IReadOnlyList<NamedEventValue> System;
|
||||
|
||||
public EventLabelCollectionSystem(string game, int maxFlag = int.MaxValue, int maxSystem = int.MaxValue, int maxValue = int.MaxValue)
|
||||
var result = new List<NamedEventValue>(strings.Count);
|
||||
var processed = new HashSet<int>();
|
||||
foreach (var s in strings)
|
||||
{
|
||||
var f = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "flag");
|
||||
var s = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "system");
|
||||
var c = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "work");
|
||||
Flag = GetFlags(f, maxFlag);
|
||||
System = GetFlags(s, maxSystem);
|
||||
Work = GetValues(c, maxValue);
|
||||
var split = s.Split('\t');
|
||||
if (split.Length != 3)
|
||||
continue;
|
||||
|
||||
var index = TryParseHexDec(split[0]);
|
||||
if (index >= maxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(index), index, "Value too high.");
|
||||
|
||||
if (processed.Contains(index))
|
||||
throw new ArgumentOutOfRangeException(nameof(index), index, "Already have an entry for this!");
|
||||
|
||||
var type = GetEventType(split[1]);
|
||||
var desc = split[2];
|
||||
|
||||
var item = new NamedEventValue(desc, index, type);
|
||||
result.Add(item);
|
||||
processed.Add(index);
|
||||
}
|
||||
|
||||
private static List<NamedEventValue> GetFlags(IReadOnlyCollection<string> strings, int maxValue)
|
||||
{
|
||||
var result = new List<NamedEventValue>(strings.Count);
|
||||
var processed = new HashSet<int>();
|
||||
foreach (var s in strings)
|
||||
{
|
||||
var split = s.Split('\t');
|
||||
if (split.Length != 3)
|
||||
continue;
|
||||
|
||||
var index = TryParseHexDec(split[0]);
|
||||
if (index >= maxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
if (processed.Contains(index))
|
||||
throw new ArgumentException("Already have an entry for this!", nameof(index));
|
||||
|
||||
var type = GetEventType(split[1]);
|
||||
var desc = split[2];
|
||||
|
||||
var item = new NamedEventValue(desc, index, type);
|
||||
result.Add(item);
|
||||
processed.Add(index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static readonly NamedEventConst Custom = new("Custom", NamedEventConst.CustomMagicValue);
|
||||
private static readonly NamedEventConst[] Empty = { Custom };
|
||||
|
||||
private static IReadOnlyList<NamedEventWork> GetValues(IReadOnlyCollection<string> strings, int maxValue)
|
||||
{
|
||||
var result = new List<NamedEventWork>(strings.Count);
|
||||
var processed = new HashSet<int>();
|
||||
foreach (var s in strings)
|
||||
{
|
||||
var split = s.Split('\t');
|
||||
if (split.Length is not (3 or 4))
|
||||
continue;
|
||||
|
||||
var index = TryParseHexDecConst(split[0]);
|
||||
if (index >= maxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
if (processed.Contains(index))
|
||||
throw new ArgumentException("Already have an entry for this!", nameof(index));
|
||||
|
||||
var type = GetEventType(split[1]);
|
||||
var desc = split[2];
|
||||
var predefined = split.Length is 3 ? Empty : GetPredefinedArray(split[3]);
|
||||
var item = new NamedEventWork(desc, index, type, predefined);
|
||||
result.Add(item);
|
||||
processed.Add(index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<NamedEventConst> GetPredefinedArray(string combined)
|
||||
{
|
||||
var result = new List<NamedEventConst> { Custom };
|
||||
var split = combined.Split(',');
|
||||
foreach (var entry in split)
|
||||
{
|
||||
var subsplit = entry.Split(':');
|
||||
var name = subsplit[1];
|
||||
var value = Convert.ToUInt16(subsplit[0]);
|
||||
result.Add(new NamedEventConst(name, value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int TryParseHexDec(string flag)
|
||||
{
|
||||
if (!flag.StartsWith("0x"))
|
||||
return Convert.ToInt16(flag);
|
||||
flag = flag[2..];
|
||||
return Convert.ToInt16(flag, 16);
|
||||
}
|
||||
|
||||
private static int TryParseHexDecConst(string c)
|
||||
{
|
||||
if (!c.StartsWith("0x40"))
|
||||
return Convert.ToInt16(c);
|
||||
c = c[4..];
|
||||
return Convert.ToInt16(c, 16);
|
||||
}
|
||||
|
||||
private static NamedEventType GetEventType(string s) => s.Length == 0 ? 0 : GetEventType(s[0]);
|
||||
|
||||
private static NamedEventType GetEventType(char c) => c switch
|
||||
{
|
||||
'h' => NamedEventType.HiddenItem,
|
||||
'm' => NamedEventType.Misc,
|
||||
'f' => NamedEventType.FlyToggle,
|
||||
't' => NamedEventType.TrainerToggle,
|
||||
's' => NamedEventType.StoryProgress,
|
||||
|
||||
'a' => NamedEventType.Achievement,
|
||||
'+' => NamedEventType.Statistic,
|
||||
'*' => NamedEventType.UsefulFeature,
|
||||
'e' => NamedEventType.EncounterEvent,
|
||||
'g' => NamedEventType.GiftAvailable,
|
||||
'r' => NamedEventType.Rebattle,
|
||||
_ => NamedEventType.None,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
public enum NamedEventType
|
||||
{
|
||||
None,
|
||||
HiddenItem,
|
||||
TrainerToggle,
|
||||
StoryProgress,
|
||||
FlyToggle,
|
||||
Misc,
|
||||
Statistic,
|
||||
private static readonly NamedEventConst Custom = new("Custom", NamedEventConst.CustomMagicValue);
|
||||
private static readonly NamedEventConst[] Empty = {Custom};
|
||||
|
||||
Achievement,
|
||||
UsefulFeature,
|
||||
EncounterEvent,
|
||||
GiftAvailable,
|
||||
Rebattle = 100,
|
||||
private static IReadOnlyList<NamedEventWork> GetValues(IReadOnlyCollection<string> strings, int maxValue)
|
||||
{
|
||||
var result = new List<NamedEventWork>(strings.Count);
|
||||
var processed = new HashSet<int>();
|
||||
foreach (var s in strings)
|
||||
{
|
||||
var split = s.Split('\t');
|
||||
if (split.Length is not (3 or 4))
|
||||
continue;
|
||||
|
||||
var index = TryParseHexDecConst(split[0]);
|
||||
if (index >= maxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(index), index, "Value too high.");
|
||||
|
||||
if (processed.Contains(index))
|
||||
throw new ArgumentOutOfRangeException(nameof(index), index, "Already have an entry for this!");
|
||||
|
||||
var type = GetEventType(split[1]);
|
||||
var desc = split[2];
|
||||
var predefined = split.Length is 3 ? Empty : GetPredefinedArray(split[3]);
|
||||
var item = new NamedEventWork(desc, index, type, predefined);
|
||||
result.Add(item);
|
||||
processed.Add(index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public record NamedEventValue(string Name, int Index, NamedEventType Type);
|
||||
|
||||
public sealed record NamedEventWork(string Name, int Index, NamedEventType Type, IReadOnlyList<NamedEventConst> PredefinedValues) : NamedEventValue(Name, Index, Type);
|
||||
|
||||
public sealed record NamedEventConst(string Name, ushort Value)
|
||||
private static IReadOnlyList<NamedEventConst> GetPredefinedArray(string combined)
|
||||
{
|
||||
public bool IsCustom => Value == CustomMagicValue;
|
||||
public const ushort CustomMagicValue = ushort.MaxValue;
|
||||
var result = new List<NamedEventConst> {Custom};
|
||||
var split = combined.Split(',');
|
||||
foreach (var entry in split)
|
||||
{
|
||||
var subsplit = entry.Split(':');
|
||||
var name = subsplit[1];
|
||||
var value = Convert.ToUInt16(subsplit[0], 10);
|
||||
result.Add(new NamedEventConst(name, value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int TryParseHexDec(string flag)
|
||||
{
|
||||
if (!flag.StartsWith("0x", StringComparison.Ordinal))
|
||||
return Convert.ToInt16(flag, 10);
|
||||
flag = flag[2..];
|
||||
return Convert.ToInt16(flag, 16);
|
||||
}
|
||||
|
||||
private static int TryParseHexDecConst(string c)
|
||||
{
|
||||
if (!c.StartsWith("0x40", StringComparison.Ordinal))
|
||||
return Convert.ToInt16(c, 10);
|
||||
c = c[4..];
|
||||
return Convert.ToInt16(c, 16);
|
||||
}
|
||||
|
||||
private static NamedEventType GetEventType(string s) => s.Length == 0 ? 0 : GetEventType(s[0]);
|
||||
|
||||
private static NamedEventType GetEventType(char c) => c switch
|
||||
{
|
||||
'h' => NamedEventType.HiddenItem,
|
||||
'm' => NamedEventType.Misc,
|
||||
'f' => NamedEventType.FlyToggle,
|
||||
't' => NamedEventType.TrainerToggle,
|
||||
's' => NamedEventType.StoryProgress,
|
||||
|
||||
'a' => NamedEventType.Achievement,
|
||||
'+' => NamedEventType.Statistic,
|
||||
'*' => NamedEventType.UsefulFeature,
|
||||
'e' => NamedEventType.EncounterEvent,
|
||||
'g' => NamedEventType.GiftAvailable,
|
||||
'r' => NamedEventType.Rebattle,
|
||||
_ => NamedEventType.None,
|
||||
};
|
||||
}
|
||||
|
||||
public sealed class EventLabelCollectionSystem
|
||||
{
|
||||
public readonly IReadOnlyList<NamedEventWork> Work;
|
||||
public readonly IReadOnlyList<NamedEventValue> Flag;
|
||||
public readonly IReadOnlyList<NamedEventValue> System;
|
||||
|
||||
public EventLabelCollectionSystem(string game, int maxFlag = int.MaxValue, int maxSystem = int.MaxValue, int maxValue = int.MaxValue)
|
||||
{
|
||||
var f = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "flag");
|
||||
var s = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "system");
|
||||
var c = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "work");
|
||||
Flag = GetFlags(f, maxFlag);
|
||||
System = GetFlags(s, maxSystem);
|
||||
Work = GetValues(c, maxValue);
|
||||
}
|
||||
|
||||
private static List<NamedEventValue> GetFlags(IReadOnlyCollection<string> strings, int maxValue)
|
||||
{
|
||||
var result = new List<NamedEventValue>(strings.Count);
|
||||
var processed = new HashSet<int>();
|
||||
foreach (var s in strings)
|
||||
{
|
||||
var split = s.Split('\t');
|
||||
if (split.Length != 3)
|
||||
continue;
|
||||
|
||||
var index = TryParseHexDec(split[0]);
|
||||
if (index >= maxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(index), index, "Value too high.");
|
||||
|
||||
if (processed.Contains(index))
|
||||
throw new ArgumentOutOfRangeException(nameof(index), index, "Already have an entry for this!");
|
||||
|
||||
var type = GetEventType(split[1]);
|
||||
var desc = split[2];
|
||||
|
||||
var item = new NamedEventValue(desc, index, type);
|
||||
result.Add(item);
|
||||
processed.Add(index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static readonly NamedEventConst Custom = new("Custom", NamedEventConst.CustomMagicValue);
|
||||
private static readonly NamedEventConst[] Empty = { Custom };
|
||||
|
||||
private static IReadOnlyList<NamedEventWork> GetValues(IReadOnlyCollection<string> strings, int maxValue)
|
||||
{
|
||||
var result = new List<NamedEventWork>(strings.Count);
|
||||
var processed = new HashSet<int>();
|
||||
foreach (var s in strings)
|
||||
{
|
||||
var split = s.Split('\t');
|
||||
if (split.Length is not (3 or 4))
|
||||
continue;
|
||||
|
||||
var index = TryParseHexDecConst(split[0]);
|
||||
if (index >= maxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(index), index, "Value too high.");
|
||||
|
||||
if (processed.Contains(index))
|
||||
throw new ArgumentOutOfRangeException(nameof(index), index, "Already have an entry for this!");
|
||||
|
||||
var type = GetEventType(split[1]);
|
||||
var desc = split[2];
|
||||
var predefined = split.Length is 3 ? Empty : GetPredefinedArray(split[3]);
|
||||
var item = new NamedEventWork(desc, index, type, predefined);
|
||||
result.Add(item);
|
||||
processed.Add(index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<NamedEventConst> GetPredefinedArray(string combined)
|
||||
{
|
||||
var result = new List<NamedEventConst> { Custom };
|
||||
var split = combined.Split(',');
|
||||
foreach (var entry in split)
|
||||
{
|
||||
var subsplit = entry.Split(':');
|
||||
var name = subsplit[1];
|
||||
var value = Convert.ToUInt16(subsplit[0], 10);
|
||||
result.Add(new NamedEventConst(name, value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int TryParseHexDec(string flag)
|
||||
{
|
||||
if (!flag.StartsWith("0x", StringComparison.Ordinal))
|
||||
return Convert.ToInt16(flag, 10);
|
||||
flag = flag[2..];
|
||||
return Convert.ToInt16(flag, 16);
|
||||
}
|
||||
|
||||
private static int TryParseHexDecConst(string c)
|
||||
{
|
||||
if (!c.StartsWith("0x40", StringComparison.Ordinal))
|
||||
return Convert.ToInt16(c, 10);
|
||||
c = c[4..];
|
||||
return Convert.ToInt16(c, 16);
|
||||
}
|
||||
|
||||
private static NamedEventType GetEventType(string s) => s.Length == 0 ? 0 : GetEventType(s[0]);
|
||||
|
||||
private static NamedEventType GetEventType(char c) => c switch
|
||||
{
|
||||
'h' => NamedEventType.HiddenItem,
|
||||
'm' => NamedEventType.Misc,
|
||||
'f' => NamedEventType.FlyToggle,
|
||||
't' => NamedEventType.TrainerToggle,
|
||||
's' => NamedEventType.StoryProgress,
|
||||
|
||||
'a' => NamedEventType.Achievement,
|
||||
'+' => NamedEventType.Statistic,
|
||||
'*' => NamedEventType.UsefulFeature,
|
||||
'e' => NamedEventType.EncounterEvent,
|
||||
'g' => NamedEventType.GiftAvailable,
|
||||
'r' => NamedEventType.Rebattle,
|
||||
_ => NamedEventType.None,
|
||||
};
|
||||
}
|
||||
|
||||
public enum NamedEventType
|
||||
{
|
||||
None,
|
||||
HiddenItem,
|
||||
TrainerToggle,
|
||||
StoryProgress,
|
||||
FlyToggle,
|
||||
Misc,
|
||||
Statistic,
|
||||
|
||||
Achievement,
|
||||
UsefulFeature,
|
||||
EncounterEvent,
|
||||
GiftAvailable,
|
||||
Rebattle = 100,
|
||||
}
|
||||
|
||||
public record NamedEventValue(string Name, int Index, NamedEventType Type);
|
||||
|
||||
public sealed record NamedEventWork(string Name, int Index, NamedEventType Type, IReadOnlyList<NamedEventConst> PredefinedValues) : NamedEventValue(Name, Index, Type);
|
||||
|
||||
public sealed record NamedEventConst(string Name, ushort Value)
|
||||
{
|
||||
public bool IsCustom => Value == CustomMagicValue;
|
||||
public const ushort CustomMagicValue = ushort.MaxValue;
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ public class EventUnlocker8b : EventUnlocker<SAV8BS>
|
|||
public EventUnlocker8b(SAV8BS sav) : base(sav) { }
|
||||
|
||||
public bool UnlockReadySpiritomb => SAV.UgSaveData.TalkedNPC < 32;
|
||||
public bool UnlockReadyBoxLegend => SAV.Work.GetFlag(308) && SAV.Work.GetWork(84) != 5; // FE_D05R0114_SPPOKE_GET, WK_SCENE_D05R0114 (1-3 story related, 4 = captured, 5 = can retry)
|
||||
public bool UnlockReadyShaymin => SAV.Work.GetFlag(545) || !(SAV.Work.GetWork(276) == 1 && SAV.Zukan.HasNationalDex && SAV.Items.GetItemQuantity(452) == 1 && SAV.Work.GetSystemFlag(5)); // HAIHUEVENT_ID_D30, Oak's Letter
|
||||
public bool UnlockReadyDarkrai => SAV.Work.GetFlag(301) || !(SAV.Work.GetWork(275) == 1 && SAV.Zukan.HasNationalDex && SAV.Items.GetItemQuantity(454) == 1); // HAIHUEVENT_ID_D18, Member Card
|
||||
public bool UnlockReadyArceus => SAV.Work.GetFlag(531) || !(SAV.Work.GetWork(188) == 0 && SAV.Zukan.HasNationalDex && SAV.Items.GetItemQuantity(455) == 1 && SAV.Work.GetSystemFlag(5)); // FE_D05R0116_LEGEND_CLEAR, Azure Flute
|
||||
public bool UnlockReadyBoxLegend => SAV.FlagWork.GetFlag(308) && SAV.FlagWork.GetWork(84) != 5; // FE_D05R0114_SPPOKE_GET, WK_SCENE_D05R0114 (1-3 story related, 4 = captured, 5 = can retry)
|
||||
public bool UnlockReadyShaymin => SAV.FlagWork.GetFlag(545) || !(SAV.FlagWork.GetWork(276) == 1 && SAV.Zukan.HasNationalDex && SAV.Items.GetItemQuantity(452) == 1 && SAV.FlagWork.GetSystemFlag(5)); // HAIHUEVENT_ID_D30, Oak's Letter
|
||||
public bool UnlockReadyDarkrai => SAV.FlagWork.GetFlag(301) || !(SAV.FlagWork.GetWork(275) == 1 && SAV.Zukan.HasNationalDex && SAV.Items.GetItemQuantity(454) == 1); // HAIHUEVENT_ID_D18, Member Card
|
||||
public bool UnlockReadyArceus => SAV.FlagWork.GetFlag(531) || !(SAV.FlagWork.GetWork(188) == 0 && SAV.Zukan.HasNationalDex && SAV.Items.GetItemQuantity(455) == 1 && SAV.FlagWork.GetSystemFlag(5)); // FE_D05R0116_LEGEND_CLEAR, Azure Flute
|
||||
|
||||
// 0 = inactive, 1 = roaming, 2 = KOed, 3 = captured
|
||||
public bool UnlockReadyRoamerMesprit => SAV.Encounter.Roamer1Encount != 1;
|
||||
|
@ -18,10 +18,10 @@ public class EventUnlocker8b : EventUnlocker<SAV8BS>
|
|||
|
||||
public void UnlockBoxLegend()
|
||||
{
|
||||
SAV.Work.SetFlag(308, false); // captured
|
||||
SAV.Work.SetFlag(393, false); // clear vanish
|
||||
SAV.Work.SetFlag(1623, false); // can retry
|
||||
SAV.Work.SetWork(84, 5); // can retry
|
||||
SAV.FlagWork.SetFlag(308, false); // captured
|
||||
SAV.FlagWork.SetFlag(393, false); // clear vanish
|
||||
SAV.FlagWork.SetFlag(1623, false); // can retry
|
||||
SAV.FlagWork.SetWork(84, 5); // can retry
|
||||
}
|
||||
|
||||
public void UnlockSpiritomb()
|
||||
|
@ -34,29 +34,29 @@ public class EventUnlocker8b : EventUnlocker<SAV8BS>
|
|||
public void UnlockShaymin()
|
||||
{
|
||||
SAV.Zukan.HasNationalDex = true; // dex
|
||||
SAV.Work.SetSystemFlag(5, true); // clear
|
||||
SAV.Work.SetWork(276, 1); // haihu
|
||||
SAV.FlagWork.SetSystemFlag(5, true); // clear
|
||||
SAV.FlagWork.SetWork(276, 1); // haihu
|
||||
SAV.Items.SetItemQuantity(452, 1); // letter
|
||||
SAV.Work.SetFlag(545, false); // clear vanish
|
||||
SAV.FlagWork.SetFlag(545, false); // clear vanish
|
||||
}
|
||||
|
||||
public void UnlockDarkrai()
|
||||
{
|
||||
SAV.Zukan.HasNationalDex = true; // dex
|
||||
SAV.Work.SetWork(275, 1); // haihu
|
||||
SAV.FlagWork.SetWork(275, 1); // haihu
|
||||
SAV.Items.SetItemQuantity(454, 1); // member
|
||||
SAV.Work.SetFlag(301, false); // clear vanish
|
||||
SAV.FlagWork.SetFlag(301, false); // clear vanish
|
||||
}
|
||||
|
||||
public void UnlockArceus()
|
||||
{
|
||||
SAV.Zukan.HasNationalDex = true; // dex
|
||||
SAV.Items.SetItemQuantity(455, 1); // flute
|
||||
SAV.Work.SetSystemFlag(5, true); // clear
|
||||
SAV.Work.SetFlag(1508, true); // wildcard
|
||||
SAV.Work.SetFlag(244, false); // captured
|
||||
SAV.Work.SetFlag(531, false); // clear vanish
|
||||
SAV.Work.SetWork(188, 0); // staircase
|
||||
SAV.FlagWork.SetSystemFlag(5, true); // clear
|
||||
SAV.FlagWork.SetFlag(1508, true); // wildcard
|
||||
SAV.FlagWork.SetFlag(244, false); // captured
|
||||
SAV.FlagWork.SetFlag(531, false); // clear vanish
|
||||
SAV.FlagWork.SetWork(188, 0); // staircase
|
||||
}
|
||||
|
||||
public void UnlockZones()
|
||||
|
@ -66,16 +66,16 @@ public class EventUnlocker8b : EventUnlocker<SAV8BS>
|
|||
|
||||
for (int i = ZONE_START; i <= ZONE_END; i++)
|
||||
{
|
||||
SAV.Work.SetSystemFlag(i, true);
|
||||
SAV.FlagWork.SetSystemFlag(i, true);
|
||||
}
|
||||
|
||||
// uncover hidden zones
|
||||
SAV.Work.SetWork(278, 1); // Fullmoon Island
|
||||
SAV.Work.SetWork(279, 1); // Newmoon Island
|
||||
SAV.Work.SetWork(280, 1); // Spring Path / Sendoff Spring
|
||||
SAV.Work.SetWork(281, 1); // Seabreak Path / Flower Paradise
|
||||
SAV.Work.SetWork(291, 1); // Pokémon League (Victory Road entrance)
|
||||
SAV.Work.SetWork(292, 1); // Ramanas Park
|
||||
SAV.FlagWork.SetWork(278, 1); // Fullmoon Island
|
||||
SAV.FlagWork.SetWork(279, 1); // Newmoon Island
|
||||
SAV.FlagWork.SetWork(280, 1); // Spring Path / Sendoff Spring
|
||||
SAV.FlagWork.SetWork(281, 1); // Seabreak Path / Flower Paradise
|
||||
SAV.FlagWork.SetWork(291, 1); // Pokémon League (Victory Road entrance)
|
||||
SAV.FlagWork.SetWork(292, 1); // Ramanas Park
|
||||
}
|
||||
|
||||
public void RespawnRoamer()
|
||||
|
@ -86,15 +86,15 @@ public class EventUnlocker8b : EventUnlocker<SAV8BS>
|
|||
|
||||
public void RespawnMesprit()
|
||||
{
|
||||
SAV.Work.SetFlag(249, false); // clear met
|
||||
SAV.Work.SetFlag(420, false); // clear vanish
|
||||
SAV.FlagWork.SetFlag(249, false); // clear met
|
||||
SAV.FlagWork.SetFlag(420, false); // clear vanish
|
||||
SAV.Encounter.Roamer1Encount = 0; // not actively roaming
|
||||
}
|
||||
|
||||
public void RespawnCresselia()
|
||||
{
|
||||
SAV.Work.SetFlag(245, false); // clear met
|
||||
SAV.Work.SetFlag(532, false); // clear vanish
|
||||
SAV.FlagWork.SetFlag(245, false); // clear met
|
||||
SAV.FlagWork.SetFlag(532, false); // clear vanish
|
||||
SAV.Encounter.Roamer2Encount = 0; // not actively roaming
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public sealed class EventBlockDiff<T, T2> : IEventWorkDiff where T : IEventFlagA
|
|||
Diff(t1, t2);
|
||||
}
|
||||
|
||||
private EventWorkDiffCompatibility SanityCheckSaveInfo(T s1, T s2)
|
||||
private static EventWorkDiffCompatibility SanityCheckSaveInfo(T s1, T s2)
|
||||
{
|
||||
if (s1.GetType() != s2.GetType())
|
||||
return DifferentGameGroup;
|
||||
|
@ -74,9 +74,9 @@ public sealed class EventBlockDiff<T, T2> : IEventWorkDiff where T : IEventFlagA
|
|||
|
||||
public IReadOnlyList<string> Summarize()
|
||||
{
|
||||
var fOn = SetFlags.Select(z => z.ToString());
|
||||
var fOff = ClearedFlags.Select(z => z.ToString());
|
||||
var wt = WorkChanged.Select((z, _) => z.ToString());
|
||||
var fOn = SetFlags.Select(z => $"{z}");
|
||||
var fOff = ClearedFlags.Select(z => $"{z}");
|
||||
var wt = WorkChanged.Select((z, _) => $"{z}");
|
||||
|
||||
var list = new List<string> { "Flags: ON", "=========" };
|
||||
list.AddRange(fOn);
|
||||
|
|
|
@ -46,9 +46,9 @@ public sealed class EventWorkDiff8b : IEventWorkDiff
|
|||
return;
|
||||
}
|
||||
|
||||
DiffSavesFlag(s1.Work, s2.Work, SetFlags, ClearedFlags);
|
||||
DiffSavesSystem(s1.Work, s2.Work, SetSystem, ClearedSystem);
|
||||
DiffSavesWork(s1.Work, s2.Work, WorkChanged, WorkDiff);
|
||||
DiffSavesFlag(s1.FlagWork, s2.FlagWork, SetFlags, ClearedFlags);
|
||||
DiffSavesSystem(s1.FlagWork, s2.FlagWork, SetSystem, ClearedSystem);
|
||||
DiffSavesWork(s1.FlagWork, s2.FlagWork, WorkChanged, WorkDiff);
|
||||
S1 = s1;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Event Flag that toggles certain features / entities on and off.
|
||||
/// </summary>
|
||||
public sealed class EventFlag : EventVar
|
||||
{
|
||||
public bool Flag;
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public EventFlag(int index, EventVarType t, IReadOnlyList<string> pieces) : base(index, t, pieces[1])
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Event Flag that toggles certain features / entities on and off.
|
||||
/// </summary>
|
||||
public sealed class EventFlag : EventVar
|
||||
{
|
||||
public bool Flag;
|
||||
|
||||
public EventFlag(int index, EventVarType t, IReadOnlyList<string> pieces) : base(index, t, pieces[1])
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +1,34 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Event variable used to determine game events.
|
||||
/// </summary>
|
||||
public abstract class EventVar
|
||||
{
|
||||
/// <summary>
|
||||
/// Event variable used to determine game events.
|
||||
/// Name of event variable
|
||||
/// </summary>
|
||||
public abstract class EventVar
|
||||
public readonly string Name;
|
||||
|
||||
/// <summary>
|
||||
/// Type of event variable
|
||||
/// </summary>
|
||||
public readonly EventVarType Type;
|
||||
|
||||
/// <summary>
|
||||
/// Raw index within the event variable (type) region.
|
||||
/// </summary>
|
||||
public int RawIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Unpacked structure's index.
|
||||
/// </summary>
|
||||
public readonly int RelativeIndex;
|
||||
|
||||
protected EventVar(int index, EventVarType t, string name)
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of event variable
|
||||
/// </summary>
|
||||
public readonly string Name;
|
||||
|
||||
/// <summary>
|
||||
/// Type of event variable
|
||||
/// </summary>
|
||||
public readonly EventVarType Type;
|
||||
|
||||
/// <summary>
|
||||
/// Raw index within the event variable (type) region.
|
||||
/// </summary>
|
||||
public int RawIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Unpacked structure's index.
|
||||
/// </summary>
|
||||
public readonly int RelativeIndex;
|
||||
|
||||
protected EventVar(int index, EventVarType t, string name)
|
||||
{
|
||||
RelativeIndex = index;
|
||||
Type = t;
|
||||
Name = name;
|
||||
}
|
||||
RelativeIndex = index;
|
||||
Type = t;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public sealed class EventVarGroup
|
||||
{
|
||||
public readonly EventVarType Type;
|
||||
public readonly List<EventVar> Vars = new();
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public EventVarGroup(EventVarType type) => Type = type;
|
||||
}
|
||||
}
|
||||
public sealed class EventVarGroup
|
||||
{
|
||||
public readonly EventVarType Type;
|
||||
public readonly List<EventVar> Vars = new();
|
||||
|
||||
public EventVarGroup(EventVarType type) => Type = type;
|
||||
}
|
||||
|
|
|
@ -1,32 +1,31 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Event number storage for more complex logic events.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public sealed class EventWork<T> : EventVar where T : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// Event number storage for more complex logic events.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public sealed class EventWork<T> : EventVar where T : struct
|
||||
public T Value;
|
||||
public readonly IList<EventWorkVal> Options = new List<EventWorkVal> { new() };
|
||||
|
||||
public EventWork(int index, EventVarType t, IReadOnlyList<string> pieces) : base(index, t, pieces[1])
|
||||
{
|
||||
public T Value;
|
||||
public readonly IList<EventWorkVal> Options = new List<EventWorkVal> { new() };
|
||||
if (pieces.Count < 3)
|
||||
return;
|
||||
|
||||
public EventWork(int index, EventVarType t, IReadOnlyList<string> pieces) : base(index, t, pieces[1])
|
||||
var items = pieces[2]
|
||||
.Split(',')
|
||||
.Select(z => z.Split(':'))
|
||||
.Where(z => z.Length == 2);
|
||||
|
||||
foreach (var s in items)
|
||||
{
|
||||
if (pieces.Count < 3)
|
||||
return;
|
||||
|
||||
var items = pieces[2]
|
||||
.Split(',')
|
||||
.Select(z => z.Split(':'))
|
||||
.Where(z => z.Length == 2);
|
||||
|
||||
foreach (var s in items)
|
||||
{
|
||||
if (int.TryParse(s[0], out var value))
|
||||
Options.Add(new EventWorkVal(s[1], value));
|
||||
}
|
||||
if (int.TryParse(s[0], out var value))
|
||||
Options.Add(new EventWorkVal(s[1], value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,148 +4,147 @@ using System.Diagnostics;
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using static PKHeX.Core.MessageStrings;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Provides utility for various <see cref="EventWork{T}"/> logic.
|
||||
/// </summary>
|
||||
public static class EventWorkUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides utility for various <see cref="EventWork{T}"/> logic.
|
||||
/// </summary>
|
||||
public static class EventWorkUtil
|
||||
private static readonly Dictionary<char, EventVarType> TypeDict = new()
|
||||
{
|
||||
private static readonly Dictionary<char, EventVarType> TypeDict = new()
|
||||
['z'] = EventVarType.Zone,
|
||||
['s'] = EventVarType.System,
|
||||
['v'] = EventVarType.Vanish,
|
||||
['c'] = EventVarType.Scene,
|
||||
['e'] = EventVarType.Event,
|
||||
};
|
||||
|
||||
private static bool GetIndex(string l, out int index, out EventVarType type)
|
||||
{
|
||||
var typeChar = l[0];
|
||||
if (!TypeDict.TryGetValue(typeChar, out type))
|
||||
{
|
||||
['z'] = EventVarType.Zone,
|
||||
['s'] = EventVarType.System,
|
||||
['v'] = EventVarType.Vanish,
|
||||
['c'] = EventVarType.Scene,
|
||||
['e'] = EventVarType.Event,
|
||||
};
|
||||
|
||||
private static bool GetIndex(string l, out int index, out EventVarType type)
|
||||
{
|
||||
var typeChar = l[0];
|
||||
if (!TypeDict.TryGetValue(typeChar, out type))
|
||||
{
|
||||
Debug.WriteLine($"Rejected line due to bad type: {typeChar}");
|
||||
index = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
var indexString = l[1..];
|
||||
if (int.TryParse(indexString, out index))
|
||||
return true;
|
||||
|
||||
Debug.WriteLine($"Rejected line due to bad index: {indexString}");
|
||||
Debug.WriteLine($"Rejected line due to bad type: {typeChar}");
|
||||
index = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses and converts <see cref="lines"/> into <see cref="EventVarGroup"/> values with the provided <see cref="constructor"/>.
|
||||
/// </summary>
|
||||
/// <param name="lines">Lines to parse</param>
|
||||
/// <param name="constructor">Object constructor</param>
|
||||
/// <returns>Converted lines grouped together by <see cref="EventVarType"/></returns>
|
||||
public static List<EventVarGroup> GetVars(IEnumerable<string> lines, Func<int, EventVarType, string[], EventVar> constructor)
|
||||
{
|
||||
var list = new List<EventVarGroup>();
|
||||
foreach (var l in lines)
|
||||
{
|
||||
var split = l.Split('\t');
|
||||
if (split.Length < 2 || split[0].Length < 2)
|
||||
continue;
|
||||
|
||||
if (!GetIndex(split[0], out var index, out var type))
|
||||
continue;
|
||||
|
||||
var group = list.Find(z => z.Type == type);
|
||||
if (group == null)
|
||||
{
|
||||
group = new EventVarGroup(type);
|
||||
list.Add(group);
|
||||
}
|
||||
|
||||
var entry = constructor(index, type, split);
|
||||
group.Vars.Add(entry);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares a <see cref="before"/> and <see cref="after"/> <see cref="IEventWork{T}"/> object of the same type to find <see cref="EventFlag"/> changes.
|
||||
/// </summary>
|
||||
/// <param name="before">Data before the event was triggered</param>
|
||||
/// <param name="after">Data after the event was triggered</param>
|
||||
/// <param name="on">List of flags that were turned on</param>
|
||||
/// <param name="off">List of flags that were turned off</param>
|
||||
public static void DiffSavesFlag(IEventFlag before, IEventFlag after, List<int> on, List<int> off)
|
||||
{
|
||||
int max = before.CountFlag;
|
||||
for (int i = 0; i < max; i++)
|
||||
{
|
||||
var b = before.GetFlag(i);
|
||||
var a = after.GetFlag(i);
|
||||
if (b == a)
|
||||
continue;
|
||||
|
||||
var arr = a ? on : off;
|
||||
arr.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares a <see cref="before"/> and <see cref="after"/> <see cref="IEventWork{T}"/> object of the same type to find <see cref="EventFlag"/> changes.
|
||||
/// </summary>
|
||||
/// <param name="before">Data before the event was triggered</param>
|
||||
/// <param name="after">Data after the event was triggered</param>
|
||||
/// <param name="on">List of flags that were turned on</param>
|
||||
/// <param name="off">List of flags that were turned off</param>
|
||||
public static void DiffSavesSystem(ISystemFlag before, ISystemFlag after, List<int> on, List<int> off)
|
||||
{
|
||||
int max = before.CountSystem;
|
||||
for (int i = 0; i < max; i++)
|
||||
{
|
||||
var b = before.GetSystemFlag(i);
|
||||
var a = after.GetSystemFlag(i);
|
||||
if (b == a)
|
||||
continue;
|
||||
|
||||
var arr = a ? on : off;
|
||||
arr.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares a <see cref="before"/> and <see cref="after"/> <see cref="IEventWork{T}"/> object of the same type to find <see cref="EventWork{T}"/> changes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of value used by <see cref="EventWork{T}"/></typeparam>
|
||||
/// <param name="before">Data before the event was triggered</param>
|
||||
/// <param name="after">Data after the event was triggered</param>
|
||||
/// <param name="changed"><see cref="EventVar.RawIndex"/> values that changed</param>
|
||||
/// <param name="changes">Summary of the <see cref="EventWork{T}"/> value change</param>
|
||||
public static void DiffSavesWork<T>(IEventWork<T> before, IEventWork<T> after, List<int> changed, List<string> changes) where T : unmanaged, IEquatable<T>
|
||||
{
|
||||
int max = before.CountWork;
|
||||
for (int i = 0; i < max; i++)
|
||||
{
|
||||
var b = before.GetWork(i);
|
||||
var a = after.GetWork(i);
|
||||
if (b.Equals(a))
|
||||
continue;
|
||||
|
||||
changed.Add(i);
|
||||
changes.Add($"{b} => {a}");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SanityCheckSaveInfo<T>(T s1, T s2, [NotNullWhen(false)] out string? Message) where T : SaveFile
|
||||
{
|
||||
if (s1.GetType() != s2.GetType())
|
||||
{ Message = string.Format(MsgSaveDifferentTypes, $"S1: {s1.GetType().Name}", $"S2: {s2.GetType().Name}"); return false; }
|
||||
|
||||
if (s1.Version != s2.Version)
|
||||
{ Message = string.Format(MsgSaveDifferentVersions, $"S1: {s1.Version}", $"S2: {s2.Version}"); return false; }
|
||||
|
||||
Message = null;
|
||||
var indexString = l[1..];
|
||||
if (int.TryParse(indexString, out index))
|
||||
return true;
|
||||
|
||||
Debug.WriteLine($"Rejected line due to bad index: {indexString}");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses and converts <see cref="lines"/> into <see cref="EventVarGroup"/> values with the provided <see cref="constructor"/>.
|
||||
/// </summary>
|
||||
/// <param name="lines">Lines to parse</param>
|
||||
/// <param name="constructor">Object constructor</param>
|
||||
/// <returns>Converted lines grouped together by <see cref="EventVarType"/></returns>
|
||||
public static List<EventVarGroup> GetVars(IEnumerable<string> lines, Func<int, EventVarType, string[], EventVar> constructor)
|
||||
{
|
||||
var list = new List<EventVarGroup>();
|
||||
foreach (var l in lines)
|
||||
{
|
||||
var split = l.Split('\t');
|
||||
if (split.Length < 2 || split[0].Length < 2)
|
||||
continue;
|
||||
|
||||
if (!GetIndex(split[0], out var index, out var type))
|
||||
continue;
|
||||
|
||||
var group = list.Find(z => z.Type == type);
|
||||
if (group == null)
|
||||
{
|
||||
group = new EventVarGroup(type);
|
||||
list.Add(group);
|
||||
}
|
||||
|
||||
var entry = constructor(index, type, split);
|
||||
group.Vars.Add(entry);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares a <see cref="before"/> and <see cref="after"/> <see cref="IEventWork{T}"/> object of the same type to find <see cref="EventFlag"/> changes.
|
||||
/// </summary>
|
||||
/// <param name="before">Data before the event was triggered</param>
|
||||
/// <param name="after">Data after the event was triggered</param>
|
||||
/// <param name="on">List of flags that were turned on</param>
|
||||
/// <param name="off">List of flags that were turned off</param>
|
||||
public static void DiffSavesFlag(IEventFlag before, IEventFlag after, List<int> on, List<int> off)
|
||||
{
|
||||
int max = before.CountFlag;
|
||||
for (int i = 0; i < max; i++)
|
||||
{
|
||||
var b = before.GetFlag(i);
|
||||
var a = after.GetFlag(i);
|
||||
if (b == a)
|
||||
continue;
|
||||
|
||||
var arr = a ? on : off;
|
||||
arr.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares a <see cref="before"/> and <see cref="after"/> <see cref="IEventWork{T}"/> object of the same type to find <see cref="EventFlag"/> changes.
|
||||
/// </summary>
|
||||
/// <param name="before">Data before the event was triggered</param>
|
||||
/// <param name="after">Data after the event was triggered</param>
|
||||
/// <param name="on">List of flags that were turned on</param>
|
||||
/// <param name="off">List of flags that were turned off</param>
|
||||
public static void DiffSavesSystem(ISystemFlag before, ISystemFlag after, List<int> on, List<int> off)
|
||||
{
|
||||
int max = before.CountSystem;
|
||||
for (int i = 0; i < max; i++)
|
||||
{
|
||||
var b = before.GetSystemFlag(i);
|
||||
var a = after.GetSystemFlag(i);
|
||||
if (b == a)
|
||||
continue;
|
||||
|
||||
var arr = a ? on : off;
|
||||
arr.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares a <see cref="before"/> and <see cref="after"/> <see cref="IEventWork{T}"/> object of the same type to find <see cref="EventWork{T}"/> changes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of value used by <see cref="EventWork{T}"/></typeparam>
|
||||
/// <param name="before">Data before the event was triggered</param>
|
||||
/// <param name="after">Data after the event was triggered</param>
|
||||
/// <param name="changed"><see cref="EventVar.RawIndex"/> values that changed</param>
|
||||
/// <param name="changes">Summary of the <see cref="EventWork{T}"/> value change</param>
|
||||
public static void DiffSavesWork<T>(IEventWork<T> before, IEventWork<T> after, List<int> changed, List<string> changes) where T : unmanaged, IEquatable<T>
|
||||
{
|
||||
int max = before.CountWork;
|
||||
for (int i = 0; i < max; i++)
|
||||
{
|
||||
var b = before.GetWork(i);
|
||||
var a = after.GetWork(i);
|
||||
if (b.Equals(a))
|
||||
continue;
|
||||
|
||||
changed.Add(i);
|
||||
changes.Add($"{b} => {a}");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SanityCheckSaveInfo<T>(T s1, T s2, [NotNullWhen(false)] out string? Message) where T : SaveFile
|
||||
{
|
||||
if (s1.GetType() != s2.GetType())
|
||||
{ Message = string.Format(MsgSaveDifferentTypes, $"S1: {s1.GetType().Name}", $"S2: {s2.GetType().Name}"); return false; }
|
||||
|
||||
if (s1.Version != s2.Version)
|
||||
{ Message = string.Format(MsgSaveDifferentVersions, $"S1: {s1.Version}", $"S2: {s2.Version}"); return false; }
|
||||
|
||||
Message = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a known value for a <see cref="EventWork{T}"/> of type <see cref="int"/>.
|
||||
/// </summary>
|
||||
public sealed class EventWorkVal
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a known value for a <see cref="EventWork{T}"/> of type <see cref="int"/>.
|
||||
/// </summary>
|
||||
public sealed class EventWorkVal
|
||||
public readonly bool Custom;
|
||||
public readonly string Text;
|
||||
public readonly int Value;
|
||||
|
||||
public EventWorkVal()
|
||||
{
|
||||
public readonly bool Custom;
|
||||
public readonly string Text;
|
||||
public readonly int Value;
|
||||
|
||||
public EventWorkVal()
|
||||
{
|
||||
Custom = true;
|
||||
Text = nameof(Custom);
|
||||
Value = int.MinValue;
|
||||
}
|
||||
|
||||
public EventWorkVal(string text, int val)
|
||||
{
|
||||
Text = text;
|
||||
Value = val;
|
||||
}
|
||||
Custom = true;
|
||||
Text = nameof(Custom);
|
||||
Value = int.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
public EventWorkVal(string text, int val)
|
||||
{
|
||||
Text = text;
|
||||
Value = val;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,66 +1,65 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Editor object that unpacks <see cref="EventWork{T}"/> into flags & work groups, and handles value get/set operations.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public sealed class SplitEventEditor<T> where T : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// Editor object that unpacks <see cref="EventWork{T}"/> into flags & work groups, and handles value get/set operations.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public sealed class SplitEventEditor<T> where T : struct
|
||||
public readonly IList<EventVarGroup> Work;
|
||||
public readonly IList<EventVarGroup> Flag;
|
||||
public readonly IEventVar<T> Block;
|
||||
|
||||
public SplitEventEditor(IEventVar<T> block, IEnumerable<string> work, IEnumerable<string> flag)
|
||||
{
|
||||
public readonly IList<EventVarGroup> Work;
|
||||
public readonly IList<EventVarGroup> Flag;
|
||||
public readonly IEventVar<T> Block;
|
||||
Block = block;
|
||||
// load lines
|
||||
var workLines = work.Where(z => !string.IsNullOrWhiteSpace(z) && z.Length > 5);
|
||||
Work = EventWorkUtil.GetVars(workLines, (index, t, data) => new EventWork<T>(index, t, data));
|
||||
var flagLines = flag.Where(z => !string.IsNullOrWhiteSpace(z) && z.Length > 5);
|
||||
Flag = EventWorkUtil.GetVars(flagLines, (index, t, data) => new EventFlag(index, t, data));
|
||||
|
||||
public SplitEventEditor(IEventVar<T> block, IEnumerable<string> work, IEnumerable<string> flag)
|
||||
// initialize lines
|
||||
foreach (var group in Work)
|
||||
{
|
||||
Block = block;
|
||||
// load lines
|
||||
var workLines = work.Where(z => !string.IsNullOrWhiteSpace(z) && z.Length > 5);
|
||||
Work = EventWorkUtil.GetVars(workLines, (index, t, data) => new EventWork<T>(index, t, data));
|
||||
var flagLines = flag.Where(z => !string.IsNullOrWhiteSpace(z) && z.Length > 5);
|
||||
Flag = EventWorkUtil.GetVars(flagLines, (index, t, data) => new EventFlag(index, t, data));
|
||||
|
||||
// initialize lines
|
||||
foreach (var group in Work)
|
||||
foreach (var item in group.Vars)
|
||||
{
|
||||
foreach (var item in group.Vars)
|
||||
{
|
||||
item.RawIndex = block.GetWorkRawIndex(item.Type, item.RelativeIndex);
|
||||
((EventWork<T>)item).Value = block.GetWork(item.RawIndex);
|
||||
}
|
||||
}
|
||||
foreach (var group in Flag)
|
||||
{
|
||||
foreach (var item in group.Vars)
|
||||
{
|
||||
item.RawIndex = block.GetFlagRawIndex(item.Type, item.RelativeIndex);
|
||||
((EventFlag)item).Flag = block.GetFlag(item.RawIndex);
|
||||
}
|
||||
item.RawIndex = block.GetWorkRawIndex(item.Type, item.RelativeIndex);
|
||||
((EventWork<T>)item).Value = block.GetWork(item.RawIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes all of the updated event values back to the block.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
foreach (var group in Flag)
|
||||
{
|
||||
foreach (var g in Work)
|
||||
foreach (var item in group.Vars)
|
||||
{
|
||||
foreach (var item in g.Vars)
|
||||
{
|
||||
var value = ((EventWork<T>)item).Value;
|
||||
Block.SetWork(item.RawIndex, value);
|
||||
}
|
||||
item.RawIndex = block.GetFlagRawIndex(item.Type, item.RelativeIndex);
|
||||
((EventFlag)item).Flag = block.GetFlag(item.RawIndex);
|
||||
}
|
||||
foreach (var g in Flag)
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes all of the updated event values back to the block.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
foreach (var g in Work)
|
||||
{
|
||||
foreach (var item in g.Vars)
|
||||
{
|
||||
foreach (var item in g.Vars)
|
||||
{
|
||||
var value = ((EventFlag)item).Flag;
|
||||
Block.SetFlag(item.RawIndex, value);
|
||||
}
|
||||
var value = ((EventWork<T>)item).Value;
|
||||
Block.SetWork(item.RawIndex, value);
|
||||
}
|
||||
}
|
||||
foreach (var g in Flag)
|
||||
{
|
||||
foreach (var item in g.Vars)
|
||||
{
|
||||
var value = ((EventFlag)item).Flag;
|
||||
Block.SetFlag(item.RawIndex, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public interface INamedFolderPath
|
||||
{
|
||||
public interface INamedFolderPath
|
||||
{
|
||||
string Path { get; }
|
||||
string DisplayText { get; }
|
||||
bool Custom { get; }
|
||||
}
|
||||
string Path { get; }
|
||||
string DisplayText { get; }
|
||||
bool Custom { get; }
|
||||
}
|
||||
|
|
|
@ -1,49 +1,49 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Bindable Summary of a <see cref="SaveFile"/> for use in a sortable table.
|
||||
/// </summary>
|
||||
public sealed class SavePreview
|
||||
{
|
||||
/// <summary>
|
||||
/// Bindable Summary of a <see cref="SaveFile"/> for use in a sortable table.
|
||||
/// </summary>
|
||||
public sealed class SavePreview
|
||||
public readonly SaveFile Save;
|
||||
public readonly string FilePath;
|
||||
|
||||
public SavePreview(SaveFile sav, string parent, string path)
|
||||
{
|
||||
public readonly SaveFile Save;
|
||||
public readonly string FilePath;
|
||||
|
||||
public SavePreview(SaveFile sav, string parent, string path)
|
||||
{
|
||||
Save = sav;
|
||||
Folder = parent;
|
||||
FilePath = path;
|
||||
}
|
||||
|
||||
public SavePreview(SaveFile sav, List<INamedFolderPath> paths)
|
||||
{
|
||||
var meta = sav.Metadata;
|
||||
var dir = meta.FileFolder;
|
||||
const string notFound = "???";
|
||||
var parent = dir == null ? notFound : paths.Find(z => dir.StartsWith(z.Path))?.DisplayText ?? new DirectoryInfo(dir).Name;
|
||||
|
||||
Save = sav;
|
||||
Folder = parent;
|
||||
FilePath = meta.FilePath ?? notFound;
|
||||
}
|
||||
|
||||
public string OT => Save.OT;
|
||||
public int G => Save.Generation;
|
||||
public GameVersion Game => Save.Version;
|
||||
|
||||
public string Played => Save.PlayTimeString.PadLeft(9, '0');
|
||||
public string FileTime => File.GetLastWriteTimeUtc(FilePath).ToString("yyyy.MM.dd:hh:mm:ss");
|
||||
|
||||
public string TID => Save.Generation >= 7 ? Save.TrainerID7.ToString("000000") : Save.TID.ToString("00000");
|
||||
public string SID => Save.Generation >= 7 ? Save.TrainerSID7.ToString("0000") : Save.SID.ToString("00000");
|
||||
|
||||
// ReSharper disable once MemberCanBePrivate.Local
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||
public string Folder { get; }
|
||||
|
||||
public string Name => Path.GetFileName(FilePath);
|
||||
Save = sav;
|
||||
Folder = parent;
|
||||
FilePath = path;
|
||||
}
|
||||
|
||||
public SavePreview(SaveFile sav, List<INamedFolderPath> paths)
|
||||
{
|
||||
var meta = sav.Metadata;
|
||||
var dir = meta.FileFolder;
|
||||
const string notFound = "???";
|
||||
var parent = dir == null ? notFound : paths.Find(z => dir.StartsWith(z.Path, StringComparison.Ordinal))?.DisplayText ?? new DirectoryInfo(dir).Name;
|
||||
|
||||
Save = sav;
|
||||
Folder = parent;
|
||||
FilePath = meta.FilePath ?? notFound;
|
||||
}
|
||||
|
||||
public string OT => Save.OT;
|
||||
public int G => Save.Generation;
|
||||
public GameVersion Game => Save.Version;
|
||||
|
||||
public string Played => Save.PlayTimeString.PadLeft(9, '0');
|
||||
public string FileTime => File.GetLastWriteTimeUtc(FilePath).ToString("yyyy.MM.dd:hh:mm:ss");
|
||||
|
||||
public string TID => Save.Generation >= 7 ? Save.TrainerID7.ToString("000000") : Save.TID.ToString("00000");
|
||||
public string SID => Save.Generation >= 7 ? Save.TrainerSID7.ToString("0000") : Save.SID.ToString("00000");
|
||||
|
||||
// ReSharper disable once MemberCanBePrivate.Local
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||
public string Folder { get; }
|
||||
|
||||
public string Name => Path.GetFileName(FilePath);
|
||||
}
|
||||
|
|
|
@ -1,58 +1,57 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Box Editor that loads the contents for easy manipulation.
|
||||
/// </summary>
|
||||
public sealed class BoxEdit
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Box Editor that loads the contents for easy manipulation.
|
||||
/// </summary>
|
||||
public sealed class BoxEdit
|
||||
private readonly SaveFile SAV;
|
||||
private readonly PKM[] CurrentContents;
|
||||
|
||||
public BoxEdit(SaveFile sav)
|
||||
{
|
||||
private readonly SaveFile SAV;
|
||||
private readonly PKM[] CurrentContents;
|
||||
SAV = sav;
|
||||
CurrentContents = new PKM[sav.BoxSlotCount];
|
||||
}
|
||||
|
||||
public BoxEdit(SaveFile sav)
|
||||
public void Reload() => LoadBox(CurrentBox);
|
||||
|
||||
public void LoadBox(int box)
|
||||
{
|
||||
if ((uint)box >= SAV.BoxCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(box));
|
||||
|
||||
SAV.AddBoxData(CurrentContents, box, 0);
|
||||
CurrentBox = box;
|
||||
}
|
||||
|
||||
public PKM this[int index]
|
||||
{
|
||||
get => CurrentContents[index];
|
||||
set
|
||||
{
|
||||
SAV = sav;
|
||||
CurrentContents = new PKM[sav.BoxSlotCount];
|
||||
}
|
||||
|
||||
public void Reload() => LoadBox(CurrentBox);
|
||||
|
||||
public void LoadBox(int box)
|
||||
{
|
||||
if ((uint)box >= SAV.BoxCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(box));
|
||||
|
||||
SAV.AddBoxData(CurrentContents, box, 0);
|
||||
CurrentBox = box;
|
||||
}
|
||||
|
||||
public PKM this[int index]
|
||||
{
|
||||
get => CurrentContents[index];
|
||||
set
|
||||
{
|
||||
CurrentContents[index] = value;
|
||||
SAV.SetBoxSlotAtIndex(value, index);
|
||||
}
|
||||
}
|
||||
|
||||
public int CurrentBox { get; private set; }
|
||||
public int BoxWallpaper { get => SAV.GetBoxWallpaper(CurrentBox); set => SAV.SetBoxWallpaper(CurrentBox, value); }
|
||||
public string BoxName { get => SAV.GetBoxName(CurrentBox); set => SAV.SetBoxName(CurrentBox, value); }
|
||||
|
||||
public int MoveLeft(bool max = false)
|
||||
{
|
||||
int newBox = max ? 0 : (CurrentBox + SAV.BoxCount - 1) % SAV.BoxCount;
|
||||
LoadBox(newBox);
|
||||
return newBox;
|
||||
}
|
||||
|
||||
public int MoveRight(bool max = false)
|
||||
{
|
||||
int newBox = max ? SAV.BoxCount - 1 : (CurrentBox + 1) % SAV.BoxCount;
|
||||
LoadBox(newBox);
|
||||
return newBox;
|
||||
CurrentContents[index] = value;
|
||||
SAV.SetBoxSlotAtIndex(value, index);
|
||||
}
|
||||
}
|
||||
|
||||
public int CurrentBox { get; private set; }
|
||||
public int BoxWallpaper { get => SAV.GetBoxWallpaper(CurrentBox); set => SAV.SetBoxWallpaper(CurrentBox, value); }
|
||||
public string BoxName { get => SAV.GetBoxName(CurrentBox); set => SAV.SetBoxName(CurrentBox, value); }
|
||||
|
||||
public int MoveLeft(bool max = false)
|
||||
{
|
||||
int newBox = max ? 0 : (CurrentBox + SAV.BoxCount - 1) % SAV.BoxCount;
|
||||
LoadBox(newBox);
|
||||
return newBox;
|
||||
}
|
||||
|
||||
public int MoveRight(bool max = false)
|
||||
{
|
||||
int newBox = max ? SAV.BoxCount - 1 : (CurrentBox + 1) % SAV.BoxCount;
|
||||
LoadBox(newBox);
|
||||
return newBox;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,228 +1,227 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public static partial class Extensions
|
||||
{
|
||||
public static partial class Extensions
|
||||
public static IReadOnlyList<PKM> GetAllPKM(this SaveFile sav)
|
||||
{
|
||||
public static IReadOnlyList<PKM> GetAllPKM(this SaveFile sav)
|
||||
{
|
||||
var result = new List<PKM>();
|
||||
if (sav.HasBox)
|
||||
result.AddRange(sav.BoxData);
|
||||
if (sav.HasParty)
|
||||
result.AddRange(sav.PartyData);
|
||||
var result = new List<PKM>();
|
||||
if (sav.HasBox)
|
||||
result.AddRange(sav.BoxData);
|
||||
if (sav.HasParty)
|
||||
result.AddRange(sav.PartyData);
|
||||
|
||||
var extra = sav.GetExtraPKM();
|
||||
result.AddRange(extra);
|
||||
result.RemoveAll(z => z.Species == 0);
|
||||
return result;
|
||||
var extra = sav.GetExtraPKM();
|
||||
result.AddRange(extra);
|
||||
result.RemoveAll(z => z.Species == 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static PKM[] GetExtraPKM(this SaveFile sav) => sav.GetExtraPKM(sav.GetExtraSlots());
|
||||
|
||||
public static PKM[] GetExtraPKM(this SaveFile sav, IList<SlotInfoMisc> slots)
|
||||
{
|
||||
var arr = new PKM[slots.Count];
|
||||
for (int i = 0; i < slots.Count; i++)
|
||||
arr[i] = slots[i].Read(sav);
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static List<SlotInfoMisc> GetExtraSlots(this SaveFile sav, bool all = false)
|
||||
{
|
||||
var slots = GetExtraSlotsUnsafe(sav, all);
|
||||
for (int i = 0; i < slots.Count;)
|
||||
{
|
||||
if (slots[i].Offset < 0)
|
||||
slots.RemoveAt(i);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
public static PKM[] GetExtraPKM(this SaveFile sav) => sav.GetExtraPKM(sav.GetExtraSlots());
|
||||
private static readonly List<SlotInfoMisc> None = new();
|
||||
|
||||
public static PKM[] GetExtraPKM(this SaveFile sav, IList<SlotInfoMisc> slots)
|
||||
private static List<SlotInfoMisc> GetExtraSlotsUnsafe(SaveFile sav, bool all) => sav switch
|
||||
{
|
||||
SAV2 sav2 => GetExtraSlots2(sav2),
|
||||
SAV3 sav3 => GetExtraSlots3(sav3),
|
||||
SAV4 sav4 => GetExtraSlots4(sav4),
|
||||
SAV5 sav5 => GetExtraSlots5(sav5),
|
||||
SAV6XY xy => GetExtraSlots6XY(xy),
|
||||
SAV6AO xy => GetExtraSlots6AO(xy),
|
||||
SAV7 sav7 => GetExtraSlots7(sav7, all),
|
||||
SAV7b lgpe => GetExtraSlots7b(lgpe),
|
||||
SAV8SWSH ss => GetExtraSlots8(ss),
|
||||
SAV8BS bs => GetExtraSlots8b(bs),
|
||||
SAV8LA la => GetExtraSlots8a(la),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots2(SAV2 sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
var arr = new PKM[slots.Count];
|
||||
for (int i = 0; i < slots.Count; i++)
|
||||
arr[i] = slots[i].Read(sav);
|
||||
return arr;
|
||||
}
|
||||
new(sav.Data, 0, sav.GetDaycareSlotOffset(0, 2)) {Type = StorageSlotType.Daycare }, // egg
|
||||
};
|
||||
}
|
||||
|
||||
public static List<SlotInfoMisc> GetExtraSlots(this SaveFile sav, bool all = false)
|
||||
private static List<SlotInfoMisc> GetExtraSlots3(SAV3 sav)
|
||||
{
|
||||
if (sav is not SAV3FRLG)
|
||||
return None;
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
var slots = GetExtraSlotsUnsafe(sav, all);
|
||||
for (int i = 0; i < slots.Count;)
|
||||
new(sav.Large, 0, 0x3C98) {Type = StorageSlotType.Daycare},
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots4(SAV4 sav)
|
||||
{
|
||||
var list = new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.General, 0, sav.GTS) {Type = StorageSlotType.GTS},
|
||||
};
|
||||
if (sav is SAV4HGSS)
|
||||
list.Add(new SlotInfoMisc(sav.General, 1, SAV4HGSS.WalkerPair) {Type = StorageSlotType.Misc});
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots5(SAV5 sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, sav.GTS) {Type = StorageSlotType.GTS},
|
||||
new(sav.Data, 0, sav.Fused) {Type = StorageSlotType.Fused},
|
||||
new(sav.Data, 0, sav.PGL) { Type = StorageSlotType.Misc },
|
||||
|
||||
new(sav.Data, 0, sav.GetBattleBoxSlot(0)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 1, sav.GetBattleBoxSlot(1)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 2, sav.GetBattleBoxSlot(2)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 3, sav.GetBattleBoxSlot(3)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 4, sav.GetBattleBoxSlot(4)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 5, sav.GetBattleBoxSlot(5)) {Type = StorageSlotType.BattleBox},
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots6XY(SAV6XY sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, sav.GTS) {Type = StorageSlotType.GTS},
|
||||
new(sav.Data, 0, sav.Fused) {Type = StorageSlotType.Fused},
|
||||
new(sav.Data, 0, sav.SUBE.Give) {Type = StorageSlotType.Misc}, // Old Man
|
||||
|
||||
new(sav.Data, 0, sav.GetBattleBoxSlot(0)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 1, sav.GetBattleBoxSlot(1)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 2, sav.GetBattleBoxSlot(2)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 3, sav.GetBattleBoxSlot(3)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 4, sav.GetBattleBoxSlot(4)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 5, sav.GetBattleBoxSlot(5)) {Type = StorageSlotType.BattleBox},
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots6AO(SAV6AO sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, SAV6AO.GTS) {Type = StorageSlotType.GTS},
|
||||
new(sav.Data, 0, SAV6AO.Fused) {Type = StorageSlotType.Fused},
|
||||
new(sav.Data, 0, sav.SUBE.Give) {Type = StorageSlotType.Misc},
|
||||
|
||||
new(sav.Data, 0, sav.GetBattleBoxSlot(0)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 1, sav.GetBattleBoxSlot(1)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 2, sav.GetBattleBoxSlot(2)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 3, sav.GetBattleBoxSlot(3)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 4, sav.GetBattleBoxSlot(4)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 5, sav.GetBattleBoxSlot(5)) {Type = StorageSlotType.BattleBox},
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots7(SAV7 sav, bool all)
|
||||
{
|
||||
var list = new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, sav.AllBlocks[07].Offset) {Type = StorageSlotType.GTS},
|
||||
new(sav.Data, 0, sav.GetFusedSlotOffset(0)) {Type = StorageSlotType.Fused},
|
||||
};
|
||||
if (sav is SAV7USUM uu)
|
||||
{
|
||||
list.AddRange(new[]
|
||||
{
|
||||
if (slots[i].Offset < 0)
|
||||
slots.RemoveAt(i);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
return slots;
|
||||
new SlotInfoMisc(uu.Data, 1, uu.GetFusedSlotOffset(1)) {Type = StorageSlotType.Fused},
|
||||
new SlotInfoMisc(uu.Data, 2, uu.GetFusedSlotOffset(2)) {Type = StorageSlotType.Fused},
|
||||
});
|
||||
var ba = uu.BattleAgency;
|
||||
list.AddRange(new[]
|
||||
{
|
||||
new SlotInfoMisc(uu.Data, 0, ba.GetSlotOffset(0)) {Type = StorageSlotType.Misc},
|
||||
new SlotInfoMisc(uu.Data, 1, ba.GetSlotOffset(1)) {Type = StorageSlotType.Misc},
|
||||
new SlotInfoMisc(uu.Data, 2, ba.GetSlotOffset(2)) {Type = StorageSlotType.Misc},
|
||||
});
|
||||
}
|
||||
|
||||
private static readonly List<SlotInfoMisc> None = new();
|
||||
if (!all)
|
||||
return list;
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlotsUnsafe(SaveFile sav, bool all) => sav switch
|
||||
for (int i = 0; i < ResortSave7.ResortCount; i++)
|
||||
list.Add(new SlotInfoMisc(sav.Data, i, sav.ResortSave.GetResortSlotOffset(i)) { Type = StorageSlotType.Resort });
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots7b(SAV7b sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
SAV2 sav2 => GetExtraSlots2(sav2),
|
||||
SAV3 sav3 => GetExtraSlots3(sav3),
|
||||
SAV4 sav4 => GetExtraSlots4(sav4),
|
||||
SAV5 sav5 => GetExtraSlots5(sav5),
|
||||
SAV6XY xy => GetExtraSlots6XY(xy),
|
||||
SAV6AO xy => GetExtraSlots6AO(xy),
|
||||
SAV7 sav7 => GetExtraSlots7(sav7, all),
|
||||
SAV7b lgpe => GetExtraSlots7b(lgpe),
|
||||
SAV8SWSH ss => GetExtraSlots8(ss),
|
||||
SAV8BS bs => GetExtraSlots8b(bs),
|
||||
SAV8LA la => GetExtraSlots8a(la),
|
||||
_ => None,
|
||||
new(sav.Data, 0, sav.Blocks.GetBlockOffset(BelugaBlockIndex.Daycare) + 8, true) {Type = StorageSlotType.Daycare},
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots8(ISaveBlock8Main sav)
|
||||
{
|
||||
var fused = sav.Fused;
|
||||
var dc = sav.Daycare;
|
||||
var list = new List<SlotInfoMisc>
|
||||
{
|
||||
new(fused.Data, 0, Fused8.GetFusedSlotOffset(0), true) {Type = StorageSlotType.Fused},
|
||||
new(fused.Data, 1, Fused8.GetFusedSlotOffset(1), true) {Type = StorageSlotType.Fused},
|
||||
new(fused.Data, 2, Fused8.GetFusedSlotOffset(2), true) {Type = StorageSlotType.Fused},
|
||||
|
||||
new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(0, 0)) {Type = StorageSlotType.Daycare},
|
||||
new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(0, 1)) {Type = StorageSlotType.Daycare},
|
||||
new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(1, 0)) {Type = StorageSlotType.Daycare},
|
||||
new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(1, 1)) {Type = StorageSlotType.Daycare},
|
||||
};
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots2(SAV2 sav)
|
||||
if (sav is SAV8SWSH {SaveRevision: >= 2} s8)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, sav.GetDaycareSlotOffset(0, 2)) {Type = StorageSlotType.Daycare }, // egg
|
||||
};
|
||||
var block = s8.Blocks.GetBlockSafe(SaveBlockAccessor8SWSH.KFusedCalyrex);
|
||||
var c = new SlotInfoMisc(block.Data, 3, 0, true) {Type = StorageSlotType.Fused};
|
||||
list.Insert(3, c);
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots3(SAV3 sav)
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots8b(SAV8BS sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
if (sav is not SAV3FRLG)
|
||||
return None;
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Large, 0, 0x3C98) {Type = StorageSlotType.Daycare},
|
||||
};
|
||||
}
|
||||
new(sav.Data, 0, sav.UgSaveData.GetSlotOffset(0), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 1, sav.UgSaveData.GetSlotOffset(1), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 2, sav.UgSaveData.GetSlotOffset(2), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 3, sav.UgSaveData.GetSlotOffset(3), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 4, sav.UgSaveData.GetSlotOffset(4), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 5, sav.UgSaveData.GetSlotOffset(5), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 6, sav.UgSaveData.GetSlotOffset(6), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 7, sav.UgSaveData.GetSlotOffset(7), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 8, sav.UgSaveData.GetSlotOffset(8), true) { Type = StorageSlotType.Misc },
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots4(SAV4 sav)
|
||||
{
|
||||
var list = new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.General, 0, sav.GTS) {Type = StorageSlotType.GTS},
|
||||
};
|
||||
if (sav is SAV4HGSS)
|
||||
list.Add(new SlotInfoMisc(sav.General, 1, SAV4HGSS.WalkerPair) {Type = StorageSlotType.Misc});
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots5(SAV5 sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, sav.GTS) {Type = StorageSlotType.GTS},
|
||||
new(sav.Data, 0, sav.Fused) {Type = StorageSlotType.Fused},
|
||||
new(sav.Data, 0, sav.PGL) { Type = StorageSlotType.Misc },
|
||||
|
||||
new(sav.Data, 0, sav.GetBattleBoxSlot(0)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 1, sav.GetBattleBoxSlot(1)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 2, sav.GetBattleBoxSlot(2)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 3, sav.GetBattleBoxSlot(3)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 4, sav.GetBattleBoxSlot(4)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 5, sav.GetBattleBoxSlot(5)) {Type = StorageSlotType.BattleBox},
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots6XY(SAV6XY sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, sav.GTS) {Type = StorageSlotType.GTS},
|
||||
new(sav.Data, 0, sav.Fused) {Type = StorageSlotType.Fused},
|
||||
new(sav.Data, 0, sav.SUBE.Give) {Type = StorageSlotType.Misc}, // Old Man
|
||||
|
||||
new(sav.Data, 0, sav.GetBattleBoxSlot(0)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 1, sav.GetBattleBoxSlot(1)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 2, sav.GetBattleBoxSlot(2)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 3, sav.GetBattleBoxSlot(3)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 4, sav.GetBattleBoxSlot(4)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 5, sav.GetBattleBoxSlot(5)) {Type = StorageSlotType.BattleBox},
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots6AO(SAV6AO sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, SAV6AO.GTS) {Type = StorageSlotType.GTS},
|
||||
new(sav.Data, 0, SAV6AO.Fused) {Type = StorageSlotType.Fused},
|
||||
new(sav.Data, 0, sav.SUBE.Give) {Type = StorageSlotType.Misc},
|
||||
|
||||
new(sav.Data, 0, sav.GetBattleBoxSlot(0)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 1, sav.GetBattleBoxSlot(1)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 2, sav.GetBattleBoxSlot(2)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 3, sav.GetBattleBoxSlot(3)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 4, sav.GetBattleBoxSlot(4)) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.Data, 5, sav.GetBattleBoxSlot(5)) {Type = StorageSlotType.BattleBox},
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots7(SAV7 sav, bool all)
|
||||
{
|
||||
var list = new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, sav.AllBlocks[07].Offset) {Type = StorageSlotType.GTS},
|
||||
new(sav.Data, 0, sav.GetFusedSlotOffset(0)) {Type = StorageSlotType.Fused},
|
||||
};
|
||||
if (sav is SAV7USUM uu)
|
||||
{
|
||||
list.AddRange(new[]
|
||||
{
|
||||
new SlotInfoMisc(uu.Data, 1, uu.GetFusedSlotOffset(1)) {Type = StorageSlotType.Fused},
|
||||
new SlotInfoMisc(uu.Data, 2, uu.GetFusedSlotOffset(2)) {Type = StorageSlotType.Fused},
|
||||
});
|
||||
var ba = uu.BattleAgency;
|
||||
list.AddRange(new[]
|
||||
{
|
||||
new SlotInfoMisc(uu.Data, 0, ba.GetSlotOffset(0)) {Type = StorageSlotType.Misc},
|
||||
new SlotInfoMisc(uu.Data, 1, ba.GetSlotOffset(1)) {Type = StorageSlotType.Misc},
|
||||
new SlotInfoMisc(uu.Data, 2, ba.GetSlotOffset(2)) {Type = StorageSlotType.Misc},
|
||||
});
|
||||
}
|
||||
|
||||
if (!all)
|
||||
return list;
|
||||
|
||||
for (int i = 0; i < ResortSave7.ResortCount; i++)
|
||||
list.Add(new SlotInfoMisc(sav.Data, i, sav.ResortSave.GetResortSlotOffset(i)) { Type = StorageSlotType.Resort });
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots7b(SAV7b sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, sav.Blocks.GetBlockOffset(BelugaBlockIndex.Daycare) + 8, true) {Type = StorageSlotType.Daycare},
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots8(ISaveBlock8Main sav)
|
||||
{
|
||||
var fused = sav.Fused;
|
||||
var dc = sav.Daycare;
|
||||
var list = new List<SlotInfoMisc>
|
||||
{
|
||||
new(fused.Data, 0, Fused8.GetFusedSlotOffset(0), true) {Type = StorageSlotType.Fused},
|
||||
new(fused.Data, 1, Fused8.GetFusedSlotOffset(1), true) {Type = StorageSlotType.Fused},
|
||||
new(fused.Data, 2, Fused8.GetFusedSlotOffset(2), true) {Type = StorageSlotType.Fused},
|
||||
|
||||
new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(0, 0)) {Type = StorageSlotType.Daycare},
|
||||
new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(0, 1)) {Type = StorageSlotType.Daycare},
|
||||
new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(1, 0)) {Type = StorageSlotType.Daycare},
|
||||
new(dc.Data, 0, Daycare8.GetDaycareSlotOffset(1, 1)) {Type = StorageSlotType.Daycare},
|
||||
};
|
||||
|
||||
if (sav is SAV8SWSH {SaveRevision: >= 2} s8)
|
||||
{
|
||||
var block = s8.Blocks.GetBlockSafe(SaveBlockAccessor8SWSH.KFusedCalyrex);
|
||||
var c = new SlotInfoMisc(block.Data, 3, 0, true) {Type = StorageSlotType.Fused};
|
||||
list.Insert(3, c);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots8b(SAV8BS sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, sav.UgSaveData.GetSlotOffset(0), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 1, sav.UgSaveData.GetSlotOffset(1), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 2, sav.UgSaveData.GetSlotOffset(2), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 3, sav.UgSaveData.GetSlotOffset(3), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 4, sav.UgSaveData.GetSlotOffset(4), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 5, sav.UgSaveData.GetSlotOffset(5), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 6, sav.UgSaveData.GetSlotOffset(6), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 7, sav.UgSaveData.GetSlotOffset(7), true) { Type = StorageSlotType.Misc },
|
||||
new(sav.Data, 8, sav.UgSaveData.GetSlotOffset(8), true) { Type = StorageSlotType.Misc },
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SlotInfoMisc> GetExtraSlots8a(SAV8LA sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>();
|
||||
}
|
||||
private static List<SlotInfoMisc> GetExtraSlots8a(SAV8LA sav)
|
||||
{
|
||||
return new List<SlotInfoMisc>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,54 +1,51 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Slot Viewer that shows many slots of <see cref="PKM"/> data.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Object that displays the <see cref="PKM"/> slot.</typeparam>
|
||||
public interface ISlotViewer<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Slot Viewer that shows many slots of <see cref="PKM"/> data.
|
||||
/// Current index the viewer is viewing.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Object that displays the <see cref="PKM"/> slot.</typeparam>
|
||||
public interface ISlotViewer<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Current index the viewer is viewing.
|
||||
/// </summary>
|
||||
int ViewIndex { get; }
|
||||
int ViewIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Notification that the <see cref="previous"/> slot is no longer the last interacted slot.
|
||||
/// </summary>
|
||||
/// <param name="previous">Last interacted slot</param>
|
||||
void NotifySlotOld(ISlotInfo previous);
|
||||
/// <summary>
|
||||
/// Notification that the <see cref="previous"/> slot is no longer the last interacted slot.
|
||||
/// </summary>
|
||||
/// <param name="previous">Last interacted slot</param>
|
||||
void NotifySlotOld(ISlotInfo previous);
|
||||
|
||||
/// <summary>
|
||||
/// Notification that the <see cref="slot"/> has just been interacted with.
|
||||
/// </summary>
|
||||
/// <param name="slot">Last interacted slot</param>
|
||||
/// <param name="type">Last interacted slot interaction type</param>
|
||||
/// <param name="pkm">Last interacted slot interaction data</param>
|
||||
void NotifySlotChanged(ISlotInfo slot, SlotTouchType type, PKM pkm);
|
||||
/// <summary>
|
||||
/// Notification that the <see cref="slot"/> has just been interacted with.
|
||||
/// </summary>
|
||||
/// <param name="slot">Last interacted slot</param>
|
||||
/// <param name="type">Last interacted slot interaction type</param>
|
||||
/// <param name="pk">Last interacted slot interaction data</param>
|
||||
void NotifySlotChanged(ISlotInfo slot, SlotTouchType type, PKM pk);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the information for the requested slot's view picture.
|
||||
/// </summary>
|
||||
/// <param name="view"></param>
|
||||
/// <returns></returns>
|
||||
ISlotInfo GetSlotData(T view);
|
||||
/// <summary>
|
||||
/// Gets the information for the requested slot's view picture.
|
||||
/// </summary>
|
||||
/// <param name="view"></param>
|
||||
ISlotInfo GetSlotData(T view);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the <see cref="T"/> view within the <see cref="ISlotViewer{T}"/>'s list of slots.
|
||||
/// </summary>
|
||||
/// <param name="slot"></param>
|
||||
/// <returns></returns>
|
||||
int GetViewIndex(ISlotInfo slot);
|
||||
/// <summary>
|
||||
/// Gets the index of the <see cref="T"/> view within the <see cref="ISlotViewer{T}"/>'s list of slots.
|
||||
/// </summary>
|
||||
/// <param name="slot"></param>
|
||||
int GetViewIndex(ISlotInfo slot);
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="T"/> views the <see cref="ISlotViewer{T}"/> is showing.
|
||||
/// </summary>
|
||||
IList<T> SlotPictureBoxes { get; }
|
||||
/// <summary>
|
||||
/// List of <see cref="T"/> views the <see cref="ISlotViewer{T}"/> is showing.
|
||||
/// </summary>
|
||||
IList<T> SlotPictureBoxes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Save data the <see cref="ISlotViewer{T}"/> is showing data from.
|
||||
/// </summary>
|
||||
SaveFile SAV { get; }
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Save data the <see cref="ISlotViewer{T}"/> is showing data from.
|
||||
/// </summary>
|
||||
SaveFile SAV { get; }
|
||||
}
|
||||
|
|
|
@ -1,48 +1,47 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Data representing info for an individual slot.
|
||||
/// </summary>
|
||||
public interface ISlotInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Data representing info for an individual slot.
|
||||
/// Indicates the type of format the slot originates. Useful for legality purposes.
|
||||
/// </summary>
|
||||
public interface ISlotInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the type of format the slot originates. Useful for legality purposes.
|
||||
/// </summary>
|
||||
SlotOrigin Origin { get; }
|
||||
SlotOrigin Origin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Differentiating slot number from other infos of the same type.
|
||||
/// </summary>
|
||||
int Slot { get; }
|
||||
/// <summary>
|
||||
/// Differentiating slot number from other infos of the same type.
|
||||
/// </summary>
|
||||
int Slot { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if this slot can write to the requested <see cref="sav"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save file to try writing to.</param>
|
||||
/// <returns>True if can write to</returns>
|
||||
bool CanWriteTo(SaveFile sav);
|
||||
/// <summary>
|
||||
/// Indicates if this slot can write to the requested <see cref="sav"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save file to try writing to.</param>
|
||||
/// <returns>True if can write to</returns>
|
||||
bool CanWriteTo(SaveFile sav);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="pkm"/> can be written to the <see cref="sav"/> for this slot.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save file to try writing to.</param>
|
||||
/// <param name="pkm">Entity data to try writing.</param>
|
||||
/// <returns>True if can write to</returns>
|
||||
WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pkm);
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="pk"/> can be written to the <see cref="sav"/> for this slot.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save file to try writing to.</param>
|
||||
/// <param name="pk">Entity data to try writing.</param>
|
||||
/// <returns>True if can write to</returns>
|
||||
WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk);
|
||||
|
||||
/// <summary>
|
||||
/// Tries writing the <see cref="pkm"/> to the <see cref="sav"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save file to try writing to.</param>
|
||||
/// <param name="pkm">Entity data to try writing.</param>
|
||||
/// <param name="setting">Setting to use when importing the <see cref="pkm"/> data</param>
|
||||
/// <returns>Returns false if it did not succeed.</returns>
|
||||
bool WriteTo(SaveFile sav, PKM pkm, PKMImportSetting setting = PKMImportSetting.UseDefault);
|
||||
/// <summary>
|
||||
/// Tries writing the <see cref="pk"/> to the <see cref="sav"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save file to try writing to.</param>
|
||||
/// <param name="pk">Entity data to try writing.</param>
|
||||
/// <param name="setting">Setting to use when importing the <see cref="pk"/> data</param>
|
||||
/// <returns>Returns false if it did not succeed.</returns>
|
||||
bool WriteTo(SaveFile sav, PKM pk, PKMImportSetting setting = PKMImportSetting.UseDefault);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a <see cref="PKM"/> from the <see cref="sav"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save file to read from.</param>
|
||||
PKM Read(SaveFile sav);
|
||||
}
|
||||
/// <summary>
|
||||
/// Reads a <see cref="PKM"/> from the <see cref="sav"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save file to read from.</param>
|
||||
PKM Read(SaveFile sav);
|
||||
}
|
||||
|
|
|
@ -1,95 +1,94 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains slot data and metadata indicating where the <see cref="PKM"/> originated from.
|
||||
/// </summary>
|
||||
public sealed class SlotCache : IComparable<SlotCache>
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains slot data and metadata indicating where the <see cref="PKM"/> originated from.
|
||||
/// Information regarding how the <see cref="Entity"/> was obtained.
|
||||
/// </summary>
|
||||
public sealed class SlotCache : IComparable<SlotCache>
|
||||
public readonly ISlotInfo Source;
|
||||
|
||||
/// <summary>
|
||||
/// Save File reference that obtained the <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
public readonly SaveFile SAV;
|
||||
|
||||
/// <summary>
|
||||
/// Data that was loaded.
|
||||
/// </summary>
|
||||
public readonly PKM Entity;
|
||||
|
||||
private static readonly FakeSaveFile NoSaveFile = new();
|
||||
|
||||
public SlotCache(SlotInfoFile source, PKM entity)
|
||||
{
|
||||
/// <summary>
|
||||
/// Information regarding how the <see cref="Entity"/> was obtained.
|
||||
/// </summary>
|
||||
public readonly ISlotInfo Source;
|
||||
Source = source;
|
||||
Entity = entity;
|
||||
SAV = NoSaveFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save File reference that obtained the <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
public readonly SaveFile SAV;
|
||||
public SlotCache(ISlotInfo source, PKM entity, SaveFile sav)
|
||||
{
|
||||
Source = source;
|
||||
Entity = entity;
|
||||
SAV = sav;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data that was loaded.
|
||||
/// </summary>
|
||||
public readonly PKM Entity;
|
||||
public string Identify() => GetFileName() + Source switch
|
||||
{
|
||||
SlotInfoBox box => $"[{box.Box + 1:00}] ({SAV.GetBoxName(box.Box)})-{box.Slot + 1:00}: {Entity.FileName}",
|
||||
SlotInfoFile file => $"File: {file.Path}",
|
||||
SlotInfoMisc misc => $"{misc.Type}-{misc.Slot}: {Entity.FileName}",
|
||||
SlotInfoParty party => $"Party: {party.Slot}: {Entity.FileName}",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(Source)),
|
||||
};
|
||||
|
||||
private static readonly FakeSaveFile NoSaveFile = new();
|
||||
private string GetFileName()
|
||||
{
|
||||
var fn = SAV.Metadata.FileName;
|
||||
if (fn is null)
|
||||
return string.Empty;
|
||||
return $"{fn} @ ";
|
||||
}
|
||||
|
||||
public SlotCache(SlotInfoFile source, PKM entity)
|
||||
{
|
||||
Source = source;
|
||||
Entity = entity;
|
||||
SAV = NoSaveFile;
|
||||
}
|
||||
public bool IsDataValid() => Entity.Species != 0 && Entity.Valid;
|
||||
|
||||
public SlotCache(ISlotInfo source, PKM entity, SaveFile sav)
|
||||
{
|
||||
Source = source;
|
||||
Entity = entity;
|
||||
SAV = sav;
|
||||
}
|
||||
public int CompareTo(SlotCache? other)
|
||||
{
|
||||
if (other is null)
|
||||
return -1;
|
||||
return string.CompareOrdinal(Identify(), other.Identify());
|
||||
}
|
||||
|
||||
public string Identify() => GetFileName() + Source switch
|
||||
{
|
||||
SlotInfoBox box => $"[{box.Box + 1:00}] ({SAV.GetBoxName(box.Box)})-{box.Slot + 1:00}: {Entity.FileName}",
|
||||
SlotInfoFile file => $"File: {file.Path}",
|
||||
SlotInfoMisc misc => $"{misc.Type}-{misc.Slot}: {Entity.FileName}",
|
||||
SlotInfoParty party => $"Party: {party.Slot}: {Entity.FileName}",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(Source)),
|
||||
};
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(this, obj))
|
||||
return true;
|
||||
return obj is SlotCache c && c.Identify() == Identify();
|
||||
}
|
||||
|
||||
private string GetFileName()
|
||||
{
|
||||
var fn = SAV.Metadata.FileName;
|
||||
if (fn is null)
|
||||
return string.Empty;
|
||||
return $"{fn} @ ";
|
||||
}
|
||||
public override int GetHashCode() => Identify().GetHashCode();
|
||||
public static bool operator ==(SlotCache left, SlotCache right) => left.Equals(right);
|
||||
public static bool operator !=(SlotCache left, SlotCache right) => !(left == right);
|
||||
public static bool operator <(SlotCache left, SlotCache right) => left.CompareTo(right) < 0;
|
||||
public static bool operator <=(SlotCache left, SlotCache right) => left.CompareTo(right) <= 0;
|
||||
public static bool operator >(SlotCache left, SlotCache right) => left.CompareTo(right) > 0;
|
||||
public static bool operator >=(SlotCache left, SlotCache right) => left.CompareTo(right) >= 0;
|
||||
|
||||
public bool IsDataValid() => Entity.Species != 0 && Entity.Valid;
|
||||
|
||||
public int CompareTo(SlotCache? other)
|
||||
{
|
||||
if (other is null)
|
||||
return -1;
|
||||
return string.CompareOrdinal(Identify(), other.Identify());
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(this, obj))
|
||||
return true;
|
||||
return obj is SlotCache c && c.Identify() == Identify();
|
||||
}
|
||||
|
||||
public override int GetHashCode() => Identify().GetHashCode();
|
||||
public static bool operator ==(SlotCache left, SlotCache right) => left.Equals(right);
|
||||
public static bool operator !=(SlotCache left, SlotCache right) => !(left == right);
|
||||
public static bool operator <(SlotCache left, SlotCache right) => left.CompareTo(right) < 0;
|
||||
public static bool operator <=(SlotCache left, SlotCache right) => left.CompareTo(right) <= 0;
|
||||
public static bool operator >(SlotCache left, SlotCache right) => left.CompareTo(right) > 0;
|
||||
public static bool operator >=(SlotCache left, SlotCache right) => left.CompareTo(right) >= 0;
|
||||
|
||||
public int CompareToSpeciesForm(SlotCache other)
|
||||
{
|
||||
var s1 = Entity;
|
||||
var s2 = other.Entity;
|
||||
var c1 = s1.Species.CompareTo(s2.Species);
|
||||
if (c1 != 0)
|
||||
return c1;
|
||||
var c2 = s1.Form.CompareTo(s2.Form);
|
||||
if (c2 != 0)
|
||||
return c2;
|
||||
return CompareTo(other);
|
||||
}
|
||||
public int CompareToSpeciesForm(SlotCache other)
|
||||
{
|
||||
var s1 = Entity;
|
||||
var s2 = other.Entity;
|
||||
var c1 = s1.Species.CompareTo(s2.Species);
|
||||
if (c1 != 0)
|
||||
return c1;
|
||||
var c2 = s1.Form.CompareTo(s2.Form);
|
||||
if (c2 != 0)
|
||||
return c2;
|
||||
return CompareTo(other);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Box Data <see cref="ISlotInfo"/>
|
||||
/// </summary>
|
||||
public sealed record SlotInfoBox(int Box, int Slot) : ISlotInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Box Data <see cref="ISlotInfo"/>
|
||||
/// </summary>
|
||||
public sealed record SlotInfoBox(int Box, int Slot) : ISlotInfo
|
||||
public SlotOrigin Origin => SlotOrigin.Box;
|
||||
public bool CanWriteTo(SaveFile sav) => sav.HasBox && !sav.IsSlotLocked(Box, Slot);
|
||||
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk) => WriteBlockedMessage.None;
|
||||
|
||||
public bool WriteTo(SaveFile sav, PKM pk, PKMImportSetting setting = PKMImportSetting.UseDefault)
|
||||
{
|
||||
public SlotOrigin Origin => SlotOrigin.Box;
|
||||
public bool CanWriteTo(SaveFile sav) => sav.HasBox && !sav.IsSlotLocked(Box, Slot);
|
||||
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pkm) => WriteBlockedMessage.None;
|
||||
|
||||
public bool WriteTo(SaveFile sav, PKM pkm, PKMImportSetting setting = PKMImportSetting.UseDefault)
|
||||
{
|
||||
sav.SetBoxSlotAtIndex(pkm, Box, Slot, setting, setting);
|
||||
return true;
|
||||
}
|
||||
|
||||
public PKM Read(SaveFile sav) => sav.GetBoxSlotAtIndex(Box, Slot);
|
||||
sav.SetBoxSlotAtIndex(pk, Box, Slot, setting, setting);
|
||||
return true;
|
||||
}
|
||||
|
||||
public PKM Read(SaveFile sav) => sav.GetBoxSlotAtIndex(Box, Slot);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public sealed record SlotInfoFile(string Path) : ISlotInfo
|
||||
{
|
||||
public SlotOrigin Origin => SlotOrigin.Party;
|
||||
public int Slot => 0;
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public bool CanWriteTo(SaveFile sav) => false;
|
||||
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pkm) => WriteBlockedMessage.InvalidDestination;
|
||||
public bool WriteTo(SaveFile sav, PKM pkm, PKMImportSetting setting = PKMImportSetting.UseDefault) => false;
|
||||
public PKM Read(SaveFile sav) => sav.BlankPKM;
|
||||
}
|
||||
public sealed record SlotInfoFile(string Path) : ISlotInfo
|
||||
{
|
||||
public SlotOrigin Origin => SlotOrigin.Party;
|
||||
public int Slot => 0;
|
||||
|
||||
public bool CanWriteTo(SaveFile sav) => false;
|
||||
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk) => WriteBlockedMessage.InvalidDestination;
|
||||
public bool WriteTo(SaveFile sav, PKM pk, PKMImportSetting setting = PKMImportSetting.UseDefault) => false;
|
||||
public PKM Read(SaveFile sav) => sav.BlankPKM;
|
||||
}
|
||||
|
|
|
@ -2,160 +2,159 @@ using System.Collections.Concurrent;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public static class SlotInfoLoader
|
||||
{
|
||||
public static class SlotInfoLoader
|
||||
// The "Add" method isn't shared for any interface... so we'll just do this twice.
|
||||
|
||||
#region ConcurrentBag Implementation
|
||||
public static void AddFromSaveFile(SaveFile sav, ConcurrentBag<SlotCache> db)
|
||||
{
|
||||
// The "Add" method isn't shared for any interface... so we'll just do this twice.
|
||||
if (sav.HasBox)
|
||||
AddBoxData(sav, db);
|
||||
|
||||
#region ConcurrentBag Implementation
|
||||
public static void AddFromSaveFile(SaveFile sav, ConcurrentBag<SlotCache> db)
|
||||
{
|
||||
if (sav.HasBox)
|
||||
AddBoxData(sav, db);
|
||||
if (sav.HasParty)
|
||||
AddPartyData(sav, db);
|
||||
|
||||
if (sav.HasParty)
|
||||
AddPartyData(sav, db);
|
||||
|
||||
AddExtraData(sav, db);
|
||||
}
|
||||
|
||||
public static void AddFromLocalFile(string file, ConcurrentBag<SlotCache> db, ITrainerInfo dest, ICollection<string> validExtensions)
|
||||
{
|
||||
var fi = new FileInfo(file);
|
||||
if (!validExtensions.Contains(fi.Extension) || !EntityDetection.IsSizePlausible(fi.Length))
|
||||
return;
|
||||
|
||||
var data = File.ReadAllBytes(file);
|
||||
_ = FileUtil.TryGetPKM(data, out var pk, fi.Extension, dest);
|
||||
if (pk?.Species is not > 0)
|
||||
return;
|
||||
|
||||
var info = new SlotInfoFile(file);
|
||||
var entry = new SlotCache(info, pk);
|
||||
db.Add(entry);
|
||||
}
|
||||
|
||||
private static void AddBoxData(SaveFile sav, ConcurrentBag<SlotCache> db)
|
||||
{
|
||||
var bd = sav.BoxData;
|
||||
var bc = sav.BoxCount;
|
||||
var sc = sav.BoxSlotCount;
|
||||
int ctr = 0;
|
||||
for (int box = 0; box < bc; box++)
|
||||
{
|
||||
for (int slot = 0; slot < sc; slot++, ctr++)
|
||||
{
|
||||
var ident = new SlotInfoBox(box, slot);
|
||||
var result = new SlotCache(ident, bd[ctr], sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddPartyData(SaveFile sav, ConcurrentBag<SlotCache> db)
|
||||
{
|
||||
var pd = sav.PartyData;
|
||||
for (var index = 0; index < pd.Count; index++)
|
||||
{
|
||||
var pk = pd[index];
|
||||
if (pk.Species == 0)
|
||||
continue;
|
||||
|
||||
var ident = new SlotInfoParty(index);
|
||||
var result = new SlotCache(ident, pk, sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddExtraData(SaveFile sav, ConcurrentBag<SlotCache> db)
|
||||
{
|
||||
var extra = sav.GetExtraSlots(true);
|
||||
foreach (var x in extra)
|
||||
{
|
||||
var pk = x.Read(sav);
|
||||
if (pk.Species == 0)
|
||||
continue;
|
||||
|
||||
var result = new SlotCache(x, pk, sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ICollection Implementation
|
||||
public static void AddFromSaveFile(SaveFile sav, ICollection<SlotCache> db)
|
||||
{
|
||||
if (sav.HasBox)
|
||||
AddBoxData(sav, db);
|
||||
|
||||
if (sav.HasParty)
|
||||
AddPartyData(sav, db);
|
||||
|
||||
AddExtraData(sav, db);
|
||||
}
|
||||
|
||||
public static void AddFromLocalFile(string file, ICollection<SlotCache> db, ITrainerInfo dest, ICollection<string> validExtensions)
|
||||
{
|
||||
var fi = new FileInfo(file);
|
||||
if (!validExtensions.Contains(fi.Extension) || !EntityDetection.IsSizePlausible(fi.Length))
|
||||
return;
|
||||
|
||||
var data = File.ReadAllBytes(file);
|
||||
_ = FileUtil.TryGetPKM(data, out var pk, fi.Extension, dest);
|
||||
if (pk?.Species is not > 0)
|
||||
return;
|
||||
|
||||
var info = new SlotInfoFile(file);
|
||||
var entry = new SlotCache(info, pk);
|
||||
db.Add(entry);
|
||||
}
|
||||
|
||||
public static void AddBoxData(SaveFile sav, ICollection<SlotCache> db)
|
||||
{
|
||||
var bd = sav.BoxData;
|
||||
var bc = sav.BoxCount;
|
||||
var sc = sav.BoxSlotCount;
|
||||
int ctr = 0;
|
||||
for (int box = 0; box < bc; box++)
|
||||
{
|
||||
for (int slot = 0; slot < sc; slot++, ctr++)
|
||||
{
|
||||
var ident = new SlotInfoBox(box, slot);
|
||||
var result = new SlotCache(ident, bd[ctr], sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddPartyData(SaveFile sav, ICollection<SlotCache> db)
|
||||
{
|
||||
var pd = sav.PartyData;
|
||||
for (var index = 0; index < pd.Count; index++)
|
||||
{
|
||||
var pk = pd[index];
|
||||
if (pk.Species == 0)
|
||||
continue;
|
||||
|
||||
var ident = new SlotInfoParty(index);
|
||||
var result = new SlotCache(ident, pk, sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddExtraData(SaveFile sav, ICollection<SlotCache> db)
|
||||
{
|
||||
var extra = sav.GetExtraSlots(true);
|
||||
foreach (var x in extra)
|
||||
{
|
||||
var pk = x.Read(sav);
|
||||
if (pk.Species == 0)
|
||||
continue;
|
||||
|
||||
var result = new SlotCache(x, pk, sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
AddExtraData(sav, db);
|
||||
}
|
||||
|
||||
public static void AddFromLocalFile(string file, ConcurrentBag<SlotCache> db, ITrainerInfo dest, ICollection<string> validExtensions)
|
||||
{
|
||||
var fi = new FileInfo(file);
|
||||
if (!validExtensions.Contains(fi.Extension) || !EntityDetection.IsSizePlausible(fi.Length))
|
||||
return;
|
||||
|
||||
var data = File.ReadAllBytes(file);
|
||||
_ = FileUtil.TryGetPKM(data, out var pk, fi.Extension, dest);
|
||||
if (pk?.Species is not > 0)
|
||||
return;
|
||||
|
||||
var info = new SlotInfoFile(file);
|
||||
var entry = new SlotCache(info, pk);
|
||||
db.Add(entry);
|
||||
}
|
||||
|
||||
private static void AddBoxData(SaveFile sav, ConcurrentBag<SlotCache> db)
|
||||
{
|
||||
var bd = sav.BoxData;
|
||||
var bc = sav.BoxCount;
|
||||
var sc = sav.BoxSlotCount;
|
||||
int ctr = 0;
|
||||
for (int box = 0; box < bc; box++)
|
||||
{
|
||||
for (int slot = 0; slot < sc; slot++, ctr++)
|
||||
{
|
||||
var ident = new SlotInfoBox(box, slot);
|
||||
var result = new SlotCache(ident, bd[ctr], sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddPartyData(SaveFile sav, ConcurrentBag<SlotCache> db)
|
||||
{
|
||||
var pd = sav.PartyData;
|
||||
for (var index = 0; index < pd.Count; index++)
|
||||
{
|
||||
var pk = pd[index];
|
||||
if (pk.Species == 0)
|
||||
continue;
|
||||
|
||||
var ident = new SlotInfoParty(index);
|
||||
var result = new SlotCache(ident, pk, sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddExtraData(SaveFile sav, ConcurrentBag<SlotCache> db)
|
||||
{
|
||||
var extra = sav.GetExtraSlots(true);
|
||||
foreach (var x in extra)
|
||||
{
|
||||
var pk = x.Read(sav);
|
||||
if (pk.Species == 0)
|
||||
continue;
|
||||
|
||||
var result = new SlotCache(x, pk, sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ICollection Implementation
|
||||
public static void AddFromSaveFile(SaveFile sav, ICollection<SlotCache> db)
|
||||
{
|
||||
if (sav.HasBox)
|
||||
AddBoxData(sav, db);
|
||||
|
||||
if (sav.HasParty)
|
||||
AddPartyData(sav, db);
|
||||
|
||||
AddExtraData(sav, db);
|
||||
}
|
||||
|
||||
public static void AddFromLocalFile(string file, ICollection<SlotCache> db, ITrainerInfo dest, ICollection<string> validExtensions)
|
||||
{
|
||||
var fi = new FileInfo(file);
|
||||
if (!validExtensions.Contains(fi.Extension) || !EntityDetection.IsSizePlausible(fi.Length))
|
||||
return;
|
||||
|
||||
var data = File.ReadAllBytes(file);
|
||||
_ = FileUtil.TryGetPKM(data, out var pk, fi.Extension, dest);
|
||||
if (pk?.Species is not > 0)
|
||||
return;
|
||||
|
||||
var info = new SlotInfoFile(file);
|
||||
var entry = new SlotCache(info, pk);
|
||||
db.Add(entry);
|
||||
}
|
||||
|
||||
public static void AddBoxData(SaveFile sav, ICollection<SlotCache> db)
|
||||
{
|
||||
var bd = sav.BoxData;
|
||||
var bc = sav.BoxCount;
|
||||
var sc = sav.BoxSlotCount;
|
||||
int ctr = 0;
|
||||
for (int box = 0; box < bc; box++)
|
||||
{
|
||||
for (int slot = 0; slot < sc; slot++, ctr++)
|
||||
{
|
||||
var ident = new SlotInfoBox(box, slot);
|
||||
var result = new SlotCache(ident, bd[ctr], sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddPartyData(SaveFile sav, ICollection<SlotCache> db)
|
||||
{
|
||||
var pd = sav.PartyData;
|
||||
for (var index = 0; index < pd.Count; index++)
|
||||
{
|
||||
var pk = pd[index];
|
||||
if (pk.Species == 0)
|
||||
continue;
|
||||
|
||||
var ident = new SlotInfoParty(index);
|
||||
var result = new SlotCache(ident, pk, sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddExtraData(SaveFile sav, ICollection<SlotCache> db)
|
||||
{
|
||||
var extra = sav.GetExtraSlots(true);
|
||||
foreach (var x in extra)
|
||||
{
|
||||
var pk = x.Read(sav);
|
||||
if (pk.Species == 0)
|
||||
continue;
|
||||
|
||||
var result = new SlotCache(x, pk, sav);
|
||||
db.Add(result);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -1,36 +1,35 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Miscellaneous origination <see cref="ISlotInfo"/>
|
||||
/// </summary>
|
||||
public sealed record SlotInfoMisc(byte[] Data, int Slot, int Offset, bool PartyFormat = false) : ISlotInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Miscellaneous origination <see cref="ISlotInfo"/>
|
||||
/// </summary>
|
||||
public sealed record SlotInfoMisc(byte[] Data, int Slot, int Offset, bool PartyFormat = false) : ISlotInfo
|
||||
public SlotOrigin Origin => PartyFormat ? SlotOrigin.Party : SlotOrigin.Box;
|
||||
public bool CanWriteTo(SaveFile sav) => false;
|
||||
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk) => WriteBlockedMessage.InvalidDestination;
|
||||
public StorageSlotType Type { get; init; }
|
||||
|
||||
public SlotInfoMisc(SaveFile sav, int slot, int offset, bool party = false) : this(GetBuffer(sav), slot, offset, party) { }
|
||||
|
||||
private static byte[] GetBuffer(SaveFile sav) => sav switch
|
||||
{
|
||||
public SlotOrigin Origin => PartyFormat ? SlotOrigin.Party : SlotOrigin.Box;
|
||||
public bool CanWriteTo(SaveFile sav) => false;
|
||||
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pkm) => WriteBlockedMessage.InvalidDestination;
|
||||
public StorageSlotType Type { get; init; }
|
||||
SAV4 s => s.General,
|
||||
SAV3 s3 => s3.Large,
|
||||
_ => sav.Data,
|
||||
};
|
||||
|
||||
public SlotInfoMisc(SaveFile sav, int slot, int offset, bool party = false) : this(GetBuffer(sav), slot, offset, party) { }
|
||||
|
||||
private static byte[] GetBuffer(SaveFile sav) => sav switch
|
||||
{
|
||||
SAV4 s => s.General,
|
||||
SAV3 s3 => s3.Large,
|
||||
_ => sav.Data,
|
||||
};
|
||||
|
||||
public bool WriteTo(SaveFile sav, PKM pkm, PKMImportSetting setting = PKMImportSetting.UseDefault)
|
||||
{
|
||||
if (PartyFormat)
|
||||
sav.SetSlotFormatParty(pkm, Data, Offset, setting, setting);
|
||||
else
|
||||
sav.SetSlotFormatStored(pkm, Data, Offset, setting, setting);
|
||||
return true;
|
||||
}
|
||||
|
||||
public PKM Read(SaveFile sav)
|
||||
{
|
||||
return PartyFormat ? sav.GetPartySlot(Data, Offset) : sav.GetStoredSlot(Data, Offset);
|
||||
}
|
||||
public bool WriteTo(SaveFile sav, PKM pk, PKMImportSetting setting = PKMImportSetting.UseDefault)
|
||||
{
|
||||
if (PartyFormat)
|
||||
sav.SetSlotFormatParty(pk, Data, Offset, setting, setting);
|
||||
else
|
||||
sav.SetSlotFormatStored(pk, Data, Offset, setting, setting);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public PKM Read(SaveFile sav)
|
||||
{
|
||||
return PartyFormat ? sav.GetPartySlot(Data, Offset) : sav.GetStoredSlot(Data, Offset);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,33 +1,32 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Party Data <see cref="ISlotInfo"/>
|
||||
/// </summary>
|
||||
public sealed record SlotInfoParty(int Slot) : ISlotInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Party Data <see cref="ISlotInfo"/>
|
||||
/// </summary>
|
||||
public sealed record SlotInfoParty(int Slot) : ISlotInfo
|
||||
public int Slot { get; private set; } = Slot;
|
||||
public SlotOrigin Origin => SlotOrigin.Party;
|
||||
public bool CanWriteTo(SaveFile sav) => sav.HasParty;
|
||||
|
||||
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pk) => pk.IsEgg && sav.IsPartyAllEggs(Slot)
|
||||
? WriteBlockedMessage.InvalidPartyConfiguration
|
||||
: WriteBlockedMessage.None;
|
||||
|
||||
public bool WriteTo(SaveFile sav, PKM pk, PKMImportSetting setting = PKMImportSetting.UseDefault)
|
||||
{
|
||||
public int Slot { get; private set; } = Slot;
|
||||
public SlotOrigin Origin => SlotOrigin.Party;
|
||||
public bool CanWriteTo(SaveFile sav) => sav.HasParty;
|
||||
|
||||
public WriteBlockedMessage CanWriteTo(SaveFile sav, PKM pkm) => pkm.IsEgg && sav.IsPartyAllEggs(Slot)
|
||||
? WriteBlockedMessage.InvalidPartyConfiguration
|
||||
: WriteBlockedMessage.None;
|
||||
|
||||
public bool WriteTo(SaveFile sav, PKM pkm, PKMImportSetting setting = PKMImportSetting.UseDefault)
|
||||
if (pk.Species == 0)
|
||||
{
|
||||
if (pkm.Species == 0)
|
||||
{
|
||||
sav.DeletePartySlot(Slot);
|
||||
Slot = Math.Max(0, sav.PartyCount - 1);
|
||||
return true;
|
||||
}
|
||||
Slot = Math.Min(Slot, sav.PartyCount); // realign if necessary
|
||||
sav.SetPartySlotAtIndex(pkm, Slot, setting, setting);
|
||||
sav.DeletePartySlot(Slot);
|
||||
Slot = Math.Max(0, sav.PartyCount - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
public PKM Read(SaveFile sav) => sav.GetPartySlotAtIndex(Slot);
|
||||
Slot = Math.Min(Slot, sav.PartyCount); // realign if necessary
|
||||
sav.SetPartySlotAtIndex(pk, Slot, setting, setting);
|
||||
return true;
|
||||
}
|
||||
|
||||
public PKM Read(SaveFile sav) => sav.GetPartySlotAtIndex(Slot);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public enum SlotOrigin : byte
|
||||
{
|
||||
public enum SlotOrigin : byte
|
||||
{
|
||||
Party = 0,
|
||||
Box = 1,
|
||||
}
|
||||
Party = 0,
|
||||
Box = 1,
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Message enumeration indicating why a Write Operation would be blocked for a given <see cref="ISlotInfo"/>
|
||||
/// </summary>
|
||||
public enum WriteBlockedMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Message enumeration indicating why a Write Operation would be blocked for a given <see cref="ISlotInfo"/>
|
||||
/// </summary>
|
||||
public enum WriteBlockedMessage
|
||||
{
|
||||
None,
|
||||
InvalidPartyConfiguration,
|
||||
IncompatibleFormat,
|
||||
InvalidDestination,
|
||||
}
|
||||
}
|
||||
None,
|
||||
InvalidPartyConfiguration,
|
||||
IncompatibleFormat,
|
||||
InvalidDestination,
|
||||
}
|
||||
|
|
|
@ -1,84 +1,83 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks <see cref="PKM"/> slot changes and provides the ability to revert a change.
|
||||
/// </summary>
|
||||
public sealed class SlotChangelog
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracks <see cref="PKM"/> slot changes and provides the ability to revert a change.
|
||||
/// </summary>
|
||||
public sealed class SlotChangelog
|
||||
private readonly SaveFile SAV;
|
||||
private readonly Stack<SlotReversion> UndoStack = new();
|
||||
private readonly Stack<SlotReversion> RedoStack = new();
|
||||
|
||||
public SlotChangelog(SaveFile sav) => SAV = sav;
|
||||
|
||||
public bool CanUndo => UndoStack.Count != 0;
|
||||
public bool CanRedo => RedoStack.Count != 0;
|
||||
|
||||
public void AddNewChange(ISlotInfo info)
|
||||
{
|
||||
private readonly SaveFile SAV;
|
||||
private readonly Stack<SlotReversion> UndoStack = new();
|
||||
private readonly Stack<SlotReversion> RedoStack = new();
|
||||
|
||||
public SlotChangelog(SaveFile sav) => SAV = sav;
|
||||
|
||||
public bool CanUndo => UndoStack.Count != 0;
|
||||
public bool CanRedo => RedoStack.Count != 0;
|
||||
|
||||
public void AddNewChange(ISlotInfo info)
|
||||
{
|
||||
var revert = GetReversion(info, SAV);
|
||||
AddUndo(revert);
|
||||
}
|
||||
|
||||
public ISlotInfo Undo()
|
||||
{
|
||||
var change = UndoStack.Pop();
|
||||
var revert = GetReversion(change.Info, SAV);
|
||||
AddRedo(revert);
|
||||
change.Revert(SAV);
|
||||
return change.Info;
|
||||
}
|
||||
|
||||
public ISlotInfo Redo()
|
||||
{
|
||||
var change = RedoStack.Pop();
|
||||
var revert = GetReversion(change.Info, SAV);
|
||||
AddUndo(revert);
|
||||
change.Revert(SAV);
|
||||
return change.Info;
|
||||
}
|
||||
|
||||
private void AddRedo(SlotReversion change)
|
||||
{
|
||||
RedoStack.Push(change);
|
||||
}
|
||||
|
||||
private void AddUndo(SlotReversion change)
|
||||
{
|
||||
UndoStack.Push(change);
|
||||
RedoStack.Clear();
|
||||
}
|
||||
|
||||
private static SlotReversion GetReversion(ISlotInfo info, SaveFile sav) => info switch
|
||||
{
|
||||
SlotInfoParty p => new PartyReversion(p, sav),
|
||||
_ => new SingleSlotReversion(info, sav),
|
||||
};
|
||||
|
||||
private abstract class SlotReversion
|
||||
{
|
||||
internal readonly ISlotInfo Info;
|
||||
protected SlotReversion(ISlotInfo info) => Info = info;
|
||||
|
||||
public abstract void Revert(SaveFile sav);
|
||||
}
|
||||
|
||||
private sealed class PartyReversion : SlotReversion
|
||||
{
|
||||
private readonly IList<PKM> Party;
|
||||
public PartyReversion(ISlotInfo info, SaveFile s) : base(info) => Party = s.PartyData;
|
||||
|
||||
public override void Revert(SaveFile sav) => sav.PartyData = Party;
|
||||
}
|
||||
|
||||
private sealed class SingleSlotReversion : SlotReversion
|
||||
{
|
||||
private readonly PKM Entity;
|
||||
public SingleSlotReversion(ISlotInfo info, SaveFile sav) : base(info) => Entity = info.Read(sav);
|
||||
|
||||
public override void Revert(SaveFile sav) => Info.WriteTo(sav, Entity, PKMImportSetting.Skip);
|
||||
}
|
||||
var revert = GetReversion(info, SAV);
|
||||
AddUndo(revert);
|
||||
}
|
||||
}
|
||||
|
||||
public ISlotInfo Undo()
|
||||
{
|
||||
var change = UndoStack.Pop();
|
||||
var revert = GetReversion(change.Info, SAV);
|
||||
AddRedo(revert);
|
||||
change.Revert(SAV);
|
||||
return change.Info;
|
||||
}
|
||||
|
||||
public ISlotInfo Redo()
|
||||
{
|
||||
var change = RedoStack.Pop();
|
||||
var revert = GetReversion(change.Info, SAV);
|
||||
AddUndo(revert);
|
||||
change.Revert(SAV);
|
||||
return change.Info;
|
||||
}
|
||||
|
||||
private void AddRedo(SlotReversion change)
|
||||
{
|
||||
RedoStack.Push(change);
|
||||
}
|
||||
|
||||
private void AddUndo(SlotReversion change)
|
||||
{
|
||||
UndoStack.Push(change);
|
||||
RedoStack.Clear();
|
||||
}
|
||||
|
||||
private static SlotReversion GetReversion(ISlotInfo info, SaveFile sav) => info switch
|
||||
{
|
||||
SlotInfoParty p => new PartyReversion(p, sav),
|
||||
_ => new SingleSlotReversion(info, sav),
|
||||
};
|
||||
|
||||
private abstract class SlotReversion
|
||||
{
|
||||
internal readonly ISlotInfo Info;
|
||||
protected SlotReversion(ISlotInfo info) => Info = info;
|
||||
|
||||
public abstract void Revert(SaveFile sav);
|
||||
}
|
||||
|
||||
private sealed class PartyReversion : SlotReversion
|
||||
{
|
||||
private readonly IList<PKM> Party;
|
||||
public PartyReversion(ISlotInfo info, SaveFile s) : base(info) => Party = s.PartyData;
|
||||
|
||||
public override void Revert(SaveFile sav) => sav.PartyData = Party;
|
||||
}
|
||||
|
||||
private sealed class SingleSlotReversion : SlotReversion
|
||||
{
|
||||
private readonly PKM Entity;
|
||||
public SingleSlotReversion(ISlotInfo info, SaveFile sav) : base(info) => Entity = info.Read(sav);
|
||||
|
||||
public override void Revert(SaveFile sav) => Info.WriteTo(sav, Entity, PKMImportSetting.Skip);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,119 +1,118 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Facilitates interaction with a <see cref="SaveFile"/> or other data location's slot data.
|
||||
/// </summary>
|
||||
public sealed class SlotEditor<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Facilitates interaction with a <see cref="SaveFile"/> or other data location's slot data.
|
||||
/// </summary>
|
||||
public sealed class SlotEditor<T>
|
||||
private readonly SaveFile SAV;
|
||||
public readonly SlotChangelog Changelog;
|
||||
public readonly SlotPublisher<T> Publisher;
|
||||
|
||||
public SlotEditor(SaveFile sav)
|
||||
{
|
||||
private readonly SaveFile SAV;
|
||||
public readonly SlotChangelog Changelog;
|
||||
public readonly SlotPublisher<T> Publisher;
|
||||
SAV = sav;
|
||||
Changelog = new SlotChangelog(sav);
|
||||
Publisher = new SlotPublisher<T>();
|
||||
}
|
||||
|
||||
public SlotEditor(SaveFile sav)
|
||||
{
|
||||
SAV = sav;
|
||||
Changelog = new SlotChangelog(sav);
|
||||
Publisher = new SlotPublisher<T>();
|
||||
}
|
||||
private void NotifySlotChanged(ISlotInfo slot, SlotTouchType type, PKM pk) => Publisher.NotifySlotChanged(slot, type, pk);
|
||||
|
||||
private void NotifySlotChanged(ISlotInfo slot, SlotTouchType type, PKM pkm) => Publisher.NotifySlotChanged(slot, type, pkm);
|
||||
/// <summary>
|
||||
/// Gets data from a slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot to retrieve from.</param>
|
||||
/// <returns>Operation succeeded or not via enum value.</returns>
|
||||
public PKM Get(ISlotInfo slot)
|
||||
{
|
||||
// Reading from a slot is always allowed.
|
||||
var pk = slot.Read(SAV);
|
||||
NotifySlotChanged(slot, SlotTouchType.Get, pk);
|
||||
return pk;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets data from a slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot to retrieve from.</param>
|
||||
/// <returns>Operation succeeded or not via enum value.</returns>
|
||||
public PKM Get(ISlotInfo slot)
|
||||
{
|
||||
// Reading from a slot is always allowed.
|
||||
var pk = slot.Read(SAV);
|
||||
NotifySlotChanged(slot, SlotTouchType.Get, pk);
|
||||
return pk;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets data to a slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot to be set to.</param>
|
||||
/// <param name="pk">Data to set.</param>
|
||||
/// <param name="type">Type of slot action</param>
|
||||
/// <returns>Operation succeeded or not via enum value.</returns>
|
||||
public SlotTouchResult Set(ISlotInfo slot, PKM pk, SlotTouchType type = SlotTouchType.Set)
|
||||
{
|
||||
if (!slot.CanWriteTo(SAV))
|
||||
return SlotTouchResult.FailWrite;
|
||||
|
||||
/// <summary>
|
||||
/// Sets data to a slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot to be set to.</param>
|
||||
/// <param name="pkm">Data to set.</param>
|
||||
/// <param name="type">Type of slot action</param>
|
||||
/// <returns>Operation succeeded or not via enum value.</returns>
|
||||
public SlotTouchResult Set(ISlotInfo slot, PKM pkm, SlotTouchType type = SlotTouchType.Set)
|
||||
{
|
||||
if (!slot.CanWriteTo(SAV))
|
||||
return SlotTouchResult.FailWrite;
|
||||
WriteSlot(slot, pk, type);
|
||||
NotifySlotChanged(slot, type, pk);
|
||||
|
||||
WriteSlot(slot, pkm, type);
|
||||
NotifySlotChanged(slot, type, pkm);
|
||||
return SlotTouchResult.Success;
|
||||
}
|
||||
|
||||
return SlotTouchResult.Success;
|
||||
}
|
||||
/// <summary>
|
||||
/// Deletes a slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot to be deleted.</param>
|
||||
/// <returns>Operation succeeded or not via enum value.</returns>
|
||||
public SlotTouchResult Delete(ISlotInfo slot)
|
||||
{
|
||||
if (!slot.CanWriteTo(SAV))
|
||||
return SlotTouchResult.FailDelete;
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot to be deleted.</param>
|
||||
/// <returns>Operation succeeded or not via enum value.</returns>
|
||||
public SlotTouchResult Delete(ISlotInfo slot)
|
||||
{
|
||||
if (!slot.CanWriteTo(SAV))
|
||||
return SlotTouchResult.FailDelete;
|
||||
var pk = DeleteSlot(slot);
|
||||
NotifySlotChanged(slot, SlotTouchType.Delete, pk);
|
||||
|
||||
var pk = DeleteSlot(slot);
|
||||
NotifySlotChanged(slot, SlotTouchType.Delete, pk);
|
||||
return SlotTouchResult.Success;
|
||||
}
|
||||
|
||||
return SlotTouchResult.Success;
|
||||
}
|
||||
/// <summary>
|
||||
/// Swaps two slots.
|
||||
/// </summary>
|
||||
/// <param name="source">Source slot to be switched with <see cref="dest"/>.</param>
|
||||
/// <param name="dest">Destination slot to be switched with <see cref="source"/>.</param>
|
||||
/// <returns>Operation succeeded or not via enum value.</returns>
|
||||
public SlotTouchResult Swap(ISlotInfo source, ISlotInfo dest)
|
||||
{
|
||||
if (!source.CanWriteTo(SAV))
|
||||
return SlotTouchResult.FailSource;
|
||||
if (!dest.CanWriteTo(SAV))
|
||||
return SlotTouchResult.FailDestination;
|
||||
|
||||
/// <summary>
|
||||
/// Swaps two slots.
|
||||
/// </summary>
|
||||
/// <param name="source">Source slot to be switched with <see cref="dest"/>.</param>
|
||||
/// <param name="dest">Destination slot to be switched with <see cref="source"/>.</param>
|
||||
/// <returns>Operation succeeded or not via enum value.</returns>
|
||||
public SlotTouchResult Swap(ISlotInfo source, ISlotInfo dest)
|
||||
{
|
||||
if (!source.CanWriteTo(SAV))
|
||||
return SlotTouchResult.FailSource;
|
||||
if (!dest.CanWriteTo(SAV))
|
||||
return SlotTouchResult.FailDestination;
|
||||
NotifySlotChanged(source, SlotTouchType.None, source.Read(SAV));
|
||||
NotifySlotChanged(dest, SlotTouchType.Swap, dest.Read(SAV));
|
||||
|
||||
NotifySlotChanged(source, SlotTouchType.None, source.Read(SAV));
|
||||
NotifySlotChanged(dest, SlotTouchType.Swap, dest.Read(SAV));
|
||||
return SlotTouchResult.Success;
|
||||
}
|
||||
|
||||
return SlotTouchResult.Success;
|
||||
}
|
||||
private void WriteSlot(ISlotInfo slot, PKM pk, SlotTouchType type = SlotTouchType.Set)
|
||||
{
|
||||
Changelog.AddNewChange(slot);
|
||||
var setDetail = type is SlotTouchType.Swap ? PKMImportSetting.Skip : PKMImportSetting.UseDefault;
|
||||
var result = slot.WriteTo(SAV, pk, setDetail);
|
||||
if (result)
|
||||
NotifySlotChanged(slot, type, pk);
|
||||
}
|
||||
|
||||
private void WriteSlot(ISlotInfo slot, PKM pkm, SlotTouchType type = SlotTouchType.Set)
|
||||
{
|
||||
Changelog.AddNewChange(slot);
|
||||
var setDetail = type is SlotTouchType.Swap ? PKMImportSetting.Skip : PKMImportSetting.UseDefault;
|
||||
var result = slot.WriteTo(SAV, pkm, setDetail);
|
||||
if (result)
|
||||
NotifySlotChanged(slot, type, pkm);
|
||||
}
|
||||
private PKM DeleteSlot(ISlotInfo slot)
|
||||
{
|
||||
var pk = SAV.BlankPKM;
|
||||
WriteSlot(slot, pk, SlotTouchType.Delete);
|
||||
return pk;
|
||||
}
|
||||
|
||||
private PKM DeleteSlot(ISlotInfo slot)
|
||||
{
|
||||
var pkm = SAV.BlankPKM;
|
||||
WriteSlot(slot, pkm, SlotTouchType.Delete);
|
||||
return pkm;
|
||||
}
|
||||
public void Undo()
|
||||
{
|
||||
if (!Changelog.CanUndo)
|
||||
return;
|
||||
var slot = Changelog.Undo();
|
||||
NotifySlotChanged(slot, SlotTouchType.Set, slot.Read(SAV));
|
||||
}
|
||||
|
||||
public void Undo()
|
||||
{
|
||||
if (!Changelog.CanUndo)
|
||||
return;
|
||||
var slot = Changelog.Undo();
|
||||
NotifySlotChanged(slot, SlotTouchType.Set, slot.Read(SAV));
|
||||
}
|
||||
|
||||
public void Redo()
|
||||
{
|
||||
if (!Changelog.CanRedo)
|
||||
return;
|
||||
var slot = Changelog.Redo();
|
||||
NotifySlotChanged(slot, SlotTouchType.Set, slot.Read(SAV));
|
||||
}
|
||||
public void Redo()
|
||||
{
|
||||
if (!Changelog.CanRedo)
|
||||
return;
|
||||
var slot = Changelog.Redo();
|
||||
NotifySlotChanged(slot, SlotTouchType.Set, slot.Read(SAV));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +1,49 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Pushes slot update notifications out to all subscribers.
|
||||
/// </summary>
|
||||
public sealed class SlotPublisher<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Pushes slot update notifications out to all subscribers.
|
||||
/// All <see cref="ISlotViewer{T}"/> instances that provide a view on individual <see cref="ISlotInfo"/> content.
|
||||
/// </summary>
|
||||
public sealed class SlotPublisher<T>
|
||||
public List<ISlotViewer<T>> Subscribers { get; } = new();
|
||||
|
||||
public ISlotInfo? Previous { get; private set; }
|
||||
public SlotTouchType PreviousType { get; private set; } = SlotTouchType.None;
|
||||
public PKM? PreviousEntity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Notifies all <see cref="Subscribers"/> with the latest slot change details.
|
||||
/// </summary>
|
||||
/// <param name="slot">Last interacted slot</param>
|
||||
/// <param name="type">Last interacted slot interaction type</param>
|
||||
/// <param name="pk">Last interacted slot interaction data</param>
|
||||
public void NotifySlotChanged(ISlotInfo slot, SlotTouchType type, PKM pk)
|
||||
{
|
||||
/// <summary>
|
||||
/// All <see cref="ISlotViewer{T}"/> instances that provide a view on individual <see cref="ISlotInfo"/> content.
|
||||
/// </summary>
|
||||
public List<ISlotViewer<T>> Subscribers { get; } = new();
|
||||
foreach (var sub in Subscribers)
|
||||
ResetView(sub, slot, type, pk);
|
||||
Previous = slot;
|
||||
PreviousType = type;
|
||||
PreviousEntity = pk;
|
||||
}
|
||||
|
||||
public ISlotInfo? Previous { get; private set; }
|
||||
public SlotTouchType PreviousType { get; private set; } = SlotTouchType.None;
|
||||
public PKM? PreviousEntity { get; private set; }
|
||||
private void ResetView(ISlotViewer<T> sub, ISlotInfo slot, SlotTouchType type, PKM pk)
|
||||
{
|
||||
if (Previous != null)
|
||||
sub.NotifySlotOld(Previous);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies all <see cref="Subscribers"/> with the latest slot change details.
|
||||
/// </summary>
|
||||
/// <param name="slot">Last interacted slot</param>
|
||||
/// <param name="type">Last interacted slot interaction type</param>
|
||||
/// <param name="pkm">Last interacted slot interaction data</param>
|
||||
public void NotifySlotChanged(ISlotInfo slot, SlotTouchType type, PKM pkm)
|
||||
{
|
||||
foreach (var sub in Subscribers)
|
||||
ResetView(sub, slot, type, pkm);
|
||||
Previous = slot;
|
||||
PreviousType = type;
|
||||
PreviousEntity = pkm;
|
||||
}
|
||||
if (slot is not SlotInfoBox b || sub.ViewIndex == b.Box)
|
||||
sub.NotifySlotChanged(slot, type, pk);
|
||||
}
|
||||
|
||||
private void ResetView(ISlotViewer<T> sub, ISlotInfo slot, SlotTouchType type, PKM pkm)
|
||||
{
|
||||
if (Previous != null)
|
||||
sub.NotifySlotOld(Previous);
|
||||
|
||||
if (slot is not SlotInfoBox b || sub.ViewIndex == b.Box)
|
||||
sub.NotifySlotChanged(slot, type, pkm);
|
||||
}
|
||||
|
||||
public void ResetView(ISlotViewer<T> sub)
|
||||
{
|
||||
if (Previous == null || PreviousEntity == null)
|
||||
return;
|
||||
ResetView(sub, Previous, PreviousType, PreviousEntity);
|
||||
}
|
||||
public void ResetView(ISlotViewer<T> sub)
|
||||
{
|
||||
if (Previous == null || PreviousEntity == null)
|
||||
return;
|
||||
ResetView(sub, Previous, PreviousType, PreviousEntity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,37 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Result indicators for modifying a Slot within a <see cref="SaveFile"/> or other data location.
|
||||
/// </summary>
|
||||
public enum SlotTouchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Result indicators for modifying a Slot within a <see cref="SaveFile"/> or other data location.
|
||||
/// Slot interaction was successful.
|
||||
/// </summary>
|
||||
public enum SlotTouchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Slot interaction was successful.
|
||||
/// </summary>
|
||||
Success,
|
||||
Success,
|
||||
|
||||
/// <summary>
|
||||
/// Slot interaction failed to do anything.
|
||||
/// </summary>
|
||||
FailNone,
|
||||
/// <summary>
|
||||
/// Slot interaction failed to do anything.
|
||||
/// </summary>
|
||||
FailNone,
|
||||
|
||||
/// <summary>
|
||||
/// Slot interaction failed to apply the data.
|
||||
/// </summary>
|
||||
FailWrite,
|
||||
/// <summary>
|
||||
/// Slot interaction failed to apply the data.
|
||||
/// </summary>
|
||||
FailWrite,
|
||||
|
||||
/// <summary>
|
||||
/// Slot interaction failed to delete the data.
|
||||
/// </summary>
|
||||
FailDelete,
|
||||
/// <summary>
|
||||
/// Slot interaction failed to delete the data.
|
||||
/// </summary>
|
||||
FailDelete,
|
||||
|
||||
/// <summary>
|
||||
/// Slot interaction failed due to a bad/unmodifiable source.
|
||||
/// </summary>
|
||||
FailSource,
|
||||
/// <summary>
|
||||
/// Slot interaction failed due to a bad/unmodifiable source.
|
||||
/// </summary>
|
||||
FailSource,
|
||||
|
||||
/// <summary>
|
||||
/// Slot interaction failed due to a bad/unmodifiable destination.
|
||||
/// </summary>
|
||||
FailDestination,
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Slot interaction failed due to a bad/unmodifiable destination.
|
||||
/// </summary>
|
||||
FailDestination,
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public enum SlotTouchType
|
||||
{
|
||||
None,
|
||||
Get,
|
||||
Set,
|
||||
Delete,
|
||||
Swap,
|
||||
}
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public static class SlotTouchTypeUtil
|
||||
{
|
||||
public static bool IsContentChange(this SlotTouchType t) => t > SlotTouchType.Get;
|
||||
}
|
||||
}
|
||||
public enum SlotTouchType
|
||||
{
|
||||
None,
|
||||
Get,
|
||||
Set,
|
||||
Delete,
|
||||
Swap,
|
||||
}
|
||||
|
||||
public static class SlotTouchTypeUtil
|
||||
{
|
||||
public static bool IsContentChange(this SlotTouchType t) => t > SlotTouchType.Get;
|
||||
}
|
||||
|
|
|
@ -1,39 +1,38 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Tuple containing data for a <see cref="Slot"/> and the originating <see cref="View"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public sealed class SlotViewInfo<T> : IEquatable<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Tuple containing data for a <see cref="Slot"/> and the originating <see cref="View"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public sealed class SlotViewInfo<T> : IEquatable<T>
|
||||
public readonly ISlotInfo Slot;
|
||||
public readonly ISlotViewer<T> View;
|
||||
|
||||
public PKM ReadCurrent() => Slot.Read(View.SAV);
|
||||
public bool CanWriteTo() => Slot.CanWriteTo(View.SAV);
|
||||
public WriteBlockedMessage CanWriteTo(PKM pk) => Slot.CanWriteTo(View.SAV, pk);
|
||||
|
||||
public SlotViewInfo(ISlotInfo slot, ISlotViewer<T> view)
|
||||
{
|
||||
public readonly ISlotInfo Slot;
|
||||
public readonly ISlotViewer<T> View;
|
||||
|
||||
public PKM ReadCurrent() => Slot.Read(View.SAV);
|
||||
public bool CanWriteTo() => Slot.CanWriteTo(View.SAV);
|
||||
public WriteBlockedMessage CanWriteTo(PKM pkm) => Slot.CanWriteTo(View.SAV, pkm);
|
||||
|
||||
public SlotViewInfo(ISlotInfo slot, ISlotViewer<T> view)
|
||||
{
|
||||
Slot = slot;
|
||||
View = view;
|
||||
}
|
||||
|
||||
private bool Equals(SlotViewInfo<T> other)
|
||||
{
|
||||
if (other.View.SAV != View.SAV)
|
||||
return false;
|
||||
if (other.View.ViewIndex != View.ViewIndex)
|
||||
return false;
|
||||
if (other.Slot.Slot != Slot.Slot)
|
||||
return false;
|
||||
return other.Slot.GetType() == Slot.GetType();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) => ReferenceEquals(this, obj) || (obj is SlotViewInfo<T> other && Equals(other));
|
||||
public override int GetHashCode() => (Slot.GetHashCode() * 397) ^ View.GetHashCode();
|
||||
bool IEquatable<T>.Equals(T other) => other != null && Equals(other);
|
||||
Slot = slot;
|
||||
View = view;
|
||||
}
|
||||
}
|
||||
|
||||
private bool Equals(SlotViewInfo<T> other)
|
||||
{
|
||||
if (other.View.SAV != View.SAV)
|
||||
return false;
|
||||
if (other.View.ViewIndex != View.ViewIndex)
|
||||
return false;
|
||||
if (other.Slot.Slot != Slot.Slot)
|
||||
return false;
|
||||
return other.Slot.GetType() == Slot.GetType();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) => ReferenceEquals(this, obj) || (obj is SlotViewInfo<T> other && Equals(other));
|
||||
public override int GetHashCode() => (Slot.GetHashCode() * 397) ^ View.GetHashCode();
|
||||
bool IEquatable<T>.Equals(T other) => other != null && Equals(other);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public enum StorageSlotType
|
||||
{
|
||||
public enum StorageSlotType
|
||||
{
|
||||
Box,
|
||||
Party,
|
||||
BattleBox,
|
||||
Daycare,
|
||||
GTS,
|
||||
Fused,
|
||||
Misc,
|
||||
Resort,
|
||||
}
|
||||
Box,
|
||||
Party,
|
||||
BattleBox,
|
||||
Daycare,
|
||||
GTS,
|
||||
Fused,
|
||||
Misc,
|
||||
Resort,
|
||||
}
|
||||
|
|
|
@ -1,183 +1,181 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static PKHeX.Core.Species;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for parsing details for <see cref="ShowdownSet"/> objects.
|
||||
/// </summary>
|
||||
public static class ShowdownParsing
|
||||
{
|
||||
private static readonly string[] genderForms = { "", "F", "" };
|
||||
|
||||
/// <summary>
|
||||
/// Logic for parsing details for <see cref="ShowdownSet"/> objects.
|
||||
/// Gets the Form ID from the input <see cref="name"/>.
|
||||
/// </summary>
|
||||
public static class ShowdownParsing
|
||||
/// <param name="name"></param>
|
||||
/// <param name="strings"></param>
|
||||
/// <param name="species">Species ID the form belongs to</param>
|
||||
/// <param name="format">Format the form name should appear in</param>
|
||||
/// <returns>Zero (base form) if no form matches the input string.</returns>
|
||||
public static int GetFormFromString(string name, GameStrings strings, int species, int format)
|
||||
{
|
||||
private static readonly string[] genderForms = { "", "F", "" };
|
||||
if (name.Length == 0)
|
||||
return 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Form ID from the input <see cref="name"/>.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="strings"></param>
|
||||
/// <param name="species">Species ID the form belongs to</param>
|
||||
/// <param name="format">Format the form name should appear in</param>
|
||||
/// <returns>Zero (base form) if no form matches the input string.</returns>
|
||||
public static int GetFormFromString(string name, GameStrings strings, int species, int format)
|
||||
string[] formStrings = FormConverter.GetFormList(species, strings.Types, strings.forms, genderForms, format);
|
||||
return Math.Max(0, Array.FindIndex(formStrings, z => z.Contains(name)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Form ID to string.
|
||||
/// </summary>
|
||||
/// <param name="form"></param>
|
||||
/// <param name="strings"></param>
|
||||
/// <param name="species"></param>
|
||||
/// <param name="format"></param>
|
||||
public static string GetStringFromForm(int form, GameStrings strings, int species, int format)
|
||||
{
|
||||
if (form <= 0)
|
||||
return string.Empty;
|
||||
|
||||
var forms = FormConverter.GetFormList(species, strings.Types, strings.forms, genderForms, format);
|
||||
return form >= forms.Length ? string.Empty : forms[form];
|
||||
}
|
||||
|
||||
private const string MiniorFormName = "Meteor";
|
||||
|
||||
/// <summary>
|
||||
/// Converts the PKHeX standard form name to Showdown's form name.
|
||||
/// </summary>
|
||||
/// <param name="species">Species ID</param>
|
||||
/// <param name="form">PKHeX form name</param>
|
||||
public static string GetShowdownFormName(int species, string form)
|
||||
{
|
||||
if (form.Length == 0)
|
||||
{
|
||||
if (name.Length == 0)
|
||||
return 0;
|
||||
|
||||
string[] formStrings = FormConverter.GetFormList(species, strings.Types, strings.forms, genderForms, format);
|
||||
return Math.Max(0, Array.FindIndex(formStrings, z => z.Contains(name)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Form ID to string.
|
||||
/// </summary>
|
||||
/// <param name="form"></param>
|
||||
/// <param name="strings"></param>
|
||||
/// <param name="species"></param>
|
||||
/// <param name="format"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetStringFromForm(int form, GameStrings strings, int species, int format)
|
||||
{
|
||||
if (form <= 0)
|
||||
return string.Empty;
|
||||
|
||||
var forms = FormConverter.GetFormList(species, strings.Types, strings.forms, genderForms, format);
|
||||
return form >= forms.Length ? string.Empty : forms[form];
|
||||
}
|
||||
|
||||
private const string MiniorFormName = "Meteor";
|
||||
|
||||
/// <summary>
|
||||
/// Converts the PKHeX standard form name to Showdown's form name.
|
||||
/// </summary>
|
||||
/// <param name="species">Species ID</param>
|
||||
/// <param name="form">PKHeX form name</param>
|
||||
public static string GetShowdownFormName(int species, string form)
|
||||
{
|
||||
if (form.Length == 0)
|
||||
{
|
||||
return species switch
|
||||
{
|
||||
(int)Minior => MiniorFormName,
|
||||
_ => form,
|
||||
};
|
||||
}
|
||||
|
||||
return species switch
|
||||
{
|
||||
(int)Basculin when form is "Blue" => "Blue-Striped",
|
||||
(int)Vivillon when form is "Poké Ball" => "Pokeball",
|
||||
(int)Zygarde => form.Replace("-C", string.Empty).Replace("50%", string.Empty),
|
||||
(int)Minior when form.StartsWith("M-") => MiniorFormName,
|
||||
(int)Minior => form.Replace("C-", string.Empty),
|
||||
(int)Necrozma when form is "Dusk" => $"{form}-Mane",
|
||||
(int)Necrozma when form is "Dawn" => $"{form}-Wings",
|
||||
(int)Polteageist or (int)Sinistea => form == "Antique" ? form : string.Empty,
|
||||
|
||||
(int)Furfrou or (int)Greninja or (int)Rockruff => string.Empty,
|
||||
|
||||
_ => Legal.Totem_USUM.Contains(species) && form == "Large"
|
||||
? Legal.Totem_Alolan.Contains(species) && species != (int)Mimikyu ? "Alola-Totem" : "Totem"
|
||||
: form.Replace(' ', '-'),
|
||||
(int)Minior => MiniorFormName,
|
||||
_ => form,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the Showdown form name to PKHeX's form name.
|
||||
/// </summary>
|
||||
/// <param name="species">Species ID</param>
|
||||
/// <param name="form">Showdown form name</param>
|
||||
/// <param name="ability">Showdown ability ID</param>
|
||||
public static string SetShowdownFormName(int species, string form, int ability)
|
||||
return species switch
|
||||
{
|
||||
if (form.Length != 0)
|
||||
form = form.Replace(' ', '-'); // inconsistencies are great
|
||||
(int)Basculin when form is "Blue" => "Blue-Striped",
|
||||
(int)Vivillon when form is "Poké Ball" => "Pokeball",
|
||||
(int)Zygarde => form.Replace("-C", string.Empty).Replace("50%", string.Empty),
|
||||
(int)Minior when form.StartsWith("M-", StringComparison.Ordinal) => MiniorFormName,
|
||||
(int)Minior => form.Replace("C-", string.Empty),
|
||||
(int)Necrozma when form is "Dusk" => $"{form}-Mane",
|
||||
(int)Necrozma when form is "Dawn" => $"{form}-Wings",
|
||||
(int)Polteageist or (int)Sinistea => form == "Antique" ? form : string.Empty,
|
||||
|
||||
return species switch
|
||||
{
|
||||
(int)Basculin when form == "Blue-Striped" => "Blue",
|
||||
(int)Vivillon when form == "Pokeball" => "Poké Ball",
|
||||
(int)Necrozma when form == "Dusk-Mane" => "Dusk",
|
||||
(int)Necrozma when form == "Dawn-Wings" => "Dawn",
|
||||
(int)Toxtricity when form == "Low-Key" => "Low Key",
|
||||
(int)Darmanitan when form == "Galar-Zen" => "Galar Zen",
|
||||
(int)Minior when form != MiniorFormName => $"C-{form}",
|
||||
(int)Zygarde when form == "Complete" => form,
|
||||
(int)Zygarde when ability == 211 => $"{(string.IsNullOrWhiteSpace(form) ? "50%" : "10%")}-C",
|
||||
(int)Greninja when ability == 210 => "Ash", // Battle Bond
|
||||
(int)Rockruff when ability == 020 => "Dusk", // Rockruff-1
|
||||
(int)Urshifu or (int)Pikachu or (int)Alcremie => form.Replace('-', ' '), // Strike and Cosplay
|
||||
(int)Furfrou or (int)Greninja or (int)Rockruff => string.Empty,
|
||||
|
||||
_ => Legal.Totem_USUM.Contains(species) && form.EndsWith("Totem") ? "Large" : form,
|
||||
};
|
||||
}
|
||||
_ => Legal.Totem_USUM.Contains(species) && form == "Large"
|
||||
? Legal.Totem_Alolan.Contains(species) && species != (int)Mimikyu ? "Alola-Totem" : "Totem"
|
||||
: form.Replace(' ', '-'),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches <see cref="ShowdownSet"/> data from the input <see cref="lines"/>.
|
||||
/// </summary>
|
||||
/// <param name="lines">Raw lines containing numerous multi-line set data.</param>
|
||||
/// <returns><see cref="ShowdownSet"/> objects until <see cref="lines"/> is consumed.</returns>
|
||||
public static IEnumerable<ShowdownSet> GetShowdownSets(IEnumerable<string> lines)
|
||||
/// <summary>
|
||||
/// Converts the Showdown form name to PKHeX's form name.
|
||||
/// </summary>
|
||||
/// <param name="species">Species ID</param>
|
||||
/// <param name="form">Showdown form name</param>
|
||||
/// <param name="ability">Showdown ability ID</param>
|
||||
public static string SetShowdownFormName(int species, string form, int ability)
|
||||
{
|
||||
if (form.Length != 0)
|
||||
form = form.Replace(' ', '-'); // inconsistencies are great
|
||||
|
||||
return species switch
|
||||
{
|
||||
// exported sets always have >4 moves; new List will always require 1 resizing, allocate 2x to save 1 reallocation.
|
||||
// intro, nature, ability, (ivs, evs, shiny, level) 4*moves
|
||||
var setLines = new List<string>(8);
|
||||
foreach (var line in lines)
|
||||
(int)Basculin when form == "Blue-Striped" => "Blue",
|
||||
(int)Vivillon when form == "Pokeball" => "Poké Ball",
|
||||
(int)Necrozma when form == "Dusk-Mane" => "Dusk",
|
||||
(int)Necrozma when form == "Dawn-Wings" => "Dawn",
|
||||
(int)Toxtricity when form == "Low-Key" => "Low Key",
|
||||
(int)Darmanitan when form == "Galar-Zen" => "Galar Zen",
|
||||
(int)Minior when form != MiniorFormName => $"C-{form}",
|
||||
(int)Zygarde when form == "Complete" => form,
|
||||
(int)Zygarde when ability == 211 => $"{(string.IsNullOrWhiteSpace(form) ? "50%" : "10%")}-C",
|
||||
(int)Greninja when ability == 210 => "Ash", // Battle Bond
|
||||
(int)Rockruff when ability == 020 => "Dusk", // Rockruff-1
|
||||
(int)Urshifu or (int)Pikachu or (int)Alcremie => form.Replace('-', ' '), // Strike and Cosplay
|
||||
|
||||
_ => Legal.Totem_USUM.Contains(species) && form.EndsWith("Totem", StringComparison.Ordinal) ? "Large" : form,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches <see cref="ShowdownSet"/> data from the input <see cref="lines"/>.
|
||||
/// </summary>
|
||||
/// <param name="lines">Raw lines containing numerous multi-line set data.</param>
|
||||
/// <returns><see cref="ShowdownSet"/> objects until <see cref="lines"/> is consumed.</returns>
|
||||
public static IEnumerable<ShowdownSet> GetShowdownSets(IEnumerable<string> lines)
|
||||
{
|
||||
// exported sets always have >4 moves; new List will always require 1 resizing, allocate 2x to save 1 reallocation.
|
||||
// intro, nature, ability, (ivs, evs, shiny, level) 4*moves
|
||||
var setLines = new List<string>(8);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
setLines.Add(line);
|
||||
continue;
|
||||
}
|
||||
if (setLines.Count == 0)
|
||||
continue;
|
||||
yield return new ShowdownSet(setLines);
|
||||
setLines.Clear();
|
||||
setLines.Add(line);
|
||||
continue;
|
||||
}
|
||||
if (setLines.Count != 0)
|
||||
yield return new ShowdownSet(setLines);
|
||||
if (setLines.Count == 0)
|
||||
continue;
|
||||
yield return new ShowdownSet(setLines);
|
||||
setLines.Clear();
|
||||
}
|
||||
if (setLines.Count != 0)
|
||||
yield return new ShowdownSet(setLines);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="PKM"/> data into an importable set format for Pokémon Showdown.
|
||||
/// </summary>
|
||||
/// <param name="pkm">PKM to convert to string</param>
|
||||
/// <returns>Multi line set data</returns>
|
||||
public static string GetShowdownText(PKM pkm)
|
||||
{
|
||||
if (pkm.Species == 0)
|
||||
return string.Empty;
|
||||
return new ShowdownSet(pkm).Text;
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts the <see cref="PKM"/> data into an importable set format for Pokémon Showdown.
|
||||
/// </summary>
|
||||
/// <param name="pk">PKM to convert to string</param>
|
||||
/// <returns>Multi line set data</returns>
|
||||
public static string GetShowdownText(PKM pk)
|
||||
{
|
||||
if (pk.Species == 0)
|
||||
return string.Empty;
|
||||
return new ShowdownSet(pk).Text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches ShowdownSet lines from the input <see cref="PKM"/> data.
|
||||
/// </summary>
|
||||
/// <param name="data">Pokémon data to summarize.</param>
|
||||
/// <returns>Consumable list of <see cref="ShowdownSet.Text"/> lines.</returns>
|
||||
public static IEnumerable<string> GetShowdownSets(IEnumerable<PKM> data) => data.Where(p => p.Species != 0).Select(GetShowdownText);
|
||||
/// <summary>
|
||||
/// Fetches ShowdownSet lines from the input <see cref="PKM"/> data.
|
||||
/// </summary>
|
||||
/// <param name="data">Pokémon data to summarize.</param>
|
||||
/// <returns>Consumable list of <see cref="ShowdownSet.Text"/> lines.</returns>
|
||||
public static IEnumerable<string> GetShowdownSets(IEnumerable<PKM> data) => data.Where(p => p.Species != 0).Select(GetShowdownText);
|
||||
|
||||
/// <summary>
|
||||
/// Fetches ShowdownSet lines from the input <see cref="PKM"/> data, and combines it into one string.
|
||||
/// </summary>
|
||||
/// <param name="data">Pokémon data to summarize.</param>
|
||||
/// <param name="separator">Splitter between each set.</param>
|
||||
/// <returns>Single string containing all <see cref="ShowdownSet.Text"/> lines.</returns>
|
||||
public static string GetShowdownSets(IEnumerable<PKM> data, string separator) => string.Join(separator, GetShowdownSets(data));
|
||||
/// <summary>
|
||||
/// Fetches ShowdownSet lines from the input <see cref="PKM"/> data, and combines it into one string.
|
||||
/// </summary>
|
||||
/// <param name="data">Pokémon data to summarize.</param>
|
||||
/// <param name="separator">Splitter between each set.</param>
|
||||
/// <returns>Single string containing all <see cref="ShowdownSet.Text"/> lines.</returns>
|
||||
public static string GetShowdownSets(IEnumerable<PKM> data, string separator) => string.Join(separator, GetShowdownSets(data));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a localized string preview of the provided <see cref="pk"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon data</param>
|
||||
/// <param name="language">Language code</param>
|
||||
/// <returns>Multi-line string</returns>
|
||||
public static string GetLocalizedPreviewText(PKM pk, string language)
|
||||
{
|
||||
var set = new ShowdownSet(pk);
|
||||
if (pk.Format <= 2) // Nature preview from IVs
|
||||
set.Nature = Experience.GetNatureVC(pk.EXP);
|
||||
return set.LocalizedText(language);
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a localized string preview of the provided <see cref="pk"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon data</param>
|
||||
/// <param name="language">Language code</param>
|
||||
/// <returns>Multi-line string</returns>
|
||||
public static string GetLocalizedPreviewText(PKM pk, string language)
|
||||
{
|
||||
var set = new ShowdownSet(pk);
|
||||
if (pk.Format <= 2) // Nature preview from IVs
|
||||
set.Nature = Experience.GetNatureVC(pk.EXP);
|
||||
return set.LocalizedText(language);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,59 +1,58 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates or coerces values pertaining to <see cref="Species.Wurmple"/> and its branched evolutions.
|
||||
/// </summary>
|
||||
public static class WurmpleUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates or coerces values pertaining to <see cref="Species.Wurmple"/> and its branched evolutions.
|
||||
/// Gets the Wurmple Evolution Value for a given <see cref="PKM.EncryptionConstant"/>
|
||||
/// </summary>
|
||||
public static class WurmpleUtil
|
||||
/// <param name="encryptionConstant">Encryption Constant</param>
|
||||
/// <returns>Wurmple Evolution Value</returns>
|
||||
public static uint GetWurmpleEvoVal(uint encryptionConstant)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Wurmple Evolution Value for a given <see cref="PKM.EncryptionConstant"/>
|
||||
/// </summary>
|
||||
/// <param name="encryptionConstant">Encryption Constant</param>
|
||||
/// <returns>Wurmple Evolution Value</returns>
|
||||
public static uint GetWurmpleEvoVal(uint encryptionConstant)
|
||||
{
|
||||
var evoVal = encryptionConstant >> 16;
|
||||
return evoVal % 10 / 5;
|
||||
}
|
||||
var evoVal = encryptionConstant >> 16;
|
||||
return evoVal % 10 / 5;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the evo chain of Wurmple
|
||||
/// </summary>
|
||||
/// <param name="species">Current species</param>
|
||||
/// <returns>-1 if not a Wurmple Evo, 0 if Silcoon chain, 1 if Cascoon chain</returns>
|
||||
public static int GetWurmpleEvoGroup(int species)
|
||||
{
|
||||
int wIndex = species - (int)Species.Silcoon;
|
||||
if ((wIndex & 3) != wIndex) // Wurmple evo, [0,3]
|
||||
return -1;
|
||||
return wIndex >> 1; // Silcoon, Cascoon
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the evo chain of Wurmple
|
||||
/// </summary>
|
||||
/// <param name="species">Current species</param>
|
||||
/// <returns>-1 if not a Wurmple Evo, 0 if Silcoon chain, 1 if Cascoon chain</returns>
|
||||
public static int GetWurmpleEvoGroup(int species)
|
||||
{
|
||||
int wIndex = species - (int)Species.Silcoon;
|
||||
if ((wIndex & 3) != wIndex) // Wurmple evo, [0,3]
|
||||
return -1;
|
||||
return wIndex >> 1; // Silcoon, Cascoon
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Wurmple <see cref="PKM.EncryptionConstant"/> for a given Evolution Value
|
||||
/// </summary>
|
||||
/// <param name="evoVal">Wurmple Evolution Value</param>
|
||||
/// <remarks>0 = Silcoon, 1 = Cascoon</remarks>
|
||||
/// <returns>Encryption Constant</returns>
|
||||
public static uint GetWurmpleEncryptionConstant(int evoVal)
|
||||
{
|
||||
uint result;
|
||||
var rnd = Util.Rand;
|
||||
do result = rnd.Rand32();
|
||||
while (evoVal != GetWurmpleEvoVal(result));
|
||||
return result;
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the Wurmple <see cref="PKM.EncryptionConstant"/> for a given Evolution Value
|
||||
/// </summary>
|
||||
/// <param name="evoVal">Wurmple Evolution Value</param>
|
||||
/// <remarks>0 = Silcoon, 1 = Cascoon</remarks>
|
||||
/// <returns>Encryption Constant</returns>
|
||||
public static uint GetWurmpleEncryptionConstant(int evoVal)
|
||||
{
|
||||
uint result;
|
||||
var rnd = Util.Rand;
|
||||
do result = rnd.Rand32();
|
||||
while (evoVal != GetWurmpleEvoVal(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if the input <see cref="pkm"/>, with species being that of Wurmple's evo chain, is valid.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Pokémon data</param>
|
||||
/// <returns>True if valid, false if invalid</returns>
|
||||
public static bool IsWurmpleEvoValid(PKM pkm)
|
||||
{
|
||||
uint evoVal = GetWurmpleEvoVal(pkm.EncryptionConstant);
|
||||
int wIndex = GetWurmpleEvoGroup(pkm.Species);
|
||||
return evoVal == wIndex;
|
||||
}
|
||||
/// <summary>
|
||||
/// Checks to see if the input <see cref="pk"/>, with species being that of Wurmple's evo chain, is valid.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon data</param>
|
||||
/// <returns>True if valid, false if invalid</returns>
|
||||
public static bool IsWurmpleEvoValid(PKM pk)
|
||||
{
|
||||
uint evoVal = GetWurmpleEvoVal(pk.EncryptionConstant);
|
||||
int wIndex = GetWurmpleEvoGroup(pk.Species);
|
||||
return evoVal == wIndex;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,278 +1,277 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Ability IDs for the corresponding English ability name.
|
||||
/// </summary>
|
||||
public enum Ability
|
||||
{
|
||||
/// <summary>
|
||||
/// Ability IDs for the corresponding English ability name.
|
||||
/// </summary>
|
||||
public enum Ability
|
||||
{
|
||||
None,
|
||||
Stench,
|
||||
Drizzle,
|
||||
SpeedBoost,
|
||||
BattleArmor,
|
||||
Sturdy,
|
||||
Damp,
|
||||
Limber,
|
||||
SandVeil,
|
||||
Static,
|
||||
VoltAbsorb,
|
||||
WaterAbsorb,
|
||||
Oblivious,
|
||||
CloudNine,
|
||||
CompoundEyes,
|
||||
Insomnia,
|
||||
ColorChange,
|
||||
Immunity,
|
||||
FlashFire,
|
||||
ShieldDust,
|
||||
OwnTempo,
|
||||
SuctionCups,
|
||||
Intimidate,
|
||||
ShadowTag,
|
||||
RoughSkin,
|
||||
WonderGuard,
|
||||
Levitate,
|
||||
EffectSpore,
|
||||
Synchronize,
|
||||
ClearBody,
|
||||
NaturalCure,
|
||||
LightningRod,
|
||||
SereneGrace,
|
||||
SwiftSwim,
|
||||
Chlorophyll,
|
||||
Illuminate,
|
||||
Trace,
|
||||
HugePower,
|
||||
PoisonPoint,
|
||||
InnerFocus,
|
||||
MagmaArmor,
|
||||
WaterVeil,
|
||||
MagnetPull,
|
||||
Soundproof,
|
||||
RainDish,
|
||||
SandStream,
|
||||
Pressure,
|
||||
ThickFat,
|
||||
EarlyBird,
|
||||
FlameBody,
|
||||
RunAway,
|
||||
KeenEye,
|
||||
HyperCutter,
|
||||
Pickup,
|
||||
Truant,
|
||||
Hustle,
|
||||
CuteCharm,
|
||||
Plus,
|
||||
Minus,
|
||||
Forecast,
|
||||
StickyHold,
|
||||
ShedSkin,
|
||||
Guts,
|
||||
MarvelScale,
|
||||
LiquidOoze,
|
||||
Overgrow,
|
||||
Blaze,
|
||||
Torrent,
|
||||
Swarm,
|
||||
RockHead,
|
||||
Drought,
|
||||
ArenaTrap,
|
||||
VitalSpirit,
|
||||
WhiteSmoke,
|
||||
PurePower,
|
||||
ShellArmor,
|
||||
AirLock,
|
||||
TangledFeet,
|
||||
MotorDrive,
|
||||
Rivalry,
|
||||
Steadfast,
|
||||
SnowCloak,
|
||||
Gluttony,
|
||||
AngerPoint,
|
||||
Unburden,
|
||||
Heatproof,
|
||||
Simple,
|
||||
DrySkin,
|
||||
Download,
|
||||
IronFist,
|
||||
PoisonHeal,
|
||||
Adaptability,
|
||||
SkillLink,
|
||||
Hydration,
|
||||
SolarPower,
|
||||
QuickFeet,
|
||||
Normalize,
|
||||
Sniper,
|
||||
MagicGuard,
|
||||
NoGuard,
|
||||
Stall,
|
||||
Technician,
|
||||
LeafGuard,
|
||||
Klutz,
|
||||
MoldBreaker,
|
||||
SuperLuck,
|
||||
Aftermath,
|
||||
Anticipation,
|
||||
Forewarn,
|
||||
Unaware,
|
||||
TintedLens,
|
||||
Filter,
|
||||
SlowStart,
|
||||
Scrappy,
|
||||
StormDrain,
|
||||
IceBody,
|
||||
SolidRock,
|
||||
SnowWarning,
|
||||
HoneyGather,
|
||||
Frisk,
|
||||
Reckless,
|
||||
Multitype,
|
||||
FlowerGift,
|
||||
BadDreams,
|
||||
Pickpocket,
|
||||
SheerForce,
|
||||
Contrary,
|
||||
Unnerve,
|
||||
Defiant,
|
||||
Defeatist,
|
||||
CursedBody,
|
||||
Healer,
|
||||
FriendGuard,
|
||||
WeakArmor,
|
||||
HeavyMetal,
|
||||
LightMetal,
|
||||
Multiscale,
|
||||
ToxicBoost,
|
||||
FlareBoost,
|
||||
Harvest,
|
||||
Telepathy,
|
||||
Moody,
|
||||
Overcoat,
|
||||
PoisonTouch,
|
||||
Regenerator,
|
||||
BigPecks,
|
||||
SandRush,
|
||||
WonderSkin,
|
||||
Analytic,
|
||||
Illusion,
|
||||
Imposter,
|
||||
Infiltrator,
|
||||
Mummy,
|
||||
Moxie,
|
||||
Justified,
|
||||
Rattled,
|
||||
MagicBounce,
|
||||
SapSipper,
|
||||
Prankster,
|
||||
SandForce,
|
||||
IronBarbs,
|
||||
ZenMode,
|
||||
VictoryStar,
|
||||
Turboblaze,
|
||||
Teravolt,
|
||||
AromaVeil,
|
||||
FlowerVeil,
|
||||
CheekPouch,
|
||||
Protean,
|
||||
FurCoat,
|
||||
Magician,
|
||||
Bulletproof,
|
||||
Competitive,
|
||||
StrongJaw,
|
||||
Refrigerate,
|
||||
SweetVeil,
|
||||
StanceChange,
|
||||
GaleWings,
|
||||
MegaLauncher,
|
||||
GrassPelt,
|
||||
Symbiosis,
|
||||
ToughClaws,
|
||||
Pixilate,
|
||||
Gooey,
|
||||
Aerilate,
|
||||
ParentalBond,
|
||||
DarkAura,
|
||||
FairyAura,
|
||||
AuraBreak,
|
||||
PrimordialSea,
|
||||
DesolateLand,
|
||||
DeltaStream,
|
||||
Stamina,
|
||||
WimpOut,
|
||||
EmergencyExit,
|
||||
WaterCompaction,
|
||||
Merciless,
|
||||
ShieldsDown,
|
||||
Stakeout,
|
||||
WaterBubble,
|
||||
Steelworker,
|
||||
Berserk,
|
||||
SlushRush,
|
||||
LongReach,
|
||||
LiquidVoice,
|
||||
Triage,
|
||||
Galvanize,
|
||||
SurgeSurfer,
|
||||
Schooling,
|
||||
Disguise,
|
||||
BattleBond,
|
||||
PowerConstruct,
|
||||
Corrosion,
|
||||
Comatose,
|
||||
QueenlyMajesty,
|
||||
InnardsOut,
|
||||
Dancer,
|
||||
Battery,
|
||||
Fluffy,
|
||||
Dazzling,
|
||||
SoulHeart,
|
||||
TanglingHair,
|
||||
Receiver,
|
||||
PowerofAlchemy,
|
||||
BeastBoost,
|
||||
RKSSystem,
|
||||
ElectricSurge,
|
||||
PsychicSurge,
|
||||
MistySurge,
|
||||
GrassySurge,
|
||||
FullMetalBody,
|
||||
ShadowShield,
|
||||
PrismArmor,
|
||||
Neuroforce,
|
||||
IntrepidSword,
|
||||
DauntlessShield,
|
||||
Libero,
|
||||
BallFetch,
|
||||
CottonDown,
|
||||
PropellerTail,
|
||||
MirrorArmor,
|
||||
GulpMissile,
|
||||
Stalwart,
|
||||
SteamEngine,
|
||||
PunkRock,
|
||||
SandSpit,
|
||||
IceScales,
|
||||
Ripen,
|
||||
IceFace,
|
||||
PowerSpot,
|
||||
Mimicry,
|
||||
ScreenCleaner,
|
||||
SteelySpirit,
|
||||
PerishBody,
|
||||
WanderingSpirit,
|
||||
GorillaTactics,
|
||||
NeutralizingGas,
|
||||
PastelVeil,
|
||||
HungerSwitch,
|
||||
QuickDraw,
|
||||
UnseenFist,
|
||||
CuriousMedicine,
|
||||
Transistor,
|
||||
DragonsMaw,
|
||||
ChillingNeigh,
|
||||
GrimNeigh,
|
||||
AsOneI,
|
||||
AsOneG,
|
||||
MAX_COUNT,
|
||||
}
|
||||
None,
|
||||
Stench,
|
||||
Drizzle,
|
||||
SpeedBoost,
|
||||
BattleArmor,
|
||||
Sturdy,
|
||||
Damp,
|
||||
Limber,
|
||||
SandVeil,
|
||||
Static,
|
||||
VoltAbsorb,
|
||||
WaterAbsorb,
|
||||
Oblivious,
|
||||
CloudNine,
|
||||
CompoundEyes,
|
||||
Insomnia,
|
||||
ColorChange,
|
||||
Immunity,
|
||||
FlashFire,
|
||||
ShieldDust,
|
||||
OwnTempo,
|
||||
SuctionCups,
|
||||
Intimidate,
|
||||
ShadowTag,
|
||||
RoughSkin,
|
||||
WonderGuard,
|
||||
Levitate,
|
||||
EffectSpore,
|
||||
Synchronize,
|
||||
ClearBody,
|
||||
NaturalCure,
|
||||
LightningRod,
|
||||
SereneGrace,
|
||||
SwiftSwim,
|
||||
Chlorophyll,
|
||||
Illuminate,
|
||||
Trace,
|
||||
HugePower,
|
||||
PoisonPoint,
|
||||
InnerFocus,
|
||||
MagmaArmor,
|
||||
WaterVeil,
|
||||
MagnetPull,
|
||||
Soundproof,
|
||||
RainDish,
|
||||
SandStream,
|
||||
Pressure,
|
||||
ThickFat,
|
||||
EarlyBird,
|
||||
FlameBody,
|
||||
RunAway,
|
||||
KeenEye,
|
||||
HyperCutter,
|
||||
Pickup,
|
||||
Truant,
|
||||
Hustle,
|
||||
CuteCharm,
|
||||
Plus,
|
||||
Minus,
|
||||
Forecast,
|
||||
StickyHold,
|
||||
ShedSkin,
|
||||
Guts,
|
||||
MarvelScale,
|
||||
LiquidOoze,
|
||||
Overgrow,
|
||||
Blaze,
|
||||
Torrent,
|
||||
Swarm,
|
||||
RockHead,
|
||||
Drought,
|
||||
ArenaTrap,
|
||||
VitalSpirit,
|
||||
WhiteSmoke,
|
||||
PurePower,
|
||||
ShellArmor,
|
||||
AirLock,
|
||||
TangledFeet,
|
||||
MotorDrive,
|
||||
Rivalry,
|
||||
Steadfast,
|
||||
SnowCloak,
|
||||
Gluttony,
|
||||
AngerPoint,
|
||||
Unburden,
|
||||
Heatproof,
|
||||
Simple,
|
||||
DrySkin,
|
||||
Download,
|
||||
IronFist,
|
||||
PoisonHeal,
|
||||
Adaptability,
|
||||
SkillLink,
|
||||
Hydration,
|
||||
SolarPower,
|
||||
QuickFeet,
|
||||
Normalize,
|
||||
Sniper,
|
||||
MagicGuard,
|
||||
NoGuard,
|
||||
Stall,
|
||||
Technician,
|
||||
LeafGuard,
|
||||
Klutz,
|
||||
MoldBreaker,
|
||||
SuperLuck,
|
||||
Aftermath,
|
||||
Anticipation,
|
||||
Forewarn,
|
||||
Unaware,
|
||||
TintedLens,
|
||||
Filter,
|
||||
SlowStart,
|
||||
Scrappy,
|
||||
StormDrain,
|
||||
IceBody,
|
||||
SolidRock,
|
||||
SnowWarning,
|
||||
HoneyGather,
|
||||
Frisk,
|
||||
Reckless,
|
||||
Multitype,
|
||||
FlowerGift,
|
||||
BadDreams,
|
||||
Pickpocket,
|
||||
SheerForce,
|
||||
Contrary,
|
||||
Unnerve,
|
||||
Defiant,
|
||||
Defeatist,
|
||||
CursedBody,
|
||||
Healer,
|
||||
FriendGuard,
|
||||
WeakArmor,
|
||||
HeavyMetal,
|
||||
LightMetal,
|
||||
Multiscale,
|
||||
ToxicBoost,
|
||||
FlareBoost,
|
||||
Harvest,
|
||||
Telepathy,
|
||||
Moody,
|
||||
Overcoat,
|
||||
PoisonTouch,
|
||||
Regenerator,
|
||||
BigPecks,
|
||||
SandRush,
|
||||
WonderSkin,
|
||||
Analytic,
|
||||
Illusion,
|
||||
Imposter,
|
||||
Infiltrator,
|
||||
Mummy,
|
||||
Moxie,
|
||||
Justified,
|
||||
Rattled,
|
||||
MagicBounce,
|
||||
SapSipper,
|
||||
Prankster,
|
||||
SandForce,
|
||||
IronBarbs,
|
||||
ZenMode,
|
||||
VictoryStar,
|
||||
Turboblaze,
|
||||
Teravolt,
|
||||
AromaVeil,
|
||||
FlowerVeil,
|
||||
CheekPouch,
|
||||
Protean,
|
||||
FurCoat,
|
||||
Magician,
|
||||
Bulletproof,
|
||||
Competitive,
|
||||
StrongJaw,
|
||||
Refrigerate,
|
||||
SweetVeil,
|
||||
StanceChange,
|
||||
GaleWings,
|
||||
MegaLauncher,
|
||||
GrassPelt,
|
||||
Symbiosis,
|
||||
ToughClaws,
|
||||
Pixilate,
|
||||
Gooey,
|
||||
Aerilate,
|
||||
ParentalBond,
|
||||
DarkAura,
|
||||
FairyAura,
|
||||
AuraBreak,
|
||||
PrimordialSea,
|
||||
DesolateLand,
|
||||
DeltaStream,
|
||||
Stamina,
|
||||
WimpOut,
|
||||
EmergencyExit,
|
||||
WaterCompaction,
|
||||
Merciless,
|
||||
ShieldsDown,
|
||||
Stakeout,
|
||||
WaterBubble,
|
||||
Steelworker,
|
||||
Berserk,
|
||||
SlushRush,
|
||||
LongReach,
|
||||
LiquidVoice,
|
||||
Triage,
|
||||
Galvanize,
|
||||
SurgeSurfer,
|
||||
Schooling,
|
||||
Disguise,
|
||||
BattleBond,
|
||||
PowerConstruct,
|
||||
Corrosion,
|
||||
Comatose,
|
||||
QueenlyMajesty,
|
||||
InnardsOut,
|
||||
Dancer,
|
||||
Battery,
|
||||
Fluffy,
|
||||
Dazzling,
|
||||
SoulHeart,
|
||||
TanglingHair,
|
||||
Receiver,
|
||||
PowerofAlchemy,
|
||||
BeastBoost,
|
||||
RKSSystem,
|
||||
ElectricSurge,
|
||||
PsychicSurge,
|
||||
MistySurge,
|
||||
GrassySurge,
|
||||
FullMetalBody,
|
||||
ShadowShield,
|
||||
PrismArmor,
|
||||
Neuroforce,
|
||||
IntrepidSword,
|
||||
DauntlessShield,
|
||||
Libero,
|
||||
BallFetch,
|
||||
CottonDown,
|
||||
PropellerTail,
|
||||
MirrorArmor,
|
||||
GulpMissile,
|
||||
Stalwart,
|
||||
SteamEngine,
|
||||
PunkRock,
|
||||
SandSpit,
|
||||
IceScales,
|
||||
Ripen,
|
||||
IceFace,
|
||||
PowerSpot,
|
||||
Mimicry,
|
||||
ScreenCleaner,
|
||||
SteelySpirit,
|
||||
PerishBody,
|
||||
WanderingSpirit,
|
||||
GorillaTactics,
|
||||
NeutralizingGas,
|
||||
PastelVeil,
|
||||
HungerSwitch,
|
||||
QuickDraw,
|
||||
UnseenFist,
|
||||
CuriousMedicine,
|
||||
Transistor,
|
||||
DragonsMaw,
|
||||
ChillingNeigh,
|
||||
GrimNeigh,
|
||||
AsOneI,
|
||||
AsOneG,
|
||||
MAX_COUNT,
|
||||
}
|
||||
|
|
|
@ -1,65 +1,64 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Ball IDs for the corresponding English ball name.
|
||||
/// </summary>
|
||||
public enum Ball : byte
|
||||
{
|
||||
None = 0,
|
||||
|
||||
Master = 1,
|
||||
Ultra = 2,
|
||||
Great = 3,
|
||||
Poke = 4,
|
||||
|
||||
Safari = 5,
|
||||
|
||||
Net = 6,
|
||||
Dive = 7,
|
||||
Nest = 8,
|
||||
Repeat = 9,
|
||||
Timer = 10,
|
||||
Luxury = 11,
|
||||
Premier = 12,
|
||||
Dusk = 13,
|
||||
Heal = 14,
|
||||
Quick = 15,
|
||||
|
||||
Cherish = 16,
|
||||
|
||||
Fast = 17,
|
||||
Level = 18,
|
||||
Lure = 19,
|
||||
Heavy = 20,
|
||||
Love = 21,
|
||||
Friend = 22,
|
||||
Moon = 23,
|
||||
|
||||
Sport = 24,
|
||||
Dream = 25,
|
||||
Beast = 26,
|
||||
|
||||
// Legends: Arceus
|
||||
Strange = 27,
|
||||
LAPoke = 28,
|
||||
LAGreat = 29,
|
||||
LAUltra = 30,
|
||||
LAFeather = 31,
|
||||
LAWing = 32,
|
||||
LAJet = 33,
|
||||
LAHeavy = 34,
|
||||
LALeaden = 35,
|
||||
LAGigaton = 36,
|
||||
LAOrigin = 37,
|
||||
}
|
||||
|
||||
public static class BallExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Ball IDs for the corresponding English ball name.
|
||||
/// Checks if the <see cref="ball"/> is an Apricorn Ball (HG/SS)
|
||||
/// </summary>
|
||||
public enum Ball : byte
|
||||
{
|
||||
None = 0,
|
||||
|
||||
Master = 1,
|
||||
Ultra = 2,
|
||||
Great = 3,
|
||||
Poke = 4,
|
||||
|
||||
Safari = 5,
|
||||
|
||||
Net = 6,
|
||||
Dive = 7,
|
||||
Nest = 8,
|
||||
Repeat = 9,
|
||||
Timer = 10,
|
||||
Luxury = 11,
|
||||
Premier = 12,
|
||||
Dusk = 13,
|
||||
Heal = 14,
|
||||
Quick = 15,
|
||||
|
||||
Cherish = 16,
|
||||
|
||||
Fast = 17,
|
||||
Level = 18,
|
||||
Lure = 19,
|
||||
Heavy = 20,
|
||||
Love = 21,
|
||||
Friend = 22,
|
||||
Moon = 23,
|
||||
|
||||
Sport = 24,
|
||||
Dream = 25,
|
||||
Beast = 26,
|
||||
|
||||
// Legends: Arceus
|
||||
Strange = 27,
|
||||
LAPoke = 28,
|
||||
LAGreat = 29,
|
||||
LAUltra = 30,
|
||||
LAFeather = 31,
|
||||
LAWing = 32,
|
||||
LAJet = 33,
|
||||
LAHeavy = 34,
|
||||
LALeaden = 35,
|
||||
LAGigaton = 36,
|
||||
LAOrigin = 37,
|
||||
}
|
||||
|
||||
public static class BallExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="ball"/> is an Apricorn Ball (HG/SS)
|
||||
/// </summary>
|
||||
/// <param name="ball">Ball ID</param>
|
||||
/// <returns>True if Apricorn, false if not.</returns>
|
||||
public static bool IsApricornBall(this Ball ball) => ball is >= Ball.Fast and <= Ball.Moon;
|
||||
}
|
||||
/// <param name="ball">Ball ID</param>
|
||||
/// <returns>True if Apricorn, false if not.</returns>
|
||||
public static bool IsApricornBall(this Ball ball) => ball is >= Ball.Fast and <= Ball.Moon;
|
||||
}
|
||||
|
|
|
@ -1,51 +1,50 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="GameVersion"/> analogues used by Colosseum/XD instead of the main-series values.
|
||||
/// </summary>
|
||||
public enum GCVersion : byte
|
||||
{
|
||||
None = 0,
|
||||
FR = 1,
|
||||
LG = 2,
|
||||
S = 8,
|
||||
R = 9,
|
||||
E = 10,
|
||||
CXD = 11,
|
||||
}
|
||||
|
||||
public static class GCVersionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="GameVersion"/> analogues used by Colosseum/XD instead of the main-series values.
|
||||
/// Translates a main-series <see cref="GameVersion"/> to the corresponding <see cref="GCVersion"/> value.
|
||||
/// </summary>
|
||||
public enum GCVersion : byte
|
||||
/// <param name="gbaVersion">Version ID while present in the main-series games</param>
|
||||
/// <returns>Version ID while present in the GameCube games</returns>
|
||||
public static GCVersion GetCXDVersionID(this GameVersion gbaVersion) => gbaVersion switch
|
||||
{
|
||||
None = 0,
|
||||
FR = 1,
|
||||
LG = 2,
|
||||
S = 8,
|
||||
R = 9,
|
||||
E = 10,
|
||||
CXD = 11,
|
||||
}
|
||||
GameVersion.S => GCVersion.S,
|
||||
GameVersion.R => GCVersion.R,
|
||||
GameVersion.E => GCVersion.E,
|
||||
GameVersion.FR => GCVersion.FR,
|
||||
GameVersion.LG => GCVersion.LG,
|
||||
GameVersion.CXD => GCVersion.CXD,
|
||||
_ => GCVersion.None,
|
||||
};
|
||||
|
||||
public static class GCVersionExtensions
|
||||
/// <summary>
|
||||
/// Translates a <see cref="GCVersion"/> to the corresponding main-series <see cref="GameVersion"/> value.
|
||||
/// </summary>
|
||||
/// <param name="gcVersion">Version ID while present in the GameCube games</param>
|
||||
/// <returns>Version ID while present in the main-series games</returns>
|
||||
public static GameVersion GetG3VersionID(this GCVersion gcVersion) => gcVersion switch
|
||||
{
|
||||
/// <summary>
|
||||
/// Translates a main-series <see cref="GameVersion"/> to the corresponding <see cref="GCVersion"/> value.
|
||||
/// </summary>
|
||||
/// <param name="gbaVersion">Version ID while present in the main-series games</param>
|
||||
/// <returns>Version ID while present in the GameCube games</returns>
|
||||
public static GCVersion GetCXDVersionID(this GameVersion gbaVersion) => gbaVersion switch
|
||||
{
|
||||
GameVersion.S => GCVersion.S,
|
||||
GameVersion.R => GCVersion.R,
|
||||
GameVersion.E => GCVersion.E,
|
||||
GameVersion.FR => GCVersion.FR,
|
||||
GameVersion.LG => GCVersion.LG,
|
||||
GameVersion.CXD => GCVersion.CXD,
|
||||
_ => GCVersion.None,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Translates a <see cref="GCVersion"/> to the corresponding main-series <see cref="GameVersion"/> value.
|
||||
/// </summary>
|
||||
/// <param name="gcVersion">Version ID while present in the GameCube games</param>
|
||||
/// <returns>Version ID while present in the main-series games</returns>
|
||||
public static GameVersion GetG3VersionID(this GCVersion gcVersion) => gcVersion switch
|
||||
{
|
||||
GCVersion.S => GameVersion.S,
|
||||
GCVersion.R => GameVersion.R,
|
||||
GCVersion.E => GameVersion.E,
|
||||
GCVersion.FR => GameVersion.FR,
|
||||
GCVersion.LG => GameVersion.LG,
|
||||
GCVersion.CXD => GameVersion.CXD,
|
||||
_ => GameVersion.Unknown,
|
||||
};
|
||||
}
|
||||
GCVersion.S => GameVersion.S,
|
||||
GCVersion.R => GameVersion.R,
|
||||
GCVersion.E => GameVersion.E,
|
||||
GCVersion.FR => GameVersion.FR,
|
||||
GCVersion.LG => GameVersion.LG,
|
||||
GCVersion.CXD => GameVersion.CXD,
|
||||
_ => GameVersion.Unknown,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,485 +1,484 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Game Version ID enum shared between actual Version IDs and lumped version groupings.
|
||||
/// </summary>
|
||||
public enum GameVersion
|
||||
{
|
||||
#region Indicators for method empty arguments & result indication. Not stored values.
|
||||
Invalid = -2,
|
||||
Any = -1,
|
||||
Unknown = 0,
|
||||
#endregion
|
||||
|
||||
// The following values are IDs stored within PKM data, and can also identify individual games.
|
||||
|
||||
#region Gen3
|
||||
/// <summary>
|
||||
/// Game Version ID enum shared between actual Version IDs and lumped version groupings.
|
||||
/// Pokémon Sapphire (GBA)
|
||||
/// </summary>
|
||||
public enum GameVersion
|
||||
{
|
||||
#region Indicators for method empty arguments & result indication. Not stored values.
|
||||
Invalid = -2,
|
||||
Any = -1,
|
||||
Unknown = 0,
|
||||
#endregion
|
||||
|
||||
// The following values are IDs stored within PKM data, and can also identify individual games.
|
||||
|
||||
#region Gen3
|
||||
/// <summary>
|
||||
/// Pokémon Sapphire (GBA)
|
||||
/// </summary>
|
||||
S = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ruby (GBA)
|
||||
/// </summary>
|
||||
R = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Emerald (GBA)
|
||||
/// </summary>
|
||||
E = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon FireRed (GBA)
|
||||
/// </summary>
|
||||
FR = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon LeafGreen (GBA)
|
||||
/// </summary>
|
||||
LG = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Colosseum & Pokémon XD (GameCube)
|
||||
/// </summary>
|
||||
CXD = 15,
|
||||
#endregion
|
||||
|
||||
#region Gen4
|
||||
/// <summary>
|
||||
/// Pokémon Diamond (NDS)
|
||||
/// </summary>
|
||||
D = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Pearl (NDS)
|
||||
/// </summary>
|
||||
P = 11,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Platinum (NDS)
|
||||
/// </summary>
|
||||
Pt = 12,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon HeartGold (NDS)
|
||||
/// </summary>
|
||||
HG = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon SoulSilver (NDS)
|
||||
/// </summary>
|
||||
SS = 8,
|
||||
#endregion
|
||||
|
||||
#region Gen5
|
||||
/// <summary>
|
||||
/// Pokémon White (NDS)
|
||||
/// </summary>
|
||||
W = 20,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black (NDS)
|
||||
/// </summary>
|
||||
B = 21,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon White 2 (NDS)
|
||||
/// </summary>
|
||||
W2 = 22,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black 2 (NDS)
|
||||
/// </summary>
|
||||
B2 = 23,
|
||||
#endregion
|
||||
|
||||
#region Gen6
|
||||
/// <summary>
|
||||
/// Pokémon X (3DS)
|
||||
/// </summary>
|
||||
X = 24,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Y (3DS)
|
||||
/// </summary>
|
||||
Y = 25,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Alpha Sapphire (3DS)
|
||||
/// </summary>
|
||||
AS = 26,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Omega Ruby (3DS)
|
||||
/// </summary>
|
||||
OR = 27,
|
||||
#endregion
|
||||
|
||||
#region Gen7
|
||||
/// <summary>
|
||||
/// Pokémon Sun (3DS)
|
||||
/// </summary>
|
||||
SN = 30,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Moon (3DS)
|
||||
/// </summary>
|
||||
MN = 31,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ultra Sun (3DS)
|
||||
/// </summary>
|
||||
US = 32,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ultra Moon (3DS)
|
||||
/// </summary>
|
||||
UM = 33,
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon GO (GO -> Let's Go/HOME transfers)
|
||||
/// </summary>
|
||||
GO = 34,
|
||||
|
||||
#region Virtual Console (3DS) Gen1
|
||||
/// <summary>
|
||||
/// Pokémon Red (3DS Virtual Console)
|
||||
/// </summary>
|
||||
RD = 35,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Green[JP]/Blue[INT] (3DS Virtual Console)
|
||||
/// </summary>
|
||||
GN = 36,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Blue[JP] (3DS Virtual Console)
|
||||
/// </summary>
|
||||
BU = 37,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Yellow [JP] (3DS Virtual Console)
|
||||
/// </summary>
|
||||
YW = 38,
|
||||
#endregion
|
||||
|
||||
#region Virtual Console (3DS) Gen2
|
||||
/// <summary>
|
||||
/// Pokémon Gold (3DS Virtual Console)
|
||||
/// </summary>
|
||||
GD = 39,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Silver (3DS Virtual Console)
|
||||
/// </summary>
|
||||
SI = 40,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Crystal (3DS Virtual Console)
|
||||
/// </summary>
|
||||
C = 41,
|
||||
#endregion
|
||||
|
||||
#region Nintendo Switch
|
||||
/// <summary>
|
||||
/// Pokémon: Let's Go, Pikachu! (NX)
|
||||
/// </summary>
|
||||
GP = 42,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon: Let's Go, Eevee! (NX)
|
||||
/// </summary>
|
||||
GE = 43,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Sword (NX)
|
||||
/// </summary>
|
||||
SW = 44,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Shield (NX)
|
||||
/// </summary>
|
||||
SH = 45,
|
||||
|
||||
// HOME = 46,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Legends: Arceus (NX)
|
||||
/// </summary>
|
||||
PLA = 47,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Brilliant Diamond (NX)
|
||||
/// </summary>
|
||||
BD = 48,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Shining Pearl (NX)
|
||||
/// </summary>
|
||||
SP = 49,
|
||||
#endregion
|
||||
|
||||
// The following values are not actually stored values in pkm data,
|
||||
// These values are assigned within PKHeX as properties for various logic branching.
|
||||
|
||||
#region Game Groupings (SaveFile type, roughly)
|
||||
/// <summary>
|
||||
/// Pokémon Red & Blue [<see cref="SAV1"/>] identifier.
|
||||
/// </summary>
|
||||
/// <seealso cref="RD"/>
|
||||
/// <seealso cref="GN"/>
|
||||
/// <seealso cref="BU"/>
|
||||
RB,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Red/Blue/Yellow [<see cref="SAV1"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="RD"/>
|
||||
/// <see cref="GN"/>
|
||||
/// <see cref="BU"/>
|
||||
/// <see cref="YW"/>
|
||||
RBY,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Gold & Silver [<see cref="SAV2"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="GD"/>
|
||||
/// <see cref="SI"/>
|
||||
GS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Gold/Silver/Crystal [<see cref="SAV2"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="GD"/>
|
||||
/// <see cref="SI"/>
|
||||
/// <see cref="C"/>
|
||||
GSC,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ruby & Sapphire [<see cref="SAV3"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="R"/>
|
||||
/// <see cref="S"/>
|
||||
RS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ruby/Sapphire/Emerald [<see cref="SAV3"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="R"/>
|
||||
/// <see cref="S"/>
|
||||
/// <see cref="E"/>
|
||||
RSE,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon FireRed/LeafGreen [<see cref="SAV3"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="FR"/>
|
||||
/// <see cref="LG"/>
|
||||
FRLG,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Box Ruby & Sapphire [<see cref="SAV3RSBox"/>] identifier.
|
||||
/// </summary>
|
||||
RSBOX,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Colosseum [<see cref="SAV3Colosseum"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="CXD"/>
|
||||
/// <remarks>Also used to mark Colosseum-only origin data as this game shares a version ID with <see cref="XD"/></remarks>
|
||||
COLO,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon XD [<see cref="SAV3XD"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="CXD"/>
|
||||
/// <remarks>Also used to mark XD-only origin data as this game shares a version ID with <see cref="COLO"/></remarks>
|
||||
XD,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Diamond & Pearl [<see cref="SAV4"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="D"/>
|
||||
/// <see cref="P"/>
|
||||
DP,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Diamond/Pearl/Platinum version group.
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="D"/>
|
||||
/// <see cref="P"/>
|
||||
/// <see cref="Pt"/>
|
||||
DPPt,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon HeartGold & SoulSilver [<see cref="SAV4"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="HG"/>
|
||||
/// <see cref="SS"/>
|
||||
HGSS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Battle Revolution [<see cref="SAV4BR"/>] identifier.
|
||||
/// </summary>
|
||||
BATREV,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black & White version group.
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="B"/>
|
||||
/// <see cref="W"/>
|
||||
BW,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black 2 & White 2 version group.
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="B2"/>
|
||||
/// <see cref="W2"/>
|
||||
B2W2,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon X & Y
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="X"/>
|
||||
/// <see cref="Y"/>
|
||||
XY,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Omega Ruby & Alpha Sapphire Demo [<see cref="SAV6"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="ORAS"/>
|
||||
ORASDEMO,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Omega Ruby & Alpha Sapphire version group.
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="OR"/>
|
||||
/// <see cref="AS"/>
|
||||
ORAS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Sun & Moon
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="SN"/>
|
||||
/// <see cref="MN"/>
|
||||
SM,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ultra Sun & Ultra Moon
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="US"/>
|
||||
/// <see cref="UM"/>
|
||||
USUM,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Let's Go Pikachu & Eevee
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="GP"/>
|
||||
/// <see cref="GE"/>
|
||||
GG,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Sword & Shield
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="SW"/>
|
||||
/// <see cref="SH"/>
|
||||
SWSH,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Brilliant Diamond & Shining Pearl
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="BD"/>
|
||||
/// <see cref="SP"/>
|
||||
BDSP,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 1 Games
|
||||
/// </summary>
|
||||
/// <see cref="RBY"/>
|
||||
Gen1,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 2 Games
|
||||
/// </summary>
|
||||
/// <see cref="GSC"/>
|
||||
Gen2,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 3 Games
|
||||
/// </summary>
|
||||
/// <see cref="RSE"/>
|
||||
/// <see cref="FRLG"/>
|
||||
Gen3,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 4 Games
|
||||
/// </summary>
|
||||
/// <see cref="DPPt"/>
|
||||
/// <see cref="HGSS"/>
|
||||
Gen4,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 5 Games
|
||||
/// </summary>
|
||||
/// <see cref="BW"/>
|
||||
/// <see cref="B2W2"/>
|
||||
Gen5,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 6 Games
|
||||
/// </summary>
|
||||
/// <see cref="XY"/>
|
||||
/// <see cref="ORAS"/>
|
||||
Gen6,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 7 Games on the Nintendo 3DS
|
||||
/// </summary>
|
||||
/// <see cref="SM"/>
|
||||
/// <see cref="USUM"/>
|
||||
Gen7,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 7 Games on the Nintendo Switch
|
||||
/// </summary>
|
||||
/// <see cref="GG"/>
|
||||
/// <see cref="GO"/>
|
||||
Gen7b,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 8 Games
|
||||
/// </summary>
|
||||
/// <see cref="SWSH"/>
|
||||
/// <see cref="BDSP"/>
|
||||
/// <see cref="PLA"/>
|
||||
Gen8,
|
||||
|
||||
/// <summary>
|
||||
/// Pocket Monsters Stadium data origin identifier
|
||||
/// </summary>
|
||||
StadiumJ,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Stadium data origin identifier
|
||||
/// </summary>
|
||||
Stadium,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Stadium 2 data origin identifier
|
||||
/// </summary>
|
||||
Stadium2,
|
||||
#endregion
|
||||
}
|
||||
S = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ruby (GBA)
|
||||
/// </summary>
|
||||
R = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Emerald (GBA)
|
||||
/// </summary>
|
||||
E = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon FireRed (GBA)
|
||||
/// </summary>
|
||||
FR = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon LeafGreen (GBA)
|
||||
/// </summary>
|
||||
LG = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Colosseum & Pokémon XD (GameCube)
|
||||
/// </summary>
|
||||
CXD = 15,
|
||||
#endregion
|
||||
|
||||
#region Gen4
|
||||
/// <summary>
|
||||
/// Pokémon Diamond (NDS)
|
||||
/// </summary>
|
||||
D = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Pearl (NDS)
|
||||
/// </summary>
|
||||
P = 11,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Platinum (NDS)
|
||||
/// </summary>
|
||||
Pt = 12,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon HeartGold (NDS)
|
||||
/// </summary>
|
||||
HG = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon SoulSilver (NDS)
|
||||
/// </summary>
|
||||
SS = 8,
|
||||
#endregion
|
||||
|
||||
#region Gen5
|
||||
/// <summary>
|
||||
/// Pokémon White (NDS)
|
||||
/// </summary>
|
||||
W = 20,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black (NDS)
|
||||
/// </summary>
|
||||
B = 21,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon White 2 (NDS)
|
||||
/// </summary>
|
||||
W2 = 22,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black 2 (NDS)
|
||||
/// </summary>
|
||||
B2 = 23,
|
||||
#endregion
|
||||
|
||||
#region Gen6
|
||||
/// <summary>
|
||||
/// Pokémon X (3DS)
|
||||
/// </summary>
|
||||
X = 24,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Y (3DS)
|
||||
/// </summary>
|
||||
Y = 25,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Alpha Sapphire (3DS)
|
||||
/// </summary>
|
||||
AS = 26,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Omega Ruby (3DS)
|
||||
/// </summary>
|
||||
OR = 27,
|
||||
#endregion
|
||||
|
||||
#region Gen7
|
||||
/// <summary>
|
||||
/// Pokémon Sun (3DS)
|
||||
/// </summary>
|
||||
SN = 30,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Moon (3DS)
|
||||
/// </summary>
|
||||
MN = 31,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ultra Sun (3DS)
|
||||
/// </summary>
|
||||
US = 32,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ultra Moon (3DS)
|
||||
/// </summary>
|
||||
UM = 33,
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon GO (GO -> Let's Go/HOME transfers)
|
||||
/// </summary>
|
||||
GO = 34,
|
||||
|
||||
#region Virtual Console (3DS) Gen1
|
||||
/// <summary>
|
||||
/// Pokémon Red (3DS Virtual Console)
|
||||
/// </summary>
|
||||
RD = 35,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Green[JP]/Blue[INT] (3DS Virtual Console)
|
||||
/// </summary>
|
||||
GN = 36,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Blue[JP] (3DS Virtual Console)
|
||||
/// </summary>
|
||||
BU = 37,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Yellow [JP] (3DS Virtual Console)
|
||||
/// </summary>
|
||||
YW = 38,
|
||||
#endregion
|
||||
|
||||
#region Virtual Console (3DS) Gen2
|
||||
/// <summary>
|
||||
/// Pokémon Gold (3DS Virtual Console)
|
||||
/// </summary>
|
||||
GD = 39,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Silver (3DS Virtual Console)
|
||||
/// </summary>
|
||||
SI = 40,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Crystal (3DS Virtual Console)
|
||||
/// </summary>
|
||||
C = 41,
|
||||
#endregion
|
||||
|
||||
#region Nintendo Switch
|
||||
/// <summary>
|
||||
/// Pokémon: Let's Go, Pikachu! (NX)
|
||||
/// </summary>
|
||||
GP = 42,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon: Let's Go, Eevee! (NX)
|
||||
/// </summary>
|
||||
GE = 43,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Sword (NX)
|
||||
/// </summary>
|
||||
SW = 44,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Shield (NX)
|
||||
/// </summary>
|
||||
SH = 45,
|
||||
|
||||
// HOME = 46,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Legends: Arceus (NX)
|
||||
/// </summary>
|
||||
PLA = 47,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Brilliant Diamond (NX)
|
||||
/// </summary>
|
||||
BD = 48,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Shining Pearl (NX)
|
||||
/// </summary>
|
||||
SP = 49,
|
||||
#endregion
|
||||
|
||||
// The following values are not actually stored values in pk data,
|
||||
// These values are assigned within PKHeX as properties for various logic branching.
|
||||
|
||||
#region Game Groupings (SaveFile type, roughly)
|
||||
/// <summary>
|
||||
/// Pokémon Red & Blue [<see cref="SAV1"/>] identifier.
|
||||
/// </summary>
|
||||
/// <seealso cref="RD"/>
|
||||
/// <seealso cref="GN"/>
|
||||
/// <seealso cref="BU"/>
|
||||
RB,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Red/Blue/Yellow [<see cref="SAV1"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="RD"/>
|
||||
/// <see cref="GN"/>
|
||||
/// <see cref="BU"/>
|
||||
/// <see cref="YW"/>
|
||||
RBY,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Gold & Silver [<see cref="SAV2"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="GD"/>
|
||||
/// <see cref="SI"/>
|
||||
GS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Gold/Silver/Crystal [<see cref="SAV2"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="GD"/>
|
||||
/// <see cref="SI"/>
|
||||
/// <see cref="C"/>
|
||||
GSC,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ruby & Sapphire [<see cref="SAV3"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="R"/>
|
||||
/// <see cref="S"/>
|
||||
RS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ruby/Sapphire/Emerald [<see cref="SAV3"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="R"/>
|
||||
/// <see cref="S"/>
|
||||
/// <see cref="E"/>
|
||||
RSE,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon FireRed/LeafGreen [<see cref="SAV3"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="FR"/>
|
||||
/// <see cref="LG"/>
|
||||
FRLG,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Box Ruby & Sapphire [<see cref="SAV3RSBox"/>] identifier.
|
||||
/// </summary>
|
||||
RSBOX,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Colosseum [<see cref="SAV3Colosseum"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="CXD"/>
|
||||
/// <remarks>Also used to mark Colosseum-only origin data as this game shares a version ID with <see cref="XD"/></remarks>
|
||||
COLO,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon XD [<see cref="SAV3XD"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="CXD"/>
|
||||
/// <remarks>Also used to mark XD-only origin data as this game shares a version ID with <see cref="COLO"/></remarks>
|
||||
XD,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Diamond & Pearl [<see cref="SAV4"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="D"/>
|
||||
/// <see cref="P"/>
|
||||
DP,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Diamond/Pearl/Platinum version group.
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="D"/>
|
||||
/// <see cref="P"/>
|
||||
/// <see cref="Pt"/>
|
||||
DPPt,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon HeartGold & SoulSilver [<see cref="SAV4"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="HG"/>
|
||||
/// <see cref="SS"/>
|
||||
HGSS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Battle Revolution [<see cref="SAV4BR"/>] identifier.
|
||||
/// </summary>
|
||||
BATREV,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black & White version group.
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="B"/>
|
||||
/// <see cref="W"/>
|
||||
BW,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black 2 & White 2 version group.
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="B2"/>
|
||||
/// <see cref="W2"/>
|
||||
B2W2,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon X & Y
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="X"/>
|
||||
/// <see cref="Y"/>
|
||||
XY,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Omega Ruby & Alpha Sapphire Demo [<see cref="SAV6"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="ORAS"/>
|
||||
ORASDEMO,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Omega Ruby & Alpha Sapphire version group.
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="OR"/>
|
||||
/// <see cref="AS"/>
|
||||
ORAS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Sun & Moon
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="SN"/>
|
||||
/// <see cref="MN"/>
|
||||
SM,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ultra Sun & Ultra Moon
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="US"/>
|
||||
/// <see cref="UM"/>
|
||||
USUM,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Let's Go Pikachu & Eevee
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="GP"/>
|
||||
/// <see cref="GE"/>
|
||||
GG,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Sword & Shield
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="SW"/>
|
||||
/// <see cref="SH"/>
|
||||
SWSH,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Brilliant Diamond & Shining Pearl
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="BD"/>
|
||||
/// <see cref="SP"/>
|
||||
BDSP,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 1 Games
|
||||
/// </summary>
|
||||
/// <see cref="RBY"/>
|
||||
Gen1,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 2 Games
|
||||
/// </summary>
|
||||
/// <see cref="GSC"/>
|
||||
Gen2,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 3 Games
|
||||
/// </summary>
|
||||
/// <see cref="RSE"/>
|
||||
/// <see cref="FRLG"/>
|
||||
Gen3,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 4 Games
|
||||
/// </summary>
|
||||
/// <see cref="DPPt"/>
|
||||
/// <see cref="HGSS"/>
|
||||
Gen4,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 5 Games
|
||||
/// </summary>
|
||||
/// <see cref="BW"/>
|
||||
/// <see cref="B2W2"/>
|
||||
Gen5,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 6 Games
|
||||
/// </summary>
|
||||
/// <see cref="XY"/>
|
||||
/// <see cref="ORAS"/>
|
||||
Gen6,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 7 Games on the Nintendo 3DS
|
||||
/// </summary>
|
||||
/// <see cref="SM"/>
|
||||
/// <see cref="USUM"/>
|
||||
Gen7,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 7 Games on the Nintendo Switch
|
||||
/// </summary>
|
||||
/// <see cref="GG"/>
|
||||
/// <see cref="GO"/>
|
||||
Gen7b,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 8 Games
|
||||
/// </summary>
|
||||
/// <see cref="SWSH"/>
|
||||
/// <see cref="BDSP"/>
|
||||
/// <see cref="PLA"/>
|
||||
Gen8,
|
||||
|
||||
/// <summary>
|
||||
/// Pocket Monsters Stadium data origin identifier
|
||||
/// </summary>
|
||||
StadiumJ,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Stadium data origin identifier
|
||||
/// </summary>
|
||||
Stadium,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Stadium 2 data origin identifier
|
||||
/// </summary>
|
||||
Stadium2,
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Gender a <see cref="PKM"/> can have
|
||||
/// </summary>
|
||||
/// <remarks><see cref="Random"/> provided to function for Encounter template values</remarks>
|
||||
public enum Gender : byte
|
||||
{
|
||||
Male = 0,
|
||||
Female = 1,
|
||||
namespace PKHeX.Core;
|
||||
|
||||
Genderless = 2,
|
||||
Random = Genderless,
|
||||
}
|
||||
/// <summary>
|
||||
/// Gender a <see cref="PKM"/> can have
|
||||
/// </summary>
|
||||
/// <remarks><see cref="Random"/> provided to function for Encounter template values</remarks>
|
||||
public enum Gender : byte
|
||||
{
|
||||
Male = 0,
|
||||
Female = 1,
|
||||
|
||||
Genderless = 2,
|
||||
Random = Genderless,
|
||||
}
|
||||
|
|
|
@ -1,50 +1,49 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="GameVersion.CXD"/> Game Language IDs
|
||||
/// </summary>
|
||||
public enum LanguageGC : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="GameVersion.CXD"/> Game Language IDs
|
||||
/// Undefined Language ID, usually indicative of a value not being set.
|
||||
/// </summary>
|
||||
public enum LanguageGC : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Undefined Language ID, usually indicative of a value not being set.
|
||||
/// </summary>
|
||||
/// <remarks>Gen5 Japanese In-game Trades happen to not have their Language value set, and express Language=0.</remarks>
|
||||
Hacked = 0,
|
||||
/// <remarks>Gen5 Japanese In-game Trades happen to not have their Language value set, and express Language=0.</remarks>
|
||||
Hacked = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Japanese (日本語)
|
||||
/// </summary>
|
||||
Japanese = 1,
|
||||
/// <summary>
|
||||
/// Japanese (日本語)
|
||||
/// </summary>
|
||||
Japanese = 1,
|
||||
|
||||
/// <summary>
|
||||
/// English (US/UK/AU)
|
||||
/// </summary>
|
||||
English = 2,
|
||||
/// <summary>
|
||||
/// English (US/UK/AU)
|
||||
/// </summary>
|
||||
English = 2,
|
||||
|
||||
/// <summary>
|
||||
/// German (Deutsch)
|
||||
/// </summary>
|
||||
German = 3,
|
||||
/// <summary>
|
||||
/// German (Deutsch)
|
||||
/// </summary>
|
||||
German = 3,
|
||||
|
||||
/// <summary>
|
||||
/// French (Français)
|
||||
/// </summary>
|
||||
French = 4,
|
||||
/// <summary>
|
||||
/// French (Français)
|
||||
/// </summary>
|
||||
French = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Italian (Italiano)
|
||||
/// </summary>
|
||||
Italian = 5,
|
||||
/// <summary>
|
||||
/// Italian (Italiano)
|
||||
/// </summary>
|
||||
Italian = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Spanish (Español)
|
||||
/// </summary>
|
||||
Spanish = 6,
|
||||
/// <summary>
|
||||
/// Spanish (Español)
|
||||
/// </summary>
|
||||
Spanish = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Unused Language ID
|
||||
/// </summary>
|
||||
/// <remarks>Was reserved for Korean in Gen3 but never utilized.</remarks>
|
||||
UNUSED_6 = 7,
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Unused Language ID
|
||||
/// </summary>
|
||||
/// <remarks>Was reserved for Korean in Gen3 but never utilized.</remarks>
|
||||
UNUSED_6 = 7,
|
||||
}
|
||||
|
|
|
@ -1,65 +1,64 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contiguous series Game Language IDs
|
||||
/// </summary>
|
||||
public enum LanguageID : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Contiguous series Game Language IDs
|
||||
/// Undefined Language ID, usually indicative of a value not being set.
|
||||
/// </summary>
|
||||
public enum LanguageID : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Undefined Language ID, usually indicative of a value not being set.
|
||||
/// </summary>
|
||||
/// <remarks>Gen5 Japanese In-game Trades happen to not have their Language value set, and express Language=0.</remarks>
|
||||
Hacked = 0,
|
||||
/// <remarks>Gen5 Japanese In-game Trades happen to not have their Language value set, and express Language=0.</remarks>
|
||||
Hacked = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Japanese (日本語)
|
||||
/// </summary>
|
||||
Japanese = 1,
|
||||
/// <summary>
|
||||
/// Japanese (日本語)
|
||||
/// </summary>
|
||||
Japanese = 1,
|
||||
|
||||
/// <summary>
|
||||
/// English (US/UK/AU)
|
||||
/// </summary>
|
||||
English = 2,
|
||||
/// <summary>
|
||||
/// English (US/UK/AU)
|
||||
/// </summary>
|
||||
English = 2,
|
||||
|
||||
/// <summary>
|
||||
/// French (Français)
|
||||
/// </summary>
|
||||
French = 3,
|
||||
/// <summary>
|
||||
/// French (Français)
|
||||
/// </summary>
|
||||
French = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Italian (Italiano)
|
||||
/// </summary>
|
||||
Italian = 4,
|
||||
/// <summary>
|
||||
/// Italian (Italiano)
|
||||
/// </summary>
|
||||
Italian = 4,
|
||||
|
||||
/// <summary>
|
||||
/// German (Deutsch)
|
||||
/// </summary>
|
||||
German = 5,
|
||||
/// <summary>
|
||||
/// German (Deutsch)
|
||||
/// </summary>
|
||||
German = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Unused Language ID
|
||||
/// </summary>
|
||||
/// <remarks>Was reserved for Korean in Gen3 but never utilized.</remarks>
|
||||
UNUSED_6 = 6,
|
||||
/// <summary>
|
||||
/// Unused Language ID
|
||||
/// </summary>
|
||||
/// <remarks>Was reserved for Korean in Gen3 but never utilized.</remarks>
|
||||
UNUSED_6 = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Spanish (Español)
|
||||
/// </summary>
|
||||
Spanish = 7,
|
||||
/// <summary>
|
||||
/// Spanish (Español)
|
||||
/// </summary>
|
||||
Spanish = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Korean (한국어)
|
||||
/// </summary>
|
||||
Korean = 8,
|
||||
/// <summary>
|
||||
/// Korean (한국어)
|
||||
/// </summary>
|
||||
Korean = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Chinese Simplified (简体中文)
|
||||
/// </summary>
|
||||
ChineseS = 9,
|
||||
/// <summary>
|
||||
/// Chinese Simplified (简体中文)
|
||||
/// </summary>
|
||||
ChineseS = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Chinese Traditional (繁體中文)
|
||||
/// </summary>
|
||||
ChineseT = 10,
|
||||
}
|
||||
/// <summary>
|
||||
/// Chinese Traditional (繁體中文)
|
||||
/// </summary>
|
||||
ChineseT = 10,
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,49 +1,48 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Elemental type a move has; additionally, types a <see cref="PKM"/> can have.
|
||||
/// </summary>
|
||||
public enum MoveType : sbyte
|
||||
{
|
||||
/// <summary>
|
||||
/// Elemental type a move has; additionally, types a <see cref="PKM"/> can have.
|
||||
/// </summary>
|
||||
public enum MoveType : sbyte
|
||||
Any = -1,
|
||||
Normal,
|
||||
Fighting,
|
||||
Flying,
|
||||
Poison,
|
||||
Ground,
|
||||
Rock,
|
||||
Bug,
|
||||
Ghost,
|
||||
Steel,
|
||||
Fire,
|
||||
Water,
|
||||
Grass,
|
||||
Electric,
|
||||
Psychic,
|
||||
Ice,
|
||||
Dragon,
|
||||
Dark,
|
||||
Fairy,
|
||||
}
|
||||
|
||||
public static class MoveTypeExtensions
|
||||
{
|
||||
public static MoveType GetMoveTypeGeneration(this MoveType type, int generation)
|
||||
{
|
||||
Any = -1,
|
||||
Normal,
|
||||
Fighting,
|
||||
Flying,
|
||||
Poison,
|
||||
Ground,
|
||||
Rock,
|
||||
Bug,
|
||||
Ghost,
|
||||
Steel,
|
||||
Fire,
|
||||
Water,
|
||||
Grass,
|
||||
Electric,
|
||||
Psychic,
|
||||
Ice,
|
||||
Dragon,
|
||||
Dark,
|
||||
Fairy,
|
||||
if (generation <= 2)
|
||||
return GetMoveTypeFromG12(type);
|
||||
return type;
|
||||
}
|
||||
|
||||
public static class MoveTypeExtensions
|
||||
private static MoveType GetMoveTypeFromG12(this MoveType type)
|
||||
{
|
||||
public static MoveType GetMoveTypeGeneration(this MoveType type, int generation)
|
||||
{
|
||||
if (generation <= 2)
|
||||
return GetMoveTypeFromG12(type);
|
||||
if (type <= MoveType.Rock)
|
||||
return type;
|
||||
}
|
||||
|
||||
private static MoveType GetMoveTypeFromG12(this MoveType type)
|
||||
{
|
||||
if (type <= MoveType.Rock)
|
||||
return type;
|
||||
type--; // Skip unused Bird type
|
||||
if (type <= MoveType.Steel)
|
||||
return type;
|
||||
type -= 10; // 10 Normal duplicates
|
||||
type--; // Skip unused Bird type
|
||||
if (type <= MoveType.Steel)
|
||||
return type;
|
||||
}
|
||||
type -= 10; // 10 Normal duplicates
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,49 +1,48 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Nature ID values for the corresponding English nature name.
|
||||
/// </summary>
|
||||
public enum Nature : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Nature ID values for the corresponding English nature name.
|
||||
/// </summary>
|
||||
public enum Nature : byte
|
||||
{
|
||||
Hardy = 0,
|
||||
Lonely = 1,
|
||||
Brave = 2,
|
||||
Adamant = 3,
|
||||
Naughty = 4,
|
||||
Bold = 5,
|
||||
Docile = 6,
|
||||
Relaxed = 7,
|
||||
Impish = 8,
|
||||
Lax = 9,
|
||||
Timid = 10,
|
||||
Hasty = 11,
|
||||
Serious = 12,
|
||||
Jolly = 13,
|
||||
Naive = 14,
|
||||
Modest = 15,
|
||||
Mild = 16,
|
||||
Quiet = 17,
|
||||
Bashful = 18,
|
||||
Rash = 19,
|
||||
Calm = 20,
|
||||
Gentle = 21,
|
||||
Sassy = 22,
|
||||
Careful = 23,
|
||||
Quirky = 24,
|
||||
Hardy = 0,
|
||||
Lonely = 1,
|
||||
Brave = 2,
|
||||
Adamant = 3,
|
||||
Naughty = 4,
|
||||
Bold = 5,
|
||||
Docile = 6,
|
||||
Relaxed = 7,
|
||||
Impish = 8,
|
||||
Lax = 9,
|
||||
Timid = 10,
|
||||
Hasty = 11,
|
||||
Serious = 12,
|
||||
Jolly = 13,
|
||||
Naive = 14,
|
||||
Modest = 15,
|
||||
Mild = 16,
|
||||
Quiet = 17,
|
||||
Bashful = 18,
|
||||
Rash = 19,
|
||||
Calm = 20,
|
||||
Gentle = 21,
|
||||
Sassy = 22,
|
||||
Careful = 23,
|
||||
Quirky = 24,
|
||||
|
||||
Random = 25,
|
||||
}
|
||||
|
||||
public static class NatureUtil
|
||||
{
|
||||
public static Nature GetNature(int value) => value switch
|
||||
{
|
||||
< 0 or >= (int)Nature.Random => Nature.Random,
|
||||
_ => (Nature)value,
|
||||
};
|
||||
|
||||
public static bool IsFixed(this Nature value) => value is >= 0 and < Nature.Random;
|
||||
|
||||
public static bool IsNeutral(this Nature value) => value.IsFixed() && (byte)value % 6 == 0;
|
||||
}
|
||||
Random = 25,
|
||||
}
|
||||
|
||||
public static class NatureUtil
|
||||
{
|
||||
public static Nature GetNature(int value) => value switch
|
||||
{
|
||||
< 0 or >= (int)Nature.Random => Nature.Random,
|
||||
_ => (Nature)value,
|
||||
};
|
||||
|
||||
public static bool IsFixed(this Nature value) => value is >= 0 and < Nature.Random;
|
||||
|
||||
public static bool IsNeutral(this Nature value) => value.IsFixed() && (byte)value % 6 == 0;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 3DS Console Region Identifiers used for Generation 6 & 7 Mystery Gifts
|
||||
/// </summary>
|
||||
public enum Region3DSIndex : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 3DS Console Region Identifiers used for Generation 6 & 7 Mystery Gifts
|
||||
/// </summary>
|
||||
public enum Region3DSIndex : byte
|
||||
{
|
||||
None = 0,
|
||||
Japan = 1,
|
||||
NorthAmerica = 2,
|
||||
Europe = 3,
|
||||
China = 4,
|
||||
Korea = 5,
|
||||
Taiwan = 6,
|
||||
}
|
||||
None = 0,
|
||||
Japan = 1,
|
||||
NorthAmerica = 2,
|
||||
Europe = 3,
|
||||
China = 4,
|
||||
Korea = 5,
|
||||
Taiwan = 6,
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,105 +2,104 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="SaveFile"/> sensitive provider for <see cref="ComboItem"/> data sources.
|
||||
/// </summary>
|
||||
public sealed class FilteredGameDataSource
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="SaveFile"/> sensitive provider for <see cref="ComboItem"/> data sources.
|
||||
/// </summary>
|
||||
public sealed class FilteredGameDataSource
|
||||
public FilteredGameDataSource(SaveFile sav, GameDataSource source, bool HaX = false)
|
||||
{
|
||||
public FilteredGameDataSource(SaveFile sav, GameDataSource source, bool HaX = false)
|
||||
Source = source;
|
||||
Species = GetFilteredSpecies(sav, source, HaX).ToList();
|
||||
Moves = GetFilteredMoves(sav, source, HaX).ToList();
|
||||
if (sav.Generation > 1)
|
||||
{
|
||||
Source = source;
|
||||
Species = GetFilteredSpecies(sav, source, HaX).ToList();
|
||||
Moves = GetFilteredMoves(sav, source, HaX).ToList();
|
||||
if (sav.Generation > 1)
|
||||
{
|
||||
var items = Source.GetItemDataSource(sav.Version, sav.Generation, sav.HeldItems, HaX);
|
||||
items.RemoveAll(i => i.Value > sav.MaxItemID);
|
||||
Items = items;
|
||||
}
|
||||
else
|
||||
{
|
||||
Items = Array.Empty<ComboItem>();
|
||||
}
|
||||
|
||||
var gamelist = GameUtil.GetVersionsWithinRange(sav, sav.Generation).ToList();
|
||||
Games = Source.VersionDataSource.Where(g => gamelist.Contains((GameVersion)g.Value)).ToList();
|
||||
|
||||
Languages = GameDataSource.LanguageDataSource(sav.Generation);
|
||||
Balls = Source.BallDataSource.Where(b => b.Value <= sav.MaxBallID).ToList();
|
||||
Abilities = Source.AbilityDataSource.Where(a => a.Value <= sav.MaxAbilityID).ToList();
|
||||
|
||||
G4GroundTiles = Source.GroundTileDataSource;
|
||||
Natures = Source.NatureDataSource;
|
||||
var items = Source.GetItemDataSource(sav.Version, sav.Generation, sav.HeldItems, HaX);
|
||||
items.RemoveAll(i => i.Value > sav.MaxItemID);
|
||||
Items = items;
|
||||
}
|
||||
else
|
||||
{
|
||||
Items = Array.Empty<ComboItem>();
|
||||
}
|
||||
|
||||
private static IEnumerable<ComboItem> GetFilteredSpecies(IGameValueLimit sav, GameDataSource source, bool HaX = false)
|
||||
{
|
||||
if (HaX)
|
||||
return source.SpeciesDataSource.Where(s => s.Value <= sav.MaxSpeciesID);
|
||||
var gamelist = GameUtil.GetVersionsWithinRange(sav, sav.Generation).ToList();
|
||||
Games = Source.VersionDataSource.Where(g => gamelist.Contains((GameVersion)g.Value)).ToList();
|
||||
|
||||
// Some games cannot acquire every Species that exists. Some can only acquire a subset.
|
||||
return sav switch
|
||||
{
|
||||
SAV7b => source.SpeciesDataSource // LGPE: Kanto 151, Meltan/Melmetal
|
||||
.Where(s => s.Value is <= (int)Core.Species.Mew or (int)Core.Species.Meltan or (int)Core.Species.Melmetal),
|
||||
SAV8LA => source.SpeciesDataSource
|
||||
.Where(s => PersonalTable.LA.IsSpeciesInGame(s.Value)),
|
||||
_ => source.SpeciesDataSource.Where(s => s.Value <= sav.MaxSpeciesID),
|
||||
};
|
||||
}
|
||||
Languages = GameDataSource.LanguageDataSource(sav.Generation);
|
||||
Balls = Source.BallDataSource.Where(b => b.Value <= sav.MaxBallID).ToList();
|
||||
Abilities = Source.AbilityDataSource.Where(a => a.Value <= sav.MaxAbilityID).ToList();
|
||||
|
||||
private static IEnumerable<ComboItem> GetFilteredMoves(IGameValueLimit sav, GameDataSource source, bool HaX = false)
|
||||
{
|
||||
if (HaX)
|
||||
return source.HaXMoveDataSource.Where(m => m.Value <= sav.MaxMoveID);
|
||||
|
||||
var legal = source.LegalMoveDataSource;
|
||||
return sav switch
|
||||
{
|
||||
SAV7b => legal.Where(s => Legal.AllowedMovesGG.Contains((short) s.Value)), // LGPE: Not all moves are available
|
||||
_ => legal.Where(m => m.Value <= sav.MaxMoveID),
|
||||
};
|
||||
}
|
||||
|
||||
public readonly GameDataSource Source;
|
||||
|
||||
public readonly IReadOnlyList<ComboItem> Moves;
|
||||
public readonly IReadOnlyList<ComboItem> Balls;
|
||||
public readonly IReadOnlyList<ComboItem> Games;
|
||||
public readonly IReadOnlyList<ComboItem> Items;
|
||||
public readonly IReadOnlyList<ComboItem> Species;
|
||||
public readonly IReadOnlyList<ComboItem> Languages;
|
||||
public readonly IReadOnlyList<ComboItem> Abilities;
|
||||
public readonly IReadOnlyList<ComboItem> Natures;
|
||||
public readonly IReadOnlyList<ComboItem> G4GroundTiles;
|
||||
public readonly IReadOnlyList<ComboItem> ConsoleRegions = GameDataSource.Regions;
|
||||
|
||||
public IReadOnlyList<ComboItem> GetAbilityList(PKM pkm)
|
||||
{
|
||||
var abilities = pkm.PersonalInfo.Abilities;
|
||||
int format = pkm.Format;
|
||||
return GetAbilityList(abilities, format);
|
||||
}
|
||||
|
||||
public IReadOnlyList<ComboItem> GetAbilityList(IReadOnlyList<int> abilities, int format)
|
||||
{
|
||||
var count = format == 3 && (abilities[1] == 0 || abilities[1] == abilities[0]) ? 1 : abilities.Count;
|
||||
var list = new ComboItem[count];
|
||||
|
||||
var alist = Source.Strings.Ability;
|
||||
var suffix = AbilityIndexSuffixes;
|
||||
for (int i = 0; i < list.Length; i++)
|
||||
{
|
||||
var ability = abilities[i];
|
||||
list[i] = new ComboItem(alist[ability] + suffix[i], ability);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static readonly string[] AbilityIndexSuffixes = { " (1)", " (2)", " (H)" };
|
||||
G4GroundTiles = Source.GroundTileDataSource;
|
||||
Natures = Source.NatureDataSource;
|
||||
}
|
||||
|
||||
private static IEnumerable<ComboItem> GetFilteredSpecies(IGameValueLimit sav, GameDataSource source, bool HaX = false)
|
||||
{
|
||||
if (HaX)
|
||||
return source.SpeciesDataSource.Where(s => s.Value <= sav.MaxSpeciesID);
|
||||
|
||||
// Some games cannot acquire every Species that exists. Some can only acquire a subset.
|
||||
return sav switch
|
||||
{
|
||||
SAV7b => source.SpeciesDataSource // LGPE: Kanto 151, Meltan/Melmetal
|
||||
.Where(s => s.Value is <= (int)Core.Species.Mew or (int)Core.Species.Meltan or (int)Core.Species.Melmetal),
|
||||
SAV8LA => source.SpeciesDataSource
|
||||
.Where(s => PersonalTable.LA.IsSpeciesInGame(s.Value)),
|
||||
_ => source.SpeciesDataSource.Where(s => s.Value <= sav.MaxSpeciesID),
|
||||
};
|
||||
}
|
||||
|
||||
private static IEnumerable<ComboItem> GetFilteredMoves(IGameValueLimit sav, GameDataSource source, bool HaX = false)
|
||||
{
|
||||
if (HaX)
|
||||
return source.HaXMoveDataSource.Where(m => m.Value <= sav.MaxMoveID);
|
||||
|
||||
var legal = source.LegalMoveDataSource;
|
||||
return sav switch
|
||||
{
|
||||
SAV7b => legal.Where(s => Legal.AllowedMovesGG.Contains((short) s.Value)), // LGPE: Not all moves are available
|
||||
_ => legal.Where(m => m.Value <= sav.MaxMoveID),
|
||||
};
|
||||
}
|
||||
|
||||
public readonly GameDataSource Source;
|
||||
|
||||
public readonly IReadOnlyList<ComboItem> Moves;
|
||||
public readonly IReadOnlyList<ComboItem> Balls;
|
||||
public readonly IReadOnlyList<ComboItem> Games;
|
||||
public readonly IReadOnlyList<ComboItem> Items;
|
||||
public readonly IReadOnlyList<ComboItem> Species;
|
||||
public readonly IReadOnlyList<ComboItem> Languages;
|
||||
public readonly IReadOnlyList<ComboItem> Abilities;
|
||||
public readonly IReadOnlyList<ComboItem> Natures;
|
||||
public readonly IReadOnlyList<ComboItem> G4GroundTiles;
|
||||
public readonly IReadOnlyList<ComboItem> ConsoleRegions = GameDataSource.Regions;
|
||||
|
||||
public IReadOnlyList<ComboItem> GetAbilityList(PKM pk)
|
||||
{
|
||||
var abilities = pk.PersonalInfo.Abilities;
|
||||
int format = pk.Format;
|
||||
return GetAbilityList(abilities, format);
|
||||
}
|
||||
|
||||
public IReadOnlyList<ComboItem> GetAbilityList(IReadOnlyList<int> abilities, int format)
|
||||
{
|
||||
var count = format == 3 && (abilities[1] == 0 || abilities[1] == abilities[0]) ? 1 : abilities.Count;
|
||||
var list = new ComboItem[count];
|
||||
|
||||
var alist = Source.Strings.Ability;
|
||||
var suffix = AbilityIndexSuffixes;
|
||||
for (int i = 0; i < list.Length; i++)
|
||||
{
|
||||
var ability = abilities[i];
|
||||
list[i] = new ComboItem(alist[ability] + suffix[i], ability);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static readonly string[] AbilityIndexSuffixes = { " (1)", " (2)", " (H)" };
|
||||
}
|
||||
|
|
|
@ -1,127 +1,126 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Bundles raw string inputs into lists that can be used in data binding.
|
||||
/// </summary>
|
||||
public sealed class GameDataSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Bundles raw string inputs into lists that can be used in data binding.
|
||||
/// </summary>
|
||||
public sealed class GameDataSource
|
||||
public static readonly IReadOnlyList<ComboItem> Regions = new List<ComboItem>
|
||||
{
|
||||
public static readonly IReadOnlyList<ComboItem> Regions = new List<ComboItem>
|
||||
new ("Japan (日本)", 0),
|
||||
new ("Americas (NA/SA)", 1),
|
||||
new ("Europe (EU/AU)", 2),
|
||||
new ("China (中国大陆)", 4),
|
||||
new ("Korea (한국)", 5),
|
||||
new ("Taiwan (香港/台灣)", 6),
|
||||
};
|
||||
|
||||
private static readonly List<ComboItem> LanguageList = new()
|
||||
{
|
||||
new ComboItem("JPN (日本語)", (int)LanguageID.Japanese),
|
||||
new ComboItem("ENG (English)", (int)LanguageID.English),
|
||||
new ComboItem("FRE (Français)", (int)LanguageID.French),
|
||||
new ComboItem("ITA (Italiano)", (int)LanguageID.Italian),
|
||||
new ComboItem("GER (Deutsch)", (int)LanguageID.German),
|
||||
new ComboItem("ESP (Español)", (int)LanguageID.Spanish),
|
||||
new ComboItem("KOR (한국어)", (int)LanguageID.Korean),
|
||||
new ComboItem("CHS (简体中文)", (int)LanguageID.ChineseS),
|
||||
new ComboItem("CHT (繁體中文)", (int)LanguageID.ChineseT),
|
||||
};
|
||||
|
||||
public GameDataSource(GameStrings s)
|
||||
{
|
||||
Strings = s;
|
||||
BallDataSource = GetBalls(s.itemlist);
|
||||
SpeciesDataSource = Util.GetCBList(s.specieslist);
|
||||
NatureDataSource = Util.GetCBList(s.natures);
|
||||
AbilityDataSource = Util.GetCBList(s.abilitylist);
|
||||
GroundTileDataSource = Util.GetUnsortedCBList(s.groundtiletypes, GroundTileTypeExtensions.ValidTileTypes);
|
||||
|
||||
var moves = Util.GetCBList(s.movelist);
|
||||
HaXMoveDataSource = moves;
|
||||
var legal = new List<ComboItem>(moves);
|
||||
legal.RemoveAll(m => MoveInfo.Z_Moves.Contains(m.Value));
|
||||
LegalMoveDataSource = legal;
|
||||
|
||||
VersionDataSource = GetVersionList(s);
|
||||
|
||||
Met = new MetDataSource(s);
|
||||
|
||||
Empty = new ComboItem(s.Species[0], 0);
|
||||
}
|
||||
|
||||
/// <summary> Strings that this object's lists were generated with. </summary>
|
||||
public readonly GameStrings Strings;
|
||||
|
||||
/// <summary> Contains Met Data lists to source lists from. </summary>
|
||||
public readonly MetDataSource Met;
|
||||
|
||||
/// <summary> Represents "(None)", localized to this object's language strings. </summary>
|
||||
public readonly ComboItem Empty;
|
||||
|
||||
public readonly IReadOnlyList<ComboItem> SpeciesDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> BallDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> NatureDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> AbilityDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> VersionDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> LegalMoveDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> HaXMoveDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> GroundTileDataSource;
|
||||
|
||||
private static IReadOnlyList<ComboItem> GetBalls(string[] itemList)
|
||||
{
|
||||
// ignores Poke/Great/Ultra
|
||||
ReadOnlySpan<ushort> ball_nums = stackalloc ushort[] { 007, 576, 013, 492, 497, 014, 495, 493, 496, 494, 011, 498, 008, 006, 012, 015, 009, 005, 499, 010, 001, 016, 851, 1785, 1710, 1711, 1712, 1713, 1746, 1747, 1748, 1749, 1750, 1771 };
|
||||
ReadOnlySpan<byte> ball_vals = stackalloc byte[] { 007, 025, 013, 017, 022, 014, 020, 018, 021, 019, 011, 023, 008, 006, 012, 015, 009, 005, 024, 010, 001, 016, 026, 0027, 0028, 0029, 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037 };
|
||||
return Util.GetVariedCBListBall(itemList, ball_nums, ball_vals);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ComboItem> GetVersionList(GameStrings s)
|
||||
{
|
||||
var list = s.gamelist;
|
||||
ReadOnlySpan<byte> games = stackalloc byte[]
|
||||
{
|
||||
new ("Japan (日本)", 0),
|
||||
new ("Americas (NA/SA)", 1),
|
||||
new ("Europe (EU/AU)", 2),
|
||||
new ("China (中国大陆)", 4),
|
||||
new ("Korea (한국)", 5),
|
||||
new ("Taiwan (香港/台灣)", 6),
|
||||
47, // 8 legends arceus
|
||||
48, 49, // 8 bdsp
|
||||
44, 45, // 8 swsh
|
||||
42, 43, // 7 gg
|
||||
30, 31, // 7 sm
|
||||
32, 33, // 7 usum
|
||||
24, 25, // 6 xy
|
||||
27, 26, // 6 oras
|
||||
21, 20, // 5 bw
|
||||
23, 22, // 5 b2w2
|
||||
10, 11, 12, // 4 dppt
|
||||
07, 08, // 4 hgss
|
||||
02, 01, 03, // 3 rse
|
||||
04, 05, // 3 frlg
|
||||
15, // 3 cxd
|
||||
|
||||
39, 40, 41, // 7vc2
|
||||
35, 36, 37, 38, // 7vc1
|
||||
34, // 7go
|
||||
};
|
||||
|
||||
private static readonly List<ComboItem> LanguageList = new()
|
||||
{
|
||||
new ComboItem("JPN (日本語)", (int)LanguageID.Japanese),
|
||||
new ComboItem("ENG (English)", (int)LanguageID.English),
|
||||
new ComboItem("FRE (Français)", (int)LanguageID.French),
|
||||
new ComboItem("ITA (Italiano)", (int)LanguageID.Italian),
|
||||
new ComboItem("GER (Deutsch)", (int)LanguageID.German),
|
||||
new ComboItem("ESP (Español)", (int)LanguageID.Spanish),
|
||||
new ComboItem("KOR (한국어)", (int)LanguageID.Korean),
|
||||
new ComboItem("CHS (简体中文)", (int)LanguageID.ChineseS),
|
||||
new ComboItem("CHT (繁體中文)", (int)LanguageID.ChineseT),
|
||||
};
|
||||
return Util.GetUnsortedCBList(list, games);
|
||||
}
|
||||
|
||||
public GameDataSource(GameStrings s)
|
||||
{
|
||||
Strings = s;
|
||||
BallDataSource = GetBalls(s.itemlist);
|
||||
SpeciesDataSource = Util.GetCBList(s.specieslist);
|
||||
NatureDataSource = Util.GetCBList(s.natures);
|
||||
AbilityDataSource = Util.GetCBList(s.abilitylist);
|
||||
GroundTileDataSource = Util.GetUnsortedCBList(s.groundtiletypes, GroundTileTypeExtensions.ValidTileTypes);
|
||||
public List<ComboItem> GetItemDataSource(GameVersion game, int generation, IReadOnlyList<ushort> allowed, bool HaX = false)
|
||||
{
|
||||
var items = Strings.GetItemStrings(generation, game);
|
||||
return HaX ? Util.GetCBList(items) : Util.GetCBList(items, allowed);
|
||||
}
|
||||
|
||||
var moves = Util.GetCBList(s.movelist);
|
||||
HaXMoveDataSource = moves;
|
||||
var legal = new List<ComboItem>(moves);
|
||||
legal.RemoveAll(m => MoveInfo.Z_Moves.Contains(m.Value));
|
||||
LegalMoveDataSource = legal;
|
||||
|
||||
VersionDataSource = GetVersionList(s);
|
||||
|
||||
Met = new MetDataSource(s);
|
||||
|
||||
Empty = new ComboItem(s.Species[0], 0);
|
||||
}
|
||||
|
||||
/// <summary> Strings that this object's lists were generated with. </summary>
|
||||
public readonly GameStrings Strings;
|
||||
|
||||
/// <summary> Contains Met Data lists to source lists from. </summary>
|
||||
public readonly MetDataSource Met;
|
||||
|
||||
/// <summary> Represents "(None)", localized to this object's language strings. </summary>
|
||||
public readonly ComboItem Empty;
|
||||
|
||||
public readonly IReadOnlyList<ComboItem> SpeciesDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> BallDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> NatureDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> AbilityDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> VersionDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> LegalMoveDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> HaXMoveDataSource;
|
||||
public readonly IReadOnlyList<ComboItem> GroundTileDataSource;
|
||||
|
||||
private static IReadOnlyList<ComboItem> GetBalls(string[] itemList)
|
||||
{
|
||||
// ignores Poke/Great/Ultra
|
||||
ReadOnlySpan<ushort> ball_nums = stackalloc ushort[] { 007, 576, 013, 492, 497, 014, 495, 493, 496, 494, 011, 498, 008, 006, 012, 015, 009, 005, 499, 010, 001, 016, 851, 1785, 1710, 1711, 1712, 1713, 1746, 1747, 1748, 1749, 1750, 1771 };
|
||||
ReadOnlySpan<byte> ball_vals = stackalloc byte[] { 007, 025, 013, 017, 022, 014, 020, 018, 021, 019, 011, 023, 008, 006, 012, 015, 009, 005, 024, 010, 001, 016, 026, 0027, 0028, 0029, 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037 };
|
||||
return Util.GetVariedCBListBall(itemList, ball_nums, ball_vals);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ComboItem> GetVersionList(GameStrings s)
|
||||
{
|
||||
var list = s.gamelist;
|
||||
ReadOnlySpan<byte> games = stackalloc byte[]
|
||||
{
|
||||
47, // 8 legends arceus
|
||||
48, 49, // 8 bdsp
|
||||
44, 45, // 8 swsh
|
||||
42, 43, // 7 gg
|
||||
30, 31, // 7 sm
|
||||
32, 33, // 7 usum
|
||||
24, 25, // 6 xy
|
||||
27, 26, // 6 oras
|
||||
21, 20, // 5 bw
|
||||
23, 22, // 5 b2w2
|
||||
10, 11, 12, // 4 dppt
|
||||
07, 08, // 4 hgss
|
||||
02, 01, 03, // 3 rse
|
||||
04, 05, // 3 frlg
|
||||
15, // 3 cxd
|
||||
|
||||
39, 40, 41, // 7vc2
|
||||
35, 36, 37, 38, // 7vc1
|
||||
34, // 7go
|
||||
};
|
||||
|
||||
return Util.GetUnsortedCBList(list, games);
|
||||
}
|
||||
|
||||
public List<ComboItem> GetItemDataSource(GameVersion game, int generation, IReadOnlyList<ushort> allowed, bool HaX = false)
|
||||
{
|
||||
var items = Strings.GetItemStrings(generation, game);
|
||||
return HaX ? Util.GetCBList(items) : Util.GetCBList(items, allowed);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<ComboItem> LanguageDataSource(int gen)
|
||||
{
|
||||
var languages = new List<ComboItem>(LanguageList);
|
||||
if (gen == 3)
|
||||
languages.RemoveAll(l => l.Value >= (int)LanguageID.Korean);
|
||||
else if (gen < 7)
|
||||
languages.RemoveAll(l => l.Value > (int)LanguageID.Korean);
|
||||
return languages;
|
||||
}
|
||||
public static IReadOnlyList<ComboItem> LanguageDataSource(int gen)
|
||||
{
|
||||
var languages = new List<ComboItem>(LanguageList);
|
||||
if (gen == 3)
|
||||
languages.RemoveAll(l => l.Value >= (int)LanguageID.Korean);
|
||||
else if (gen < 7)
|
||||
languages.RemoveAll(l => l.Value > (int)LanguageID.Korean);
|
||||
return languages;
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue