using PkmnLib.Static.Utils; namespace PkmnLib.Dynamic.Models; /// /// 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, and holds the battle-scoped /// wrappers for the party's Pokémon. /// public interface IBattleParty : IDeepCloneable { /// /// The backing Pokemon party. Battle code should generally use instead, as that /// view contains the battle-scoped wrappers. /// IPokemonParty Party { get; } /// /// The battle-scoped view of the party. Index-aligned with . Only available after /// has been called by the battle. /// IReadOnlyList BattlePokemon { get; } /// /// 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. /// IBattlePokemon? GetBattlePokemon(IPokemon pokemon); /// /// 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. /// void Initialize(IBattle battle); /// /// Whether the party is responsible for the specified side and position. /// bool IsResponsibleForIndex(ResponsibleIndex index); /// /// Whether the party has a living Pokemon left that is not in the field. /// bool HasUsablePokemonNotInField(); /// /// Gets all usable Pokemon that are not currently in the field. /// IEnumerable GetUsablePokemonNotInField(); } /// /// A position on the battlefield that a party is responsible for. /// Contains the index of the side and the position on that side. /// public record struct ResponsibleIndex(byte Side, byte Position); /// public class BattlePartyImpl : IBattleParty { private readonly ResponsibleIndex[] _responsibleIndices; private IBattlePokemon?[] _battlePokemon = []; private IBattle? _battle; /// public BattlePartyImpl(IPokemonParty party, ResponsibleIndex[] responsibleIndices) { Party = party; _responsibleIndices = responsibleIndices; } /// public IPokemonParty Party { get; } /// public IReadOnlyList BattlePokemon => _battlePokemon; /// public IBattlePokemon? GetBattlePokemon(IPokemon pokemon) => _battlePokemon.FirstOrDefault(x => x != null && (x == pokemon || x.UnderlyingPokemon == pokemon)); /// 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]); }; } /// public bool IsResponsibleForIndex(ResponsibleIndex index) => _responsibleIndices.Contains(index); /// public bool HasUsablePokemonNotInField() => _battlePokemon.WhereNotNull().Any(x => x.IsUsable && !x.IsOnBattlefield); /// public IEnumerable GetUsablePokemonNotInField() => _battlePokemon.WhereNotNull().Where(x => x.IsUsable && !x.IsOnBattlefield); }