Implements species/form changing

This commit is contained in:
Deukhoofd 2024-09-03 10:15:48 +02:00
parent 656c208e5f
commit 213dfdb69e
5 changed files with 124 additions and 11 deletions

View File

@ -0,0 +1,19 @@
using PkmnLib.Dynamic.Models;
using PkmnLib.Static.Species;
namespace PkmnLib.Dynamic.Events;
public class FormChangeEvent : IEventData
{
public IPokemon Pokemon { get; }
public IForm Form { get; }
public FormChangeEvent(IPokemon pokemon, IForm form)
{
Pokemon = pokemon;
Form = form;
}
/// <inheritdoc />
public EventBatchId BatchId { get; init; }
}

View File

@ -0,0 +1,21 @@
using PkmnLib.Dynamic.Models;
using PkmnLib.Static.Species;
namespace PkmnLib.Dynamic.Events;
public class SpeciesChangeEvent : IEventData
{
public IPokemon Pokemon { get; }
public ISpecies Species { get; }
public IForm Form { get; }
public SpeciesChangeEvent(IPokemon pokemon, ISpecies species, IForm form)
{
Pokemon = pokemon;
Species = species;
Form = form;
}
/// <inheritdoc />
public EventBatchId BatchId { get; init; }
}

View File

@ -19,4 +19,10 @@ public enum DamageSource
/// The damage is done because of struggling.
/// </summary>
Struggle = 2,
/// <summary>
/// The damage is done because of a form change.
/// This happens when the form of a Pokemon changes, and it has less max HP than it had before.
/// </summary>
FormChange = 3,
}

View File

@ -246,7 +246,7 @@ public interface IPokemon : IScriptSource
/// <summary>
/// Change the form of the Pokemon.
/// </summary>
void ChangeForm(IForm form);
void ChangeForm(IForm form, EventBatchId batchId = default);
/// <summary>
/// Whether the Pokemon is useable in a battle.
@ -305,7 +305,9 @@ public interface IPokemon : IScriptSource
/// </summary>
void MarkOpponentAsSeen(IPokemon pokemon);
// TODO: (de)serialize
/// <summary>
/// Converts the data structure to a serializable format.
/// </summary>
SerializedPokemon Serialize();
}
@ -426,7 +428,8 @@ public class PokemonImpl : ScriptSource, IPokemon
if (serializedPokemon.Status != null)
{
if (!library.ScriptResolver.TryResolve(ScriptCategory.Status, serializedPokemon.Status, out var statusScript))
if (!library.ScriptResolver.TryResolve(ScriptCategory.Status, serializedPokemon.Status,
out var statusScript))
throw new KeyNotFoundException($"Status script {serializedPokemon.Status} not found");
StatusScript.Set(statusScript);
}
@ -439,7 +442,7 @@ public class PokemonImpl : ScriptSource, IPokemon
public ISpecies Species { get; }
/// <inheritdoc />
public IForm Form { get; }
public IForm Form { get; private set; }
/// <inheritdoc />
public ISpecies? DisplaySpecies { get; set; }
@ -457,7 +460,7 @@ public class PokemonImpl : ScriptSource, IPokemon
public uint PersonalityValue { get; }
/// <inheritdoc />
public Gender Gender { get; }
public Gender Gender { get; private set; }
/// <inheritdoc />
public byte Coloring { get; }
@ -623,18 +626,82 @@ public class PokemonImpl : ScriptSource, IPokemon
/// <inheritdoc />
public void ChangeSpecies(ISpecies species, IForm form)
{
throw new NotImplementedException();
if (Species == species)
{
if (form != Form)
ChangeForm(form, new EventBatchId());
return;
}
// If the Pokémon is genderless, but its new species is not, we want to set its gender
if (Gender != Gender.Genderless && species.GenderRate < 0.0)
{
var random = (IRandom?)BattleData?.Battle.Random ?? new RandomImpl();
Gender = species.GetRandomGender(random);
}
// Else if the new species is genderless, but the Pokémon has a gender, make the creature genderless.
else if (species.GenderRate < 0.0 && Gender != Gender.Genderless)
{
Gender = Gender.Genderless;
}
var batchId = new EventBatchId();
BattleData?.Battle.EventHook.Invoke(new SpeciesChangeEvent(this, species, form)
{
BatchId = batchId,
});
ChangeForm(form, batchId);
}
/// <inheritdoc />
public void ChangeForm(IForm form)
public void ChangeForm(IForm form, EventBatchId batchId)
{
throw new NotImplementedException();
if (form == Form)
return;
var oldAbility = Form.GetAbility(AbilityIndex);
Form = form;
Types = form.Types.ToList();
WeightInKg = form.Weight;
HeightInMeters = form.Height;
var newAbility = Form.GetAbility(AbilityIndex);
if (OverrideAbility == null && oldAbility != newAbility)
{
AbilityScript.Clear();
if (!Library.StaticLibrary.Abilities.TryGet(newAbility, out var ability))
throw new KeyNotFoundException($"Ability {newAbility} not found.");
if (Library.ScriptResolver.TryResolve(ScriptCategory.Ability, newAbility, out var abilityScript))
{
AbilityScript.Set(abilityScript);
abilityScript.OnInitialize(Library, ability.Parameters);
}
else
{
AbilityScript.Clear();
}
}
var oldHealth = BoostedStats.Hp;
RecalculateFlatStats();
var diffHealth = (long)BoostedStats.Hp - oldHealth;
if (diffHealth > 0)
{
Heal((uint)diffHealth, true);
}
// TODO: form specific moves?
BattleData?.Battle.EventHook.Invoke(new FormChangeEvent(this, form)
{
BatchId = batchId,
});
}
/// <inheritdoc />
/// <remarks>
/// Currently this checks the Pokemon is not an egg, not caught, and not fainted.
/// Currently this checks the Pokémon is not an egg, not caught, and not fainted.
/// </remarks>
public bool IsUsable => !IsCaught && !IsEgg && !IsFainted;
@ -644,7 +711,7 @@ public class PokemonImpl : ScriptSource, IPokemon
/// <inheritdoc />
public void Damage(uint damage, DamageSource source, EventBatchId batchId)
{
// If the Pokemon is already fainted, we don't need to do anything.
// If the Pokémon is already fainted, we don't need to do anything.
if (IsFainted)
return;
// If the damage is more than the current health, we cap it at the current health, to prevent

View File

@ -67,7 +67,7 @@ public abstract class Script
/// <summary>
/// This function is ran when this script starts being in effect.
/// </summary>
public virtual void OnInitialize(IDynamicLibrary library, IReadOnlyDictionary<StringKey, object>? parameters)
public virtual void OnInitialize(IDynamicLibrary library, IReadOnlyDictionary<StringKey, object?>? parameters)
{
}