PkmnLibRSharp/PkmnLibRSharp/DynamicData/PokemonBuilder.cs

206 lines
7.1 KiB
C#

using System;
using System.Collections.Generic;
using PkmnLibSharp.DynamicData.Libraries;
using PkmnLibSharp.StaticData;
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();
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;
/// <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;
_species = species;
_level = level;
_randomSeed = DefaultRandom.Next();
_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,
_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;
}
/// <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);
_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))
{
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);
}
/// <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))
{
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);
}
}
}