801 lines
22 KiB
C#
801 lines
22 KiB
C#
using JetBrains.Annotations;
|
|
using PkmnLib.Dynamic.Events;
|
|
using PkmnLib.Dynamic.Libraries;
|
|
using PkmnLib.Dynamic.ScriptHandling;
|
|
using PkmnLib.Static;
|
|
using PkmnLib.Static.Species;
|
|
using PkmnLib.Static.Utils;
|
|
|
|
namespace PkmnLib.Dynamic.Models;
|
|
|
|
/// <summary>
|
|
/// The data of a Pokemon.
|
|
/// </summary>
|
|
public interface IPokemon : IScriptSource
|
|
{
|
|
/// <summary>
|
|
/// The library data of the Pokemon.
|
|
/// </summary>
|
|
IDynamicLibrary Library { get; }
|
|
|
|
/// <summary>
|
|
/// The species of the Pokemon.
|
|
/// </summary>
|
|
ISpecies Species { get; }
|
|
|
|
/// <summary>
|
|
/// The form of the Pokemon.
|
|
/// </summary>
|
|
IForm Form { get; }
|
|
|
|
/// <summary>
|
|
/// An optional display species of the Pokemon. If this is set, the client should display this
|
|
/// species. An example of usage for this is the Illusion ability.
|
|
/// </summary>
|
|
ISpecies? DisplaySpecies { get; }
|
|
|
|
/// <summary>
|
|
/// An optional display form of the Pokemon. If this is set, the client should display this
|
|
/// form. An example of usage for this is the Illusion ability.
|
|
/// </summary>
|
|
IForm? DisplayForm { get; }
|
|
|
|
/// <summary>
|
|
/// The current level of the Pokemon.
|
|
/// </summary>
|
|
LevelInt Level { get; }
|
|
|
|
/// <summary>
|
|
/// The amount of experience of the Pokemon.
|
|
/// </summary>
|
|
uint Experience { get; }
|
|
|
|
/// <summary>
|
|
/// The personality value of the Pokemon.
|
|
/// </summary>
|
|
uint PersonalityValue { get; }
|
|
|
|
/// <summary>
|
|
/// The gender of the Pokemon.
|
|
/// </summary>
|
|
Gender Gender { get; }
|
|
|
|
/// <summary>
|
|
/// The coloring of the Pokemon. Value 0 is the default, value 1 means shiny. Other values are
|
|
/// currently not used, and can be used for other implementations.
|
|
/// </summary>
|
|
byte Coloring { get; }
|
|
|
|
/// <summary>
|
|
/// The held item of the Pokemon.
|
|
/// </summary>
|
|
IItem? HeldItem { get; }
|
|
|
|
/// <summary>
|
|
/// The remaining health points of the Pokemon.
|
|
/// </summary>
|
|
uint CurrentHealth { get; }
|
|
|
|
/// <summary>
|
|
/// The weight of the Pokemon in kilograms.
|
|
/// </summary>
|
|
float WeightInKm { get; set; }
|
|
|
|
/// <summary>
|
|
/// The height of the Pokemon in meters.
|
|
/// </summary>
|
|
float HeightInMeters { get; set; }
|
|
|
|
/// <summary>
|
|
/// The happiness of the Pokemon. Also known as friendship.
|
|
/// </summary>
|
|
byte Happiness { get; }
|
|
|
|
/// <summary>
|
|
/// The stats of the Pokemon when disregarding any stat boosts.
|
|
/// </summary>
|
|
StatisticSet<uint> FlatStats { get; }
|
|
|
|
/// <summary>
|
|
/// The statistics boosts of the Pokemon. Will prevent the value from going above 6, and below
|
|
/// -6.
|
|
/// </summary>
|
|
StatBoostStatisticSet StatBoost { get; }
|
|
|
|
/// <summary>
|
|
/// The stats of the Pokemon including the stat boosts
|
|
/// </summary>
|
|
StatisticSet<uint> BoostedStats { get; }
|
|
|
|
/// <summary>
|
|
/// The individual values of the Pokemon.
|
|
/// </summary>
|
|
IndividualValueStatisticSet IndividualValues { get; }
|
|
|
|
/// <summary>
|
|
/// The effort values of the Pokemon.
|
|
/// </summary>
|
|
EffortValueStatisticSet EffortValues { get; }
|
|
|
|
/// <summary>
|
|
/// The nature of the Pokemon.
|
|
/// </summary>
|
|
INature Nature { get; }
|
|
|
|
/// <summary>
|
|
/// An optional nickname of the Pokemon.
|
|
/// </summary>
|
|
string? Nickname { get; }
|
|
|
|
/// <summary>
|
|
/// An index of the ability to find the actual ability on the form.
|
|
/// </summary>
|
|
AbilityIndex AbilityIndex { get; }
|
|
|
|
/// <summary>
|
|
/// An ability can be overriden to an arbitrary ability. This is for example used for the Mummy
|
|
/// ability.
|
|
/// </summary>
|
|
IAbility? OverrideAbility { get; }
|
|
|
|
/// <summary>
|
|
/// If in battle, we have additional data.
|
|
/// </summary>
|
|
IPokemonBattleData? BattleData { get; }
|
|
|
|
/// <summary>
|
|
/// The moves the Pokemon has learned. This is of a set length of <see cref="Const.MovesCount"/>. Empty move slots
|
|
/// are null.
|
|
/// </summary>
|
|
IReadOnlyList<ILearnedMove?> Moves { get; }
|
|
|
|
/// <summary>
|
|
/// Whether or not the Pokemon is allowed to gain experience.
|
|
/// </summary>
|
|
bool AllowedExperience { get; }
|
|
|
|
/// <summary>
|
|
/// The current types of the Pokemon.
|
|
/// </summary>
|
|
IReadOnlyList<TypeIdentifier> Types { get; }
|
|
|
|
/// <summary>
|
|
/// Whether or not this Pokemon is an egg.
|
|
/// </summary>
|
|
bool IsEgg { get; }
|
|
|
|
/// <summary>
|
|
/// Whether or not this Pokemon was caught this battle.
|
|
/// </summary>
|
|
bool IsCaught { get; }
|
|
|
|
/// <summary>
|
|
/// The script for the held item.
|
|
/// </summary>
|
|
ScriptContainer HeldItemTriggerScript { get; }
|
|
|
|
/// <summary>
|
|
/// The script for the ability.
|
|
/// </summary>
|
|
ScriptContainer AbilityScript { get; }
|
|
|
|
/// <summary>
|
|
/// The script for the status.
|
|
/// </summary>
|
|
ScriptContainer StatusScript { get; }
|
|
|
|
/// <summary>
|
|
/// The volatile status scripts of the Pokemon.
|
|
/// </summary>
|
|
IScriptSet Volatile { get; }
|
|
|
|
/// <summary>
|
|
/// Checks whether the Pokemon is holding an item with a specific name.
|
|
/// </summary>
|
|
bool HasHeldItem(StringKey itemName);
|
|
|
|
/// <summary>
|
|
/// Changes the held item of the Pokemon. Returns the previously held item.
|
|
/// </summary>
|
|
[MustUseReturnValue]
|
|
IItem? SetHeldItem(IItem? item);
|
|
|
|
/// <summary>
|
|
/// Removes the held item from the Pokemon. Returns the previously held item.
|
|
/// </summary>
|
|
[MustUseReturnValue]
|
|
IItem? RemoveHeldItem();
|
|
|
|
/// <summary>
|
|
/// Makes the Pokemon uses its held item. Returns whether the item was consumed.
|
|
/// </summary>
|
|
bool ConsumeHeldItem();
|
|
|
|
/// <summary>
|
|
/// Change a boosted stat by a certain amount.
|
|
/// </summary>
|
|
/// <param name="stat">The stat to be changed</param>
|
|
/// <param name="change">The amount to change the stat by</param>
|
|
/// <param name="selfInflicted">Whether the change was self-inflicted. This can be relevant in scripts.</param>
|
|
bool ChangeStatBoost(Statistic stat, sbyte change, bool selfInflicted);
|
|
|
|
/// <summary>
|
|
/// Returns the currently active ability.
|
|
/// </summary>
|
|
IAbility ActiveAbility { get; }
|
|
|
|
/// <summary>
|
|
/// Calculates the flat stats on the Pokemon. This should be called when for example the base
|
|
/// stats, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted
|
|
/// stats, as those depend on the flat stats.
|
|
/// </summary>
|
|
void RecalculateFlatStats();
|
|
|
|
/// <summary>
|
|
/// Calculates the boosted stats on the Pokemon, _without_ recalculating the flat stats.
|
|
/// This should be called when a stat boost changes.
|
|
/// </summary>
|
|
void RecalculateBoostedStats();
|
|
|
|
/// <summary>
|
|
/// Change the species of the Pokemon.
|
|
/// </summary>
|
|
void ChangeSpecies(ISpecies species, IForm form);
|
|
|
|
/// <summary>
|
|
/// Change the form of the Pokemon.
|
|
/// </summary>
|
|
void ChangeForm(IForm form);
|
|
|
|
/// <summary>
|
|
/// Whether the Pokemon is useable in a battle.
|
|
/// </summary>
|
|
bool IsUsable { get; }
|
|
|
|
/// <summary>
|
|
/// Whether the Pokemon is fainted.
|
|
/// </summary>
|
|
bool IsFainted { get; }
|
|
|
|
/// <summary>
|
|
/// Damages the Pokemon by a certain amount of damage, from a damage source.
|
|
/// </summary>
|
|
void Damage(uint damage, DamageSource source, EventBatchId batchId);
|
|
|
|
/// <summary>
|
|
/// Heals the Pokemon by a specific amount. Unless allow_revive is set to true, this will not
|
|
/// heal if the Pokemon has 0 health. If the amount healed is 0, this will return false.
|
|
/// </summary>
|
|
bool Heal(uint heal, bool allowRevive);
|
|
|
|
/// <summary>
|
|
/// Learn a move by name.
|
|
/// </summary>
|
|
void LearnMove(StringKey moveName, MoveLearnMethod method, byte index);
|
|
|
|
/// <summary>
|
|
/// Removes the current non-volatile status from the Pokemon.
|
|
/// </summary>
|
|
void ClearStatus();
|
|
|
|
/// <summary>
|
|
/// Modifies the level by a certain amount
|
|
/// </summary>
|
|
void ChangeLevelBy(int change);
|
|
|
|
/// <summary>
|
|
/// Sets the current battle the Pokémon is in.
|
|
/// </summary>
|
|
void SetBattleData(IBattle battle, byte sideIndex);
|
|
|
|
/// <summary>
|
|
/// Sets whether the Pokémon is on the battlefield.
|
|
/// </summary>
|
|
void SetOnBattlefield(bool onBattleField);
|
|
|
|
/// <summary>
|
|
/// Sets the position the Pokémon has within its side.
|
|
/// </summary>
|
|
/// <param name="position"></param>
|
|
void SetBattleSidePosition(byte position);
|
|
|
|
/// <summary>
|
|
/// Marks a Pokemon as seen in the battle.
|
|
/// </summary>
|
|
void MarkOpponentAsSeen(IPokemon pokemon);
|
|
|
|
// TODO: (de)serialize
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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>
|
|
public interface IPokemonBattleData
|
|
{
|
|
/// <summary>
|
|
/// The battle the Pokemon is in.
|
|
/// </summary>
|
|
IBattle Battle { get; internal set; }
|
|
|
|
/// <summary>
|
|
/// The index of the side of the Pokemon
|
|
/// </summary>
|
|
byte SideIndex { get; internal set; }
|
|
|
|
/// <summary>
|
|
/// The index of the position of the Pokemon on the field
|
|
/// </summary>
|
|
byte Position { get; internal set; }
|
|
|
|
/// <summary>
|
|
/// A list of opponents the Pokemon has seen this battle.
|
|
/// </summary>
|
|
IReadOnlyList<IPokemon> SeenOpponents { get; }
|
|
|
|
/// <summary>
|
|
/// Whether the Pokemon is on the battlefield.
|
|
/// </summary>
|
|
bool IsOnBattlefield { get; set; }
|
|
|
|
/// <summary>
|
|
/// Adds an opponent to the list of seen opponents.
|
|
/// </summary>
|
|
void MarkOpponentAsSeen(IPokemon opponent);
|
|
}
|
|
|
|
/// <inheritdoc cref="IPokemon"/>
|
|
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; private set; }
|
|
|
|
/// <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()
|
|
{
|
|
if (HeldItem is null)
|
|
return false;
|
|
if (!Library.ScriptResolver.TryResolveItemScript(HeldItem, out _))
|
|
return false;
|
|
// TODO: actually consume the item
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool ChangeStatBoost(Statistic stat, sbyte change, bool selfInflicted)
|
|
{
|
|
var prevented = false;
|
|
this.RunScriptHook(script => script.PreventStatBoostChange(this, stat, change, selfInflicted, ref prevented));
|
|
if (prevented)
|
|
return false;
|
|
this.RunScriptHook(script => script.ChangeStatBoostChange(this, stat, selfInflicted, ref change));
|
|
if (change == 0)
|
|
return false;
|
|
var changed = false;
|
|
var oldBoost = StatBoost.GetStatistic(stat);
|
|
changed = change switch
|
|
{
|
|
> 0 => StatBoost.IncreaseStatistic(stat, change),
|
|
< 0 => StatBoost.DecreaseStatistic(stat, change),
|
|
_ => changed,
|
|
};
|
|
if (!changed)
|
|
return false;
|
|
if (BattleData != null)
|
|
{
|
|
var newBoost = StatBoost.GetStatistic(stat);
|
|
BattleData.Battle.EventHook.Invoke(new StatBoostEvent(this, stat, oldBoost, newBoost));
|
|
}
|
|
|
|
RecalculateBoostedStats();
|
|
return true;
|
|
}
|
|
|
|
/// <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(StringKey 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() => StatusScript.Clear();
|
|
|
|
/// <inheritdoc />
|
|
public void ChangeLevelBy(int change)
|
|
{
|
|
var newLevel = Level + change;
|
|
Level = (LevelInt)Math.Clamp(newLevel, 1, Library.StaticLibrary.Settings.MaxLevel);
|
|
RecalculateFlatStats();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void SetBattleData(IBattle battle, byte sideIndex)
|
|
{
|
|
if (BattleData is not null)
|
|
{
|
|
BattleData.Battle = battle;
|
|
BattleData.SideIndex = sideIndex;
|
|
}
|
|
else
|
|
{
|
|
BattleData = new PokemonBattleDataImpl(battle, sideIndex);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void SetOnBattlefield(bool onBattleField)
|
|
{
|
|
if (BattleData is not null)
|
|
{
|
|
BattleData.IsOnBattlefield = onBattleField;
|
|
if (!onBattleField)
|
|
{
|
|
Volatile.Clear();
|
|
WeightInKm = Form.Weight;
|
|
HeightInMeters = Form.Height;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void SetBattleSidePosition(byte position)
|
|
{
|
|
if (BattleData is not null)
|
|
{
|
|
BattleData.Position = position;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void MarkOpponentAsSeen(IPokemon pokemon) => BattleData?.MarkOpponentAsSeen(pokemon);
|
|
|
|
/// <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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public class PokemonBattleDataImpl : IPokemonBattleData
|
|
{
|
|
/// <inheritdoc cref="PokemonBattleDataImpl"/>
|
|
public PokemonBattleDataImpl(IBattle battle, byte sideIndex)
|
|
{
|
|
Battle = battle;
|
|
SideIndex = sideIndex;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IBattle Battle { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public byte SideIndex { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public byte Position { get; set; }
|
|
|
|
private readonly List<IPokemon> _seenOpponents = [];
|
|
/// <inheritdoc />
|
|
public IReadOnlyList<IPokemon> SeenOpponents => _seenOpponents;
|
|
|
|
/// <inheritdoc />
|
|
public bool IsOnBattlefield { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public void MarkOpponentAsSeen(IPokemon opponent)
|
|
{
|
|
_seenOpponents.Add(opponent);
|
|
}
|
|
} |