Move all battle state from IPokemon to an ephemeral IBattlePokemon wrapper
This commit is contained in:
@@ -92,7 +92,7 @@ public interface IBattle : IScriptSource, IDeepCloneable, IDisposable
|
||||
/// <summary>
|
||||
/// Get a Pokemon on the battlefield, on a specific side and an index on that side.
|
||||
/// </summary>
|
||||
IPokemon? GetPokemon(byte side, byte position);
|
||||
IBattlePokemon? GetPokemon(byte side, byte position);
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a slot on the battlefield can still be filled. If no party is responsible
|
||||
@@ -116,7 +116,7 @@ public interface IBattle : IScriptSource, IDeepCloneable, IDisposable
|
||||
/// Checks whether a Pokemon has a forced turn choice. If it does, this returns true and the choice
|
||||
/// is set in the out parameter. If it does not, this returns false and the out parameter is null.
|
||||
/// </summary>
|
||||
bool HasForcedTurn(IPokemon pokemon, [NotNullWhen(true)] out ITurnChoice? choice);
|
||||
bool HasForcedTurn(IBattlePokemon pokemon, [NotNullWhen(true)] out ITurnChoice? choice);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a choice is actually possible.
|
||||
@@ -202,6 +202,8 @@ public class BattleImpl : ScriptSource, IBattle
|
||||
Sides = sides;
|
||||
Random = randomSeed.HasValue ? new BattleRandomImpl(randomSeed.Value) : new BattleRandomImpl();
|
||||
EventHook = new EventHook();
|
||||
foreach (var party in parties)
|
||||
party.Initialize(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -248,12 +250,22 @@ public class BattleImpl : ScriptSource, IBattle
|
||||
public BattleChoiceQueue? ChoiceQueue { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPokemon? GetPokemon(byte side, byte position) => Sides[side].Pokemon[position];
|
||||
public IBattlePokemon? GetPokemon(byte side, byte position) => Sides[side].Pokemon[position];
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanSlotBeFilled(byte side, byte position) => Parties.Any(x =>
|
||||
x.IsResponsibleForIndex(new ResponsibleIndex(side, position)) && x.HasUsablePokemonNotInField());
|
||||
|
||||
private readonly List<IPokemon> _capturedPokemon = [];
|
||||
|
||||
/// <summary>
|
||||
/// Attaches battle-wide result data, such as the captured Pokémon, to a result.
|
||||
/// </summary>
|
||||
private BattleResult FinalizeResult(BattleResult result) => result with
|
||||
{
|
||||
CapturedPokemon = _capturedPokemon.ToList(),
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ValidateBattleState()
|
||||
{
|
||||
@@ -265,7 +277,7 @@ public class BattleImpl : ScriptSource, IBattle
|
||||
{
|
||||
if (side.HasFledBattle)
|
||||
{
|
||||
Result = BattleResult.Inconclusive;
|
||||
Result = FinalizeResult(BattleResult.Inconclusive);
|
||||
HasEnded = true;
|
||||
return;
|
||||
}
|
||||
@@ -283,13 +295,13 @@ public class BattleImpl : ScriptSource, IBattle
|
||||
// If every side is defeated, the battle is a draw
|
||||
if (!survivingSideExists)
|
||||
{
|
||||
Result = BattleResult.Inconclusive;
|
||||
Result = FinalizeResult(BattleResult.Inconclusive);
|
||||
HasEnded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If only one side is left, that side has won
|
||||
Result = BattleResult.Conclusive(survivingSide!.Index);
|
||||
Result = FinalizeResult(BattleResult.Conclusive(survivingSide!.Index));
|
||||
HasEnded = true;
|
||||
}
|
||||
|
||||
@@ -297,22 +309,15 @@ public class BattleImpl : ScriptSource, IBattle
|
||||
public void ForceEndBattle()
|
||||
{
|
||||
HasEnded = true;
|
||||
Result = BattleResult.Inconclusive;
|
||||
Result = FinalizeResult(BattleResult.Inconclusive);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasForcedTurn(IPokemon pokemon, [NotNullWhen(true)] out ITurnChoice? choice)
|
||||
public bool HasForcedTurn(IBattlePokemon pokemon, [NotNullWhen(true)] out ITurnChoice? choice)
|
||||
{
|
||||
var battleData = pokemon.BattleData;
|
||||
if (battleData == null)
|
||||
{
|
||||
choice = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
ITurnChoice? forcedChoice = null;
|
||||
pokemon.RunScriptHook<IScriptForceTurnSelection>(script =>
|
||||
script.ForceTurnSelection(this, battleData.SideIndex, battleData.Position, ref forcedChoice));
|
||||
script.ForceTurnSelection(this, pokemon.SideIndex, pokemon.Position, ref forcedChoice));
|
||||
choice = forcedChoice;
|
||||
return choice != null;
|
||||
}
|
||||
@@ -346,7 +351,7 @@ public class BattleImpl : ScriptSource, IBattle
|
||||
if (!switchChoice.SwitchTo.IsUsable)
|
||||
return false;
|
||||
// Can't switch to a Pokémon already on the field
|
||||
if (switchChoice.SwitchTo.BattleData is { IsOnBattlefield: true })
|
||||
if (switchChoice.SwitchTo.IsOnBattlefield)
|
||||
return false;
|
||||
if (switchChoice.SwitchTo == switchChoice.User)
|
||||
return false;
|
||||
@@ -389,10 +394,10 @@ public class BattleImpl : ScriptSource, IBattle
|
||||
{
|
||||
if (!CanUse(choice))
|
||||
return false;
|
||||
if (choice.User.BattleData?.IsOnBattlefield != true)
|
||||
if (!choice.User.IsOnBattlefield)
|
||||
return false;
|
||||
var side = Sides[choice.User.BattleData!.SideIndex];
|
||||
side.SetChoice(choice.User.BattleData!.Position, choice);
|
||||
var side = Sides[choice.User.SideIndex];
|
||||
side.SetChoice(choice.User.Position, choice);
|
||||
CheckChoicesSetAndRun();
|
||||
return true;
|
||||
}
|
||||
@@ -555,8 +560,8 @@ public class BattleImpl : ScriptSource, IBattle
|
||||
if (attemptCapture.IsCaught)
|
||||
{
|
||||
target.MarkAsCaught();
|
||||
var side = Sides[target.BattleData!.SideIndex];
|
||||
side.ForceClearPokemonFromField(target.BattleData.Position);
|
||||
_capturedPokemon.Add(target.UnderlyingPokemon);
|
||||
target.BattleSide.ForceClearPokemonFromField(target.Position);
|
||||
}
|
||||
EventHook.Invoke(new CaptureAttemptEvent(target, attemptCapture, item));
|
||||
|
||||
@@ -592,9 +597,9 @@ public class BattleImpl : ScriptSource, IBattle
|
||||
{
|
||||
foreach (var party in Parties)
|
||||
{
|
||||
foreach (var pokemon in party.Party.WhereNotNull())
|
||||
foreach (var pokemon in party.BattlePokemon.WhereNotNull())
|
||||
{
|
||||
pokemon.ClearBattleData();
|
||||
pokemon.OnBattleEnd();
|
||||
}
|
||||
}
|
||||
_weatherScript.Clear();
|
||||
|
||||
@@ -87,7 +87,7 @@ public class BattleChoiceQueue : IDeepCloneable
|
||||
/// <returns>
|
||||
/// Returns true if the Pokémon was found and moved, false otherwise.
|
||||
/// </returns>
|
||||
public bool MovePokemonChoiceNext(IPokemon pokemon)
|
||||
public bool MovePokemonChoiceNext(IBattlePokemon pokemon)
|
||||
{
|
||||
var index = Array.FindIndex(_choices, _currentIndex, choice => choice?.User == pokemon);
|
||||
if (index == -1)
|
||||
@@ -110,7 +110,7 @@ public class BattleChoiceQueue : IDeepCloneable
|
||||
/// <returns>
|
||||
/// Returns true if the Pokémon was found and moved, false otherwise.
|
||||
/// </returns>
|
||||
public bool MovePokemonChoiceLast(IPokemon pokemon)
|
||||
public bool MovePokemonChoiceLast(IBattlePokemon pokemon)
|
||||
{
|
||||
var index = Array.FindIndex(_choices, _currentIndex, choice => choice?.User == pokemon);
|
||||
if (index == -1)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
1043
PkmnLib.Dynamic/Models/BattlePokemon.cs
Normal file
1043
PkmnLib.Dynamic/Models/BattlePokemon.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ public interface IBattleRandom : IRandom, IDeepCloneable
|
||||
/// rolls whether it triggers. As a side effect this run scripts to allow modifying this random
|
||||
/// chance.
|
||||
/// </summary>
|
||||
bool EffectChance(float chance, IExecutingMove executingMove, IPokemon target, byte hitNumber);
|
||||
bool EffectChance(float chance, IExecutingMove executingMove, IBattlePokemon target, byte hitNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBattleRandom"/>
|
||||
@@ -36,7 +36,7 @@ public class BattleRandomImpl : RandomImpl, IBattleRandom
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool EffectChance(float chance, IExecutingMove executingMove, IPokemon target, byte hitNumber)
|
||||
public bool EffectChance(float chance, IExecutingMove executingMove, IBattlePokemon target, byte hitNumber)
|
||||
{
|
||||
executingMove.RunScriptHook<IScriptChangeEffectChance>(script =>
|
||||
script.ChangeEffectChance(executingMove, target, hitNumber, ref chance));
|
||||
|
||||
@@ -30,4 +30,11 @@ public record struct BattleResult
|
||||
/// The side that won the battle. If null, no side has won.
|
||||
/// </summary>
|
||||
public byte? WinningSide { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Pokémon that were captured during the battle. These are the underlying Pokémon, so they remain
|
||||
/// valid after the battle has been disposed. This is set for both conclusive and inconclusive results, as
|
||||
/// captures complete the moment they happen.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IPokemon> CapturedPokemon { get; init; } = [];
|
||||
}
|
||||
@@ -24,7 +24,7 @@ public interface IBattleSide : IScriptSource, IDeepCloneable
|
||||
/// <summary>
|
||||
/// A list of Pokémon currently on the battlefield.
|
||||
/// </summary>
|
||||
IReadOnlyList<IPokemon?> Pokemon { get; }
|
||||
IReadOnlyList<IBattlePokemon?> Pokemon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The currently set choices for all Pokémon on the battlefield. Cleared when the turn starts.
|
||||
@@ -84,7 +84,14 @@ public interface IBattleSide : IScriptSource, IDeepCloneable
|
||||
/// Switches out a spot on the field for a different Pokémon. If null is passed, the spot is
|
||||
/// cleared. Returns the Pokémon that was previously in the spot.
|
||||
/// </summary>
|
||||
IPokemon? SwapPokemon(byte position, IPokemon? pokemon);
|
||||
IBattlePokemon? SwapPokemon(byte position, IBattlePokemon? pokemon);
|
||||
|
||||
/// <summary>
|
||||
/// Sends out a Pokémon by its persistent representation. This resolves the battle-scoped wrapper for
|
||||
/// the Pokémon from the parties in the battle, and swaps it into the given position. This is the
|
||||
/// convenient entry point for hosts, which generally hold the persistent party Pokémon.
|
||||
/// </summary>
|
||||
IBattlePokemon? SendOut(byte position, IPokemon pokemon);
|
||||
|
||||
/// <summary>
|
||||
/// Swaps two Pokémon on the side.
|
||||
@@ -94,7 +101,7 @@ public interface IBattleSide : IScriptSource, IDeepCloneable
|
||||
/// <summary>
|
||||
/// Checks whether a Pokemon is on the field in this side.
|
||||
/// </summary>
|
||||
bool IsPokemonOnSide(IPokemon pokemon);
|
||||
bool IsPokemonOnSide(IBattlePokemon pokemon);
|
||||
|
||||
/// <summary>
|
||||
/// Marks a slot as unfillable. This happens when no parties are able to fill the slot anymore.
|
||||
@@ -166,7 +173,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
||||
{
|
||||
Index = index;
|
||||
NumberOfPositions = numberOfPositions;
|
||||
_pokemon = new IPokemon?[numberOfPositions];
|
||||
_pokemon = new IBattlePokemon?[numberOfPositions];
|
||||
_setChoices = new ITurnChoice?[numberOfPositions];
|
||||
_fillablePositions = new bool[numberOfPositions];
|
||||
for (byte i = 0; i < numberOfPositions; i++)
|
||||
@@ -183,10 +190,10 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
||||
/// <inheritdoc />
|
||||
public byte NumberOfPositions { get; }
|
||||
|
||||
private readonly IPokemon?[] _pokemon;
|
||||
private readonly IBattlePokemon?[] _pokemon;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<IPokemon?> Pokemon => _pokemon;
|
||||
public IReadOnlyList<IBattlePokemon?> Pokemon => _pokemon;
|
||||
|
||||
private readonly ITurnChoice?[] _setChoices;
|
||||
|
||||
@@ -249,28 +256,31 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
||||
if (pokemon is not null)
|
||||
{
|
||||
pokemon.RunScriptHook<IScriptOnRemove>(script => script.OnRemove());
|
||||
pokemon.SetOnBattlefield(false);
|
||||
pokemon.OnSwitchedOut();
|
||||
}
|
||||
|
||||
_pokemon[index] = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPokemon? SwapPokemon(byte position, IPokemon? pokemon)
|
||||
public IBattlePokemon? SwapPokemon(byte position, IBattlePokemon? pokemon)
|
||||
{
|
||||
var oldPokemon = _pokemon[position];
|
||||
if (oldPokemon is not null)
|
||||
{
|
||||
oldPokemon.RunScriptHook<IScriptOnSwitchOut>(script => script.OnSwitchOut(oldPokemon, position));
|
||||
oldPokemon.RunScriptHook<IScriptOnRemove>(script => script.OnRemove());
|
||||
oldPokemon.SetOnBattlefield(false);
|
||||
oldPokemon.OnSwitchedOut();
|
||||
}
|
||||
_pokemon[position] = pokemon;
|
||||
if (pokemon is not null)
|
||||
{
|
||||
pokemon.SetBattleData(Battle, Index);
|
||||
pokemon.SetOnBattlefield(true);
|
||||
pokemon.SetBattleSidePosition(position);
|
||||
if (pokemon.SideIndex != Index)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"A battle Pokémon can only be sent out on the side its party is responsible for.");
|
||||
}
|
||||
pokemon.OnSwitchedIn(position);
|
||||
Battle.EventHook.Invoke(new SwitchEvent(Index, position, pokemon));
|
||||
pokemon.RunScriptHook<IScriptOnSwitchIn>(script => script.OnSwitchIn(pokemon, position));
|
||||
|
||||
@@ -300,6 +310,14 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
||||
return oldPokemon;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IBattlePokemon? SendOut(byte position, IPokemon pokemon)
|
||||
{
|
||||
var wrapper = Battle.Parties.Select(p => p.GetBattlePokemon(pokemon)).FirstOrDefault(x => x != null) ??
|
||||
throw new InvalidOperationException("The Pokémon does not belong to any party in this battle.");
|
||||
return SwapPokemon(position, wrapper);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SwapPokemon(byte position1, byte position2)
|
||||
{
|
||||
@@ -307,7 +325,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPokemonOnSide(IPokemon pokemon) => _pokemon.Contains(pokemon);
|
||||
public bool IsPokemonOnSide(IBattlePokemon pokemon) => _pokemon.Contains(pokemon);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void MarkPositionAsUnfillable(byte position) => _fillablePositions[position] = false;
|
||||
|
||||
@@ -13,7 +13,7 @@ public interface IFleeChoice : ITurnChoice
|
||||
public class FleeTurnChoice : TurnChoice, IFleeChoice
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public FleeTurnChoice(IPokemon user) : base(user)
|
||||
public FleeTurnChoice(IBattlePokemon user) : base(user)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -16,15 +16,15 @@ public interface IItemChoice : ITurnChoice
|
||||
/// <summary>
|
||||
/// The target Pokémon of the item, if any.
|
||||
/// </summary>
|
||||
IPokemon? GetTargetPokemon(IBattle battle);
|
||||
IBattlePokemon? GetTargetPokemon(IBattle battle);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IItemChoice"/>
|
||||
public class ItemChoice : TurnChoice, IItemChoice
|
||||
{
|
||||
/// <inheritdoc cref="ItemChoice"/>
|
||||
private ItemChoice(IPokemon user, IItem item, byte? targetSide, byte? targetPosition, IPokemon? targetPokemon) :
|
||||
base(user)
|
||||
private ItemChoice(IBattlePokemon user, IItem item, byte? targetSide, byte? targetPosition,
|
||||
IBattlePokemon? targetPokemon) : base(user)
|
||||
{
|
||||
Item = item;
|
||||
TargetSide = targetSide;
|
||||
@@ -32,13 +32,13 @@ public class ItemChoice : TurnChoice, IItemChoice
|
||||
TargetPokemon = targetPokemon;
|
||||
}
|
||||
|
||||
public static ItemChoice CreateWithoutTarget(IPokemon user, IItem item) =>
|
||||
public static ItemChoice CreateWithoutTarget(IBattlePokemon user, IItem item) =>
|
||||
new(user, item, null, null, null);
|
||||
|
||||
public static ItemChoice CreateForOpponent(IPokemon user, IItem item, byte targetSide, byte targetPosition) =>
|
||||
public static ItemChoice CreateForOpponent(IBattlePokemon user, IItem item, byte targetSide, byte targetPosition) =>
|
||||
new(user, item, targetSide, targetPosition, null);
|
||||
|
||||
public static ItemChoice CreateForPartyMember(IPokemon user, IItem item, IPokemon targetPokemon) =>
|
||||
public static ItemChoice CreateForPartyMember(IBattlePokemon user, IItem item, IBattlePokemon targetPokemon) =>
|
||||
new(user, item, null, null, targetPokemon);
|
||||
|
||||
/// <summary>
|
||||
@@ -47,7 +47,7 @@ public class ItemChoice : TurnChoice, IItemChoice
|
||||
public IItem Item { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPokemon? GetTargetPokemon(IBattle battle)
|
||||
public IBattlePokemon? GetTargetPokemon(IBattle battle)
|
||||
{
|
||||
if (TargetPokemon != null)
|
||||
return TargetPokemon;
|
||||
@@ -71,7 +71,7 @@ public class ItemChoice : TurnChoice, IItemChoice
|
||||
/// <summary>
|
||||
/// The target Pokémon of the item, if any. This is used for party members.
|
||||
/// </summary>
|
||||
private IPokemon? TargetPokemon { get; }
|
||||
private IBattlePokemon? TargetPokemon { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int ScriptCount => User.ScriptCount;
|
||||
|
||||
@@ -51,7 +51,7 @@ public interface IMoveChoice : ITurnChoice
|
||||
public class MoveChoice : TurnChoice, IMoveChoice
|
||||
{
|
||||
/// <inheritdoc cref="MoveChoice"/>
|
||||
public MoveChoice(IPokemon user, ILearnedMove usedMove, byte targetSide, byte targetPosition) : base(user)
|
||||
public MoveChoice(IBattlePokemon user, ILearnedMove usedMove, byte targetSide, byte targetPosition) : base(user)
|
||||
{
|
||||
ChosenMove = usedMove;
|
||||
TargetSide = targetSide;
|
||||
|
||||
@@ -13,7 +13,7 @@ public interface IPassChoice : ITurnChoice
|
||||
public class PassChoice : TurnChoice, IPassChoice
|
||||
{
|
||||
/// <inheritdoc cref="PassChoice"/>
|
||||
public PassChoice(IPokemon user) : base(user)
|
||||
public PassChoice(IBattlePokemon user) : base(user)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -10,20 +10,20 @@ public interface ISwitchChoice : ITurnChoice
|
||||
/// <summary>
|
||||
/// The Pokémon to switch to.
|
||||
/// </summary>
|
||||
IPokemon SwitchTo { get; }
|
||||
IBattlePokemon SwitchTo { get; }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ISwitchChoice"/>
|
||||
public class SwitchChoice : TurnChoice, ISwitchChoice
|
||||
{
|
||||
/// <inheritdoc cref="SwitchChoice"/>
|
||||
public SwitchChoice(IPokemon user, IPokemon switchTo) : base(user)
|
||||
public SwitchChoice(IBattlePokemon user, IBattlePokemon switchTo) : base(user)
|
||||
{
|
||||
SwitchTo = switchTo;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPokemon SwitchTo { get; }
|
||||
public IBattlePokemon SwitchTo { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int ScriptCount => User.ScriptCount;
|
||||
|
||||
@@ -11,7 +11,7 @@ public interface ITurnChoice : IScriptSource, IDeepCloneable
|
||||
/// <summary>
|
||||
/// The user of the turn choice
|
||||
/// </summary>
|
||||
IPokemon User { get; }
|
||||
IBattlePokemon User { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The speed of the user at the beginning of the turn.
|
||||
@@ -34,7 +34,7 @@ public interface ITurnChoice : IScriptSource, IDeepCloneable
|
||||
/// Fails the choice. This will prevent it from executing and run a specific fail handling during
|
||||
/// execution. Note that this can not be undone.
|
||||
/// </summary>
|
||||
public void Fail();
|
||||
void Fail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -43,7 +43,7 @@ public interface ITurnChoice : IScriptSource, IDeepCloneable
|
||||
public abstract class TurnChoice : ScriptSource, ITurnChoice
|
||||
{
|
||||
/// <inheritdoc cref="TurnChoice"/>
|
||||
protected TurnChoice(IPokemon user)
|
||||
protected TurnChoice(IBattlePokemon user)
|
||||
{
|
||||
User = user;
|
||||
}
|
||||
@@ -51,7 +51,7 @@ public abstract class TurnChoice : ScriptSource, ITurnChoice
|
||||
/// <summary>
|
||||
/// The Pokemon for which the choice is made.
|
||||
/// </summary>
|
||||
public IPokemon User { get; }
|
||||
public IBattlePokemon User { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The speed of the user at the beginning of the turn.
|
||||
|
||||
@@ -129,7 +129,7 @@ public interface IExecutingMove : IScriptSource
|
||||
/// <summary>
|
||||
/// The user of the move.
|
||||
/// </summary>
|
||||
IPokemon User { get; }
|
||||
IBattlePokemon User { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The move the user has actually chosen to do.
|
||||
@@ -151,17 +151,17 @@ public interface IExecutingMove : IScriptSource
|
||||
/// <summary>
|
||||
/// Gets a hit data for a target, with a specific index.
|
||||
/// </summary>
|
||||
IHitData GetHitData(IPokemon target, byte hit);
|
||||
IHitData GetHitData(IBattlePokemon target, byte hit);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a Pokémon is a target for this move.
|
||||
/// </summary>
|
||||
bool IsPokemonTarget(IPokemon target);
|
||||
bool IsPokemonTarget(IBattlePokemon target);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the hits in this move where the hits for a specific target start.
|
||||
/// </summary>
|
||||
int GetTargetIndex(IPokemon target);
|
||||
int GetTargetIndex(IBattlePokemon target);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a hit based on its raw index.
|
||||
@@ -171,7 +171,7 @@ public interface IExecutingMove : IScriptSource
|
||||
/// <summary>
|
||||
/// Gets the targets of this move.
|
||||
/// </summary>
|
||||
IReadOnlyList<IPokemon?> Targets { get; }
|
||||
IReadOnlyList<IBattlePokemon?> Targets { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The underlying move choice.
|
||||
@@ -192,12 +192,12 @@ public interface IExecutingMove : IScriptSource
|
||||
/// <inheritdoc cref="IExecutingMove"/>
|
||||
public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
||||
{
|
||||
private readonly IReadOnlyList<IPokemon?> _targets;
|
||||
private readonly IReadOnlyList<IBattlePokemon?> _targets;
|
||||
private readonly IHitData[] _hits;
|
||||
private readonly IBattle _battle;
|
||||
|
||||
/// <inheritdoc cref="ExecutingMoveImpl"/>
|
||||
public ExecutingMoveImpl(IReadOnlyList<IPokemon?> targets, byte numberOfHits, ILearnedMove chosenMove,
|
||||
public ExecutingMoveImpl(IReadOnlyList<IBattlePokemon?> targets, byte numberOfHits, ILearnedMove chosenMove,
|
||||
IMoveData useMove, IMoveChoice moveChoice, IBattle battle)
|
||||
{
|
||||
_targets = targets;
|
||||
@@ -222,7 +222,7 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
||||
public byte NumberOfHits { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPokemon User => MoveChoice.User;
|
||||
public IBattlePokemon User => MoveChoice.User;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ILearnedMove ChosenMove { get; }
|
||||
@@ -239,7 +239,7 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
||||
public IScriptSet Volatile => MoveChoice.Volatile;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IHitData GetHitData(IPokemon target, byte hit)
|
||||
public IHitData GetHitData(IBattlePokemon target, byte hit)
|
||||
{
|
||||
var targetIndex = _targets.IndexOf(target);
|
||||
if (targetIndex == -1)
|
||||
@@ -252,10 +252,10 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPokemonTarget(IPokemon target) => _targets.Contains(target);
|
||||
public bool IsPokemonTarget(IBattlePokemon target) => _targets.Contains(target);
|
||||
|
||||
/// <inheritdoc />
|
||||
public int GetTargetIndex(IPokemon target)
|
||||
public int GetTargetIndex(IBattlePokemon target)
|
||||
{
|
||||
var targetIndex = _targets.IndexOf(target);
|
||||
if (targetIndex == -1)
|
||||
@@ -273,7 +273,7 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<IPokemon?> Targets => _targets.ToList();
|
||||
public IReadOnlyList<IBattlePokemon?> Targets => _targets.ToList();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IMoveChoice MoveChoice { get; }
|
||||
|
||||
@@ -35,29 +35,30 @@ public static class ItemTargetTypeHelpers
|
||||
/// <summary>
|
||||
/// Determines if the given target is valid based on the ItemTargetType.
|
||||
/// </summary>
|
||||
public static bool IsValidTarget(this ItemTargetType targetType, IBattle battle, IPokemon user, IPokemon target)
|
||||
public static bool IsValidTarget(this ItemTargetType targetType, IBattle battle, IBattlePokemon user,
|
||||
IBattlePokemon target)
|
||||
{
|
||||
if (targetType == ItemTargetType.None)
|
||||
return true;
|
||||
|
||||
if (targetType.HasFlag(ItemTargetType.OwnPokemon))
|
||||
{
|
||||
var userParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(user));
|
||||
var targetParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(target));
|
||||
var userParty = battle.Parties.FirstOrDefault(x => x.BattlePokemon.Contains(user));
|
||||
var targetParty = battle.Parties.FirstOrDefault(x => x.BattlePokemon.Contains(target));
|
||||
if (userParty is not null && targetParty is not null && userParty == targetParty)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (targetType.HasFlag(ItemTargetType.AllyPokemon))
|
||||
{
|
||||
if (user.BattleData?.BattleSide == target.BattleData?.BattleSide)
|
||||
if (user.BattleSide == target.BattleSide)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (targetType.HasFlag(ItemTargetType.FoePokemon))
|
||||
{
|
||||
var userParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(user));
|
||||
var targetParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(target));
|
||||
var userParty = battle.Parties.FirstOrDefault(x => x.BattlePokemon.Contains(user));
|
||||
var targetParty = battle.Parties.FirstOrDefault(x => x.BattlePokemon.Contains(target));
|
||||
if (userParty is not null && targetParty is not null && userParty != targetParty)
|
||||
return true;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,7 @@ public record SerializedPokemon
|
||||
Nature = pokemon.Nature.Name;
|
||||
Nickname = pokemon.Nickname;
|
||||
Ability = pokemon.Form.GetAbility(pokemon.AbilityIndex);
|
||||
Moves = pokemon.BaseMoves.Select(move =>
|
||||
Moves = pokemon.Moves.Select(move =>
|
||||
{
|
||||
if (move == null)
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user