Initial work on implementing Pokemon
This commit is contained in:
parent
3d5fb1a818
commit
554e1cf2cd
22
PkmnLib.Dynamic/Events/DamageEvent.cs
Normal file
22
PkmnLib.Dynamic/Events/DamageEvent.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using PkmnLib.Dynamic.Models;
|
||||||
|
|
||||||
|
namespace PkmnLib.Dynamic.Events;
|
||||||
|
|
||||||
|
public record DamageEvent : IEventData
|
||||||
|
{
|
||||||
|
public DamageEvent(IPokemon pokemon, uint previousHealth, uint newHealth, DamageSource source)
|
||||||
|
{
|
||||||
|
Pokemon = pokemon;
|
||||||
|
PreviousHealth = previousHealth;
|
||||||
|
NewHealth = newHealth;
|
||||||
|
Source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPokemon Pokemon { get; init; }
|
||||||
|
public uint PreviousHealth { get; init; }
|
||||||
|
public uint NewHealth { get; init; }
|
||||||
|
public DamageSource Source { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public EventBatchId BatchId { get; init; } = new();
|
||||||
|
}
|
16
PkmnLib.Dynamic/Events/FaintEvent.cs
Normal file
16
PkmnLib.Dynamic/Events/FaintEvent.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using PkmnLib.Dynamic.Models;
|
||||||
|
|
||||||
|
namespace PkmnLib.Dynamic.Events;
|
||||||
|
|
||||||
|
public class FaintEvent : IEventData
|
||||||
|
{
|
||||||
|
public FaintEvent(IPokemon pokemon)
|
||||||
|
{
|
||||||
|
Pokemon = pokemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPokemon Pokemon { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public EventBatchId BatchId { get; init; } = new();
|
||||||
|
}
|
@ -7,5 +7,5 @@ namespace PkmnLib.Dynamic.Events;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IEventData
|
public interface IEventData
|
||||||
{
|
{
|
||||||
|
public EventBatchId BatchId { get; init; }
|
||||||
}
|
}
|
21
PkmnLib.Dynamic/Events/HealEvent.cs
Normal file
21
PkmnLib.Dynamic/Events/HealEvent.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using PkmnLib.Dynamic.Models;
|
||||||
|
|
||||||
|
namespace PkmnLib.Dynamic.Events;
|
||||||
|
|
||||||
|
public class HealEvent : IEventData
|
||||||
|
{
|
||||||
|
public HealEvent(IPokemon pokemon, uint previousHealth, uint newHealth)
|
||||||
|
{
|
||||||
|
Pokemon = pokemon;
|
||||||
|
PreviousHealth = previousHealth;
|
||||||
|
NewHealth = newHealth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPokemon Pokemon { get; }
|
||||||
|
public uint PreviousHealth { get; }
|
||||||
|
public uint NewHealth { get; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public EventBatchId BatchId { get; init; } = new();
|
||||||
|
}
|
@ -108,7 +108,4 @@ public interface IBattle : IScriptSource
|
|||||||
/// Gets the current weather of the battle. If no weather is present, this returns null.
|
/// Gets the current weather of the battle. If no weather is present, this returns null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string? WeatherName { get; }
|
string? WeatherName { get; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ using PkmnLib.Dynamic.Libraries;
|
|||||||
using PkmnLib.Dynamic.ScriptHandling;
|
using PkmnLib.Dynamic.ScriptHandling;
|
||||||
using PkmnLib.Static;
|
using PkmnLib.Static;
|
||||||
using PkmnLib.Static.Species;
|
using PkmnLib.Static.Species;
|
||||||
|
using PkmnLib.Static.Utils;
|
||||||
|
|
||||||
namespace PkmnLib.Dynamic.Models;
|
namespace PkmnLib.Dynamic.Models;
|
||||||
|
|
||||||
@ -191,17 +192,19 @@ public interface IPokemon : IScriptSource
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether the Pokemon is holding an item with a specific name.
|
/// Checks whether the Pokemon is holding an item with a specific name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool HasHeldItem(string itemName);
|
bool HasHeldItem(StringKey itemName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the held item of the Pokemon. Returns the previously held item.
|
/// Changes the held item of the Pokemon. Returns the previously held item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MustUseReturnValue] IItem? SetHeldItem(IItem? item);
|
[MustUseReturnValue]
|
||||||
|
IItem? SetHeldItem(IItem? item);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the held item from the Pokemon. Returns the previously held item.
|
/// Removes the held item from the Pokemon. Returns the previously held item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MustUseReturnValue] IItem? RemoveHeldItem();
|
[MustUseReturnValue]
|
||||||
|
IItem? RemoveHeldItem();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Makes the Pokemon uses its held item. Returns whether the item was consumed.
|
/// Makes the Pokemon uses its held item. Returns whether the item was consumed.
|
||||||
@ -285,11 +288,383 @@ public interface IPokemon : IScriptSource
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The data of the Pokemon related to being in a battle.
|
/// The data of the Pokemon related to being in a battle.
|
||||||
|
/// This is only set when the Pokemon is on the field in a battle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IPokemonBattleData
|
public interface IPokemonBattleData
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The optional battle the Pokemon is in.
|
/// The battle the Pokemon is in.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IBattle? Battle { get; }
|
IBattle Battle { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The index of the side of the Pokemon
|
||||||
|
/// </summary>
|
||||||
|
byte SideIndex { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The index of the position of the Pokemon on the field
|
||||||
|
/// </summary>
|
||||||
|
byte Position { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of opponents the Pokemon has seen this battle.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyList<IPokemon> SeenOpponents { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public class PokemonImpl : ScriptSource, IPokemon
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="PokemonImpl"/>
|
||||||
|
public PokemonImpl(IDynamicLibrary library, ISpecies species, IForm form, AbilityIndex abilityIndex, LevelInt level,
|
||||||
|
uint personalityValue, Gender gender, byte coloring, StringKey natureName)
|
||||||
|
{
|
||||||
|
Library = library;
|
||||||
|
Species = species;
|
||||||
|
Form = form;
|
||||||
|
AbilityIndex = abilityIndex;
|
||||||
|
Level = level;
|
||||||
|
PersonalityValue = personalityValue;
|
||||||
|
Gender = gender;
|
||||||
|
Coloring = coloring;
|
||||||
|
Types = form.Types.ToList();
|
||||||
|
|
||||||
|
Experience = library.StaticLibrary.GrowthRates.CalculateExperience(species.GrowthRate, level);
|
||||||
|
WeightInKm = form.Weight;
|
||||||
|
HeightInMeters = form.Height;
|
||||||
|
Happiness = species.BaseHappiness;
|
||||||
|
if (!library.StaticLibrary.Natures.TryGet(natureName, out var nature))
|
||||||
|
throw new KeyNotFoundException($"Nature {natureName} not found.");
|
||||||
|
Nature = nature;
|
||||||
|
|
||||||
|
RecalculateFlatStats();
|
||||||
|
CurrentHealth = BoostedStats.Hp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IDynamicLibrary Library { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ISpecies Species { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IForm Form { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ISpecies? DisplaySpecies { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IForm? DisplayForm { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public LevelInt Level { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public uint Experience { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public uint PersonalityValue { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Gender Gender { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public byte Coloring { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IItem? HeldItem { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public uint CurrentHealth { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public float WeightInKm { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public float HeightInMeters { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public byte Happiness { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public StatisticSet<uint> FlatStats { get; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public StatBoostStatisticSet StatBoost { get; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public StatisticSet<uint> BoostedStats { get; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IndividualValueStatisticSet IndividualValues { get; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public EffortValueStatisticSet EffortValues { get; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public INature Nature { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string? Nickname { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public AbilityIndex AbilityIndex { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IAbility? OverrideAbility { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IPokemonBattleData? BattleData { get; private set; }
|
||||||
|
|
||||||
|
private readonly ILearnedMove?[] _learnedMoves = new ILearnedMove[Const.MovesCount];
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IReadOnlyList<ILearnedMove?> Moves => _learnedMoves;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool AllowedExperience { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IReadOnlyList<TypeIdentifier> Types { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsEgg { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsCaught { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ScriptContainer HeldItemTriggerScript { get; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ScriptContainer AbilityScript { get; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ScriptContainer StatusScript { get; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IScriptSet Volatile { get; } = new ScriptSet();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool HasHeldItem(StringKey itemName) => HeldItem?.Name == itemName;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IItem? SetHeldItem(IItem? item)
|
||||||
|
{
|
||||||
|
var previous = HeldItem;
|
||||||
|
HeldItem = item;
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IItem? RemoveHeldItem()
|
||||||
|
{
|
||||||
|
var previous = HeldItem;
|
||||||
|
HeldItem = null;
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool ConsumeHeldItem()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void ChangeStatBoost(Statistic stat, sbyte change, bool selfInflicted)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IAbility ActiveAbility
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (OverrideAbility != null)
|
||||||
|
return OverrideAbility;
|
||||||
|
var ability = Form.GetAbility(AbilityIndex);
|
||||||
|
if (!Library.StaticLibrary.Abilities.TryGet(ability, out var abilityObj))
|
||||||
|
throw new KeyNotFoundException($"Ability {ability} not found.");
|
||||||
|
return abilityObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void RecalculateFlatStats()
|
||||||
|
{
|
||||||
|
Library.StatCalculator.CalculateFlatStats(this, FlatStats);
|
||||||
|
RecalculateBoostedStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void RecalculateBoostedStats()
|
||||||
|
{
|
||||||
|
Library.StatCalculator.CalculateBoostedStats(this, BoostedStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void ChangeSpecies(ISpecies species, IForm form)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void ChangeForm(IForm form)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <remarks>
|
||||||
|
/// Currently this checks the Pokemon is not an egg, not caught, and not fainted.
|
||||||
|
/// </remarks>
|
||||||
|
public bool IsUsable => !IsCaught && !IsEgg && !IsFainted;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsFainted => CurrentHealth == 0;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Damage(uint damage, DamageSource source, EventBatchId batchId)
|
||||||
|
{
|
||||||
|
// If the Pokemon 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
|
||||||
|
// underflow.
|
||||||
|
if (damage >= CurrentHealth)
|
||||||
|
damage = CurrentHealth;
|
||||||
|
// Calculate the new health.
|
||||||
|
var newHealth = CurrentHealth - damage;
|
||||||
|
if (BattleData is not null)
|
||||||
|
{
|
||||||
|
// If the Pokémon is in a battle, we trigger an event to the front-end.
|
||||||
|
BattleData.Battle.EventHook.Invoke(new DamageEvent(this, CurrentHealth, newHealth, source)
|
||||||
|
{
|
||||||
|
BatchId = batchId
|
||||||
|
});
|
||||||
|
// And allow scripts to execute.
|
||||||
|
this.RunScriptHook(script => script.OnDamage(this, source, CurrentHealth, newHealth));
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentHealth = newHealth;
|
||||||
|
|
||||||
|
// If the Pokémon is now fainted, we also run faint handling.
|
||||||
|
if (IsFainted)
|
||||||
|
{
|
||||||
|
OnFaint(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFaint(DamageSource source)
|
||||||
|
{
|
||||||
|
// If the Pokémon is not in a battle, we don't need to do anything.
|
||||||
|
if (BattleData is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Trigger the faint event to the front-end.
|
||||||
|
BattleData.Battle.EventHook.Invoke(new FaintEvent(this));
|
||||||
|
|
||||||
|
// Allow scripts to trigger based on the faint.
|
||||||
|
this.RunScriptHook(script => script.OnFaint(this, source));
|
||||||
|
// Make sure the OnRemove script is run.
|
||||||
|
this.RunScriptHook(script => script.OnRemove());
|
||||||
|
|
||||||
|
// Mark the position as unfillable if it can't be filled by any party.
|
||||||
|
if (!BattleData.Battle.CanSlotBeFilled(BattleData.SideIndex, BattleData.Position))
|
||||||
|
{
|
||||||
|
BattleData.Battle.Sides[BattleData.SideIndex].MarkPositionAsUnfillable(BattleData.Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the battle state to see if the battle is over.
|
||||||
|
BattleData.Battle.ValidateBattleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Heal(uint heal, bool allowRevive)
|
||||||
|
{
|
||||||
|
if (IsFainted && !allowRevive)
|
||||||
|
return false;
|
||||||
|
var maxAmount = this.BoostedStats.Hp - CurrentHealth;
|
||||||
|
if (heal > maxAmount)
|
||||||
|
heal = maxAmount;
|
||||||
|
if (heal == 0)
|
||||||
|
return false;
|
||||||
|
var newHealth = CurrentHealth + heal;
|
||||||
|
BattleData?.Battle.EventHook.Invoke(new HealEvent(this, CurrentHealth, newHealth));
|
||||||
|
CurrentHealth = newHealth;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <remarks>
|
||||||
|
/// If the index is 255, it will try to find the first empty move slot.
|
||||||
|
/// </remarks>
|
||||||
|
public void LearnMove(string moveName, MoveLearnMethod method, byte index)
|
||||||
|
{
|
||||||
|
if (index == 255)
|
||||||
|
{
|
||||||
|
for (byte i = 0; i < Moves.Count; i++)
|
||||||
|
{
|
||||||
|
if (Moves[i] is not null) continue;
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index >= Moves.Count)
|
||||||
|
throw new InvalidOperationException("No empty move slot found.");
|
||||||
|
if (!Library.StaticLibrary.Moves.TryGet(moveName, out var move))
|
||||||
|
throw new KeyNotFoundException($"Move {moveName} not found.");
|
||||||
|
|
||||||
|
_learnedMoves[index] = new LearnedMoveImpl(move, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void ClearStatus()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void ChangeLevelBy(int change)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override int ScriptCount
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var c = 4;
|
||||||
|
if (BattleData != null)
|
||||||
|
{
|
||||||
|
var side = BattleData.Battle.Sides[BattleData.SideIndex];
|
||||||
|
c += side.ScriptCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts)
|
||||||
|
{
|
||||||
|
scripts.Add(HeldItemTriggerScript);
|
||||||
|
scripts.Add(AbilityScript);
|
||||||
|
scripts.Add(StatusScript);
|
||||||
|
scripts.Add(Volatile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void CollectScripts(List<IEnumerable<ScriptContainer>> scripts)
|
||||||
|
{
|
||||||
|
GetOwnScripts(scripts);
|
||||||
|
if (BattleData != null)
|
||||||
|
{
|
||||||
|
var side = BattleData.Battle.Sides[BattleData.SideIndex];
|
||||||
|
side.CollectScripts(scripts);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -20,7 +20,7 @@ public abstract class Script
|
|||||||
/// The name of a script is its unique identifier. This should generally be set on load, and be
|
/// The name of a script is its unique identifier. This should generally be set on load, and be
|
||||||
/// the same as the key that was used to load it.
|
/// the same as the key that was used to load it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract string Name { get; }
|
public abstract StringKey Name { get; }
|
||||||
|
|
||||||
public bool MarkForDeletion() => _markedForDeletion = true;
|
public bool MarkForDeletion() => _markedForDeletion = true;
|
||||||
public bool IsMarkedForDeletion() => _markedForDeletion;
|
public bool IsMarkedForDeletion() => _markedForDeletion;
|
||||||
|
@ -9,6 +9,17 @@ namespace PkmnLib.Dynamic.ScriptHandling;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ScriptContainer : IEnumerable<ScriptContainer>
|
public class ScriptContainer : IEnumerable<ScriptContainer>
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc cref="ScriptContainer"/>
|
||||||
|
public ScriptContainer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ScriptContainer"/>
|
||||||
|
public ScriptContainer(Script script)
|
||||||
|
{
|
||||||
|
Script = script;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this container is empty.
|
/// Whether this container is empty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections;
|
||||||
using PkmnLib.Static.Utils;
|
using PkmnLib.Static.Utils;
|
||||||
|
|
||||||
namespace PkmnLib.Dynamic.ScriptHandling;
|
namespace PkmnLib.Dynamic.ScriptHandling;
|
||||||
@ -19,7 +20,7 @@ public interface IScriptSet : IEnumerable<ScriptContainer>
|
|||||||
/// Adds a script with a name to the set. If the script with that name already exists in this
|
/// Adds a script with a name to the set. If the script with that name already exists in this
|
||||||
/// set, this makes that script stack instead. The return value here is that script.
|
/// set, this makes that script stack instead. The return value here is that script.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ScriptContainer? Add(StringKey scriptKey);
|
ScriptContainer? StackOrAdd(StringKey scriptKey, Func<Script> instantiation);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a script from the set using its unique name.
|
/// Gets a script from the set using its unique name.
|
||||||
@ -39,7 +40,7 @@ public interface IScriptSet : IEnumerable<ScriptContainer>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the set has a script with the given name.
|
/// Checks if the set has a script with the given name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Contains(StringKey scriptKey);
|
bool Contains(StringKey scriptKey);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a script from the set at a specific index.
|
/// Gets a script from the set at a specific index.
|
||||||
@ -56,3 +57,79 @@ public interface IScriptSet : IEnumerable<ScriptContainer>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
IEnumerable<StringKey> GetScriptNames();
|
IEnumerable<StringKey> GetScriptNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IScriptSet"/>
|
||||||
|
public class ScriptSet : IScriptSet
|
||||||
|
{
|
||||||
|
private readonly List<ScriptContainer> _scripts = [];
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerator<ScriptContainer> GetEnumerator() => _scripts.GetEnumerator();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ScriptContainer Add(Script script)
|
||||||
|
{
|
||||||
|
var existing = _scripts.FirstOrDefault(s => s.Script?.Name == script.Name);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
existing.Script!.Stack();
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
var container = new ScriptContainer(script);
|
||||||
|
_scripts.Add(container);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ScriptContainer? StackOrAdd(StringKey scriptKey, Func<Script?> instantiation)
|
||||||
|
{
|
||||||
|
var existing = _scripts.FirstOrDefault(s => s.Script?.Name == scriptKey);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
existing.Script!.Stack();
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
var script = instantiation();
|
||||||
|
if (script is null)
|
||||||
|
return null;
|
||||||
|
var container = new ScriptContainer(script);
|
||||||
|
_scripts.Add(container);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ScriptContainer? Get(StringKey scriptKey) => _scripts.FirstOrDefault(s => s.Script?.Name == scriptKey);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Remove(StringKey scriptKey)
|
||||||
|
{
|
||||||
|
var script = _scripts.FirstOrDefault(s => s.Script?.Name == scriptKey);
|
||||||
|
if (script is null)
|
||||||
|
return;
|
||||||
|
_scripts.Remove(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Clear() => _scripts.Clear();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Contains(StringKey scriptKey) => _scripts.Any(s => s.Script?.Name == scriptKey);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ScriptContainer At(int index) => _scripts[index];
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int Count => _scripts.Count;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<StringKey> GetScriptNames() =>
|
||||||
|
_scripts
|
||||||
|
.Select(x => x.Script)
|
||||||
|
.WhereNotNull()
|
||||||
|
.Select(s => s.Name);
|
||||||
|
}
|
@ -24,7 +24,38 @@ public interface IReadOnlyGrowthRateLibrary
|
|||||||
/// Whether the library is empty.
|
/// Whether the library is empty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsEmpty { get; }
|
bool IsEmpty { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the experience for a given growth key name and a certain level.
|
||||||
|
/// </summary>
|
||||||
|
uint CalculateExperience(StringKey key, LevelInt level);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the level for a given growth key name and a certain experience.
|
||||||
|
/// </summary>
|
||||||
|
LevelInt CalculateLevel(StringKey key, uint experience);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IReadOnlyGrowthRateLibrary"/>
|
/// <inheritdoc cref="IReadOnlyGrowthRateLibrary"/>
|
||||||
public class GrowthRateLibrary : DataLibrary<IGrowthRate>, IReadOnlyGrowthRateLibrary;
|
public class GrowthRateLibrary : DataLibrary<IGrowthRate>, IReadOnlyGrowthRateLibrary
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public uint CalculateExperience(StringKey key, LevelInt level)
|
||||||
|
{
|
||||||
|
if (!TryGet(key, out var growthRate))
|
||||||
|
{
|
||||||
|
throw new KeyNotFoundException($"Growth rate {key} not found.");
|
||||||
|
}
|
||||||
|
return growthRate.CalculateExperience(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public LevelInt CalculateLevel(StringKey key, uint experience)
|
||||||
|
{
|
||||||
|
if (!TryGet(key, out var growthRate))
|
||||||
|
{
|
||||||
|
throw new KeyNotFoundException($"Growth rate {key} not found.");
|
||||||
|
}
|
||||||
|
return growthRate.CalculateLevel(experience);
|
||||||
|
}
|
||||||
|
}
|
@ -75,6 +75,10 @@ public record ImmutableStatisticSet<T>
|
|||||||
public record StatisticSet<T> : ImmutableStatisticSet<T>
|
public record StatisticSet<T> : ImmutableStatisticSet<T>
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
|
public StatisticSet() : base(default, default, default, default, default, default)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="StatisticSet{T}"/>
|
/// <inheritdoc cref="StatisticSet{T}"/>
|
||||||
public StatisticSet(T hp, T attack, T defense, T specialAttack, T specialDefense, T speed) : base(hp, attack,
|
public StatisticSet(T hp, T attack, T defense, T specialAttack, T specialDefense, T speed) : base(hp, attack,
|
||||||
defense, specialAttack, specialDefense, speed)
|
defense, specialAttack, specialDefense, speed)
|
||||||
@ -266,6 +270,10 @@ public record StatBoostStatisticSet : ClampedStatisticSet<sbyte>
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override sbyte Max => 6;
|
protected override sbyte Max => 6;
|
||||||
|
|
||||||
|
public StatBoostStatisticSet() : base(0, 0, 0, 0, 0, 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="StatBoostStatisticSet"/>
|
/// <inheritdoc cref="StatBoostStatisticSet"/>
|
||||||
public StatBoostStatisticSet(sbyte hp, sbyte attack, sbyte defense, sbyte specialAttack, sbyte specialDefense,
|
public StatBoostStatisticSet(sbyte hp, sbyte attack, sbyte defense, sbyte specialAttack, sbyte specialDefense,
|
||||||
sbyte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed)
|
sbyte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed)
|
||||||
@ -284,6 +292,10 @@ public record IndividualValueStatisticSet : ClampedStatisticSet<byte>
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override byte Max => 31;
|
protected override byte Max => 31;
|
||||||
|
|
||||||
|
public IndividualValueStatisticSet() : base(0, 0, 0, 0, 0, 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IndividualValueStatisticSet"/>
|
/// <inheritdoc cref="IndividualValueStatisticSet"/>
|
||||||
public IndividualValueStatisticSet(byte hp, byte attack, byte defense, byte specialAttack, byte specialDefense,
|
public IndividualValueStatisticSet(byte hp, byte attack, byte defense, byte specialAttack, byte specialDefense,
|
||||||
byte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed)
|
byte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed)
|
||||||
@ -302,6 +314,10 @@ public record EffortValueStatisticSet : ClampedStatisticSet<byte>
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override byte Max => 252;
|
protected override byte Max => 252;
|
||||||
|
|
||||||
|
public EffortValueStatisticSet() : base(0, 0, 0, 0, 0, 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="EffortValueStatisticSet"/>
|
/// <inheritdoc cref="EffortValueStatisticSet"/>
|
||||||
public EffortValueStatisticSet(byte hp, byte attack, byte defense, byte specialAttack, byte specialDefense,
|
public EffortValueStatisticSet(byte hp, byte attack, byte defense, byte specialAttack, byte specialDefense,
|
||||||
byte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed)
|
byte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed)
|
||||||
|
7
PkmnLib.Static/Utils/EnumerableHelpers.cs
Normal file
7
PkmnLib.Static/Utils/EnumerableHelpers.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace PkmnLib.Static.Utils;
|
||||||
|
|
||||||
|
public static class EnumerableHelpers
|
||||||
|
{
|
||||||
|
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> enumerable) where T : class =>
|
||||||
|
enumerable.Where(x => x is not null)!;
|
||||||
|
}
|
@ -15,7 +15,7 @@ namespace PkmnLib.Plugin.Gen7.Moves;
|
|||||||
public class Acrobatics : Script
|
public class Acrobatics : Script
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string Name => "acrobatics";
|
public override StringKey Name => "acrobatics";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref byte basePower)
|
public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref byte basePower)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user