using System.Collections.Immutable; using PkmnLib.Static.Utils; using PkmnLib.Static.Utils.Errors; namespace PkmnLib.Static.Species; /// <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 interface IForm : INamedValue { /// <summary> /// The height of the form in meters. /// </summary> float Height { get; } /// <summary> /// The weight of the form in kilograms. /// </summary> float Weight { get; } /// <summary> /// 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 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 Pokémon. /// </summary> ImmutableStatisticSet<ushort> BaseStats { get; } /// <summary> /// The possible abilities a Pokémon with this form can have. /// </summary> IReadOnlyList<StringKey> Abilities { get; } /// <summary> /// The possible hidden abilities a Pokémon with this form can have. /// </summary> IReadOnlyList<StringKey> HiddenAbilities { get; } /// <summary> /// The moves a Pokémon with this form can learn. /// </summary> ILearnableMoves Moves { get; } /// <summary> /// Arbitrary flags can be set on a form for scripting use. /// </summary> ImmutableHashSet<StringKey> Flags { get; } /// <summary> /// Get a type of the form at a certain index. /// </summary> TypeIdentifier GetType(int index); /// <summary> /// Gets a single base stat value. /// </summary> ushort GetBaseStat(Statistic stat); /// <summary> /// Find the index of an ability that can be on this form. /// </summary> AbilityIndex? FindAbilityIndex(IAbility ability); /// <summary> /// Gets an ability from the form. /// </summary> StringKey GetAbility(AbilityIndex index); /// <summary> /// Gets a random ability from the form. /// </summary> StringKey GetRandomAbility(IRandom rand); /// <summary> /// Gets a random hidden ability from the form. /// </summary> StringKey GetRandomHiddenAbility(IRandom rand); /// <summary> /// Check if the form has a specific flag set. /// </summary> bool HasFlag(string key); } /// <inheritdoc /> public class FormImpl : IForm { /// <inheritdoc cref="FormImpl" /> public FormImpl(StringKey name, float height, float weight, uint baseExperience, IEnumerable<TypeIdentifier> types, ImmutableStatisticSet<ushort> baseStats, IEnumerable<StringKey> abilities, IEnumerable<StringKey> hiddenAbilities, ILearnableMoves moves, ImmutableHashSet<StringKey> flags) { Name = name; Height = height; Weight = weight; BaseExperience = baseExperience; Types = [..types]; BaseStats = baseStats; Abilities = [..abilities]; HiddenAbilities = [..hiddenAbilities]; Moves = moves; Flags = flags; if (Types.Count == 0) throw new ArgumentException("A form must have at least one type."); if (Abilities.Count == 0) throw new ArgumentException("A form must have at least one ability."); } /// <inheritdoc /> public StringKey Name { get; } /// <inheritdoc /> public float Height { get; } /// <inheritdoc /> public float Weight { get; } /// <inheritdoc /> public uint BaseExperience { get; } /// <inheritdoc /> public IReadOnlyList<TypeIdentifier> Types { get; } /// <inheritdoc /> public ImmutableStatisticSet<ushort> BaseStats { get; } /// <inheritdoc /> public IReadOnlyList<StringKey> Abilities { get; } /// <inheritdoc /> public IReadOnlyList<StringKey> HiddenAbilities { get; } /// <inheritdoc /> public ILearnableMoves Moves { get; } /// <inheritdoc /> public ImmutableHashSet<StringKey> Flags { get; } /// <inheritdoc /> public TypeIdentifier GetType(int index) { if (index < 0 || index >= Types.Count) throw new OutOfRangeException("Type", index, Types.Count); return Types[index]; } /// <inheritdoc /> public ushort GetBaseStat(Statistic stat) => BaseStats.GetStatistic(stat); /// <inheritdoc /> public AbilityIndex? FindAbilityIndex(IAbility ability) { for (var i = 0; i < Abilities.Count && i < 255; i++) { if (Abilities[i] == ability.Name) return new AbilityIndex { IsHidden = false, Index = (byte)i, }; } for (var i = 0; i < HiddenAbilities.Count && i < 255; i++) { if (HiddenAbilities[i] == ability.Name) return new AbilityIndex { IsHidden = true, Index = (byte)i, }; } return null; } /// <inheritdoc /> public StringKey GetAbility(AbilityIndex index) { var array = index.IsHidden ? HiddenAbilities : Abilities; if (index.Index >= array.Count) throw new OutOfRangeException("Ability", index.Index, array.Count); return array[index.Index]; } /// <inheritdoc /> public StringKey GetRandomAbility(IRandom rand) { return Abilities[rand.GetInt(Abilities.Count)]; } /// <inheritdoc /> public StringKey GetRandomHiddenAbility(IRandom rand) { return HiddenAbilities[rand.GetInt(HiddenAbilities.Count)]; } /// <inheritdoc /> public bool HasFlag(string key) { return Flags.Contains(key); } }