A load more work on FFI
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2023-04-15 09:58:21 +02:00
parent 3369696956
commit 2448f3bfb5
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
23 changed files with 870 additions and 25 deletions

View File

@ -6,7 +6,7 @@ namespace PkmnLibSharp.DynamicData.Libraries
{ {
public abstract class BattleStatCalculator : ExternPointer<object> public abstract class BattleStatCalculator : ExternPointer<object>
{ {
public BattleStatCalculator(IdentifiablePointer ptr, bool isOwner) : base(ptr, isOwner){} protected BattleStatCalculator(IdentifiablePointer ptr, bool isOwner) : base(ptr, isOwner){}
protected override object CreateCache() => new(); protected override object CreateCache() => new();

View File

@ -8,17 +8,26 @@ namespace PkmnLibSharp.DynamicData.Libraries
{ {
public class CacheData public class CacheData
{ {
public StaticData.Libraries.StaticData? StaticData { get; internal set; }
} }
internal DynamicLibrary(IdentifiablePointer ptr) : base(ptr, false) {} internal DynamicLibrary(IdentifiablePointer ptr) : base(ptr, false)
{
}
public DynamicLibrary(StaticData.Libraries.StaticData staticData, BattleStatCalculator statCalculator, public DynamicLibrary(StaticData.Libraries.StaticData staticData, BattleStatCalculator statCalculator,
DamageLibrary damageLibrary, MiscLibrary miscLibrary, ScriptResolver scriptResolver) : base( DamageLibrary damageLibrary, MiscLibrary miscLibrary, ScriptResolver scriptResolver) : base(
Interface.dynamic_library_new(staticData.Ptr, statCalculator.Ptr, damageLibrary.Ptr, miscLibrary.Ptr, Interface.dynamic_library_new(staticData.TakeOwnershipAndInvalidate(),
scriptResolver.Ptr), true) statCalculator.TakeOwnershipAndInvalidate(), damageLibrary.TakeOwnershipAndInvalidate(),
miscLibrary.TakeOwnershipAndInvalidate(), scriptResolver.TakeOwnershipAndInvalidate()), true)
{ {
} }
public StaticData.Libraries.StaticData StaticData =>
Cache.StaticData ??=
new StaticData.Libraries.StaticData(Interface.dynamic_library_get_static_data(Ptr), false);
protected override CacheData CreateCache() => new(); protected override CacheData CreateCache() => new();
protected override void Destructor() => Interface.dynamic_library_drop(Ptr); protected override void Destructor() => Interface.dynamic_library_drop(Ptr);
} }

View File

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using PkmnLibSharp.DynamicData.Libraries;
using PkmnLibSharp.StaticData;
using PkmnLibSharp.Utils;
namespace PkmnLibSharp.DynamicData
{
public class PokemonBuilder
{
private static readonly Random DefaultRandom = new();
private readonly Random _random;
private readonly int _randomSeed;
private readonly DynamicLibrary _library;
private readonly string _species;
private string _form = "default";
private readonly LevelInt _level;
private bool? _forceShininess;
private Gender? _gender;
private uint? _identifier;
private string? _natureName;
private string? _ability;
private uint? _experience;
private readonly List<(string name, MoveLearnMethod method)> _moves = new();
private bool _hiddenAbility;
private byte _abilityIndex;
private byte _coloring;
public PokemonBuilder(DynamicLibrary library, string species, LevelInt level)
{
_library = library;
_species = species;
_level = level;
_randomSeed = DefaultRandom.Next();
_random = new Random(_randomSeed);
}
public PokemonBuilder WithForm(string form)
{
_form = form;
return this;
}
public PokemonBuilder ForceShiny(bool value)
{
_forceShininess = value;
return this;
}
public PokemonBuilder WithGender(Gender gender)
{
_gender = gender;
return this;
}
public PokemonBuilder WithIdentifier(uint identifier)
{
_identifier = identifier;
return this;
}
public PokemonBuilder WithNature(string nature)
{
_natureName = nature;
return this;
}
public PokemonBuilder WithAbility(string ability)
{
_ability = ability;
return this;
}
public PokemonBuilder WithExperience(uint experience)
{
_experience = experience;
return this;
}
public PokemonBuilder LearnMove(string moveName, MoveLearnMethod learnMethod)
{
_moves.Add((moveName, learnMethod));
return this;
}
protected virtual Pokemon Finalize(Species species, Form form, Item? heldItem)
{
var pokemon = new Pokemon(_library, species, form, _hiddenAbility, _abilityIndex, _level,
_identifier ?? (uint)_random.Next(), _gender!.Value, _coloring, _natureName!);
if (heldItem != null)
pokemon.SetHeldItem(heldItem);
foreach (var move in _moves)
{
pokemon.LearnMove(move.name, move.method);
}
return pokemon;
}
protected virtual void PopulateUninitialized(Species species, Form form, Random random)
{
_experience ??= _library.StaticData.GrowthRateLibrary.CalculateExperience(species.GrowthRate, _level);
_identifier ??= (uint)random.Next();
_gender ??= species.GetRandomGender((ulong)_randomSeed);
if (_forceShininess.HasValue)
{
_coloring = _forceShininess.Value ? (byte)1 : (byte)0;
}
else if (random.Next((int)_library.StaticData.LibrarySettings.ShinyRate) == 0)
{
_coloring = 1;
}
if (string.IsNullOrEmpty(_ability))
{
_hiddenAbility = false;
_abilityIndex = (byte)_random.Next(0, form.Abilities.Count);
}
else
{
(_hiddenAbility, _abilityIndex) = GetAbilityIndex(species, form, _ability!);
}
if (string.IsNullOrEmpty(_natureName))
{
using var nature = _library.StaticData.NatureLibrary.GetRandomNature((ulong)_randomSeed);
_natureName = _library.StaticData.NatureLibrary.GetNatureName(nature);
}
}
private static (bool, byte) GetAbilityIndex(Species species, Form form, string ability)
{
var i = form.Abilities.IndexOf(ability);
if (i != -1)
return (false, (byte)i);
i = form.HiddenAbilities.IndexOf(ability);
if (i == -1)
{
throw new Exception(
$"Invalid ability '{ability}' for Pokemon '{species.Name}' and forme '{form.Name}'.");
}
return (true, (byte)i);
}
public Pokemon Build()
{
if (!_library.StaticData.SpeciesLibrary.TryGetValue(_species, out var species))
{
throw new Exception($"Species '{_species}' was not found.");
}
if (!species.TryGetForm(_form, out var form))
{
throw new Exception($"Forme '{_form}' was not found on species '{_species}'");
}
PopulateUninitialized(species, form, _random);
return Finalize(species, form, null);
}
}
}

View File

@ -18,5 +18,11 @@ namespace PkmnLibSharp.FFI.DynamicData.Libraries
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern void dynamic_library_drop(IntPtr dynamicLibrary); internal static extern void dynamic_library_drop(IntPtr dynamicLibrary);
/// <summary>
/// The static data is the immutable storage data for this library.
/// </summary>
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IdentifiablePointer dynamic_library_get_static_data(IntPtr dynamicLibrary);
} }
} }

View File

@ -7,12 +7,16 @@ namespace PkmnLibSharp.FFI.StaticData.Libraries
internal static class LibrarySettings internal static class LibrarySettings
{ {
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IdentifiablePointer library_settings_new(LevelInt maxLevel); internal static extern IdentifiablePointer library_settings_new(LevelInt maxLevel, uint shinyRate);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern void library_settings_drop(IntPtr ptr); internal static extern void library_settings_drop(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern LevelInt library_settings_maximum_level(IntPtr ptr); internal static extern LevelInt library_settings_maximum_level(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern uint library_settings_shiny_rate(IntPtr ptr);
} }
} }

View File

@ -17,6 +17,9 @@ namespace PkmnLibSharp.FFI.StaticData.Libraries
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IdentifiablePointer nature_library_get_nature(IntPtr ptr, IntPtr name); internal static extern IdentifiablePointer nature_library_get_nature(IntPtr ptr, IntPtr name);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IdentifiablePointer nature_library_get_random_nature(IntPtr ptr, ulong seed);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern IntPtr nature_library_get_nature_name(IntPtr ptr, IntPtr nature); internal static extern IntPtr nature_library_get_nature_name(IntPtr ptr, IntPtr nature);
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using PkmnLibSharp.StaticData;
namespace PkmnLibSharp.FFI.StaticData namespace PkmnLibSharp.FFI.StaticData
{ {
@ -33,5 +34,8 @@ namespace PkmnLibSharp.FFI.StaticData
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)] [DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern IdentifiablePointer species_get_form(IntPtr ptr, IntPtr name); internal static extern IdentifiablePointer species_get_form(IntPtr ptr, IntPtr name);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern Gender species_get_random_gender(IntPtr ptr, ulong seed);
} }
} }

View File

@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<RootNamespace>PkmnLibSharp</RootNamespace> <RootNamespace>PkmnLibSharp</RootNamespace>
<LangVersion>11</LangVersion> <LangVersion>11</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

View File

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using PkmnLibSharp.Utils; using PkmnLibSharp.Utils;
using Interface = PkmnLibSharp.FFI.StaticData.GrowthRate; using Interface = PkmnLibSharp.FFI.StaticData.GrowthRate;
@ -22,6 +24,7 @@ namespace PkmnLibSharp.StaticData
public uint CalculateExperience(LevelInt level) public uint CalculateExperience(LevelInt level)
{ {
Debug.Assert(level >= 1);
return Interface.growth_rate_calculate_experience(Ptr, level); return Interface.growth_rate_calculate_experience(Ptr, level);
} }
@ -50,4 +53,120 @@ namespace PkmnLibSharp.StaticData
Dispose(); Dispose();
} }
} }
/// <summary>
/// A collection of the growth rates that have been used since generation 5.
/// </summary>
public static class CommonGrowthRates
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Power2(uint i)
{
return i * i;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Power3(uint i)
{
return i * i * i;
}
public static LookupGrowthRate Fast(LevelInt maxLevel)
{
var arr = new uint[maxLevel];
arr[0] = 0;
for (uint i = 2; i < maxLevel + 1; i++)
{
arr[i - 1] = 4 * Power3(i) / 5;
}
return new LookupGrowthRate(arr);
}
public static LookupGrowthRate MediumFast(LevelInt maxLevel)
{
var arr = new uint[maxLevel];
arr[0] = 0;
for (uint i = 2; i < maxLevel + 1; i++)
{
arr[i - 1] = Power3(i);
}
return new LookupGrowthRate(arr);
}
public static LookupGrowthRate MediumSlow(LevelInt maxLevel)
{
var arr = new uint[maxLevel];
arr[0] = 0;
for (uint i = 2; i < maxLevel + 1; i++)
{
arr[i - 1] = 6 * Power3(i) / 5 - 15 * Power2(i) + 100 * i - 140;
}
return new LookupGrowthRate(arr);
}
public static LookupGrowthRate Slow(LevelInt maxLevel)
{
var arr = new uint[maxLevel];
arr[0] = 0;
for (uint i = 2; i < maxLevel + 1; i++)
{
arr[i - 1] = 5 * Power3(i) / 4;
}
return new LookupGrowthRate(arr);
}
public static LookupGrowthRate Erratic(LevelInt maxLevel)
{
var arr = new uint[maxLevel];
arr[0] = 0;
uint i = 2;
for (; i < 50; i++)
{
arr[i - 1] = Power3(i) * (100 - i) / 50;
}
for (; i < 68; i++)
{
arr[i - 1] = Power3(i) * (150 - i) / 100;
}
for (; i < 98; i++)
{
arr[i - 1] = Power3(i) * (uint)((1911 - 10 * i) / 3.0) / 500;
}
for (; i < maxLevel + 1; i++)
{
arr[i - 1] = Power3(i) * (160 - i) / 100;
}
return new LookupGrowthRate(arr);
}
public static LookupGrowthRate Fluctuating(LevelInt maxLevel)
{
var arr = new uint[maxLevel];
arr[0] = 0;
uint i = 2;
for (; i < 15; i++)
{
arr[i - 1] = Power3(i) * (uint)((Math.Floor((i + 1) / 3.0) + 24) / 50.0);
}
for (; i < 36; i++)
{
arr[i - 1] = Power3(i) * (i + 14) / 50;
}
for (; i < maxLevel + 1; i++)
{
arr[i - 1] = Power3(i) * (i / 2 + 32) / 50;
}
return new LookupGrowthRate(arr);
}
}
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using PkmnLibSharp.FFI; using PkmnLibSharp.FFI;
using PkmnLibSharp.Utils; using PkmnLibSharp.Utils;
@ -29,7 +30,7 @@ namespace PkmnLibSharp.StaticData.Libraries
return GetValueByKey(key) != null; return GetValueByKey(key) != null;
} }
public bool TryGetValue(string key, out T value) public bool TryGetValue(string key, [NotNullWhen(true)] out T value)
{ {
if (Cache.ValueCache.TryGetValue(key, out value) && value != null) if (Cache.ValueCache.TryGetValue(key, out value) && value != null)
return true; return true;

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using JetBrains.Annotations; using JetBrains.Annotations;
using PkmnLibSharp.FFI; using PkmnLibSharp.FFI;
using PkmnLibSharp.Utils; using PkmnLibSharp.Utils;
@ -21,8 +22,11 @@ namespace PkmnLibSharp.StaticData.Libraries
Interface.growth_rate_library_calculate_level(Ptr, growthRate.ToPtr(), experience); Interface.growth_rate_library_calculate_level(Ptr, growthRate.ToPtr(), experience);
[MustUseReturnValue] [MustUseReturnValue]
public uint CalculateExperience(string growthRate, LevelInt level) => public uint CalculateExperience(string growthRate, LevelInt level)
Interface.growth_rate_library_calculate_experience(Ptr, growthRate.ToPtr(), level); {
Debug.Assert(level >= 1);
return Interface.growth_rate_library_calculate_experience(Ptr, growthRate.ToPtr(), level);
}
public void AddGrowthRate(string name, GrowthRate growthRate) => public void AddGrowthRate(string name, GrowthRate growthRate) =>
Interface.growth_rate_library_add_growth_rate(Ptr, name.ToPtr(), growthRate.TakeOwnershipAndInvalidate()); Interface.growth_rate_library_add_growth_rate(Ptr, name.ToPtr(), growthRate.TakeOwnershipAndInvalidate());

View File

@ -1,27 +1,49 @@
using System; using System.Diagnostics;
using PkmnLibSharp.FFI; using PkmnLibSharp.FFI;
using PkmnLibSharp.Utils; using PkmnLibSharp.Utils;
using Interface = PkmnLibSharp.FFI.StaticData.Libraries.LibrarySettings; using Interface = PkmnLibSharp.FFI.StaticData.Libraries.LibrarySettings;
namespace PkmnLibSharp.StaticData.Libraries namespace PkmnLibSharp.StaticData.Libraries
{ {
/// <summary>
/// This library holds several misc settings for the library.
/// </summary>
public class LibrarySettings : ExternPointer<LibrarySettings.CacheData> public class LibrarySettings : ExternPointer<LibrarySettings.CacheData>
{ {
public class CacheData public class CacheData
{ {
public LevelInt? MaxLevel { get; internal set; } public LevelInt? MaxLevel { get; internal set; }
public uint? ShinyRate { get; internal set; }
} }
public LibrarySettings(LevelInt maxLevel) : base(Interface.library_settings_new(maxLevel), true) /// <inheritdoc cref="LibrarySettings"/>
/// <param name="maxLevel">The highest level a Pokemon can be.</param>
/// <param name="shinyRate">
/// The chance of a Pokemon being shiny, as the denominator of a fraction, where the nominator
/// is 1. For example, if this is 1000, then the chance of a Pokemon being shiny is 1/1000.
/// </param>
public LibrarySettings(LevelInt maxLevel, uint shinyRate)
{ {
Debug.Assert(maxLevel >= 1);
Debug.Assert(shinyRate >= 1);
InitializePointer(Interface.library_settings_new(maxLevel, shinyRate), true);
} }
internal LibrarySettings(IdentifiablePointer ptr, bool isOwner) : base(ptr, isOwner) internal LibrarySettings(IdentifiablePointer ptr, bool isOwner) : base(ptr, isOwner)
{ {
} }
/// <summary>
/// The highest level a Pokemon can be.
/// </summary>
public LevelInt MaxLevel => Cache.MaxLevel ??= Interface.library_settings_maximum_level(Ptr); public LevelInt MaxLevel => Cache.MaxLevel ??= Interface.library_settings_maximum_level(Ptr);
/// <summary>
/// The chance of a Pokemon being shiny, as the denominator of a fraction, where the nominator
/// is 1. For example, if this is 1000, then the chance of a Pokemon being shiny is 1/1000.
/// </summary>
public uint ShinyRate => Cache.ShinyRate ??= Interface.library_settings_shiny_rate(Ptr);
protected override CacheData CreateCache() => new(); protected override CacheData CreateCache() => new();
protected override void Destructor() => Interface.library_settings_drop(Ptr); protected override void Destructor() => Interface.library_settings_drop(Ptr);

View File

@ -34,6 +34,12 @@ namespace PkmnLibSharp.StaticData.Libraries
return nature; return nature;
} }
public Nature GetRandomNature(ulong seed)
{
return new Nature(Interface.nature_library_get_random_nature(Ptr, seed));
}
public string GetNatureName(Nature nature) public string GetNatureName(Nature nature)
{ {
var fd = Cache.Natures.FirstOrDefault(x => x.Value == nature); var fd = Cache.Natures.FirstOrDefault(x => x.Value == nature);

View File

@ -1,4 +1,5 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using PkmnLibSharp.FFI;
using PkmnLibSharp.Utils; using PkmnLibSharp.Utils;
using Interface = PkmnLibSharp.FFI.StaticData.Libraries.StaticData; using Interface = PkmnLibSharp.FFI.StaticData.Libraries.StaticData;
@ -18,6 +19,8 @@ namespace PkmnLibSharp.StaticData.Libraries
public AbilityLibrary? AbilityLibrary { get; internal set; } public AbilityLibrary? AbilityLibrary { get; internal set; }
} }
internal StaticData(IdentifiablePointer ptr, bool isOwner) : base(ptr, isOwner){}
public StaticData(LibrarySettings settings, SpeciesLibrary speciesLibrary, MoveLibrary moveLibrary, public StaticData(LibrarySettings settings, SpeciesLibrary speciesLibrary, MoveLibrary moveLibrary,
ItemLibrary itemLibrary, GrowthRateLibrary growthRateLibrary, TypeLibrary typeLibrary, ItemLibrary itemLibrary, GrowthRateLibrary growthRateLibrary, TypeLibrary typeLibrary,
NatureLibrary natureLibrary, AbilityLibrary abilityLibrary) : base( NatureLibrary natureLibrary, AbilityLibrary abilityLibrary) : base(
@ -42,16 +45,16 @@ namespace PkmnLibSharp.StaticData.Libraries
Cache.ItemLibrary ??= new ItemLibrary(Interface.static_data_items(Ptr), false); Cache.ItemLibrary ??= new ItemLibrary(Interface.static_data_items(Ptr), false);
public GrowthRateLibrary GrowthRateLibrary => public GrowthRateLibrary GrowthRateLibrary =>
Cache.GrowthRateLibrary ??= new GrowthRateLibrary(Interface.static_data_items(Ptr), false); Cache.GrowthRateLibrary ??= new GrowthRateLibrary(Interface.static_data_growth_rates(Ptr), false);
public TypeLibrary TypeLibrary => public TypeLibrary TypeLibrary =>
Cache.TypeLibrary ??= new TypeLibrary(Interface.static_data_items(Ptr), false); Cache.TypeLibrary ??= new TypeLibrary(Interface.static_data_types(Ptr), false);
public NatureLibrary NatureLibrary => public NatureLibrary NatureLibrary =>
Cache.NatureLibrary ??= new NatureLibrary(Interface.static_data_items(Ptr), false); Cache.NatureLibrary ??= new NatureLibrary(Interface.static_data_natures(Ptr), false);
public AbilityLibrary AbilityLibrary => public AbilityLibrary AbilityLibrary =>
Cache.AbilityLibrary ??= new AbilityLibrary(Interface.static_data_items(Ptr), false); Cache.AbilityLibrary ??= new AbilityLibrary(Interface.static_data_abilities(Ptr), false);
protected override CacheData CreateCache() => new(); protected override CacheData CreateCache() => new();

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using PkmnLibSharp.FFI; using PkmnLibSharp.FFI;
using PkmnLibSharp.Utils; using PkmnLibSharp.Utils;
@ -38,7 +39,16 @@ namespace PkmnLibSharp.StaticData
public string GrowthRate => Cache.GrowthRate ??= Interface.species_growth_rate(Ptr).PtrString()!; public string GrowthRate => Cache.GrowthRate ??= Interface.species_growth_rate(Ptr).PtrString()!;
public byte CaptureRate => Cache.CaptureRate ??= Interface.species_capture_rate(Ptr); public byte CaptureRate => Cache.CaptureRate ??= Interface.species_capture_rate(Ptr);
public bool TryGetForm(string formName, out Form? form) public Form DefaultForm
{
get
{
TryGetForm("default", out var form);
return form!;
}
}
public bool TryGetForm(string formName, [NotNullWhen(true)] out Form? form)
{ {
if (Cache.Forms.TryGetValue(formName, out form)) if (Cache.Forms.TryGetValue(formName, out form))
return true; return true;
@ -57,6 +67,8 @@ namespace PkmnLibSharp.StaticData
public void AddForm(Form form) => public void AddForm(Form form) =>
Interface.species_add_form(Ptr, form.Name.ToPtr(), form.TakeOwnershipAndInvalidate()); Interface.species_add_form(Ptr, form.Name.ToPtr(), form.TakeOwnershipAndInvalidate());
public Gender GetRandomGender(ulong seed) => Interface.species_get_random_gender(Ptr, seed);
protected override CacheData CreateCache() => new CacheData(); protected override CacheData CreateCache() => new CacheData();
protected override void Destructor() => Interface.species_drop(Ptr); protected override void Destructor() => Interface.species_drop(Ptr);

View File

@ -94,5 +94,40 @@ namespace PkmnLibSharp.Utils
_isInvalidated = true; _isInvalidated = true;
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public override bool Equals(object obj)
{
if (obj is ExternPointer<TCache> other)
{
return Identifier == other.Identifier;
}
return false;
}
protected bool Equals(ExternPointer<TCache> other)
{
if (Identifier != 0)
{
return Identifier == other.Identifier;
}
return Ptr == other.Ptr;
}
public override int GetHashCode()
{
if (Identifier != 0)
return (int)Identifier;
return (int)Ptr;
}
public static bool operator ==(ExternPointer<TCache>? left, ExternPointer<TCache>? right)
{
return Equals(left, right);
}
public static bool operator !=(ExternPointer<TCache>? left, ExternPointer<TCache>? right)
{
return !Equals(left, right);
}
} }
} }

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace PkmnLibSharp.Utils
{
internal static class MiscExtensions
{
internal static int IndexOf<T>(this IReadOnlyList<T> list, T element)
{
for (int i = 0; i < list.Count; i++)
{
if (Equals(list[i], element))
{
return i;
}
}
return -1;
}
}
}

View File

@ -0,0 +1,208 @@
// https://github.com/dotnet/runtime/blob/527f9ae88a0ee216b44d556f9bdc84037fe0ebda/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
#pragma warning disable
#define INTERNAL_NULLABLE_ATTRIBUTES
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Diagnostics.CodeAnalysis
{
#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class AllowNullAttribute : Attribute
{ }
/// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class DisallowNullAttribute : Attribute
{ }
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class MaybeNullAttribute : Attribute
{ }
/// <summary>Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class NotNullAttribute : Attribute
{ }
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class MaybeNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter may be null.
/// </param>
public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class NotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class NotNullIfNotNullAttribute : Attribute
{
/// <summary>Initializes the attribute with the associated parameter name.</summary>
/// <param name="parameterName">
/// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
/// </param>
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
/// <summary>Gets the associated parameter name.</summary>
public string ParameterName { get; }
}
/// <summary>Applied to a method that will never return under any circumstance.</summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class DoesNotReturnAttribute : Attribute
{ }
/// <summary>Specifies that the method will not return if the associated Boolean parameter is passed the specified value.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class DoesNotReturnIfAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified parameter value.</summary>
/// <param name="parameterValue">
/// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to
/// the associated parameter matches this value.
/// </param>
public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;
/// <summary>Gets the condition parameter value.</summary>
public bool ParameterValue { get; }
}
#endif
#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 || NETCOREAPP3_1 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48
/// <summary>Specifies that the method or property will ensure that the listed field and property members have not-null values.</summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class MemberNotNullAttribute : Attribute
{
/// <summary>Initializes the attribute with a field or property member.</summary>
/// <param name="member">
/// The field or property member that is promised to be not-null.
/// </param>
public MemberNotNullAttribute(string member) => Members = new[] { member };
/// <summary>Initializes the attribute with the list of field and property members.</summary>
/// <param name="members">
/// The list of field and property members that are promised to be not-null.
/// </param>
public MemberNotNullAttribute(params string[] members) => Members = members;
/// <summary>Gets field or property member names.</summary>
public string[] Members { get; }
}
/// <summary>Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition.</summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class MemberNotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition and a field or property member.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
/// <param name="member">
/// The field or property member that is promised to be not-null.
/// </param>
public MemberNotNullWhenAttribute(bool returnValue, string member)
{
ReturnValue = returnValue;
Members = new[] { member };
}
/// <summary>Initializes the attribute with the specified return value condition and list of field and property members.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
/// <param name="members">
/// The list of field and property members that are promised to be not-null.
/// </param>
public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
{
ReturnValue = returnValue;
Members = members;
}
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
/// <summary>Gets field or property member names.</summary>
public string[] Members { get; }
}
#endif
}

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

Binary file not shown.

View File

@ -0,0 +1,130 @@
using System;
using NUnit.Framework;
using PkmnLibSharp.DynamicData;
using PkmnLibSharp.DynamicData.Libraries;
using PkmnLibSharp.StaticData;
using PkmnLibSharp.StaticData.Libraries;
namespace PkmnLibRSharpTests.DynamicData
{
public class PokemonTests
{
[Test]
public void Pokemon_GetLibrary()
{
using var library = GetLibrary();
using var pokemon = new PokemonBuilder(library, "testSpecies", 100).Build();
Assert.AreEqual(library, pokemon.Library);
}
[Test]
public void Pokemon_GetSpecies()
{
using var library = GetLibrary();
using var pokemon = new PokemonBuilder(library, "testSpecies", 100).Build();
Assert.AreEqual(library.StaticData.SpeciesLibrary["testSpecies"], pokemon.Species);
}
[Test]
public void Pokemon_GetForm()
{
using var library = GetLibrary();
using var pokemon = new PokemonBuilder(library, "testSpecies", 100).Build();
Assert.AreEqual(library.StaticData.SpeciesLibrary["testSpecies"].DefaultForm, pokemon.Form);
}
[Test]
public void Pokemon_GetDisplaySpecies()
{
using var library = GetLibrary();
using var pokemon = new PokemonBuilder(library, "testSpecies", 100).Build();
Assert.AreEqual(library.StaticData.SpeciesLibrary["testSpecies"], pokemon.Species);
}
[Test]
public void Pokemon_GetDisplayForm()
{
using var library = GetLibrary();
using var pokemon = new PokemonBuilder(library, "testSpecies", 100).Build();
Assert.AreEqual(library.StaticData.SpeciesLibrary["testSpecies"].DefaultForm, pokemon.Form);
}
[Test]
public void Pokemon_GetLevel()
{
using var library = GetLibrary();
using var pokemon = new PokemonBuilder(library, "testSpecies", 100).Build();
Assert.AreEqual(100, pokemon.Level);
}
[Test]
public void Pokemon_GetExperience()
{
using var library = GetLibrary();
using var pokemon = new PokemonBuilder(library, "testSpecies", 100).Build();
Assert.AreEqual(library.StaticData.GrowthRateLibrary.CalculateExperience("testGrowthrate", 100),
pokemon.Experience);
}
[Test]
public void Pokemon_GetUniqueIdentifier()
{
using var library = GetLibrary();
using var pokemon = new PokemonBuilder(library, "testSpecies", 100).WithIdentifier(1000).Build();
Assert.AreEqual(1000, pokemon.UniqueIdentifier);
}
[Test]
public void Pokemon_GetGender()
{
using var library = GetLibrary();
using var pokemon = new PokemonBuilder(library, "testSpecies", 100).WithGender(Gender.Male).Build();
Assert.AreEqual(Gender.Male, pokemon.Gender);
}
[Test]
public void Pokemon_GetColoring()
{
using var library = GetLibrary();
using var pokemon = new PokemonBuilder(library, "testSpecies", 100).ForceShiny(true).Build();
Assert.AreEqual(1, pokemon.Coloring);
}
private static DynamicLibrary GetLibrary()
{
using var settings = new LibrarySettings(100, 4096);
using var speciesLibrary = new SpeciesLibrary(0);
FillSpeciesLibrary(speciesLibrary);
using var moves = new MoveLibrary(0);
using var items = new ItemLibrary(0);
using var growthRates = new GrowthRateLibrary(0);
using var gr = CommonGrowthRates.Erratic(100);
growthRates.AddGrowthRate("testGrowthrate", gr);
using var types = new TypeLibrary(0);
using var natures = new NatureLibrary(0);
natures.LoadNature("testNature", Nature.NeutralNature());
using var abilities = new AbilityLibrary(0);
using var library = new PkmnLibSharp.StaticData.Libraries.StaticData(settings, speciesLibrary, moves, items,
growthRates, types, natures, abilities);
using var statCalc = new Gen7BattleStatCalculator();
using var damageLib = new Gen7DamageLibrary(false);
using var miscLib = new Gen7MiscLibrary();
using var scriptResolver = new WasmScriptResolver();
return new DynamicLibrary(library, statCalc, damageLib, miscLib, scriptResolver);
}
private static void FillSpeciesLibrary(SpeciesLibrary speciesLibrary)
{
using var stats = new StaticStatisticSet<short>(5, 10, 30, 20, 2, 0);
using var moves = new LearnableMoves();
using var form = new Form("foobar", 0.2f, 5.8f, 300, new TypeIdentifier[] { new(1), new(2) }, stats,
new[] { "foo", "bar" }, new[] { "set" }, moves, Array.Empty<string>());
using var species =
new Species(10, "testSpecies", 0.2f, "testGrowthrate", 120, form, Array.Empty<string>());
speciesLibrary.Add(species.Name, species);
}
}
}

View File

@ -0,0 +1,94 @@
using NUnit.Framework;
using PkmnLibSharp.StaticData;
namespace PkmnLibRSharpTests.StaticData.Libraries
{
public class CommonGrowthRateTests
{
[TestCase(1, 0)]
[TestCase(20, 12_800)]
[TestCase(40, 76_800)]
[TestCase(60, 194_400)]
[TestCase(68, 257_834)]
[TestCase(70, 276_458)]
[TestCase(80, 378_880)]
[TestCase(90, 491_346)]
[TestCase(98, 583_539)]
[TestCase(100, 600_000)]
public void ErraticExperience(byte level, int experience)
{
using var growthRate = CommonGrowthRates.Erratic(100);
Assert.AreEqual(experience, growthRate.CalculateExperience(level));
}
[TestCase(1, 0)]
[TestCase(20, 6_400)]
[TestCase(40, 51_200)]
[TestCase(60, 172_800)]
[TestCase(70, 274_400)]
[TestCase(80, 409_600)]
[TestCase(90, 583_200)]
[TestCase(100, 800_000)]
public void FastExperience(byte level, int experience)
{
using var growthRate = CommonGrowthRates.Fast(100);
Assert.AreEqual(experience, growthRate.CalculateExperience(level));
}
[TestCase(1, 0)]
[TestCase(20, 8_000)]
[TestCase(40, 64_000)]
[TestCase(60, 216_000)]
[TestCase(70, 343_000)]
[TestCase(80, 512_000)]
[TestCase(90, 729_000)]
[TestCase(100, 1_000_000)]
public void MediumFastExperience(byte level, int experience)
{
using var growthRate = CommonGrowthRates.MediumFast(100);
Assert.AreEqual(experience, growthRate.CalculateExperience(level));
}
[TestCase(1, 0)]
[TestCase(20, 5_460)]
[TestCase(40, 56_660)]
[TestCase(60, 211_060)]
[TestCase(70, 344_960)]
[TestCase(80, 526_260)]
[TestCase(90, 762_160)]
[TestCase(100, 1_059_860)]
public void MediumSlowExperience(byte level, int experience)
{
using var growthRate = CommonGrowthRates.MediumSlow(100);
Assert.AreEqual(experience, growthRate.CalculateExperience(level));
}
[TestCase(1, 0)]
[TestCase(20, 10_000)]
[TestCase(40, 80_000)]
[TestCase(60, 270_000)]
[TestCase(70, 428_750)]
[TestCase(80, 640_000)]
[TestCase(90, 911_250)]
[TestCase(100, 1_250_000)]
public void SlowExperience(byte level, int experience)
{
using var growthRate = CommonGrowthRates.Slow(100);
Assert.AreEqual(experience, growthRate.CalculateExperience(level));
}
[TestCase(1, 0)]
[TestCase(20, 5_440)]
[TestCase(40, 66_560)]
[TestCase(60, 267_840)]
[TestCase(70, 459_620)]
[TestCase(80, 737_280)]
[TestCase(90, 1_122_660)]
[TestCase(100, 1_640_000)]
public void FluctuatingExperience(byte level, int experience)
{
using var growthRate = CommonGrowthRates.Fluctuating(100);
Assert.AreEqual(experience, growthRate.CalculateExperience(level));
}
}
}

View File

@ -8,13 +8,13 @@ namespace PkmnLibRSharpTests.StaticData.Libraries
[Test] [Test]
public void CreateLibrarySettings() public void CreateLibrarySettings()
{ {
using var settings = new LibrarySettings(100); using var settings = new LibrarySettings(100, 4096);
} }
[Test] [Test]
public void GetMaxLevel() public void GetMaxLevel()
{ {
using var settings = new LibrarySettings(100); using var settings = new LibrarySettings(100, 4096);
Assert.AreEqual(100, settings.MaxLevel); Assert.AreEqual(100, settings.MaxLevel);
} }
} }

View File

@ -7,7 +7,7 @@ namespace PkmnLibRSharpTests.StaticData.Libraries
{ {
private PkmnLibSharp.StaticData.Libraries.StaticData Build() private PkmnLibSharp.StaticData.Libraries.StaticData Build()
{ {
using var settings = new LibrarySettings(100); using var settings = new LibrarySettings(100, 4096);
using var species = new SpeciesLibrary(0); using var species = new SpeciesLibrary(0);
using var moves = new MoveLibrary(0); using var moves = new MoveLibrary(0);
using var items = new ItemLibrary(0); using var items = new ItemLibrary(0);