Move all battle state from IPokemon to an ephemeral IBattlePokemon wrapper
This commit is contained in:
@@ -4,15 +4,35 @@ namespace PkmnLib.Dynamic.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A battle party is a wrapper around a Pokemon party that provides additional functionality for battles.
|
||||
/// It indicates for which side and position the party is responsible.
|
||||
/// It indicates for which side and position the party is responsible, and holds the battle-scoped
|
||||
/// <see cref="IBattlePokemon"/> wrappers for the party's Pokémon.
|
||||
/// </summary>
|
||||
public interface IBattleParty : IDeepCloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// The backing Pokemon party.
|
||||
/// The backing Pokemon party. Battle code should generally use <see cref="BattlePokemon"/> instead, as that
|
||||
/// view contains the battle-scoped wrappers.
|
||||
/// </summary>
|
||||
IPokemonParty Party { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The battle-scoped view of the party. Index-aligned with <see cref="Party"/>. Only available after
|
||||
/// <see cref="Initialize"/> has been called by the battle.
|
||||
/// </summary>
|
||||
IReadOnlyList<IBattlePokemon?> BattlePokemon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the battle-scoped wrapper for a Pokémon in this party. Returns null if the Pokémon is not in this
|
||||
/// party. Accepts either the underlying Pokémon or the wrapper itself.
|
||||
/// </summary>
|
||||
IBattlePokemon? GetBattlePokemon(IPokemon pokemon);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the battle-scoped wrappers for this party. This is called by the battle when it is created,
|
||||
/// and should not be called by user code.
|
||||
/// </summary>
|
||||
void Initialize(IBattle battle);
|
||||
|
||||
/// <summary>
|
||||
/// Whether the party is responsible for the specified side and position.
|
||||
/// </summary>
|
||||
@@ -26,7 +46,7 @@ public interface IBattleParty : IDeepCloneable
|
||||
/// <summary>
|
||||
/// Gets all usable Pokemon that are not currently in the field.
|
||||
/// </summary>
|
||||
IEnumerable<IPokemon> GetUsablePokemonNotInField();
|
||||
IEnumerable<IBattlePokemon> GetUsablePokemonNotInField();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,6 +59,8 @@ public record struct ResponsibleIndex(byte Side, byte Position);
|
||||
public class BattlePartyImpl : IBattleParty
|
||||
{
|
||||
private readonly ResponsibleIndex[] _responsibleIndices;
|
||||
private IBattlePokemon?[] _battlePokemon = [];
|
||||
private IBattle? _battle;
|
||||
|
||||
/// <inheritdoc cref="BattlePartyImpl"/>
|
||||
public BattlePartyImpl(IPokemonParty party, ResponsibleIndex[] responsibleIndices)
|
||||
@@ -50,14 +72,53 @@ public class BattlePartyImpl : IBattleParty
|
||||
/// <inheritdoc />
|
||||
public IPokemonParty Party { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<IBattlePokemon?> BattlePokemon => _battlePokemon;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IBattlePokemon? GetBattlePokemon(IPokemon pokemon) =>
|
||||
_battlePokemon.FirstOrDefault(x => x != null && (x == pokemon || x.UnderlyingPokemon == pokemon));
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize(IBattle battle)
|
||||
{
|
||||
if (_battle != null)
|
||||
throw new InvalidOperationException("This battle party has already been initialized for a battle.");
|
||||
if (_responsibleIndices.Length == 0)
|
||||
throw new InvalidOperationException("A battle party must be responsible for at least one position.");
|
||||
var sideIndex = _responsibleIndices[0].Side;
|
||||
if (_responsibleIndices.Any(x => x.Side != sideIndex))
|
||||
throw new InvalidOperationException("A battle party can only be responsible for a single side.");
|
||||
|
||||
_battle = battle;
|
||||
_battlePokemon = new IBattlePokemon?[Party.Count];
|
||||
for (var i = 0; i < Party.Count; i++)
|
||||
{
|
||||
var pokemon = Party[i];
|
||||
if (pokemon != null)
|
||||
_battlePokemon[i] = new BattlePokemonImpl(pokemon, battle, sideIndex);
|
||||
}
|
||||
|
||||
Party.OnSwapInto += (_, args) =>
|
||||
{
|
||||
var (pokemon, index) = args;
|
||||
_battlePokemon[index] = pokemon == null ? null : new BattlePokemonImpl(pokemon, battle, sideIndex);
|
||||
};
|
||||
Party.OnSwap += (_, args) =>
|
||||
{
|
||||
var (index1, index2) = args;
|
||||
(_battlePokemon[index1], _battlePokemon[index2]) = (_battlePokemon[index2], _battlePokemon[index1]);
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsResponsibleForIndex(ResponsibleIndex index) => _responsibleIndices.Contains(index);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasUsablePokemonNotInField() =>
|
||||
Party.WhereNotNull().Any(x => x.IsUsable && x.BattleData?.IsOnBattlefield != true);
|
||||
_battlePokemon.WhereNotNull().Any(x => x.IsUsable && !x.IsOnBattlefield);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IPokemon> GetUsablePokemonNotInField() =>
|
||||
Party.WhereNotNull().Where(x => x.IsUsable && x.BattleData?.IsOnBattlefield != true);
|
||||
public IEnumerable<IBattlePokemon> GetUsablePokemonNotInField() =>
|
||||
_battlePokemon.WhereNotNull().Where(x => x.IsUsable && !x.IsOnBattlefield);
|
||||
}
|
||||
Reference in New Issue
Block a user