using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using PkmnLib.Static.Utils; namespace PkmnLib.Static.Species; /// <summary> /// The data belonging to a Pokémon with certain characteristics. /// </summary> public interface ISpecies : INamedValue { /// <summary> /// The national dex identifier of the Pokémon. /// </summary> ushort Id { get; } /// <summary> /// 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; } /// <summary> /// How much experience is required for a level. /// </summary> StringKey GrowthRate { get; } /// <summary> /// 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 Pokémon. /// </summary> byte BaseHappiness { get; } /// <summary> /// The forms that belong to this Pokémon. /// </summary> IReadOnlyDictionary<StringKey, IForm> Forms { get; } /// <summary> /// The arbitrary flags that can be set on a Pokémon for script use. /// </summary> ImmutableHashSet<StringKey> Flags { get; } /// <summary> /// Gets a form by name. /// </summary> bool TryGetForm(StringKey id, [MaybeNullWhen(false)] out IForm form); /// <summary> /// Gets the form the Pokémon will have by default, if no other form is specified. /// </summary> IForm GetDefaultForm(); /// <summary> /// Gets a random gender. /// </summary> Gender GetRandomGender(IRandom rand); /// <summary> /// Check whether the Pokémon has a specific flag set. /// </summary> bool HasFlag(string key); /// <summary> /// The data regarding into which Pokémon this species can evolve, and how. /// </summary> IReadOnlyList<IEvolution> EvolutionData { get; } ICollection<StringKey> EggGroups { get; } } /// <inheritdoc /> public class SpeciesImpl : ISpecies { /// <inheritdoc cref="SpeciesImpl" /> public SpeciesImpl(ushort id, StringKey name, float genderRate, StringKey growthRate, byte captureRate, byte baseHappiness, IReadOnlyDictionary<StringKey, IForm> forms, IEnumerable<StringKey> flags, IReadOnlyList<IEvolution> evolutionData, IEnumerable<StringKey> eggGroups) { Id = id; Name = name; GenderRate = genderRate; GrowthRate = growthRate; CaptureRate = captureRate; BaseHappiness = baseHappiness; Forms = forms; Flags = flags.ToImmutableHashSet(); EvolutionData = evolutionData; EggGroups = eggGroups.ToImmutableHashSet(); if (Forms.Count == 0) throw new ArgumentException("Species must have at least one form."); if (!Forms.ContainsKey("default")) throw new ArgumentException("Species must have a default form."); } /// <inheritdoc /> public ushort Id { get; } /// <inheritdoc /> public StringKey Name { get; } /// <inheritdoc /> public float GenderRate { get; } /// <inheritdoc /> public StringKey GrowthRate { get; } /// <inheritdoc /> public byte CaptureRate { get; } /// <inheritdoc /> public byte BaseHappiness { get; } /// <inheritdoc /> public IReadOnlyDictionary<StringKey, IForm> Forms { get; } /// <inheritdoc /> public ImmutableHashSet<StringKey> Flags { get; } /// <inheritdoc /> public IReadOnlyList<IEvolution> EvolutionData { get; } /// <inheritdoc /> public ICollection<StringKey> EggGroups { get; } /// <inheritdoc /> public bool TryGetForm(StringKey id, [MaybeNullWhen(false)] out IForm form) => Forms.TryGetValue(id, out form); /// <inheritdoc /> public IForm GetDefaultForm() => Forms["default"]; /// <inheritdoc /> public Gender GetRandomGender(IRandom rand) { if (GenderRate < 0.0f) return Gender.Genderless; var v = rand.GetFloat(); return v < GenderRate ? Gender.Female : Gender.Male; } /// <inheritdoc /> public bool HasFlag(string key) => Flags.Contains(key); }