Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
using System.Drawing;
|
2018-07-15 00:53:37 +00:00
|
|
|
using PKHeX.Core;
|
2021-11-27 23:48:08 +00:00
|
|
|
using PKHeX.Drawing.PokeSprite.Properties;
|
2018-07-15 00:53:37 +00:00
|
|
|
|
2021-12-10 08:15:04 +00:00
|
|
|
namespace PKHeX.Drawing.PokeSprite;
|
|
|
|
|
|
|
|
public abstract class SpriteBuilder : ISpriteBuilder<Image>
|
2018-07-15 00:53:37 +00:00
|
|
|
{
|
2021-12-10 08:15:04 +00:00
|
|
|
public static bool ShowEggSpriteAsItem { get; set; } = true;
|
|
|
|
public static bool ShowEncounterBall { get; set; } = true;
|
|
|
|
public static SpriteBackgroundType ShowEncounterColor { get; set; } = SpriteBackgroundType.FullBackground;
|
|
|
|
public static SpriteBackgroundType ShowEncounterColorPKM { get; set; }
|
2022-11-25 01:42:17 +00:00
|
|
|
public static SpriteBackgroundType ShowTeraType { get; set; } = SpriteBackgroundType.TopStripe;
|
2021-12-10 08:15:04 +00:00
|
|
|
public static bool ShowExperiencePercent { get; set; }
|
2022-11-25 01:42:17 +00:00
|
|
|
public static byte ShowTeraOpacityStripe { get; set; }
|
|
|
|
public static int ShowTeraThicknessStripe { get; set; }
|
|
|
|
public static byte ShowTeraOpacityBackground { get; set; }
|
2021-12-10 08:15:04 +00:00
|
|
|
public static byte ShowEncounterOpacityStripe { get; set; }
|
|
|
|
public static byte ShowEncounterOpacityBackground { get; set; }
|
|
|
|
public static int ShowEncounterThicknessStripe { get; set; }
|
|
|
|
|
|
|
|
/// <summary> Width of the generated Sprite image. </summary>
|
|
|
|
public abstract int Width { get; }
|
|
|
|
/// <summary> Height of the generated Sprite image. </summary>
|
|
|
|
public abstract int Height { get; }
|
|
|
|
|
|
|
|
/// <summary> Minimum amount of padding on the right side of the image when layering an item sprite. </summary>
|
|
|
|
protected abstract int ItemShiftX { get; }
|
|
|
|
/// <summary> Minimum amount of padding on the bottom side of the image when layering an item sprite. </summary>
|
|
|
|
protected abstract int ItemShiftY { get; }
|
|
|
|
/// <summary> Max width / height of an item image. </summary>
|
|
|
|
protected abstract int ItemMaxSize { get; }
|
|
|
|
|
|
|
|
protected abstract int EggItemShiftX { get; }
|
|
|
|
protected abstract int EggItemShiftY { get; }
|
|
|
|
|
2022-02-05 22:54:01 +00:00
|
|
|
public abstract bool HasFallbackMethod { get; }
|
|
|
|
|
2021-12-10 08:15:04 +00:00
|
|
|
public abstract Bitmap Hover { get; }
|
|
|
|
public abstract Bitmap View { get; }
|
|
|
|
public abstract Bitmap Set { get; }
|
|
|
|
public abstract Bitmap Delete { get; }
|
|
|
|
public abstract Bitmap Transparent { get; }
|
|
|
|
public abstract Bitmap Drag { get; }
|
|
|
|
public abstract Bitmap UnknownItem { get; }
|
|
|
|
public abstract Bitmap None { get; }
|
|
|
|
public abstract Bitmap ItemTM { get; }
|
|
|
|
public abstract Bitmap ItemTR { get; }
|
|
|
|
|
|
|
|
private const double UnknownFormTransparency = 0.5;
|
|
|
|
private const double ShinyTransparency = 0.7;
|
|
|
|
private const double EggUnderLayerTransparency = 0.33;
|
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
protected abstract string GetSpriteStringSpeciesOnly(ushort species);
|
2021-12-10 08:15:04 +00:00
|
|
|
|
2022-08-27 19:53:30 +00:00
|
|
|
protected abstract string GetSpriteAll(ushort species, byte form, int gender, uint formarg, bool shiny, int generation);
|
|
|
|
protected abstract string GetSpriteAllSecondary(ushort species, byte form, int gender, uint formarg, bool shiny, int generation);
|
2021-12-10 08:15:04 +00:00
|
|
|
protected abstract string GetItemResourceName(int item);
|
|
|
|
protected abstract Bitmap Unknown { get; }
|
2022-08-27 06:43:36 +00:00
|
|
|
protected abstract Bitmap GetEggSprite(ushort species);
|
2021-12-10 08:15:04 +00:00
|
|
|
public abstract Bitmap ShadowLugia { get; }
|
|
|
|
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Ensures all data is set up to generate sprites for the save file.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="sav"></param>
|
2021-12-10 08:15:04 +00:00
|
|
|
public void Initialize(SaveFile sav)
|
2018-07-15 00:53:37 +00:00
|
|
|
{
|
2021-12-10 08:15:04 +00:00
|
|
|
if (sav.Generation != 3)
|
|
|
|
return;
|
2018-07-15 00:53:37 +00:00
|
|
|
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
// If the game is indeterminate, we might have different form sprites.
|
|
|
|
// Currently, this only applies to Gen3's FireRed / LeafGreen
|
2021-12-10 08:15:04 +00:00
|
|
|
Game = sav.Version;
|
|
|
|
if (Game == GameVersion.FRLG)
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
Game = ReferenceEquals(sav.Personal, PersonalTable.FR) ? GameVersion.FR : GameVersion.LG;
|
2021-12-10 08:15:04 +00:00
|
|
|
}
|
2018-07-22 02:20:11 +00:00
|
|
|
|
2021-12-10 08:15:04 +00:00
|
|
|
private GameVersion Game;
|
2018-07-15 00:53:37 +00:00
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
private static byte GetDeoxysForm(GameVersion game) => game switch
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
|
|
|
GameVersion.FR => 1, // Attack
|
|
|
|
GameVersion.LG => 2, // Defense
|
|
|
|
GameVersion.E => 3, // Speed
|
|
|
|
_ => 0,
|
|
|
|
};
|
2018-07-15 00:53:37 +00:00
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
private static byte GetArceusForm4(byte form) => form switch
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
2022-08-27 06:43:36 +00:00
|
|
|
> 9 => --form, // Realign to Gen5+ type indexes
|
|
|
|
9 => byte.MaxValue, // Curse, make it show as unrecognized form since we don't have a sprite.
|
2021-12-10 08:15:04 +00:00
|
|
|
_ => form,
|
|
|
|
};
|
2021-09-06 18:21:59 +00:00
|
|
|
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Builds a new sprite image with the requested parameters.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="species">Entity Species ID</param>
|
|
|
|
/// <param name="form">Entity Form index</param>
|
|
|
|
/// <param name="gender">Entity gender</param>
|
|
|
|
/// <param name="formarg">Entity <see cref="IFormArgument.FormArgument"/> raw value</param>
|
|
|
|
/// <param name="heldItem">Entity held item ID</param>
|
|
|
|
/// <param name="isEgg">Is currently in an egg</param>
|
2022-08-22 00:34:32 +00:00
|
|
|
/// <param name="shiny">Is it shiny</param>
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
/// <param name="generation"></param>
|
|
|
|
/// <param name="tweak"></param>
|
2022-08-27 06:43:36 +00:00
|
|
|
public Image GetSprite(ushort species, byte form, int gender, uint formarg, int heldItem, bool isEgg, Shiny shiny = Shiny.Never, int generation = -1, SpriteBuilderTweak tweak = SpriteBuilderTweak.None)
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
|
|
|
if (species == 0)
|
|
|
|
return None;
|
2018-07-15 00:53:37 +00:00
|
|
|
|
2021-12-10 08:15:04 +00:00
|
|
|
if (generation == 3 && species == (int)Species.Deoxys) // Depends on Gen3 save file version
|
|
|
|
form = GetDeoxysForm(Game);
|
|
|
|
else if (generation == 4 && species == (int)Species.Arceus) // Curse type's existence in Gen4
|
|
|
|
form = GetArceusForm4(form);
|
2018-07-15 00:53:37 +00:00
|
|
|
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
var baseImage = GetBaseImage(species, form, gender, formarg, shiny.IsShiny(), generation);
|
|
|
|
return GetSprite(baseImage, species, heldItem, isEgg, shiny, generation, tweak);
|
2021-12-10 08:15:04 +00:00
|
|
|
}
|
2018-10-21 04:05:15 +00:00
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
public Image GetSprite(Image baseSprite, ushort species, int heldItem, bool isEgg, Shiny shiny, int generation = -1, SpriteBuilderTweak tweak = SpriteBuilderTweak.None)
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
|
|
|
if (isEgg)
|
|
|
|
baseSprite = LayerOverImageEgg(baseSprite, species, heldItem != 0);
|
|
|
|
if (heldItem > 0)
|
|
|
|
baseSprite = LayerOverImageItem(baseSprite, heldItem, generation);
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
if (shiny.IsShiny())
|
|
|
|
baseSprite = LayerOverImageShiny(baseSprite, tweak, generation >= 8 && shiny == Shiny.AlwaysSquare ? Shiny.AlwaysSquare : Shiny.Always);
|
2021-12-10 08:15:04 +00:00
|
|
|
return baseSprite;
|
|
|
|
}
|
2018-07-15 00:53:37 +00:00
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
private Image GetBaseImage(ushort species, byte form, int gender, uint formarg, bool shiny, int generation)
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
|
|
|
var img = FormInfo.IsTotemForm(species, form, generation)
|
|
|
|
? GetBaseImageTotem(species, form, gender, formarg, shiny, generation)
|
|
|
|
: GetBaseImageDefault(species, form, gender, formarg, shiny, generation);
|
|
|
|
return img ?? GetBaseImageFallback(species, form, gender, formarg, shiny, generation);
|
|
|
|
}
|
2018-07-15 00:53:37 +00:00
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
private Image? GetBaseImageTotem(ushort species, byte form, int gender, uint formarg, bool shiny, int generation)
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
|
|
|
var baseform = FormInfo.GetTotemBaseForm(species, form);
|
|
|
|
var baseImage = GetBaseImageDefault(species, baseform, gender, formarg, shiny, generation);
|
|
|
|
if (baseImage == null)
|
|
|
|
return null;
|
|
|
|
return ImageUtil.ToGrayscale(baseImage);
|
|
|
|
}
|
2018-07-15 00:53:37 +00:00
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
private Image? GetBaseImageDefault(ushort species, byte form, int gender, uint formarg, bool shiny, int generation)
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
|
|
|
var file = GetSpriteAll(species, form, gender, formarg, shiny, generation);
|
2022-02-05 22:54:01 +00:00
|
|
|
var resource = (Image?)Resources.ResourceManager.GetObject(file);
|
|
|
|
if (resource is null && HasFallbackMethod)
|
|
|
|
{
|
|
|
|
file = GetSpriteAllSecondary(species, form, gender, formarg, shiny, generation);
|
|
|
|
resource = (Image?)Resources.ResourceManager.GetObject(file);
|
|
|
|
}
|
|
|
|
return resource;
|
2021-12-10 08:15:04 +00:00
|
|
|
}
|
2018-07-15 00:53:37 +00:00
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
private Image GetBaseImageFallback(ushort species, byte form, int gender, uint formarg, bool shiny, int generation)
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
|
|
|
if (shiny) // try again without shiny
|
2018-07-15 00:53:37 +00:00
|
|
|
{
|
2021-12-10 08:15:04 +00:00
|
|
|
var img = GetBaseImageDefault(species, form, gender, formarg, false, generation);
|
|
|
|
if (img != null)
|
|
|
|
return img;
|
2018-07-15 00:53:37 +00:00
|
|
|
}
|
|
|
|
|
2021-12-10 08:15:04 +00:00
|
|
|
// try again without form
|
|
|
|
var baseImage = (Image?)Resources.ResourceManager.GetObject(GetSpriteStringSpeciesOnly(species));
|
|
|
|
if (baseImage == null) // failed again
|
|
|
|
return Unknown;
|
|
|
|
return ImageUtil.LayerImage(baseImage, Unknown, 0, 0, UnknownFormTransparency);
|
|
|
|
}
|
2018-07-15 00:53:37 +00:00
|
|
|
|
2021-12-10 08:15:04 +00:00
|
|
|
private Image LayerOverImageItem(Image baseImage, int item, int generation)
|
|
|
|
{
|
2022-11-25 01:42:17 +00:00
|
|
|
var lump = HeldItemLumpUtil.GetIsLump(item, generation);
|
|
|
|
var itemimg = lump switch
|
2018-07-15 00:53:37 +00:00
|
|
|
{
|
2022-11-25 01:42:17 +00:00
|
|
|
HeldItemLumpImage.TechnicalMachine => ItemTM,
|
|
|
|
HeldItemLumpImage.TechnicalRecord => ItemTR,
|
2021-12-10 08:15:04 +00:00
|
|
|
_ => (Image?)Resources.ResourceManager.GetObject(GetItemResourceName(item)) ?? UnknownItem,
|
|
|
|
};
|
2018-07-15 00:53:37 +00:00
|
|
|
|
2021-12-10 08:15:04 +00:00
|
|
|
// Redraw item in bottom right corner; since images are cropped, try to not have them at the edge
|
|
|
|
int x = baseImage.Width - itemimg.Width - ((ItemMaxSize - itemimg.Width) / 4) - ItemShiftX;
|
|
|
|
int y = baseImage.Height - itemimg.Height - ItemShiftY;
|
|
|
|
return ImageUtil.LayerImage(baseImage, itemimg, x, y);
|
|
|
|
}
|
2018-08-19 23:45:20 +00:00
|
|
|
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
private static Image LayerOverImageShiny(Image baseImage, SpriteBuilderTweak tweak, Shiny shiny)
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
|
|
|
// Add shiny star to top left of image.
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
Bitmap rare;
|
|
|
|
if (shiny is Shiny.AlwaysSquare)
|
2021-12-10 08:15:04 +00:00
|
|
|
rare = Resources.rare_icon_2;
|
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
|
|
|
else if (tweak.HasFlagFast(SpriteBuilderTweak.BoxBackgroundRed))
|
|
|
|
rare = Resources.rare_icon_alt;
|
|
|
|
else
|
|
|
|
rare = Resources.rare_icon;
|
2021-12-10 08:15:04 +00:00
|
|
|
return ImageUtil.LayerImage(baseImage, rare, 0, 0, ShinyTransparency);
|
|
|
|
}
|
2018-08-19 23:45:20 +00:00
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
private Image LayerOverImageEgg(Image baseImage, ushort species, bool hasItem)
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
|
|
|
if (ShowEggSpriteAsItem && !hasItem)
|
|
|
|
return LayerOverImageEggAsItem(baseImage, species);
|
|
|
|
return LayerOverImageEggTransparentSpecies(baseImage, species);
|
|
|
|
}
|
2021-08-25 02:52:06 +00:00
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
private Image LayerOverImageEggTransparentSpecies(Image baseImage, ushort species)
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
|
|
|
// Partially transparent species.
|
|
|
|
baseImage = ImageUtil.ChangeOpacity(baseImage, EggUnderLayerTransparency);
|
|
|
|
// Add the egg layer over-top with full opacity.
|
|
|
|
var egg = GetEggSprite(species);
|
|
|
|
return ImageUtil.LayerImage(baseImage, egg, 0, 0);
|
|
|
|
}
|
|
|
|
|
2022-08-27 06:43:36 +00:00
|
|
|
private Image LayerOverImageEggAsItem(Image baseImage, ushort species)
|
2021-12-10 08:15:04 +00:00
|
|
|
{
|
|
|
|
var egg = GetEggSprite(species);
|
|
|
|
return ImageUtil.LayerImage(baseImage, egg, EggItemShiftX, EggItemShiftY); // similar to held item, since they can't have any
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void LoadSettings(ISpriteSettings sprite)
|
|
|
|
{
|
|
|
|
ShowEggSpriteAsItem = sprite.ShowEggSpriteAsHeldItem;
|
|
|
|
ShowEncounterBall = sprite.ShowEncounterBall;
|
|
|
|
|
|
|
|
ShowEncounterColor = sprite.ShowEncounterColor;
|
|
|
|
ShowEncounterColorPKM = sprite.ShowEncounterColorPKM;
|
|
|
|
ShowEncounterThicknessStripe = sprite.ShowEncounterThicknessStripe;
|
|
|
|
ShowEncounterOpacityBackground = sprite.ShowEncounterOpacityBackground;
|
|
|
|
ShowEncounterOpacityStripe = sprite.ShowEncounterOpacityStripe;
|
|
|
|
ShowExperiencePercent = sprite.ShowExperiencePercent;
|
2022-11-25 01:42:17 +00:00
|
|
|
|
|
|
|
ShowTeraType = sprite.ShowTeraType;
|
|
|
|
ShowTeraThicknessStripe = sprite.ShowTeraThicknessStripe;
|
|
|
|
ShowTeraOpacityBackground = sprite.ShowTeraOpacityBackground;
|
|
|
|
ShowTeraOpacityStripe = sprite.ShowTeraOpacityStripe;
|
2021-08-25 02:52:06 +00:00
|
|
|
}
|
2018-07-15 00:53:37 +00:00
|
|
|
}
|