Further work on static side

This commit is contained in:
Deukhoofd 2024-07-20 16:12:39 +02:00
parent 3845f91601
commit 1b501dee7e
29 changed files with 670 additions and 155 deletions

View File

@ -5,13 +5,8 @@ namespace PkmnLib.Static;
/// <summary>
/// A growth rate defines how much experience is required per level.
/// </summary>
public interface IGrowthRate
public interface IGrowthRate : INamedValue
{
/// <summary>
/// The name of the growth rate.
/// </summary>
public StringKey Name { get; }
///<summary>
/// Calculate the level something with this growth rate would have at a certain experience.
/// </summary>

View File

@ -14,7 +14,7 @@ public enum ItemCategory
MiscItem,
/// <summary>
/// Pokeballs are used for capturing Pokemons.
/// Pokeballs are used for capturing Pokémons.
/// </summary>
Pokeball,
@ -60,17 +60,17 @@ public enum BattleItemCategory
None,
/// <summary>
/// This item is used for healing Pokemon.
/// This item is used for healing Pokémon.
/// </summary>
Healing,
/// <summary>
/// This item is used for healing Pokemon from a status.
/// This item is used for healing Pokémon from a status.
/// </summary>
StatusHealing,
/// <summary>
/// This item is used for capturing Pokemon.
/// This item is used for capturing Pokémon.
/// </summary>
Pokeball,
@ -83,13 +83,8 @@ public enum BattleItemCategory
/// <summary>
/// An item is an object which the player can pick up, keep in their Bag, and use in some manner.
/// </summary>
public interface IItem
public interface IItem : INamedValue
{
/// <summary>
/// The name of the item.
/// </summary>
StringKey Name { get; }
/// <summary>
/// Which bag slot items are stored in.
/// </summary>

View File

@ -0,0 +1,34 @@
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Static.Species;
using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Libraries;
/// <summary>
/// The library for all abilities in the game.
/// </summary>
public interface IReadOnlyAbilityLibrary
{
/// <summary>
/// Tries to get an ability from the library. Returns false if the ability is not found.
/// </summary>
bool TryGet(StringKey key, [MaybeNullWhen(false)] out IAbility value);
/// <summary>
/// Gets a random ability from the library.
/// </summary>
IAbility GetRandom(IRandom random);
/// <summary>
/// The amount of abilities in the library.
/// </summary>
int Count { get; }
/// <summary>
/// Whether the library is empty.
/// </summary>
bool IsEmpty { get; }
}
/// <inheritdoc cref="IReadOnlyAbilityLibrary"/>
public class AbilityLibrary : DataLibrary<IAbility>, IReadOnlyAbilityLibrary;

View File

@ -0,0 +1,52 @@
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Libraries;
/// <summary>
/// A basic library for data types. Stores data both by name and by index.
/// </summary>
public abstract class DataLibrary<T>
where T : INamedValue
{
private readonly Dictionary<StringKey, T> _data = new();
private readonly List<T> _values = [];
/// <summary>
/// Adds a value to the library.
/// </summary>
public void Add(T value)
{
_data.Add(value.Name, value);
_values.Add(value);
}
/// <summary>
/// Removes a value from the library.
/// </summary>
public void Remove(T value)
{
_data.Remove(value.Name);
_values.Remove(value);
}
/// <summary>
/// Try to get a value from the library. Returns false if the value is not found.
/// </summary>
public bool TryGet(StringKey key, [MaybeNullWhen(false)] out T value) => _data.TryGetValue(key, out value);
/// <summary>
/// Get a random value from the library.
/// </summary>
public T GetRandom(IRandom random) => _values[random.GetInt(_values.Count)];
/// <summary>
/// The number of values in the library.
/// </summary>
public int Count => _values.Count;
/// <summary>
/// Whether the library is empty.
/// </summary>
public bool IsEmpty => _values.Count == 0;
}

View File

@ -0,0 +1,30 @@
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Libraries;
/// <summary>
/// The library for all growth rates in the game.
/// </summary>
public interface IReadOnlyGrowthRateLibrary
{
/// <summary>
/// Tries to get a growth rate from the library. Returns false if the growth rate is not found.
/// </summary>
bool TryGet(StringKey key, [MaybeNullWhen(false)] out IGrowthRate value);
/// <summary>
/// Gets a random growth rate from the library.
/// </summary>
IGrowthRate GetRandom(IRandom random);
/// <summary>
/// Gets the amount of growth rates in the library.
/// </summary>
int Count { get; }
/// <summary>
/// Whether the library is empty.
/// </summary>
bool IsEmpty { get; }
}
/// <inheritdoc cref="IReadOnlyGrowthRateLibrary"/>
public class GrowthRateLibrary : DataLibrary<IGrowthRate>, IReadOnlyGrowthRateLibrary;

View File

@ -0,0 +1,33 @@
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Libraries;
/// <summary>
/// The library for all items in the game.
/// </summary>
public interface IReadOnlyItemLibrary
{
/// <summary>
/// Tries to get an item from the library. Returns false if the item is not found.
/// </summary>
bool TryGet(StringKey key, [MaybeNullWhen(false)] out IItem value);
/// <summary>
/// Gets a random item from the library.
/// </summary>
IItem GetRandom(IRandom random);
/// <summary>
/// The amount of items in the library.
/// </summary>
int Count { get; }
/// <summary>
/// Whether the library is empty.
/// </summary>
bool IsEmpty { get; }
}
/// <inheritdoc cref="IReadOnlyItemLibrary"/>
public class ItemLibrary : DataLibrary<IItem>, IReadOnlyItemLibrary;

View File

@ -0,0 +1,18 @@
namespace PkmnLib.Static.Libraries;
/// <summary>
/// General settings for the library.
/// </summary>
public record LibrarySettings
{
/// <summary>
/// The maximum level a Pokémon can reach.
/// </summary>
public required LevelInt MaxLevel { get; init; }
/// <summary>
/// The chance of a Pokémon being shiny, as the denominator of a fraction, where the nominator
/// is 1. For example, if this is 1000, then the chance of a Pokémon being shiny is 1/1000.
/// </summary>
public required uint ShinyRate { get; init; }
}

View File

@ -0,0 +1,31 @@
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Libraries;
/// <summary>
/// The library for all moves in the game.
/// </summary>
public interface IReadOnlyMoveLibrary
{
/// <summary>
/// Tries to get a move from the library. Returns false if the move is not found.
/// </summary>
bool TryGet(StringKey key, [MaybeNullWhen(false)] out IMoveData value);
/// <summary>
/// Gets a random move from the library.
/// </summary>
IMoveData GetRandom(IRandom random);
/// <summary>
/// The amount of moves in the library.
/// </summary>
int Count { get; }
/// <summary>
/// Whether the library is empty.
/// </summary>
bool IsEmpty { get; }
}
/// <inheritdoc cref="IReadOnlyMoveLibrary"/>
public class MoveLibrary : DataLibrary<IMoveData>, IReadOnlyMoveLibrary;

View File

@ -0,0 +1,33 @@
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Libraries;
/// <summary>
/// The library for all natures in the game.
/// </summary>
public interface IReadOnlyNatureLibrary
{
/// <summary>
/// Tries to get a nature from the library. Returns false if the nature is not found.
/// </summary>
bool TryGet(StringKey key, [MaybeNullWhen(false)] out INature value);
/// <summary>
/// Gets a random nature from the library.
/// </summary>
INature GetRandom(IRandom random);
/// <summary>
/// The amount of natures in the library.
/// </summary>
int Count { get; }
/// <summary>
/// Whether the library is empty.
/// </summary>
bool IsEmpty { get; }
}
/// <inheritdoc cref="IReadOnlyNatureLibrary"/>
public class NatureLibrary : DataLibrary<INature>, IReadOnlyNatureLibrary;

View File

@ -0,0 +1,34 @@
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Static.Species;
using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Libraries;
/// <summary>
/// The library for all species in the game.
/// </summary>
public interface IReadOnlySpeciesLibrary
{
/// <summary>
/// Tries to get a species from the library. Returns false if the species is not found.
/// </summary>
bool TryGet(StringKey key, [MaybeNullWhen(false)] out ISpecies value);
/// <summary>
/// Gets a random species from the library.
/// </summary>
ISpecies GetRandom(IRandom random);
/// <summary>
/// The amount of species in the library.
/// </summary>
int Count { get; }
/// <summary>
/// Whether the library is empty.
/// </summary>
bool IsEmpty { get; }
}
/// <inheritdoc cref="IReadOnlySpeciesLibrary"/>
public class SpeciesLibrary : DataLibrary<ISpecies>, IReadOnlySpeciesLibrary;

View File

@ -0,0 +1,83 @@
namespace PkmnLib.Static.Libraries;
/// <summary>
/// The storage for all different static libraries.
/// </summary>
public interface IStaticLibrary
{
/// <summary>
/// The miscellaneous settings for the library.
/// </summary>
LibrarySettings Settings { get; }
/// <summary>
/// All data for Pokémon species.
/// </summary>
IReadOnlySpeciesLibrary Species { get; }
/// <summary>
/// All data for Pokémon moves.
/// </summary>
IReadOnlyMoveLibrary Moves { get; }
/// <summary>
/// All data for Pokémon abilities.
/// </summary>
IReadOnlyAbilityLibrary Abilities { get; }
/// <summary>
/// All data for Pokémon types and their effectiveness.
/// </summary>
IReadOnlyTypeLibrary Types { get; }
/// <summary>
/// All data for Pokémon natures.
/// </summary>
IReadOnlyNatureLibrary Natures { get; }
/// <summary>
/// All data for Pokémon growth rates.
/// </summary>
IReadOnlyGrowthRateLibrary GrowthRates { get; }
/// <summary>
/// All data for Pokémon items.
/// </summary>
IReadOnlyItemLibrary Items { get; }
}
/// <inheritdoc />
public class StaticLibraryImpl : IStaticLibrary
{
/// <inheritdoc cref="StaticLibraryImpl" />
public StaticLibraryImpl(LibrarySettings settings, IReadOnlySpeciesLibrary species, IReadOnlyMoveLibrary moves, IReadOnlyAbilityLibrary abilities, IReadOnlyTypeLibrary types, IReadOnlyNatureLibrary natures, IReadOnlyGrowthRateLibrary growthRates, IReadOnlyItemLibrary items)
{
Settings = settings;
Species = species;
Moves = moves;
Abilities = abilities;
Types = types;
Natures = natures;
GrowthRates = growthRates;
Items = items;
}
/// <inheritdoc />
public LibrarySettings Settings { get; }
/// <inheritdoc />
public IReadOnlySpeciesLibrary Species { get; }
/// <inheritdoc />
public IReadOnlyMoveLibrary Moves { get; }
/// <inheritdoc />
public IReadOnlyAbilityLibrary Abilities { get; }
/// <inheritdoc />
public IReadOnlyTypeLibrary Types { get; }
/// <inheritdoc />
public IReadOnlyNatureLibrary Natures { get; }
/// <inheritdoc />
public IReadOnlyGrowthRateLibrary GrowthRates { get; }
/// <inheritdoc />
public IReadOnlyItemLibrary Items { get; }
}

View File

@ -0,0 +1,99 @@
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Libraries;
/// <summary>
/// All data related to types and their effectiveness.
/// </summary>
public interface IReadOnlyTypeLibrary
{
/// <summary>
/// Gets the type identifier for a type with a name.
/// </summary>
bool TryGetTypeIdentifier(StringKey key, out TypeIdentifier typeIdentifier);
/// <summary>
/// Gets the type name from the type identifier.
/// </summary>
bool TryGetTypeName(TypeIdentifier t, [NotNullWhen(true)] out StringKey? stringKey);
/// <summary>
/// Gets the effectiveness for a single attacking type against a single defending type.
/// </summary>
float GetSingleEffectiveness(TypeIdentifier attacking, TypeIdentifier defending);
/// <summary>
/// Gets the effectiveness for a single attacking type against an amount of defending types.
/// This is equivalent to running get_single_effectiveness on each defending type, and multiplying the results with each other.
/// </summary>
float GetEffectiveness(TypeIdentifier attacking, TypeIdentifier[] defending);
}
/// <inheritdoc />
public class TypeLibrary : IReadOnlyTypeLibrary
{
private readonly Dictionary<StringKey, TypeIdentifier> _types = new();
private readonly List<List<float>> _effectiveness = new();
/// <inheritdoc />
public bool TryGetTypeIdentifier(StringKey key, out TypeIdentifier type) => _types.TryGetValue(key, out type);
/// <inheritdoc />
public bool TryGetTypeName(TypeIdentifier t, [NotNullWhen(true)] out StringKey? stringKey)
{
foreach (var (key, value) in _types)
{
if (value == t)
{
stringKey = key;
return true;
}
}
stringKey = default;
return false;
}
/// <inheritdoc />
public float GetSingleEffectiveness(TypeIdentifier attacking, TypeIdentifier defending)
{
if (attacking.Value < 1 || attacking.Value > _effectiveness.Count)
throw new ArgumentOutOfRangeException(nameof(attacking));
if (defending.Value < 1 || defending.Value > _effectiveness.Count)
throw new ArgumentOutOfRangeException(nameof(defending));
return _effectiveness[attacking.Value - 1][defending.Value - 1];
}
/// <inheritdoc />
public float GetEffectiveness(TypeIdentifier attacking, TypeIdentifier[] defending) =>
defending.Aggregate<TypeIdentifier, float>(1,
(current, type) => current * GetSingleEffectiveness(attacking, type));
/// <summary>
/// Registers a new type in the library.
/// </summary>
public TypeIdentifier RegisterType(StringKey name)
{
var id = new TypeIdentifier((byte)( _types.Count + 1));
_types.Add(name, id);
_effectiveness.Add(new List<float>(_types.Count));
foreach (var list in _effectiveness)
{
list.Add(1);
}
return id;
}
/// <summary>
/// Sets the effectiveness for an attacking type against a defending type.
/// </summary>
public void SetEffectiveness(TypeIdentifier attacking, TypeIdentifier defending, float effectiveness)
{
if (attacking.Value < 1 || attacking.Value > _effectiveness.Count)
throw new ArgumentOutOfRangeException(nameof(attacking));
if (defending.Value < 1 || defending.Value > _effectiveness.Count)
throw new ArgumentOutOfRangeException(nameof(defending));
_effectiveness[attacking.Value - 1][defending.Value - 1] = effectiveness;
}
}

View File

@ -30,25 +30,25 @@ public enum MoveCategory
public enum MoveTarget
{
/// <summary>
/// Adjacent allows a move to target any Pokemon that is either directly to the left or right of
/// Adjacent allows a move to target any Pokémon that is either directly to the left or right of
/// the user, opposed to the user, or left or right of the slot that is opposing the user.
/// </summary>
Adjacent = 0,
/// <summary>
/// AdjacentAlly allows a move to target any Pokemon that is directly to the left or right of
/// AdjacentAlly allows a move to target any Pokémon that is directly to the left or right of
/// the user.
/// </summary>
AdjacentAlly,
/// <summary>
/// AdjacentAllySelf allows a move to target any Pokemon that is either directly to the left or
/// AdjacentAllySelf allows a move to target any Pokémon that is either directly to the left or
/// right of the user, or the user itself.
/// </summary>
AdjacentAllySelf,
/// <summary>
/// AdjacentOpponent allows a move to target any Pokemon that is either the opponent, or directly
/// AdjacentOpponent allows a move to target any Pokémon that is either the opponent, or directly
/// to the left or right of it.
/// </summary>
AdjacentOpponent,
@ -69,22 +69,22 @@ public enum MoveTarget
AllAdjacentOpponent,
/// <summary>
/// AllAlly targets all Pokemon on the same side as the user.
/// AllAlly targets all Pokémon on the same side as the user.
/// </summary>
AllAlly,
/// <summary>
/// AllOpponent targets all Pokemon on an opposing side from the user.
/// AllOpponent targets all Pokémon on an opposing side from the user.
/// </summary>
AllOpponent,
/// <summary>
/// Any allows a move to target a single Pokemon, in any position.
/// Any allows a move to target a single Pokémon, in any position.
/// </summary>
Any,
/// <summary>
/// RandomOpponent allows a move to target a single Pokemon, in a random position.
/// RandomOpponent allows a move to target a single Pokémon, in a random position.
/// </summary>
RandomOpponent,
@ -97,13 +97,8 @@ public enum MoveTarget
/// <summary>
/// A move is the skill Pokémon primarily use in battle. This is the data related to that.
/// </summary>
public interface IMoveData
public interface IMoveData : INamedValue
{
/// <summary>
/// The name of the move.
/// </summary>
string Name { get; }
/// <summary>
/// The attacking type of the move.
/// </summary>
@ -154,7 +149,7 @@ public interface IMoveData
public class MoveDataImpl : IMoveData
{
/// <inheritdoc cref="MoveDataImpl" />
public MoveDataImpl(string name, TypeIdentifier moveType, MoveCategory category, byte basePower, byte accuracy,
public MoveDataImpl(StringKey name, TypeIdentifier moveType, MoveCategory category, byte basePower, byte accuracy,
byte baseUsages, MoveTarget target, sbyte priority, ISecondaryEffect? secondaryEffect,
IEnumerable<StringKey> flags)
{
@ -171,7 +166,7 @@ public class MoveDataImpl : IMoveData
}
/// <inheritdoc />
public string Name { get; }
public StringKey Name { get; }
/// <inheritdoc />
public TypeIdentifier MoveType { get; }

View File

@ -1,18 +1,13 @@
using System;
using PkmnLib.Static.Utils;
namespace PkmnLib.Static;
/// <summary>
/// A nature is an attribute on a Pokemon that modifies the effective base stats on a Pokemon. They
/// A nature is an attribute on a Pokémon that modifies the effective base stats on a Pokémon. They
/// can have an increased statistic and a decreased statistic, or be neutral.
/// </summary>
public interface INature
public interface INature : INamedValue
{
/// <summary>
/// The name of the nature.
/// </summary>
string Name { get; }
/// <summary>
/// The stat that should receive the increased modifier.
/// </summary>
@ -51,7 +46,7 @@ public interface INature
/// <inheritdoc />
public class Nature(
string name,
StringKey name,
Statistic increaseStat,
Statistic decreaseStat,
float increaseModifier,
@ -59,7 +54,7 @@ public class Nature(
: INature
{
/// <inheritdoc />
public string Name { get; } = name;
public StringKey Name { get; } = name;
/// <inheritdoc />
public Statistic IncreasedStat { get; } = increaseStat;

View File

@ -25,8 +25,4 @@
<PackageReference Include="System.Collections.Immutable" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Libraries\" />
</ItemGroup>
</Project>

View File

@ -3,15 +3,10 @@ using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Species;
/// <summary>
/// An ability is a passive effect in battle that is attached to a Pokemon.
/// An ability is a passive effect in battle that is attached to a Pokémon.
/// </summary>
public interface IAbility
public interface IAbility : INamedValue
{
/// <summary>
/// The name of the ability.
/// </summary>
StringKey Name { get; }
/// <summary>
/// The name of the script effect of the ability. This should refer to the name of the script that will be executed
/// when the ability is triggered.

View File

@ -3,12 +3,12 @@ using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Species;
/// <summary>
/// Data about how and into which Pokemon a species can evolve.
/// Data about how and into which Pokémon a species can evolve.
/// </summary>
public interface IEvolution
{
/// <summary>
/// The species that the Pokemon evolves into.
/// The species that the Pokémon evolves into.
/// </summary>
StringKey ToSpecies { get; }
}
@ -19,7 +19,7 @@ public interface IEvolution
public record LevelEvolution : IEvolution
{
/// <summary>
/// The level at which the Pokemon evolves.
/// The level at which the Pokémon evolves.
/// </summary>
public required uint Level { get; init; }
@ -28,17 +28,17 @@ public record LevelEvolution : IEvolution
}
/// <summary>
/// Evolves when a certain level is reached, and the Pokemon is a specific gender
/// Evolves when a certain level is reached, and the Pokémon is a specific gender
/// </summary>
public record LevelGenderEvolution : IEvolution
{
/// <summary>
/// The level at which the Pokemon evolves.
/// The level at which the Pokémon evolves.
/// </summary>
public required uint Level { get; init; }
/// <summary>
/// The gender the Pokemon needs to have to evolve
/// The gender the Pokémon needs to have to evolve
/// </summary>
public required Gender Gender { get; init; }
@ -47,7 +47,7 @@ public record LevelGenderEvolution : IEvolution
}
/// <summary>
/// Evolves when an item is used on the Pokemon.
/// Evolves when an item is used on the Pokémon.
/// </summary>
public record ItemUseEvolution : IEvolution
{
@ -61,7 +61,7 @@ public record ItemUseEvolution : IEvolution
}
/// <summary>
/// Evolves when an item is used on the Pokemon, and the Pokemon is a specific gender
/// Evolves when an item is used on the Pokémon, and the Pokémon is a specific gender
/// </summary>
public record ItemGenderEvolution : IEvolution
{
@ -71,7 +71,7 @@ public record ItemGenderEvolution : IEvolution
public required StringKey Item { get; init; }
/// <summary>
/// The gender the Pokemon needs to have to evolve
/// The gender the Pokémon needs to have to evolve
/// </summary>
public Gender Gender { get; init; }
@ -80,7 +80,7 @@ public record ItemGenderEvolution : IEvolution
}
/// <summary>
/// Evolves when an item is held by the Pokemon, and the Pokemon levels up.
/// Evolves when an item is held by the Pokémon, and the Pokémon levels up.
/// </summary>
public record HoldItemEvolution : IEvolution
{
@ -94,7 +94,7 @@ public record HoldItemEvolution : IEvolution
}
/// <summary>
/// Evolves when an item is held by the Pokemon, and the Pokemon levels up, and it's day.
/// Evolves when an item is held by the Pokémon, and the Pokémon levels up, and it's day.
/// </summary>
public record DayHoldItemEvolution : IEvolution
{
@ -103,12 +103,12 @@ public record DayHoldItemEvolution : IEvolution
/// </summary>
public required StringKey Item { get; init; }
/// < inheritdoc />
/// <inheritdoc />
public required StringKey ToSpecies { get; init; }
}
/// <summary>
/// Evolves when an item is held by the Pokemon, and the Pokemon levels up, and it's night.
/// Evolves when an item is held by the Pokémon, and the Pokémon levels up, and it's night.
/// </summary>
public record NightHoldItemEvolution : IEvolution
{
@ -117,12 +117,12 @@ public record NightHoldItemEvolution : IEvolution
/// </summary>
public required StringKey Item { get; init; }
/// < inheritdoc />
/// <inheritdoc />
public required StringKey ToSpecies { get; init; }
}
/// <summary>
/// Evolves when the Pokemon knows a certain move, and the Pokemon levels up.
/// Evolves when the Pokémon knows a certain move, and the Pokémon levels up.
/// </summary>
public record HasMoveEvolution : IEvolution
{
@ -131,12 +131,12 @@ public record HasMoveEvolution : IEvolution
/// </summary>
public required StringKey MoveName { get; init; }
/// < inheritdoc />
/// <inheritdoc />
public required StringKey ToSpecies { get; init; }
}
/// <summary>
/// Evolves when above a certain happiness level, and the Pokemon levels up.
/// Evolves when above a certain happiness level, and the Pokémon levels up.
/// </summary>
public record HappinessEvolution : IEvolution
{
@ -145,12 +145,12 @@ public record HappinessEvolution : IEvolution
/// </summary>
public required byte Happiness { get; init; }
/// < inheritdoc />
/// <inheritdoc />
public required StringKey ToSpecies { get; init; }
}
/// <summary>
/// Evolves when above a certain happiness level, and the Pokemon levels up, and it's day.
/// Evolves when above a certain happiness level, and the Pokémon levels up, and it's day.
/// </summary>
public record HappinessDayEvolution : IEvolution
{
@ -159,12 +159,12 @@ public record HappinessDayEvolution : IEvolution
/// </summary>
public required byte Happiness { get; init; }
/// < inheritdoc />
/// <inheritdoc />
public required StringKey ToSpecies { get; init; }
}
/// <summary>
/// Evolves when above a certain happiness level, and the Pokemon levels up, and it's night.
/// Evolves when above a certain happiness level, and the Pokémon levels up, and it's night.
/// </summary>
public record HappinessNightEvolution : IEvolution
{
@ -173,7 +173,7 @@ public record HappinessNightEvolution : IEvolution
/// </summary>
public required byte Happiness { get; init; }
/// < inheritdoc />
/// <inheritdoc />
public required StringKey ToSpecies { get; init; }
}
@ -182,7 +182,7 @@ public record HappinessNightEvolution : IEvolution
/// </summary>
public record TradeEvolution : IEvolution
{
/// < inheritdoc />
/// <inheritdoc />
public required StringKey ToSpecies { get; init; }
}
@ -196,7 +196,7 @@ public record TradeSpeciesEvolution : IEvolution
/// </summary>
public required StringKey WithSpecies { get; init; }
/// < inheritdoc />
/// <inheritdoc />
public required StringKey ToSpecies { get; init; }
}
@ -210,7 +210,7 @@ public record TradeItemEvolution : IEvolution
/// </summary>
public required StringKey Item { get; init; }
/// < inheritdoc />
/// <inheritdoc />
public required StringKey ToSpecies { get; init; }
}
@ -229,6 +229,6 @@ public record CustomEvolution : IEvolution
/// </summary>
public required IReadOnlyDictionary<StringKey, object> Parameters { get; init; }
/// < inheritdoc />
/// <inheritdoc />
public required StringKey ToSpecies { get; init; }
}

View File

@ -9,13 +9,8 @@ namespace PkmnLib.Static.Species;
/// A form is a variant of a specific species. A species always has at least one form, but can have
/// many more.
/// </summary>
public interface IForm
public interface IForm : INamedValue
{
/// <summary>
/// The name of the form.
/// </summary>
StringKey Name { get; }
/// <summary>
/// The height of the form in meters.
/// </summary>
@ -27,32 +22,32 @@ public interface IForm
float Weight { get; }
/// <summary>
/// The base amount of experience that is gained when beating a Pokemon with this form.
/// The base amount of experience that is gained when beating a Pokémon with this form.
/// </summary>
uint BaseExperience { get; }
/// <summary>
/// The normal types a Pokemon with this form has.
/// The normal types a Pokémon with this form has.
/// </summary>
IReadOnlyList<TypeIdentifier> Types { get; }
/// <summary>
/// The inherent values of a form of species that are used for the stats of a Pokemon.
/// The inherent values of a form of species that are used for the stats of a Pokémon.
/// </summary>
StaticStatisticSet<ushort> BaseStats { get; }
ImmutableStatisticSet<ushort> BaseStats { get; }
/// <summary>
/// The possible abilities a Pokemon with this form can have.
/// The possible abilities a Pokémon with this form can have.
/// </summary>
IReadOnlyList<StringKey> Abilities { get; }
/// <summary>
/// The possible hidden abilities a Pokemon with this form can have.
/// The possible hidden abilities a Pokémon with this form can have.
/// </summary>
IReadOnlyList<StringKey> HiddenAbilities { get; }
/// <summary>
/// The moves a Pokemon with this form can learn.
/// The moves a Pokémon with this form can learn.
/// </summary>
ILearnableMoves Moves { get; }
@ -102,7 +97,7 @@ public class FormImpl : IForm
{
/// <inheritdoc cref="FormImpl" />
public FormImpl(StringKey name, float height, float weight, uint baseExperience,
IEnumerable<TypeIdentifier> types, StaticStatisticSet<ushort> baseStats, IEnumerable<StringKey> abilities,
IEnumerable<TypeIdentifier> types, ImmutableStatisticSet<ushort> baseStats, IEnumerable<StringKey> abilities,
IEnumerable<StringKey> hiddenAbilities, ILearnableMoves moves, ImmutableHashSet<StringKey> flags)
{
Name = name;
@ -140,7 +135,7 @@ public class FormImpl : IForm
public IReadOnlyList<TypeIdentifier> Types { get; }
/// <inheritdoc />
public StaticStatisticSet<ushort> BaseStats { get; }
public ImmutableStatisticSet<ushort> BaseStats { get; }
/// <inheritdoc />
public IReadOnlyList<StringKey> Abilities { get; }

View File

@ -1,21 +1,21 @@
namespace PkmnLib.Static;
namespace PkmnLib.Static.Species;
/// <summary>
/// Gender is a Pokemon characteristic.
/// Gender is a Pokémon characteristic.
///
/// Required for standard pokemon functions, but somewhat controversial nowadays. Consider adding a feature
/// Required for standard Pokémon 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
{
/// The Pokemon has no gender.
/// The Pokémon has no gender.
Genderless,
/// <summary>
/// The Pokemon is male.
/// The Pokémon is male.
/// </summary>
Male,
/// <summary>
/// The Pokemon is female.
/// The Pokémon is female.
/// </summary>
Female
}

View File

@ -3,38 +3,40 @@ using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Species;
/// <summary>
/// The storage of the moves a Pokemon can learn.
/// The storage of the moves a Pokémon can learn.
/// </summary>
public interface ILearnableMoves
{
/// <summary>
/// Adds a new level move the Pokemon can learn.
/// Adds a new level move the Pokémon can learn.
/// </summary>
/// <param name="level">The level the Pokemon learns the move at.</param>
/// <param name="move">The move the Pokemon learns.</param>
/// <param name="level">The level the Pokémon learns the move at.</param>
/// <param name="move">The move the Pokémon learns.</param>
/// <returns>Whether the move was added successfully.</returns>
void AddLevelMove(LevelInt level, StringKey move);
/// <summary>
/// Gets all moves a Pokemon can learn when leveling up to a specific level.
/// Gets all moves a Pokémon can learn when leveling up to a specific level.
/// </summary>
/// <param name="level">The level the Pokemon is learning moves at.</param>
/// <returns>The moves the Pokemon learns at that level.</returns>
/// <param name="level">The level the Pokémon is learning moves at.</param>
/// <returns>The moves the Pokémon learns at that level.</returns>
IReadOnlyList<StringKey> GetLearnedByLevel(LevelInt level);
/// <summary>
/// Gets the distinct moves a Pokemon can learn through leveling up.
/// Gets the distinct moves a Pokémon can learn through leveling up.
/// </summary>
/// <returns>The moves the Pokemon can learn through leveling up.</returns>
/// <returns>The moves the Pokémon can learn through leveling up.</returns>
IReadOnlyList<StringKey> GetDistinctLevelMoves();
}
/// <inheritdoc />
public class LearnableMovesImpl : ILearnableMoves
{
private readonly Dictionary<LevelInt, List<StringKey>> _learnedByLevel = new();
private readonly HashSet<StringKey> _distinctLevelMoves = new();
/// <inheritdoc />
public void AddLevelMove(LevelInt level, StringKey move)
{
if (!_learnedByLevel.TryGetValue(level, out var value))
@ -44,6 +46,7 @@ public class LearnableMovesImpl : ILearnableMoves
_distinctLevelMoves.Add(move);
}
/// <inheritdoc />
public IReadOnlyList<StringKey> GetLearnedByLevel(LevelInt level)
{
if (!_learnedByLevel.TryGetValue(level, out var value))
@ -51,8 +54,9 @@ public class LearnableMovesImpl : ILearnableMoves
return value;
}
/// <inheritdoc />
public IReadOnlyList<StringKey> GetDistinctLevelMoves()
{
return _distinctLevelMoves.ToList();
}
}
}

View File

@ -5,23 +5,18 @@ using PkmnLib.Static.Utils;
namespace PkmnLib.Static.Species;
/// <summary>
/// The data belonging to a Pokemon with certain characteristics.
/// The data belonging to a Pokémon with certain characteristics.
/// </summary>
public interface ISpecies
public interface ISpecies : INamedValue
{
/// <summary>
/// The national dex identifier of the Pokemon.
/// The national dex identifier of the Pokémon.
/// </summary>
ushort Id { get; }
/// <summary>
/// The name of the Pokemon.
/// </summary>
StringKey Name { get; }
/// <summary>
/// The chance between 0.0 and 1.0 that a Pokemon is female. 0.0 means always male, 1.0 means always female.
/// If less than 0, the Pokemon is genderless.
/// The chance between 0.0 and 1.0 that a Pokémon is female. 0.0 means always male, 1.0 means always female.
/// If less than 0, the Pokémon is genderless.
/// </summary>
float GenderRate { get; }
@ -31,23 +26,23 @@ public interface ISpecies
StringKey GrowthRate { get; }
/// <summary>
/// How hard it is to capture a Pokemon. 255 means this will be always caught, 0 means this is
/// How hard it is to capture a Pokémon. 255 means this will be always caught, 0 means this is
/// uncatchable.
/// </summary>
byte CaptureRate { get; }
/// <summary>
/// The base happiness of the Pokemon.
/// The base happiness of the Pokémon.
/// </summary>
byte BaseHappiness { get; }
/// <summary>
/// The forms that belong to this Pokemon.
/// The forms that belong to this Pokémon.
/// </summary>
IReadOnlyDictionary<StringKey, IForm> Forms { get; }
/// <summary>
/// The arbitrary flags that can be set on a Pokemon for script use.
/// The arbitrary flags that can be set on a Pokémon for script use.
/// </summary>
ImmutableHashSet<StringKey> Flags { get; }
@ -57,7 +52,7 @@ public interface ISpecies
bool TryGetForm(StringKey id, [MaybeNullWhen(false)] out IForm form);
/// <summary>
/// Gets the form the Pokemon will have by default, if no other form is specified.
/// Gets the form the Pokémon will have by default, if no other form is specified.
/// </summary>
IForm GetDefaultForm();
@ -67,12 +62,12 @@ public interface ISpecies
Gender GetRandomGender(IRandom rand);
/// <summary>
/// Check whether the Pokemon has a specific flag set.
/// Check whether the Pokémon has a specific flag set.
/// </summary>
bool HasFlag(string key);
/// <summary>
/// The data regarding into which Pokemon this species can evolve, and how.
/// The data regarding into which Pokémon this species can evolve, and how.
/// </summary>
IReadOnlyList<IEvolution> EvolutionData { get; }
}

View File

@ -1,32 +1,32 @@
namespace PkmnLib.Static;
/// <summary>
/// Stats are numerical values on Pokemon that are used in battle.
/// Stats are numerical values on Pokémon that are used in battle.
/// </summary>
public enum Statistic : byte
{
/// <summary>
/// Health Points determine how much damage a Pokemon can receive before fainting.
/// Health Points determine how much damage a Pokémon can receive before fainting.
/// </summary>
Hp,
/// <summary>
/// Attack determines how much damage a Pokemon deals when using a physical attack.
/// Attack determines how much damage a Pokémon deals when using a physical attack.
/// </summary>
Attack,
/// <summary>
/// Defense determines how much damage a Pokemon receives when it is hit by a physical attack.
/// Defense determines how much damage a Pokémon receives when it is hit by a physical attack.
/// </summary>
Defense,
/// <summary>
/// Special Attack determines how much damage a Pokemon deals when using a special attack.
/// Special Attack determines how much damage a Pokémon deals when using a special attack.
/// </summary>
SpecialAttack,
/// <summary>
/// Special Defense determines how much damage a Pokemon receives when it is hit by a special attack.
/// Special Defense determines how much damage a Pokémon receives when it is hit by a special attack.
/// </summary>
SpecialDefense,
/// <summary>
/// Speed determines the order that a Pokemon can act in battle.
/// Speed determines the order that a Pokémon can act in battle.
/// </summary>
Speed
}

View File

@ -1,8 +1,10 @@
using System;
namespace PkmnLib.Static;
public record StaticStatisticSet<T>
/// <summary>
/// A set of statistics that cannot be changed.
/// </summary>
/// <typeparam name="T">The size of the integer to be used</typeparam>
public record ImmutableStatisticSet<T>
where T : struct
{
/// <summary>
@ -35,7 +37,8 @@ public record StaticStatisticSet<T>
/// </summary>
public T Speed { get; protected set; }
public StaticStatisticSet(T hp, T attack, T defense, T specialAttack, T specialDefense, T speed)
/// <inheritdoc cref="ImmutableStatisticSet{T}"/>
public ImmutableStatisticSet(T hp, T attack, T defense, T specialAttack, T specialDefense, T speed)
{
Hp = hp;
Attack = attack;
@ -45,6 +48,9 @@ public record StaticStatisticSet<T>
Speed = speed;
}
/// <summary>
/// Gets a statistic from the set.
/// </summary>
public T GetStatistic(Statistic stat)
{
return stat switch
@ -60,14 +66,22 @@ public record StaticStatisticSet<T>
}
}
public record StatisticSet<T> : StaticStatisticSet<T>
/// <summary>
/// A set of statistics that can be changed.
/// </summary>
/// <typeparam name="T"></typeparam>
public record StatisticSet<T> : ImmutableStatisticSet<T>
where T : struct
{
/// <inheritdoc cref="StatisticSet{T}"/>
public StatisticSet(T hp, T attack, T defense, T specialAttack, T specialDefense, T speed) : base(hp, attack,
defense, specialAttack, specialDefense, speed)
{
}
/// <summary>
/// Modifies a statistic in the set.
/// </summary>
public void SetStatistic(Statistic stat, T value)
{
switch (stat)
@ -95,6 +109,9 @@ public record StatisticSet<T> : StaticStatisticSet<T>
}
}
/// <summary>
/// Increases a statistic in the set by a value.
/// </summary>
public void IncreaseStatistic(Statistic stat, T value)
{
switch (stat)
@ -124,6 +141,9 @@ public record StatisticSet<T> : StaticStatisticSet<T>
}
}
/// <summary>
/// Decreases a statistic in the set by a value.
/// </summary>
public void DecreaseStatistic(Statistic stat, T value)
{
switch (stat)

View File

@ -1,7 +1,7 @@
namespace PkmnLib.Static;
/// <summary>
/// The time of day. These values are the 4 different groups of time of day in Pokemon games since
/// The time of day. These values are the 4 different groups of time of day in Pokémon games since
/// gen 5. The exact times these correspond to differ between games.
/// </summary>
public enum TimeOfDay : byte

View File

@ -1,15 +1,28 @@
using PkmnLib.Static.Libraries;
namespace PkmnLib.Static;
/// <summary>
/// A number that identifies a type. To be used with <see cref="TypeLibrary"/>
/// </summary>
public readonly record struct TypeIdentifier
{
private byte Value { get; init; }
/// <summary>
/// The underlying value of the type identifier.
/// </summary>
public byte Value { get; }
/// <inheritdoc cref="TypeIdentifier"/>
public TypeIdentifier(byte value)
{
Value = value;
}
/// <summary>
/// Converts a byte to a type identifier.
/// </summary>
public static implicit operator TypeIdentifier(byte value) => new(value);
/// <inheritdoc />
public override int GetHashCode() => Value.GetHashCode();
}

View File

@ -2,8 +2,12 @@ using FluentResults;
namespace PkmnLib.Static.Utils.Errors;
/// <summary>
/// A result that indicates an index is out of range.
/// </summary>
public class OutOfRange : Error
{
/// <inheritdoc cref="OutOfRange"/>
public OutOfRange(string hint, int index, int max)
: base($"{hint} index {index} is out of range. Must be between 0 and {max - 1}.")
{

View File

@ -0,0 +1,12 @@
namespace PkmnLib.Static.Utils;
/// <summary>
/// Indicates that a value has a name.
/// </summary>
public interface INamedValue
{
/// <summary>
/// The name of the value.
/// </summary>
StringKey Name { get; }
}

View File

@ -1,43 +1,95 @@
namespace PkmnLib.Static.Utils;
/// <summary>
/// Wrapper interface for getting random numbers.
/// </summary>
public interface IRandom
{
/// <summary>
/// Get a random integer between min and max.
/// </summary>
/// <param name="min">The minimum value (inclusive).</param>
/// <param name="max">The maximum value (exclusive).</param>
/// <returns>A random integer that is greater than or equal to min and less than max.</returns>
public int GetInt(int min, int max);
/// <summary>
/// Get a random integer between 0 and max.
/// </summary>
/// <param name="max">The maximum value (exclusive).</param>
/// <returns>A random integer that is greater than or equal to 0 and less than max.</returns>
public int GetInt(int max);
/// <summary>
/// Get a random integer between 0 and <see cref="int.MaxValue"/>.
/// </summary>
/// <returns>A random integer that is greater than or equal to 0 and less than <see cref="int.MaxValue"/>.</returns>
public int GetInt();
/// <summary>
/// Get a random float that is greater than or equal to 0.0 and less than 1.0.
/// </summary>
/// <returns>A random float that is greater than or equal to 0.0 and less than 1.0.</returns>
public float GetFloat();
/// <summary>
/// Get a random float that is greater than or equal to min and less than max.
/// </summary>
/// <param name="min">The minimum value (inclusive).</param>
/// <param name="max">The maximum value (exclusive).</param>
/// <returns>A random float that is greater than or equal to min and less than max.</returns>
public float GetFloat(float min, float max);
/// <summary>
/// Get a random boolean. 50% chance of being true.
/// </summary>
public bool GetBool();
}
/// <inheritdoc />
public class RandomImpl : IRandom
{
private Random _random;
private readonly Random _random;
/// <summary>
/// Creates a new instance that uses the given <see cref="Random"/> instance as its source of randomness.
/// </summary>
public RandomImpl(Random random)
{
_random = random;
}
/// <summary>
/// Creates a new instance that uses the given seed to create a new <see cref="Random"/> instance.
/// </summary>
public RandomImpl(int seed)
{
_random = new Random(seed);
}
/// <summary>
/// Creates a new instance that uses a new <see cref="Random"/> instance.
/// </summary>
public RandomImpl()
{
_random = new Random();
}
/// <inheritdoc />
public int GetInt(int min, int max) => _random.Next(min, max);
/// <inheritdoc />
public int GetInt(int max) => _random.Next(max);
/// <inheritdoc />
public int GetInt() => _random.Next();
/// <inheritdoc />
public float GetFloat() => (float)_random.NextDouble();
/// <inheritdoc />
public float GetFloat(float min, float max) => (float)(_random.NextDouble() * (max - min) + min);
/// <inheritdoc />
public bool GetBool() => _random.Next(2) == 1;
}

View File

@ -1,5 +1,3 @@
using System;
namespace PkmnLib.Static.Utils;
/// <summary>
@ -12,24 +10,28 @@ public readonly record struct StringKey
{
private readonly string _key;
/// <inheritdoc cref="StringKey"/>
public StringKey(string key)
{
_key = key;
}
/// <summary>
/// Converts a <see cref="StringKey"/> to a <see cref="string"/>.
/// </summary>
public static implicit operator string(StringKey key) => key._key;
/// <summary>
/// Converts a <see cref="string"/> to a <see cref="StringKey"/>.
/// </summary>
public static implicit operator StringKey(string key) => new(key);
/// <inheritdoc cref="string.ToString()"/>
public override string ToString() => _key.ToLowerInvariant();
public bool Equals(StringKey other)
{
return string.Equals(_key, other._key, StringComparison.InvariantCultureIgnoreCase);
}
public override int GetHashCode()
{
return StringComparer.InvariantCultureIgnoreCase.GetHashCode(_key);
}
/// <inheritdoc />
public bool Equals(StringKey other) => string.Equals(_key, other._key, StringComparison.InvariantCultureIgnoreCase);
/// <inheritdoc />
public override int GetHashCode() => StringComparer.InvariantCultureIgnoreCase.GetHashCode(_key);
}