Documents all public classes, adds PokemonParty
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2023-07-15 14:24:29 +02:00
parent a8a6b775b3
commit d03569aa3f
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
50 changed files with 1364 additions and 97 deletions

View File

@ -1,5 +1,8 @@
namespace PkmnLibSharp.DynamicData
{
/// <summary>
/// A source of damage.
/// </summary>
public enum DamageSource : byte
{
/// <summary>

View File

@ -4,10 +4,17 @@ using Interface = PkmnLibSharp.FFI.DynamicData.LearnedMove;
namespace PkmnLibSharp.DynamicData
{
/// <summary>
/// A learned move is the data attached to a Pokemon for a move it has learned. It has information
/// such as the remaining amount of users, how it has been learned, etc.
/// </summary>
public class LearnedMove : HandleType
{
private LearnedMove(FFIHandle handle) : base(handle){}
/// <summary>
/// Creates a new learned move.
/// </summary>
public static LearnedMove Create(MoveData moveData, MoveLearnMethod learnMethod)
{
var handle = Interface.learned_move_new(moveData.Handle, learnMethod);
@ -53,6 +60,9 @@ namespace PkmnLibSharp.DynamicData
public void RestoreUses(byte amount) => Interface.learned_move_restore_uses(Handle, amount);
}
/// <summary>
/// The different ways a move can be learned.
/// </summary>
public enum MoveLearnMethod : byte
{
/// We do not know the learn method.

View File

@ -4,19 +4,30 @@ using Interface = PkmnLibSharp.FFI.DynamicData.Libraries.BattleStatCalculator;
namespace PkmnLibSharp.DynamicData.Libraries
{
/// <summary>
/// A battle stat calculator is used to calculate stats for a Pokemon.
/// </summary>
public abstract class BattleStatCalculator : HandleType
{
/// <inheritdoc cref="BattleStatCalculator"/>
protected BattleStatCalculator(FFIHandle handle) : base(handle){}
}
/// <summary>
/// A basic implementation of the Gen 7 stat calculator.
/// </summary>
public class Gen7BattleStatCalculator : BattleStatCalculator
{
/// <inheritdoc cref="Gen7BattleStatCalculator"/>
protected Gen7BattleStatCalculator(FFIHandle handle) : base(handle){}
/// <summary>
/// Creates a new Gen 7 battle stat calculator
/// </summary>
public static Gen7BattleStatCalculator Create()
{
var handle = Interface.gen_7_battle_stat_calculator_new();
return Resolver.Instance.ResolveGen7BattleStatCalculator(handle.Resolve());
return (Gen7BattleStatCalculator)Resolver.Instance.ResolveBattleStatCalculator(handle.Resolve());
}
}
}

View File

@ -4,23 +4,38 @@ using Interface = PkmnLibSharp.FFI.DynamicData.Libraries.DamageLibrary;
namespace PkmnLibSharp.DynamicData.Libraries
{
/// <summary>
/// A damage library holds the functions related to the calculation of damage.
/// </summary>
public abstract class DamageLibrary : HandleType
{
/// <inheritdoc cref="DamageLibrary"/>
protected DamageLibrary(FFIHandle ptr) : base(ptr)
{
}
}
/// <summary>
/// The implementation of a Damage Library for generation 7.
/// </summary>
public class Gen7DamageLibrary : DamageLibrary
{
/// <inheritdoc cref="Gen7DamageLibrary"/>
public Gen7DamageLibrary(FFIHandle ptr) : base(ptr)
{
}
/// <summary>
/// Creates a new generation 7 damage library
/// </summary>
/// <param name="hasRandomness">
/// Whether or not a random damage modifier (0.85x - 1.00x) is applied to the calculated damage
/// </param>
/// <returns></returns>
public static Gen7DamageLibrary Create(bool hasRandomness)
{
var handle = Interface.gen_7_damage_library_new((byte)(hasRandomness ? 1 : 0));
return Resolver.Instance.ResolveGen7DamageLibrary(handle.Resolve());
return (Gen7DamageLibrary)Resolver.Instance.ResolveDamageLibrary(handle.Resolve());
}
}
}

View File

@ -4,12 +4,20 @@ using Interface = PkmnLibSharp.FFI.DynamicData.Libraries.DynamicLibrary;
namespace PkmnLibSharp.DynamicData.Libraries
{
/// <summary>
/// The dynamic library stores a static data library, as well as holding different libraries and
/// calculators that might be customized between different generations and implementations.
/// </summary>
public class DynamicLibrary : HandleType
{
/// <inheritdoc cref="DynamicLibrary"/>
protected DynamicLibrary(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new DynamicLibrary with given libraries.
/// </summary>
public static DynamicLibrary Create(StaticData.Libraries.StaticData staticData,
BattleStatCalculator statCalculator, DamageLibrary damageLibrary, MiscLibrary miscLibrary,
ScriptResolver scriptResolver)
@ -21,6 +29,9 @@ namespace PkmnLibSharp.DynamicData.Libraries
return lib;
}
/// <summary>
/// The static data is the immutable storage data for this library.
/// </summary>
public StaticData.Libraries.StaticData StaticData { get; private set; } = null!;
}
}

View File

@ -4,23 +4,34 @@ using Interface = PkmnLibSharp.FFI.DynamicData.Libraries.MiscLibrary;
namespace PkmnLibSharp.DynamicData.Libraries
{
/// <summary>
/// The misc library holds several misc functions required for the battle to run.
/// </summary>
public abstract class MiscLibrary : HandleType
{
/// <inheritdoc cref="MiscLibrary"/>
protected MiscLibrary(FFIHandle handle) : base(handle)
{
}
}
/// <summary>
/// A gen 7 implementation for the MiscLibrary.
/// </summary>
public class Gen7MiscLibrary : MiscLibrary
{
/// <inheritdoc cref="Gen7MiscLibrary"/>
protected Gen7MiscLibrary(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new MiscLibrary.
/// </summary>
public static Gen7MiscLibrary Create()
{
var handle = Interface.gen_7_misc_library_new();
return Resolver.Instance.ResolveGen7MiscLibrary(handle.Resolve());
return (Gen7MiscLibrary)Resolver.Instance.ResolveMiscLibrary(handle.Resolve());
}
}
}

View File

@ -5,8 +5,14 @@ using Interface = PkmnLibSharp.FFI.DynamicData.Libraries.ScriptResolver;
namespace PkmnLibSharp.DynamicData.Libraries
{
/// <summary>
/// A script resolver deals with the resolving of scripts. These scripts are non-hardcoded
/// implementations of different effects in Pokemon. This allows for things such as generational
/// differences, and custom implementations.
/// </summary>
public abstract class ScriptResolver : HandleType
{
/// <inheritdoc cref="ScriptResolver"/>
protected ScriptResolver(FFIHandle handle) : base(handle){}
}
@ -15,8 +21,12 @@ namespace PkmnLibSharp.DynamicData.Libraries
/// </summary>
public class EmptyScriptResolver : ScriptResolver
{
/// <inheritdoc cref="EmptyScriptResolver"/>
protected EmptyScriptResolver(FFIHandle handle) : base(handle){}
/// <summary>
/// Creates a new empty script resolver.
/// </summary>
public static EmptyScriptResolver Create()
{
var handle = Interface.empty_script_resolver_new();

View File

@ -4,16 +4,23 @@ using Interface = PkmnLibSharp.FFI.DynamicData.Libraries.ScriptResolver;
namespace PkmnLibSharp.DynamicData.Libraries
{
/// <summary>
/// A WebAssembly script resolver implements the dynamic scripts functionality with WebAssembly.
/// </summary>
public class WasmScriptResolver : ScriptResolver
{
/// <inheritdoc cref="WasmScriptResolver"/>
protected WasmScriptResolver(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new WebAssemblyScriptResolver.
/// </summary>
public static WasmScriptResolver Create()
{
var handle = Interface.webassembly_script_resolver_new();
return Resolver.Instance.ResolveWasmScriptResolver(handle.Resolve());
return (WasmScriptResolver)Resolver.Instance.ResolveScriptResolver(handle.Resolve());
}
/// <summary>

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using PkmnLibSharp.DynamicData.Libraries;
using PkmnLibSharp.StaticData;
using PkmnLibSharp.Utils;
@ -8,20 +9,40 @@ using Species = PkmnLibSharp.StaticData.Species;
namespace PkmnLibSharp.DynamicData
{
/// <summary>
/// The data of a Pokemon.
/// </summary>
public class Pokemon : HandleType
{
protected Pokemon(FFIHandle handle) : base(handle){}
public static Pokemon Create(DynamicLibrary dynamicLibrary, Species species, Form form, bool hiddenAbility, byte abilityIndex,
LevelInt level, uint uid, Gender gender, byte coloring, string nature)
/// <inheritdoc cref="Pokemon"/>
protected Pokemon(FFIHandle handle) : base(handle)
{
var handle = Interface.pokemon_new(dynamicLibrary.Handle, species.Handle, form.Handle, hiddenAbility.ForeignBool(),
abilityIndex, level, uid, gender, coloring, nature.ToPtr())
}
/// <summary>
/// Instantiates a new Pokemon.
/// </summary>
/// <param name="dynamicLibrary">The library the Pokemon should fetch its data from</param>
/// <param name="species">The species of the Pokemon</param>
/// <param name="form">The form the Pokemon should be</param>
/// <param name="hiddenAbility">Whether or not the ability the Pokemon has is hidden</param>
/// <param name="abilityIndex">The index of the ability</param>
/// <param name="level">The level of the Pokemon</param>
/// <param name="uid">The unique identifier of the Pokemon</param>
/// <param name="gender">The gender of the Pokemon</param>
/// <param name="coloring">The coloring of the Pokemon. This should be 0 for regular, 1 for shiny, and any other value for custom implementations</param>
/// <param name="nature">The nature of the Pokemon</param>
public static Pokemon Create(DynamicLibrary dynamicLibrary, Species species, Form form, bool hiddenAbility,
byte abilityIndex, LevelInt level, uint uid, Gender gender, byte coloring, string nature)
{
var handle = Interface.pokemon_new(dynamicLibrary.Handle, species.Handle, form.Handle,
hiddenAbility.ForeignBool(), abilityIndex, level, uid, gender, coloring, nature.ToPtr())
.Result();
return Resolver.Instance.ResolvePokemon(handle.Resolve());
}
private DynamicLibrary? _library;
/// <summary>
/// The library data the Pokemon uses.
/// </summary>
@ -49,13 +70,43 @@ namespace PkmnLibSharp.DynamicData
/// </summary>
public Form DisplayForm => Resolver.Instance.ResolveForm(Interface.pokemon_display_form(Handle).Resolve());
/// <summary>
/// The level of the Pokemon.
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Level">See also</a>
/// </summary>
public LevelInt Level => Interface.pokemon_level(Handle);
/// <summary>
/// The experience of the Pokemon.
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Experience">See also</a>
/// </summary>
public uint Experience => Interface.pokemon_experience(Handle);
public uint UniqueIdentifier => Interface.pokemon_unique_identifier(Handle);
/// <summary>
/// The personality value of the Pokemon.
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Personality_value">See also</a>
/// </summary>
public uint PersonalityValue => Interface.pokemon_personality_value(Handle);
/// <summary>
/// The gender of the Pokemon.
/// </summary>
public Gender Gender => Interface.pokemon_gender(Handle);
/// <summary>
/// The coloring of the Pokemon. If this is 1, the Pokemon is shiny, otherwise it is not. This can
/// also be used for other custom coloring schemes.
/// </summary>
public byte Coloring => Interface.pokemon_coloring(Handle);
/// <summary>
/// Whether or not the Pokemon is shiny.
/// </summary>
public bool IsShiny => Coloring == 1;
/// <summary>
/// Gets the held item of a Pokemon. If the Pokemon does not have a held item, this will return null.
/// </summary>
public Item? HeldItem
{
get
@ -65,91 +116,241 @@ namespace PkmnLibSharp.DynamicData
}
}
/// <summary>
/// Checks whether the Pokemon is holding an item with a specific name.
/// </summary>
/// <param name="name">The name of the item to check for</param>
public bool HasHeldItem(string name) => Interface.pokemon_has_held_item(Handle, name.ToPtr()) == 1;
public void SetHeldItem(Item? item)
/// <summary>
/// Changes the held item of the Pokemon. Returns the previously held item.
/// </summary>
/// <param name="item">The new item to use. If this is given null, it will call <see cref="RemoveHeldItem"/></param>
public Item? SetHeldItem(Item? item)
{
if (item == null)
RemoveHeldItem();
else
Interface.pokemon_set_held_item(Handle, item.Handle);
{
return RemoveHeldItem();
}
var handle = Interface.pokemon_set_held_item(Handle, item.Handle);
return handle.IsNull ? null : Resolver.Instance.ResolveItem(handle.Resolve());
}
public void RemoveHeldItem() => Interface.pokemon_remove_held_item(Handle);
/// <summary>
/// Removes the held item from the Pokemon. Returns the previously held item.
/// </summary>
/// <returns>The previous held item. If no item was previously held, this returns null</returns>
public Item? RemoveHeldItem()
{
var handle = Interface.pokemon_remove_held_item(Handle);
return handle.IsNull ? null : Resolver.Instance.ResolveItem(handle.Resolve());
}
/// <summary>
/// Makes the Pokemon uses its held item.
/// </summary>
/// <returns>Whether or not the held item was successfully consumed</returns>
public bool ConsumeHeldItem() => Interface.pokemon_consume_held_item(Handle).Result() == 1;
/// <summary>
/// The current health of the Pokemon.
/// </summary>
public uint CurrentHealth => Interface.pokemon_current_health(Handle);
/// <summary>
/// The max health of the Pokemon.
/// </summary>
public uint MaxHealth => Interface.pokemon_max_health(Handle);
/// <summary>
/// The current weight of the Pokemon.
/// </summary>
public float Weight => Interface.pokemon_weight(Handle);
/// <summary>
/// The current height of the Pokemon.
/// </summary>
public float Height => Interface.pokemon_height(Handle);
/// <summary>
/// An optional nickname of the Pokemon. If the Pokemon does not have a nickname, this will return null.
/// </summary>
public string? Nickname => Interface.pokemon_nickname(Handle).Result().PtrString();
/// <summary>
/// Whether the actual ability the Pokemon has (so not its potentially overriden ability) is a hidden ability.
/// </summary>
public bool HasHiddenAbility => Interface.pokemon_real_ability_is_hidden(Handle) == 1;
/// <summary>
/// The index of the actual ability the Pokemon has (so not its potentially overriden ability).
/// </summary>
public byte AbilityIndex => Interface.pokemon_real_ability_index(Handle);
private ExternValueArray<TypeIdentifier>? _types;
public ExternValueArray<TypeIdentifier> Types =>
/// <summary>
/// An array of the types of the Pokemon.
/// </summary>
public IReadOnlyList<TypeIdentifier> Types =>
_types ??= new ExternValueArray<TypeIdentifier>(() => Interface.pokemon_types_length(Handle),
arg => Interface.pokemon_types_get(Handle, arg).Result());
public LearnedMove? LearnedMove(ulong index)
{
var ptr = Interface.pokemon_learned_move_get(Handle, index);
return ptr.IsNull ? null : Resolver.Instance.ResolveLearnedMove(ptr.Resolve());
}
private ExternValueArray<LearnedMove?>? _moves;
/// <summary>
/// The moves the Pokemon has learned and can use.
/// </summary>
public IReadOnlyList<LearnedMove?> LearnedMoves =>
_moves ??= new ExternValueArray<LearnedMove?>(() => 4, arg =>
{
var ptr = Interface.pokemon_learned_move_get(Handle, arg);
return ptr.IsNull ? null : Resolver.Instance.ResolveLearnedMove(ptr.Resolve());
});
private StatisticSet<uint>? _flatStats;
/// <summary>
/// The stats of the Pokemon when disregarding any stat boosts.
/// </summary>
public StatisticSet<uint> FlatStats =>
_flatStats ??=
Resolver.Instance.ResolveStatisticSet<uint>(Interface.pokemon_flat_stats(Handle).Resolve());
_flatStats ??= Resolver.Instance.ResolveStatisticSet<uint>(Interface.pokemon_flat_stats(Handle).Resolve());
private StatisticSet<uint>? _boostedStats;
/// <summary>
/// The stats of the Pokemon including the stat boosts.
/// </summary>
public StatisticSet<uint> BoostedStats =>
_boostedStats ??=
Resolver.Instance.ResolveStatisticSet<uint>(Interface.pokemon_boosted_stats(Handle).Resolve());
/// <summary>
/// Get the stat boosts for a specific stat. Between -6 and 6.
/// </summary>
public sbyte GetStatBoost(Statistic statistic) => Interface.pokemon_get_stat_boost(Handle, statistic);
public byte GetIndividualValue(Statistic statistic) => Interface.pokemon_get_individual_value(Handle, statistic);
/// <summary>
/// Gets an individual value of the Pokemon.
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Individual_values">See also</a>
/// </summary>
public byte GetIndividualValue(Statistic statistic) =>
Interface.pokemon_get_individual_value(Handle, statistic);
/// <summary>
/// Gets an effort value of the Pokemon.
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Effort_values">See also</a>
/// </summary>
public byte GetEffortValue(Statistic statistic) => Interface.pokemon_get_effort_value(Handle, statistic);
/// <summary>
/// Modifies an individual value of the Pokemon.
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Individual_values">See also</a>
/// </summary>
public void SetIndividualValue(Statistic statistic, byte value) =>
Interface.pokemon_set_individual_value(Handle, statistic, value).Result();
/// <summary>
/// Modifies a effort value of the Pokemon.
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Effort_values">See also</a>
/// </summary>
public void SetEffortValue(Statistic statistic, byte value) =>
Interface.pokemon_set_effort_value(Handle, statistic, value).Result();
// TODO: Battle getter
/// <summary>
/// Get the index of the side of the battle the Pokemon is in. If the Pokemon
/// is not on the battlefield, this always returns 0.
/// </summary>
public byte BattleSideIndex => Interface.pokemon_get_battle_side_index(Handle);
/// <summary>
/// Get the index of the slot on the side of the battle the Pokemon is in. If the Pokemon
/// is not on the battlefield, this always returns 0.
/// </summary>
public byte BattleIndex => Interface.pokemon_get_battle_index(Handle);
/// <summary>
/// Returns whether something overrides the ability.
/// </summary>
public bool IsAbilityOverriden => Interface.pokemon_is_ability_overriden(Handle) == 1;
/// <summary>
/// Returns the currently active ability.
/// </summary>
public Ability ActiveAbility =>
Resolver.Instance.ResolveAbility(Interface.pokemon_active_ability(Handle).Result().Resolve());
/// <summary>
/// Whether or not the Pokemon is allowed to gain experience.
/// </summary>
public bool AllowedExperienceGain => Interface.pokemon_allowed_experience_gain(Handle) == 1;
/// <summary>
/// The nature of the Pokemon.
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Nature">See also</a>
/// </summary>
public Nature Nature => Resolver.Instance.ResolveNature(Interface.pokemon_nature(Handle).Resolve());
/// <summary>
/// Calculates the flat stats on the Pokemon. This should be called when for example the base
/// stats, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted
/// stats, as those depend on the flat stats.
/// </summary>
public void RecalculateFlatStats() => Interface.pokemon_recalculate_flat_stats(Handle).Result();
/// <summary>
/// Calculates the boosted stats on the Pokemon. This should be called when a stat boost changes.
/// </summary>
public void RecalculateBoostedStats() => Interface.pokemon_recalculate_boosted_stats(Handle).Result();
/// <summary>
/// Change the species of the Pokemon.
/// </summary>
public void ChangeSpecies(Species species, Form form) =>
Interface.pokemon_change_species(Handle, species.Handle, form.Handle).Result();
/// <summary>
/// Change the form of the Pokemon.
/// </summary>
public void ChangeForm(Form form) => Interface.pokemon_change_form(Handle, form.Handle).Result();
/// <summary>
/// Whether or not the Pokemon is useable in a battle.
/// </summary>
public bool IsUsable => Interface.pokemon_is_usable(Handle) == 1;
/// <summary>
/// Returns whether the Pokemon is fainted.
/// </summary>
public bool IsFainted => Interface.pokemon_is_fainted(Handle) == 1;
/// <summary>
/// Whether or not the Pokemon is on the battlefield.
/// </summary>
public bool IsOnBattleField => Interface.pokemon_is_on_battlefield(Handle) == 1;
public void Damage(uint amount, DamageSource source) => Interface.pokemon_damage(Handle, amount, source).Result();
/// <summary>
/// Damages the Pokemon by a certain amount of damage, from a damage source.
/// </summary>
public void Damage(uint amount, DamageSource source) =>
Interface.pokemon_damage(Handle, amount, source).Result();
/// <summary>
/// Heals the Pokemon by a specific amount. Unless allow_revive is set to true, this will not
/// heal if the Pokemon has 0 health. If the amount healed is 0, this will return false.
/// </summary>
public bool Heal(uint amount, bool allowRevive) =>
Interface.pokemon_heal(Handle, amount, allowRevive.ForeignBool()) == 1;
/// <summary>
/// Learn a move.
/// </summary>
public void LearnMove(string moveName, MoveLearnMethod learnMethod) =>
Interface.pokemon_learn_move(Handle, moveName.ToPtr(), learnMethod).Result();
/// <summary>
/// Removes the current non-volatile status from the Pokemon.
/// </summary>
public void ClearStatus() => Interface.pokemon_clear_status(Handle);
}
}

View File

@ -6,6 +6,9 @@ using PkmnLibSharp.Utils;
namespace PkmnLibSharp.DynamicData
{
/// <summary>
/// A builder class for creating <see cref="Pokemon"/> instances.
/// </summary>
public class PokemonBuilder
{
private static readonly Random DefaultRandom = new();
@ -29,6 +32,10 @@ namespace PkmnLibSharp.DynamicData
private byte _abilityIndex;
private byte _coloring;
/// <inheritdoc cref="PokemonBuilder"/>
/// <param name="library">The library the Pokemon should use for data</param>
/// <param name="species">The name of the species of Pokemon</param>
/// <param name="level">The level the Pokemon should be</param>
public PokemonBuilder(DynamicLibrary library, string species, LevelInt level)
{
_library = library;
@ -38,54 +45,82 @@ namespace PkmnLibSharp.DynamicData
_random = new Random(_randomSeed);
}
/// <summary>
/// Sets the name of the form of the Pokemon.
/// </summary>
public PokemonBuilder WithForm(string form)
{
_form = form;
return this;
}
/// <summary>
/// Force the Pokemon to be shiny or not shiny, instead of letting this be determined randomly.
/// </summary>
public PokemonBuilder ForceShiny(bool value)
{
_forceShininess = value;
return this;
}
/// <summary>
/// Sets the gender of the Pokemon to a specific value, instead of letting this be determined randomly.
/// </summary>
public PokemonBuilder WithGender(Gender gender)
{
_gender = gender;
return this;
}
/// <summary>
/// Sets the identifier of the Pokemon to a specific value, instead of letting this be determined randomly.
/// </summary>
public PokemonBuilder WithIdentifier(uint identifier)
{
_identifier = identifier;
return this;
}
/// <summary>
/// Sets the nature of the Pokemon to a specific value, instead of letting this be determined randomly.
/// </summary>
public PokemonBuilder WithNature(string nature)
{
_natureName = nature;
return this;
}
/// <summary>
/// Sets the ability of the Pokemon to a specific value, instead of letting this be determined randomly.
/// </summary>
public PokemonBuilder WithAbility(string ability)
{
_ability = ability;
return this;
}
/// <summary>
/// Gives the Pokemon a specific amount of experience. If not set, the experience will be calculated based on
/// the Pokemon's level and growth rate.
/// </summary>
public PokemonBuilder WithExperience(uint experience)
{
_experience = experience;
return this;
}
/// <summary>
/// Teaches the Pokemon a move.
/// </summary>
public PokemonBuilder LearnMove(string moveName, MoveLearnMethod learnMethod)
{
_moves.Add((moveName, learnMethod));
return this;
}
/// <summary>
/// This method is called after all properties have been set, and will create the Pokemon.
/// </summary>
protected virtual Pokemon Finalize(Species species, Form form, Item? heldItem)
{
var pokemon = Pokemon.Create(_library, species, form, _hiddenAbility, _abilityIndex, _level,
@ -100,6 +135,9 @@ namespace PkmnLibSharp.DynamicData
return pokemon;
}
/// <summary>
/// Populates any properties that have not been set with random values.
/// </summary>
protected virtual void PopulateUninitialized(Species species, Form form, Random random)
{
_experience ??= _library.StaticData.GrowthRateLibrary.CalculateExperience(species.GrowthRate, _level);
@ -146,6 +184,9 @@ namespace PkmnLibSharp.DynamicData
return (true, (byte)i);
}
/// <summary>
/// Builds the Pokemon, based on the properties that have been set.
/// </summary>
public Pokemon Build()
{
if (!_library.StaticData.SpeciesLibrary.TryGetValue(_species, out var species))

View File

@ -0,0 +1,102 @@
using System.Collections;
using System.Collections.Generic;
using PkmnLibSharp.Utils;
using Interface = PkmnLibSharp.FFI.DynamicData.PokemonParty;
namespace PkmnLibSharp.DynamicData
{
/// <summary>
/// A list of Pokemon belonging to a trainer.
/// </summary>
public class PokemonParty : HandleType, IReadOnlyList<Pokemon?>
{
/// <summary>
/// A cache of the Pokemon in the party.
/// </summary>
private Pokemon?[] _cachedPokemon = null!;
internal PokemonParty(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a party with a set size.
/// </summary>
/// <param name="capacity">The length of the party</param>
public static PokemonParty Create(ulong capacity)
{
var handle = Interface.pokemon_party_new(capacity).Resolve();
var party = Resolver.Instance.ResolvePokemonParty(handle);
party._cachedPokemon = new Pokemon?[capacity];
return party;
}
/// <inheritdoc />
public IEnumerator<Pokemon?> GetEnumerator()
{
return ((IEnumerable<Pokemon?>)_cachedPokemon).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <inheritdoc />
public int Count => _cachedPokemon.Length;
/// <inheritdoc />
public Pokemon? this[int index] => _cachedPokemon[index];
/// <summary>
/// Swaps two Pokemon in the party around.
/// </summary>
public void Switch(int a, int b)
{
Interface.pokemon_party_switch(Handle, (ulong)a, (ulong)b).Result();
(_cachedPokemon[a], _cachedPokemon[b]) = (_cachedPokemon[b], _cachedPokemon[a]);
}
/// <summary>
/// Sets the Pokemon at an index to a Pokemon, returning the old Pokemon.
/// </summary>
/// <param name="index">The index to switch at</param>
/// <param name="pokemon">The pokemon to switch into the slot</param>
/// <returns>The previous Pokemon in the slot</returns>
public Pokemon? SwapInto(int index, Pokemon? pokemon)
{
var handle = Interface.pokemon_party_swap_into(Handle, (ulong)index, pokemon?.Handle ?? FFIHandle.Zero)
.Result();
var p = handle.IsNull ? null : Resolver.Instance.ResolvePokemon(handle.Resolve());
_cachedPokemon[index] = pokemon;
return p;
}
/// <summary>
/// Whether or not the party still has Pokemon that can be used in battle.
/// </summary>
public bool HasUsablePokemon() => Interface.pokemon_party_has_usable_pokemon(Handle) == 1;
/// <summary>
/// Checks if the party contains a given pokemon.
/// </summary>
/// <param name="pokemon">The pokemon to check for</param>
public bool HasPokemon(Pokemon pokemon) => Interface.pokemon_party_has_pokemon(Handle, pokemon.Handle) == 1;
/// <summary>
/// Makes sure there are no empty spots in the party anymore, leaving the length the same.
/// </summary>
public void PackParty()
{
Interface.pokemon_party_pack_party(Handle);
// Ensure the old cache is kept alive while we update the new one.
var oldCache = _cachedPokemon;
// Update the cache.
_cachedPokemon = new Pokemon?[oldCache.Length];
for (var i = 0; i < oldCache.Length; i++)
{
var handle = Interface.pokemon_party_at(Handle, (ulong)i).Resolve();
_cachedPokemon[i] = Resolver.Instance.ResolvePokemon(handle);
}
}
}
}

View File

@ -54,7 +54,7 @@ namespace PkmnLibSharp.FFI.DynamicData
internal static extern uint pokemon_experience(FFIHandleValue pokemon);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern uint pokemon_unique_identifier(FFIHandleValue pokemon);
internal static extern uint pokemon_personality_value(FFIHandleValue pokemon);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern Gender pokemon_gender(FFIHandleValue pokemon);
@ -78,13 +78,13 @@ namespace PkmnLibSharp.FFI.DynamicData
/// Changes the held item of the Pokemon. Returns the previously held item.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IdentifiablePointer pokemon_set_held_item(FFIHandleValue pokemon, FFIHandle item);
internal static extern FFIHandleValue pokemon_set_held_item(FFIHandleValue pokemon, FFIHandle item);
/// <summary>
/// Removes the held item from the Pokemon. Returns the previously held item.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IdentifiablePointer pokemon_remove_held_item(FFIHandleValue pokemon);
internal static extern FFIHandleValue pokemon_remove_held_item(FFIHandleValue pokemon);
/// <summary>
/// Makes the Pokemon uses its held item.
@ -191,7 +191,7 @@ namespace PkmnLibSharp.FFI.DynamicData
/// returns null.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IdentifiablePointer pokemon_get_battle(FFIHandleValue pokemon);
internal static extern FFIHandleValue pokemon_get_battle(FFIHandleValue pokemon);
/// <summary>
/// Get the index of the side of the battle the Pokemon is in. If the Pokemon

View File

@ -0,0 +1,59 @@
using System;
using System.Runtime.InteropServices;
using PkmnLibSharp.StaticData;
using PkmnLibSharp.Utils;
namespace PkmnLibSharp.FFI.DynamicData
{
internal static class PokemonParty
{
/// <summary>
/// Instantiates a party with a set size.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern FFIHandleValue pokemon_party_new(ulong capacity);
/// <summary>
/// Gets a Pokemon at an index in the party.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern FFIHandleValue pokemon_party_at(FFIHandleValue party, ulong index);
/// <summary>
/// Swaps two Pokemon in the party around.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern NativeResult pokemon_party_switch(FFIHandle party, ulong a, ulong b);
/// <summary>
/// Sets the Pokemon at an index to a Pokemon, returning the old Pokemon.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern NativeResult<FFIHandleValue> pokemon_party_swap_into(FFIHandleValue party, ulong index,
FFIHandleValue pokemon);
/// <summary>
/// Whether or not the party still has Pokemon that can be used in battle.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern byte pokemon_party_has_usable_pokemon(FFIHandleValue party);
/// <summary>
/// Get the length of the underlying list of Pokemon.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern ulong pokemon_party_length(FFIHandleValue party);
/// <summary>
/// Makes sure there are no empty spots in the party anymore, leaving the length the same.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern NativeResult pokemon_party_pack_party(FFIHandleValue party);
/// <summary>
/// Checks if the party contains a given pokemon.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern byte pokemon_party_has_pokemon(FFIHandleValue party, FFIHandleValue pokemon);
}
}

View File

@ -1,14 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace PkmnLibSharp.FFI
{
[StructLayout(LayoutKind.Sequential)]
public struct IdentifiablePointer
{
public readonly IntPtr Ptr;
public readonly nuint Identifier;
public bool IsNull => Ptr == IntPtr.Zero;
}
}

View File

@ -43,6 +43,9 @@ namespace PkmnLibSharp.FFI
}
}
/// <summary>
/// An error thrown by the library.
/// </summary>
public class PkmnLibException : Exception
{
internal PkmnLibException(string message) : base(message){}

View File

@ -10,7 +10,7 @@ namespace PkmnLibSharp.FFI.StaticData.Libraries
internal static extern FFIHandleValue move_library_new(ulong capacity);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IdentifiablePointer move_library_get(FFIHandleValue ptr, IntPtr key);
internal static extern FFIHandleValue move_library_get(FFIHandleValue ptr, IntPtr key);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IntPtr move_library_get_key_by_index(FFIHandleValue ptr, ulong index);

View File

@ -14,11 +14,13 @@
<NoWarn>IDISP012</NoWarn>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>TRACE;WASM;</DefineConstants>
<DocumentationFile>bin\Debug\PkmnLibRSharp.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>TRACE;WASM;</DefineConstants>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>

View File

@ -5,20 +5,38 @@ using Interface = PkmnLibSharp.FFI.StaticData.Ability;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// An ability is a passive effect in battle that is attached to a Pokemon.
/// </summary>
public class Ability : HandleType
{
private string? _name;
/// <summary>
/// The name of the ability.
/// </summary>
public string Name => _name ??= Interface.ability_name(Handle).Result().PtrString()!;
private string? _effect;
/// <summary>
/// The name of the script effect of the ability.
/// </summary>
public string Effect => _effect ??= Interface.ability_effect(Handle).Result().PtrString()!;
/// <summary>
/// The parameters for the script effect of the ability.
/// </summary>
public IReadOnlyList<EffectParameter> Parameters { get; private set; } = null!;
/// <inheritdoc cref="Ability"/>
protected Ability(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new ability.
/// </summary>
public static Ability Create(string name, string effect, IReadOnlyList<EffectParameter> parameters)
{
var parameterArray = parameters.Select(x => (FFIHandleValue)x.Handle).ToArray();

View File

@ -4,13 +4,27 @@ using Interface = PkmnLibSharp.FFI.StaticData.EffectParameter;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// A parameter for an effect. This is basically a simple way to dynamically store multiple different
/// primitives on data.
/// </summary>
public class EffectParameter : HandleType
{
private ParameterType? _type;
/// <summary>
/// The underlying type of the parameter.
/// </summary>
public ParameterType? Type => _type ??= (ParameterType)Interface.effect_parameter_get_type(Handle);
private object? _data;
/// <summary>
/// The data stored in the parameter.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the parameter type is not recognized. This should never happen.
/// </exception>
public object Data
{
get
@ -27,35 +41,75 @@ namespace PkmnLibSharp.StaticData
}
}
/// <inheritdoc cref="EffectParameter"/>
protected EffectParameter(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Creates a new parameter from a boolean.
/// </summary>
public static EffectParameter FromBool(bool b) =>
Resolver.Instance.ResolveEffectParameter(Interface.effect_parameter_new_bool(b.ForeignBool()).Resolve());
/// <summary>
/// Creates a new parameter from a 64 bit integer.
/// </summary>
public static EffectParameter FromLong(long l) =>
Resolver.Instance.ResolveEffectParameter(Interface.effect_parameter_new_int(l).Resolve());
/// <summary>
/// Creates a new parameter from a float.
/// </summary>
public static EffectParameter FromFloat(float f) =>
Resolver.Instance.ResolveEffectParameter(Interface.effect_parameter_new_float(f).Resolve());
/// <summary>
/// Creates a new parameter from a string.
/// </summary>
public static EffectParameter FromString(string s) =>
Resolver.Instance.ResolveEffectParameter(Interface.effect_parameter_new_string(s.ToPtr()).Result().Resolve());
Resolver.Instance.ResolveEffectParameter(
Interface.effect_parameter_new_string(s.ToPtr()).Result().Resolve());
/// <inheritdoc cref="FromBool"/>
public static implicit operator EffectParameter(bool b) => FromBool(b);
/// <inheritdoc cref="FromLong"/>
public static implicit operator EffectParameter(long l) => FromLong(l);
/// <inheritdoc cref="FromFloat"/>
public static implicit operator EffectParameter(float f) => FromFloat(f);
/// <inheritdoc cref="FromString"/>
public static implicit operator EffectParameter(string s) => FromString(s);
/// <summary>
/// The different types of parameters.
/// </summary>
public enum ParameterType : byte
{
/// <summary>
/// A boolean parameter.
/// </summary>
Bool = 0,
/// <summary>
/// A 64 bit integer parameter.
/// </summary>
Int = 1,
/// <summary>
/// A 32 bit floating point parameter.
/// </summary>
Float = 2,
/// <summary>
/// A string parameter.
/// </summary>
String = 3,
}
/// <inheritdoc />
public override string ToString()
{
var data = Data;

View File

@ -5,12 +5,20 @@ using Interface = PkmnLibSharp.FFI.StaticData.Form;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// A form is a variant of a specific species. A species always has at least one form, but can have
/// many more.
/// </summary>
public class Form : HandleType
{
/// <inheritdoc cref="Form"/>
protected Form(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new form.
/// </summary>
public static Form Create(string name, float height, float weight, uint baseExperience, TypeIdentifier[] types,
StaticStatisticSet<ushort> baseStats, IReadOnlyCollection<string> abilities,
IReadOnlyCollection<string> hiddenAbilities, LearnableMoves learnableMoves,
@ -32,34 +40,69 @@ namespace PkmnLibSharp.StaticData
private string? _name;
/// <summary>
/// The name of the form.
/// </summary>
public string Name => _name ??= Interface.form_name(Handle).Result().PtrString()!;
private float? _height;
/// <summary>
/// The height of the form in meters.
/// </summary>
public float Height => _height ??= Interface.form_height(Handle);
private float? _weight;
/// <summary>
/// The weight of the form in kilograms.
/// </summary>
public float Weight => _weight ??= Interface.form_weight(Handle);
private uint? _baseExperience;
/// <summary>
/// The base amount of experience that is gained when beating a Pokemon with this form.
/// </summary>
public uint BaseExperience => _baseExperience ??= Interface.form_base_experience(Handle);
private IReadOnlyList<TypeIdentifier>? _types;
/// <summary>
/// The normal types a Pokemon with this form has.
/// </summary>
public IReadOnlyList<TypeIdentifier> Types =>
_types ??= new CachedExternValueArray<TypeIdentifier>(Interface.form_types_length(Handle),
arg => Interface.form_types_get(Handle, arg));
private StaticStatisticSet<ushort>? _baseStats;
/// <summary>
/// The inherent values of a form of species that are used for the stats of a Pokemon.
/// </summary>
public StaticStatisticSet<ushort> BaseStats =>
_baseStats ??= Resolver.Instance.ResolveStaticStatisticSet<ushort>(Interface.form_base_stats(Handle).Resolve());
private IReadOnlyList<string>? _abilities;
/// <summary>
/// The possible abilities a Pokemon with this form can have.
/// </summary>
public IReadOnlyList<string> Abilities =>
_abilities ??= new CachedExternArray<string>(Interface.form_abilities_length(Handle),
arg => Interface.form_abilities_get(Handle, arg).PtrString()!);
private IReadOnlyList<string>? _hiddenAbilities;
/// <summary>
/// The possible hidden abilities a Pokemon with this form can have.
/// </summary>
public IReadOnlyList<string> HiddenAbilities =>
_hiddenAbilities ??= new CachedExternArray<string>(Interface.form_hidden_abilities_length(Handle),
arg => Interface.form_hidden_abilities_get(Handle, arg).PtrString()!);
private LearnableMoves? _learnableMoves;
/// <summary>
/// The moves a Pokemon with this form can learn.
/// </summary>
public LearnableMoves LearnableMoves =>
_learnableMoves ??= Resolver.Instance.ResolveLearnableMoves(Interface.form_moves(Handle).Resolve());
}

View File

@ -1,5 +1,11 @@
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// Gender is a Pokemon characteristic.
///
/// Required for standard pokemon functions, but somewhat controversial nowadays. Consider adding a feature
/// that allows for a more progressive gender system for those that want it?
/// </summary>
public enum Gender : byte
{
/// <summary>

View File

@ -5,28 +5,45 @@ using Interface = PkmnLibSharp.FFI.StaticData.GrowthRate;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// A growth rate defines how much experience is required per level.
/// </summary>
public abstract class GrowthRate : HandleType
{
/// <inheritdoc cref="GrowthRate"/>
protected internal GrowthRate(FFIHandle ptr) : base(ptr)
{
}
/// <summary>
/// Calculate the level something with this growth rate would have at a certain experience.
/// </summary>
public LevelInt CalculateLevel(uint experience)
{
return Interface.growth_rate_calculate_level(Handle, experience);
}
/// <summary>
/// Calculate the experience something with this growth rate would have at a certain level.
/// </summary>
public uint CalculateExperience(LevelInt level)
{
return Interface.growth_rate_calculate_experience(Handle, level).Result();
}
}
/// <summary>
/// An implementation of the growth rate that uses a lookup table for experience.
/// </summary>
public class LookupGrowthRate : GrowthRate
{
/// <inheritdoc cref="LookupGrowthRate"/>
protected LookupGrowthRate(FFIHandle handle) : base(handle){}
/// <summary>
/// Instantiates a new lookup growth rate. The experience vec should be the amount of experience
/// required per level, with the first element being the experience required for level 1 (generally 0).
/// </summary>
public static LookupGrowthRate Create(uint[] experienceArray)
{
var arrayPtr = experienceArray.ArrayPtr();
@ -52,6 +69,10 @@ namespace PkmnLibSharp.StaticData
return i * i * i;
}
/// <summary>
/// The Fast experience group
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Experience#Fast">See also</a>
/// </summary>
public static LookupGrowthRate Fast(LevelInt maxLevel)
{
var arr = new uint[maxLevel];
@ -64,6 +85,10 @@ namespace PkmnLibSharp.StaticData
return LookupGrowthRate.Create(arr);
}
/// <summary>
/// The Medium Fast experience group
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Experience#Medium_Fast">See also</a>
/// </summary>
public static LookupGrowthRate MediumFast(LevelInt maxLevel)
{
var arr = new uint[maxLevel];
@ -76,6 +101,10 @@ namespace PkmnLibSharp.StaticData
return LookupGrowthRate.Create(arr);
}
/// <summary>
/// The Medium Slow experience group
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Experience#Medium_Slow">See also</a>
/// </summary>
public static LookupGrowthRate MediumSlow(LevelInt maxLevel)
{
var arr = new uint[maxLevel];
@ -88,6 +117,10 @@ namespace PkmnLibSharp.StaticData
return LookupGrowthRate.Create(arr);
}
/// <summary>
/// The Slow experience group
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Experience#Slow">See also</a>
/// </summary>
public static LookupGrowthRate Slow(LevelInt maxLevel)
{
var arr = new uint[maxLevel];
@ -99,6 +132,10 @@ namespace PkmnLibSharp.StaticData
return LookupGrowthRate.Create(arr);
}
/// <summary>
/// The Erratic experience group
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Experience#Erratic">See also</a>
/// </summary>
public static LookupGrowthRate Erratic(LevelInt maxLevel)
{
var arr = new uint[maxLevel];
@ -127,6 +164,10 @@ namespace PkmnLibSharp.StaticData
return LookupGrowthRate.Create(arr);
}
/// <summary>
/// The fluctuating experience group
/// <a href="https://bulbapedia.bulbagarden.net/wiki/Experience#Fluctuating">See also</a>
/// </summary>
public static LookupGrowthRate Fluctuating(LevelInt maxLevel)
{
var arr = new uint[maxLevel];

View File

@ -82,10 +82,17 @@ namespace PkmnLibSharp.StaticData
MiscBattleItem,
}
/// <summary>
/// An item is an object which the player can pick up, keep in their Bag, and use in some manner
/// </summary>
public class Item : HandleType
{
/// <inheritdoc cref="Item"/>
protected Item(FFIHandle handle) : base(handle){}
/// <summary>
/// Instantiates an item.
/// </summary>
public static Item Create(string name, ItemCategory category, BattleItemCategory battleItemCategory, int price,
IEnumerable<string> flags)
{
@ -97,14 +104,29 @@ namespace PkmnLibSharp.StaticData
}
private string? _name;
/// <summary>
/// The name of the item.
/// </summary>
public string Name => _name ??= Interface.item_name(Handle).Result().PtrString()!;
private ItemCategory? _category;
/// <summary>
/// Which bag slot items are stored in.
/// </summary>
public ItemCategory Category => _category ??= Interface.item_category(Handle);
private BattleItemCategory? _battleCategory;
/// <summary>
/// How the item is categorized when in battle.
/// </summary>
public BattleItemCategory BattleCategory => _battleCategory ??= Interface.item_battle_category(Handle);
private int? _price;
/// <summary>
/// The buying value of the item.
/// </summary>
public int Price => _price ??= Interface.item_price(Handle);
/// <summary>
/// Checks whether the item has a specific flag.
/// </summary>
public bool HasFlag(string flag) => Interface.item_has_flag(Handle, flag.ToPtr()) == 1;
}
}

View File

@ -3,23 +3,36 @@ using Interface = PkmnLibSharp.FFI.StaticData.LearnableMoves;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// The storage of the moves a Pokemon can learn.
/// </summary>
public class LearnableMoves : HandleType
{
/// <inheritdoc cref="LearnableMoves"/>
protected LearnableMoves(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new object to store the moves a Pokemon can learn.
/// </summary>
public static LearnableMoves Create()
{
var handle = Interface.learnable_moves_new();
return Resolver.Instance.ResolveLearnableMoves(handle.Resolve());
}
/// <summary>
/// Adds a new level move the Pokemon can learn.
/// </summary>
public void AddLevelMove(LevelInt level, string moveName)
{
Interface.learnable_moves_add_level_move(Handle, level, moveName.ToPtr());
}
/// <summary>
/// Adds a new level move the Pokemon can learn.
/// </summary>
public void AddLevelMove(LevelInt level, MoveData move)
{
Interface.learnable_moves_add_level_move(Handle, level, move.Name.ToPtr());

View File

@ -1,29 +1,48 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BackingLevelInt = System.Byte;
// ReSharper disable BuiltInTypeReferenceStyle
// The type we store a level in. As our implementation is aimed at normal Pokemon behaviour, a u8
// is probably enough, as we'd go up to 100. If you for some reason want to go higher, you can just
// change this type to hold a higher number.
using BackingLevelInt = System.Byte;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// The data structure used to store a level. This is a struct to allow for easy modification of the data type.
/// </summary>
/// <remarks>
/// If you want to change the type of the level, you can just change the type of <see cref="BackingLevelInt"/>.
/// Most of the implementation below is to allow for easy conversion between the two types, and to allow for
/// using the type as a normal integer.
/// </remarks>
[StructLayout(LayoutKind.Explicit)]
public struct LevelInt : IComparable, IComparable<BackingLevelInt>, IComparable<LevelInt>, IConvertible,
IEquatable<BackingLevelInt>, IEquatable<LevelInt>, IEquatable<int>, IFormattable
{
[FieldOffset(0)] private BackingLevelInt _value;
/// <summary>
/// Converts a <see cref="LevelInt"/> to a <see cref="BackingLevelInt"/>.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator BackingLevelInt(LevelInt l)
{
return l._value;
}
/// <summary>
/// Converts a <see cref="BackingLevelInt"/> to a <see cref="LevelInt"/>.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator LevelInt(BackingLevelInt b)
{
return new LevelInt { _value = b };
}
/// <inheritdoc />
public int CompareTo(object obj)
{
if (obj is LevelInt l)
@ -31,89 +50,120 @@ namespace PkmnLibSharp.StaticData
return _value.CompareTo(obj);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(BackingLevelInt other) => _value.CompareTo(other);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(LevelInt other) => _value.CompareTo(other._value);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TypeCode GetTypeCode() => Type.GetTypeCode(typeof(BackingLevelInt));
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ToBoolean(IFormatProvider provider) => ((IConvertible)_value).ToBoolean(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ToByte(IFormatProvider provider) => ((IConvertible)_value).ToByte(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public char ToChar(IFormatProvider provider) => ((IConvertible)_value).ToChar(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DateTime ToDateTime(IFormatProvider provider) => ((IConvertible)_value).ToDateTime(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public decimal ToDecimal(IFormatProvider provider) => ((IConvertible)_value).ToDecimal(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double ToDouble(IFormatProvider provider) => ((IConvertible)_value).ToDouble(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public short ToInt16(IFormatProvider provider) => ((IConvertible)_value).ToInt16(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ToInt32(IFormatProvider provider) => ((IConvertible)_value).ToInt32(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public long ToInt64(IFormatProvider provider) => ((IConvertible)_value).ToInt64(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public sbyte ToSByte(IFormatProvider provider) => ((IConvertible)_value).ToSByte(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float ToSingle(IFormatProvider provider) => ((IConvertible)_value).ToSingle(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ToString(IFormatProvider provider) => _value.ToString(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public object ToType(Type conversionType, IFormatProvider provider) =>
((IConvertible)_value).ToType(conversionType, provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ushort ToUInt16(IFormatProvider provider) => ((IConvertible)_value).ToUInt16(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint ToUInt32(IFormatProvider provider) => ((IConvertible)_value).ToUInt32(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong ToUInt64(IFormatProvider provider) => ((IConvertible)_value).ToUInt64(provider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(BackingLevelInt other) => _value == other;
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ToString(string format, IFormatProvider formatProvider) =>
_value.ToString(format, formatProvider);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(LevelInt other) => _value == other._value;
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(int other) => _value == other;
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object? obj) => obj is LevelInt other && Equals(other);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return _value.GetHashCode();
}
/// <summary>
/// Equality operator for <see cref="LevelInt"/>.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(LevelInt left, LevelInt right) => left.Equals(right);
/// <summary>
/// Inequality operator for <see cref="LevelInt"/>.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(LevelInt left, LevelInt right) => !left.Equals(right);
}

View File

@ -6,12 +6,19 @@ using Interface = PkmnLibSharp.FFI.StaticData.Libraries.AbilityLibrary;
namespace PkmnLibSharp.StaticData.Libraries
{
/// <summary>
/// A storage for all abilities that can be used in this data library.
/// </summary>
public class AbilityLibrary : DataLibrary<Ability>
{
/// <inheritdoc cref="AbilityLibrary"/>
protected AbilityLibrary(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new ability library.
/// </summary>
public static AbilityLibrary Create(ulong capacity)
{
var handle = Interface.ability_library_new(capacity).Resolve();
@ -20,6 +27,7 @@ namespace PkmnLibSharp.StaticData.Libraries
return self;
}
/// <inheritdoc />
protected override void AddNative(string key, Ability value) =>
Interface.ability_library_add(Handle, key.ToPtr(), value.Handle);
}

View File

@ -5,19 +5,30 @@ using PkmnLibSharp.Utils;
namespace PkmnLibSharp.StaticData.Libraries
{
/// <summary>
/// The base backing class for all data libraries.
/// </summary>
/// <typeparam name="T">
/// The type that this data library stores.
/// </typeparam>
public abstract class DataLibrary<T> : HandleType, IReadOnlyDictionary<string, T> where T : HandleType
{
private Dictionary<string, T> _backingDictionary = new(StringComparer.InvariantCultureIgnoreCase);
/// <inheritdoc cref="DataLibrary{T}"/>
protected DataLibrary(FFIHandle handle) : base(handle)
{
}
protected void ReserveCapacity(ulong capacity)
/// <summary>
/// Reserves the cache capacity for this data library.
/// </summary>
private protected void ReserveCapacity(ulong capacity)
{
_backingDictionary = new Dictionary<string, T>((int)capacity, StringComparer.InvariantCultureIgnoreCase);
}
/// <inheritdoc />
public IEnumerator<KeyValuePair<string, T>> GetEnumerator()
{
return _backingDictionary.GetEnumerator();
@ -28,22 +39,36 @@ namespace PkmnLibSharp.StaticData.Libraries
return GetEnumerator();
}
/// <summary>
/// Calls the native add function for this data library.
/// </summary>
protected abstract void AddNative(string key, T value);
/// <summary>
/// Adds a new value to the library.
/// </summary>
public void Add(string key, T value)
{
AddNative(key, value);
_backingDictionary.Add(key, value);
}
/// <inheritdoc />
public int Count => _backingDictionary.Count;
/// <inheritdoc />
public bool ContainsKey(string key) => _backingDictionary.ContainsKey(key);
/// <inheritdoc />
public bool TryGetValue(string key, out T value) => _backingDictionary.TryGetValue(key, out value);
/// <inheritdoc />
public T this[string key] => _backingDictionary[key];
/// <inheritdoc />
public IEnumerable<string> Keys => _backingDictionary.Keys;
/// <inheritdoc />
public IEnumerable<T> Values => _backingDictionary.Values;
}
}

View File

@ -6,15 +6,22 @@ using Interface = PkmnLibSharp.FFI.StaticData.Libraries.GrowthRateLibrary;
namespace PkmnLibSharp.StaticData.Libraries
{
/// <summary>
/// A library to store all growth rates.
/// </summary>
public class GrowthRateLibrary : HandleType
{
// ReSharper disable once CollectionNeverQueried.Local
private Dictionary<string, GrowthRate> _growthRates = new();
/// <inheritdoc cref="GrowthRateLibrary"/>
protected GrowthRateLibrary(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new growth rate library with a capacity.
/// </summary>
public static GrowthRateLibrary Create(ulong capacity)
{
var handle = Interface.growth_rate_library_new(capacity);
@ -24,16 +31,25 @@ namespace PkmnLibSharp.StaticData.Libraries
return lib;
}
/// <summary>
/// Calculates the level for a given growth key name and a certain experience.
/// </summary>
[MustUseReturnValue]
public LevelInt CalculateLevel(string name, uint experience) =>
Interface.growth_rate_library_calculate_level(Handle, name.ToPtr(), experience).Result();
/// <summary>
/// Calculates the experience for a given growth key name and a certain level.
/// </summary>
[MustUseReturnValue]
public uint CalculateExperience(string name, LevelInt level)
{
return Interface.growth_rate_library_calculate_experience(Handle, name.ToPtr(), level).Result();
}
/// <summary>
/// Adds a new growth rate with a name and value.
/// </summary>
public void AddGrowthRate(string name, GrowthRate growthRate)
{
Interface.growth_rate_library_add_growth_rate(Handle, name.ToPtr(), growthRate.Handle);

View File

@ -5,10 +5,17 @@ using Interface = PkmnLibSharp.FFI.StaticData.Libraries.ItemLibrary;
namespace PkmnLibSharp.StaticData.Libraries
{
/// <summary>
/// A library to store all items.
/// </summary>
public class ItemLibrary : DataLibrary<Item>
{
/// <inheritdoc />
protected ItemLibrary(FFIHandle handle) : base(handle){}
/// <summary>
/// Instantiates a new Item Library.
/// </summary>
public static ItemLibrary Create(ulong capacity)
{
var handle = Interface.item_library_new(capacity).Resolve();
@ -17,6 +24,7 @@ namespace PkmnLibSharp.StaticData.Libraries
return self;
}
/// <inheritdoc />
protected override void AddNative(string key, Item value)
{
Interface.item_library_add(Handle, key.ToPtr(), value.Handle);

View File

@ -10,6 +10,7 @@ namespace PkmnLibSharp.StaticData.Libraries
/// </summary>
public class LibrarySettings : HandleType
{
/// <inheritdoc cref="LibrarySettings"/>
protected LibrarySettings(FFIHandle handle) : base(handle) {}
/// <inheritdoc cref="LibrarySettings"/>

View File

@ -6,18 +6,26 @@ using Interface = PkmnLibSharp.FFI.StaticData.Libraries.MoveLibrary;
namespace PkmnLibSharp.StaticData.Libraries
{
/// <summary>
/// A library to store all data for moves.
/// </summary>
public class MoveLibrary : DataLibrary<MoveData>
{
/// <inheritdoc cref="MoveLibrary"/>
protected MoveLibrary(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new Move Library.
/// </summary>
public static MoveLibrary Create(ulong capacity)
{
var handle = Interface.move_library_new(capacity);
return Resolver.Instance.ResolveMoveLibrary(handle.Resolve());
}
/// <inheritdoc />
protected override void AddNative(string key, MoveData value)
{
Interface.move_library_add(Handle, key.ToPtr(), value.Handle);

View File

@ -8,14 +8,21 @@ using Interface = PkmnLibSharp.FFI.StaticData.Libraries.NatureLibrary;
namespace PkmnLibSharp.StaticData.Libraries
{
/// <summary>
/// A library of all natures that can be used, stored by their names.
/// </summary>
public class NatureLibrary : HandleType
{
private Dictionary<string, Nature> _natures = new();
/// <inheritdoc cref="NatureLibrary"/>
protected NatureLibrary(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Creates a new nature library with a given capacity.
/// </summary>
public static NatureLibrary Create(ulong capacity)
{
var handle = Interface.nature_library_new(capacity);
@ -25,6 +32,9 @@ namespace PkmnLibSharp.StaticData.Libraries
return lib;
}
/// <summary>
/// Try to get a nature from the library by its name.
/// </summary>
public bool TryGetNature(string name, [NotNullWhen(true)] out Nature? nature)
{
if (_natures.TryGetValue(name, out nature))
@ -37,17 +47,29 @@ namespace PkmnLibSharp.StaticData.Libraries
return true;
}
/// <summary>
/// Gets a random nature.
/// </summary>
/// <param name="seed">
/// The seed to use for the random number generator.
/// </param>
public Nature GetRandomNature(ulong seed)
{
return Resolver.Instance.ResolveNature(Interface.nature_library_get_random_nature(Handle, seed).Result().Resolve());
}
/// <summary>
/// Gets the name of a nature from its object.
/// </summary>
public string GetNatureName(Nature nature)
{
var fd = _natures.FirstOrDefault(x => x.Value == nature);
return fd.Key ?? Interface.nature_library_get_nature_name(Handle, nature.Handle).PtrString()!;
}
/// <summary>
/// Adds a new nature with name to the library.
/// </summary>
public void LoadNature(string name, Nature nature)
{
Interface.nature_library_load_nature(Handle, name.ToPtr(), nature.Handle);

View File

@ -3,12 +3,19 @@ using Interface = PkmnLibSharp.FFI.StaticData.Libraries.SpeciesLibrary;
namespace PkmnLibSharp.StaticData.Libraries
{
/// <summary>
/// A library to store all data for Pokemon species.
/// </summary>
public class SpeciesLibrary : DataLibrary<Species>
{
/// <inheritdoc cref="SpeciesLibrary"/>
protected SpeciesLibrary(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new Species Library.
/// </summary>
public static SpeciesLibrary Create(ulong capacity)
{
var handle = Interface.species_library_new(capacity);
@ -18,6 +25,7 @@ namespace PkmnLibSharp.StaticData.Libraries
return lib;
}
/// <inheritdoc />
protected override void AddNative(string key, Species value)
{
Interface.species_library_add(Handle, key.ToPtr(), value.Handle);

View File

@ -3,12 +3,19 @@ using Interface = PkmnLibSharp.FFI.StaticData.Libraries.StaticData;
namespace PkmnLibSharp.StaticData.Libraries
{
/// <summary>
/// The storage for all different libraries.
/// </summary>
public class StaticData : HandleType
{
/// <inheritdoc cref="StaticData"/>
protected StaticData(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new data collection.
/// </summary>
public static StaticData Create(LibrarySettings settings, SpeciesLibrary speciesLibrary,
MoveLibrary moveLibrary, ItemLibrary itemLibrary, GrowthRateLibrary growthRateLibrary,
TypeLibrary typeLibrary, NatureLibrary natureLibrary, AbilityLibrary abilityLibrary)
@ -28,13 +35,44 @@ namespace PkmnLibSharp.StaticData.Libraries
return data;
}
/// <summary>
/// Several misc settings for the library.
/// </summary>
public LibrarySettings LibrarySettings { get; private set; } = null!;
/// <summary>
/// All data for Pokemon species.
/// </summary>
public SpeciesLibrary SpeciesLibrary { get; private set; } = null!;
/// <summary>
/// All data for the moves.
/// </summary>
public MoveLibrary MoveLibrary { get; private set; } = null!;
/// <summary>
/// All data for the items.
/// </summary>
public ItemLibrary ItemLibrary { get; private set; } = null!;
/// <summary>
/// All data for growth rates.
/// </summary>
public GrowthRateLibrary GrowthRateLibrary { get; private set; } = null!;
/// <summary>
/// All data related to types and type effectiveness.
/// </summary>
public TypeLibrary TypeLibrary { get; private set; } = null!;
/// <summary>
/// All data related to natures.
/// </summary>
public NatureLibrary NatureLibrary { get; private set; } = null!;
/// <summary>
/// All data related to abilities.
/// </summary>
public AbilityLibrary AbilityLibrary { get; private set; } = null!;
}
}

View File

@ -7,53 +7,81 @@ using Interface = PkmnLibSharp.FFI.StaticData.Libraries.TypeLibrary;
namespace PkmnLibSharp.StaticData.Libraries
{
/// <summary>
/// All data related to types and effectiveness.
/// </summary>
public class TypeLibrary : HandleType
{
public Dictionary<string, TypeIdentifier> TypeCache { get; private set; } =
new(StringComparer.InvariantCultureIgnoreCase);
protected TypeLibrary(FFIHandle handle) : base(handle){}
private Dictionary<string, TypeIdentifier> _typeCache = new(StringComparer.InvariantCultureIgnoreCase);
/// <inheritdoc cref="TypeLibrary"/>
protected TypeLibrary(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new type library with a specific capacity.
/// </summary>
public static TypeLibrary Create(ulong capacity)
{
var handle = Interface.type_library_new(capacity);
var lib = Resolver.Instance.ResolveTypeLibrary(handle.Resolve());
lib.TypeCache = new Dictionary<string, TypeIdentifier>((int)capacity, StringComparer.InvariantCultureIgnoreCase);
lib._typeCache =
new Dictionary<string, TypeIdentifier>((int)capacity, StringComparer.InvariantCultureIgnoreCase);
return lib;
}
/// <summary>
/// Gets the type identifier for a type with a name.
/// </summary>
public TypeIdentifier GetTypeId(string name)
{
if (TypeCache.TryGetValue(name, out var typeIdentifier))
if (_typeCache.TryGetValue(name, out var typeIdentifier))
return typeIdentifier;
throw new KeyNotFoundException($"No type found with name `{name}`");
}
/// <summary>
/// Gets the type name from the type identifier.
/// </summary>
public string GetTypeName(TypeIdentifier typeIdentifier)
{
var fd = TypeCache.FirstOrDefault(x => x.Value == typeIdentifier);
var fd = _typeCache.FirstOrDefault(x => x.Value == typeIdentifier);
if (fd.Key != null)
return fd.Key;
throw new KeyNotFoundException($"No type found for given identifier");
}
/// <summary>
/// Gets the effectiveness for a single attacking type against a single defending type.
/// </summary>
public float GetSingleEffectiveness(TypeIdentifier attacking, TypeIdentifier defending) =>
Interface.type_library_get_single_effectiveness(Handle, attacking, defending);
/// <summary>
/// Gets the effectiveness for a single attacking type against an amount of defending types.
/// This is equivalent to running <see cref="GetSingleEffectiveness"/> on each defending type, and
/// multiplying the results with each other.
/// </summary>
public float GetEffectiveness(TypeIdentifier attacking, TypeIdentifier[] defending)
{
var arrayPtr = defending.ArrayPtr();
return Interface.type_library_get_effectiveness(Handle, attacking, arrayPtr, (ulong)defending.Length);
}
/// <summary>
/// Registers a new type in the library.
/// </summary>
public TypeIdentifier RegisterType(string name)
{
var typeId = Interface.type_library_register_type(Handle, name.ToPtr());
TypeCache.Add(name, typeId);
_typeCache.Add(name, typeId);
return typeId;
}
/// <summary>
/// Sets the effectiveness for an attacking type against a defending type.
/// </summary>
public void SetEffectiveness(TypeIdentifier attacking, TypeIdentifier defending, float effectiveness) =>
Interface.type_library_set_effectiveness(Handle, attacking, defending, effectiveness);
}

View File

@ -5,6 +5,9 @@ using Interface = PkmnLibSharp.FFI.StaticData.MoveData;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// The move category defines what global kind of move this move is.
/// </summary>
public enum MoveCategory : byte
{
/// A physical move uses the physical attack stats and physical defense stats to calculate damage.
@ -17,6 +20,9 @@ namespace PkmnLibSharp.StaticData
Status = 2,
}
/// <summary>
/// The move target defines what kind of targets the move can touch.
/// </summary>
public enum MoveTarget : byte
{
/// Adjacent allows a move to target any Pokemon that is either directly to the left or right of
@ -60,40 +66,87 @@ namespace PkmnLibSharp.StaticData
SelfUse,
}
/// <summary>
/// A move is the skill Pokémon primarily use in battle. This is the data related to that.
/// </summary>
public class MoveData : HandleType
{
protected MoveData(FFIHandle handle) : base(handle) {}
public static MoveData Create(string name, TypeIdentifier moveType, MoveCategory category, byte basePower, byte accuracy,
byte baseUsages, MoveTarget target, sbyte priority, SecondaryEffect? secondaryEffect,
/// <inheritdoc cref="MoveData"/>
protected MoveData(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new move.
/// </summary>
public static MoveData Create(string name, TypeIdentifier moveType, MoveCategory category, byte basePower,
byte accuracy, byte baseUsages, MoveTarget target, sbyte priority, SecondaryEffect? secondaryEffect,
IEnumerable<string> flags)
{
var ptrArray = flags.Select(x => x.ToPtr()).ToArray();
var ptrToPtrArray = ptrArray.ArrayPtr();
var handle = Interface.move_data_new(name.ToPtr(), moveType, category, basePower, accuracy, baseUsages, target,
priority, secondaryEffect?.Handle ?? FFIHandle.Zero, ptrToPtrArray,
(ulong)ptrArray.Length);
var handle = Interface.move_data_new(name.ToPtr(), moveType, category, basePower, accuracy, baseUsages,
target, priority, secondaryEffect?.Handle ?? FFIHandle.Zero, ptrToPtrArray, (ulong)ptrArray.Length);
return Resolver.Instance.ResolveMoveData(handle.Result().Resolve());
}
private string? _name;
/// <summary>
/// The name of the move.
/// </summary>
public string Name => _name ??= Interface.move_data_name(Handle).Result().PtrString()!;
private TypeIdentifier? _type;
/// <summary>
/// The attacking type of the move.
/// </summary>
public TypeIdentifier Type => _type ??= Interface.move_data_move_type(Handle);
private byte? _basePower;
public byte BasePower => _basePower ??= Interface.move_data_base_power(Handle);
private MoveCategory? _category;
/// <summary>
/// The category of the move.
/// </summary>
public MoveCategory Category => _category ??= Interface.move_data_category(Handle);
private byte? _basePower;
/// <summary>
/// The base power, not considering any modifiers, the move has.
/// </summary>
public byte BasePower => _basePower ??= Interface.move_data_base_power(Handle);
private byte? _accuracy;
/// <summary>
/// The accuracy of the move in percentage. Should be 255 for moves that always hit.
/// </summary>
public byte Accuracy => _accuracy ??= Interface.move_data_accuracy(Handle);
private byte? _baseUsages;
/// <summary>
/// The number of times the move can be used. This can be modified on actually learned moves using
/// PP-Ups
/// </summary>
public byte BaseUsages => _baseUsages ??= Interface.move_data_base_usages(Handle);
private MoveTarget? _target;
/// <summary>
/// How the move handles targets.
/// </summary>
public MoveTarget Target => _target ??= Interface.move_data_target(Handle);
private sbyte? _priority;
/// <summary>
/// The priority of the move. A higher priority means the move should go before other moves.
/// </summary>
public sbyte Priority => _priority ??= Interface.move_data_priority(Handle);
private SecondaryEffect? _secondaryEffect;
/// <summary>
/// The optional secondary effect the move has.
/// </summary>
public SecondaryEffect? SecondaryEffect
{
get
@ -108,6 +161,9 @@ namespace PkmnLibSharp.StaticData
}
}
/// <summary>
/// Checks if the move has a specific flag.
/// </summary>
public bool HasFlag(string flag)
{
return Interface.move_data_has_flag(Handle, flag.ToPtr()) == 1;

View File

@ -3,36 +3,59 @@ using Interface = PkmnLibSharp.FFI.StaticData.Nature;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// A nature is an attribute on a Pokemon that modifies the effective base stats on a Pokemon. They
/// can have an increased statistic and a decreased statistic, or be neutral.
/// </summary>
public class Nature : HandleType
{
/// <inheritdoc cref="Nature"/>
protected Nature(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new nature.
/// </summary>
public static Nature Create(Statistic increasedStat, Statistic decreasedStat, float increaseModifier = 1.1f,
float decreaseModifier = 0.9f)
{
var handle = Interface.nature_new(increasedStat, decreasedStat, increaseModifier, decreaseModifier);
return Resolver.Instance.ResolveNature(handle.Resolve());
}
/// <summary>
/// Instantiates a new nature that does not modify any stats.
/// </summary>
public static Nature NeutralNature()
{
return Create(Statistic.HP, Statistic.HP, 1f, 1f);
}
private Statistic? _increasedStat;
/// <summary>
/// The stat that should receive the increased modifier.
/// </summary>
public Statistic IncreasedStat
{
get { return _increasedStat ??= Interface.nature_increased_stat(Handle); }
}
private Statistic? _decreasedStat;
/// <summary>
/// The stat that should receive the decreased modifier.
/// </summary>
public Statistic DecreasedStat
{
get { return _decreasedStat ??= Interface.nature_decreased_stat(Handle); }
}
/// <summary>
/// Calculates the modifier for a given stat. If it's the increased stat, returns the increased
/// modifier, if it's the decreased stat, returns the decreased modifier. Otherwise returns 1.0
/// </summary>
public float GetStatModifier(Statistic statistic)
{
return Interface.nature_get_stat_modifier(Handle, statistic);

View File

@ -5,12 +5,19 @@ using Interface = PkmnLibSharp.FFI.StaticData.MoveData;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// A secondary effect is an effect on a move that happens after it hits.
/// </summary>
public class SecondaryEffect : HandleType
{
/// <inheritdoc cref="SecondaryEffect"/>
protected SecondaryEffect(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Instantiates a new Secondary Effect.
/// </summary>
public static SecondaryEffect Create(float chance, string effectName, IReadOnlyList<EffectParameter> parameters)
{
var parameterPtrs = parameters.Select(x => (FFIHandleValue)x.Handle).ToArray();
@ -23,11 +30,20 @@ namespace PkmnLibSharp.StaticData
}
private float? _chance;
/// <summary>
/// The chance in percentages that the effect triggers. -1 to make it always trigger.
/// </summary>
public float Chance => _chance ??= Interface.secondary_effect_chance(Handle);
private string? _name;
/// <summary>
/// The name of the effect.
/// </summary>
public string Name => _name ??= Interface.secondary_effect_effect_name(Handle).Result().PtrString()!;
/// <summary>
/// A list of parameters for the effect.
/// </summary>
public IReadOnlyList<EffectParameter> Parameters { get; private set; } = null!;
}
}

View File

@ -7,14 +7,21 @@ using Interface = PkmnLibSharp.FFI.StaticData.Species;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// The data belonging to a Pokemon with certain characteristics.
/// </summary>
public class Species : HandleType
{
/// <inheritdoc cref="Species"/>
protected Species(FFIHandle handle) : base(handle)
{
}
public static Species Create(ushort id, string name, float genderRate, string growthRate, byte captureRate, Form defaultForm,
IReadOnlyCollection<string> flags)
/// <summary>
/// Creates a new species.
/// </summary>
public static Species Create(ushort id, string name, float genderRate, string growthRate, byte captureRate,
Form defaultForm, IReadOnlyCollection<string> flags)
{
var flagsPtrArray = flags.Select(x => x.ToPtr()).ToArray();
var handle = Interface.species_new(id, name.ToPtr(), genderRate, growthRate.ToPtr(), captureRate,
@ -25,16 +32,44 @@ namespace PkmnLibSharp.StaticData
}
private ushort? _id;
/// <summary>
/// The national dex identifier of the Pokemon.
/// </summary>
public ushort Id => _id ??= Interface.species_id(Handle);
private string? _name;
/// <summary>
/// The name of the Pokemon.
/// </summary>
public string Name => _name ??= Interface.species_name(Handle).PtrString()!;
private float? _genderRate;
/// <summary>
/// The chance between 0.0 and 1.0 that a Pokemon is female.
/// </summary>
public float GenderRate => _genderRate ??= Interface.species_gender_rate(Handle);
private string? _growthRate;
/// <summary>
/// How much experience is required for a level.
/// </summary>
public string GrowthRate => _growthRate ??= Interface.species_growth_rate(Handle).PtrString()!;
private byte? _captureRate;
public byte CaptureRate => _captureRate ??= Interface.species_capture_rate(Handle);
/// <summary>
/// How hard it is to capture a Pokemon. 255 means this will be always caught, 0 means this is
/// uncatchable.
/// </summary>
public byte CaptureRate => _captureRate ??= Interface.species_capture_rate(Handle);
/// <summary>
/// Gets the form the Pokemon will have by default, if no other form is specified.
/// </summary>
public Form DefaultForm
{
get
@ -44,7 +79,11 @@ namespace PkmnLibSharp.StaticData
}
}
private Dictionary<string, Form> _forms = new(StringComparer.InvariantCultureIgnoreCase);
private readonly Dictionary<string, Form> _forms = new(StringComparer.InvariantCultureIgnoreCase);
/// <summary>
/// Gets a form by name.
/// </summary>
public bool TryGetForm(string formName, [NotNullWhen(true)] out Form? form)
{
if (_forms.TryGetValue(formName, out form))
@ -61,12 +100,19 @@ namespace PkmnLibSharp.StaticData
return true;
}
/// <summary>
/// Adds a new form to the species.
/// </summary>
/// <param name="form"></param>
public void AddForm(Form form)
{
Interface.species_add_form(Handle, form.Name.ToPtr(), form.Handle).Result();
_forms.Add(form.Name, form);
}
/// <summary>
/// Gets a random gender, returning a value based on the species gender ratio.
/// </summary>
public Gender GetRandomGender(ulong seed) => Interface.species_get_random_gender(Handle, seed);
}
}

View File

@ -5,13 +5,24 @@ using Interface = PkmnLibSharp.FFI.StaticData.StaticStatisticSet;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// A collection of statistics that can not be modified after creation.
/// </summary>
/// <typeparam name="T">
/// The integer type that this statistic set will use. This type must be one of the following: ushort
/// </typeparam>
public class StaticStatisticSet<T> : HandleType where T : struct, IConvertible
{
/// <inheritdoc cref="StaticStatisticSet{T}"/>
protected StaticStatisticSet(FFIHandle handle) : base(handle)
{
}
public static StaticStatisticSet<T> Create(T hp, T attack, T defense, T specialAttack, T specialDefense, T speed)
/// <summary>
/// Create a new static statistic set.
/// </summary>
public static StaticStatisticSet<T> Create(T hp, T attack, T defense, T specialAttack, T specialDefense,
T speed)
{
var handle = typeof(T) switch
{
@ -23,6 +34,9 @@ namespace PkmnLibSharp.StaticData
return Resolver.Instance.ResolveStaticStatisticSet<T>(handle.Resolve());
}
/// <summary>
/// Get the value of a specific stat
/// </summary>
[PublicAPI]
public T GetStatistic(Statistic statistic)
{
@ -73,22 +87,45 @@ namespace PkmnLibSharp.StaticData
}
private T? _hp;
/// <summary>
/// The health point stat value.
/// </summary>
public T HP => GetStatistic(Statistic.HP);
private T? _attack;
/// <summary>
/// The physical attack stat value.
/// </summary>
public T Attack => GetStatistic(Statistic.Attack);
private T? _defense;
/// <summary>
/// The physical defense stat value.
/// </summary>
public T Defense => GetStatistic(Statistic.Defense);
private T? _specialAttack;
/// <summary>
/// The special attack stat value.
/// </summary>
public T SpecialAttack => GetStatistic(Statistic.SpecialAttack);
private T? _specialDefense;
/// <summary>
/// The special defense stat value.
/// </summary>
public T SpecialDefense => GetStatistic(Statistic.SpecialDefense);
private T? _speed;
/// <summary>
/// The speed stat value.
/// </summary>
public T Speed => GetStatistic(Statistic.Speed);
}
}

View File

@ -1,12 +1,38 @@
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// Stats are numerical values on Pokemon that are used in battle.
/// </summary>
public enum Statistic : byte
{
/// <summary>
/// Health Points determine how much damage a Pokemon can receive before fainting.
/// </summary>
HP = 0,
/// <summary>
/// Attack determines how much damage a Pokemon deals when using a physical attack.
/// </summary>
Attack = 1,
/// <summary>
/// Defense determines how much damage a Pokemon receives when it is hit by a physical attack.
/// </summary>
Defense = 2,
/// <summary>
/// Special Attack determines how much damage a Pokemon deals when using a special attack.
/// </summary>
SpecialAttack = 3,
/// <summary>
/// Special Defense determines how much damage a Pokemon receives when it is hit by a special attack.
/// </summary>
SpecialDefense = 4,
/// <summary>
/// Speed determines the order that a Pokemon can act in battle.
/// </summary>
Speed = 5,
}
}

View File

@ -5,12 +5,23 @@ using Interface = PkmnLibSharp.FFI.StaticData.StatisticSet;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// A collection of every individual stat. This set can hold any value that is valid for its integer
/// type, and can be modified at will.
/// </summary>
/// <typeparam name="T">
/// The integer type that this statistic set will use. This type must be one of the following: byte, sbyte, uint.
/// </typeparam>
public class StatisticSet<T> : HandleType where T : struct, IConvertible
{
/// <inheritdoc cref="StatisticSet{T}"/>
protected StatisticSet(FFIHandle handle) : base(handle)
{
}
/// <summary>
/// Creates a new statistic set with given stats.
/// </summary>
public static StatisticSet<T> Create(T hp, T attack, T defense, T specialAttack, T specialDefense, T speed)
{
var handle = typeof(T) switch
@ -29,6 +40,9 @@ namespace PkmnLibSharp.StaticData
return Resolver.Instance.ResolveStatisticSet<T>(handle.Resolve());
}
/// <summary>
/// Get the value of a specific stat
/// </summary>
[PublicAPI]
public T GetStatistic(Statistic statistic)
{
@ -42,6 +56,9 @@ namespace PkmnLibSharp.StaticData
return (T)p;
}
/// <summary>
/// Modify the value of a specific stat.
/// </summary>
[PublicAPI]
public void SetStatistic(Statistic statistic, T value)
{
@ -53,36 +70,54 @@ namespace PkmnLibSharp.StaticData
else throw new ArgumentOutOfRangeException();
}
/// <summary>
/// The health point stat value.
/// </summary>
public T HP
{
get => GetStatistic(Statistic.HP);
set => SetStatistic(Statistic.HP, value);
}
/// <summary>
/// The physical attack stat value.
/// </summary>
public T Attack
{
get => GetStatistic(Statistic.Attack);
set => SetStatistic(Statistic.Attack, value);
}
/// <summary>
/// The physical defense stat value.
/// </summary>
public T Defense
{
get => GetStatistic(Statistic.Defense);
set => SetStatistic(Statistic.Defense, value);
}
/// <summary>
/// The special attack stat value.
/// </summary>
public T SpecialAttack
{
get => GetStatistic(Statistic.SpecialAttack);
set => SetStatistic(Statistic.SpecialAttack, value);
}
/// <summary>
/// The special defense stat value.
/// </summary>
public T SpecialDefense
{
get => GetStatistic(Statistic.SpecialDefense);
set => SetStatistic(Statistic.SpecialDefense, value);
}
/// <summary>
/// The speed stat value.
/// </summary>
public T Speed
{
get => GetStatistic(Statistic.Speed);

View File

@ -1,43 +1,61 @@
using System;
using System.Runtime.InteropServices;
namespace PkmnLibSharp.StaticData
{
/// <summary>
/// An identifier for a type. This is stored as a single byte internally, but is represented as a struct so that
/// we can use the type system to ensure that we don't accidentally mix up type identifiers with other bytes.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public readonly struct TypeIdentifier
public readonly struct TypeIdentifier : IEquatable<TypeIdentifier>
{
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
[FieldOffset(0)] private readonly byte _identifier;
/// <summary>
/// Creates a new type identifier.
/// </summary>
public TypeIdentifier(byte b)
{
_identifier = b;
}
/// <inheritdoc />
public bool Equals(TypeIdentifier other)
{
return _identifier == other._identifier;
}
/// <inheritdoc />
public override bool Equals(object? obj)
{
return obj is TypeIdentifier other && Equals(other);
}
/// <inheritdoc />
public override int GetHashCode()
{
return _identifier.GetHashCode();
}
/// <summary>
/// Equality operator for type identifiers.
/// </summary>
public static bool operator ==(TypeIdentifier left, TypeIdentifier right)
{
return left.Equals(right);
}
/// <summary>
/// Inequality operator for type identifiers.
/// </summary>
public static bool operator !=(TypeIdentifier left, TypeIdentifier right)
{
return !left.Equals(right);
}
/// <inheritdoc />
public override string ToString()
{
return $"Type({_identifier})";

View File

@ -5,7 +5,7 @@ using System.Runtime.CompilerServices;
namespace PkmnLibSharp.Utils
{
public sealed class CachedExternArray<T> : IReadOnlyList<T>
internal sealed class CachedExternArray<T> : IReadOnlyList<T>
where T: class
{
private readonly T?[] _array;
@ -50,7 +50,7 @@ namespace PkmnLibSharp.Utils
}
public class CachedExternValueArray<T> : IReadOnlyList<T>
internal class CachedExternValueArray<T> : IReadOnlyList<T>
where T: struct
{
private readonly T?[] _array;

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace PkmnLibSharp.Utils
{
public class ExternValueArray<T> : IReadOnlyList<T> where T : struct
internal class ExternValueArray<T> : IReadOnlyList<T>
{
private readonly Func<ulong> _getLength;
private readonly Func<ulong, T> _getItem;

View File

@ -60,6 +60,9 @@ namespace PkmnLibSharp.Utils
}
}
/// <summary>
/// A handle to a native object.
/// </summary>
public struct FFIHandle
{
internal FFIHandle(ulong handle, FFIHandleReference handleReference)
@ -68,7 +71,11 @@ namespace PkmnLibSharp.Utils
_handleReference = handleReference;
}
public ulong Handle { get; }
internal ulong Handle { get; }
/// <summary>
/// A handle with a value of 0, which is the null handle.
/// </summary>
public static FFIHandle Zero => new(0, null!);
/// <summary>

View File

@ -1,12 +1,22 @@
using JetBrains.Annotations;
namespace PkmnLibSharp.Utils
{
/// <summary>
/// A basic type that holds a handle to a native object.
/// </summary>
[UsedImplicitly(ImplicitUseTargetFlags.Default | ImplicitUseTargetFlags.WithInheritors)]
public abstract class HandleType
{
/// <inheritdoc cref="HandleType"/>
protected internal HandleType(FFIHandle handle)
{
Handle = handle;
}
/// <summary>
/// The internal handle to the native object.
/// </summary>
protected internal FFIHandle Handle { get; }
}
}

View File

@ -3,17 +3,31 @@ using System.Collections.Generic;
using System.Reflection;
using PkmnLibSharp.DynamicData;
using PkmnLibSharp.DynamicData.Libraries;
using PkmnLibSharp.FFI;
using PkmnLibSharp.StaticData;
using PkmnLibSharp.StaticData.Libraries;
namespace PkmnLibSharp.Utils
{
/// <summary>
/// This class is used to resolve FFIHandles to their respective classes. We use this so we can handle inheritance
/// in our objects. By overriding a method in this class, we can return a different object than the default throughout
/// the library.
///
/// This class is a singleton, so you can replace the default resolver with your own by setting the Instance property.
/// </summary>
public class Resolver
{
/// <summary>
/// The global resolver instance. This is used throughout the library to resolve FFIHandles to their respective
/// classes.
/// </summary>
public static Resolver Instance { get; set; } = new();
private static readonly Dictionary<FFIHandle, object> Instances = new();
/// <summary>
/// Either returns an existing instance of the given type, or creates a new one and returns it.
/// </summary>
protected static T GetOrCreateInstance<T>(FFIHandle handle)
{
if (!Instances.TryGetValue(handle, out var instance))
@ -28,79 +42,177 @@ namespace PkmnLibSharp.Utils
return (T)instance!;
}
/// <summary>
/// Resolve an effect parameter from the given handle.
/// </summary>
public virtual EffectParameter ResolveEffectParameter(FFIHandle handle) =>
GetOrCreateInstance<EffectParameter>(handle);
/// <summary>
/// Resolve a move effect from the given handle.
/// </summary>
public virtual Ability ResolveAbility(FFIHandle handle) => GetOrCreateInstance<Ability>(handle);
/// <summary>
/// Resolve a form from the given handle.
/// </summary>
public virtual Form ResolveForm(FFIHandle handle) => GetOrCreateInstance<Form>(handle);
/// <summary>
/// Resolve a static statistic set from the given handle.
/// </summary>
public virtual StaticStatisticSet<T> ResolveStaticStatisticSet<T>(FFIHandle handle)
where T : struct, IConvertible =>
GetOrCreateInstance<StaticStatisticSet<T>>(handle);
/// <summary>
/// Resolve a statistic set from the given handle.
/// </summary>
public virtual StatisticSet<T> ResolveStatisticSet<T>(FFIHandle handle) where T : struct, IConvertible =>
GetOrCreateInstance<StatisticSet<T>>(handle);
/// <summary>
/// Resolve a learnable moves object from the given handle.
/// </summary>
public virtual LearnableMoves ResolveLearnableMoves(FFIHandle handle) =>
GetOrCreateInstance<LearnableMoves>(handle);
/// <summary>
/// Resolve an item object from the given handle.
/// </summary>
public virtual Item ResolveItem(FFIHandle handle) => GetOrCreateInstance<Item>(handle);
/// <summary>
/// Resolve a secondary effect object from the given handle.
/// </summary>
public virtual SecondaryEffect ResolveSecondaryEffect(FFIHandle handle) =>
GetOrCreateInstance<SecondaryEffect>(handle);
/// <summary>
/// Resolve a move data object from the given handle.
/// </summary>
public virtual MoveData ResolveMoveData(FFIHandle handle) => GetOrCreateInstance<MoveData>(handle);
/// <summary>
/// Resolve an nature from the given handle.
/// </summary>
public virtual Nature ResolveNature(FFIHandle handle) => GetOrCreateInstance<Nature>(handle);
/// <summary>
/// Resolve a species object from the given handle.
/// </summary>
public virtual Species ResolveSpecies(FFIHandle handle) => GetOrCreateInstance<Species>(handle);
public virtual AbilityLibrary ResolveAbilityLibrary(FFIHandle handle) =>
GetOrCreateInstance<AbilityLibrary>(handle);
public virtual ItemLibrary ResolveItemLibrary(FFIHandle handle) => GetOrCreateInstance<ItemLibrary>(handle);
/// <summary>
/// Resolve a growthrate object from the given handle.
/// </summary>
public virtual LookupGrowthRate ResolveLookupGrowthRate(FFIHandle ptr) =>
GetOrCreateInstance<LookupGrowthRate>(ptr);
/// <summary>
/// Resolve an ability library object from the given handle.
/// </summary>
public virtual AbilityLibrary ResolveAbilityLibrary(FFIHandle handle) =>
GetOrCreateInstance<AbilityLibrary>(handle);
/// <summary>
/// Resolve an item library object from the given handle.
/// </summary>
public virtual ItemLibrary ResolveItemLibrary(FFIHandle handle) => GetOrCreateInstance<ItemLibrary>(handle);
/// <summary>
/// Resolve a growthrate library object from the given handle.
/// </summary>
public virtual GrowthRateLibrary ResolveGrowthRateLibrary(FFIHandle handle) =>
GetOrCreateInstance<GrowthRateLibrary>(handle);
/// <summary>
/// Resolve a move library object from the given handle.
/// </summary>
public virtual MoveLibrary ResolveMoveLibrary(FFIHandle handle) => GetOrCreateInstance<MoveLibrary>(handle);
/// <summary>
/// Resolve a nature library object from the given handle.
/// </summary>
public virtual NatureLibrary ResolveNatureLibrary(FFIHandle handle) =>
GetOrCreateInstance<NatureLibrary>(handle);
/// <summary>
/// Resolve a species library object from the given handle.
/// </summary>
public virtual SpeciesLibrary ResolveSpeciesLibrary(FFIHandle handle) =>
GetOrCreateInstance<SpeciesLibrary>(handle);
/// <summary>
/// Resolve a library settings object from the given handle.
/// </summary>
public virtual LibrarySettings ResolveLibrarySettings(FFIHandle handle) =>
GetOrCreateInstance<LibrarySettings>(handle);
/// <summary>
/// Resolve a type library object from the given handle.
/// </summary>
public virtual TypeLibrary ResolveTypeLibrary(FFIHandle handle) => GetOrCreateInstance<TypeLibrary>(handle);
/// <summary>
/// Resolve a static data object from the given handle.
/// </summary>
public virtual StaticData.Libraries.StaticData ResolveStaticData(FFIHandle handle) =>
GetOrCreateInstance<StaticData.Libraries.StaticData>(handle);
/// <summary>
/// Resolve a learned move object from the given handle.
/// </summary>
public virtual LearnedMove ResolveLearnedMove(FFIHandle resolve) => GetOrCreateInstance<LearnedMove>(resolve);
public virtual Gen7BattleStatCalculator ResolveGen7BattleStatCalculator(FFIHandle handle) =>
/// <summary>
/// Resolve a battle stat calculator object from the given handle.
/// </summary>
public virtual BattleStatCalculator ResolveBattleStatCalculator(FFIHandle handle) =>
GetOrCreateInstance<Gen7BattleStatCalculator>(handle);
public virtual Gen7DamageLibrary ResolveGen7DamageLibrary(FFIHandle handle) =>
/// <summary>
/// Resolve a damage library object from the given handle.
/// </summary>
public virtual DamageLibrary ResolveDamageLibrary(FFIHandle handle) =>
GetOrCreateInstance<Gen7DamageLibrary>(handle);
public virtual Gen7MiscLibrary ResolveGen7MiscLibrary(FFIHandle handle) =>
GetOrCreateInstance<Gen7MiscLibrary>(handle);
/// <summary>
/// Resolve a misc library object from the given handle.
/// </summary>
public virtual MiscLibrary ResolveMiscLibrary(FFIHandle handle) => GetOrCreateInstance<Gen7MiscLibrary>(handle);
/// <summary>
/// Resolve an empty script resolver object from the given handle.
/// </summary>
public virtual EmptyScriptResolver ResolveEmptyScriptResolver(FFIHandle handle) =>
GetOrCreateInstance<EmptyScriptResolver>(handle);
/// <summary>
/// Resolve a script resolver object from the given handle.
/// </summary>
#if WASM
public virtual WasmScriptResolver ResolveWasmScriptResolver(FFIHandle handle) =>
public virtual ScriptResolver ResolveScriptResolver(FFIHandle handle) =>
GetOrCreateInstance<WasmScriptResolver>(handle);
#else
public virtual ScriptResolver ResolveScriptResolver(FFIHandle handle) => ResolveEmptyScriptResolver(handle);
#endif
public DynamicLibrary ResolveDynamicLibrary(FFIHandle resolve) => GetOrCreateInstance<DynamicLibrary>(resolve);
/// <summary>
/// Resolve a dynamic library object from the given handle.
/// </summary>
public virtual DynamicLibrary ResolveDynamicLibrary(FFIHandle resolve) =>
GetOrCreateInstance<DynamicLibrary>(resolve);
public Pokemon ResolvePokemon(FFIHandle resolve) => GetOrCreateInstance<Pokemon>(resolve);
/// <summary>
/// Resolve a pokemon object from the given handle.
/// </summary>
public virtual Pokemon ResolvePokemon(FFIHandle resolve) => GetOrCreateInstance<Pokemon>(resolve);
/// <summary>
/// Resolve a pokemon party object from the given handle.
/// </summary>
public virtual PokemonParty ResolvePokemonParty(FFIHandle resolve) =>
GetOrCreateInstance<PokemonParty>(resolve);
}
}

BIN
PkmnLibRSharp/libpkmn_lib.so (Stored with Git LFS)

Binary file not shown.

View File

@ -72,7 +72,7 @@ namespace PkmnLibRSharpTests.DynamicData
{
var library = GetLibrary();
var pokemon = new PokemonBuilder(library, "testSpecies", 100).WithIdentifier(1000).Build();
Assert.AreEqual(1000, pokemon.UniqueIdentifier);
Assert.AreEqual(1000, pokemon.PersonalityValue);
}
[Test]