Move all battle state from IPokemon to an ephemeral IBattlePokemon wrapper
This commit is contained in:
@@ -163,19 +163,19 @@ public static class TestCommandRunner
|
|||||||
var pokemon1 = battle.Sides[0].Pokemon[0];
|
var pokemon1 = battle.Sides[0].Pokemon[0];
|
||||||
while (pokemon1 is null && !battle.HasEnded)
|
while (pokemon1 is null && !battle.HasEnded)
|
||||||
{
|
{
|
||||||
pokemon1 = battle.Parties[0].Party.WhereNotNull().FirstOrDefault(x => x.IsUsable);
|
var replacement1 = battle.Parties[0].BattlePokemon.WhereNotNull().FirstOrDefault(x => x.IsUsable);
|
||||||
if (pokemon1 is null)
|
if (replacement1 is null)
|
||||||
throw new InvalidOperationException("No usable Pokémon found in party 1.");
|
throw new InvalidOperationException("No usable Pokémon found in party 1.");
|
||||||
battle.Sides[0].SwapPokemon(0, pokemon1);
|
battle.Sides[0].SwapPokemon(0, replacement1);
|
||||||
pokemon1 = battle.Sides[0].Pokemon[0];
|
pokemon1 = battle.Sides[0].Pokemon[0];
|
||||||
}
|
}
|
||||||
var pokemon2 = battle.Sides[1].Pokemon[0];
|
var pokemon2 = battle.Sides[1].Pokemon[0];
|
||||||
while (pokemon2 is null && !battle.HasEnded)
|
while (pokemon2 is null && !battle.HasEnded)
|
||||||
{
|
{
|
||||||
pokemon2 = battle.Parties[1].Party.WhereNotNull().FirstOrDefault(x => x.IsUsable);
|
var replacement2 = battle.Parties[1].BattlePokemon.WhereNotNull().FirstOrDefault(x => x.IsUsable);
|
||||||
if (pokemon2 is null)
|
if (replacement2 is null)
|
||||||
throw new InvalidOperationException("No usable Pokémon found in party 2.");
|
throw new InvalidOperationException("No usable Pokémon found in party 2.");
|
||||||
battle.Sides[1].SwapPokemon(0, pokemon2);
|
battle.Sides[1].SwapPokemon(0, replacement2);
|
||||||
pokemon2 = battle.Sides[1].Pokemon[0];
|
pokemon2 = battle.Sides[1].Pokemon[0];
|
||||||
}
|
}
|
||||||
if (pokemon1 is null || pokemon2 is null)
|
if (pokemon1 is null || pokemon2 is null)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public static class AIHelpers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Estimates the amount of damage that will be done by a move against a target.
|
/// Estimates the amount of damage that will be done by a move against a target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static uint CalculateDamageEstimation(IMoveData move, IPokemon user, IPokemon target,
|
public static uint CalculateDamageEstimation(IMoveData move, IBattlePokemon user, IBattlePokemon target,
|
||||||
IDynamicLibrary library)
|
IDynamicLibrary library)
|
||||||
{
|
{
|
||||||
var hitData = new CustomHitData
|
var hitData = new CustomHitData
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace PkmnLib.Dynamic.AI.Explicit;
|
|||||||
public class AIMoveState
|
public class AIMoveState
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="AIMoveState" />
|
/// <inheritdoc cref="AIMoveState" />
|
||||||
public AIMoveState(IPokemon user, IMoveData move)
|
public AIMoveState(IBattlePokemon user, IMoveData move)
|
||||||
{
|
{
|
||||||
User = user;
|
User = user;
|
||||||
Move = move;
|
Move = move;
|
||||||
@@ -18,7 +18,7 @@ public class AIMoveState
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The user that's being wrapper
|
/// The user that's being wrapper
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon User { get; }
|
public IBattlePokemon User { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The move that's being wrapper
|
/// The move that's being wrapper
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace PkmnLib.Dynamic.AI.Explicit;
|
|||||||
|
|
||||||
public partial class ExplicitAI
|
public partial class ExplicitAI
|
||||||
{
|
{
|
||||||
private bool TryChooseToSwitchOut(IBattle battle, IPokemon pokemon, bool terribleMoves,
|
private bool TryChooseToSwitchOut(IBattle battle, IBattlePokemon pokemon, bool terribleMoves,
|
||||||
[NotNullWhen(true)] out ITurnChoice? choice)
|
[NotNullWhen(true)] out ITurnChoice? choice)
|
||||||
{
|
{
|
||||||
choice = null;
|
choice = null;
|
||||||
@@ -17,13 +17,13 @@ public partial class ExplicitAI
|
|||||||
return false;
|
return false;
|
||||||
if (TrainerHighSkill)
|
if (TrainerHighSkill)
|
||||||
{
|
{
|
||||||
var opponentSide = battle.Sides.First(x => x != pokemon.BattleData?.BattleSide);
|
var opponentSide = battle.Sides.First(x => x != pokemon.BattleSide);
|
||||||
var foeCanAct = opponentSide.Pokemon.WhereNotNull().Any(CanAttack);
|
var foeCanAct = opponentSide.Pokemon.WhereNotNull().Any(CanAttack);
|
||||||
if (!foeCanAct)
|
if (!foeCanAct)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var party = battle.Parties.FirstOrDefault(x => x.IsResponsibleForIndex(
|
var party = battle.Parties.FirstOrDefault(x => x.IsResponsibleForIndex(
|
||||||
new ResponsibleIndex(pokemon.BattleData!.SideIndex, pokemon.BattleData.Position)));
|
new ResponsibleIndex(pokemon.SideIndex, pokemon.Position)));
|
||||||
if (party is null)
|
if (party is null)
|
||||||
return false;
|
return false;
|
||||||
var usablePokemon = party.GetUsablePokemonNotInField().ToList();
|
var usablePokemon = party.GetUsablePokemonNotInField().ToList();
|
||||||
@@ -44,20 +44,20 @@ public partial class ExplicitAI
|
|||||||
if (!shouldSwitch)
|
if (!shouldSwitch)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var battleSide = pokemon.BattleData!.BattleSide;
|
var battleSide = pokemon.BattleSide;
|
||||||
var bestReplacement = ChooseBestReplacementPokemon(terribleMoves, usablePokemon, battleSide);
|
var bestReplacement = ChooseBestReplacementPokemon(terribleMoves, usablePokemon, battleSide);
|
||||||
if (bestReplacement is null)
|
if (bestReplacement is null)
|
||||||
{
|
{
|
||||||
AILogging.LogInformation(
|
AILogging.LogInformation(
|
||||||
$"ExplicitAI: No suitable replacement Pokemon found for {pokemon} at position {pokemon.BattleData.Position}.");
|
$"ExplicitAI: No suitable replacement Pokemon found for {pokemon} at position {pokemon.Position}.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
choice = new SwitchChoice(pokemon, bestReplacement);
|
choice = new SwitchChoice(pokemon, bestReplacement);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IPokemon? ChooseBestReplacementPokemon(bool terribleMoves, IReadOnlyList<IPokemon> usablePokemon,
|
private IBattlePokemon? ChooseBestReplacementPokemon(bool terribleMoves,
|
||||||
IBattleSide battleSide)
|
IReadOnlyList<IBattlePokemon> usablePokemon, IBattleSide battleSide)
|
||||||
{
|
{
|
||||||
var options = usablePokemon.Where((_, index) =>
|
var options = usablePokemon.Where((_, index) =>
|
||||||
{
|
{
|
||||||
@@ -84,7 +84,7 @@ public partial class ExplicitAI
|
|||||||
private static readonly StringKey ToxicSpikesName = "toxic_spikes";
|
private static readonly StringKey ToxicSpikesName = "toxic_spikes";
|
||||||
private static readonly StringKey StickyWebName = "sticky_web";
|
private static readonly StringKey StickyWebName = "sticky_web";
|
||||||
|
|
||||||
private int RateReplacementPokemon(IPokemon pokemon, IBattleSide battleSide)
|
private int RateReplacementPokemon(IBattlePokemon pokemon, IBattleSide battleSide)
|
||||||
{
|
{
|
||||||
var score = 0;
|
var score = 0;
|
||||||
var types = pokemon.Types;
|
var types = pokemon.Types;
|
||||||
@@ -107,7 +107,7 @@ public partial class ExplicitAI
|
|||||||
var opponentSide = battleSide.Battle.Sides.First(x => x != battleSide);
|
var opponentSide = battleSide.Battle.Sides.First(x => x != battleSide);
|
||||||
foreach (var foe in opponentSide.Pokemon.WhereNotNull())
|
foreach (var foe in opponentSide.Pokemon.WhereNotNull())
|
||||||
{
|
{
|
||||||
var lastMoveUsed = foe.BattleData?.LastMoveChoice;
|
var lastMoveUsed = foe.LastMoveChoice;
|
||||||
if (lastMoveUsed is null || lastMoveUsed.ChosenMove.MoveData.Category == MoveCategory.Status)
|
if (lastMoveUsed is null || lastMoveUsed.ChosenMove.MoveData.Category == MoveCategory.Status)
|
||||||
continue;
|
continue;
|
||||||
var moveType = lastMoveUsed.ChosenMove.MoveData.MoveType;
|
var moveType = lastMoveUsed.ChosenMove.MoveData.MoveType;
|
||||||
@@ -134,22 +134,19 @@ public partial class ExplicitAI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the expected entry hazard damage for a given Pokémon on a given battle side.
|
/// Calculates the expected entry hazard damage for a given Pokémon on a given battle side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static uint CalculateEntryHazardDamage(IPokemon pokemon, IBattleSide side)
|
public static uint CalculateEntryHazardDamage(IBattlePokemon pokemon, IBattleSide side)
|
||||||
{
|
{
|
||||||
var damage = 0u;
|
var damage = 0u;
|
||||||
side.RunScriptHook<IAIInfoScriptExpectedEntryDamage>(x => x.ExpectedEntryDamage(pokemon, ref damage));
|
side.RunScriptHook<IAIInfoScriptExpectedEntryDamage>(x => x.ExpectedEntryDamage(pokemon, ref damage));
|
||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool CanSwitch(IPokemon pokemon)
|
private static bool CanSwitch(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var battleData = pokemon.BattleData;
|
if (pokemon.Battle.IsWildBattle)
|
||||||
if (battleData == null)
|
|
||||||
return false;
|
return false;
|
||||||
if (battleData.Battle.IsWildBattle)
|
var partyForIndex = pokemon.Battle.Parties.FirstOrDefault(x =>
|
||||||
return false;
|
x.IsResponsibleForIndex(new ResponsibleIndex(pokemon.SideIndex, pokemon.Position)));
|
||||||
var partyForIndex = battleData.Battle.Parties.FirstOrDefault(x =>
|
|
||||||
x.IsResponsibleForIndex(new ResponsibleIndex(battleData.SideIndex, battleData.Position)));
|
|
||||||
return partyForIndex != null && partyForIndex.HasUsablePokemonNotInField();
|
return partyForIndex != null && partyForIndex.HasUsablePokemonNotInField();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ public partial class ExplicitAI
|
|||||||
private static readonly StringKey KomalaName = "komala";
|
private static readonly StringKey KomalaName = "komala";
|
||||||
private static readonly StringKey MiniorName = "minior";
|
private static readonly StringKey MiniorName = "minior";
|
||||||
|
|
||||||
private static bool CanBePoisoned(IPokemon pokemon, IBattle battle)
|
private static bool CanBePoisoned(IBattlePokemon pokemon, IBattle battle)
|
||||||
{
|
{
|
||||||
if (battle.TerrainName == MistyTerrainName)
|
if (battle.TerrainName == MistyTerrainName)
|
||||||
return false;
|
return false;
|
||||||
@@ -60,7 +60,7 @@ public partial class ExplicitAI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool CanAbsorbMove(IPokemon pokemon, IMoveData move, TypeIdentifier moveType, IBattle battle)
|
private static bool CanAbsorbMove(IBattlePokemon pokemon, IMoveData move, TypeIdentifier moveType, IBattle battle)
|
||||||
{
|
{
|
||||||
if (pokemon.ActiveAbility == null)
|
if (pokemon.ActiveAbility == null)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
public IRandom Random => _random;
|
public IRandom Random => _random;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon)
|
public override ITurnChoice GetChoice(IBattle battle, IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
if (battle.HasForcedTurn(pokemon, out var choice))
|
if (battle.HasForcedTurn(pokemon, out var choice))
|
||||||
return choice;
|
return choice;
|
||||||
@@ -131,8 +131,8 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
var moveChoices = GetMoveScores(pokemon, battle);
|
var moveChoices = GetMoveScores(pokemon, battle);
|
||||||
if (moveChoices.Count == 0)
|
if (moveChoices.Count == 0)
|
||||||
{
|
{
|
||||||
var opponentSide = (byte)(pokemon.BattleData!.SideIndex == 0 ? 1 : 0);
|
var opponentSide = (byte)(pokemon.SideIndex == 0 ? 1 : 0);
|
||||||
return battle.Library.MiscLibrary.ReplacementChoice(pokemon, opponentSide, pokemon.BattleData.Position);
|
return battle.Library.MiscLibrary.ReplacementChoice(pokemon, opponentSide, pokemon.Position);
|
||||||
}
|
}
|
||||||
var maxScore = moveChoices.Max(x => x.score);
|
var maxScore = moveChoices.Max(x => x.score);
|
||||||
if (TrainerHighSkill && CanSwitch(pokemon))
|
if (TrainerHighSkill && CanSwitch(pokemon))
|
||||||
@@ -144,7 +144,7 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
if (!badMoves && _random.GetInt(100) < 25)
|
if (!badMoves && _random.GetInt(100) < 25)
|
||||||
badMoves = true;
|
badMoves = true;
|
||||||
}
|
}
|
||||||
else if (maxScore < MoveBaseScore * MoveScoreThreshold && pokemon.BattleData?.TurnsOnField > 2 &&
|
else if (maxScore < MoveBaseScore * MoveScoreThreshold && pokemon.TurnsOnField > 2 &&
|
||||||
_random.GetInt(100) < 80)
|
_random.GetInt(100) < 80)
|
||||||
{
|
{
|
||||||
badMoves = true;
|
badMoves = true;
|
||||||
@@ -164,8 +164,8 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
var totalScore = considerChoices.Sum(x => x.Item2);
|
var totalScore = considerChoices.Sum(x => x.Item2);
|
||||||
if (totalScore == 0)
|
if (totalScore == 0)
|
||||||
{
|
{
|
||||||
var opponentSide = (byte)(pokemon.BattleData!.SideIndex == 0 ? 1 : 0);
|
var opponentSide = (byte)(pokemon.SideIndex == 0 ? 1 : 0);
|
||||||
return battle.Library.MiscLibrary.ReplacementChoice(pokemon, opponentSide, pokemon.BattleData.Position);
|
return battle.Library.MiscLibrary.ReplacementChoice(pokemon, opponentSide, pokemon.Position);
|
||||||
}
|
}
|
||||||
var initialRandomValue = _random.GetFloat(0, totalScore);
|
var initialRandomValue = _random.GetFloat(0, totalScore);
|
||||||
var randomValue = initialRandomValue;
|
var randomValue = initialRandomValue;
|
||||||
@@ -177,15 +177,15 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
|
|
||||||
var (index, _, targetIndex) = considerChoices[i].x;
|
var (index, _, targetIndex) = considerChoices[i].x;
|
||||||
var learnedMove = pokemon.Moves[index];
|
var learnedMove = pokemon.Moves[index];
|
||||||
var opponentSide = (byte)(pokemon.BattleData!.SideIndex == 0 ? 1 : 0);
|
var opponentSide = (byte)(pokemon.SideIndex == 0 ? 1 : 0);
|
||||||
if (targetIndex == -1)
|
if (targetIndex == -1)
|
||||||
targetIndex = pokemon.BattleData.Position;
|
targetIndex = pokemon.Position;
|
||||||
return new MoveChoice(pokemon, learnedMove!, opponentSide, (byte)targetIndex);
|
return new MoveChoice(pokemon, learnedMove!, opponentSide, (byte)targetIndex);
|
||||||
}
|
}
|
||||||
throw new InvalidOperationException("No valid move choice found. This should not happen.");
|
throw new InvalidOperationException("No valid move choice found. This should not happen.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<(int index, int score, int targetIndex)> GetMoveScores(IPokemon user, IBattle battle)
|
private List<(int index, int score, int targetIndex)> GetMoveScores(IBattlePokemon user, IBattle battle)
|
||||||
{
|
{
|
||||||
var choices = new List<(int index, int score, int targetIndex)>();
|
var choices = new List<(int index, int score, int targetIndex)>();
|
||||||
foreach (var (learnedMove, index) in user.Moves.Select((x, i) => (x, i)).Where(x => x.x != null))
|
foreach (var (learnedMove, index) in user.Moves.Select((x, i) => (x, i)).Where(x => x.x != null))
|
||||||
@@ -249,30 +249,24 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
// TODO: get redirected target
|
// TODO: get redirected target
|
||||||
foreach (var pokemon in battle.Sides.SelectMany(x => x.Pokemon).WhereNotNull())
|
foreach (var pokemon in battle.Sides.SelectMany(x => x.Pokemon).WhereNotNull())
|
||||||
{
|
{
|
||||||
var battleData = pokemon.BattleData;
|
if (!TargetResolver.IsValidTarget(pokemon.SideIndex, pokemon.Position, target, user))
|
||||||
if (battleData == null)
|
|
||||||
continue;
|
continue;
|
||||||
if (!TargetResolver.IsValidTarget(battleData.SideIndex, battleData.Position, target, user))
|
if (target.TargetsFoe() && pokemon.SideIndex == user.SideIndex)
|
||||||
continue;
|
|
||||||
if (target.TargetsFoe() && battleData.SideIndex == user.BattleData?.SideIndex)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var score = GetMoveScoreAgainstTarget(user, aiMove, pokemon, battle);
|
var score = GetMoveScoreAgainstTarget(user, aiMove, pokemon, battle);
|
||||||
AddMoveToChoices(index, score, battleData.Position);
|
AddMoveToChoices(index, score, pokemon.Position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var targets = new List<IPokemon>();
|
var targets = new List<IBattlePokemon>();
|
||||||
foreach (var pokemon in battle.Sides.SelectMany(x => x.Pokemon).WhereNotNull())
|
foreach (var pokemon in battle.Sides.SelectMany(x => x.Pokemon).WhereNotNull())
|
||||||
{
|
{
|
||||||
var battleData = pokemon.BattleData;
|
if (!TargetResolver.IsValidTarget(pokemon.SideIndex, pokemon.Position, target, user))
|
||||||
if (battleData == null)
|
|
||||||
continue;
|
continue;
|
||||||
if (!TargetResolver.IsValidTarget(battleData.SideIndex, battleData.Position, target, user))
|
if (target.TargetsFoe() && pokemon.SideIndex == user.SideIndex)
|
||||||
continue;
|
|
||||||
if (target.TargetsFoe() && battleData.SideIndex == user.BattleData?.SideIndex)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -295,7 +289,7 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool PredictMoveFailure(IPokemon user, IBattle battle, AIMoveState aiMove)
|
private bool PredictMoveFailure(IBattlePokemon user, IBattle battle, AIMoveState aiMove)
|
||||||
{
|
{
|
||||||
if (user.HasStatus("sleep"))
|
if (user.HasStatus("sleep"))
|
||||||
{
|
{
|
||||||
@@ -333,14 +327,15 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
private static readonly StringKey SubstituteName = new("substitute");
|
private static readonly StringKey SubstituteName = new("substitute");
|
||||||
private static readonly StringKey InfiltratorName = new("infiltrator");
|
private static readonly StringKey InfiltratorName = new("infiltrator");
|
||||||
|
|
||||||
private bool PredictMoveFailureAgainstTarget(IPokemon user, AIMoveState aiMove, IPokemon target, IBattle battle)
|
private bool PredictMoveFailureAgainstTarget(IBattlePokemon user, AIMoveState aiMove, IBattlePokemon target,
|
||||||
|
IBattle battle)
|
||||||
{
|
{
|
||||||
if (aiMove.Move.SecondaryEffect != null && _handlers.MoveWillFailAgainstTarget(this,
|
if (aiMove.Move.SecondaryEffect != null && _handlers.MoveWillFailAgainstTarget(this,
|
||||||
aiMove.Move.SecondaryEffect.Name, new MoveOption(aiMove, battle, target)))
|
aiMove.Move.SecondaryEffect.Name, new MoveOption(aiMove, battle, target)))
|
||||||
return true;
|
return true;
|
||||||
if (aiMove.Move.Priority > 0)
|
if (aiMove.Move.Priority > 0)
|
||||||
{
|
{
|
||||||
if (target.BattleData?.SideIndex != user.BattleData?.SideIndex)
|
if (target.SideIndex != user.SideIndex)
|
||||||
{
|
{
|
||||||
// Psychic Terrain makes all priority moves fail if the target is affected
|
// Psychic Terrain makes all priority moves fail if the target is affected
|
||||||
if (battle.TerrainName == PsychicTerrainName && !target.IsFloating)
|
if (battle.TerrainName == PsychicTerrainName && !target.IsFloating)
|
||||||
@@ -348,7 +343,7 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Dazzling and Queenly Majesty prevent priority moves from being used against the Pokémon with those abilities
|
// Dazzling and Queenly Majesty prevent priority moves from being used against the Pokémon with those abilities
|
||||||
if (target.BattleData?.BattleSide.Pokemon.WhereNotNull().Any(x =>
|
if (target.BattleSide.Pokemon.WhereNotNull().Any(x =>
|
||||||
x.ActiveAbility?.Name == DazzlingName || x.ActiveAbility?.Name == QueenlyMajestyName) == true)
|
x.ActiveAbility?.Name == DazzlingName || x.ActiveAbility?.Name == QueenlyMajestyName) == true)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -362,7 +357,7 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
if (aiMove.Move.Category != MoveCategory.Status && typeEffectiveness == 0)
|
if (aiMove.Move.Category != MoveCategory.Status && typeEffectiveness == 0)
|
||||||
return true;
|
return true;
|
||||||
if (user.ActiveAbility?.Name == PranksterName && aiMove.Move.Category == MoveCategory.Status &&
|
if (user.ActiveAbility?.Name == PranksterName && aiMove.Move.Category == MoveCategory.Status &&
|
||||||
target.Types.Any(x => x.Name == DarkName) && target.BattleData?.SideIndex != user.BattleData?.SideIndex)
|
target.Types.Any(x => x.Name == DarkName) && target.SideIndex != user.SideIndex)
|
||||||
return true;
|
return true;
|
||||||
if (aiMove.Move.Category != MoveCategory.Status && moveType.Name == GroundName && target.IsFloating)
|
if (aiMove.Move.Category != MoveCategory.Status && moveType.Name == GroundName && target.IsFloating)
|
||||||
return true;
|
return true;
|
||||||
@@ -375,7 +370,8 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetMoveScore(IPokemon user, AIMoveState aiMove, IBattle battle, IReadOnlyList<IPokemon>? targets = null)
|
private int GetMoveScore(IBattlePokemon user, AIMoveState aiMove, IBattle battle,
|
||||||
|
IReadOnlyList<IBattlePokemon>? targets = null)
|
||||||
{
|
{
|
||||||
var score = MoveBaseScore;
|
var score = MoveBaseScore;
|
||||||
if (targets != null)
|
if (targets != null)
|
||||||
@@ -411,7 +407,8 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetMoveScoreAgainstTarget(IPokemon user, AIMoveState aiMove, IPokemon target, IBattle battle)
|
private int GetMoveScoreAgainstTarget(IBattlePokemon user, AIMoveState aiMove, IBattlePokemon target,
|
||||||
|
IBattle battle)
|
||||||
{
|
{
|
||||||
if (_skillFlags.CanPredictMoveFailure && PredictMoveFailureAgainstTarget(user, aiMove, target, battle))
|
if (_skillFlags.CanPredictMoveFailure && PredictMoveFailureAgainstTarget(user, aiMove, target, battle))
|
||||||
{
|
{
|
||||||
@@ -427,8 +424,7 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
_handlers.ApplyGenerateMoveAgainstTargetScoreModifiers(this, moveOption, ref score);
|
_handlers.ApplyGenerateMoveAgainstTargetScoreModifiers(this, moveOption, ref score);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aiMove.Move.Target.TargetsFoe() && target.BattleData?.SideIndex == user.BattleData?.SideIndex &&
|
if (aiMove.Move.Target.TargetsFoe() && target.SideIndex == user.SideIndex && target.Position != user.Position)
|
||||||
target.BattleData?.Position != user.BattleData?.Position)
|
|
||||||
{
|
{
|
||||||
if (score == MoveUselessScore)
|
if (score == MoveUselessScore)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -442,7 +438,7 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
private static readonly StringKey OvercoatName = new("overcoat");
|
private static readonly StringKey OvercoatName = new("overcoat");
|
||||||
private static readonly StringKey SafetyGogglesName = new("safety_goggles");
|
private static readonly StringKey SafetyGogglesName = new("safety_goggles");
|
||||||
|
|
||||||
private static bool AffectedByPowder(IPokemon pokemon)
|
private static bool AffectedByPowder(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
if (pokemon.Types.Any(x => x.Name == GrassName))
|
if (pokemon.Types.Any(x => x.Name == GrassName))
|
||||||
return false;
|
return false;
|
||||||
@@ -456,7 +452,7 @@ public partial class ExplicitAI : PokemonAI, IExplicitAI
|
|||||||
private static readonly StringKey TruantName = "truant";
|
private static readonly StringKey TruantName = "truant";
|
||||||
private static readonly StringKey TruantEffectName = "truant_effect";
|
private static readonly StringKey TruantEffectName = "truant_effect";
|
||||||
|
|
||||||
private static bool CanAttack(IPokemon pokemon)
|
private static bool CanAttack(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
if (pokemon.Volatile.Contains("requires_recharge"))
|
if (pokemon.Volatile.Contains("requires_recharge"))
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace PkmnLib.Dynamic.AI.Explicit;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// An option where a move is used against a target
|
/// An option where a move is used against a target
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record struct MoveOption(AIMoveState Move, IBattle Battle, IPokemon? Target, uint EstimatedDamage = 0);
|
public record struct MoveOption(AIMoveState Move, IBattle Battle, IBattlePokemon? Target, uint EstimatedDamage = 0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A function that takes an explicit AI and a move option and returns a boolean value.
|
/// A function that takes an explicit AI and a move option and returns a boolean value.
|
||||||
@@ -16,8 +16,8 @@ public delegate bool AIBoolHandler(IExplicitAI ai, MoveOption option);
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A function for returning whether a Pokemon should switch.
|
/// A function for returning whether a Pokemon should switch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public delegate bool AISwitchBoolHandler(IExplicitAI ai, IPokemon pokemon, IBattle battle,
|
public delegate bool AISwitchBoolHandler(IExplicitAI ai, IBattlePokemon pokemon, IBattle battle,
|
||||||
IReadOnlyList<IPokemon> reserves);
|
IReadOnlyList<IBattlePokemon> reserves);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A function for returning the base power of a move.
|
/// A function for returning the base power of a move.
|
||||||
@@ -112,7 +112,7 @@ public interface IReadOnlyExplicitAIHandlers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates whether a Pokemon should switch into another Pokemon
|
/// Indicates whether a Pokemon should switch into another Pokemon
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool ShouldSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList<IPokemon> reserves);
|
bool ShouldSwitch(IExplicitAI ai, IBattlePokemon pokemon, IBattle battle, IReadOnlyList<IBattlePokemon> reserves);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Functions that indicate whether a Pokemon should NOT switch
|
/// Functions that indicate whether a Pokemon should NOT switch
|
||||||
@@ -122,7 +122,8 @@ public interface IReadOnlyExplicitAIHandlers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates whether a Pokemon should NOT switch into another Pokemon
|
/// Indicates whether a Pokemon should NOT switch into another Pokemon
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool ShouldNotSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList<IPokemon> reserves);
|
bool ShouldNotSwitch(IExplicitAI ai, IBattlePokemon pokemon, IBattle battle,
|
||||||
|
IReadOnlyList<IBattlePokemon> reserves);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Scores abilities
|
/// Scores abilities
|
||||||
@@ -246,7 +247,8 @@ public class ExplicitAIHandlers : IReadOnlyExplicitAIHandlers
|
|||||||
ShouldSwitchFunctions;
|
ShouldSwitchFunctions;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool ShouldSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList<IPokemon> reserves)
|
public bool ShouldSwitch(IExplicitAI ai, IBattlePokemon pokemon, IBattle battle,
|
||||||
|
IReadOnlyList<IBattlePokemon> reserves)
|
||||||
{
|
{
|
||||||
var shouldSwitch = false;
|
var shouldSwitch = false;
|
||||||
foreach (var (_, handler) in ShouldSwitchFunctions)
|
foreach (var (_, handler) in ShouldSwitchFunctions)
|
||||||
@@ -272,7 +274,8 @@ public class ExplicitAIHandlers : IReadOnlyExplicitAIHandlers
|
|||||||
public FunctionHandlerDictionary<AIScoreMoveHandler> AbilityRanking = [];
|
public FunctionHandlerDictionary<AIScoreMoveHandler> AbilityRanking = [];
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool ShouldNotSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList<IPokemon> reserves)
|
public bool ShouldNotSwitch(IExplicitAI ai, IBattlePokemon pokemon, IBattle battle,
|
||||||
|
IReadOnlyList<IBattlePokemon> reserves)
|
||||||
{
|
{
|
||||||
var shouldNotSwitch = false;
|
var shouldNotSwitch = false;
|
||||||
foreach (var (_, handler) in ShouldNotSwitchFunctions)
|
foreach (var (_, handler) in ShouldNotSwitchFunctions)
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ public class HighestDamageAI : PokemonAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon)
|
public override ITurnChoice GetChoice(IBattle battle, IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var opponentSide = pokemon.BattleData!.SideIndex == 0 ? (byte)1 : (byte)0;
|
var opponentSide = pokemon.SideIndex == 0 ? (byte)1 : (byte)0;
|
||||||
var opponent = battle.Sides[opponentSide].Pokemon.WhereNotNull().FirstOrDefault(x => x.IsUsable);
|
var opponent = battle.Sides[opponentSide].Pokemon.WhereNotNull().FirstOrDefault(x => x.IsUsable);
|
||||||
var moves = pokemon.Moves.WhereNotNull().Where(x => battle.CanUse(new MoveChoice(pokemon, x, opponentSide, 0)))
|
var moves = pokemon.Moves.WhereNotNull().Where(x => battle.CanUse(new MoveChoice(pokemon, x, opponentSide, 0)))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|||||||
@@ -14,5 +14,5 @@ public class PassTurnAI : PokemonAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon) => new PassChoice(pokemon);
|
public override ITurnChoice GetChoice(IBattle battle, IBattlePokemon pokemon) => new PassChoice(pokemon);
|
||||||
}
|
}
|
||||||
@@ -26,70 +26,69 @@ public abstract class PokemonAI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the choice for the Pokémon.
|
/// Gets the choice for the Pokémon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract ITurnChoice GetChoice(IBattle battle, IPokemon pokemon);
|
public abstract ITurnChoice GetChoice(IBattle battle, IBattlePokemon pokemon);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For a given user and move, returns the valid targets for that move.
|
/// For a given user and move, returns the valid targets for that move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<(byte side, byte position)> GetValidTargetsForMove(IPokemon user, ILearnedMove move)
|
public IEnumerable<(byte side, byte position)> GetValidTargetsForMove(IBattlePokemon user, ILearnedMove move)
|
||||||
{
|
{
|
||||||
var userBattleData = user.BattleData!;
|
|
||||||
switch (move.MoveData.Target)
|
switch (move.MoveData.Target)
|
||||||
{
|
{
|
||||||
case MoveTarget.Adjacent:
|
case MoveTarget.Adjacent:
|
||||||
yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
|
yield return (GetOppositeSide(user.SideIndex), user.Position);
|
||||||
break;
|
break;
|
||||||
case MoveTarget.AdjacentAlly:
|
case MoveTarget.AdjacentAlly:
|
||||||
if (userBattleData.Position > 0)
|
if (user.Position > 0)
|
||||||
yield return (userBattleData.SideIndex, (byte)(userBattleData.Position - 1));
|
yield return (user.SideIndex, (byte)(user.Position - 1));
|
||||||
if (userBattleData.Battle.PositionsPerSide > userBattleData.Position + 1)
|
if (user.Battle.PositionsPerSide > user.Position + 1)
|
||||||
yield return (userBattleData.SideIndex, (byte)(userBattleData.Position + 1));
|
yield return (user.SideIndex, (byte)(user.Position + 1));
|
||||||
break;
|
break;
|
||||||
case MoveTarget.AdjacentAllySelf:
|
case MoveTarget.AdjacentAllySelf:
|
||||||
if (userBattleData.Position > 0)
|
if (user.Position > 0)
|
||||||
yield return (userBattleData.SideIndex, (byte)(userBattleData.Position - 1));
|
yield return (user.SideIndex, (byte)(user.Position - 1));
|
||||||
if (userBattleData.Battle.PositionsPerSide > userBattleData.Position + 1)
|
if (user.Battle.PositionsPerSide > user.Position + 1)
|
||||||
yield return (userBattleData.SideIndex, (byte)(userBattleData.Position + 1));
|
yield return (user.SideIndex, (byte)(user.Position + 1));
|
||||||
yield return (userBattleData.SideIndex, userBattleData.Position);
|
yield return (user.SideIndex, user.Position);
|
||||||
break;
|
break;
|
||||||
case MoveTarget.AdjacentOpponent:
|
case MoveTarget.AdjacentOpponent:
|
||||||
yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
|
yield return (GetOppositeSide(user.SideIndex), user.Position);
|
||||||
if (userBattleData.Position > 0)
|
if (user.Position > 0)
|
||||||
yield return (GetOppositeSide(userBattleData.SideIndex), (byte)(userBattleData.Position - 1));
|
yield return (GetOppositeSide(user.SideIndex), (byte)(user.Position - 1));
|
||||||
if (userBattleData.Battle.PositionsPerSide > userBattleData.Position + 1)
|
if (user.Battle.PositionsPerSide > user.Position + 1)
|
||||||
yield return (GetOppositeSide(userBattleData.SideIndex), (byte)(userBattleData.Position + 1));
|
yield return (GetOppositeSide(user.SideIndex), (byte)(user.Position + 1));
|
||||||
break;
|
break;
|
||||||
case MoveTarget.All:
|
case MoveTarget.All:
|
||||||
yield return (userBattleData.SideIndex, userBattleData.Position);
|
yield return (user.SideIndex, user.Position);
|
||||||
break;
|
break;
|
||||||
case MoveTarget.AllAdjacent:
|
case MoveTarget.AllAdjacent:
|
||||||
yield return (userBattleData.SideIndex, userBattleData.Position);
|
yield return (user.SideIndex, user.Position);
|
||||||
break;
|
break;
|
||||||
case MoveTarget.AllAdjacentOpponent:
|
case MoveTarget.AllAdjacentOpponent:
|
||||||
yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
|
yield return (GetOppositeSide(user.SideIndex), user.Position);
|
||||||
break;
|
break;
|
||||||
case MoveTarget.AllAlly:
|
case MoveTarget.AllAlly:
|
||||||
yield return (userBattleData.SideIndex, userBattleData.Position);
|
yield return (user.SideIndex, user.Position);
|
||||||
break;
|
break;
|
||||||
case MoveTarget.AllOpponent:
|
case MoveTarget.AllOpponent:
|
||||||
yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
|
yield return (GetOppositeSide(user.SideIndex), user.Position);
|
||||||
break;
|
break;
|
||||||
case MoveTarget.Any:
|
case MoveTarget.Any:
|
||||||
foreach (var side in userBattleData.Battle.Sides)
|
foreach (var side in user.Battle.Sides)
|
||||||
{
|
{
|
||||||
foreach (var pokemon in side.Pokemon)
|
foreach (var pokemon in side.Pokemon)
|
||||||
{
|
{
|
||||||
if (pokemon?.BattleData == null)
|
if (pokemon == null)
|
||||||
continue;
|
continue;
|
||||||
yield return (side.Index, pokemon.BattleData!.Position);
|
yield return (side.Index, pokemon.Position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MoveTarget.RandomOpponent:
|
case MoveTarget.RandomOpponent:
|
||||||
yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
|
yield return (GetOppositeSide(user.SideIndex), user.Position);
|
||||||
break;
|
break;
|
||||||
case MoveTarget.SelfUse:
|
case MoveTarget.SelfUse:
|
||||||
yield return (userBattleData.SideIndex, userBattleData.Position);
|
yield return (user.SideIndex, user.Position);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ public class PrescientAI : PokemonAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon)
|
public override ITurnChoice GetChoice(IBattle battle, IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var opponentSide = pokemon.BattleData!.SideIndex == 0 ? (byte)1 : (byte)0;
|
var opponentSide = pokemon.SideIndex == 0 ? (byte)1 : (byte)0;
|
||||||
var moves = pokemon.Moves.WhereNotNull().Where(x => battle.CanUse(new MoveChoice(pokemon, x, opponentSide, 0)))
|
var moves = pokemon.Moves.WhereNotNull().Where(x => battle.CanUse(new MoveChoice(pokemon, x, opponentSide, 0)))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@@ -34,13 +34,13 @@ public class PrescientAI : PokemonAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<(ITurnChoice Choice, float Score)> ScoreChoices(IBattle battle,
|
private static IEnumerable<(ITurnChoice Choice, float Score)> ScoreChoices(IBattle battle,
|
||||||
IReadOnlyList<ILearnedMove> moves, IPokemon pokemon)
|
IReadOnlyList<ILearnedMove> moves, IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var opponentSide = pokemon.BattleData!.SideIndex == 0 ? (byte)1 : (byte)0;
|
var opponentSide = pokemon.SideIndex == 0 ? (byte)1 : (byte)0;
|
||||||
foreach (var learnedMoveOriginal in moves.WhereNotNull())
|
foreach (var learnedMoveOriginal in moves.WhereNotNull())
|
||||||
{
|
{
|
||||||
var battleClone = battle.DeepClone();
|
var battleClone = battle.DeepClone();
|
||||||
var pokemonClone = battleClone.Sides[pokemon.BattleData!.SideIndex].Pokemon[pokemon.BattleData.Position]!;
|
var pokemonClone = battleClone.Sides[pokemon.SideIndex].Pokemon[pokemon.Position]!;
|
||||||
var learnedMove = pokemonClone.Moves.WhereNotNull()
|
var learnedMove = pokemonClone.Moves.WhereNotNull()
|
||||||
.First(m => m.MoveData.Name == learnedMoveOriginal.MoveData.Name);
|
.First(m => m.MoveData.Name == learnedMoveOriginal.MoveData.Name);
|
||||||
var choice = new MoveChoice(pokemonClone, learnedMove, opponentSide, 0);
|
var choice = new MoveChoice(pokemonClone, learnedMove, opponentSide, 0);
|
||||||
@@ -57,17 +57,16 @@ public class PrescientAI : PokemonAI
|
|||||||
}
|
}
|
||||||
if (battleClone.TrySetChoice(choice))
|
if (battleClone.TrySetChoice(choice))
|
||||||
{
|
{
|
||||||
var score = CalculateScore(battleClone.Parties[pokemon.BattleData.SideIndex],
|
var score = CalculateScore(battleClone.Parties[pokemon.SideIndex], battleClone.Parties[opponentSide]);
|
||||||
battleClone.Parties[opponentSide]);
|
|
||||||
var realChoice = new MoveChoice(pokemon, learnedMoveOriginal, opponentSide, 0);
|
var realChoice = new MoveChoice(pokemon, learnedMoveOriginal, opponentSide, 0);
|
||||||
yield return (realChoice, score);
|
yield return (realChoice, score);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ITurnChoice GetOpponentChoice(IBattle battle, IPokemon pokemon)
|
private static ITurnChoice GetOpponentChoice(IBattle battle, IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var opponentSide = pokemon.BattleData!.SideIndex == 0 ? (byte)1 : (byte)0;
|
var opponentSide = pokemon.SideIndex == 0 ? (byte)1 : (byte)0;
|
||||||
var opponent = battle.Sides[opponentSide].Pokemon[0];
|
var opponent = battle.Sides[opponentSide].Pokemon[0];
|
||||||
if (opponent is null)
|
if (opponent is null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class RandomAI : PokemonAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon)
|
public override ITurnChoice GetChoice(IBattle battle, IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var moves = pokemon.Moves.WhereNotNull().Where(x => x.CurrentPp > 0).ToList();
|
var moves = pokemon.Moves.WhereNotNull().Where(x => x.CurrentPp > 0).ToList();
|
||||||
while (moves.Count > 0)
|
while (moves.Count > 0)
|
||||||
@@ -28,7 +28,7 @@ public class RandomAI : PokemonAI
|
|||||||
var targets = GetValidTargetsForMove(pokemon, move).ToArray();
|
var targets = GetValidTargetsForMove(pokemon, move).ToArray();
|
||||||
if (move.MoveData.Category is MoveCategory.Physical or MoveCategory.Special)
|
if (move.MoveData.Category is MoveCategory.Physical or MoveCategory.Special)
|
||||||
{
|
{
|
||||||
targets = targets.Where(x => x.side != pokemon.BattleData!.SideIndex).ToArray();
|
targets = targets.Where(x => x.side != pokemon.SideIndex).ToArray();
|
||||||
}
|
}
|
||||||
if (targets.Length == 0)
|
if (targets.Length == 0)
|
||||||
{
|
{
|
||||||
@@ -43,7 +43,7 @@ public class RandomAI : PokemonAI
|
|||||||
}
|
}
|
||||||
moves.Remove(move);
|
moves.Remove(move);
|
||||||
}
|
}
|
||||||
return battle.Library.MiscLibrary.ReplacementChoice(pokemon,
|
return battle.Library.MiscLibrary.ReplacementChoice(pokemon, pokemon.SideIndex == 0 ? (byte)1 : (byte)0,
|
||||||
pokemon.BattleData!.SideIndex == 0 ? (byte)1 : (byte)0, pokemon.BattleData.Position);
|
pokemon.Position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ public static class MoveTurnExecutor
|
|||||||
{
|
{
|
||||||
internal static void ExecuteMoveChoice(IBattle battle, IMoveChoice moveChoice)
|
internal static void ExecuteMoveChoice(IBattle battle, IMoveChoice moveChoice)
|
||||||
{
|
{
|
||||||
moveChoice.User.BattleData!.LastMoveChoice = moveChoice;
|
moveChoice.User.LastMoveChoice = moveChoice;
|
||||||
var chosenMove = moveChoice.ChosenMove;
|
var chosenMove = moveChoice.ChosenMove;
|
||||||
var useMove = chosenMove.MoveData;
|
var useMove = chosenMove.MoveData;
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ public static class MoveTurnExecutor
|
|||||||
|
|
||||||
private static readonly ThreadLocal<List<TypeIdentifier>> TypeListCache = new(() => []);
|
private static readonly ThreadLocal<List<TypeIdentifier>> TypeListCache = new(() => []);
|
||||||
|
|
||||||
private static void ExecuteMoveChoiceForTarget(IBattle battle, IExecutingMove executingMove, IPokemon target)
|
private static void ExecuteMoveChoiceForTarget(IBattle battle, IExecutingMove executingMove, IBattlePokemon target)
|
||||||
{
|
{
|
||||||
var failed = false;
|
var failed = false;
|
||||||
target.RunScriptHook<IScriptFailIncomingMove>(x => x.FailIncomingMove(executingMove, target, ref failed));
|
target.RunScriptHook<IScriptFailIncomingMove>(x => x.FailIncomingMove(executingMove, target, ref failed));
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ public static class TargetResolver
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the targets of a move based on the target type, and the selected side and position to target.
|
/// Get the targets of a move based on the target type, and the selected side and position to target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IReadOnlyList<IPokemon?> ResolveTargets(IBattle battle, byte side, byte position, MoveTarget target)
|
public static IReadOnlyList<IBattlePokemon?> ResolveTargets(IBattle battle, byte side, byte position,
|
||||||
|
MoveTarget target)
|
||||||
{
|
{
|
||||||
return target switch
|
return target switch
|
||||||
{
|
{
|
||||||
@@ -29,13 +30,10 @@ public static class TargetResolver
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates whether a given target is valid for a move choice. Returns true if the target is valid.
|
/// Validates whether a given target is valid for a move choice. Returns true if the target is valid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool IsValidTarget(byte side, byte position, MoveTarget target, IPokemon user)
|
public static bool IsValidTarget(byte side, byte position, MoveTarget target, IBattlePokemon user)
|
||||||
{
|
{
|
||||||
var userBattleData = user.BattleData;
|
var userSide = user.SideIndex;
|
||||||
if (userBattleData == null)
|
var userPosition = user.Position;
|
||||||
throw new ArgumentNullException(nameof(user.BattleData));
|
|
||||||
var userSide = userBattleData.SideIndex;
|
|
||||||
var userPosition = userBattleData.Position;
|
|
||||||
|
|
||||||
switch (target)
|
switch (target)
|
||||||
{
|
{
|
||||||
@@ -80,7 +78,7 @@ public static class TargetResolver
|
|||||||
throw new ArgumentOutOfRangeException(nameof(target), target, null);
|
throw new ArgumentOutOfRangeException(nameof(target), target, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IReadOnlyList<IPokemon?> GetAllTargets(IBattle battle) =>
|
private static IReadOnlyList<IBattlePokemon?> GetAllTargets(IBattle battle) =>
|
||||||
battle.Sides.SelectMany(x => x.Pokemon).ToList();
|
battle.Sides.SelectMany(x => x.Pokemon).ToList();
|
||||||
|
|
||||||
private static byte GetOppositeSide(byte side) => side == 0 ? (byte)1 : (byte)0;
|
private static byte GetOppositeSide(byte side) => side == 0 ? (byte)1 : (byte)0;
|
||||||
@@ -89,7 +87,7 @@ public static class TargetResolver
|
|||||||
/// Gets all Pokémon that are adjacent to of directly opposite of a Pokémon. This means the target,
|
/// Gets all Pokémon that are adjacent to of directly opposite of a Pokémon. This means the target,
|
||||||
/// the Pokémon left of it, the Pokémon right of it, and the Pokémon opposite of it.
|
/// the Pokémon left of it, the Pokémon right of it, and the Pokémon opposite of it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static IReadOnlyList<IPokemon?> GetAllAdjacentAndOpponent(IBattle battle, byte side, byte position)
|
private static IReadOnlyList<IBattlePokemon?> GetAllAdjacentAndOpponent(IBattle battle, byte side, byte position)
|
||||||
{
|
{
|
||||||
var left = position - 1;
|
var left = position - 1;
|
||||||
var right = position + 1;
|
var right = position + 1;
|
||||||
@@ -123,7 +121,7 @@ public static class TargetResolver
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IReadOnlyList<IPokemon?> GetAllAdjacent(IBattle battle, byte side, byte position)
|
private static IReadOnlyList<IBattlePokemon?> GetAllAdjacent(IBattle battle, byte side, byte position)
|
||||||
{
|
{
|
||||||
var left = position - 1;
|
var left = position - 1;
|
||||||
var right = position + 1;
|
var right = position + 1;
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public static class TurnRunner
|
|||||||
return;
|
return;
|
||||||
if (!choice.User.IsUsable)
|
if (!choice.User.IsUsable)
|
||||||
return;
|
return;
|
||||||
if (choice.User.BattleData?.IsOnBattlefield != true)
|
if (!choice.User.IsOnBattlefield)
|
||||||
return;
|
return;
|
||||||
switch (choice)
|
switch (choice)
|
||||||
{
|
{
|
||||||
@@ -108,9 +108,6 @@ public static class TurnRunner
|
|||||||
private static void ExecuteSwitchChoice(IBattle battle, ISwitchChoice fleeChoice)
|
private static void ExecuteSwitchChoice(IBattle battle, ISwitchChoice fleeChoice)
|
||||||
{
|
{
|
||||||
var user = fleeChoice.User;
|
var user = fleeChoice.User;
|
||||||
var battleData = user.BattleData;
|
|
||||||
if (battleData == null)
|
|
||||||
return;
|
|
||||||
var preventSwitch = false;
|
var preventSwitch = false;
|
||||||
fleeChoice.RunScriptHook<IScriptPreventSelfSwitch>(script =>
|
fleeChoice.RunScriptHook<IScriptPreventSelfSwitch>(script =>
|
||||||
script.PreventSelfSwitch(fleeChoice, ref preventSwitch));
|
script.PreventSelfSwitch(fleeChoice, ref preventSwitch));
|
||||||
@@ -118,7 +115,7 @@ public static class TurnRunner
|
|||||||
return;
|
return;
|
||||||
foreach (var side in battle.Sides)
|
foreach (var side in battle.Sides)
|
||||||
{
|
{
|
||||||
if (side.Index == battleData.SideIndex)
|
if (side.Index == user.SideIndex)
|
||||||
continue;
|
continue;
|
||||||
foreach (var pokemon in side.Pokemon.WhereNotNull())
|
foreach (var pokemon in side.Pokemon.WhereNotNull())
|
||||||
{
|
{
|
||||||
@@ -129,16 +126,12 @@ public static class TurnRunner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
user.Volatile.Clear();
|
user.Volatile.Clear();
|
||||||
var userSide = battle.Sides[battleData.SideIndex];
|
user.BattleSide.SwapPokemon(user.Position, fleeChoice.SwitchTo);
|
||||||
userSide.SwapPokemon(battleData.Position, fleeChoice.SwitchTo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ExecuteFleeChoice(IBattle battle, IFleeChoice fleeChoice)
|
private static void ExecuteFleeChoice(IBattle battle, IFleeChoice fleeChoice)
|
||||||
{
|
{
|
||||||
var user = fleeChoice.User;
|
var user = fleeChoice.User;
|
||||||
var battleData = user.BattleData;
|
|
||||||
if (battleData == null)
|
|
||||||
return;
|
|
||||||
if (!battle.CanFlee)
|
if (!battle.CanFlee)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -150,7 +143,7 @@ public static class TurnRunner
|
|||||||
|
|
||||||
foreach (var side in battle.Sides)
|
foreach (var side in battle.Sides)
|
||||||
{
|
{
|
||||||
if (side.Index == battleData.SideIndex)
|
if (side.Index == user.SideIndex)
|
||||||
continue;
|
continue;
|
||||||
foreach (var pokemon in side.Pokemon.WhereNotNull())
|
foreach (var pokemon in side.Pokemon.WhereNotNull())
|
||||||
{
|
{
|
||||||
@@ -167,8 +160,7 @@ public static class TurnRunner
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var userSide = battle.Sides[battleData.SideIndex];
|
user.BattleSide.MarkAsFled();
|
||||||
userSide.MarkAsFled();
|
|
||||||
battle.EventHook.Invoke(new FleeEvent(user, true));
|
battle.EventHook.Invoke(new FleeEvent(user, true));
|
||||||
battle.ValidateBattleState();
|
battle.ValidateBattleState();
|
||||||
}
|
}
|
||||||
@@ -176,9 +168,6 @@ public static class TurnRunner
|
|||||||
private static void ExecuteItemChoice(IBattle battle, IItemChoice itemChoice)
|
private static void ExecuteItemChoice(IBattle battle, IItemChoice itemChoice)
|
||||||
{
|
{
|
||||||
var user = itemChoice.User;
|
var user = itemChoice.User;
|
||||||
var battleData = user.BattleData;
|
|
||||||
if (battleData == null)
|
|
||||||
return;
|
|
||||||
var target = itemChoice.GetTargetPokemon(battle);
|
var target = itemChoice.GetTargetPokemon(battle);
|
||||||
battle.EventHook.Invoke(new ItemUseEvent(user, itemChoice.Item));
|
battle.EventHook.Invoke(new ItemUseEvent(user, itemChoice.Item));
|
||||||
itemChoice.Item.RunItemScript(battle.Library.ScriptResolver, target ?? user, user, battle, battle.EventHook);
|
itemChoice.Item.RunItemScript(battle.Library.ScriptResolver, target ?? user, user, battle, battle.EventHook);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public record AbilityTriggerEvent : IEventData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokémon whose ability is being triggered.
|
/// The Pokémon whose ability is being triggered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon Pokemon { get; }
|
public IBattlePokemon Pokemon { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ability that is being triggered for the Pokémon.
|
/// The ability that is being triggered for the Pokémon.
|
||||||
@@ -22,7 +22,7 @@ public record AbilityTriggerEvent : IEventData
|
|||||||
public Dictionary<StringKey, object?>? Metadata { get; init; } = null;
|
public Dictionary<StringKey, object?>? Metadata { get; init; } = null;
|
||||||
|
|
||||||
/// <inheritdoc cref="AbilityTriggerEvent"/>
|
/// <inheritdoc cref="AbilityTriggerEvent"/>
|
||||||
public AbilityTriggerEvent(IPokemon pokemon)
|
public AbilityTriggerEvent(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
Pokemon = pokemon;
|
Pokemon = pokemon;
|
||||||
Ability = pokemon.ActiveAbility;
|
Ability = pokemon.ActiveAbility;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace PkmnLib.Dynamic.Events;
|
|||||||
public class CaptureAttemptEvent : IEventData
|
public class CaptureAttemptEvent : IEventData
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="CaptureAttemptEvent"/>
|
/// <inheritdoc cref="CaptureAttemptEvent"/>
|
||||||
public CaptureAttemptEvent(IPokemon target, CaptureResult result, IItem captureItem)
|
public CaptureAttemptEvent(IBattlePokemon target, CaptureResult result, IItem captureItem)
|
||||||
{
|
{
|
||||||
Target = target;
|
Target = target;
|
||||||
Result = result;
|
Result = result;
|
||||||
@@ -20,7 +20,7 @@ public class CaptureAttemptEvent : IEventData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokémon that is being captured.
|
/// The Pokémon that is being captured.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon Target { get; init; }
|
public IBattlePokemon Target { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The result of the capture attempt.
|
/// The result of the capture attempt.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace PkmnLib.Dynamic.Events;
|
|||||||
public record DamageEvent : IEventData
|
public record DamageEvent : IEventData
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="DamageEvent"/>
|
/// <inheritdoc cref="DamageEvent"/>
|
||||||
public DamageEvent(IPokemon pokemon, uint previousHealth, uint newHealth, DamageSource source)
|
public DamageEvent(IBattlePokemon pokemon, uint previousHealth, uint newHealth, DamageSource source)
|
||||||
{
|
{
|
||||||
Pokemon = pokemon;
|
Pokemon = pokemon;
|
||||||
PreviousHealth = previousHealth;
|
PreviousHealth = previousHealth;
|
||||||
@@ -19,7 +19,7 @@ public record DamageEvent : IEventData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokemon that took damage.
|
/// The Pokemon that took damage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon Pokemon { get; init; }
|
public IBattlePokemon Pokemon { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The previous health of the Pokemon.
|
/// The previous health of the Pokemon.
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ namespace PkmnLib.Dynamic.Events;
|
|||||||
|
|
||||||
public class DisplaySpeciesChangeEvent : IEventData
|
public class DisplaySpeciesChangeEvent : IEventData
|
||||||
{
|
{
|
||||||
public IPokemon Pokemon { get; }
|
public IBattlePokemon Pokemon { get; }
|
||||||
public ISpecies? Species { get; }
|
public ISpecies? Species { get; }
|
||||||
public IForm? Form { get; }
|
public IForm? Form { get; }
|
||||||
|
|
||||||
public DisplaySpeciesChangeEvent(IPokemon pokemon, ISpecies? species, IForm? form)
|
public DisplaySpeciesChangeEvent(IBattlePokemon pokemon, ISpecies? species, IForm? form)
|
||||||
{
|
{
|
||||||
Pokemon = pokemon;
|
Pokemon = pokemon;
|
||||||
Species = species;
|
Species = species;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace PkmnLib.Dynamic.Events;
|
|||||||
public class FaintEvent : IEventData
|
public class FaintEvent : IEventData
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="FaintEvent"/>
|
/// <inheritdoc cref="FaintEvent"/>
|
||||||
public FaintEvent(IPokemon pokemon)
|
public FaintEvent(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
Pokemon = pokemon;
|
Pokemon = pokemon;
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ public class FaintEvent : IEventData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokemon that fainted.
|
/// The Pokemon that fainted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon Pokemon { get; init; }
|
public IBattlePokemon Pokemon { get; init; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EventBatchId BatchId { get; init; } = new();
|
public EventBatchId BatchId { get; init; } = new();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace PkmnLib.Dynamic.Events;
|
|||||||
public class FleeEvent : IEventData
|
public class FleeEvent : IEventData
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="FleeEvent"/>
|
/// <inheritdoc cref="FleeEvent"/>
|
||||||
public FleeEvent(IPokemon pokemon, bool success)
|
public FleeEvent(IBattlePokemon pokemon, bool success)
|
||||||
{
|
{
|
||||||
Pokemon = pokemon;
|
Pokemon = pokemon;
|
||||||
Success = success;
|
Success = success;
|
||||||
@@ -17,7 +17,7 @@ public class FleeEvent : IEventData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokémon that attempted to flee.
|
/// The Pokémon that attempted to flee.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon Pokemon { get; }
|
public IBattlePokemon Pokemon { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates whether the flee attempt was successful.
|
/// Indicates whether the flee attempt was successful.
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ namespace PkmnLib.Dynamic.Events;
|
|||||||
|
|
||||||
public record ItemUseEvent : IEventData
|
public record ItemUseEvent : IEventData
|
||||||
{
|
{
|
||||||
public ItemUseEvent(IPokemon pokemon, IItem itemUsed)
|
public ItemUseEvent(IBattlePokemon pokemon, IItem itemUsed)
|
||||||
{
|
{
|
||||||
Pokemon = pokemon;
|
Pokemon = pokemon;
|
||||||
ItemUsed = itemUsed;
|
ItemUsed = itemUsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPokemon Pokemon { get; set; }
|
public IBattlePokemon Pokemon { get; set; }
|
||||||
public IItem ItemUsed { get; set; }
|
public IItem ItemUsed { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ public class MoveHitEvent : IEventData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The target of the move.
|
/// The target of the move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon Target { get; }
|
public IBattlePokemon Target { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="MoveHitEvent"/>
|
/// <inheritdoc cref="MoveHitEvent"/>
|
||||||
public MoveHitEvent(IExecutingMove executingMove, IHitData hitData, IPokemon target)
|
public MoveHitEvent(IExecutingMove executingMove, IHitData hitData, IBattlePokemon target)
|
||||||
{
|
{
|
||||||
ExecutingMove = executingMove;
|
ExecutingMove = executingMove;
|
||||||
HitData = hitData;
|
HitData = hitData;
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ namespace PkmnLib.Dynamic.Events;
|
|||||||
public class MoveInvulnerableEvent : IEventData
|
public class MoveInvulnerableEvent : IEventData
|
||||||
{
|
{
|
||||||
public IExecutingMove ExecutingMove { get; }
|
public IExecutingMove ExecutingMove { get; }
|
||||||
public IPokemon Target { get; }
|
public IBattlePokemon Target { get; }
|
||||||
|
|
||||||
public MoveInvulnerableEvent(IExecutingMove executingMove, IPokemon target)
|
public MoveInvulnerableEvent(IExecutingMove executingMove, IBattlePokemon target)
|
||||||
{
|
{
|
||||||
ExecutingMove = executingMove;
|
ExecutingMove = executingMove;
|
||||||
Target = target;
|
Target = target;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace PkmnLib.Dynamic.Events;
|
|||||||
public class StatBoostEvent : IEventData
|
public class StatBoostEvent : IEventData
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="StatBoostEvent" />
|
/// <inheritdoc cref="StatBoostEvent" />
|
||||||
public StatBoostEvent(IPokemon pokemon, Statistic statistic, sbyte oldBoost, sbyte newBoost)
|
public StatBoostEvent(IBattlePokemon pokemon, Statistic statistic, sbyte oldBoost, sbyte newBoost)
|
||||||
{
|
{
|
||||||
Pokemon = pokemon;
|
Pokemon = pokemon;
|
||||||
Statistic = statistic;
|
Statistic = statistic;
|
||||||
@@ -20,7 +20,7 @@ public class StatBoostEvent : IEventData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokemon that had its stat boosted.
|
/// The Pokemon that had its stat boosted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon Pokemon { get; }
|
public IBattlePokemon Pokemon { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The statistic that was boosted.
|
/// The statistic that was boosted.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace PkmnLib.Dynamic.Events;
|
|||||||
public record StatusChangeEvent : IEventData
|
public record StatusChangeEvent : IEventData
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="StatusChangeEvent"/>
|
/// <inheritdoc cref="StatusChangeEvent"/>
|
||||||
public StatusChangeEvent(IPokemon pokemon, StringKey? previousStatus, StringKey? newStatus)
|
public StatusChangeEvent(IBattlePokemon pokemon, StringKey? previousStatus, StringKey? newStatus)
|
||||||
{
|
{
|
||||||
Pokemon = pokemon;
|
Pokemon = pokemon;
|
||||||
PreviousStatus = previousStatus;
|
PreviousStatus = previousStatus;
|
||||||
@@ -19,7 +19,7 @@ public record StatusChangeEvent : IEventData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokémon whose status has changed.
|
/// The Pokémon whose status has changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon Pokemon { get; }
|
public IBattlePokemon Pokemon { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The new status of the Pokémon after the change.
|
/// The new status of the Pokémon after the change.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace PkmnLib.Dynamic.Events;
|
|||||||
public class SwitchEvent : IEventData
|
public class SwitchEvent : IEventData
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="SwitchEvent"/>
|
/// <inheritdoc cref="SwitchEvent"/>
|
||||||
public SwitchEvent(byte sideIndex, byte position, IPokemon? pokemon)
|
public SwitchEvent(byte sideIndex, byte position, IBattlePokemon? pokemon)
|
||||||
{
|
{
|
||||||
SideIndex = sideIndex;
|
SideIndex = sideIndex;
|
||||||
Position = position;
|
Position = position;
|
||||||
@@ -28,7 +28,7 @@ public class SwitchEvent : IEventData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokémon that is switching in. If null, no Pokémon is switching in, and the slot is empty after the switch.
|
/// The Pokémon that is switching in. If null, no Pokémon is switching in, and the slot is empty after the switch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon? Pokemon { get; init; }
|
public IBattlePokemon? Pokemon { get; init; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EventBatchId BatchId { get; init; }
|
public EventBatchId BatchId { get; init; }
|
||||||
|
|||||||
@@ -21,15 +21,16 @@ public interface IBattleStatCalculator
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculate all the boosted stats of a Pokemon, including stat boosts.
|
/// Calculate all the boosted stats of a Pokemon, including stat boosts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void CalculateBoostedStats(IPokemon pokemon, StatisticSet<uint> stats);
|
void CalculateBoostedStats(IBattlePokemon pokemon, StatisticSet<uint> stats);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculate a single boosted stat of a Pokemon, including stat boosts.
|
/// Calculate a single boosted stat of a Pokemon, including stat boosts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
uint CalculateBoostedStat(IPokemon pokemon, Statistic stat);
|
uint CalculateBoostedStat(IBattlePokemon pokemon, Statistic stat);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the accuracy for a move, taking into account any accuracy modifiers.
|
/// Calculates the accuracy for a move, taking into account any accuracy modifiers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte CalculateModifiedAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, byte moveAccuracy);
|
byte CalculateModifiedAccuracy(IExecutingMove executingMove, IBattlePokemon target, byte hitIndex,
|
||||||
|
byte moveAccuracy);
|
||||||
}
|
}
|
||||||
@@ -46,5 +46,5 @@ public interface ICaptureLibrary
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to capture a Pokémon using a specified item (e.g., Poké Ball).
|
/// Attempts to capture a Pokémon using a specified item (e.g., Poké Ball).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
CaptureResult TryCapture(IPokemon target, IItem captureItem, IBattleRandom random);
|
CaptureResult TryCapture(IBattlePokemon target, IItem captureItem, IBattleRandom random);
|
||||||
}
|
}
|
||||||
@@ -11,16 +11,16 @@ public interface IDamageCalculator
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculate the damage for a given hit on a Pokemon.
|
/// Calculate the damage for a given hit on a Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
uint GetDamage(IExecutingMove? executingMove, MoveCategory category, IPokemon user, IPokemon target,
|
uint GetDamage(IExecutingMove? executingMove, MoveCategory category, IBattlePokemon user, IBattlePokemon target,
|
||||||
int targetCount, byte hitNumber, IHitData hitData);
|
int targetCount, byte hitNumber, IHitData hitData);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculate the base power for a given hit on a Pokemon.
|
/// Calculate the base power for a given hit on a Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ushort GetBasePower(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData);
|
ushort GetBasePower(IExecutingMove executingMove, IBattlePokemon target, byte hitNumber, IHitData hitData);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether a specified hit should be critical or not.
|
/// Returns whether a specified hit should be critical or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsCritical(IBattle battle, IExecutingMove executingMove, IPokemon target, byte hitNumber);
|
bool IsCritical(IBattle battle, IExecutingMove executingMove, IBattlePokemon target, byte hitNumber);
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ public interface IMiscLibrary
|
|||||||
/// Returns the choice that's used when a Pokemon is unable to make the move choice it wants to, or when it has no
|
/// Returns the choice that's used when a Pokemon is unable to make the move choice it wants to, or when it has no
|
||||||
/// moves left, yet wants to make a move.
|
/// moves left, yet wants to make a move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ITurnChoice ReplacementChoice(IPokemon user, byte targetSide, byte targetPosition);
|
ITurnChoice ReplacementChoice(IBattlePokemon user, byte targetSide, byte targetPosition);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether the given choice is the choice that is used when the user is unable to make a move choice.
|
/// Returns whether the given choice is the choice that is used when the user is unable to make a move choice.
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public interface IBattle : IScriptSource, IDeepCloneable, IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a Pokemon on the battlefield, on a specific side and an index on that side.
|
/// Get a Pokemon on the battlefield, on a specific side and an index on that side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IPokemon? GetPokemon(byte side, byte position);
|
IBattlePokemon? GetPokemon(byte side, byte position);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether a slot on the battlefield can still be filled. If no party is responsible
|
/// 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
|
/// 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.
|
/// is set in the out parameter. If it does not, this returns false and the out parameter is null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool HasForcedTurn(IPokemon pokemon, [NotNullWhen(true)] out ITurnChoice? choice);
|
bool HasForcedTurn(IBattlePokemon pokemon, [NotNullWhen(true)] out ITurnChoice? choice);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether a choice is actually possible.
|
/// Checks whether a choice is actually possible.
|
||||||
@@ -202,6 +202,8 @@ public class BattleImpl : ScriptSource, IBattle
|
|||||||
Sides = sides;
|
Sides = sides;
|
||||||
Random = randomSeed.HasValue ? new BattleRandomImpl(randomSeed.Value) : new BattleRandomImpl();
|
Random = randomSeed.HasValue ? new BattleRandomImpl(randomSeed.Value) : new BattleRandomImpl();
|
||||||
EventHook = new EventHook();
|
EventHook = new EventHook();
|
||||||
|
foreach (var party in parties)
|
||||||
|
party.Initialize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -248,12 +250,22 @@ public class BattleImpl : ScriptSource, IBattle
|
|||||||
public BattleChoiceQueue? ChoiceQueue { get; private set; }
|
public BattleChoiceQueue? ChoiceQueue { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IPokemon? GetPokemon(byte side, byte position) => Sides[side].Pokemon[position];
|
public IBattlePokemon? GetPokemon(byte side, byte position) => Sides[side].Pokemon[position];
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool CanSlotBeFilled(byte side, byte position) => Parties.Any(x =>
|
public bool CanSlotBeFilled(byte side, byte position) => Parties.Any(x =>
|
||||||
x.IsResponsibleForIndex(new ResponsibleIndex(side, position)) && x.HasUsablePokemonNotInField());
|
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 />
|
/// <inheritdoc />
|
||||||
public void ValidateBattleState()
|
public void ValidateBattleState()
|
||||||
{
|
{
|
||||||
@@ -265,7 +277,7 @@ public class BattleImpl : ScriptSource, IBattle
|
|||||||
{
|
{
|
||||||
if (side.HasFledBattle)
|
if (side.HasFledBattle)
|
||||||
{
|
{
|
||||||
Result = BattleResult.Inconclusive;
|
Result = FinalizeResult(BattleResult.Inconclusive);
|
||||||
HasEnded = true;
|
HasEnded = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -283,13 +295,13 @@ public class BattleImpl : ScriptSource, IBattle
|
|||||||
// If every side is defeated, the battle is a draw
|
// If every side is defeated, the battle is a draw
|
||||||
if (!survivingSideExists)
|
if (!survivingSideExists)
|
||||||
{
|
{
|
||||||
Result = BattleResult.Inconclusive;
|
Result = FinalizeResult(BattleResult.Inconclusive);
|
||||||
HasEnded = true;
|
HasEnded = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If only one side is left, that side has won
|
// If only one side is left, that side has won
|
||||||
Result = BattleResult.Conclusive(survivingSide!.Index);
|
Result = FinalizeResult(BattleResult.Conclusive(survivingSide!.Index));
|
||||||
HasEnded = true;
|
HasEnded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,22 +309,15 @@ public class BattleImpl : ScriptSource, IBattle
|
|||||||
public void ForceEndBattle()
|
public void ForceEndBattle()
|
||||||
{
|
{
|
||||||
HasEnded = true;
|
HasEnded = true;
|
||||||
Result = BattleResult.Inconclusive;
|
Result = FinalizeResult(BattleResult.Inconclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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;
|
ITurnChoice? forcedChoice = null;
|
||||||
pokemon.RunScriptHook<IScriptForceTurnSelection>(script =>
|
pokemon.RunScriptHook<IScriptForceTurnSelection>(script =>
|
||||||
script.ForceTurnSelection(this, battleData.SideIndex, battleData.Position, ref forcedChoice));
|
script.ForceTurnSelection(this, pokemon.SideIndex, pokemon.Position, ref forcedChoice));
|
||||||
choice = forcedChoice;
|
choice = forcedChoice;
|
||||||
return choice != null;
|
return choice != null;
|
||||||
}
|
}
|
||||||
@@ -346,7 +351,7 @@ public class BattleImpl : ScriptSource, IBattle
|
|||||||
if (!switchChoice.SwitchTo.IsUsable)
|
if (!switchChoice.SwitchTo.IsUsable)
|
||||||
return false;
|
return false;
|
||||||
// Can't switch to a Pokémon already on the field
|
// Can't switch to a Pokémon already on the field
|
||||||
if (switchChoice.SwitchTo.BattleData is { IsOnBattlefield: true })
|
if (switchChoice.SwitchTo.IsOnBattlefield)
|
||||||
return false;
|
return false;
|
||||||
if (switchChoice.SwitchTo == switchChoice.User)
|
if (switchChoice.SwitchTo == switchChoice.User)
|
||||||
return false;
|
return false;
|
||||||
@@ -389,10 +394,10 @@ public class BattleImpl : ScriptSource, IBattle
|
|||||||
{
|
{
|
||||||
if (!CanUse(choice))
|
if (!CanUse(choice))
|
||||||
return false;
|
return false;
|
||||||
if (choice.User.BattleData?.IsOnBattlefield != true)
|
if (!choice.User.IsOnBattlefield)
|
||||||
return false;
|
return false;
|
||||||
var side = Sides[choice.User.BattleData!.SideIndex];
|
var side = Sides[choice.User.SideIndex];
|
||||||
side.SetChoice(choice.User.BattleData!.Position, choice);
|
side.SetChoice(choice.User.Position, choice);
|
||||||
CheckChoicesSetAndRun();
|
CheckChoicesSetAndRun();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -555,8 +560,8 @@ public class BattleImpl : ScriptSource, IBattle
|
|||||||
if (attemptCapture.IsCaught)
|
if (attemptCapture.IsCaught)
|
||||||
{
|
{
|
||||||
target.MarkAsCaught();
|
target.MarkAsCaught();
|
||||||
var side = Sides[target.BattleData!.SideIndex];
|
_capturedPokemon.Add(target.UnderlyingPokemon);
|
||||||
side.ForceClearPokemonFromField(target.BattleData.Position);
|
target.BattleSide.ForceClearPokemonFromField(target.Position);
|
||||||
}
|
}
|
||||||
EventHook.Invoke(new CaptureAttemptEvent(target, attemptCapture, item));
|
EventHook.Invoke(new CaptureAttemptEvent(target, attemptCapture, item));
|
||||||
|
|
||||||
@@ -592,9 +597,9 @@ public class BattleImpl : ScriptSource, IBattle
|
|||||||
{
|
{
|
||||||
foreach (var party in Parties)
|
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();
|
_weatherScript.Clear();
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public class BattleChoiceQueue : IDeepCloneable
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// Returns true if the Pokémon was found and moved, false otherwise.
|
/// Returns true if the Pokémon was found and moved, false otherwise.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public bool MovePokemonChoiceNext(IPokemon pokemon)
|
public bool MovePokemonChoiceNext(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var index = Array.FindIndex(_choices, _currentIndex, choice => choice?.User == pokemon);
|
var index = Array.FindIndex(_choices, _currentIndex, choice => choice?.User == pokemon);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
@@ -110,7 +110,7 @@ public class BattleChoiceQueue : IDeepCloneable
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// Returns true if the Pokémon was found and moved, false otherwise.
|
/// Returns true if the Pokémon was found and moved, false otherwise.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public bool MovePokemonChoiceLast(IPokemon pokemon)
|
public bool MovePokemonChoiceLast(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var index = Array.FindIndex(_choices, _currentIndex, choice => choice?.User == pokemon);
|
var index = Array.FindIndex(_choices, _currentIndex, choice => choice?.User == pokemon);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
|
|||||||
@@ -4,15 +4,35 @@ namespace PkmnLib.Dynamic.Models;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A battle party is a wrapper around a Pokemon party that provides additional functionality for battles.
|
/// 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>
|
/// </summary>
|
||||||
public interface IBattleParty : IDeepCloneable
|
public interface IBattleParty : IDeepCloneable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
IPokemonParty Party { get; }
|
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>
|
/// <summary>
|
||||||
/// Whether the party is responsible for the specified side and position.
|
/// Whether the party is responsible for the specified side and position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -26,7 +46,7 @@ public interface IBattleParty : IDeepCloneable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all usable Pokemon that are not currently in the field.
|
/// Gets all usable Pokemon that are not currently in the field.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IEnumerable<IPokemon> GetUsablePokemonNotInField();
|
IEnumerable<IBattlePokemon> GetUsablePokemonNotInField();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -39,6 +59,8 @@ public record struct ResponsibleIndex(byte Side, byte Position);
|
|||||||
public class BattlePartyImpl : IBattleParty
|
public class BattlePartyImpl : IBattleParty
|
||||||
{
|
{
|
||||||
private readonly ResponsibleIndex[] _responsibleIndices;
|
private readonly ResponsibleIndex[] _responsibleIndices;
|
||||||
|
private IBattlePokemon?[] _battlePokemon = [];
|
||||||
|
private IBattle? _battle;
|
||||||
|
|
||||||
/// <inheritdoc cref="BattlePartyImpl"/>
|
/// <inheritdoc cref="BattlePartyImpl"/>
|
||||||
public BattlePartyImpl(IPokemonParty party, ResponsibleIndex[] responsibleIndices)
|
public BattlePartyImpl(IPokemonParty party, ResponsibleIndex[] responsibleIndices)
|
||||||
@@ -50,14 +72,53 @@ public class BattlePartyImpl : IBattleParty
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IPokemonParty Party { get; }
|
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 />
|
/// <inheritdoc />
|
||||||
public bool IsResponsibleForIndex(ResponsibleIndex index) => _responsibleIndices.Contains(index);
|
public bool IsResponsibleForIndex(ResponsibleIndex index) => _responsibleIndices.Contains(index);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool HasUsablePokemonNotInField() =>
|
public bool HasUsablePokemonNotInField() =>
|
||||||
Party.WhereNotNull().Any(x => x.IsUsable && x.BattleData?.IsOnBattlefield != true);
|
_battlePokemon.WhereNotNull().Any(x => x.IsUsable && !x.IsOnBattlefield);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<IPokemon> GetUsablePokemonNotInField() =>
|
public IEnumerable<IBattlePokemon> GetUsablePokemonNotInField() =>
|
||||||
Party.WhereNotNull().Where(x => x.IsUsable && x.BattleData?.IsOnBattlefield != true);
|
_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
|
/// rolls whether it triggers. As a side effect this run scripts to allow modifying this random
|
||||||
/// chance.
|
/// chance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool EffectChance(float chance, IExecutingMove executingMove, IPokemon target, byte hitNumber);
|
bool EffectChance(float chance, IExecutingMove executingMove, IBattlePokemon target, byte hitNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IBattleRandom"/>
|
/// <inheritdoc cref="IBattleRandom"/>
|
||||||
@@ -36,7 +36,7 @@ public class BattleRandomImpl : RandomImpl, IBattleRandom
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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 =>
|
executingMove.RunScriptHook<IScriptChangeEffectChance>(script =>
|
||||||
script.ChangeEffectChance(executingMove, target, hitNumber, ref chance));
|
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.
|
/// The side that won the battle. If null, no side has won.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte? WinningSide { get; }
|
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>
|
/// <summary>
|
||||||
/// A list of Pokémon currently on the battlefield.
|
/// A list of Pokémon currently on the battlefield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<IPokemon?> Pokemon { get; }
|
IReadOnlyList<IBattlePokemon?> Pokemon { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently set choices for all Pokémon on the battlefield. Cleared when the turn starts.
|
/// 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
|
/// 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.
|
/// cleared. Returns the Pokémon that was previously in the spot.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Swaps two Pokémon on the side.
|
/// Swaps two Pokémon on the side.
|
||||||
@@ -94,7 +101,7 @@ public interface IBattleSide : IScriptSource, IDeepCloneable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether a Pokemon is on the field in this side.
|
/// Checks whether a Pokemon is on the field in this side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsPokemonOnSide(IPokemon pokemon);
|
bool IsPokemonOnSide(IBattlePokemon pokemon);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marks a slot as unfillable. This happens when no parties are able to fill the slot anymore.
|
/// 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;
|
Index = index;
|
||||||
NumberOfPositions = numberOfPositions;
|
NumberOfPositions = numberOfPositions;
|
||||||
_pokemon = new IPokemon?[numberOfPositions];
|
_pokemon = new IBattlePokemon?[numberOfPositions];
|
||||||
_setChoices = new ITurnChoice?[numberOfPositions];
|
_setChoices = new ITurnChoice?[numberOfPositions];
|
||||||
_fillablePositions = new bool[numberOfPositions];
|
_fillablePositions = new bool[numberOfPositions];
|
||||||
for (byte i = 0; i < numberOfPositions; i++)
|
for (byte i = 0; i < numberOfPositions; i++)
|
||||||
@@ -183,10 +190,10 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public byte NumberOfPositions { get; }
|
public byte NumberOfPositions { get; }
|
||||||
|
|
||||||
private readonly IPokemon?[] _pokemon;
|
private readonly IBattlePokemon?[] _pokemon;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyList<IPokemon?> Pokemon => _pokemon;
|
public IReadOnlyList<IBattlePokemon?> Pokemon => _pokemon;
|
||||||
|
|
||||||
private readonly ITurnChoice?[] _setChoices;
|
private readonly ITurnChoice?[] _setChoices;
|
||||||
|
|
||||||
@@ -249,28 +256,31 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
|||||||
if (pokemon is not null)
|
if (pokemon is not null)
|
||||||
{
|
{
|
||||||
pokemon.RunScriptHook<IScriptOnRemove>(script => script.OnRemove());
|
pokemon.RunScriptHook<IScriptOnRemove>(script => script.OnRemove());
|
||||||
pokemon.SetOnBattlefield(false);
|
pokemon.OnSwitchedOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
_pokemon[index] = null;
|
_pokemon[index] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IPokemon? SwapPokemon(byte position, IPokemon? pokemon)
|
public IBattlePokemon? SwapPokemon(byte position, IBattlePokemon? pokemon)
|
||||||
{
|
{
|
||||||
var oldPokemon = _pokemon[position];
|
var oldPokemon = _pokemon[position];
|
||||||
if (oldPokemon is not null)
|
if (oldPokemon is not null)
|
||||||
{
|
{
|
||||||
oldPokemon.RunScriptHook<IScriptOnSwitchOut>(script => script.OnSwitchOut(oldPokemon, position));
|
oldPokemon.RunScriptHook<IScriptOnSwitchOut>(script => script.OnSwitchOut(oldPokemon, position));
|
||||||
oldPokemon.RunScriptHook<IScriptOnRemove>(script => script.OnRemove());
|
oldPokemon.RunScriptHook<IScriptOnRemove>(script => script.OnRemove());
|
||||||
oldPokemon.SetOnBattlefield(false);
|
oldPokemon.OnSwitchedOut();
|
||||||
}
|
}
|
||||||
_pokemon[position] = pokemon;
|
_pokemon[position] = pokemon;
|
||||||
if (pokemon is not null)
|
if (pokemon is not null)
|
||||||
{
|
{
|
||||||
pokemon.SetBattleData(Battle, Index);
|
if (pokemon.SideIndex != Index)
|
||||||
pokemon.SetOnBattlefield(true);
|
{
|
||||||
pokemon.SetBattleSidePosition(position);
|
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));
|
Battle.EventHook.Invoke(new SwitchEvent(Index, position, pokemon));
|
||||||
pokemon.RunScriptHook<IScriptOnSwitchIn>(script => script.OnSwitchIn(pokemon, position));
|
pokemon.RunScriptHook<IScriptOnSwitchIn>(script => script.OnSwitchIn(pokemon, position));
|
||||||
|
|
||||||
@@ -300,6 +310,14 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
|||||||
return oldPokemon;
|
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 />
|
/// <inheritdoc />
|
||||||
public void SwapPokemon(byte position1, byte position2)
|
public void SwapPokemon(byte position1, byte position2)
|
||||||
{
|
{
|
||||||
@@ -307,7 +325,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsPokemonOnSide(IPokemon pokemon) => _pokemon.Contains(pokemon);
|
public bool IsPokemonOnSide(IBattlePokemon pokemon) => _pokemon.Contains(pokemon);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void MarkPositionAsUnfillable(byte position) => _fillablePositions[position] = false;
|
public void MarkPositionAsUnfillable(byte position) => _fillablePositions[position] = false;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public interface IFleeChoice : ITurnChoice
|
|||||||
public class FleeTurnChoice : TurnChoice, IFleeChoice
|
public class FleeTurnChoice : TurnChoice, IFleeChoice
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public FleeTurnChoice(IPokemon user) : base(user)
|
public FleeTurnChoice(IBattlePokemon user) : base(user)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,15 +16,15 @@ public interface IItemChoice : ITurnChoice
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The target Pokémon of the item, if any.
|
/// The target Pokémon of the item, if any.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IPokemon? GetTargetPokemon(IBattle battle);
|
IBattlePokemon? GetTargetPokemon(IBattle battle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IItemChoice"/>
|
/// <inheritdoc cref="IItemChoice"/>
|
||||||
public class ItemChoice : TurnChoice, IItemChoice
|
public class ItemChoice : TurnChoice, IItemChoice
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="ItemChoice"/>
|
/// <inheritdoc cref="ItemChoice"/>
|
||||||
private ItemChoice(IPokemon user, IItem item, byte? targetSide, byte? targetPosition, IPokemon? targetPokemon) :
|
private ItemChoice(IBattlePokemon user, IItem item, byte? targetSide, byte? targetPosition,
|
||||||
base(user)
|
IBattlePokemon? targetPokemon) : base(user)
|
||||||
{
|
{
|
||||||
Item = item;
|
Item = item;
|
||||||
TargetSide = targetSide;
|
TargetSide = targetSide;
|
||||||
@@ -32,13 +32,13 @@ public class ItemChoice : TurnChoice, IItemChoice
|
|||||||
TargetPokemon = targetPokemon;
|
TargetPokemon = targetPokemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ItemChoice CreateWithoutTarget(IPokemon user, IItem item) =>
|
public static ItemChoice CreateWithoutTarget(IBattlePokemon user, IItem item) =>
|
||||||
new(user, item, null, null, null);
|
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);
|
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);
|
new(user, item, null, null, targetPokemon);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -47,7 +47,7 @@ public class ItemChoice : TurnChoice, IItemChoice
|
|||||||
public IItem Item { get; }
|
public IItem Item { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IPokemon? GetTargetPokemon(IBattle battle)
|
public IBattlePokemon? GetTargetPokemon(IBattle battle)
|
||||||
{
|
{
|
||||||
if (TargetPokemon != null)
|
if (TargetPokemon != null)
|
||||||
return TargetPokemon;
|
return TargetPokemon;
|
||||||
@@ -71,7 +71,7 @@ public class ItemChoice : TurnChoice, IItemChoice
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The target Pokémon of the item, if any. This is used for party members.
|
/// The target Pokémon of the item, if any. This is used for party members.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private IPokemon? TargetPokemon { get; }
|
private IBattlePokemon? TargetPokemon { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override int ScriptCount => User.ScriptCount;
|
public override int ScriptCount => User.ScriptCount;
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public interface IMoveChoice : ITurnChoice
|
|||||||
public class MoveChoice : TurnChoice, IMoveChoice
|
public class MoveChoice : TurnChoice, IMoveChoice
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="MoveChoice"/>
|
/// <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;
|
ChosenMove = usedMove;
|
||||||
TargetSide = targetSide;
|
TargetSide = targetSide;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public interface IPassChoice : ITurnChoice
|
|||||||
public class PassChoice : TurnChoice, IPassChoice
|
public class PassChoice : TurnChoice, IPassChoice
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="PassChoice"/>
|
/// <inheritdoc cref="PassChoice"/>
|
||||||
public PassChoice(IPokemon user) : base(user)
|
public PassChoice(IBattlePokemon user) : base(user)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,20 +10,20 @@ public interface ISwitchChoice : ITurnChoice
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokémon to switch to.
|
/// The Pokémon to switch to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IPokemon SwitchTo { get; }
|
IBattlePokemon SwitchTo { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ISwitchChoice"/>
|
/// <inheritdoc cref="ISwitchChoice"/>
|
||||||
public class SwitchChoice : TurnChoice, ISwitchChoice
|
public class SwitchChoice : TurnChoice, ISwitchChoice
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="SwitchChoice"/>
|
/// <inheritdoc cref="SwitchChoice"/>
|
||||||
public SwitchChoice(IPokemon user, IPokemon switchTo) : base(user)
|
public SwitchChoice(IBattlePokemon user, IBattlePokemon switchTo) : base(user)
|
||||||
{
|
{
|
||||||
SwitchTo = switchTo;
|
SwitchTo = switchTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IPokemon SwitchTo { get; }
|
public IBattlePokemon SwitchTo { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override int ScriptCount => User.ScriptCount;
|
public override int ScriptCount => User.ScriptCount;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public interface ITurnChoice : IScriptSource, IDeepCloneable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The user of the turn choice
|
/// The user of the turn choice
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IPokemon User { get; }
|
IBattlePokemon User { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The speed of the user at the beginning of the turn.
|
/// 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
|
/// Fails the choice. This will prevent it from executing and run a specific fail handling during
|
||||||
/// execution. Note that this can not be undone.
|
/// execution. Note that this can not be undone.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Fail();
|
void Fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -43,7 +43,7 @@ public interface ITurnChoice : IScriptSource, IDeepCloneable
|
|||||||
public abstract class TurnChoice : ScriptSource, ITurnChoice
|
public abstract class TurnChoice : ScriptSource, ITurnChoice
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="TurnChoice"/>
|
/// <inheritdoc cref="TurnChoice"/>
|
||||||
protected TurnChoice(IPokemon user)
|
protected TurnChoice(IBattlePokemon user)
|
||||||
{
|
{
|
||||||
User = user;
|
User = user;
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ public abstract class TurnChoice : ScriptSource, ITurnChoice
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Pokemon for which the choice is made.
|
/// The Pokemon for which the choice is made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPokemon User { get; }
|
public IBattlePokemon User { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The speed of the user at the beginning of the turn.
|
/// The speed of the user at the beginning of the turn.
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ public interface IExecutingMove : IScriptSource
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The user of the move.
|
/// The user of the move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IPokemon User { get; }
|
IBattlePokemon User { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The move the user has actually chosen to do.
|
/// The move the user has actually chosen to do.
|
||||||
@@ -151,17 +151,17 @@ public interface IExecutingMove : IScriptSource
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a hit data for a target, with a specific index.
|
/// Gets a hit data for a target, with a specific index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IHitData GetHitData(IPokemon target, byte hit);
|
IHitData GetHitData(IBattlePokemon target, byte hit);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether a Pokémon is a target for this move.
|
/// Checks whether a Pokémon is a target for this move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsPokemonTarget(IPokemon target);
|
bool IsPokemonTarget(IBattlePokemon target);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the index of the hits in this move where the hits for a specific target start.
|
/// Gets the index of the hits in this move where the hits for a specific target start.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int GetTargetIndex(IPokemon target);
|
int GetTargetIndex(IBattlePokemon target);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a hit based on its raw index.
|
/// Gets a hit based on its raw index.
|
||||||
@@ -171,7 +171,7 @@ public interface IExecutingMove : IScriptSource
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the targets of this move.
|
/// Gets the targets of this move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<IPokemon?> Targets { get; }
|
IReadOnlyList<IBattlePokemon?> Targets { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The underlying move choice.
|
/// The underlying move choice.
|
||||||
@@ -192,12 +192,12 @@ public interface IExecutingMove : IScriptSource
|
|||||||
/// <inheritdoc cref="IExecutingMove"/>
|
/// <inheritdoc cref="IExecutingMove"/>
|
||||||
public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
||||||
{
|
{
|
||||||
private readonly IReadOnlyList<IPokemon?> _targets;
|
private readonly IReadOnlyList<IBattlePokemon?> _targets;
|
||||||
private readonly IHitData[] _hits;
|
private readonly IHitData[] _hits;
|
||||||
private readonly IBattle _battle;
|
private readonly IBattle _battle;
|
||||||
|
|
||||||
/// <inheritdoc cref="ExecutingMoveImpl"/>
|
/// <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)
|
IMoveData useMove, IMoveChoice moveChoice, IBattle battle)
|
||||||
{
|
{
|
||||||
_targets = targets;
|
_targets = targets;
|
||||||
@@ -222,7 +222,7 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
|||||||
public byte NumberOfHits { get; }
|
public byte NumberOfHits { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IPokemon User => MoveChoice.User;
|
public IBattlePokemon User => MoveChoice.User;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ILearnedMove ChosenMove { get; }
|
public ILearnedMove ChosenMove { get; }
|
||||||
@@ -239,7 +239,7 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
|||||||
public IScriptSet Volatile => MoveChoice.Volatile;
|
public IScriptSet Volatile => MoveChoice.Volatile;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IHitData GetHitData(IPokemon target, byte hit)
|
public IHitData GetHitData(IBattlePokemon target, byte hit)
|
||||||
{
|
{
|
||||||
var targetIndex = _targets.IndexOf(target);
|
var targetIndex = _targets.IndexOf(target);
|
||||||
if (targetIndex == -1)
|
if (targetIndex == -1)
|
||||||
@@ -252,10 +252,10 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsPokemonTarget(IPokemon target) => _targets.Contains(target);
|
public bool IsPokemonTarget(IBattlePokemon target) => _targets.Contains(target);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int GetTargetIndex(IPokemon target)
|
public int GetTargetIndex(IBattlePokemon target)
|
||||||
{
|
{
|
||||||
var targetIndex = _targets.IndexOf(target);
|
var targetIndex = _targets.IndexOf(target);
|
||||||
if (targetIndex == -1)
|
if (targetIndex == -1)
|
||||||
@@ -273,7 +273,7 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyList<IPokemon?> Targets => _targets.ToList();
|
public IReadOnlyList<IBattlePokemon?> Targets => _targets.ToList();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IMoveChoice MoveChoice { get; }
|
public IMoveChoice MoveChoice { get; }
|
||||||
|
|||||||
@@ -35,29 +35,30 @@ public static class ItemTargetTypeHelpers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines if the given target is valid based on the ItemTargetType.
|
/// Determines if the given target is valid based on the ItemTargetType.
|
||||||
/// </summary>
|
/// </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)
|
if (targetType == ItemTargetType.None)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (targetType.HasFlag(ItemTargetType.OwnPokemon))
|
if (targetType.HasFlag(ItemTargetType.OwnPokemon))
|
||||||
{
|
{
|
||||||
var userParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(user));
|
var userParty = battle.Parties.FirstOrDefault(x => x.BattlePokemon.Contains(user));
|
||||||
var targetParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(target));
|
var targetParty = battle.Parties.FirstOrDefault(x => x.BattlePokemon.Contains(target));
|
||||||
if (userParty is not null && targetParty is not null && userParty == targetParty)
|
if (userParty is not null && targetParty is not null && userParty == targetParty)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetType.HasFlag(ItemTargetType.AllyPokemon))
|
if (targetType.HasFlag(ItemTargetType.AllyPokemon))
|
||||||
{
|
{
|
||||||
if (user.BattleData?.BattleSide == target.BattleData?.BattleSide)
|
if (user.BattleSide == target.BattleSide)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetType.HasFlag(ItemTargetType.FoePokemon))
|
if (targetType.HasFlag(ItemTargetType.FoePokemon))
|
||||||
{
|
{
|
||||||
var userParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(user));
|
var userParty = battle.Parties.FirstOrDefault(x => x.BattlePokemon.Contains(user));
|
||||||
var targetParty = battle.Parties.FirstOrDefault(x => x.Party.Contains(target));
|
var targetParty = battle.Parties.FirstOrDefault(x => x.BattlePokemon.Contains(target));
|
||||||
if (userParty is not null && targetParty is not null && userParty != targetParty)
|
if (userParty is not null && targetParty is not null && userParty != targetParty)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,7 @@ public record SerializedPokemon
|
|||||||
Nature = pokemon.Nature.Name;
|
Nature = pokemon.Nature.Name;
|
||||||
Nickname = pokemon.Nickname;
|
Nickname = pokemon.Nickname;
|
||||||
Ability = pokemon.Form.GetAbility(pokemon.AbilityIndex);
|
Ability = pokemon.Form.GetAbility(pokemon.AbilityIndex);
|
||||||
Moves = pokemon.BaseMoves.Select(move =>
|
Moves = pokemon.Moves.Select(move =>
|
||||||
{
|
{
|
||||||
if (move == null)
|
if (move == null)
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public abstract class ItemScript : IDeepCloneable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether the item is usable on the given target.
|
/// Returns whether the item is usable on the given target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool IsTargetValid(IPokemon target) => false;
|
public virtual bool IsTargetValid(IBattlePokemon target) => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether the item can be held by a Pokémon.
|
/// Returns whether the item can be held by a Pokémon.
|
||||||
@@ -51,7 +51,7 @@ public abstract class ItemScript : IDeepCloneable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether the item can be held by the given target.
|
/// Returns whether the item can be held by the given target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool CanTargetHold(IPokemon pokemon) => true;
|
public virtual bool CanTargetHold(IBattlePokemon pokemon) => true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles the use of the item.
|
/// Handles the use of the item.
|
||||||
@@ -63,7 +63,7 @@ public abstract class ItemScript : IDeepCloneable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles the use of the item on the given target.
|
/// Handles the use of the item on the given target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void OnUseWithTarget(IPokemon target, EventHook eventHook)
|
public virtual void OnUseWithTarget(IBattlePokemon target, EventHook eventHook)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,9 +17,9 @@ public abstract class PokeballScript : ItemScript
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the catch rate of the Pokéball against the given target Pokémon.
|
/// Returns the catch rate of the Pokéball against the given target Pokémon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract void ChangeCatchRate(IPokemon target, ref byte catchRate);
|
public abstract void ChangeCatchRate(IBattlePokemon target, ref byte catchRate);
|
||||||
|
|
||||||
public virtual void OnAfterSuccessfulCapture(IPokemon target)
|
public virtual void OnAfterSuccessfulCapture(IBattlePokemon target)
|
||||||
{
|
{
|
||||||
// Default implementation does nothing.
|
// Default implementation does nothing.
|
||||||
// Override this method in derived classes to add custom behavior after a successful capture.
|
// Override this method in derived classes to add custom behavior after a successful capture.
|
||||||
@@ -32,16 +32,13 @@ public abstract class PokeballScript : ItemScript
|
|||||||
public override ItemTargetType TargetType => ItemTargetType.FoePokemon;
|
public override ItemTargetType TargetType => ItemTargetType.FoePokemon;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool IsTargetValid(IPokemon target) =>
|
public override bool IsTargetValid(IBattlePokemon target) => target.Battle.IsWildBattle;
|
||||||
target.BattleData is not null && target.BattleData.Battle.IsWildBattle;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnUseWithTarget(IPokemon target, EventHook eventHook)
|
public override void OnUseWithTarget(IBattlePokemon target, EventHook eventHook)
|
||||||
{
|
{
|
||||||
var battleData = target.BattleData;
|
var result = target.Battle.AttempCapture(target.SideIndex, target.Position, Item);
|
||||||
|
if (result.IsCaught)
|
||||||
var result = battleData?.Battle.AttempCapture(battleData.SideIndex, battleData.Position, Item);
|
|
||||||
if (result is { IsCaught: true })
|
|
||||||
{
|
{
|
||||||
OnAfterSuccessfulCapture(target);
|
OnAfterSuccessfulCapture(target);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public interface IAIInfoScriptExpectedEndOfTurnDamage
|
|||||||
/// This function returns the expected end of turn damage for the script. This is used for scripts that
|
/// This function returns the expected end of turn damage for the script. This is used for scripts that
|
||||||
/// have an end of turn effect, such as Poison or Burn.
|
/// have an end of turn effect, such as Poison or Burn.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ExpectedEndOfTurnDamage(IPokemon pokemon, ref int damage);
|
void ExpectedEndOfTurnDamage(IBattlePokemon pokemon, ref int damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -37,5 +37,5 @@ public interface IAIInfoScriptExpectedEntryDamage
|
|||||||
/// This function returns the expected entry damage for the script. This is used for scripts that have
|
/// This function returns the expected entry damage for the script. This is used for scripts that have
|
||||||
/// an entry hazard effect, such as Spikes or Stealth Rock.
|
/// an entry hazard effect, such as Spikes or Stealth Rock.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ExpectedEntryDamage(IPokemon pokemon, ref uint damage);
|
void ExpectedEntryDamage(IBattlePokemon pokemon, ref uint damage);
|
||||||
}
|
}
|
||||||
@@ -100,8 +100,8 @@ public static class ScriptExecution
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Executes a script on an item.
|
/// Executes a script on an item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void RunItemScript(this IItem item, ScriptResolver scriptResolver, IPokemon? target, IPokemon user,
|
public static void RunItemScript(this IItem item, ScriptResolver scriptResolver, IBattlePokemon? target,
|
||||||
IBattle battle, EventHook eventHook)
|
IBattlePokemon user, IBattle battle, EventHook eventHook)
|
||||||
{
|
{
|
||||||
if (!scriptResolver.TryResolveBattleItemScript(item, out var itemScript))
|
if (!scriptResolver.TryResolveBattleItemScript(item, out var itemScript))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public interface IScriptChangeTargets
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the targets of a move choice. This allows for changing the targets of a move before the move starts.
|
/// Changes the targets of a move choice. This allows for changing the targets of a move before the move starts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeTargets(IMoveChoice moveChoice, ref IReadOnlyList<IPokemon?> targets);
|
void ChangeTargets(IMoveChoice moveChoice, ref IReadOnlyList<IBattlePokemon?> targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -136,7 +136,7 @@ public interface IScriptChangeIncomingTargets
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows you to change the targets of a move choice before the move starts.
|
/// This function allows you to change the targets of a move choice before the move starts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeIncomingTargets(IMoveChoice moveChoice, ref IReadOnlyList<IPokemon?> targets);
|
void ChangeIncomingTargets(IMoveChoice moveChoice, ref IReadOnlyList<IBattlePokemon?> targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -219,7 +219,7 @@ public interface IScriptChangeMoveType
|
|||||||
/// This function allows the script to change the actual type that is used for the move on a target.
|
/// This function allows the script to change the actual type that is used for the move on a target.
|
||||||
/// If this is set to null, the move will be treated as a typeless move.
|
/// If this is set to null, the move will be treated as a typeless move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeMoveType(IExecutingMove move, IPokemon target, byte hit, ref TypeIdentifier? typeIdentifier);
|
void ChangeMoveType(IExecutingMove move, IBattlePokemon target, byte hit, ref TypeIdentifier? typeIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -230,7 +230,7 @@ public interface IScriptChangeEffectiveness
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows the script to change how effective a move is on a target.
|
/// This function allows the script to change how effective a move is on a target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeEffectiveness(IExecutingMove move, IPokemon target, byte hit, ref float effectiveness);
|
void ChangeEffectiveness(IExecutingMove move, IBattlePokemon target, byte hit, ref float effectiveness);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -241,7 +241,7 @@ public interface IScriptChangeIncomingEffectiveness
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows the script to override how effective a move is on a target.
|
/// This function allows the script to override how effective a move is on a target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeIncomingEffectiveness(IExecutingMove executingMove, IPokemon target, byte hitIndex,
|
void ChangeIncomingEffectiveness(IExecutingMove executingMove, IBattlePokemon target, byte hitIndex,
|
||||||
ref float effectiveness);
|
ref float effectiveness);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,7 +253,7 @@ public interface IScriptBlockCriticalHit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to block an outgoing move from being critical.
|
/// This function allows a script to block an outgoing move from being critical.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void BlockCriticalHit(IExecutingMove move, IPokemon target, byte hit, ref bool block);
|
void BlockCriticalHit(IExecutingMove move, IBattlePokemon target, byte hit, ref bool block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -264,7 +264,7 @@ public interface IScriptBlockIncomingCriticalHit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to block an incoming move from being critical.
|
/// This function allows a script to block an incoming move from being critical.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void BlockIncomingCriticalHit(IExecutingMove move, IPokemon target, byte hit, ref bool block);
|
void BlockIncomingCriticalHit(IExecutingMove move, IBattlePokemon target, byte hit, ref bool block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -276,7 +276,7 @@ public interface IScriptOnIncomingHit
|
|||||||
/// This function triggers when an incoming hit happens. This triggers after the damage is done,
|
/// This function triggers when an incoming hit happens. This triggers after the damage is done,
|
||||||
/// but before the secondary effect of the move happens.
|
/// but before the secondary effect of the move happens.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit);
|
void OnIncomingHit(IExecutingMove move, IBattlePokemon target, byte hit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -287,7 +287,7 @@ public interface IScriptOnOpponentFaints
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function triggers when an opponent on the f ield faints due to the move that is being executed.
|
/// This function triggers when an opponent on the f ield faints due to the move that is being executed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnOpponentFaints(IExecutingMove move, IPokemon target, byte hit);
|
void OnOpponentFaints(IExecutingMove move, IBattlePokemon target, byte hit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -300,7 +300,7 @@ public interface IScriptOnSecondaryEffect
|
|||||||
/// secondary effects here. Status moves should implement their actual functionality in this
|
/// secondary effects here. Status moves should implement their actual functionality in this
|
||||||
/// function as well, as status moves effects are defined as secondary effects for simplicity.
|
/// function as well, as status moves effects are defined as secondary effects for simplicity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit);
|
void OnSecondaryEffect(IExecutingMove move, IBattlePokemon target, byte hit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -312,7 +312,7 @@ public interface IScriptFailIncomingMove
|
|||||||
/// This function allows a script to prevent a move that is targeted at its owner. If set to true
|
/// This function allows a script to prevent a move that is targeted at its owner. If set to true
|
||||||
/// the move fails, and fail events get triggered.
|
/// the move fails, and fail events get triggered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void FailIncomingMove(IExecutingMove move, IPokemon target, ref bool fail);
|
void FailIncomingMove(IExecutingMove move, IBattlePokemon target, ref bool fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -323,7 +323,7 @@ public interface IScriptIsInvulnerableToMove
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to make its owner invulnerable to an incoming move.
|
/// This function allows a script to make its owner invulnerable to an incoming move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void IsInvulnerableToMove(IExecutingMove move, IPokemon target, ref bool invulnerable);
|
void IsInvulnerableToMove(IExecutingMove move, IBattlePokemon target, ref bool invulnerable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -335,7 +335,7 @@ public interface IScriptOnMoveMiss
|
|||||||
/// This function allows a script to run when a move misses its target. This is used for moves
|
/// This function allows a script to run when a move misses its target. This is used for moves
|
||||||
/// that have a secondary effect that should run even if the move misses, such as Spore.
|
/// that have a secondary effect that should run even if the move misses, such as Spore.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnMoveMiss(IExecutingMove move, IPokemon target);
|
void OnMoveMiss(IExecutingMove move, IBattlePokemon target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -347,7 +347,7 @@ public interface IScriptChangeAccuracyModifier
|
|||||||
/// This function allows a script to modify the accuracy of a move used. This value represents
|
/// This function allows a script to modify the accuracy of a move used. This value represents
|
||||||
/// the percentage accuracy, so anything above 100% will make it always hit.
|
/// the percentage accuracy, so anything above 100% will make it always hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeAccuracyModifier(IExecutingMove move, IPokemon target, byte hit, ref float modifier);
|
void ChangeAccuracyModifier(IExecutingMove move, IBattlePokemon target, byte hit, ref float modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -358,7 +358,7 @@ public interface IScriptChangeCriticalStage
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to change the critical stage of the move used.
|
/// This function allows a script to change the critical stage of the move used.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeCriticalStage(IExecutingMove move, IPokemon target, byte hit, ref byte stage);
|
void ChangeCriticalStage(IExecutingMove move, IBattlePokemon target, byte hit, ref byte stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -370,7 +370,7 @@ public interface IScriptChangeCriticalModifier
|
|||||||
/// This function allows a script to change the damage modifier of a critical hit. This will only
|
/// This function allows a script to change the damage modifier of a critical hit. This will only
|
||||||
/// run when a hit is critical.
|
/// run when a hit is critical.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeCriticalModifier(IExecutingMove move, IPokemon target, byte hit, ref float modifier);
|
void ChangeCriticalModifier(IExecutingMove move, IBattlePokemon target, byte hit, ref float modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -382,7 +382,7 @@ public interface IScriptChangeStabModifier
|
|||||||
/// This function allows a script to change the damage modifier of a Same Type Attack Bonus, which
|
/// This function allows a script to change the damage modifier of a Same Type Attack Bonus, which
|
||||||
/// occurs when the user has the move type as one of its own types.
|
/// occurs when the user has the move type as one of its own types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeStabModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber, bool isStab,
|
void ChangeStabModifier(IExecutingMove executingMove, IBattlePokemon target, byte hitNumber, bool isStab,
|
||||||
ref float modifier);
|
ref float modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,7 +394,7 @@ public interface IScriptChangeBasePower
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to change the effective base power of a move hit.
|
/// This function allows a script to change the effective base power of a move hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower);
|
void ChangeBasePower(IExecutingMove move, IBattlePokemon target, byte hit, ref ushort basePower);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -407,7 +407,7 @@ public interface IScriptBypassDefensiveStatBoosts
|
|||||||
/// If this is true, the damage will be calculated as if the target has no positive stat boosts. Negative
|
/// If this is true, the damage will be calculated as if the target has no positive stat boosts. Negative
|
||||||
/// stat boosts will still be applied.
|
/// stat boosts will still be applied.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void BypassDefensiveStatBoosts(IExecutingMove move, IPokemon target, byte hit, ref bool bypass);
|
void BypassDefensiveStatBoosts(IExecutingMove move, IBattlePokemon target, byte hit, ref bool bypass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -419,7 +419,7 @@ public interface IScriptBypassEvasionStatBoosts
|
|||||||
/// This function allows a script to bypass evasion stat boosts for a move hit.
|
/// This function allows a script to bypass evasion stat boosts for a move hit.
|
||||||
/// If this is true, the move will handle the evasion stat boosts as if the target has no positive stat boosts.
|
/// If this is true, the move will handle the evasion stat boosts as if the target has no positive stat boosts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void BypassEvasionStatBoosts(IExecutingMove move, IPokemon target, byte hitIndex, ref bool bypass);
|
void BypassEvasionStatBoosts(IExecutingMove move, IBattlePokemon target, byte hitIndex, ref bool bypass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -432,7 +432,7 @@ public interface IScriptBypassOffensiveStatBoosts
|
|||||||
/// If this is true, the damage will be calculated as if the user has no negative offensive stat boosts. Positive
|
/// If this is true, the damage will be calculated as if the user has no negative offensive stat boosts. Positive
|
||||||
/// stat boosts will still be applied.
|
/// stat boosts will still be applied.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void BypassOffensiveStatBoosts(IExecutingMove move, IPokemon target, byte hit, ref bool bypass);
|
void BypassOffensiveStatBoosts(IExecutingMove move, IBattlePokemon target, byte hit, ref bool bypass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -443,7 +443,7 @@ public interface IScriptChangeOffensiveStatValue
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to change the actual offensive stat values used when calculating damage
|
/// This function allows a script to change the actual offensive stat values used when calculating damage
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
|
void ChangeOffensiveStatValue(IExecutingMove move, IBattlePokemon target, byte hit, uint defensiveStat,
|
||||||
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value);
|
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,7 +455,7 @@ public interface IScriptChangeDefensiveStatValue
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to change the actual defensive stat values used when calculating damage.
|
/// This function allows a script to change the actual defensive stat values used when calculating damage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
|
void ChangeDefensiveStatValue(IExecutingMove move, IBattlePokemon target, byte hit, uint offensiveStat,
|
||||||
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value);
|
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,7 +467,7 @@ public interface IScriptChangeIncomingMoveOffensiveStatValue
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to change the offensive stat value of an incoming move.
|
/// This function allows a script to change the offensive stat value of an incoming move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeIncomingMoveOffensiveStatValue(IExecutingMove executingMove, IPokemon target, byte hitNumber,
|
void ChangeIncomingMoveOffensiveStatValue(IExecutingMove executingMove, IBattlePokemon target, byte hitNumber,
|
||||||
uint defensiveStat, StatisticSet<uint> targetStats, Statistic offensive, ref uint offensiveStat);
|
uint defensiveStat, StatisticSet<uint> targetStats, Statistic offensive, ref uint offensiveStat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +479,7 @@ public interface IScriptChangeIncomingMoveDefensiveStatValue
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to change the defensive stat value of an incoming move.
|
/// This function allows a script to change the defensive stat value of an incoming move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeIncomingMoveDefensiveStatValue(IExecutingMove executingMove, IPokemon target, byte hitNumber,
|
void ChangeIncomingMoveDefensiveStatValue(IExecutingMove executingMove, IBattlePokemon target, byte hitNumber,
|
||||||
uint origOffensiveStat, StatisticSet<uint> targetStats, Statistic defensive, ref uint defensiveStat);
|
uint origOffensiveStat, StatisticSet<uint> targetStats, Statistic defensive, ref uint defensiveStat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,7 +492,7 @@ public interface IScriptChangeDamageStatModifier
|
|||||||
/// This function allows a script to change the raw modifier we retrieved from the stats of the
|
/// This function allows a script to change the raw modifier we retrieved from the stats of the
|
||||||
/// defender and attacker. The default value is the offensive stat divided by the defensive stat.
|
/// defender and attacker. The default value is the offensive stat divided by the defensive stat.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeDamageStatModifier(IExecutingMove move, IPokemon target, byte hit, ref float modifier);
|
void ChangeDamageStatModifier(IExecutingMove move, IBattlePokemon target, byte hit, ref float modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -503,7 +503,7 @@ public interface IScriptChangeDamageModifier
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to apply a raw multiplier to the damage done by a move.
|
/// This function allows a script to apply a raw multiplier to the damage done by a move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeDamageModifier(IExecutingMove move, IPokemon target, byte hit, ref float modifier);
|
void ChangeDamageModifier(IExecutingMove move, IBattlePokemon target, byte hit, ref float modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -514,7 +514,7 @@ public interface IScriptChangeIncomingMoveDamageModifier
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to change the damage modifier of an incoming move.
|
/// This function allows a script to change the damage modifier of an incoming move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeIncomingMoveDamageModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber,
|
void ChangeIncomingMoveDamageModifier(IExecutingMove executingMove, IBattlePokemon target, byte hitNumber,
|
||||||
ref float modifier);
|
ref float modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,7 +526,7 @@ public interface IScriptChangeMoveDamage
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to modify the outgoing damage done by a move.
|
/// This function allows a script to modify the outgoing damage done by a move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage);
|
void ChangeMoveDamage(IExecutingMove move, IBattlePokemon target, byte hit, ref uint damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -537,7 +537,7 @@ public interface IScriptChangeIncomingMoveDamage
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to modify the incoming damage done by a move.
|
/// This function allows a script to modify the incoming damage done by a move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage);
|
void ChangeIncomingMoveDamage(IExecutingMove move, IBattlePokemon target, byte hit, ref uint damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -549,7 +549,8 @@ public interface IScriptPreventStatBoostChange
|
|||||||
/// This function allows a script attached to a Pokemon or its parents to prevent stat boost
|
/// This function allows a script attached to a Pokemon or its parents to prevent stat boost
|
||||||
/// changes on that Pokemon.
|
/// changes on that Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void PreventStatBoostChange(IPokemon target, Statistic stat, sbyte amount, bool selfInflicted, ref bool prevent);
|
void PreventStatBoostChange(IBattlePokemon target, Statistic stat, sbyte amount, bool selfInflicted,
|
||||||
|
ref bool prevent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -562,7 +563,7 @@ public interface IScriptChangeStatBoostChange
|
|||||||
/// which the stat boost will change. If the stat boost is done by the user itself, self
|
/// which the stat boost will change. If the stat boost is done by the user itself, self
|
||||||
/// inflicted will be true, otherwise it will be false.
|
/// inflicted will be true, otherwise it will be false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeStatBoostChange(IPokemon target, Statistic stat, bool selfInflicted, ref sbyte amount);
|
void ChangeStatBoostChange(IBattlePokemon target, Statistic stat, bool selfInflicted, ref sbyte amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -573,7 +574,7 @@ public interface IScriptOnAfterStatBoostChange
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to run after a stat boost change has been applied.
|
/// This function allows a script to run after a stat boost change has been applied.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnAfterStatBoostChange(IPokemon pokemon, Statistic stat, bool selfInflicted, sbyte change);
|
void OnAfterStatBoostChange(IBattlePokemon pokemon, Statistic stat, bool selfInflicted, sbyte change);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -586,7 +587,7 @@ public interface IScriptPreventSecondaryEffect
|
|||||||
/// This means the move will still hit and do damage, but not trigger its secondary effect. Note that this
|
/// This means the move will still hit and do damage, but not trigger its secondary effect. Note that this
|
||||||
/// function is not called for status moves.
|
/// function is not called for status moves.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void PreventSecondaryEffect(IExecutingMove move, IPokemon target, byte hit, ref bool prevent);
|
void PreventSecondaryEffect(IExecutingMove move, IBattlePokemon target, byte hit, ref bool prevent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -599,7 +600,7 @@ public interface IScriptPreventIncomingSecondaryEffect
|
|||||||
/// secondary effect. This means the move will still hit and do damage, but not trigger its
|
/// secondary effect. This means the move will still hit and do damage, but not trigger its
|
||||||
/// secondary effect. Note that this function is not called for status moves.
|
/// secondary effect. Note that this function is not called for status moves.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void PreventIncomingSecondaryEffect(IExecutingMove move, IPokemon target, byte hit, ref bool prevent);
|
void PreventIncomingSecondaryEffect(IExecutingMove move, IBattlePokemon target, byte hit, ref bool prevent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -613,7 +614,7 @@ public interface IScriptChangeEffectChance
|
|||||||
/// changing this to above or equal to 100 will make it always hit, while setting it to equal or
|
/// changing this to above or equal to 100 will make it always hit, while setting it to equal or
|
||||||
/// below 0 will make it never hit.
|
/// below 0 will make it never hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeEffectChance(IExecutingMove move, IPokemon target, byte hit, ref float chance);
|
void ChangeEffectChance(IExecutingMove move, IBattlePokemon target, byte hit, ref float chance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -627,7 +628,7 @@ public interface IScriptChangeIncomingEffectChance
|
|||||||
/// so changing this to above or equal to 100 will make it always hit, while setting it to equal
|
/// so changing this to above or equal to 100 will make it always hit, while setting it to equal
|
||||||
/// or below 0 will make it never hit.
|
/// or below 0 will make it never hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeIncomingEffectChance(IExecutingMove move, IPokemon target, byte hit, ref float chance);
|
void ChangeIncomingEffectChance(IExecutingMove move, IBattlePokemon target, byte hit, ref float chance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -638,7 +639,7 @@ public interface IScriptOnAfterHits
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function triggers on a move or its parents when all hits on a target are finished.
|
/// This function triggers on a move or its parents when all hits on a target are finished.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnAfterHits(IExecutingMove move, IPokemon target);
|
void OnAfterHits(IExecutingMove move, IBattlePokemon target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -706,7 +707,7 @@ public interface IScriptOnDamage
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function is triggered on a Pokemon and its parents when the given Pokemon takes damage.
|
/// This function is triggered on a Pokemon and its parents when the given Pokemon takes damage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnDamage(IPokemon pokemon, DamageSource source, uint oldHealth, uint newHealth);
|
void OnDamage(IBattlePokemon pokemon, DamageSource source, uint oldHealth, uint newHealth);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -717,7 +718,7 @@ public interface IScriptOnFaint
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function is triggered on a Pokemon and its parents when the given Pokemon faints.
|
/// This function is triggered on a Pokemon and its parents when the given Pokemon faints.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnFaint(IPokemon pokemon, DamageSource source);
|
void OnFaint(IBattlePokemon pokemon, DamageSource source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -728,7 +729,7 @@ public interface IScriptOnAllyFaint
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function is triggered on a Pokemon when an ally Pokemon faints.
|
/// This function is triggered on a Pokemon when an ally Pokemon faints.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnAllyFaint(IPokemon ally, IPokemon faintedPokemon);
|
void OnAllyFaint(IBattlePokemon ally, IBattlePokemon faintedPokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -740,7 +741,7 @@ public interface IScriptOnSwitchOut
|
|||||||
/// This function is triggered on a Pokemon and its parents when the given Pokemon switches out
|
/// This function is triggered on a Pokemon and its parents when the given Pokemon switches out
|
||||||
/// of the battlefield.
|
/// of the battlefield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnSwitchOut(IPokemon oldPokemon, byte position);
|
void OnSwitchOut(IBattlePokemon oldPokemon, byte position);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -752,7 +753,7 @@ public interface IScriptOnSwitchIn
|
|||||||
/// This function is triggered on a Pokemon and its parents when the given Pokemon is switched into
|
/// This function is triggered on a Pokemon and its parents when the given Pokemon is switched into
|
||||||
/// the battlefield.
|
/// the battlefield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnSwitchIn(IPokemon pokemon, byte position);
|
void OnSwitchIn(IBattlePokemon pokemon, byte position);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -763,7 +764,7 @@ public interface IScriptOnOpponentSwitchIn
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function is triggered on a Pokemon and its parents when an opponent switches in.
|
/// This function is triggered on a Pokemon and its parents when an opponent switches in.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnOpponentSwitchIn(IPokemon pokemon, byte position);
|
void OnOpponentSwitchIn(IBattlePokemon pokemon, byte position);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -787,7 +788,7 @@ public interface IScriptOnAfterItemConsume
|
|||||||
/// This function is triggered on a Pokemon and its parents when the given Pokemon consumes the
|
/// This function is triggered on a Pokemon and its parents when the given Pokemon consumes the
|
||||||
/// held item it had.
|
/// held item it had.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnAfterItemConsume(IPokemon pokemon, IItem item);
|
void OnAfterItemConsume(IBattlePokemon pokemon, IItem item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -798,7 +799,7 @@ public interface IScriptBlockIncomingHit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to block an incoming hit.
|
/// This function allows a script to block an incoming hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block);
|
void BlockIncomingHit(IExecutingMove executingMove, IBattlePokemon target, byte hitIndex, ref bool block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -809,7 +810,7 @@ public interface IScriptBlockOutgoingHit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to block an outgoing hit.
|
/// This function allows a script to block an outgoing hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void BlockOutgoingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block);
|
void BlockOutgoingHit(IExecutingMove executingMove, IBattlePokemon target, byte hitIndex, ref bool block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -820,7 +821,7 @@ public interface IScriptPreventHeldItemConsume
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to prevent a held item from being consumed.
|
/// This function allows a script to prevent a held item from being consumed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void PreventHeldItemConsume(IPokemon pokemon, IItem heldItem, ref bool prevented);
|
void PreventHeldItemConsume(IBattlePokemon pokemon, IItem heldItem, ref bool prevented);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -831,7 +832,7 @@ public interface IScriptChangeIncomingDamage
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to change any kind of damage that is incoming.
|
/// This function allows a script to change any kind of damage that is incoming.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeIncomingDamage(IPokemon pokemon, DamageSource source, ref uint damage);
|
void ChangeIncomingDamage(IBattlePokemon pokemon, DamageSource source, ref uint damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -853,7 +854,7 @@ public interface IScriptPreventHeal
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to prevent a Pokemon from being healed.
|
/// This function allows a script to prevent a Pokemon from being healed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void PreventHeal(IPokemon pokemon, uint heal, bool allowRevive, ref bool prevented);
|
void PreventHeal(IBattlePokemon pokemon, uint heal, bool allowRevive, ref bool prevented);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -865,7 +866,8 @@ public interface IScriptChangeTypesForMove
|
|||||||
/// This function allows a script to change the types a target has. Multiple types can be set, and will be used
|
/// This function allows a script to change the types a target has. Multiple types can be set, and will be used
|
||||||
/// for the effectiveness calculation.
|
/// for the effectiveness calculation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeTypesForMove(IExecutingMove executingMove, IPokemon target, byte hitIndex, IList<TypeIdentifier> types);
|
void ChangeTypesForMove(IExecutingMove executingMove, IBattlePokemon target, byte hitIndex,
|
||||||
|
IList<TypeIdentifier> types);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -877,7 +879,7 @@ public interface IScriptChangeTypesForIncomingMove
|
|||||||
/// This function allows a script to change the types a Pokemon has for a move that's incoming. Multiple types can
|
/// This function allows a script to change the types a Pokemon has for a move that's incoming. Multiple types can
|
||||||
/// be set, and will be used for the effectiveness calculation.
|
/// be set, and will be used for the effectiveness calculation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeTypesForIncomingMove(IExecutingMove executingMove, IPokemon target, byte hitIndex,
|
void ChangeTypesForIncomingMove(IExecutingMove executingMove, IBattlePokemon target, byte hitIndex,
|
||||||
IList<TypeIdentifier> types);
|
IList<TypeIdentifier> types);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -890,7 +892,7 @@ public interface IScriptChangeCategory
|
|||||||
/// This function allows a script to change the handling of the move category. This is used for moves that
|
/// This function allows a script to change the handling of the move category. This is used for moves that
|
||||||
/// are sometimes a status move, and sometimes a damaging move, such as pollen puff.
|
/// are sometimes a status move, and sometimes a damaging move, such as pollen puff.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeCategory(IExecutingMove move, IPokemon target, byte hitIndex, ref MoveCategory category);
|
void ChangeCategory(IExecutingMove move, IBattlePokemon target, byte hitIndex, ref MoveCategory category);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -901,7 +903,7 @@ public interface IScriptOnBeforeHit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Triggers first when we're about to hit a target.
|
/// Triggers first when we're about to hit a target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnBeforeHit(IExecutingMove move, IPokemon target, byte hitIndex);
|
void OnBeforeHit(IExecutingMove move, IBattlePokemon target, byte hitIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -912,7 +914,7 @@ public interface IScriptPreventStatusChange
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to prevent a Pokemon from being affected by a status condition.
|
/// This function allows a script to prevent a Pokemon from being affected by a status condition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void PreventStatusChange(IPokemon pokemon, StringKey status, bool selfInflicted, ref bool preventStatus);
|
void PreventStatusChange(IBattlePokemon pokemon, StringKey status, bool selfInflicted, ref bool preventStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -923,7 +925,7 @@ public interface IScriptOnAfterStatusChange
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function triggers after a status condition has been applied to a Pokemon.
|
/// This function triggers after a status condition has been applied to a Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnAfterStatusChange(IPokemon pokemon, StringKey status, IPokemon? originPokemon);
|
void OnAfterStatusChange(IBattlePokemon pokemon, StringKey status, IPokemon? originPokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -946,7 +948,7 @@ public interface IScriptIsFloating
|
|||||||
/// This function allows a script to make the Pokémon it is attached to float. This is used for moves
|
/// This function allows a script to make the Pokémon it is attached to float. This is used for moves
|
||||||
/// such as levitate, and allows for moves such as earthquake to not hit the Pokémon.
|
/// such as levitate, and allows for moves such as earthquake to not hit the Pokémon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void IsFloating(IPokemon pokemon, ref bool isFloating);
|
void IsFloating(IBattlePokemon pokemon, ref bool isFloating);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -991,7 +993,7 @@ public interface IScriptModifyIsContact
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Modifies whether a move is a contact move or not. This is used for abilities such as Long Reach.
|
/// Modifies whether a move is a contact move or not. This is used for abilities such as Long Reach.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ModifyIsContact(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool isContact);
|
void ModifyIsContact(IExecutingMove executingMove, IBattlePokemon target, byte hitIndex, ref bool isContact);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1002,7 +1004,7 @@ public interface IScriptPreventHeldItemSteal
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to prevent a held item from being stolen by an effect such as Thief or Covet.
|
/// This function allows a script to prevent a held item from being stolen by an effect such as Thief or Covet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void PreventHeldItemSteal(IPokemon pokemon, IItem heldItem, ref bool prevent);
|
void PreventHeldItemSteal(IBattlePokemon pokemon, IItem heldItem, ref bool prevent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1013,7 +1015,7 @@ public interface IScriptOnAfterHeldItemChange
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This function allows a script to run after a held item has changed.
|
/// This function allows a script to run after a held item has changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnAfterHeldItemChange(IPokemon pokemon, IItem? previous, IItem? item);
|
void OnAfterHeldItemChange(IBattlePokemon pokemon, IItem? previous, IItem? item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1080,7 +1082,7 @@ public interface IScriptChangeExperienceGained
|
|||||||
/// This function is triggered on a Pokemon and its parents when the given Pokemon gains experience,
|
/// This function is triggered on a Pokemon and its parents when the given Pokemon gains experience,
|
||||||
/// and allows for changing this amount of experience.
|
/// and allows for changing this amount of experience.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeExperienceGained(IPokemon faintedPokemon, IPokemon winningPokemon, ref uint amount);
|
void ChangeExperienceGained(IBattlePokemon faintedPokemon, IBattlePokemon winningPokemon, ref uint amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1093,7 +1095,8 @@ public interface IScriptShareExperience
|
|||||||
/// and allows for making the experience be shared across multiple Pokemon.
|
/// and allows for making the experience be shared across multiple Pokemon.
|
||||||
/// Amount is the modifier for how much experience is shared, with 1 being the default amount.
|
/// Amount is the modifier for how much experience is shared, with 1 being the default amount.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ShareExperience(IPokemon faintedPokemon, IPokemon winningPokemon, ref bool share, ref float amount);
|
void ShareExperience(IBattlePokemon faintedPokemon, IBattlePokemon winningPokemon, ref bool share,
|
||||||
|
ref float amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1117,7 +1120,7 @@ public interface IScriptChangeCatchRateBonus
|
|||||||
/// rate of this attempt. Pokeball modifier effects should be implemented here, as well as for
|
/// rate of this attempt. Pokeball modifier effects should be implemented here, as well as for
|
||||||
/// example status effects that change capture rates.
|
/// example status effects that change capture rates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeCatchRateBonus(IPokemon pokemon, IItem pokeball, ref byte modifier);
|
void ChangeCatchRateBonus(IBattlePokemon pokemon, IItem pokeball, ref byte modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1142,7 +1145,7 @@ public interface IScriptChangeAccuracy
|
|||||||
/// A custom case goes when 255 is returned, in which case the entire accuracy check is skipped, and the move
|
/// A custom case goes when 255 is returned, in which case the entire accuracy check is skipped, and the move
|
||||||
/// will always hit.
|
/// will always hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref int modifiedAccuracy);
|
void ChangeAccuracy(IExecutingMove executingMove, IBattlePokemon target, byte hitIndex, ref int modifiedAccuracy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1155,5 +1158,6 @@ public interface IScriptChangeIncomingAccuracy
|
|||||||
/// A custom case goes when 255 is returned, in which case the entire accuracy check is skipped, and the move
|
/// A custom case goes when 255 is returned, in which case the entire accuracy check is skipped, and the move
|
||||||
/// will always hit.
|
/// will always hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ChangeIncomingAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref int modifiedAccuracy);
|
void ChangeIncomingAccuracy(IExecutingMove executingMove, IBattlePokemon target, byte hitIndex,
|
||||||
|
ref int modifiedAccuracy);
|
||||||
}
|
}
|
||||||
160
PkmnLib.Tests/Dynamic/BattleLifecycleTests.cs
Normal file
160
PkmnLib.Tests/Dynamic/BattleLifecycleTests.cs
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
using PkmnLib.Dynamic.Libraries;
|
||||||
|
using PkmnLib.Dynamic.Models;
|
||||||
|
using PkmnLib.Static;
|
||||||
|
using PkmnLib.Static.Species;
|
||||||
|
using PkmnLib.Tests.Integration;
|
||||||
|
|
||||||
|
namespace PkmnLib.Tests.Dynamic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Regression tests for the battle lifecycle of <see cref="IBattlePokemon"/>: battle-only state must die
|
||||||
|
/// with the battle, and the underlying <see cref="IPokemon"/> must come out of a battle unchanged except
|
||||||
|
/// for the deliberately persistent parts (health, PP, non-volatile status, experience).
|
||||||
|
/// </summary>
|
||||||
|
public class BattleLifecycleTests
|
||||||
|
{
|
||||||
|
private static IPokemon CreatePokemon(IDynamicLibrary library, string speciesName)
|
||||||
|
{
|
||||||
|
if (!library.StaticLibrary.Species.TryGet(speciesName, out var species))
|
||||||
|
throw new InvalidOperationException($"Failed to load {speciesName} species.");
|
||||||
|
return new PokemonImpl(library, species, species.GetDefaultForm(), new AbilityIndex
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
IsHidden = false,
|
||||||
|
}, 50, 0, Gender.Male, 0, "hardy");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IBattle CreateBattle(IDynamicLibrary library, IPokemon own, IPokemon opponent,
|
||||||
|
bool isWildBattle = false, int seed = 0)
|
||||||
|
{
|
||||||
|
var party = new PokemonPartyImpl(1);
|
||||||
|
party.SwapInto(own, 0);
|
||||||
|
var opponentParty = new PokemonPartyImpl(1);
|
||||||
|
opponentParty.SwapInto(opponent, 0);
|
||||||
|
var parties = new[]
|
||||||
|
{
|
||||||
|
new BattlePartyImpl(party, [new ResponsibleIndex(0, 0)]),
|
||||||
|
new BattlePartyImpl(opponentParty, [new ResponsibleIndex(1, 0)]),
|
||||||
|
};
|
||||||
|
var battle = new BattleImpl(library, parties, false, 2, 1, isWildBattle, "grass", seed);
|
||||||
|
battle.Sides[0].SendOut(0, own);
|
||||||
|
battle.Sides[1].SendOut(0, opponent);
|
||||||
|
return battle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The original bug this refactor removes by construction: a captured Pokémon must not carry any
|
||||||
|
/// battle state into the party of its captor. The capture is reported through
|
||||||
|
/// <see cref="BattleResult.CapturedPokemon"/> as the persistent Pokémon.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task Capture_ReportsPersistentPokemonAndLeavesItUsable()
|
||||||
|
{
|
||||||
|
var library = LibraryHelpers.LoadLibrary();
|
||||||
|
if (!library.StaticLibrary.Items.TryGet("master_ball", out var masterBall))
|
||||||
|
throw new InvalidOperationException("Failed to load master ball.");
|
||||||
|
|
||||||
|
// The capture roll is random; find a seed where the capture succeeds. The loop is deterministic,
|
||||||
|
// so the test always exercises the same battle.
|
||||||
|
var result = CaptureResult.Failed;
|
||||||
|
IBattle? battle = null;
|
||||||
|
IPokemon? wildPokemon = null;
|
||||||
|
for (var seed = 0; seed < 100 && !result.IsCaught; seed++)
|
||||||
|
{
|
||||||
|
battle?.Dispose();
|
||||||
|
var own = CreatePokemon(library, "bulbasaur");
|
||||||
|
wildPokemon = CreatePokemon(library, "caterpie");
|
||||||
|
battle = CreateBattle(library, own, wildPokemon, true, seed);
|
||||||
|
var wildBattlePokemon = battle.GetPokemon(1, 0)!;
|
||||||
|
wildBattlePokemon.Damage(wildBattlePokemon.CurrentHealth - 1, DamageSource.MoveDamage);
|
||||||
|
result = battle.AttempCapture(1, 0, masterBall);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Assert.That(result.IsCaught).IsTrue();
|
||||||
|
await Assert.That(battle!.HasEnded).IsTrue();
|
||||||
|
await Assert.That(battle.Result!.Value.CapturedPokemon).Contains(wildPokemon!);
|
||||||
|
|
||||||
|
battle.Dispose();
|
||||||
|
|
||||||
|
// The persistent Pokémon left the battle without any battle state: it is usable and simply damaged.
|
||||||
|
await Assert.That(wildPokemon!.IsUsable).IsTrue();
|
||||||
|
await Assert.That(wildPokemon.CurrentHealth).IsEqualTo(1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Items removed or stolen during a battle are a battle-only overlay: after the battle, the victim
|
||||||
|
/// still holds its item.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task StolenHeldItem_IsRestoredAfterBattle()
|
||||||
|
{
|
||||||
|
var library = LibraryHelpers.LoadLibrary();
|
||||||
|
if (!library.StaticLibrary.Items.TryGet("oran_berry", out var berry))
|
||||||
|
throw new InvalidOperationException("Failed to load oran berry.");
|
||||||
|
var own = CreatePokemon(library, "bulbasaur");
|
||||||
|
_ = own.ForceSetHeldItem(berry);
|
||||||
|
var opponent = CreatePokemon(library, "charmander");
|
||||||
|
|
||||||
|
var battle = CreateBattle(library, own, opponent);
|
||||||
|
var battlePokemon = battle.GetPokemon(0, 0)!;
|
||||||
|
await Assert.That(battlePokemon.TryStealHeldItem(out _)).IsTrue();
|
||||||
|
await Assert.That(battlePokemon.HeldItem).IsNull();
|
||||||
|
|
||||||
|
battle.Dispose();
|
||||||
|
|
||||||
|
await Assert.That(own.HeldItem).IsSameReferenceAs(berry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A battle-only form (such as a mega evolution) reverts to the original form when the battle ends.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task BattleOnlyForm_RevertsWhenBattleEnds()
|
||||||
|
{
|
||||||
|
var library = LibraryHelpers.LoadLibrary();
|
||||||
|
var own = CreatePokemon(library, "absol");
|
||||||
|
var originalForm = own.Form;
|
||||||
|
if (!own.Species.TryGetForm("mega", out var megaForm))
|
||||||
|
throw new InvalidOperationException("Absol has no mega form.");
|
||||||
|
await Assert.That(megaForm.IsBattleOnlyForm).IsTrue();
|
||||||
|
var opponent = CreatePokemon(library, "charmander");
|
||||||
|
|
||||||
|
var battle = CreateBattle(library, own, opponent);
|
||||||
|
var battlePokemon = battle.GetPokemon(0, 0)!;
|
||||||
|
battlePokemon.ChangeForm(megaForm);
|
||||||
|
await Assert.That(own.Form).IsSameReferenceAs(megaForm);
|
||||||
|
|
||||||
|
battle.Dispose();
|
||||||
|
|
||||||
|
await Assert.That(own.Form).IsSameReferenceAs(originalForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A second battle with the same party starts with fresh battle state: no seen opponents or stale
|
||||||
|
/// original species from the previous battle.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task SecondBattleWithSameParty_StartsWithFreshBattleState()
|
||||||
|
{
|
||||||
|
var library = LibraryHelpers.LoadLibrary();
|
||||||
|
var own = CreatePokemon(library, "bulbasaur");
|
||||||
|
var opponent1 = CreatePokemon(library, "charmander");
|
||||||
|
|
||||||
|
var battle1 = CreateBattle(library, own, opponent1);
|
||||||
|
var firstWrapper = battle1.GetPokemon(0, 0)!;
|
||||||
|
await Assert.That(firstWrapper.SeenOpponents.Count).IsEqualTo(1);
|
||||||
|
firstWrapper.ChangeStatBoost(Statistic.Attack, 3, true, false);
|
||||||
|
battle1.Dispose();
|
||||||
|
|
||||||
|
var opponent2 = CreatePokemon(library, "squirtle");
|
||||||
|
var battle2 = CreateBattle(library, own, opponent2);
|
||||||
|
var secondWrapper = battle2.GetPokemon(0, 0)!;
|
||||||
|
|
||||||
|
await Assert.That(secondWrapper).IsNotSameReferenceAs(firstWrapper);
|
||||||
|
await Assert.That(secondWrapper.SeenOpponents.Count).IsEqualTo(1);
|
||||||
|
await Assert.That(secondWrapper.SeenOpponents[0].UnderlyingPokemon).IsSameReferenceAs(opponent2);
|
||||||
|
await Assert.That(secondWrapper.StatBoost.Attack).IsEqualTo((sbyte)0);
|
||||||
|
await Assert.That(secondWrapper.OriginalSpecies).IsSameReferenceAs(own.Species);
|
||||||
|
battle2.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
219
PkmnLib.Tests/Dynamic/BattlePokemonTests.cs
Normal file
219
PkmnLib.Tests/Dynamic/BattlePokemonTests.cs
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
using PkmnLib.Dynamic.Models;
|
||||||
|
using PkmnLib.Static;
|
||||||
|
using PkmnLib.Static.Species;
|
||||||
|
using PkmnLib.Tests.Integration;
|
||||||
|
|
||||||
|
namespace PkmnLib.Tests.Dynamic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests for the ephemeral <see cref="IBattlePokemon"/> wrapper. Battle-only state must live on the wrapper
|
||||||
|
/// and never leak into the underlying <see cref="IPokemon"/>; dropping the wrapper is all the cleanup a
|
||||||
|
/// battle needs.
|
||||||
|
/// </summary>
|
||||||
|
public class BattlePokemonTests
|
||||||
|
{
|
||||||
|
private static (IBattle battle, IBattlePokemon wrapper, IPokemon inner) CreateBattleWithWrapper(
|
||||||
|
string heldItem = "oran_berry")
|
||||||
|
{
|
||||||
|
var library = LibraryHelpers.LoadLibrary();
|
||||||
|
if (!library.StaticLibrary.Species.TryGet("bulbasaur", out var bulbasaur))
|
||||||
|
throw new InvalidOperationException("Failed to load bulbasaur species.");
|
||||||
|
if (!library.StaticLibrary.Species.TryGet("charmander", out var charmander))
|
||||||
|
throw new InvalidOperationException("Failed to load charmander species.");
|
||||||
|
if (!library.StaticLibrary.Items.TryGet(heldItem, out var item))
|
||||||
|
throw new InvalidOperationException($"Failed to load item {heldItem}.");
|
||||||
|
|
||||||
|
var pokemon = new PokemonImpl(library, bulbasaur, bulbasaur.GetDefaultForm(), new AbilityIndex
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
IsHidden = false,
|
||||||
|
}, 50, 0, Gender.Male, 0, "hardy");
|
||||||
|
pokemon.LearnMove("tackle", MoveLearnMethod.LevelUp, 0);
|
||||||
|
_ = pokemon.ForceSetHeldItem(item);
|
||||||
|
|
||||||
|
var opponent = new PokemonImpl(library, charmander, charmander.GetDefaultForm(), new AbilityIndex
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
IsHidden = false,
|
||||||
|
}, 50, 0, Gender.Male, 0, "hardy");
|
||||||
|
|
||||||
|
var party1 = new PokemonPartyImpl(6);
|
||||||
|
party1.SwapInto(pokemon, 0);
|
||||||
|
var party2 = new PokemonPartyImpl(6);
|
||||||
|
party2.SwapInto(opponent, 0);
|
||||||
|
var parties = new[]
|
||||||
|
{
|
||||||
|
new BattlePartyImpl(party1, [new ResponsibleIndex(0, 0)]),
|
||||||
|
new BattlePartyImpl(party2, [new ResponsibleIndex(1, 0)]),
|
||||||
|
};
|
||||||
|
var battle = new BattleImpl(library, parties, false, 2, 1, false, "grass", 0);
|
||||||
|
var wrapper = parties[0].GetBattlePokemon(pokemon) ??
|
||||||
|
throw new InvalidOperationException("Wrapper not created.");
|
||||||
|
return (battle, wrapper, pokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task PersistentMembersAreProxiedToUnderlyingPokemon()
|
||||||
|
{
|
||||||
|
var (_, wrapper, inner) = CreateBattleWithWrapper();
|
||||||
|
|
||||||
|
await Assert.That(wrapper.UnderlyingPokemon).IsSameReferenceAs(inner);
|
||||||
|
await Assert.That(wrapper.Species).IsSameReferenceAs(inner.Species);
|
||||||
|
await Assert.That(wrapper.Form).IsSameReferenceAs(inner.Form);
|
||||||
|
await Assert.That(wrapper.Level).IsEqualTo(inner.Level);
|
||||||
|
await Assert.That(wrapper.CurrentHealth).IsEqualTo(inner.CurrentHealth);
|
||||||
|
await Assert.That(wrapper.Nature).IsSameReferenceAs(inner.Nature);
|
||||||
|
await Assert.That(wrapper.OriginalSpecies).IsSameReferenceAs(inner.Species);
|
||||||
|
await Assert.That(wrapper.OriginalForm).IsSameReferenceAs(inner.Form);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task TypeOverlayDoesNotTouchUnderlyingPokemon()
|
||||||
|
{
|
||||||
|
var (_, wrapper, inner) = CreateBattleWithWrapper();
|
||||||
|
var originalTypes = inner.Types.ToList();
|
||||||
|
|
||||||
|
wrapper.SetTypes([new TypeIdentifier(18, "water")]);
|
||||||
|
|
||||||
|
await Assert.That(wrapper.Types.Count).IsEqualTo(1);
|
||||||
|
await Assert.That(inner.Types.SequenceEqual(originalTypes)).IsTrue();
|
||||||
|
|
||||||
|
wrapper.OnSwitchedOut();
|
||||||
|
await Assert.That(wrapper.Types.SequenceEqual(inner.Form.Types)).IsTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task TemporaryMoveOverlayDoesNotTouchUnderlyingPokemonAndResetsOnSwitchOut()
|
||||||
|
{
|
||||||
|
var (_, wrapper, inner) = CreateBattleWithWrapper();
|
||||||
|
|
||||||
|
wrapper.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
||||||
|
|
||||||
|
await Assert.That(wrapper.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("swords_dance");
|
||||||
|
await Assert.That(wrapper.BaseMoves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
|
||||||
|
await Assert.That(inner.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
|
||||||
|
|
||||||
|
wrapper.OnSwitchedOut();
|
||||||
|
await Assert.That(wrapper.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task StatBoostLivesOnWrapperAndResetsOnSwitchOut()
|
||||||
|
{
|
||||||
|
var (_, wrapper, inner) = CreateBattleWithWrapper();
|
||||||
|
var innerFlatAttack = inner.FlatStats.Attack;
|
||||||
|
|
||||||
|
var changed = wrapper.ChangeStatBoost(Statistic.Attack, 2, true, false);
|
||||||
|
|
||||||
|
await Assert.That(changed).IsTrue();
|
||||||
|
await Assert.That(wrapper.StatBoost.Attack).IsEqualTo((sbyte)2);
|
||||||
|
await Assert.That(wrapper.BoostedStats.Attack > wrapper.FlatStats.Attack).IsTrue();
|
||||||
|
await Assert.That(inner.FlatStats.Attack).IsEqualTo(innerFlatAttack);
|
||||||
|
|
||||||
|
wrapper.OnSwitchedOut();
|
||||||
|
await Assert.That(wrapper.StatBoost.Attack).IsEqualTo((sbyte)0);
|
||||||
|
await Assert.That(wrapper.BoostedStats.Attack).IsEqualTo(wrapper.FlatStats.Attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task HeldItemOverlayDoesNotTouchUnderlyingPokemon()
|
||||||
|
{
|
||||||
|
var (_, wrapper, inner) = CreateBattleWithWrapper();
|
||||||
|
var innerItem = inner.HeldItem;
|
||||||
|
await Assert.That(innerItem).IsNotNull();
|
||||||
|
|
||||||
|
var removed = wrapper.RemoveHeldItemForBattle();
|
||||||
|
|
||||||
|
await Assert.That(removed).IsSameReferenceAs(innerItem);
|
||||||
|
await Assert.That(wrapper.HeldItem).IsNull();
|
||||||
|
await Assert.That(wrapper.HasItemBeenRemovedForBattle).IsTrue();
|
||||||
|
await Assert.That(inner.HeldItem).IsSameReferenceAs(innerItem);
|
||||||
|
|
||||||
|
wrapper.RestoreRemovedHeldItem();
|
||||||
|
await Assert.That(wrapper.HeldItem).IsSameReferenceAs(innerItem);
|
||||||
|
await Assert.That(wrapper.HasItemBeenRemovedForBattle).IsFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task StealingHeldItemDoesNotTouchUnderlyingPokemon()
|
||||||
|
{
|
||||||
|
var (_, wrapper, inner) = CreateBattleWithWrapper();
|
||||||
|
var innerItem = inner.HeldItem;
|
||||||
|
|
||||||
|
var stolen = wrapper.TryStealHeldItem(out var item);
|
||||||
|
|
||||||
|
await Assert.That(stolen).IsTrue();
|
||||||
|
await Assert.That(item).IsSameReferenceAs(innerItem);
|
||||||
|
await Assert.That(wrapper.HeldItem).IsNull();
|
||||||
|
await Assert.That(inner.HeldItem).IsSameReferenceAs(innerItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task MarkAsCaughtOnlyAffectsWrapper()
|
||||||
|
{
|
||||||
|
var (_, wrapper, inner) = CreateBattleWithWrapper();
|
||||||
|
|
||||||
|
wrapper.MarkAsCaught();
|
||||||
|
|
||||||
|
await Assert.That(wrapper.IsCaught).IsTrue();
|
||||||
|
await Assert.That(wrapper.IsUsable).IsFalse();
|
||||||
|
await Assert.That(inner.IsUsable).IsTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task AbilityOverrideAndSuppressionLiveOnWrapper()
|
||||||
|
{
|
||||||
|
var (_, wrapper, inner) = CreateBattleWithWrapper();
|
||||||
|
var naturalAbility = inner.Ability;
|
||||||
|
await Assert.That(naturalAbility).IsNotNull();
|
||||||
|
|
||||||
|
wrapper.SuppressAbility();
|
||||||
|
await Assert.That(wrapper.AbilitySuppressed).IsTrue();
|
||||||
|
await Assert.That(wrapper.ActiveAbility).IsNull();
|
||||||
|
await Assert.That(inner.Ability).IsSameReferenceAs(naturalAbility);
|
||||||
|
|
||||||
|
wrapper.OnSwitchedOut();
|
||||||
|
await Assert.That(wrapper.AbilitySuppressed).IsFalse();
|
||||||
|
await Assert.That(wrapper.ActiveAbility).IsSameReferenceAs(naturalAbility);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task GetOwnScriptsUsesUnderlyingStatusScript()
|
||||||
|
{
|
||||||
|
var (_, wrapper, inner) = CreateBattleWithWrapper();
|
||||||
|
|
||||||
|
var scripts = new List<IEnumerable<PkmnLib.Dynamic.ScriptHandling.ScriptContainer>>();
|
||||||
|
wrapper.GetOwnScripts(scripts);
|
||||||
|
|
||||||
|
await Assert.That(scripts.Count).IsEqualTo(4);
|
||||||
|
await Assert.That(scripts[0]).IsSameReferenceAs(wrapper.HeldItemTriggerScript);
|
||||||
|
await Assert.That(scripts[1]).IsSameReferenceAs(wrapper.AbilityScript);
|
||||||
|
await Assert.That(scripts[2]).IsSameReferenceAs(inner.StatusScript);
|
||||||
|
await Assert.That(scripts[3]).IsSameReferenceAs(wrapper.Volatile);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task GetBattlePokemonFindsWrapperByInnerAndByWrapper()
|
||||||
|
{
|
||||||
|
var (battle, wrapper, inner) = CreateBattleWithWrapper();
|
||||||
|
var party = battle.Parties[0];
|
||||||
|
|
||||||
|
await Assert.That(party.GetBattlePokemon(inner)).IsSameReferenceAs(wrapper);
|
||||||
|
await Assert.That(party.GetBattlePokemon(wrapper)).IsSameReferenceAs(wrapper);
|
||||||
|
await Assert.That(battle.Parties[1].GetBattlePokemon(inner)).IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task OnSwitchedInTracksTurnAndPosition()
|
||||||
|
{
|
||||||
|
var (_, wrapper, _) = CreateBattleWithWrapper();
|
||||||
|
await Assert.That(wrapper.IsOnBattlefield).IsFalse();
|
||||||
|
|
||||||
|
wrapper.OnSwitchedIn(0);
|
||||||
|
|
||||||
|
await Assert.That(wrapper.IsOnBattlefield).IsTrue();
|
||||||
|
await Assert.That(wrapper.Position).IsEqualTo((byte)0);
|
||||||
|
// Bulbasaur's ability (Overgrow) has no script in the Gen7 plugin, so the ability script stays empty.
|
||||||
|
await Assert.That(wrapper.AbilityScript.IsEmpty).IsTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,8 +8,8 @@ public class ChoiceQueueTests
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task ChoiceQueue_HighSpeedFirstWhenPriorityEqual()
|
public async Task ChoiceQueue_HighSpeedFirstWhenPriorityEqual()
|
||||||
{
|
{
|
||||||
var pokemon1 = Substitute.For<IPokemon>();
|
var pokemon1 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon2 = Substitute.For<IPokemon>();
|
var pokemon2 = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
var choice1 = Substitute.For<IMoveChoice>();
|
var choice1 = Substitute.For<IMoveChoice>();
|
||||||
choice1.User.Returns(pokemon1);
|
choice1.User.Returns(pokemon1);
|
||||||
@@ -28,8 +28,8 @@ public class ChoiceQueueTests
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task ChoiceQueue_HighPriorityFirst()
|
public async Task ChoiceQueue_HighPriorityFirst()
|
||||||
{
|
{
|
||||||
var pokemon1 = Substitute.For<IPokemon>();
|
var pokemon1 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon2 = Substitute.For<IPokemon>();
|
var pokemon2 = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
var choice1 = Substitute.For<IMoveChoice>();
|
var choice1 = Substitute.For<IMoveChoice>();
|
||||||
choice1.User.Returns(pokemon1);
|
choice1.User.Returns(pokemon1);
|
||||||
@@ -48,10 +48,10 @@ public class ChoiceQueueTests
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task ChoiceQueue_MovePokemonChoiceNext()
|
public async Task ChoiceQueue_MovePokemonChoiceNext()
|
||||||
{
|
{
|
||||||
var pokemon1 = Substitute.For<IPokemon>();
|
var pokemon1 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon2 = Substitute.For<IPokemon>();
|
var pokemon2 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon3 = Substitute.For<IPokemon>();
|
var pokemon3 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon4 = Substitute.For<IPokemon>();
|
var pokemon4 = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
var choice1 = Substitute.For<IMoveChoice>();
|
var choice1 = Substitute.For<IMoveChoice>();
|
||||||
choice1.User.Returns(pokemon1);
|
choice1.User.Returns(pokemon1);
|
||||||
@@ -75,10 +75,10 @@ public class ChoiceQueueTests
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task ChoiceQueue_MovePokemonChoiceNextFailsIfAlreadyExecuted()
|
public async Task ChoiceQueue_MovePokemonChoiceNextFailsIfAlreadyExecuted()
|
||||||
{
|
{
|
||||||
var pokemon1 = Substitute.For<IPokemon>();
|
var pokemon1 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon2 = Substitute.For<IPokemon>();
|
var pokemon2 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon3 = Substitute.For<IPokemon>();
|
var pokemon3 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon4 = Substitute.For<IPokemon>();
|
var pokemon4 = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
var choice1 = Substitute.For<IMoveChoice>();
|
var choice1 = Substitute.For<IMoveChoice>();
|
||||||
choice1.User.Returns(pokemon1);
|
choice1.User.Returns(pokemon1);
|
||||||
@@ -103,10 +103,10 @@ public class ChoiceQueueTests
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task ChoiceQueue_MovePokemonChoiceLast()
|
public async Task ChoiceQueue_MovePokemonChoiceLast()
|
||||||
{
|
{
|
||||||
var pokemon1 = Substitute.For<IPokemon>();
|
var pokemon1 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon2 = Substitute.For<IPokemon>();
|
var pokemon2 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon3 = Substitute.For<IPokemon>();
|
var pokemon3 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon4 = Substitute.For<IPokemon>();
|
var pokemon4 = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
var choice1 = Substitute.For<IMoveChoice>();
|
var choice1 = Substitute.For<IMoveChoice>();
|
||||||
choice1.User.Returns(pokemon1);
|
choice1.User.Returns(pokemon1);
|
||||||
@@ -133,10 +133,10 @@ public class ChoiceQueueTests
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task ChoiceQueue_MovePokemonChoiceLastFailsIfAlreadyExecuted()
|
public async Task ChoiceQueue_MovePokemonChoiceLastFailsIfAlreadyExecuted()
|
||||||
{
|
{
|
||||||
var pokemon1 = Substitute.For<IPokemon>();
|
var pokemon1 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon2 = Substitute.For<IPokemon>();
|
var pokemon2 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon3 = Substitute.For<IPokemon>();
|
var pokemon3 = Substitute.For<IBattlePokemon>();
|
||||||
var pokemon4 = Substitute.For<IPokemon>();
|
var pokemon4 = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
var choice1 = Substitute.For<IMoveChoice>();
|
var choice1 = Substitute.For<IMoveChoice>();
|
||||||
choice1.User.Returns(pokemon1);
|
choice1.User.Returns(pokemon1);
|
||||||
|
|||||||
@@ -7,16 +7,31 @@ namespace PkmnLib.Tests.Dynamic;
|
|||||||
|
|
||||||
public class PokemonStatBoostTests
|
public class PokemonStatBoostTests
|
||||||
{
|
{
|
||||||
private static IPokemon CreatePokemon()
|
private static IBattlePokemon CreatePokemon()
|
||||||
{
|
{
|
||||||
var library = LibraryHelpers.LoadLibrary();
|
var library = LibraryHelpers.LoadLibrary();
|
||||||
if (!library.StaticLibrary.Species.TryGet("bulbasaur", out var species))
|
if (!library.StaticLibrary.Species.TryGet("bulbasaur", out var species))
|
||||||
throw new InvalidOperationException("Failed to load bulbasaur species.");
|
throw new InvalidOperationException("Failed to load bulbasaur species.");
|
||||||
return new PokemonImpl(library, species, species.GetDefaultForm(), new AbilityIndex
|
var pokemon = new PokemonImpl(library, species, species.GetDefaultForm(), new AbilityIndex
|
||||||
{
|
{
|
||||||
Index = 0,
|
Index = 0,
|
||||||
IsHidden = false,
|
IsHidden = false,
|
||||||
}, 50, 0, Gender.Male, 0, "hardy");
|
}, 50, 0, Gender.Male, 0, "hardy");
|
||||||
|
var party = new PokemonPartyImpl(1);
|
||||||
|
party.SwapInto(pokemon, 0);
|
||||||
|
var opponentParty = new PokemonPartyImpl(1);
|
||||||
|
opponentParty.SwapInto(new PokemonImpl(library, species, species.GetDefaultForm(), new AbilityIndex
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
IsHidden = false,
|
||||||
|
}, 50, 0, Gender.Male, 0, "hardy"), 0);
|
||||||
|
var parties = new[]
|
||||||
|
{
|
||||||
|
new BattlePartyImpl(party, [new ResponsibleIndex(0, 0)]),
|
||||||
|
new BattlePartyImpl(opponentParty, [new ResponsibleIndex(1, 0)]),
|
||||||
|
};
|
||||||
|
var battle = new BattleImpl(library, parties, false, 2, 1, false, "grass", 0);
|
||||||
|
return battle.Parties[0].GetBattlePokemon(pokemon)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -6,65 +6,78 @@ using PkmnLib.Tests.Integration;
|
|||||||
namespace PkmnLib.Tests.Dynamic;
|
namespace PkmnLib.Tests.Dynamic;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests for the temporary move overlay (<see cref="IPokemon.LearnTemporaryMove"/>), used by effects such as
|
/// Tests for the temporary move overlay (<see cref="IBattlePokemon.LearnTemporaryMove"/>), used by effects
|
||||||
/// Mimic. The permanently learned moves must never be mutated by a temporary move, and the overlay must be
|
/// such as Mimic. The permanently learned moves must never be mutated by a temporary move, and the overlay
|
||||||
/// discarded by the engine itself when the Pokemon leaves the battlefield.
|
/// must be discarded by the engine itself when the Pokemon leaves the battlefield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PokemonTemporaryMoveTests
|
public class PokemonTemporaryMoveTests
|
||||||
{
|
{
|
||||||
private static IPokemon CreatePokemon()
|
private static (IBattle battle, IBattlePokemon battlePokemon, IPokemon pokemon) CreateBattlePokemon()
|
||||||
{
|
{
|
||||||
var library = LibraryHelpers.LoadLibrary();
|
var library = LibraryHelpers.LoadLibrary();
|
||||||
if (!library.StaticLibrary.Species.TryGet("bulbasaur", out var species))
|
if (!library.StaticLibrary.Species.TryGet("bulbasaur", out var species))
|
||||||
throw new InvalidOperationException("Failed to load bulbasaur species.");
|
throw new InvalidOperationException("Failed to load bulbasaur species.");
|
||||||
var pokemon = new PokemonImpl(library, species, species.GetDefaultForm(), new AbilityIndex
|
|
||||||
{
|
IPokemon CreateBulbasaur() =>
|
||||||
Index = 0,
|
new PokemonImpl(library, species, species.GetDefaultForm(), new AbilityIndex
|
||||||
IsHidden = false,
|
{
|
||||||
}, 50, 0, Gender.Male, 0, "hardy");
|
Index = 0,
|
||||||
|
IsHidden = false,
|
||||||
|
}, 50, 0, Gender.Male, 0, "hardy");
|
||||||
|
|
||||||
|
var pokemon = CreateBulbasaur();
|
||||||
pokemon.LearnMove("tackle", MoveLearnMethod.LevelUp, 0);
|
pokemon.LearnMove("tackle", MoveLearnMethod.LevelUp, 0);
|
||||||
return pokemon;
|
var party = new PokemonPartyImpl(1);
|
||||||
|
party.SwapInto(pokemon, 0);
|
||||||
|
var opponentParty = new PokemonPartyImpl(1);
|
||||||
|
opponentParty.SwapInto(CreateBulbasaur(), 0);
|
||||||
|
var parties = new[]
|
||||||
|
{
|
||||||
|
new BattlePartyImpl(party, [new ResponsibleIndex(0, 0)]),
|
||||||
|
new BattlePartyImpl(opponentParty, [new ResponsibleIndex(1, 0)]),
|
||||||
|
};
|
||||||
|
var battle = new BattleImpl(library, parties, false, 2, 1, false, "grass", 0);
|
||||||
|
return (battle, battle.Parties[0].GetBattlePokemon(pokemon)!, pokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task LearnTemporaryMove_ReplacesMoveInMovesButNotInBaseMoves()
|
public async Task LearnTemporaryMove_ReplacesMoveInMovesButNotInBaseMoves()
|
||||||
{
|
{
|
||||||
var pokemon = CreatePokemon();
|
var (_, battlePokemon, pokemon) = CreateBattlePokemon();
|
||||||
|
|
||||||
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
battlePokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
||||||
|
|
||||||
await Assert.That(pokemon.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("swords_dance");
|
await Assert.That(battlePokemon.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("swords_dance");
|
||||||
await Assert.That(pokemon.Moves[0]!.LearnMethod).IsEqualTo(MoveLearnMethod.Mimic);
|
await Assert.That(battlePokemon.Moves[0]!.LearnMethod).IsEqualTo(MoveLearnMethod.Mimic);
|
||||||
await Assert.That(pokemon.BaseMoves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
|
await Assert.That(battlePokemon.BaseMoves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
|
||||||
|
await Assert.That(pokemon.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task SetOnBattlefield_LeavingField_RestoresOriginalMoveWithItsPP()
|
public async Task OnSwitchedOut_RestoresOriginalMoveWithItsPP()
|
||||||
{
|
{
|
||||||
var pokemon = CreatePokemon();
|
var (_, battlePokemon, _) = CreateBattlePokemon();
|
||||||
pokemon.SetBattleData(Substitute.For<IBattle>(), 0);
|
battlePokemon.OnSwitchedIn(0);
|
||||||
pokemon.SetOnBattlefield(true);
|
|
||||||
|
|
||||||
// Use the original move once, so we can verify its PP survives the temporary replacement untouched.
|
// Use the original move once, so we can verify its PP survives the temporary replacement untouched.
|
||||||
var originalMove = pokemon.Moves[0]!;
|
var originalMove = battlePokemon.Moves[0]!;
|
||||||
originalMove.TryUse();
|
originalMove.TryUse();
|
||||||
var expectedPp = originalMove.CurrentPp;
|
var expectedPp = originalMove.CurrentPp;
|
||||||
|
|
||||||
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
battlePokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
||||||
pokemon.SetOnBattlefield(false);
|
battlePokemon.OnSwitchedOut();
|
||||||
|
|
||||||
await Assert.That(ReferenceEquals(pokemon.Moves[0], originalMove)).IsTrue();
|
await Assert.That(ReferenceEquals(battlePokemon.Moves[0], originalMove)).IsTrue();
|
||||||
await Assert.That(pokemon.Moves[0]!.CurrentPp).IsEqualTo(expectedPp);
|
await Assert.That(battlePokemon.Moves[0]!.CurrentPp).IsEqualTo(expectedPp);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task ClearBattleData_RestoresOriginalMove()
|
public async Task BattleEnd_LeavesOriginalMovesUntouched()
|
||||||
{
|
{
|
||||||
var pokemon = CreatePokemon();
|
var (battle, battlePokemon, pokemon) = CreateBattlePokemon();
|
||||||
pokemon.SetBattleData(Substitute.For<IBattle>(), 0);
|
battlePokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
||||||
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
|
||||||
|
|
||||||
pokemon.ClearBattleData();
|
battle.Dispose();
|
||||||
|
|
||||||
await Assert.That(pokemon.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
|
await Assert.That(pokemon.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
|
||||||
}
|
}
|
||||||
@@ -72,10 +85,10 @@ public class PokemonTemporaryMoveTests
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task Serialize_WithActiveTemporaryMove_WritesOriginalMove()
|
public async Task Serialize_WithActiveTemporaryMove_WritesOriginalMove()
|
||||||
{
|
{
|
||||||
var pokemon = CreatePokemon();
|
var (_, battlePokemon, _) = CreateBattlePokemon();
|
||||||
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
battlePokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
||||||
|
|
||||||
var serialized = pokemon.Serialize();
|
var serialized = battlePokemon.Serialize();
|
||||||
|
|
||||||
await Assert.That(serialized.Moves[0]!.MoveName.ToString()).IsEqualTo("tackle");
|
await Assert.That(serialized.Moves[0]!.MoveName.ToString()).IsEqualTo("tackle");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class SetPokemonAction : IntegrationTestAction
|
|||||||
|
|
||||||
public override Task Execute(IBattle battle)
|
public override Task Execute(IBattle battle)
|
||||||
{
|
{
|
||||||
var mon = battle.Parties[FromParty[0]].Party[FromParty[1]];
|
var mon = battle.Parties[FromParty[0]].BattlePokemon[FromParty[1]];
|
||||||
battle.Sides[Place[0]].SwapPokemon(Place[1], mon);
|
battle.Sides[Place[0]].SwapPokemon(Place[1], mon);
|
||||||
Console.WriteLine($"Set: {mon} to place {Place[0]}:{Place[1]}");
|
Console.WriteLine($"Set: {mon} to place {Place[0]}:{Place[1]}");
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|||||||
@@ -110,11 +110,12 @@ public class DeepCloneTests
|
|||||||
new BattlePartyImpl(party2, [new ResponsibleIndex(1, 0)]),
|
new BattlePartyImpl(party2, [new ResponsibleIndex(1, 0)]),
|
||||||
};
|
};
|
||||||
using var battle = new BattleImpl(library, parties, false, 2, 3, false, "grass", 0);
|
using var battle = new BattleImpl(library, parties, false, 2, 3, false, "grass", 0);
|
||||||
battle.Sides[0].SwapPokemon(0, party1[0]);
|
battle.Sides[0].SendOut(0, party1[0]!);
|
||||||
battle.Sides[1].SwapPokemon(0, party2[0]);
|
battle.Sides[1].SendOut(0, party2[0]!);
|
||||||
party1[0]!.ChangeStatBoost(Statistic.Defense, 2, true, false);
|
var battlePokemon1 = parties[0].GetBattlePokemon(party1[0]!)!;
|
||||||
await Assert.That(party1[0]!.StatBoost.Defense).IsEqualTo((sbyte)2);
|
battlePokemon1.ChangeStatBoost(Statistic.Defense, 2, true, false);
|
||||||
party1[0]!.Volatile.Add(new ChargeBounceEffect(party1[0]!));
|
await Assert.That(battlePokemon1.StatBoost.Defense).IsEqualTo((sbyte)2);
|
||||||
|
battlePokemon1.Volatile.Add(new ChargeBounceEffect(battlePokemon1));
|
||||||
|
|
||||||
var clone = battle.DeepClone();
|
var clone = battle.DeepClone();
|
||||||
await Assert.That(clone).IsNotEqualTo(battle);
|
await Assert.That(clone).IsNotEqualTo(battle);
|
||||||
@@ -129,11 +130,10 @@ public class DeepCloneTests
|
|||||||
var pokemon = clone.Sides[0].Pokemon[0]!;
|
var pokemon = clone.Sides[0].Pokemon[0]!;
|
||||||
await Assert.That(pokemon).IsNotNull();
|
await Assert.That(pokemon).IsNotNull();
|
||||||
await Assert.That(pokemon).IsNotEqualTo(battle.Sides[0].Pokemon[0]!);
|
await Assert.That(pokemon).IsNotEqualTo(battle.Sides[0].Pokemon[0]!);
|
||||||
await Assert.That(pokemon.BattleData).IsNotNull();
|
await Assert.That(pokemon.UnderlyingPokemon).IsNotEqualTo(battle.Sides[0].Pokemon[0]!.UnderlyingPokemon);
|
||||||
await Assert.That(pokemon.BattleData).IsNotEqualTo(battle.Sides[0].Pokemon[0]!.BattleData!);
|
await Assert.That(pokemon.Battle).IsEqualTo((IBattle)clone);
|
||||||
await Assert.That(pokemon.BattleData!.Battle).IsEqualTo((IBattle)clone);
|
await Assert.That(pokemon.SeenOpponents).Contains(clone.Sides[1].Pokemon[0]!);
|
||||||
await Assert.That(pokemon.BattleData!.SeenOpponents).Contains(clone.Sides[1].Pokemon[0]!);
|
await Assert.That(pokemon.SeenOpponents).DoesNotContain(battle.Sides[1].Pokemon[0]!);
|
||||||
await Assert.That(pokemon.BattleData!.SeenOpponents).DoesNotContain(battle.Sides[1].Pokemon[0]!);
|
|
||||||
await Assert.That(pokemon.StatBoost.Defense).IsEqualTo((sbyte)2);
|
await Assert.That(pokemon.StatBoost.Defense).IsEqualTo((sbyte)2);
|
||||||
await Assert.That(pokemon.Volatile.Get<ChargeBounceEffect>()).IsNotNull();
|
await Assert.That(pokemon.Volatile.Get<ChargeBounceEffect>()).IsNotNull();
|
||||||
await Assert.That(pokemon.Volatile.Get<ChargeBounceEffect>()).IsNotEqualTo(
|
await Assert.That(pokemon.Volatile.Get<ChargeBounceEffect>()).IsNotEqualTo(
|
||||||
@@ -142,7 +142,7 @@ public class DeepCloneTests
|
|||||||
var ownerGetter =
|
var ownerGetter =
|
||||||
typeof(ChargeBounceEffect).GetField("_owner", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
typeof(ChargeBounceEffect).GetField("_owner", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||||
var owner = ownerGetter.GetValue(pokemon.Volatile.Get<ChargeBounceEffect>()!);
|
var owner = ownerGetter.GetValue(pokemon.Volatile.Get<ChargeBounceEffect>()!);
|
||||||
await Assert.That((IPokemon)owner!).IsEqualTo(pokemon);
|
await Assert.That((IBattlePokemon)owner!).IsEqualTo(pokemon);
|
||||||
pokemon.Volatile.Remove<ChargeBounceEffect>();
|
pokemon.Volatile.Remove<ChargeBounceEffect>();
|
||||||
|
|
||||||
await Assert.That(pokemon.Volatile.Get<ChargeBounceEffect>()).IsNull();
|
await Assert.That(pokemon.Volatile.Get<ChargeBounceEffect>()).IsNull();
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class DamageCalculatorTests
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task BulbapediaExampleDamageTest()
|
public async Task BulbapediaExampleDamageTest()
|
||||||
{
|
{
|
||||||
var attacker = Substitute.For<IPokemon>();
|
var attacker = Substitute.For<IBattlePokemon>();
|
||||||
// Imagine a level 75 Glaceon
|
// Imagine a level 75 Glaceon
|
||||||
attacker.Level.Returns((byte)75);
|
attacker.Level.Returns((byte)75);
|
||||||
// with an effective Attack stat of 123
|
// with an effective Attack stat of 123
|
||||||
@@ -31,7 +31,7 @@ public class DamageCalculatorTests
|
|||||||
// We use 10 as the Ice type
|
// We use 10 as the Ice type
|
||||||
attacker.Types.Returns([new TypeIdentifier(10, "ice")]);
|
attacker.Types.Returns([new TypeIdentifier(10, "ice")]);
|
||||||
|
|
||||||
var defender = Substitute.For<IPokemon>();
|
var defender = Substitute.For<IBattlePokemon>();
|
||||||
// a Garchomp with an effective Defense stat of 163
|
// a Garchomp with an effective Defense stat of 163
|
||||||
defender.BoostedStats.Returns(new StatisticSet<uint>(1, 1, 163, 1, 1, 1));
|
defender.BoostedStats.Returns(new StatisticSet<uint>(1, 1, 163, 1, 1, 1));
|
||||||
defender.GetScripts().Returns(new ScriptIterator([]));
|
defender.GetScripts().Returns(new ScriptIterator([]));
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ public class AftermathTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a fully mocked test setup for Aftermath tests.
|
/// Creates a fully mocked test setup for Aftermath tests.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (Aftermath aftermath, IExecutingMove move, IPokemon target, IPokemon user, EventHook eventHook,
|
private static (Aftermath aftermath, IExecutingMove move, IBattlePokemon target, IBattlePokemon user, EventHook
|
||||||
IBattle battle) CreateFullTestSetup(bool isContact, uint userMaxHealth = 100)
|
eventHook, IBattle battle) CreateFullTestSetup(bool isContact, uint userMaxHealth = 100)
|
||||||
{
|
{
|
||||||
var aftermath = new Aftermath();
|
var aftermath = new Aftermath();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
hitData.IsContact.Returns(isContact);
|
hitData.IsContact.Returns(isContact);
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
@@ -30,20 +30,14 @@ public class AftermathTests
|
|||||||
|
|
||||||
// Setup empty sides by default (no Damp on field)
|
// Setup empty sides by default (no Damp on field)
|
||||||
var side = Substitute.For<IBattleSide>();
|
var side = Substitute.For<IBattleSide>();
|
||||||
side.Pokemon.Returns(new List<IPokemon?>());
|
side.Pokemon.Returns(new List<IBattlePokemon?>());
|
||||||
battle.Sides.Returns(new[] { side });
|
battle.Sides.Returns(new[] { side });
|
||||||
|
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.IsUsable.Returns(true);
|
user.IsUsable.Returns(true);
|
||||||
|
user.Battle.Returns(battle);
|
||||||
user.MaxHealth.Returns(userMaxHealth);
|
user.MaxHealth.Returns(userMaxHealth);
|
||||||
|
|
||||||
// Configure BattleData return value using NSubstitute's callback pattern
|
|
||||||
// First return null to suppress auto-substitution, then configure actual value
|
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
|
||||||
return (aftermath, move, target, user, eventHook, battle);
|
return (aftermath, move, target, user, eventHook, battle);
|
||||||
@@ -52,7 +46,7 @@ public class AftermathTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the damage amount from a substitute's received Damage calls.
|
/// Helper to extract the damage amount from a substitute's received Damage calls.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static uint GetDamageDealt(IPokemon user)
|
private static uint GetDamageDealt(IBattlePokemon user)
|
||||||
{
|
{
|
||||||
var damageCall = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
var damageCall = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
||||||
return damageCall != null ? (uint)damageCall.GetArguments()[0]! : 0;
|
return damageCall != null ? (uint)damageCall.GetArguments()[0]! : 0;
|
||||||
@@ -61,7 +55,7 @@ public class AftermathTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the damage source from a substitute's received Damage calls.
|
/// Helper to extract the damage source from a substitute's received Damage calls.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static DamageSource? GetDamageSource(IPokemon user)
|
private static DamageSource? GetDamageSource(IBattlePokemon user)
|
||||||
{
|
{
|
||||||
var damageCall = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
var damageCall = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
||||||
return damageCall != null ? (DamageSource)damageCall.GetArguments()[1]! : null;
|
return damageCall != null ? (DamageSource)damageCall.GetArguments()[1]! : null;
|
||||||
@@ -151,12 +145,12 @@ public class AftermathTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var aftermath = new Aftermath();
|
var aftermath = new Aftermath();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
hitData.IsContact.Returns(true);
|
hitData.IsContact.Returns(true);
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.IsUsable.Returns(false); // Attacker already fainted
|
user.IsUsable.Returns(false); // Attacker already fainted
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
|
||||||
@@ -168,34 +162,6 @@ public class AftermathTests
|
|||||||
await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Damage")).IsFalse();
|
await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Damage")).IsFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Bulbapedia: "the attacking Pokémon takes damage".
|
|
||||||
/// If the attacker has no battle data, no damage should be dealt.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task OnFaint_AttackerHasNoBattleData_DoesNotDealDamage()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var aftermath = new Aftermath();
|
|
||||||
var move = Substitute.For<IExecutingMove>();
|
|
||||||
var target = Substitute.For<IPokemon>();
|
|
||||||
var hitData = Substitute.For<IHitData>();
|
|
||||||
hitData.IsContact.Returns(true);
|
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.IsUsable.Returns(true);
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
move.User.Returns(user);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
aftermath.OnIncomingHit(move, target, 0);
|
|
||||||
aftermath.OnFaint(target, DamageSource.MoveDamage);
|
|
||||||
|
|
||||||
// Assert - Damage should never be called because BattleData is null
|
|
||||||
await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Damage")).IsFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Technical test: Verifies OnFaint handles the case where no attack was received.
|
/// Technical test: Verifies OnFaint handles the case where no attack was received.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -204,7 +170,7 @@ public class AftermathTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var aftermath = new Aftermath();
|
var aftermath = new Aftermath();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
// Act & Assert - Should not throw when _lastAttack is null
|
// Act & Assert - Should not throw when _lastAttack is null
|
||||||
aftermath.OnFaint(target, DamageSource.MoveDamage);
|
aftermath.OnFaint(target, DamageSource.MoveDamage);
|
||||||
@@ -220,19 +186,16 @@ public class AftermathTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var aftermath = new Aftermath();
|
var aftermath = new Aftermath();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
var eventHook = new EventHook();
|
var eventHook = new EventHook();
|
||||||
battle.EventHook.Returns(eventHook);
|
battle.EventHook.Returns(eventHook);
|
||||||
|
|
||||||
// Setup empty sides (no Damp on field)
|
// Setup empty sides (no Damp on field)
|
||||||
var side = Substitute.For<IBattleSide>();
|
var side = Substitute.For<IBattleSide>();
|
||||||
side.Pokemon.Returns(new List<IPokemon?>());
|
side.Pokemon.Returns(new List<IBattlePokemon?>());
|
||||||
battle.Sides.Returns(new[] { side });
|
battle.Sides.Returns(new[] { side });
|
||||||
|
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
|
|
||||||
// First hit - non-contact
|
// First hit - non-contact
|
||||||
var move1 = Substitute.For<IExecutingMove>();
|
var move1 = Substitute.For<IExecutingMove>();
|
||||||
var hitData1 = Substitute.For<IHitData>();
|
var hitData1 = Substitute.For<IHitData>();
|
||||||
@@ -244,10 +207,10 @@ public class AftermathTests
|
|||||||
var hitData2 = Substitute.For<IHitData>();
|
var hitData2 = Substitute.For<IHitData>();
|
||||||
hitData2.IsContact.Returns(true);
|
hitData2.IsContact.Returns(true);
|
||||||
move2.GetHitData(target, 0).Returns(hitData2);
|
move2.GetHitData(target, 0).Returns(hitData2);
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.IsUsable.Returns(true);
|
user.IsUsable.Returns(true);
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
user.MaxHealth.Returns(100u);
|
user.MaxHealth.Returns(100u);
|
||||||
|
user.Battle.Returns(battle);
|
||||||
move2.User.Returns(user);
|
move2.User.Returns(user);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -268,7 +231,7 @@ public class AftermathTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var aftermath = new Aftermath();
|
var aftermath = new Aftermath();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
// First hit - contact
|
// First hit - contact
|
||||||
var move1 = Substitute.For<IExecutingMove>();
|
var move1 = Substitute.For<IExecutingMove>();
|
||||||
@@ -346,14 +309,14 @@ public class AftermathTests
|
|||||||
var (aftermath, move, target, user, _, battle) = CreateFullTestSetup(true, 100);
|
var (aftermath, move, target, user, _, battle) = CreateFullTestSetup(true, 100);
|
||||||
|
|
||||||
// Create a Pokemon with Damp ability on the field
|
// Create a Pokemon with Damp ability on the field
|
||||||
var dampPokemon = Substitute.For<IPokemon>();
|
var dampPokemon = Substitute.For<IBattlePokemon>();
|
||||||
var dampAbility = Substitute.For<IAbility>();
|
var dampAbility = Substitute.For<IAbility>();
|
||||||
dampAbility.Name.Returns(new StringKey("damp"));
|
dampAbility.Name.Returns(new StringKey("damp"));
|
||||||
dampPokemon.ActiveAbility.Returns(dampAbility);
|
dampPokemon.ActiveAbility.Returns(dampAbility);
|
||||||
|
|
||||||
// Update battle sides to include the Damp Pokemon
|
// Update battle sides to include the Damp Pokemon
|
||||||
var side = Substitute.For<IBattleSide>();
|
var side = Substitute.For<IBattleSide>();
|
||||||
side.Pokemon.Returns(new List<IPokemon?> { dampPokemon });
|
side.Pokemon.Returns(new List<IBattlePokemon?> { dampPokemon });
|
||||||
battle.Sides.Returns(new[] { side });
|
battle.Sides.Returns(new[] { side });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ public class MegaLauncherTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var healPercent = 0.5f;
|
var healPercent = 0.5f;
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
move.UseMove.Category.Returns(MoveCategory.Special);
|
move.UseMove.Category.Returns(MoveCategory.Special);
|
||||||
move.UseMove.HasFlag(MoveFlags.Pulse).Returns(true);
|
move.UseMove.HasFlag(MoveFlags.Pulse).Returns(true);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class FireGrassPledgeMoveTests
|
|||||||
/// Creates a fully mocked test setup. The target's battle side gets a real <see cref="ScriptSet"/> as
|
/// Creates a fully mocked test setup. The target's battle side gets a real <see cref="ScriptSet"/> as
|
||||||
/// its volatile script set, so the sea of fire created by the combined move can be inspected and driven.
|
/// its volatile script set, so the sea of fire created by the combined move can be inspected and driven.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (FireGrassPledgeMove script, IExecutingMove move, IPokemon target, IBattleSide side, IScriptSet
|
private static (FireGrassPledgeMove script, IExecutingMove move, IBattlePokemon target, IBattleSide side, IScriptSet
|
||||||
sideVolatile) CreateTestSetup()
|
sideVolatile) CreateTestSetup()
|
||||||
{
|
{
|
||||||
var script = new FireGrassPledgeMove();
|
var script = new FireGrassPledgeMove();
|
||||||
@@ -33,11 +33,9 @@ public class FireGrassPledgeMoveTests
|
|||||||
var sideVolatile = new ScriptSet(side);
|
var sideVolatile = new ScriptSet(side);
|
||||||
side.VolatileScripts.Returns(sideVolatile);
|
side.VolatileScripts.Returns(sideVolatile);
|
||||||
|
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
battleData.BattleSide.Returns(side);
|
|
||||||
var target = Substitute.For<IPokemon>();
|
|
||||||
target.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
|
target.BattleSide.Returns(side);
|
||||||
return (script, move, target, side, sideVolatile);
|
return (script, move, target, side, sideVolatile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,9 +43,9 @@ public class FireGrassPledgeMoveTests
|
|||||||
/// Creates a Pokémon substitute of the given type with the given maximum HP, for placing on the side
|
/// Creates a Pokémon substitute of the given type with the given maximum HP, for placing on the side
|
||||||
/// covered by the sea of fire.
|
/// covered by the sea of fire.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static IPokemon CreateSidePokemon(string typeName, uint maxHealth)
|
private static IBattlePokemon CreateSidePokemon(string typeName, uint maxHealth)
|
||||||
{
|
{
|
||||||
var pokemon = Substitute.For<IPokemon>();
|
var pokemon = Substitute.For<IBattlePokemon>();
|
||||||
pokemon.Types.Returns(new List<TypeIdentifier> { new(1, typeName) });
|
pokemon.Types.Returns(new List<TypeIdentifier> { new(1, typeName) });
|
||||||
pokemon.MaxHealth.Returns(maxHealth);
|
pokemon.MaxHealth.Returns(maxHealth);
|
||||||
return pokemon;
|
return pokemon;
|
||||||
@@ -56,7 +54,7 @@ public class FireGrassPledgeMoveTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to count the Damage calls a Pokémon received.
|
/// Helper to count the Damage calls a Pokémon received.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static int CountDamageCalls(IPokemon pokemon) =>
|
private static int CountDamageCalls(IBattlePokemon pokemon) =>
|
||||||
pokemon.ReceivedCalls().Count(c => c.GetMethodInfo().Name == "Damage");
|
pokemon.ReceivedCalls().Count(c => c.GetMethodInfo().Name == "Damage");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -112,24 +110,6 @@ public class FireGrassPledgeMoveTests
|
|||||||
await Assert.That(sideVolatile.Get<SeaOfFireEffect>()).IsNotNull();
|
await Assert.That(sideVolatile.Get<SeaOfFireEffect>()).IsNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: if the target has no <see cref="IPokemon.BattleData"/>, no sea of fire is created
|
|
||||||
/// and the script does not throw.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task OnSecondaryEffect_TargetHasNoBattleData_DoesNothing()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var (script, move, target, _, sideVolatile) = CreateTestSetup();
|
|
||||||
target.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
await Assert.That(sideVolatile.Count).IsEqualTo(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bulbapedia: "The sea of fire damages all non-Fire Pokémon on that side of the field for 1/8 of
|
/// Bulbapedia: "The sea of fire damages all non-Fire Pokémon on that side of the field for 1/8 of
|
||||||
/// their maximum HP at the end of each turn."
|
/// their maximum HP at the end of each turn."
|
||||||
@@ -141,7 +121,7 @@ public class FireGrassPledgeMoveTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, target, side, sideVolatile) = CreateTestSetup();
|
var (script, move, target, side, sideVolatile) = CreateTestSetup();
|
||||||
var waterPokemon = CreateSidePokemon("water", maxHealth);
|
var waterPokemon = CreateSidePokemon("water", maxHealth);
|
||||||
side.Pokemon.Returns(new List<IPokemon?> { waterPokemon });
|
side.Pokemon.Returns(new List<IBattlePokemon?> { waterPokemon });
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
var seaOfFire = sideVolatile.Get<SeaOfFireEffect>()!;
|
var seaOfFire = sideVolatile.Get<SeaOfFireEffect>()!;
|
||||||
|
|
||||||
@@ -164,7 +144,7 @@ public class FireGrassPledgeMoveTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, target, side, sideVolatile) = CreateTestSetup();
|
var (script, move, target, side, sideVolatile) = CreateTestSetup();
|
||||||
var firePokemon = CreateSidePokemon("fire", 100);
|
var firePokemon = CreateSidePokemon("fire", 100);
|
||||||
side.Pokemon.Returns(new List<IPokemon?> { firePokemon });
|
side.Pokemon.Returns(new List<IBattlePokemon?> { firePokemon });
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
var seaOfFire = sideVolatile.Get<SeaOfFireEffect>()!;
|
var seaOfFire = sideVolatile.Get<SeaOfFireEffect>()!;
|
||||||
|
|
||||||
@@ -185,7 +165,7 @@ public class FireGrassPledgeMoveTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, target, side, sideVolatile) = CreateTestSetup();
|
var (script, move, target, side, sideVolatile) = CreateTestSetup();
|
||||||
var waterPokemon = CreateSidePokemon("water", 96);
|
var waterPokemon = CreateSidePokemon("water", 96);
|
||||||
side.Pokemon.Returns(new List<IPokemon?> { waterPokemon });
|
side.Pokemon.Returns(new List<IBattlePokemon?> { waterPokemon });
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
var seaOfFire = sideVolatile.Get<SeaOfFireEffect>()!;
|
var seaOfFire = sideVolatile.Get<SeaOfFireEffect>()!;
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
@@ -207,7 +187,7 @@ public class FireGrassPledgeMoveTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, target, side, sideVolatile) = CreateTestSetup();
|
var (script, move, target, side, sideVolatile) = CreateTestSetup();
|
||||||
side.Pokemon.Returns(new List<IPokemon?>());
|
side.Pokemon.Returns(new List<IBattlePokemon?>());
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
var seaOfFire = sideVolatile.Get<SeaOfFireEffect>()!;
|
var seaOfFire = sideVolatile.Get<SeaOfFireEffect>()!;
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class FireWaterPledgeMoveTests
|
|||||||
/// Creates a fully mocked test setup. The user's battle side gets a real <see cref="ScriptSet"/> as its
|
/// Creates a fully mocked test setup. The user's battle side gets a real <see cref="ScriptSet"/> as its
|
||||||
/// volatile script set, so the rainbow created by the combined move can be inspected and driven.
|
/// volatile script set, so the rainbow created by the combined move can be inspected and driven.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (FireWaterPledgeMove script, IExecutingMove move, IPokemon user, IBattleSide side, IScriptSet
|
private static (FireWaterPledgeMove script, IExecutingMove move, IBattlePokemon user, IBattleSide side, IScriptSet
|
||||||
sideVolatile) CreateTestSetup()
|
sideVolatile) CreateTestSetup()
|
||||||
{
|
{
|
||||||
var script = new FireWaterPledgeMove();
|
var script = new FireWaterPledgeMove();
|
||||||
@@ -31,11 +31,9 @@ public class FireWaterPledgeMoveTests
|
|||||||
var sideVolatile = new ScriptSet(side);
|
var sideVolatile = new ScriptSet(side);
|
||||||
side.VolatileScripts.Returns(sideVolatile);
|
side.VolatileScripts.Returns(sideVolatile);
|
||||||
|
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
battleData.BattleSide.Returns(side);
|
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
user.BattleSide.Returns(side);
|
||||||
|
|
||||||
return (script, move, user, side, sideVolatile);
|
return (script, move, user, side, sideVolatile);
|
||||||
}
|
}
|
||||||
@@ -67,7 +65,7 @@ public class FireWaterPledgeMoveTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, _, _, _) = CreateTestSetup();
|
var (script, move, _, _, _) = CreateTestSetup();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
ushort basePower = 80;
|
ushort basePower = 80;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -86,7 +84,7 @@ public class FireWaterPledgeMoveTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, _, _, sideVolatile) = CreateTestSetup();
|
var (script, move, _, _, sideVolatile) = CreateTestSetup();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
@@ -95,25 +93,6 @@ public class FireWaterPledgeMoveTests
|
|||||||
await Assert.That(sideVolatile.Get<RainbowEffect>()).IsNotNull();
|
await Assert.That(sideVolatile.Get<RainbowEffect>()).IsNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: if the user has no <see cref="IPokemon.BattleData"/>, no rainbow is created and the
|
|
||||||
/// script does not throw.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task OnSecondaryEffect_UserHasNoBattleData_DoesNothing()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var (script, move, user, _, sideVolatile) = CreateTestSetup();
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
var target = Substitute.For<IPokemon>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
await Assert.That(sideVolatile.Count).IsEqualTo(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bulbapedia: "The rainbow doubles the probability of additional effects taking place for moves used
|
/// Bulbapedia: "The rainbow doubles the probability of additional effects taking place for moves used
|
||||||
/// by that side of the field".
|
/// by that side of the field".
|
||||||
@@ -123,7 +102,7 @@ public class FireWaterPledgeMoveTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, _, _, sideVolatile) = CreateTestSetup();
|
var (script, move, _, _, sideVolatile) = CreateTestSetup();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
var rainbow = sideVolatile.Get<RainbowEffect>()!;
|
var rainbow = sideVolatile.Get<RainbowEffect>()!;
|
||||||
|
|
||||||
@@ -143,7 +122,7 @@ public class FireWaterPledgeMoveTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, _, side, sideVolatile) = CreateTestSetup();
|
var (script, move, _, side, sideVolatile) = CreateTestSetup();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
var rainbow = sideVolatile.Get<RainbowEffect>()!;
|
var rainbow = sideVolatile.Get<RainbowEffect>()!;
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
@@ -165,7 +144,7 @@ public class FireWaterPledgeMoveTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, _, side, sideVolatile) = CreateTestSetup();
|
var (script, move, _, side, sideVolatile) = CreateTestSetup();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
var rainbow = sideVolatile.Get<RainbowEffect>()!;
|
var rainbow = sideVolatile.Get<RainbowEffect>()!;
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ public class AcrobaticsTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
ushort basePower = 10;
|
ushort basePower = 10;
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.HeldItem.Returns((IItem?)null);
|
user.HeldItem.Returns((IItem?)null);
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var acrobatics = new Acrobatics();
|
var acrobatics = new Acrobatics();
|
||||||
@@ -30,9 +30,9 @@ public class AcrobaticsTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
ushort basePower = 10;
|
ushort basePower = 10;
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.HeldItem.Returns(Substitute.For<IItem>());
|
user.HeldItem.Returns(Substitute.For<IItem>());
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var acrobatics = new Acrobatics();
|
var acrobatics = new Acrobatics();
|
||||||
@@ -49,9 +49,9 @@ public class AcrobaticsTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
ushort basePower = ushort.MaxValue - 100;
|
ushort basePower = ushort.MaxValue - 100;
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
user.HeldItem.Returns((IItem?)null);
|
user.HeldItem.Returns((IItem?)null);
|
||||||
var acrobatics = new Acrobatics();
|
var acrobatics = new Acrobatics();
|
||||||
|
|||||||
@@ -13,24 +13,22 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AcupressureTests
|
public class AcupressureTests
|
||||||
{
|
{
|
||||||
private static (Acupressure script, IExecutingMove move, IPokemon target, IBattleRandom random, IHitData hitData)
|
private static (Acupressure script, IExecutingMove move, IBattlePokemon target, IBattleRandom random, IHitData
|
||||||
CreateTestSetup()
|
hitData) CreateTestSetup()
|
||||||
{
|
{
|
||||||
var script = new Acupressure();
|
var script = new Acupressure();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
|
|
||||||
var random = Substitute.For<IBattleRandom>();
|
var random = Substitute.For<IBattleRandom>();
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
battle.Random.Returns(random);
|
battle.Random.Returns(random);
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
user.Battle.Returns(battle);
|
||||||
|
|
||||||
target.StatBoost.Returns(new StatBoostStatisticSet());
|
target.StatBoost.Returns(new StatBoostStatisticSet());
|
||||||
|
|
||||||
@@ -40,7 +38,7 @@ public class AcupressureTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the stat argument of a ChangeStatBoost call received by the target.
|
/// Helper to extract the stat argument of a ChangeStatBoost call received by the target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static object?[]? GetStatBoostCallArgs(IPokemon target)
|
private static object?[]? GetStatBoostCallArgs(IBattlePokemon target)
|
||||||
{
|
{
|
||||||
var call = target.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
var call = target.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
||||||
return call?.GetArguments();
|
return call?.GetArguments();
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AfterYouTests
|
public class AfterYouTests
|
||||||
{
|
{
|
||||||
private static IMoveChoice CreateChoice(IPokemon user, uint speed)
|
private static IMoveChoice CreateChoice(IBattlePokemon user, uint speed)
|
||||||
{
|
{
|
||||||
var choice = Substitute.For<IMoveChoice>();
|
var choice = Substitute.For<IMoveChoice>();
|
||||||
choice.User.Returns(user);
|
choice.User.Returns(user);
|
||||||
@@ -25,16 +25,14 @@ public class AfterYouTests
|
|||||||
var script = new AfterYou();
|
var script = new AfterYou();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
move.GetHitData(Arg.Any<IPokemon>(), Arg.Any<byte>()).Returns(hitData);
|
move.GetHitData(Arg.Any<IBattlePokemon>(), Arg.Any<byte>()).Returns(hitData);
|
||||||
|
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
battle.ChoiceQueue.Returns(queue);
|
battle.ChoiceQueue.Returns(queue);
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
user.Battle.Returns(battle);
|
||||||
|
|
||||||
return (script, move, hitData);
|
return (script, move, hitData);
|
||||||
}
|
}
|
||||||
@@ -47,9 +45,9 @@ public class AfterYouTests
|
|||||||
public async Task OnSecondaryEffect_TargetLaterInQueue_TargetMovesNext()
|
public async Task OnSecondaryEffect_TargetLaterInQueue_TargetMovesNext()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var other = Substitute.For<IPokemon>();
|
var other = Substitute.For<IBattlePokemon>();
|
||||||
// Sorted by speed: user (100), other (75), target (50)
|
// Sorted by speed: user (100), other (75), target (50)
|
||||||
var queue = new BattleChoiceQueue([
|
var queue = new BattleChoiceQueue([
|
||||||
CreateChoice(user, 100), CreateChoice(other, 75), CreateChoice(target, 50),
|
CreateChoice(user, 100), CreateChoice(other, 75), CreateChoice(target, 50),
|
||||||
@@ -75,8 +73,8 @@ public class AfterYouTests
|
|||||||
public void OnSecondaryEffect_TargetAlreadyMoved_Fails()
|
public void OnSecondaryEffect_TargetAlreadyMoved_Fails()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
// Sorted by speed: target (100), user (50)
|
// Sorted by speed: target (100), user (50)
|
||||||
var queue = new BattleChoiceQueue([
|
var queue = new BattleChoiceQueue([
|
||||||
CreateChoice(target, 100), CreateChoice(user, 50),
|
CreateChoice(target, 100), CreateChoice(user, 50),
|
||||||
@@ -103,8 +101,8 @@ public class AfterYouTests
|
|||||||
public void OnSecondaryEffect_TargetAlreadyNext_Fails()
|
public void OnSecondaryEffect_TargetAlreadyNext_Fails()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
// Sorted by speed: user (100), target (50)
|
// Sorted by speed: user (100), target (50)
|
||||||
var queue = new BattleChoiceQueue([
|
var queue = new BattleChoiceQueue([
|
||||||
CreateChoice(user, 100), CreateChoice(target, 50),
|
CreateChoice(user, 100), CreateChoice(target, 50),
|
||||||
@@ -132,7 +130,7 @@ public class AfterYouTests
|
|||||||
var (script, move, hitData) = CreateTestSetup(null);
|
var (script, move, hitData) = CreateTestSetup(null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
hitData.DidNotReceive().Fail();
|
hitData.DidNotReceive().Fail();
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AssistTests
|
public class AssistTests
|
||||||
{
|
{
|
||||||
private static IPokemon CreatePokemonWithMoves(params string[] moveNames)
|
private static IBattlePokemon CreatePokemonWithMoves(params string[] moveNames)
|
||||||
{
|
{
|
||||||
var pokemon = Substitute.For<IPokemon>();
|
var pokemon = Substitute.For<IBattlePokemon>();
|
||||||
var moves = moveNames.Select(name =>
|
var moves = moveNames.Select(name =>
|
||||||
{
|
{
|
||||||
var learned = Substitute.For<ILearnedMove>();
|
var learned = Substitute.For<ILearnedMove>();
|
||||||
@@ -30,27 +30,23 @@ public class AssistTests
|
|||||||
return pokemon;
|
return pokemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (Assist script, IMoveChoice choice, IPokemon user, IBattleRandom random) CreateTestSetup(
|
private static (Assist script, IMoveChoice choice, IBattlePokemon user, IBattleRandom random) CreateTestSetup(
|
||||||
params IPokemon?[] otherPartyMembers)
|
params IBattlePokemon?[] otherPartyMembers)
|
||||||
{
|
{
|
||||||
var script = new Assist();
|
var script = new Assist();
|
||||||
var user = CreatePokemonWithMoves("tackle");
|
var user = CreatePokemonWithMoves("tackle");
|
||||||
|
|
||||||
var members = new List<IPokemon?> { user };
|
var members = new List<IBattlePokemon?> { user };
|
||||||
members.AddRange(otherPartyMembers);
|
members.AddRange(otherPartyMembers);
|
||||||
|
|
||||||
var party = Substitute.For<IPokemonParty>();
|
|
||||||
party.GetEnumerator().Returns(_ => members.GetEnumerator());
|
|
||||||
var battleParty = Substitute.For<IBattleParty>();
|
var battleParty = Substitute.For<IBattleParty>();
|
||||||
battleParty.Party.Returns(party);
|
battleParty.BattlePokemon.Returns(members);
|
||||||
|
|
||||||
var random = Substitute.For<IBattleRandom>();
|
var random = Substitute.For<IBattleRandom>();
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
battle.Parties.Returns(new[] { battleParty });
|
battle.Parties.Returns(new[] { battleParty });
|
||||||
battle.Random.Returns(random);
|
battle.Random.Returns(random);
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
user.Battle.Returns(battle);
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
var choice = Substitute.For<IMoveChoice>();
|
var choice = Substitute.For<IMoveChoice>();
|
||||||
choice.User.Returns(user);
|
choice.User.Returns(user);
|
||||||
@@ -180,26 +176,4 @@ public class AssistTests
|
|||||||
await Assert.That(moveName).IsEqualTo(new StringKey("growl"));
|
await Assert.That(moveName).IsEqualTo(new StringKey("growl"));
|
||||||
choice.DidNotReceive().Fail();
|
choice.DidNotReceive().Fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: outside of battle (no battle data) the script returns without failing the choice.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task ChangeMove_NoBattleData_DoesNothing()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var script = new Assist();
|
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
var choice = Substitute.For<IMoveChoice>();
|
|
||||||
choice.User.Returns(user);
|
|
||||||
StringKey moveName = "assist";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.ChangeMove(choice, ref moveName);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
choice.DidNotReceive().Fail();
|
|
||||||
await Assert.That(moveName).IsEqualTo(new StringKey("assist"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -15,12 +15,12 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AttractTests
|
public class AttractTests
|
||||||
{
|
{
|
||||||
private static (Attract script, IExecutingMove move, IPokemon target, IScriptSet targetVolatile, IHitData hitData)
|
private static (Attract script, IExecutingMove move, IBattlePokemon target, IScriptSet targetVolatile, IHitData
|
||||||
CreateTestSetup(Gender userGender, Gender targetGender)
|
hitData) CreateTestSetup(Gender userGender, Gender targetGender)
|
||||||
{
|
{
|
||||||
var script = new Attract();
|
var script = new Attract();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ public class AttractTests
|
|||||||
target.Volatile.Returns(targetVolatile);
|
target.Volatile.Returns(targetVolatile);
|
||||||
target.Gender.Returns(targetGender);
|
target.Gender.Returns(targetGender);
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.Gender.Returns(userGender);
|
user.Gender.Returns(userGender);
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ public class AuroraVeilTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (AuroraVeil script, IExecutingMove move, IPokemon target, IPokemon user, IScriptSet sideScripts,
|
private static (AuroraVeil script, IExecutingMove move, IBattlePokemon target, IBattlePokemon user, IScriptSet
|
||||||
IHitData hitData) CreateTestSetup(StringKey? weather)
|
sideScripts, IHitData hitData) CreateTestSetup(StringKey? weather)
|
||||||
{
|
{
|
||||||
var script = new AuroraVeil();
|
var script = new AuroraVeil();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
|
|
||||||
@@ -51,13 +51,10 @@ public class AuroraVeilTests
|
|||||||
side.VolatileScripts.Returns(sideScripts);
|
side.VolatileScripts.Returns(sideScripts);
|
||||||
battle.Sides.Returns(new[] { side });
|
battle.Sides.Returns(new[] { side });
|
||||||
|
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
battleData.SideIndex.Returns((byte)0);
|
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
user.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
|
user.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
|
||||||
|
user.SideIndex.Returns((byte)0);
|
||||||
|
user.Battle.Returns(battle);
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
|
||||||
return (script, move, target, user, sideScripts, hitData);
|
return (script, move, target, user, sideScripts, hitData);
|
||||||
@@ -154,25 +151,4 @@ public class AuroraVeilTests
|
|||||||
var effect = (AuroraVeilEffect)((Func<Script?>)call.GetArguments()[1]!)()!;
|
var effect = (AuroraVeilEffect)((Func<Script?>)call.GetArguments()[1]!)()!;
|
||||||
await Assert.That(effect.NumberOfTurns).IsEqualTo(8);
|
await Assert.That(effect.NumberOfTurns).IsEqualTo(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: outside of battle (no battle data) the script returns without throwing.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task OnSecondaryEffect_NoBattleData_DoesNothing()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var script = new AuroraVeil();
|
|
||||||
var move = Substitute.For<IExecutingMove>();
|
|
||||||
var target = Substitute.For<IPokemon>();
|
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
move.User.Returns(user);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
|
||||||
|
|
||||||
// Assert - no hit data was touched
|
|
||||||
await Assert.That(move.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "GetHitData")).IsFalse();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -17,8 +17,8 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AutotomizeTests
|
public class AutotomizeTests
|
||||||
{
|
{
|
||||||
private static (Autotomize script, IExecutingMove move, IPokemon user, IScriptSet userVolatile, EventHook eventHook)
|
private static (Autotomize script, IExecutingMove move, IBattlePokemon user, IScriptSet userVolatile, EventHook
|
||||||
CreateTestSetup(float weightInKg, bool speedRaiseSucceeds, AutotomizeEffect? existingEffect = null)
|
eventHook) CreateTestSetup(float weightInKg, bool speedRaiseSucceeds, AutotomizeEffect? existingEffect = null)
|
||||||
{
|
{
|
||||||
var script = new Autotomize();
|
var script = new Autotomize();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
@@ -26,15 +26,13 @@ public class AutotomizeTests
|
|||||||
var eventHook = new EventHook();
|
var eventHook = new EventHook();
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
battle.EventHook.Returns(eventHook);
|
battle.EventHook.Returns(eventHook);
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
|
|
||||||
var userVolatile = Substitute.For<IScriptSet>();
|
var userVolatile = Substitute.For<IScriptSet>();
|
||||||
userVolatile.Get<AutotomizeEffect>().Returns(existingEffect);
|
userVolatile.Get<AutotomizeEffect>().Returns(existingEffect);
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
user.Volatile.Returns(userVolatile);
|
user.Volatile.Returns(userVolatile);
|
||||||
|
user.Battle.Returns(battle);
|
||||||
user.WeightInKg.Returns(weightInKg);
|
user.WeightInKg.Returns(weightInKg);
|
||||||
user.ChangeStatBoost(Arg.Any<Statistic>(), Arg.Any<sbyte>(), Arg.Any<bool>(), Arg.Any<bool>())
|
user.ChangeStatBoost(Arg.Any<Statistic>(), Arg.Any<sbyte>(), Arg.Any<bool>(), Arg.Any<bool>())
|
||||||
.Returns(speedRaiseSucceeds);
|
.Returns(speedRaiseSucceeds);
|
||||||
@@ -56,7 +54,7 @@ public class AutotomizeTests
|
|||||||
var (script, move, user, _, _) = CreateTestSetup(100f, true);
|
var (script, move, user, _, _) = CreateTestSetup(100f, true);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
user.Received(1).ChangeStatBoost(Statistic.Speed, 2, true, false);
|
user.Received(1).ChangeStatBoost(Statistic.Speed, 2, true, false);
|
||||||
@@ -79,7 +77,7 @@ public class AutotomizeTests
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsTrue();
|
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsTrue();
|
||||||
@@ -100,7 +98,7 @@ public class AutotomizeTests
|
|||||||
eventHook.Handler += (_, _) => eventFired = true;
|
eventHook.Handler += (_, _) => eventFired = true;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsFalse();
|
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsFalse();
|
||||||
@@ -119,7 +117,7 @@ public class AutotomizeTests
|
|||||||
var (script, move, _, userVolatile, _) = CreateTestSetup(50f, true);
|
var (script, move, _, userVolatile, _) = CreateTestSetup(50f, true);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsTrue();
|
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsTrue();
|
||||||
@@ -140,7 +138,7 @@ public class AutotomizeTests
|
|||||||
var (script, move, _, userVolatile, _) = CreateTestSetup(50f, true, existingEffect);
|
var (script, move, _, userVolatile, _) = CreateTestSetup(50f, true, existingEffect);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert - the weight should still be reduced (to the minimum)
|
// Assert - the weight should still be reduced (to the minimum)
|
||||||
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsTrue();
|
await Assert.That(ReceivedStackOrAdd(userVolatile)).IsTrue();
|
||||||
@@ -160,7 +158,7 @@ public class AutotomizeTests
|
|||||||
eventHook.Handler += (_, _) => eventFired = true;
|
eventHook.Handler += (_, _) => eventFired = true;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
user.Received(1).ChangeStatBoost(Statistic.Speed, 2, true, false);
|
user.Received(1).ChangeStatBoost(Statistic.Speed, 2, true, false);
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ public class BanefulBunkerTests
|
|||||||
/// <see cref="ProtectionScript.OnSecondaryEffect"/>. The <c>target</c> of the secondary effect is the
|
/// <see cref="ProtectionScript.OnSecondaryEffect"/>. The <c>target</c> of the secondary effect is the
|
||||||
/// Pokémon using Baneful Bunker itself, as the move is self-targeted.
|
/// Pokémon using Baneful Bunker itself, as the move is self-targeted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (BanefulBunker script, IExecutingMove move, IPokemon target, IHitData hitData, IScriptSet volatileSet
|
private static (BanefulBunker script, IExecutingMove move, IBattlePokemon target, IHitData hitData, IScriptSet
|
||||||
) CreateProtectSetup(bool userMovesLast, float randomRoll)
|
volatileSet ) CreateProtectSetup(bool userMovesLast, float randomRoll)
|
||||||
{
|
{
|
||||||
var script = new BanefulBunker();
|
var script = new BanefulBunker();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
|
|
||||||
@@ -38,9 +38,7 @@ public class BanefulBunkerTests
|
|||||||
battle.ChoiceQueue.Returns(queue);
|
battle.ChoiceQueue.Returns(queue);
|
||||||
battle.Random.Returns(random);
|
battle.Random.Returns(random);
|
||||||
|
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
target.Battle.Returns(battle);
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
target.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
// Give the mock a real script iterator (used by the volatile add hook) and a real volatile script set.
|
// Give the mock a real script iterator (used by the volatile add hook) and a real volatile script set.
|
||||||
target.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
target.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
||||||
@@ -54,12 +52,12 @@ public class BanefulBunkerTests
|
|||||||
/// Creates a fully mocked setup for driving <see cref="BanefulBunkerEffect.BlockIncomingHit"/>, the
|
/// Creates a fully mocked setup for driving <see cref="BanefulBunkerEffect.BlockIncomingHit"/>, the
|
||||||
/// volatile script that <see cref="BanefulBunker"/> attaches to its user.
|
/// volatile script that <see cref="BanefulBunker"/> attaches to its user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (BanefulBunkerEffect effect, IExecutingMove move, IPokemon target, IPokemon attacker)
|
private static (BanefulBunkerEffect effect, IExecutingMove move, IBattlePokemon target, IBattlePokemon attacker)
|
||||||
CreateBlockSetup(bool isContact, bool hasProtectFlag, MoveCategory category = MoveCategory.Physical)
|
CreateBlockSetup(bool isContact, bool hasProtectFlag, MoveCategory category = MoveCategory.Physical)
|
||||||
{
|
{
|
||||||
var effect = new BanefulBunkerEffect();
|
var effect = new BanefulBunkerEffect();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
hitData.IsContact.Returns(isContact);
|
hitData.IsContact.Returns(isContact);
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
@@ -69,19 +67,17 @@ public class BanefulBunkerTests
|
|||||||
useMove.Category.Returns(category);
|
useMove.Category.Returns(category);
|
||||||
move.UseMove.Returns(useMove);
|
move.UseMove.Returns(useMove);
|
||||||
|
|
||||||
var attacker = Substitute.For<IPokemon>();
|
var attacker = Substitute.For<IBattlePokemon>();
|
||||||
attacker.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
attacker.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
||||||
move.User.Returns(attacker);
|
move.User.Returns(attacker);
|
||||||
|
|
||||||
target.BattleData.Returns(Substitute.For<IPokemonBattleData>());
|
|
||||||
|
|
||||||
return (effect, move, target, attacker);
|
return (effect, move, target, attacker);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the status name from a Pokémon's received <see cref="IPokemon.SetStatus"/> calls.
|
/// Helper to extract the status name from a Pokémon's received <see cref="IBattlePokemon.SetStatus"/> calls.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static string? GetStatusSet(IPokemon pokemon)
|
private static string? GetStatusSet(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "SetStatus");
|
var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "SetStatus");
|
||||||
return call != null ? ((StringKey)call.GetArguments()[0]!).ToString() : null;
|
return call != null ? ((StringKey)call.GetArguments()[0]!).ToString() : null;
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ public class BatonPassTests
|
|||||||
/// Creates a mocked Pokémon with a real volatile <see cref="ScriptSet"/> and a real
|
/// Creates a mocked Pokémon with a real volatile <see cref="ScriptSet"/> and a real
|
||||||
/// <see cref="StatBoostStatisticSet"/>.
|
/// <see cref="StatBoostStatisticSet"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static IPokemon CreateMockPokemon(out IScriptSet volatileSet)
|
private static IBattlePokemon CreateMockPokemon(out IScriptSet volatileSet)
|
||||||
{
|
{
|
||||||
var pokemon = Substitute.For<IPokemon>();
|
var pokemon = Substitute.For<IBattlePokemon>();
|
||||||
pokemon.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
pokemon.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
||||||
var set = new ScriptSet(pokemon);
|
var set = new ScriptSet(pokemon);
|
||||||
pokemon.Volatile.Returns(set);
|
pokemon.Volatile.Returns(set);
|
||||||
@@ -36,8 +36,8 @@ public class BatonPassTests
|
|||||||
/// Creates a fully mocked test setup for Baton Pass tests. The Pokémon to switch in is stored in the
|
/// Creates a fully mocked test setup for Baton Pass tests. The Pokémon to switch in is stored in the
|
||||||
/// move choice's <see cref="IMoveChoice.AdditionalData"/> under the <c>to_switch</c> key.
|
/// move choice's <see cref="IMoveChoice.AdditionalData"/> under the <c>to_switch</c> key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (BatonPass script, IExecutingMove move, IPokemon user, IPokemon toSwitch, IBattleSide side,
|
private static (BatonPass script, IExecutingMove move, IBattlePokemon user, IBattlePokemon toSwitch, IBattleSide
|
||||||
IScriptSet userVolatile, IScriptSet switchInVolatile) CreateTestSetup()
|
side, IScriptSet userVolatile, IScriptSet switchInVolatile) CreateTestSetup()
|
||||||
{
|
{
|
||||||
var script = new BatonPass();
|
var script = new BatonPass();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
@@ -52,11 +52,9 @@ public class BatonPassTests
|
|||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
battle.Sides.Returns(new[] { side });
|
battle.Sides.Returns(new[] { side });
|
||||||
|
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
user.Battle.Returns(battle);
|
||||||
battleData.Battle.Returns(battle);
|
user.SideIndex.Returns((byte)0);
|
||||||
battleData.SideIndex.Returns((byte)0);
|
user.Position.Returns((byte)1);
|
||||||
battleData.Position.Returns((byte)1);
|
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
return (script, move, user, toSwitch, side, userVolatile, switchInVolatile);
|
return (script, move, user, toSwitch, side, userVolatile, switchInVolatile);
|
||||||
@@ -213,7 +211,7 @@ public class BatonPassTests
|
|||||||
script.OnSecondaryEffect(move, user, 0);
|
script.OnSecondaryEffect(move, user, 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
side.DidNotReceive().SwapPokemon(Arg.Any<byte>(), Arg.Any<IPokemon?>());
|
side.DidNotReceive().SwapPokemon(Arg.Any<byte>(), Arg.Any<IBattlePokemon?>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -230,26 +228,6 @@ public class BatonPassTests
|
|||||||
script.OnSecondaryEffect(move, user, 0);
|
script.OnSecondaryEffect(move, user, 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
side.DidNotReceive().SwapPokemon(Arg.Any<byte>(), Arg.Any<IPokemon?>());
|
side.DidNotReceive().SwapPokemon(Arg.Any<byte>(), Arg.Any<IBattlePokemon?>());
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: if the user has no <see cref="IPokemon.BattleData"/>, the script returns without
|
|
||||||
/// switching and without clearing the user's volatile scripts.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task OnSecondaryEffect_NoBattleData_DoesNotSwitch()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var (script, move, user, _, side, userVolatile, _) = CreateTestSetup();
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
userVolatile.Add(new AutotomizeEffect());
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.OnSecondaryEffect(move, user, 0);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
side.DidNotReceive().SwapPokemon(Arg.Any<byte>(), Arg.Any<IPokemon?>());
|
|
||||||
await Assert.That(userVolatile.Get<AutotomizeEffect>()).IsNotNull();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,28 +17,19 @@ public class BeakBlastTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a fully mocked setup for driving <see cref="BeakBlast.OnBeforeTurnStart"/>.
|
/// Creates a fully mocked setup for driving <see cref="BeakBlast.OnBeforeTurnStart"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (BeakBlast script, ITurnChoice choice, IPokemon user, IScriptSet volatileSet, EventHook eventHook)
|
private static (BeakBlast script, ITurnChoice choice, IBattlePokemon user, IScriptSet volatileSet, EventHook
|
||||||
CreateChargeSetup(bool hasBattleData = true)
|
eventHook) CreateChargeSetup()
|
||||||
{
|
{
|
||||||
var script = new BeakBlast();
|
var script = new BeakBlast();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
user.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
||||||
IScriptSet volatileSet = new ScriptSet(user);
|
IScriptSet volatileSet = new ScriptSet(user);
|
||||||
user.Volatile.Returns(volatileSet);
|
user.Volatile.Returns(volatileSet);
|
||||||
|
|
||||||
var eventHook = new EventHook();
|
var eventHook = new EventHook();
|
||||||
if (hasBattleData)
|
var battle = Substitute.For<IBattle>();
|
||||||
{
|
battle.EventHook.Returns(eventHook);
|
||||||
var battle = Substitute.For<IBattle>();
|
user.Battle.Returns(battle);
|
||||||
battle.EventHook.Returns(eventHook);
|
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
}
|
|
||||||
|
|
||||||
var choice = Substitute.For<ITurnChoice>();
|
var choice = Substitute.For<ITurnChoice>();
|
||||||
choice.User.Returns(user);
|
choice.User.Returns(user);
|
||||||
@@ -85,24 +76,7 @@ public class BeakBlastTests
|
|||||||
// Assert
|
// Assert
|
||||||
await Assert.That(capturedEvent).IsNotNull();
|
await Assert.That(capturedEvent).IsNotNull();
|
||||||
await Assert.That(capturedEvent!.Message).IsEqualTo("beak_blast_charge");
|
await Assert.That(capturedEvent!.Message).IsEqualTo("beak_blast_charge");
|
||||||
await Assert.That((IPokemon)capturedEvent.Parameters!["user"]).IsEqualTo(user);
|
await Assert.That((IBattlePokemon)capturedEvent.Parameters!["user"]).IsEqualTo(user);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: a Pokémon without <see cref="IPokemon.BattleData"/> is not in battle, so no charging
|
|
||||||
/// phase starts.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task OnBeforeTurnStart_NoBattleData_DoesNotAddChargeEffect()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var (script, choice, _, volatileSet, _) = CreateChargeSetup(false);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.OnBeforeTurnStart(choice);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
await Assert.That(volatileSet.Get<BeakBlastEffect>()).IsNull();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -115,8 +89,8 @@ public class BeakBlastTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var script = new BeakBlast();
|
var script = new BeakBlast();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
user.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
||||||
IScriptSet volatileSet = new ScriptSet(user);
|
IScriptSet volatileSet = new ScriptSet(user);
|
||||||
user.Volatile.Returns(volatileSet);
|
user.Volatile.Returns(volatileSet);
|
||||||
@@ -133,26 +107,26 @@ public class BeakBlastTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a fully mocked setup for driving <see cref="BeakBlastEffect.OnIncomingHit"/>.
|
/// Creates a fully mocked setup for driving <see cref="BeakBlastEffect.OnIncomingHit"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (BeakBlastEffect effect, IExecutingMove move, IPokemon target, IPokemon attacker)
|
private static (BeakBlastEffect effect, IExecutingMove move, IBattlePokemon target, IBattlePokemon attacker)
|
||||||
CreateIncomingHitSetup(bool isContact)
|
CreateIncomingHitSetup(bool isContact)
|
||||||
{
|
{
|
||||||
var effect = new BeakBlastEffect();
|
var effect = new BeakBlastEffect();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
hitData.IsContact.Returns(isContact);
|
hitData.IsContact.Returns(isContact);
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
|
|
||||||
var attacker = Substitute.For<IPokemon>();
|
var attacker = Substitute.For<IBattlePokemon>();
|
||||||
move.User.Returns(attacker);
|
move.User.Returns(attacker);
|
||||||
|
|
||||||
return (effect, move, target, attacker);
|
return (effect, move, target, attacker);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the status name from a Pokémon's received <see cref="IPokemon.SetStatus"/> calls.
|
/// Helper to extract the status name from a Pokémon's received <see cref="IBattlePokemon.SetStatus"/> calls.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static string? GetStatusSet(IPokemon pokemon)
|
private static string? GetStatusSet(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "SetStatus");
|
var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "SetStatus");
|
||||||
return call != null ? ((StringKey)call.GetArguments()[0]!).ToString() : null;
|
return call != null ? ((StringKey)call.GetArguments()[0]!).ToString() : null;
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ public class BeatUpTests
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a mocked party member with the given base Attack stat, usability, and optional non-volatile
|
/// Creates a mocked party member with the given base Attack stat, usability, and optional non-volatile
|
||||||
/// status script in its <see cref="IPokemon.StatusScript"/>.
|
/// status script in its <see cref="IBattlePokemon.StatusScript"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static IPokemon CreatePartyMember(ushort baseAttack = 100, bool usable = true, Script? status = null)
|
private static IBattlePokemon CreatePartyMember(ushort baseAttack = 100, bool usable = true, Script? status = null)
|
||||||
{
|
{
|
||||||
var pokemon = Substitute.For<IPokemon>();
|
var pokemon = Substitute.For<IBattlePokemon>();
|
||||||
pokemon.IsUsable.Returns(usable);
|
pokemon.IsUsable.Returns(usable);
|
||||||
pokemon.StatusScript.Returns(status == null ? new ScriptContainer() : new ScriptContainer(status));
|
pokemon.StatusScript.Returns(status == null ? new ScriptContainer() : new ScriptContainer(status));
|
||||||
var form = Substitute.For<IForm>();
|
var form = Substitute.For<IForm>();
|
||||||
@@ -35,24 +35,20 @@ public class BeatUpTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a fully mocked test setup where the user and the given other Pokémon form a party in a battle.
|
/// Creates a fully mocked test setup where the user and the given other Pokémon form a party in a battle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (BeatUp script, IMoveChoice choice, IExecutingMove move) CreateTestSetup(IPokemon user,
|
private static (BeatUp script, IMoveChoice choice, IExecutingMove move) CreateTestSetup(IBattlePokemon user,
|
||||||
params IPokemon?[] otherPartyMembers)
|
params IBattlePokemon?[] otherPartyMembers)
|
||||||
{
|
{
|
||||||
var script = new BeatUp();
|
var script = new BeatUp();
|
||||||
|
|
||||||
var members = new List<IPokemon?> { user };
|
var members = new List<IBattlePokemon?> { user };
|
||||||
members.AddRange(otherPartyMembers);
|
members.AddRange(otherPartyMembers);
|
||||||
|
|
||||||
var party = Substitute.For<IPokemonParty>();
|
|
||||||
party.GetEnumerator().Returns(_ => members.GetEnumerator());
|
|
||||||
var battleParty = Substitute.For<IBattleParty>();
|
var battleParty = Substitute.For<IBattleParty>();
|
||||||
battleParty.Party.Returns(party);
|
battleParty.BattlePokemon.Returns(members);
|
||||||
|
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
battle.Parties.Returns(new[] { battleParty });
|
battle.Parties.Returns(new[] { battleParty });
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
user.Battle.Returns(battle);
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
var choice = Substitute.For<IMoveChoice>();
|
var choice = Substitute.For<IMoveChoice>();
|
||||||
choice.User.Returns(user);
|
choice.User.Returns(user);
|
||||||
@@ -121,28 +117,6 @@ public class BeatUpTests
|
|||||||
await Assert.That(numberOfHits).IsEqualTo((byte)1);
|
await Assert.That(numberOfHits).IsEqualTo((byte)1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: outside of battle (no <see cref="IPokemon.BattleData"/>) there are no relevant party
|
|
||||||
/// members, and the number of hits falls back to a single strike.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task ChangeNumberOfHits_NoBattleData_SingleHit()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var script = new BeatUp();
|
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
var choice = Substitute.For<IMoveChoice>();
|
|
||||||
choice.User.Returns(user);
|
|
||||||
byte numberOfHits = 3;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.ChangeNumberOfHits(choice, ref numberOfHits);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
await Assert.That(numberOfHits).IsEqualTo((byte)1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bulbapedia: "the base power per strike is no longer 10, but instead individually based on the Attack
|
/// Bulbapedia: "the base power per strike is no longer 10, but instead individually based on the Attack
|
||||||
/// base stats of the party Pokémon: BasePower = BaseAttack(PartyMember)/10 + 5".
|
/// base stats of the party Pokémon: BasePower = BaseAttack(PartyMember)/10 + 5".
|
||||||
@@ -155,7 +129,7 @@ public class BeatUpTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var user = CreatePartyMember(baseAttack);
|
var user = CreatePartyMember(baseAttack);
|
||||||
var (script, _, move) = CreateTestSetup(user);
|
var (script, _, move) = CreateTestSetup(user);
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
ushort basePower = 10;
|
ushort basePower = 10;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -175,7 +149,7 @@ public class BeatUpTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var user = CreatePartyMember(100);
|
var user = CreatePartyMember(100);
|
||||||
var (script, _, move) = CreateTestSetup(user, CreatePartyMember(250));
|
var (script, _, move) = CreateTestSetup(user, CreatePartyMember(250));
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
ushort basePower = 10;
|
ushort basePower = 10;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -197,7 +171,7 @@ public class BeatUpTests
|
|||||||
var user = CreatePartyMember(100);
|
var user = CreatePartyMember(100);
|
||||||
var (script, _, move) = CreateTestSetup(user, CreatePartyMember(250, status: new Burned()),
|
var (script, _, move) = CreateTestSetup(user, CreatePartyMember(250, status: new Burned()),
|
||||||
CreatePartyMember(60));
|
CreatePartyMember(60));
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
ushort basePower = 10;
|
ushort basePower = 10;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -217,7 +191,7 @@ public class BeatUpTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var user = CreatePartyMember(100);
|
var user = CreatePartyMember(100);
|
||||||
var (script, _, move) = CreateTestSetup(user);
|
var (script, _, move) = CreateTestSetup(user);
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
ushort basePower = 10;
|
ushort basePower = 10;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
public class BelchTests
|
public class BelchTests
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a fully mocked test setup where the user's <see cref="IPokemonBattleData.ConsumedItems"/>
|
/// Creates a fully mocked test setup where the user's <see cref="IBattlePokemon.ConsumedItems"/>
|
||||||
/// contains one item per given <see cref="ItemCategory"/>.
|
/// contains one item per given <see cref="ItemCategory"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (Belch script, IMoveChoice choice) CreateTestSetup(params ItemCategory[] consumedItemCategories)
|
private static (Belch script, IMoveChoice choice) CreateTestSetup(params ItemCategory[] consumedItemCategories)
|
||||||
{
|
{
|
||||||
var script = new Belch();
|
var script = new Belch();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
var items = consumedItemCategories.Select(category =>
|
var items = consumedItemCategories.Select(category =>
|
||||||
{
|
{
|
||||||
@@ -27,9 +27,7 @@ public class BelchTests
|
|||||||
item.Category.Returns(category);
|
item.Category.Returns(category);
|
||||||
return item;
|
return item;
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
user.ConsumedItems.Returns(items);
|
||||||
battleData.ConsumedItems.Returns(items);
|
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
var choice = Substitute.For<IMoveChoice>();
|
var choice = Substitute.For<IMoveChoice>();
|
||||||
choice.User.Returns(user);
|
choice.User.Returns(user);
|
||||||
@@ -108,26 +106,4 @@ public class BelchTests
|
|||||||
// Assert
|
// Assert
|
||||||
await Assert.That(prevent).IsFalse();
|
await Assert.That(prevent).IsFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: outside of battle (no <see cref="IPokemon.BattleData"/>) the script does not prevent
|
|
||||||
/// selection.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task PreventMoveSelection_NoBattleData_SelectionAllowed()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var script = new Belch();
|
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
var choice = Substitute.For<IMoveChoice>();
|
|
||||||
choice.User.Returns(user);
|
|
||||||
var prevent = false;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.PreventMoveSelection(choice, ref prevent);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
await Assert.That(prevent).IsFalse();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -14,11 +14,11 @@ public class BellyDrumTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a fully mocked test setup for Belly Drum. The target of the secondary effect is the user itself.
|
/// Creates a fully mocked test setup for Belly Drum. The target of the secondary effect is the user itself.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (BellyDrum script, IExecutingMove move, IPokemon user, IHitData hitData) CreateTestSetup(uint maxHp,
|
private static (BellyDrum script, IExecutingMove move, IBattlePokemon user, IHitData hitData) CreateTestSetup(
|
||||||
uint currentHp, sbyte attackBoost = 0)
|
uint maxHp, uint currentHp, sbyte attackBoost = 0)
|
||||||
{
|
{
|
||||||
var script = new BellyDrum();
|
var script = new BellyDrum();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.BoostedStats.Returns(new StatisticSet<uint>(maxHp, 10, 10, 10, 10, 10));
|
user.BoostedStats.Returns(new StatisticSet<uint>(maxHp, 10, 10, 10, 10, 10));
|
||||||
user.CurrentHealth.Returns(currentHp);
|
user.CurrentHealth.Returns(currentHp);
|
||||||
user.StatBoost.Returns(new StatBoostStatisticSet(0, attackBoost, 0, 0, 0, 0));
|
user.StatBoost.Returns(new StatBoostStatisticSet(0, attackBoost, 0, 0, 0, 0));
|
||||||
@@ -34,7 +34,7 @@ public class BellyDrumTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the received Damage call from the user, if any.
|
/// Helper to extract the received Damage call from the user, if any.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (uint damage, DamageSource source, bool forceDamage)? GetDamageCall(IPokemon user)
|
private static (uint damage, DamageSource source, bool forceDamage)? GetDamageCall(IBattlePokemon user)
|
||||||
{
|
{
|
||||||
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
||||||
if (call == null)
|
if (call == null)
|
||||||
@@ -46,7 +46,7 @@ public class BellyDrumTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the received ChangeStatBoost call from the user, if any.
|
/// Helper to extract the received ChangeStatBoost call from the user, if any.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (Statistic stat, sbyte change, bool selfInflicted)? GetStatBoostCall(IPokemon user)
|
private static (Statistic stat, sbyte change, bool selfInflicted)? GetStatBoostCall(IBattlePokemon user)
|
||||||
{
|
{
|
||||||
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
||||||
if (call == null)
|
if (call == null)
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class BestowTests
|
public class BestowTests
|
||||||
{
|
{
|
||||||
private static (Bestow script, IExecutingMove move, IPokemon user, IPokemon target, IHitData hitData)
|
private static (Bestow script, IExecutingMove move, IBattlePokemon user, IBattlePokemon target, IHitData hitData)
|
||||||
CreateTestSetup(IItem? userItem, IItem? targetItem)
|
CreateTestSetup(IItem? userItem, IItem? targetItem)
|
||||||
{
|
{
|
||||||
var script = new Bestow();
|
var script = new Bestow();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
target.HeldItem.Returns(targetItem);
|
target.HeldItem.Returns(targetItem);
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.HeldItem.Returns(userItem);
|
user.HeldItem.Returns(userItem);
|
||||||
user.RemoveHeldItemForBattle().Returns(userItem);
|
user.RemoveHeldItemForBattle().Returns(userItem);
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
@@ -68,7 +68,7 @@ public class BestowTests
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bulbapedia: "Items given away in Trainer battles return to the original Pokémon after the battle."
|
/// Bulbapedia: "Items given away in Trainer battles return to the original Pokémon after the battle."
|
||||||
/// The item is taken from the user through <see cref="IPokemon.RemoveHeldItemForBattle"/>, which only
|
/// The item is taken from the user through <see cref="IBattlePokemon.RemoveHeldItemForBattle"/>, which only
|
||||||
/// removes the item for the duration of the battle.
|
/// removes the item for the duration of the battle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -13,16 +13,16 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class BideTests
|
public class BideTests
|
||||||
{
|
{
|
||||||
private static (Bide script, IExecutingMove move, IPokemon user, IPokemon target, IScriptSet userVolatile, IHitData
|
private static (Bide script, IExecutingMove move, IBattlePokemon user, IBattlePokemon target, IScriptSet
|
||||||
hitData) CreateTestSetup()
|
userVolatile, IHitData hitData) CreateTestSetup()
|
||||||
{
|
{
|
||||||
var script = new Bide();
|
var script = new Bide();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
|
user.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
|
||||||
// A real script set so the volatile Bide effect can actually be added, retrieved and removed.
|
// A real script set so the volatile Bide effect can actually be added, retrieved and removed.
|
||||||
var userVolatile = new ScriptSet(user);
|
var userVolatile = new ScriptSet(user);
|
||||||
@@ -32,12 +32,10 @@ public class BideTests
|
|||||||
return (script, move, user, target, userVolatile, hitData);
|
return (script, move, user, target, userVolatile, hitData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IPokemon CreateAttacker(bool onBattlefield = true)
|
private static IBattlePokemon CreateAttacker(bool onBattlefield = true)
|
||||||
{
|
{
|
||||||
var attacker = Substitute.For<IPokemon>();
|
var attacker = Substitute.For<IBattlePokemon>();
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
attacker.IsOnBattlefield.Returns(onBattlefield);
|
||||||
battleData.IsOnBattlefield.Returns(onBattlefield);
|
|
||||||
attacker.BattleData.Returns(battleData);
|
|
||||||
return attacker;
|
return attacker;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,8 +43,8 @@ public class BideTests
|
|||||||
/// Adds a <see cref="BideEffect"/> to the user's volatile scripts, as if Bide has already been storing
|
/// Adds a <see cref="BideEffect"/> to the user's volatile scripts, as if Bide has already been storing
|
||||||
/// energy for the given number of executed turns.
|
/// energy for the given number of executed turns.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static BideEffect AddStoredBideEffect(IScriptSet userVolatile, IPokemon user, byte turns, uint damageTaken,
|
private static BideEffect AddStoredBideEffect(IScriptSet userVolatile, IBattlePokemon user, byte turns,
|
||||||
params IPokemon[] hitBy)
|
uint damageTaken, params IBattlePokemon[] hitBy)
|
||||||
{
|
{
|
||||||
var effect = new BideEffect(user)
|
var effect = new BideEffect(user)
|
||||||
{
|
{
|
||||||
@@ -61,13 +59,13 @@ public class BideTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to check whether a Pokémon received any Damage call.
|
/// Helper to check whether a Pokémon received any Damage call.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static bool ReceivedDamage(IPokemon pokemon) =>
|
private static bool ReceivedDamage(IBattlePokemon pokemon) =>
|
||||||
pokemon.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Damage");
|
pokemon.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Damage");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the damage amount from a Pokémon's received Damage calls.
|
/// Helper to extract the damage amount from a Pokémon's received Damage calls.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static uint? GetDamageAmount(IPokemon pokemon)
|
private static uint? GetDamageAmount(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
||||||
return call != null ? (uint)call.GetArguments()[0]! : null;
|
return call != null ? (uint)call.GetArguments()[0]! : null;
|
||||||
@@ -76,7 +74,7 @@ public class BideTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the damage source from a Pokémon's received Damage calls.
|
/// Helper to extract the damage source from a Pokémon's received Damage calls.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static DamageSource? GetDamageSource(IPokemon pokemon)
|
private static DamageSource? GetDamageSource(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
||||||
return call != null ? (DamageSource)call.GetArguments()[1]! : null;
|
return call != null ? (DamageSource)call.GetArguments()[1]! : null;
|
||||||
@@ -245,7 +243,7 @@ public class BideTests
|
|||||||
public async Task BideEffect_OnDamage_AccumulatesDamageTaken()
|
public async Task BideEffect_OnDamage_AccumulatesDamageTaken()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
var effect = new BideEffect(user);
|
var effect = new BideEffect(user);
|
||||||
|
|
||||||
// Act - the user drops from 100 to 60 HP, then from 60 to 50 HP
|
// Act - the user drops from 100 to 60 HP, then from 60 to 50 HP
|
||||||
@@ -265,10 +263,10 @@ public class BideTests
|
|||||||
public async Task BideEffect_OnIncomingHit_RecordsAttacker()
|
public async Task BideEffect_OnIncomingHit_RecordsAttacker()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
var effect = new BideEffect(user);
|
var effect = new BideEffect(user);
|
||||||
var incomingMove = Substitute.For<IExecutingMove>();
|
var incomingMove = Substitute.For<IExecutingMove>();
|
||||||
var attacker = Substitute.For<IPokemon>();
|
var attacker = Substitute.For<IBattlePokemon>();
|
||||||
incomingMove.User.Returns(attacker);
|
incomingMove.User.Returns(attacker);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|||||||
@@ -44,16 +44,16 @@ public class BindTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (Bind script, IExecutingMove move, IPokemon user, IPokemon target, IScriptSet targetVolatile)
|
private static (Bind script, IExecutingMove move, IBattlePokemon user, IBattlePokemon target, IScriptSet
|
||||||
CreateTestSetup(params Script[] userScripts)
|
targetVolatile) CreateTestSetup(params Script[] userScripts)
|
||||||
{
|
{
|
||||||
var script = new Bind();
|
var script = new Bind();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var targetVolatile = Substitute.For<IScriptSet>();
|
var targetVolatile = Substitute.For<IScriptSet>();
|
||||||
target.Volatile.Returns(targetVolatile);
|
target.Volatile.Returns(targetVolatile);
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
// RunScriptHook iterates the user's scripts; give the mock a real iterator so the ModifyBind trigger
|
// RunScriptHook iterates the user's scripts; give the mock a real iterator so the ModifyBind trigger
|
||||||
// pass runs (empty unless the test attaches scripts such as the Grip Claw / Binding Band stand-in).
|
// pass runs (empty unless the test attaches scripts such as the Grip Claw / Binding Band stand-in).
|
||||||
var containers = userScripts.Select(IEnumerable<ScriptContainer> (s) => new ScriptContainer(s)).ToArray();
|
var containers = userScripts.Select(IEnumerable<ScriptContainer> (s) => new ScriptContainer(s)).ToArray();
|
||||||
@@ -73,7 +73,7 @@ public class BindTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the damage amount from the target's first received Damage call.
|
/// Helper to extract the damage amount from the target's first received Damage call.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static uint? GetDamageAmount(IPokemon pokemon)
|
private static uint? GetDamageAmount(IBattlePokemon pokemon)
|
||||||
{
|
{
|
||||||
var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
||||||
return call != null ? (uint)call.GetArguments()[0]! : null;
|
return call != null ? (uint)call.GetArguments()[0]! : null;
|
||||||
@@ -82,7 +82,7 @@ public class BindTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs end-of-turn handling on the effect repeatedly and counts how many turns dealt damage to the target.
|
/// Runs end-of-turn handling on the effect repeatedly and counts how many turns dealt damage to the target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static int CountEndTurnDamageTicks(BindEffect effect, IPokemon target, int maxTurns = 10)
|
private static int CountEndTurnDamageTicks(BindEffect effect, IBattlePokemon target, int maxTurns = 10)
|
||||||
{
|
{
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
for (var i = 0; i < maxTurns; i++)
|
for (var i = 0; i < maxTurns; i++)
|
||||||
@@ -204,7 +204,7 @@ public class BindTests
|
|||||||
public async Task BindEffect_WhileActive_PreventsSwitching()
|
public async Task BindEffect_WhileActive_PreventsSwitching()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var effect = new BindEffect(target, 5, 1f / 8f);
|
var effect = new BindEffect(target, 5, 1f / 8f);
|
||||||
var prevent = false;
|
var prevent = false;
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ public class BindTests
|
|||||||
public async Task BindEffect_WhileActive_PreventsRunningAway()
|
public async Task BindEffect_WhileActive_PreventsRunningAway()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var effect = new BindEffect(target, 5, 1f / 8f);
|
var effect = new BindEffect(target, 5, 1f / 8f);
|
||||||
var prevent = false;
|
var prevent = false;
|
||||||
|
|
||||||
@@ -242,7 +242,7 @@ public class BindTests
|
|||||||
public async Task BindEffect_AfterDurationExpires_NoLongerPreventsSwitching()
|
public async Task BindEffect_AfterDurationExpires_NoLongerPreventsSwitching()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
target.MaxHealth.Returns(160u);
|
target.MaxHealth.Returns(160u);
|
||||||
var effect = new BindEffect(target, 1, 1f / 8f);
|
var effect = new BindEffect(target, 1, 1f / 8f);
|
||||||
|
|
||||||
|
|||||||
@@ -14,14 +14,15 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class BlockTests
|
public class BlockTests
|
||||||
{
|
{
|
||||||
private static (Block script, IExecutingMove move, IPokemon target, ScriptSet targetVolatile) CreateTestSetup()
|
private static (Block script, IExecutingMove move, IBattlePokemon target, ScriptSet targetVolatile)
|
||||||
|
CreateTestSetup()
|
||||||
{
|
{
|
||||||
var script = new Block();
|
var script = new Block();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
// Use a real script set so the volatile script added by Block can be inspected afterwards.
|
// Use a real script set so the volatile script added by Block can be inspected afterwards.
|
||||||
var targetVolatile = new ScriptSet(target);
|
var targetVolatile = new ScriptSet(target);
|
||||||
target.Volatile.Returns(targetVolatile);
|
target.Volatile.Returns(targetVolatile);
|
||||||
@@ -105,10 +106,8 @@ public class BlockTests
|
|||||||
|
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
battle.Library.Returns(library);
|
battle.Library.Returns(library);
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
move.User.Battle.Returns(battle);
|
||||||
battleData.Battle.Returns(battle);
|
target.Battle.Returns(battle);
|
||||||
move.User.BattleData.Returns(battleData);
|
|
||||||
target.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class BounceTests
|
public class BounceTests
|
||||||
{
|
{
|
||||||
private static (Bounce script, IExecutingMove move, IPokemon user, ScriptSet userVolatile, IMoveChoice moveChoice,
|
private static (Bounce script, IExecutingMove move, IBattlePokemon user, ScriptSet userVolatile, IMoveChoice
|
||||||
IBattleRandom random) CreateTestSetup()
|
moveChoice, IBattleRandom random) CreateTestSetup()
|
||||||
{
|
{
|
||||||
var script = new Bounce();
|
var script = new Bounce();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
// Use a real script set so the charge volatile added by Bounce can be inspected afterwards.
|
// Use a real script set so the charge volatile added by Bounce can be inspected afterwards.
|
||||||
var userVolatile = new ScriptSet(user);
|
var userVolatile = new ScriptSet(user);
|
||||||
user.Volatile.Returns(userVolatile);
|
user.Volatile.Returns(userVolatile);
|
||||||
@@ -39,9 +39,7 @@ public class BounceTests
|
|||||||
var random = Substitute.For<IBattleRandom>();
|
var random = Substitute.For<IBattleRandom>();
|
||||||
battle.Random.Returns(random);
|
battle.Random.Returns(random);
|
||||||
battle.EventHook.Returns(new EventHook());
|
battle.EventHook.Returns(new EventHook());
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
user.Battle.Returns(battle);
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
return (script, move, user, userVolatile, moveChoice, random);
|
return (script, move, user, userVolatile, moveChoice, random);
|
||||||
}
|
}
|
||||||
@@ -148,7 +146,7 @@ public class BounceTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, user, _, _, random) = CreateTestSetup();
|
var (script, move, user, _, _, random) = CreateTestSetup();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
random.EffectChance(30, move, target, 0).Returns(true);
|
random.EffectChance(30, move, target, 0).Returns(true);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -168,7 +166,7 @@ public class BounceTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, _, _, _, random) = CreateTestSetup();
|
var (script, move, _, _, _, random) = CreateTestSetup();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
random.EffectChance(30, move, target, 0).Returns(false);
|
random.EffectChance(30, move, target, 0).Returns(false);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -187,7 +185,7 @@ public class BounceTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, _, _, _, random) = CreateTestSetup();
|
var (script, move, _, _, _, random) = CreateTestSetup();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
@@ -197,24 +195,6 @@ public class BounceTests
|
|||||||
await Assert.That(random.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "EffectChance")).IsTrue();
|
await Assert.That(random.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "EffectChance")).IsTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: outside of battle (no battle data) the secondary effect does nothing and does not throw.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task OnSecondaryEffect_NoBattleData_DoesNotParalyzeTarget()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var (script, move, user, _, _, _) = CreateTestSetup();
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
var target = Substitute.For<IPokemon>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "SetStatus")).IsFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bulbapedia: "While in Sky-High status, the user is invulnerable to most moves".
|
/// Bulbapedia: "While in Sky-High status, the user is invulnerable to most moves".
|
||||||
/// The <see cref="ChargeBounceEffect"/> added by the charge turn blocks incoming hits from moves that
|
/// The <see cref="ChargeBounceEffect"/> added by the charge turn blocks incoming hits from moves that
|
||||||
|
|||||||
@@ -15,21 +15,19 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
public class BrickBreakTests
|
public class BrickBreakTests
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a mocked Pokémon whose <see cref="IPokemonBattleData.BattleSide"/> is the given side.
|
/// Creates a mocked Pokémon whose <see cref="IBattlePokemon.BattleSide"/> is the given side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static IPokemon CreatePokemonOnSide(IBattleSide side)
|
private static IBattlePokemon CreatePokemonOnSide(IBattleSide side)
|
||||||
{
|
{
|
||||||
var pokemon = Substitute.For<IPokemon>();
|
var pokemon = Substitute.For<IBattlePokemon>();
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
pokemon.BattleSide.Returns(side);
|
||||||
battleData.BattleSide.Returns(side);
|
|
||||||
pokemon.BattleData.Returns(battleData);
|
|
||||||
return pokemon;
|
return pokemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a fully mocked test setup where the user targets a Pokémon on the opposing side.
|
/// Creates a fully mocked test setup where the user targets a Pokémon on the opposing side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (BrickBreak script, IExecutingMove move, IPokemon user, IScriptSet userSideScripts, IScriptSet
|
private static (BrickBreak script, IExecutingMove move, IBattlePokemon user, IScriptSet userSideScripts, IScriptSet
|
||||||
targetSideScripts) CreateTestSetup()
|
targetSideScripts) CreateTestSetup()
|
||||||
{
|
{
|
||||||
var script = new BrickBreak();
|
var script = new BrickBreak();
|
||||||
@@ -45,7 +43,7 @@ public class BrickBreakTests
|
|||||||
var user = CreatePokemonOnSide(userSide);
|
var user = CreatePokemonOnSide(userSide);
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var target = CreatePokemonOnSide(targetSide);
|
var target = CreatePokemonOnSide(targetSide);
|
||||||
move.Targets.Returns(new IPokemon?[] { target });
|
move.Targets.Returns(new IBattlePokemon?[] { target });
|
||||||
|
|
||||||
return (script, move, user, userSideScripts, targetSideScripts);
|
return (script, move, user, userSideScripts, targetSideScripts);
|
||||||
}
|
}
|
||||||
@@ -115,8 +113,8 @@ public class BrickBreakTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, user, userSideScripts, _) = CreateTestSetup();
|
var (script, move, user, userSideScripts, _) = CreateTestSetup();
|
||||||
var ally = CreatePokemonOnSide(user.BattleData!.BattleSide);
|
var ally = CreatePokemonOnSide(user.BattleSide);
|
||||||
move.Targets.Returns(new IPokemon?[] { ally });
|
move.Targets.Returns(new IBattlePokemon?[] { ally });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnBeforeMove(move);
|
script.OnBeforeMove(move);
|
||||||
@@ -146,7 +144,7 @@ public class BrickBreakTests
|
|||||||
var user = CreatePokemonOnSide(userSide);
|
var user = CreatePokemonOnSide(userSide);
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var target = CreatePokemonOnSide(targetSide);
|
var target = CreatePokemonOnSide(targetSide);
|
||||||
move.Targets.Returns(new IPokemon?[] { target });
|
move.Targets.Returns(new IBattlePokemon?[] { target });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnBeforeMove(move);
|
script.OnBeforeMove(move);
|
||||||
@@ -165,10 +163,9 @@ public class BrickBreakTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var script = new BrickBreak();
|
var script = new BrickBreak();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
move.Targets.Returns(Array.Empty<IPokemon?>());
|
move.Targets.Returns(Array.Empty<IBattlePokemon?>());
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await Assert.That(() => script.OnBeforeMove(move)).ThrowsNothing();
|
await Assert.That(() => script.OnBeforeMove(move)).ThrowsNothing();
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ public class BrineTests
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a fully mocked test setup for Brine tests, with a target whose max HP
|
/// Creates a fully mocked test setup for Brine tests, with a target whose max HP
|
||||||
/// (<see cref="IPokemon.BoostedStats"/>) and <see cref="IPokemon.CurrentHealth"/> are configured.
|
/// (<see cref="IBattlePokemon.BoostedStats"/>) and <see cref="IBattlePokemon.CurrentHealth"/> are configured.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (Brine brine, IExecutingMove move, IPokemon target) CreateTestSetup(uint maxHp, uint currentHp)
|
private static (Brine brine, IExecutingMove move, IBattlePokemon target) CreateTestSetup(uint maxHp, uint currentHp)
|
||||||
{
|
{
|
||||||
var brine = new Brine();
|
var brine = new Brine();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
target.BoostedStats.Returns(new StatisticSet<uint>(maxHp, 0, 0, 0, 0, 0));
|
target.BoostedStats.Returns(new StatisticSet<uint>(maxHp, 0, 0, 0, 0, 0));
|
||||||
target.CurrentHealth.Returns(currentHp);
|
target.CurrentHealth.Returns(currentHp);
|
||||||
return (brine, move, target);
|
return (brine, move, target);
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ public class BugBiteTests
|
|||||||
/// real resolver with a single registered item script constructor for <see cref="BerryEffectName"/>, so
|
/// real resolver with a single registered item script constructor for <see cref="BerryEffectName"/>, so
|
||||||
/// eating a Berry runs a <see cref="RecordingItemScript"/> that the test can inspect.
|
/// eating a Berry runs a <see cref="RecordingItemScript"/> that the test can inspect.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (BugBite bugBite, IExecutingMove move, IPokemon target, IHitData hitData, List<RecordingItemScript>
|
private static (BugBite bugBite, IExecutingMove move, IBattlePokemon target, IHitData hitData,
|
||||||
createdItemScripts) CreateTestSetup(IItem? targetHeldItem, bool canSteal = true)
|
List<RecordingItemScript> createdItemScripts) CreateTestSetup(IItem? targetHeldItem, bool canSteal = true)
|
||||||
{
|
{
|
||||||
var bugBite = new BugBite();
|
var bugBite = new BugBite();
|
||||||
|
|
||||||
@@ -66,17 +66,14 @@ public class BugBiteTests
|
|||||||
dynamicLibrary.ScriptResolver.Returns(resolver);
|
dynamicLibrary.ScriptResolver.Returns(resolver);
|
||||||
battle.Library.Returns(dynamicLibrary);
|
battle.Library.Returns(dynamicLibrary);
|
||||||
|
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
|
user.Battle.Returns(battle);
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
move.Battle.Returns(battle);
|
move.Battle.Returns(battle);
|
||||||
|
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
target.HeldItem.Returns(targetHeldItem);
|
target.HeldItem.Returns(targetHeldItem);
|
||||||
if (targetHeldItem != null && canSteal)
|
if (targetHeldItem != null && canSteal)
|
||||||
{
|
{
|
||||||
@@ -111,7 +108,7 @@ public class BugBiteTests
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bulbapedia: "If the target is holding a Berry, the user will eat the Berry and gain its effect."
|
/// Bulbapedia: "If the target is holding a Berry, the user will eat the Berry and gain its effect."
|
||||||
/// Eating the Berry removes it from the target via <see cref="IPokemon.ForceSetHeldItem"/>.
|
/// Eating the Berry removes it from the target via <see cref="IBattlePokemon.ForceSetHeldItem"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task OnSecondaryEffect_TargetHoldsBerry_BerryIsRemovedFromTarget()
|
public async Task OnSecondaryEffect_TargetHoldsBerry_BerryIsRemovedFromTarget()
|
||||||
@@ -202,7 +199,7 @@ public class BugBiteTests
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bulbapedia: "Bug Bite will not consume the Berry of a target that has the Ability Sticky Hold."
|
/// Bulbapedia: "Bug Bite will not consume the Berry of a target that has the Ability Sticky Hold."
|
||||||
/// When the Berry cannot be stolen (<see cref="IPokemon.TryStealHeldItem"/> returns false, as with
|
/// When the Berry cannot be stolen (<see cref="IBattlePokemon.TryStealHeldItem"/> returns false, as with
|
||||||
/// Sticky Hold), the target keeps its Berry and the effect fails.
|
/// Sticky Hold), the target keeps its Berry and the effect fails.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
@@ -219,25 +216,4 @@ public class BugBiteTests
|
|||||||
await Assert.That(createdItemScripts.Count).IsEqualTo(0);
|
await Assert.That(createdItemScripts.Count).IsEqualTo(0);
|
||||||
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsTrue();
|
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If the user has no <see cref="IPokemon.BattleData"/>, the script does nothing: no Berry is eaten
|
|
||||||
/// and the hit is not failed.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task OnSecondaryEffect_UserHasNoBattleData_DoesNothing()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var (bugBite, move, target, hitData, _) = CreateTestSetup(CreateBerry());
|
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
move.User.Returns(user);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
bugBite.OnSecondaryEffect(move, target, 0);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ForceSetHeldItem")).IsFalse();
|
|
||||||
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsFalse();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -18,8 +18,8 @@ public class BurnUpTests
|
|||||||
/// <see cref="TypeLibrary"/> with "fire" and "water" registered. The user is a Water-type, optionally
|
/// <see cref="TypeLibrary"/> with "fire" and "water" registered. The user is a Water-type, optionally
|
||||||
/// also carrying the Fire type.
|
/// also carrying the Fire type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (BurnUp burnUp, IExecutingMove move, IPokemon user, IPokemon target, IHitData hitData, TypeIdentifier
|
private static (BurnUp burnUp, IExecutingMove move, IBattlePokemon user, IBattlePokemon target, IHitData hitData,
|
||||||
fireType) CreateTestSetup(bool userIsFireType, bool userIsFrozen = false)
|
TypeIdentifier fireType) CreateTestSetup(bool userIsFireType, bool userIsFrozen = false)
|
||||||
{
|
{
|
||||||
var burnUp = new BurnUp();
|
var burnUp = new BurnUp();
|
||||||
|
|
||||||
@@ -33,11 +33,9 @@ public class BurnUpTests
|
|||||||
dynamicLibrary.StaticLibrary.Returns(staticLibrary);
|
dynamicLibrary.StaticLibrary.Returns(staticLibrary);
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
battle.Library.Returns(dynamicLibrary);
|
battle.Library.Returns(dynamicLibrary);
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.BattleData.Returns(battleData);
|
user.Battle.Returns(battle);
|
||||||
user.Types.Returns(userIsFireType
|
user.Types.Returns(userIsFireType
|
||||||
? new List<TypeIdentifier> { fireType, waterType }
|
? new List<TypeIdentifier> { fireType, waterType }
|
||||||
: new List<TypeIdentifier> { waterType });
|
: new List<TypeIdentifier> { waterType });
|
||||||
@@ -46,7 +44,7 @@ public class BurnUpTests
|
|||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
|
|
||||||
@@ -54,9 +52,9 @@ public class BurnUpTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper that checks whether <see cref="IPokemon.RemoveType"/> was called with the given type.
|
/// Helper that checks whether <see cref="IBattlePokemon.RemoveType"/> was called with the given type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static bool ReceivedRemoveType(IPokemon user, TypeIdentifier type) =>
|
private static bool ReceivedRemoveType(IBattlePokemon user, TypeIdentifier type) =>
|
||||||
user.ReceivedCalls().Any(c =>
|
user.ReceivedCalls().Any(c =>
|
||||||
c.GetMethodInfo().Name == "RemoveType" && type.Equals((TypeIdentifier)c.GetArguments()[0]!));
|
c.GetMethodInfo().Name == "RemoveType" && type.Equals((TypeIdentifier)c.GetArguments()[0]!));
|
||||||
|
|
||||||
@@ -177,23 +175,4 @@ public class BurnUpTests
|
|||||||
// Assert
|
// Assert
|
||||||
await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ClearStatus")).IsFalse();
|
await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ClearStatus")).IsFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If the user has no <see cref="IPokemon.BattleData"/>, the script does nothing: no type is removed
|
|
||||||
/// and the hit is not failed.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task OnSecondaryEffect_UserHasNoBattleData_DoesNothing()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var (burnUp, move, user, target, hitData, _) = CreateTestSetup(true);
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
burnUp.OnSecondaryEffect(move, target, 0);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "RemoveType")).IsFalse();
|
|
||||||
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsFalse();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class CamouflageTests
|
public class CamouflageTests
|
||||||
{
|
{
|
||||||
private static (Camouflage script, IExecutingMove move, IPokemon user, IBattle battle) CreateTestSetup(
|
private static (Camouflage script, IExecutingMove move, IBattlePokemon user, IBattle battle) CreateTestSetup(
|
||||||
string? terrainName, string environmentName)
|
string? terrainName, string environmentName)
|
||||||
{
|
{
|
||||||
var script = new Camouflage();
|
var script = new Camouflage();
|
||||||
@@ -24,7 +24,7 @@ public class CamouflageTests
|
|||||||
battle.TerrainName.Returns(terrainName == null ? null : new StringKey?(new StringKey(terrainName)));
|
battle.TerrainName.Returns(terrainName == null ? null : new StringKey?(new StringKey(terrainName)));
|
||||||
battle.EnvironmentName.Returns(new StringKey(environmentName));
|
battle.EnvironmentName.Returns(new StringKey(environmentName));
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
move.Battle.Returns(battle);
|
move.Battle.Returns(battle);
|
||||||
@@ -34,9 +34,9 @@ public class CamouflageTests
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper that returns the single type the user was changed to, or null when
|
/// Helper that returns the single type the user was changed to, or null when
|
||||||
/// <see cref="IPokemon.SetTypes"/> was never called.
|
/// <see cref="IBattlePokemon.SetTypes"/> was never called.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static TypeIdentifier? GetSetType(IPokemon user)
|
private static TypeIdentifier? GetSetType(IBattlePokemon user)
|
||||||
{
|
{
|
||||||
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "SetTypes");
|
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "SetTypes");
|
||||||
return call != null ? ((IReadOnlyList<TypeIdentifier>)call.GetArguments()[0]!).Single() : null;
|
return call != null ? ((IReadOnlyList<TypeIdentifier>)call.GetArguments()[0]!).Single() : null;
|
||||||
@@ -56,7 +56,7 @@ public class CamouflageTests
|
|||||||
battle.Library.StaticLibrary.Types.TryGetTypeIdentifier(expectedType, out var expected);
|
battle.Library.StaticLibrary.Types.TryGetTypeIdentifier(expectedType, out var expected);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(GetSetType(user)!.Value).IsEqualTo(expected);
|
await Assert.That(GetSetType(user)!.Value).IsEqualTo(expected);
|
||||||
@@ -75,7 +75,7 @@ public class CamouflageTests
|
|||||||
battle.Library.StaticLibrary.Types.TryGetTypeIdentifier(expectedType, out var expected);
|
battle.Library.StaticLibrary.Types.TryGetTypeIdentifier(expectedType, out var expected);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(GetSetType(user)!.Value).IsEqualTo(expected);
|
await Assert.That(GetSetType(user)!.Value).IsEqualTo(expected);
|
||||||
@@ -92,7 +92,7 @@ public class CamouflageTests
|
|||||||
battle.Library.StaticLibrary.Types.TryGetTypeIdentifier("normal", out var expected);
|
battle.Library.StaticLibrary.Types.TryGetTypeIdentifier("normal", out var expected);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(GetSetType(user)!.Value).IsEqualTo(expected);
|
await Assert.That(GetSetType(user)!.Value).IsEqualTo(expected);
|
||||||
@@ -107,7 +107,7 @@ public class CamouflageTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, _, _) = CreateTestSetup(null, "field");
|
var (script, move, _, _) = CreateTestSetup(null, "field");
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
|
|||||||
@@ -14,16 +14,16 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class CaptivateTests
|
public class CaptivateTests
|
||||||
{
|
{
|
||||||
private static (Captivate script, IExecutingMove move, IPokemon user, IPokemon target, IHitData hitData)
|
private static (Captivate script, IExecutingMove move, IBattlePokemon user, IBattlePokemon target, IHitData hitData)
|
||||||
CreateTestSetup(Gender userGender, Gender targetGender)
|
CreateTestSetup(Gender userGender, Gender targetGender)
|
||||||
{
|
{
|
||||||
var script = new Captivate();
|
var script = new Captivate();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.Gender.Returns(userGender);
|
user.Gender.Returns(userGender);
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
target.Gender.Returns(targetGender);
|
target.Gender.Returns(targetGender);
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
@@ -34,7 +34,7 @@ public class CaptivateTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper that checks whether a stat boost change was applied to the given Pokémon.
|
/// Helper that checks whether a stat boost change was applied to the given Pokémon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static bool ReceivedStatBoost(IPokemon pokemon, Statistic stat, sbyte amount) =>
|
private static bool ReceivedStatBoost(IBattlePokemon pokemon, Statistic stat, sbyte amount) =>
|
||||||
pokemon.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ChangeStatBoost" &&
|
pokemon.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ChangeStatBoost" &&
|
||||||
(Statistic)c.GetArguments()[0]! == stat &&
|
(Statistic)c.GetArguments()[0]! == stat &&
|
||||||
(sbyte)c.GetArguments()[1]! == amount);
|
(sbyte)c.GetArguments()[1]! == amount);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class ChangeTargetSpecialDefenseTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the arguments of the ChangeStatBoost call received by a substitute target.
|
/// Helper to extract the arguments of the ChangeStatBoost call received by a substitute target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static object?[]? GetStatBoostCallArgs(IPokemon target)
|
private static object?[]? GetStatBoostCallArgs(IBattlePokemon target)
|
||||||
{
|
{
|
||||||
var call = target.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
var call = target.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
||||||
return call?.GetArguments();
|
return call?.GetArguments();
|
||||||
@@ -41,9 +41,9 @@ public class ChangeTargetSpecialDefenseTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var script = CreateInitializedScript();
|
var script = CreateInitializedScript();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
@@ -66,9 +66,9 @@ public class ChangeTargetSpecialDefenseTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var script = CreateInitializedScript();
|
var script = CreateInitializedScript();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
@@ -90,9 +90,9 @@ public class ChangeTargetSpecialDefenseTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var script = CreateInitializedScript();
|
var script = CreateInitializedScript();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
@@ -114,9 +114,9 @@ public class ChangeTargetSpecialDefenseTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var script = CreateInitializedScript();
|
var script = CreateInitializedScript();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
@@ -137,7 +137,7 @@ public class ChangeTargetSpecialDefenseTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var script = CreateInitializedScript();
|
var script = CreateInitializedScript();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
|
||||||
// Act - the user is hit by its own move (e.g. redirected)
|
// Act - the user is hit by its own move (e.g. redirected)
|
||||||
@@ -159,9 +159,9 @@ public class ChangeTargetSpecialDefenseTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var script = CreateInitializedScript(amount);
|
var script = CreateInitializedScript(amount);
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
script.OnSecondaryEffect(move, target, 0);
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChargeTests
|
public class ChargeTests
|
||||||
{
|
{
|
||||||
private static (Charge script, IExecutingMove move, IPokemon user, IScriptSet userVolatile) CreateTestSetup()
|
private static (Charge script, IExecutingMove move, IBattlePokemon user, IScriptSet userVolatile) CreateTestSetup()
|
||||||
{
|
{
|
||||||
var script = new Charge();
|
var script = new Charge();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
// Use a real script set so the volatile script added by Charge can be inspected afterwards.
|
// Use a real script set so the volatile script added by Charge can be inspected afterwards.
|
||||||
var userVolatile = new ScriptSet(user);
|
var userVolatile = new ScriptSet(user);
|
||||||
user.Volatile.Returns(userVolatile);
|
user.Volatile.Returns(userVolatile);
|
||||||
@@ -32,16 +32,14 @@ public class ChargeTests
|
|||||||
/// Creates an executing move of the given type whose damage modifier can be changed by
|
/// Creates an executing move of the given type whose damage modifier can be changed by
|
||||||
/// <see cref="ChargeEffect.ChangeDamageModifier"/>.
|
/// <see cref="ChargeEffect.ChangeDamageModifier"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (IExecutingMove move, IPokemon target) CreateExecutingMoveOfType(string typeName)
|
private static (IExecutingMove move, IBattlePokemon target) CreateExecutingMoveOfType(string typeName)
|
||||||
{
|
{
|
||||||
var library = LibraryHelpers.LoadLibrary();
|
var library = LibraryHelpers.LoadLibrary();
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
battle.Library.Returns(library);
|
battle.Library.Returns(library);
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
var target = Substitute.For<IPokemon>();
|
|
||||||
target.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
|
target.Battle.Returns(battle);
|
||||||
library.StaticLibrary.Types.TryGetTypeIdentifier(typeName, out var typeIdentifier);
|
library.StaticLibrary.Types.TryGetTypeIdentifier(typeName, out var typeIdentifier);
|
||||||
var useMove = Substitute.For<IMoveData>();
|
var useMove = Substitute.For<IMoveData>();
|
||||||
useMove.MoveType.Returns(typeIdentifier);
|
useMove.MoveType.Returns(typeIdentifier);
|
||||||
@@ -61,7 +59,7 @@ public class ChargeTests
|
|||||||
var (script, move, user, _) = CreateTestSetup();
|
var (script, move, user, _) = CreateTestSetup();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var boost = user.ReceivedCalls().SingleOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
var boost = user.ReceivedCalls().SingleOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
||||||
@@ -82,7 +80,7 @@ public class ChargeTests
|
|||||||
var (script, move, _, userVolatile) = CreateTestSetup();
|
var (script, move, _, userVolatile) = CreateTestSetup();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(userVolatile.Contains(ScriptUtils.ResolveName<ChargeEffect>())).IsTrue();
|
await Assert.That(userVolatile.Contains(ScriptUtils.ResolveName<ChargeEffect>())).IsTrue();
|
||||||
@@ -134,7 +132,7 @@ public class ChargeTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, user, userVolatile) = CreateTestSetup();
|
var (script, move, user, userVolatile) = CreateTestSetup();
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
await Assert.That(userVolatile.TryGet<ChargeEffect>(out var effect)).IsTrue();
|
await Assert.That(userVolatile.TryGet<ChargeEffect>(out var effect)).IsTrue();
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ public class ChipAwayTests
|
|||||||
var bypass = false;
|
var bypass = false;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.BypassDefensiveStatBoosts(Substitute.For<IExecutingMove>(), Substitute.For<IPokemon>(), 0, ref bypass);
|
script.BypassDefensiveStatBoosts(Substitute.For<IExecutingMove>(), Substitute.For<IBattlePokemon>(), 0,
|
||||||
|
ref bypass);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(bypass).IsTrue();
|
await Assert.That(bypass).IsTrue();
|
||||||
@@ -40,7 +41,8 @@ public class ChipAwayTests
|
|||||||
var bypass = false;
|
var bypass = false;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.BypassEvasionStatBoosts(Substitute.For<IExecutingMove>(), Substitute.For<IPokemon>(), 0, ref bypass);
|
script.BypassEvasionStatBoosts(Substitute.For<IExecutingMove>(), Substitute.For<IBattlePokemon>(), 0,
|
||||||
|
ref bypass);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(bypass).IsTrue();
|
await Assert.That(bypass).IsTrue();
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ public class Conversion2Tests
|
|||||||
{
|
{
|
||||||
private static readonly IDynamicLibrary Library = LibraryHelpers.LoadLibrary();
|
private static readonly IDynamicLibrary Library = LibraryHelpers.LoadLibrary();
|
||||||
|
|
||||||
private static (Conversion2 script, IExecutingMove move, IPokemon user, IPokemon target, IHitData hitData)
|
private static (Conversion2 script, IExecutingMove move, IBattlePokemon user, IBattlePokemon target, IHitData
|
||||||
CreateTestSetup(TypeIdentifier? lastMoveType)
|
hitData) CreateTestSetup(TypeIdentifier? lastMoveType)
|
||||||
{
|
{
|
||||||
var script = new Conversion2();
|
var script = new Conversion2();
|
||||||
|
|
||||||
@@ -30,15 +30,13 @@ public class Conversion2Tests
|
|||||||
battle.Library.Returns(Library);
|
battle.Library.Returns(Library);
|
||||||
battle.Random.Returns(random);
|
battle.Random.Returns(random);
|
||||||
|
|
||||||
var userBattleData = Substitute.For<IPokemonBattleData>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
userBattleData.Battle.Returns(battle);
|
user.Battle.Returns(battle);
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.BattleData.Returns(userBattleData);
|
|
||||||
|
|
||||||
var targetBattleData = Substitute.For<IPokemonBattleData>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
if (lastMoveType == null)
|
if (lastMoveType == null)
|
||||||
{
|
{
|
||||||
targetBattleData.LastMoveChoice.Returns((IMoveChoice?)null);
|
target.LastMoveChoice.Returns((IMoveChoice?)null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -48,12 +46,9 @@ public class Conversion2Tests
|
|||||||
learnedMove.MoveData.Returns(moveData);
|
learnedMove.MoveData.Returns(moveData);
|
||||||
var lastChoice = Substitute.For<IMoveChoice>();
|
var lastChoice = Substitute.For<IMoveChoice>();
|
||||||
lastChoice.ChosenMove.Returns(learnedMove);
|
lastChoice.ChosenMove.Returns(learnedMove);
|
||||||
targetBattleData.LastMoveChoice.Returns(lastChoice);
|
target.LastMoveChoice.Returns(lastChoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
var target = Substitute.For<IPokemon>();
|
|
||||||
target.BattleData.Returns(targetBattleData);
|
|
||||||
|
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
@@ -70,9 +65,9 @@ public class Conversion2Tests
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper that returns the single type the user was changed to, or null when
|
/// Helper that returns the single type the user was changed to, or null when
|
||||||
/// <see cref="IPokemon.SetTypes"/> was never called.
|
/// <see cref="IBattlePokemon.SetTypes"/> was never called.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static TypeIdentifier? GetSetType(IPokemon user)
|
private static TypeIdentifier? GetSetType(IBattlePokemon user)
|
||||||
{
|
{
|
||||||
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "SetTypes");
|
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "SetTypes");
|
||||||
return call != null ? ((IReadOnlyList<TypeIdentifier>)call.GetArguments()[0]!).Single() : null;
|
return call != null ? ((IReadOnlyList<TypeIdentifier>)call.GetArguments()[0]!).Single() : null;
|
||||||
|
|||||||
@@ -22,15 +22,15 @@ public class ConversionTests
|
|||||||
return learned;
|
return learned;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (Conversion script, IExecutingMove move, IPokemon user, IPokemon target, IHitData hitData)
|
private static (Conversion script, IExecutingMove move, IBattlePokemon user, IBattlePokemon target, IHitData hitData
|
||||||
CreateTestSetup(params ILearnedMove?[] moves)
|
) CreateTestSetup(params ILearnedMove?[] moves)
|
||||||
{
|
{
|
||||||
var script = new Conversion();
|
var script = new Conversion();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.Moves.Returns(moves);
|
user.Moves.Returns(moves);
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
return (script, move, user, target, hitData);
|
return (script, move, user, target, hitData);
|
||||||
|
|||||||
@@ -17,11 +17,10 @@ public class CopycatTests
|
|||||||
private static (Copycat script, IMoveChoice choice) CreateTestSetup(string? lastMoveName)
|
private static (Copycat script, IMoveChoice choice) CreateTestSetup(string? lastMoveName)
|
||||||
{
|
{
|
||||||
var script = new Copycat();
|
var script = new Copycat();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
|
||||||
if (lastMoveName == null)
|
if (lastMoveName == null)
|
||||||
{
|
{
|
||||||
battleData.LastMoveChoice.Returns((IMoveChoice?)null);
|
user.LastMoveChoice.Returns((IMoveChoice?)null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -31,9 +30,8 @@ public class CopycatTests
|
|||||||
learnedMove.MoveData.Returns(moveData);
|
learnedMove.MoveData.Returns(moveData);
|
||||||
var lastChoice = Substitute.For<IMoveChoice>();
|
var lastChoice = Substitute.For<IMoveChoice>();
|
||||||
lastChoice.ChosenMove.Returns(learnedMove);
|
lastChoice.ChosenMove.Returns(learnedMove);
|
||||||
battleData.LastMoveChoice.Returns(lastChoice);
|
user.LastMoveChoice.Returns(lastChoice);
|
||||||
}
|
}
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
var choice = Substitute.For<IMoveChoice>();
|
var choice = Substitute.For<IMoveChoice>();
|
||||||
choice.User.Returns(user);
|
choice.User.Returns(user);
|
||||||
@@ -94,25 +92,4 @@ public class CopycatTests
|
|||||||
choice.Received(1).Fail();
|
choice.Received(1).Fail();
|
||||||
await Assert.That(moveName).IsEqualTo(new StringKey("copycat"));
|
await Assert.That(moveName).IsEqualTo(new StringKey("copycat"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: outside of battle (no battle data) there is no last move, so Copycat fails.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void ChangeMove_NoBattleData_Fails()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var script = new Copycat();
|
|
||||||
var user = Substitute.For<IPokemon>();
|
|
||||||
user.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
var choice = Substitute.For<IMoveChoice>();
|
|
||||||
choice.User.Returns(user);
|
|
||||||
StringKey moveName = "copycat";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.ChangeMove(choice, ref moveName);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
choice.Received(1).Fail();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -14,16 +14,14 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class CoreEnforcerTests
|
public class CoreEnforcerTests
|
||||||
{
|
{
|
||||||
private static (CoreEnforcer script, IExecutingMove move, IMoveChoice currentChoice, IPokemon target, IHitData
|
private static (CoreEnforcer script, IExecutingMove move, IMoveChoice currentChoice, IBattlePokemon target, IHitData
|
||||||
hitData, IBattle battle) CreateTestSetup()
|
hitData, IBattle battle) CreateTestSetup()
|
||||||
{
|
{
|
||||||
var script = new CoreEnforcer();
|
var script = new CoreEnforcer();
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
var target = Substitute.For<IPokemon>();
|
|
||||||
target.BattleData.Returns(battleData);
|
|
||||||
|
|
||||||
|
target.Battle.Returns(battle);
|
||||||
var currentChoice = Substitute.For<IMoveChoice>();
|
var currentChoice = Substitute.For<IMoveChoice>();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
move.MoveChoice.Returns(currentChoice);
|
move.MoveChoice.Returns(currentChoice);
|
||||||
@@ -120,24 +118,6 @@ public class CoreEnforcerTests
|
|||||||
hitData.Received(1).Fail();
|
hitData.Received(1).Fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: a target without battle data (not in battle) is left untouched.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void OnSecondaryEffect_TargetHasNoBattleData_DoesNothing()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var (script, move, _, target, hitData, _) = CreateTestSetup();
|
|
||||||
target.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.OnSecondaryEffect(move, target, 0);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
target.DidNotReceive().SuppressAbility();
|
|
||||||
hitData.DidNotReceive().Fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bulbapedia: the condition is that "the target has already used a move" — an action by a different
|
/// Bulbapedia: the condition is that "the target has already used a move" — an action by a different
|
||||||
/// Pokémon (e.g. the user's ally in a Double Battle) does not count as the target having acted.
|
/// Pokémon (e.g. the user's ally in a Double Battle) does not count as the target having acted.
|
||||||
@@ -148,7 +128,7 @@ public class CoreEnforcerTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var (script, move, currentChoice, target, hitData, battle) = CreateTestSetup();
|
var (script, move, currentChoice, target, hitData, battle) = CreateTestSetup();
|
||||||
var allyChoice = Substitute.For<IMoveChoice>();
|
var allyChoice = Substitute.For<IMoveChoice>();
|
||||||
allyChoice.User.Returns(Substitute.For<IPokemon>());
|
allyChoice.User.Returns(Substitute.For<IBattlePokemon>());
|
||||||
SetTurnChoices(battle, allyChoice, currentChoice);
|
SetTurnChoices(battle, allyChoice, currentChoice);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -162,7 +142,7 @@ public class CoreEnforcerTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bulbapedia: "The move cannot suppress certain signature abilities including Multitype, Stance
|
/// Bulbapedia: "The move cannot suppress certain signature abilities including Multitype, Stance
|
||||||
/// Change, Schooling, Comatose, Shields Down, Disguise, RKS System, Battle Bond, Power Construct".
|
/// Change, Schooling, Comatose, Shields Down, Disguise, RKS System, Battle Bond, Power Construct".
|
||||||
/// The script requests the suppression unconditionally; <see cref="IPokemon.SuppressAbility"/> refuses
|
/// The script requests the suppression unconditionally; <see cref="IBattlePokemon.SuppressAbility"/> refuses
|
||||||
/// it when <see cref="IAbility.CanBeChanged"/> is false, so these abilities must carry that flag in the
|
/// it when <see cref="IAbility.CanBeChanged"/> is false, so these abilities must carry that flag in the
|
||||||
/// Gen7 data.
|
/// Gen7 data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ public class CounterTests
|
|||||||
/// Creates a user whose volatile scripts contain a <see cref="CounterHelperEffect"/> that has recorded
|
/// Creates a user whose volatile scripts contain a <see cref="CounterHelperEffect"/> that has recorded
|
||||||
/// an incoming physical hit of the given damage by <paramref name="attacker"/>.
|
/// an incoming physical hit of the given damage by <paramref name="attacker"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static IPokemon CreateUserHitBy(IPokemon? attacker, uint damage, bool physical = true)
|
private static IBattlePokemon CreateUserHitBy(IBattlePokemon? attacker, uint damage, bool physical = true)
|
||||||
{
|
{
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
var userVolatile = new ScriptSet(user);
|
var userVolatile = new ScriptSet(user);
|
||||||
user.Volatile.Returns(userVolatile);
|
user.Volatile.Returns(userVolatile);
|
||||||
user.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
|
user.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
|
||||||
@@ -55,7 +55,7 @@ public class CounterTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var script = new Counter();
|
var script = new Counter();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
var userVolatile = new ScriptSet(user);
|
var userVolatile = new ScriptSet(user);
|
||||||
user.Volatile.Returns(userVolatile);
|
user.Volatile.Returns(userVolatile);
|
||||||
user.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
|
user.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
|
||||||
@@ -78,11 +78,11 @@ public class CounterTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var script = new Counter();
|
var script = new Counter();
|
||||||
var attacker = Substitute.For<IPokemon>();
|
var attacker = Substitute.For<IBattlePokemon>();
|
||||||
var user = CreateUserHitBy(attacker, 40);
|
var user = CreateUserHitBy(attacker, 40);
|
||||||
var choice = Substitute.For<IMoveChoice>();
|
var choice = Substitute.For<IMoveChoice>();
|
||||||
choice.User.Returns(user);
|
choice.User.Returns(user);
|
||||||
IReadOnlyList<IPokemon?> targets = new IPokemon?[] { Substitute.For<IPokemon>() };
|
IReadOnlyList<IBattlePokemon?> targets = new IBattlePokemon?[] { Substitute.For<IBattlePokemon>() };
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.ChangeTargets(choice, ref targets);
|
script.ChangeTargets(choice, ref targets);
|
||||||
@@ -103,7 +103,7 @@ public class CounterTests
|
|||||||
var user = CreateUserHitBy(null, 0);
|
var user = CreateUserHitBy(null, 0);
|
||||||
var choice = Substitute.For<IMoveChoice>();
|
var choice = Substitute.For<IMoveChoice>();
|
||||||
choice.User.Returns(user);
|
choice.User.Returns(user);
|
||||||
IReadOnlyList<IPokemon?> targets = new IPokemon?[] { Substitute.For<IPokemon>() };
|
IReadOnlyList<IBattlePokemon?> targets = new IBattlePokemon?[] { Substitute.For<IBattlePokemon>() };
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.ChangeTargets(choice, ref targets);
|
script.ChangeTargets(choice, ref targets);
|
||||||
@@ -122,7 +122,7 @@ public class CounterTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var script = new Counter();
|
var script = new Counter();
|
||||||
var attacker = Substitute.For<IPokemon>();
|
var attacker = Substitute.For<IBattlePokemon>();
|
||||||
var user = CreateUserHitBy(attacker, damageTaken);
|
var user = CreateUserHitBy(attacker, damageTaken);
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
@@ -144,9 +144,9 @@ public class CounterTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var script = new Counter();
|
var script = new Counter();
|
||||||
var attacker = Substitute.For<IPokemon>();
|
var attacker = Substitute.For<IBattlePokemon>();
|
||||||
var user = CreateUserHitBy(attacker, 40);
|
var user = CreateUserHitBy(attacker, 40);
|
||||||
var someoneElse = Substitute.For<IPokemon>();
|
var someoneElse = Substitute.For<IBattlePokemon>();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
var hitData = Substitute.For<IHitData>();
|
var hitData = Substitute.For<IHitData>();
|
||||||
@@ -167,7 +167,7 @@ public class CounterTests
|
|||||||
public async Task CounterHelperEffect_PhysicalHit_RecordsAttackerAndDamage()
|
public async Task CounterHelperEffect_PhysicalHit_RecordsAttackerAndDamage()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attacker = Substitute.For<IPokemon>();
|
var attacker = Substitute.For<IBattlePokemon>();
|
||||||
var user = CreateUserHitBy(attacker, 40);
|
var user = CreateUserHitBy(attacker, 40);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
@@ -184,7 +184,7 @@ public class CounterTests
|
|||||||
public async Task CounterHelperEffect_SpecialHit_IsNotRecorded()
|
public async Task CounterHelperEffect_SpecialHit_IsNotRecorded()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attacker = Substitute.For<IPokemon>();
|
var attacker = Substitute.For<IBattlePokemon>();
|
||||||
var user = CreateUserHitBy(attacker, 40, false);
|
var user = CreateUserHitBy(attacker, 40, false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class CovetTests
|
public class CovetTests
|
||||||
{
|
{
|
||||||
private static (Covet script, IExecutingMove move, IPokemon user, IPokemon target) CreateTestSetup(
|
private static (Covet script, IExecutingMove move, IBattlePokemon user, IBattlePokemon target) CreateTestSetup(
|
||||||
IItem? targetItem)
|
IItem? targetItem)
|
||||||
{
|
{
|
||||||
var script = new Covet();
|
var script = new Covet();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
// Explicitly return null to suppress NSubstitute's auto-substitution; Covet must see an empty-handed user.
|
// Explicitly return null to suppress NSubstitute's auto-substitution; Covet must see an empty-handed user.
|
||||||
user.HeldItem.Returns((IItem?)null);
|
user.HeldItem.Returns((IItem?)null);
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
target.HeldItem.Returns(targetItem);
|
target.HeldItem.Returns(targetItem);
|
||||||
if (targetItem != null)
|
if (targetItem != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,13 +26,11 @@ public class CraftyShieldTests
|
|||||||
|
|
||||||
var battle = Substitute.For<IBattle>();
|
var battle = Substitute.For<IBattle>();
|
||||||
battle.Sides.Returns(new[] { side });
|
battle.Sides.Returns(new[] { side });
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
|
||||||
battleData.Battle.Returns(battle);
|
|
||||||
battleData.SideIndex.Returns((byte)0);
|
|
||||||
|
|
||||||
var user = Substitute.For<IPokemon>();
|
var user = Substitute.For<IBattlePokemon>();
|
||||||
user.BattleData.Returns(battleData);
|
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
|
user.SideIndex.Returns((byte)0);
|
||||||
|
user.Battle.Returns(battle);
|
||||||
move.User.Returns(user);
|
move.User.Returns(user);
|
||||||
|
|
||||||
return (script, move, sideVolatile);
|
return (script, move, sideVolatile);
|
||||||
@@ -58,7 +56,7 @@ public class CraftyShieldTests
|
|||||||
var (script, move, sideVolatile) = CreateTestSetup();
|
var (script, move, sideVolatile) = CreateTestSetup();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
script.OnSecondaryEffect(move, Substitute.For<IBattlePokemon>(), 0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.That(sideVolatile.Contains(ScriptUtils.ResolveName<CraftyShieldEffect>())).IsTrue();
|
await Assert.That(sideVolatile.Contains(ScriptUtils.ResolveName<CraftyShieldEffect>())).IsTrue();
|
||||||
@@ -99,21 +97,4 @@ public class CraftyShieldTests
|
|||||||
// Assert
|
// Assert
|
||||||
await Assert.That(stop).IsFalse();
|
await Assert.That(stop).IsFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Technical test: outside of battle (no battle data) no shield can be raised and nothing happens.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task OnSecondaryEffect_NoBattleData_DoesNothing()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var (script, move, sideVolatile) = CreateTestSetup();
|
|
||||||
move.User.BattleData.Returns((IPokemonBattleData?)null);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
|
|
||||||
|
|
||||||
// Assert - no effect is added
|
|
||||||
await Assert.That(sideVolatile.Contains(ScriptUtils.ResolveName<CraftyShieldEffect>())).IsFalse();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -11,12 +11,12 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class CrushGripTests
|
public class CrushGripTests
|
||||||
{
|
{
|
||||||
private static (CrushGrip script, IExecutingMove move, IPokemon target) CreateTestSetup(uint currentHealth,
|
private static (CrushGrip script, IExecutingMove move, IBattlePokemon target) CreateTestSetup(uint currentHealth,
|
||||||
uint maxHealth)
|
uint maxHealth)
|
||||||
{
|
{
|
||||||
var script = new CrushGrip();
|
var script = new CrushGrip();
|
||||||
var move = Substitute.For<IExecutingMove>();
|
var move = Substitute.For<IExecutingMove>();
|
||||||
var target = Substitute.For<IPokemon>();
|
var target = Substitute.For<IBattlePokemon>();
|
||||||
target.CurrentHealth.Returns(currentHealth);
|
target.CurrentHealth.Returns(currentHealth);
|
||||||
target.BoostedStats.Returns(new StatisticSet<uint>(maxHealth, 1, 1, 1, 1, 1));
|
target.BoostedStats.Returns(new StatisticSet<uint>(maxHealth, 1, 1, 1, 1, 1));
|
||||||
return (script, move, target);
|
return (script, move, target);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user