Move all battle state from IPokemon to an ephemeral IBattlePokemon wrapper

This commit is contained in:
2026-08-28 15:20:26 +02:00
parent 942be8eaeb
commit 8a2733a0a9
859 changed files with 4408 additions and 5331 deletions

View File

@@ -8,13 +8,13 @@ namespace PkmnLib.Plugin.Gen7.AI;
public static class AIHelperFunctions
{
public static int GetScoreForTargetStatRaise(int score, AIMoveState move, IPokemon target,
public static int GetScoreForTargetStatRaise(int score, AIMoveState move, IBattlePokemon target,
StatisticSet<sbyte> statChanges, bool fixedChange = false, bool ignoreContrary = false)
{
var wholeEffect = move.Move.Category != MoveCategory.Status;
var desireMult = 1;
if (move.User.BattleData?.SideIndex != target.BattleData?.SideIndex)
if (move.User.SideIndex != target.SideIndex)
desireMult = -1;
if (!ignoreContrary && !fixedChange && target.ActiveAbility?.Name == AbilityNames.Contrary)
@@ -41,8 +41,7 @@ public static class AIHelperFunctions
if (!move.User.HasMoveWithEffect(MoveEffectNames.PowerTrip))
{
var foeIsAware =
target.BattleData?.BattleSide.Pokemon.Any(x => x?.ActiveAbility?.Name == AbilityNames.Unaware) != true;
var foeIsAware = target.BattleSide.Pokemon.Any(x => x?.ActiveAbility?.Name == AbilityNames.Unaware) != true;
if (!foeIsAware)
{
return wholeEffect ? ExplicitAI.MoveUselessScore : score;
@@ -80,13 +79,13 @@ public static class AIHelperFunctions
return score;
}
public static int GetScoreForTargetStatDrop(int score, AIMoveState move, IPokemon target,
public static int GetScoreForTargetStatDrop(int score, AIMoveState move, IBattlePokemon target,
StatisticSet<sbyte> statChanges, bool fixedChange = false, bool ignoreContrary = false)
{
var wholeEffect = move.Move.Category != MoveCategory.Status;
var desireMult = -1;
if (move.User.BattleData?.SideIndex == target.BattleData?.SideIndex)
if (move.User.SideIndex == target.SideIndex)
desireMult = 1;
if (!ignoreContrary && !fixedChange && target.ActiveAbility?.Name == AbilityNames.Contrary)
{
@@ -109,7 +108,7 @@ public static class AIHelperFunctions
return wholeEffect ? ExplicitAI.MoveUselessScore : score;
var foeIsAware = false;
if (target.BattleData?.BattleSide.Pokemon.All(x => x?.ActiveAbility?.Name != AbilityNames.Unaware) == true)
if (target.BattleSide.Pokemon.All(x => x?.ActiveAbility?.Name != AbilityNames.Unaware) == true)
{
foeIsAware = true;
}
@@ -148,7 +147,8 @@ public static class AIHelperFunctions
/// <summary>
/// Checks if a stat raise is worthwhile for the given Pokémon and stat.
/// </summary>
private static bool IsStatRaiseWorthwhile(IPokemon pokemon, Statistic stat, sbyte amount, bool fixedChange = false)
private static bool IsStatRaiseWorthwhile(IBattlePokemon pokemon, Statistic stat, sbyte amount,
bool fixedChange = false)
{
if (!fixedChange && pokemon.StatBoost.GetStatistic(stat) == StatBoostStatisticSet.MaxStatBoost)
return false;
@@ -169,7 +169,7 @@ public static class AIHelperFunctions
}
case Statistic.Defense:
{
var opponentSide = pokemon.BattleData!.Battle.Sides.First(x => x != pokemon.BattleData.BattleSide);
var opponentSide = pokemon.Battle.Sides.First(x => x != pokemon.BattleSide);
return opponentSide.Pokemon.WhereNotNull().Any(x => x.Moves.WhereNotNull().Any(y =>
y.MoveData.Category == MoveCategory.Physical ||
y.MoveData.SecondaryEffect?.Name == MoveEffectNames.Psyshock));
@@ -184,7 +184,7 @@ public static class AIHelperFunctions
}
case Statistic.SpecialDefense:
{
var opponentSide = pokemon.BattleData!.Battle.Sides.First(x => x != pokemon.BattleData.BattleSide);
var opponentSide = pokemon.Battle.Sides.First(x => x != pokemon.BattleSide);
return opponentSide.Pokemon.WhereNotNull().Any(x => x.Moves.WhereNotNull().Any(y =>
y.MoveData.Category == MoveCategory.Special &&
y.MoveData.SecondaryEffect?.Name != MoveEffectNames.Psyshock));
@@ -194,7 +194,7 @@ public static class AIHelperFunctions
if (!pokemon.HasMoveWithEffect(MoveEffectNames.ElectroBall, MoveEffectNames.PowerTrip))
{
var targetSpeed = pokemon.BoostedStats.Speed;
var opponentSide = pokemon.BattleData!.Battle.Sides.First(x => x != pokemon.BattleData.BattleSide);
var opponentSide = pokemon.Battle.Sides.First(x => x != pokemon.BattleSide);
var meaningful = opponentSide.Pokemon.WhereNotNull().Select(opponent => opponent.BoostedStats.Speed)
.Any(foeSpeed => targetSpeed < foeSpeed && targetSpeed * 2.5 > foeSpeed);
if (!meaningful)
@@ -208,7 +208,7 @@ public static class AIHelperFunctions
if (minAccuracy >= 90 && pokemon.StatBoost.Accuracy >= 0)
{
var meaningful = false;
var opponentSide = pokemon.BattleData!.Battle.Sides.First(x => x != pokemon.BattleData.BattleSide);
var opponentSide = pokemon.Battle.Sides.First(x => x != pokemon.BattleSide);
if (opponentSide.Pokemon.WhereNotNull().Any(x => x.StatBoost.Evasion > 0))
{
meaningful = true;
@@ -222,7 +222,7 @@ public static class AIHelperFunctions
return true;
}
private static bool IsStatDropWorthwhile(IPokemon pokemon, Statistic stat, sbyte amount)
private static bool IsStatDropWorthwhile(IBattlePokemon pokemon, Statistic stat, sbyte amount)
{
if (amount == 0)
return false;
@@ -236,7 +236,7 @@ public static class AIHelperFunctions
}
case Statistic.Defense:
{
var opponentSide = pokemon.BattleData!.Battle.Sides.First(x => x != pokemon.BattleData.BattleSide);
var opponentSide = pokemon.Battle.Sides.First(x => x != pokemon.BattleSide);
return opponentSide.Pokemon.WhereNotNull().Any(x => x.Moves.WhereNotNull().Any(y =>
y.MoveData.Category == MoveCategory.Physical ||
y.MoveData.SecondaryEffect?.Name == MoveEffectNames.Psyshock));
@@ -247,7 +247,7 @@ public static class AIHelperFunctions
}
case Statistic.SpecialDefense:
{
var opponentSide = pokemon.BattleData!.Battle.Sides.First(x => x != pokemon.BattleData.BattleSide);
var opponentSide = pokemon.Battle.Sides.First(x => x != pokemon.BattleSide);
return opponentSide.Pokemon.WhereNotNull().Any(x => x.Moves.WhereNotNull().Any(y =>
y.MoveData.Category == MoveCategory.Special &&
y.MoveData.SecondaryEffect?.Name != MoveEffectNames.Psyshock));
@@ -257,7 +257,7 @@ public static class AIHelperFunctions
if (!pokemon.HasMoveWithEffect(MoveEffectNames.ElectroBall))
{
var targetSpeed = pokemon.BoostedStats.Speed;
var opponentSide = pokemon.BattleData!.Battle.Sides.First(x => x != pokemon.BattleData.BattleSide);
var opponentSide = pokemon.Battle.Sides.First(x => x != pokemon.BattleSide);
return opponentSide.Pokemon.WhereNotNull().Select(opponent => opponent.BoostedStats.Speed)
.Any(foeSpeed => targetSpeed > foeSpeed && targetSpeed < foeSpeed * 2.5);
}
@@ -272,8 +272,8 @@ public static class AIHelperFunctions
return true;
}
private static void GetTargetStatRaiseScoreOne(ref int score, IPokemon target, Statistic stat, sbyte increment,
AIMoveState move, float desireMult = 1)
private static void GetTargetStatRaiseScoreOne(ref int score, IBattlePokemon target, Statistic stat,
sbyte increment, AIMoveState move, float desireMult = 1)
{
var oldStage = target.StatBoost.GetStatistic(stat);
var newStage = (sbyte)(oldStage + increment);
@@ -282,7 +282,7 @@ public static class AIHelperFunctions
var actualIncrement = incMult;
incMult -= 1;
incMult *= desireMult;
var opponentSide = target.BattleData!.Battle.Sides.First(x => x != target.BattleData.BattleSide);
var opponentSide = target.Battle.Sides.First(x => x != target.BattleSide);
switch (stat)
{
@@ -407,7 +407,7 @@ public static class AIHelperFunctions
}
}
private static void GetTargetStatDropScoreOne(ref int score, IPokemon target, Statistic stat, sbyte decrement,
private static void GetTargetStatDropScoreOne(ref int score, IBattlePokemon target, Statistic stat, sbyte decrement,
AIMoveState move, float desireMult = 1)
{
var oldStage = target.StatBoost.GetStatistic(stat);
@@ -416,7 +416,7 @@ public static class AIHelperFunctions
Gen7BattleStatCalculator.GetStatBoostModifier(Math.Max(newStage, (sbyte)-6));
decMult -= 1;
decMult *= desireMult;
var opponentSide = target.BattleData!.Battle.Sides.First(x => x != target.BattleData.BattleSide);
var opponentSide = target.Battle.Sides.First(x => x != target.BattleSide);
switch (stat)
{
@@ -519,11 +519,11 @@ public static class AIHelperFunctions
/// <summary>
/// Calculates the score for the generic concept of raising a target's stats.
/// </summary>
private static int GetTargetStatRaiseScoreGeneric(int score, IPokemon target, StatisticSet<sbyte> statChanges,
private static int GetTargetStatRaiseScoreGeneric(int score, IBattlePokemon target, StatisticSet<sbyte> statChanges,
AIMoveState move, float desireMult = 1)
{
var totalIncrement = statChanges.Sum(x => x.value);
var turns = target.BattleData!.Battle.CurrentTurnNumber - target.BattleData!.SwitchInTurn;
var turns = target.Battle.CurrentTurnNumber - target.SwitchInTurn;
if (turns < 2 && move.Move.Category == MoveCategory.Status)
score += (int)(totalIncrement * desireMult * 5);
@@ -532,11 +532,11 @@ public static class AIHelperFunctions
return score;
}
private static int GetTargetStatDropScoreGeneric(int score, IPokemon target, StatisticSet<sbyte> statChanges,
private static int GetTargetStatDropScoreGeneric(int score, IBattlePokemon target, StatisticSet<sbyte> statChanges,
AIMoveState move, float desireMult = 1)
{
var totalDecrement = statChanges.Sum(x => x.value);
var turns = target.BattleData!.Battle.CurrentTurnNumber - target.BattleData!.SwitchInTurn;
var turns = target.Battle.CurrentTurnNumber - target.SwitchInTurn;
if (turns < 2 && move.Move.Category == MoveCategory.Status)
score += (int)(totalDecrement * desireMult * 5);
@@ -545,34 +545,34 @@ public static class AIHelperFunctions
return score;
}
private static int GetScoreChangeForAdditionalEffect(this AIMoveState move, IPokemon? target)
private static int GetScoreChangeForAdditionalEffect(this AIMoveState move, IBattlePokemon? target)
{
if (move.Move.SecondaryEffect is null)
return 0;
if (move.User.ActiveAbility?.Name == AbilityNames.SheerForce)
return -999;
if (target is not null && target.BattleData?.Position != move.User.BattleData?.Position &&
if (target is not null && target.Position != move.User.Position &&
target.ActiveAbility?.Name == AbilityNames.ShieldDust)
return -999;
if ((move.Move.SecondaryEffect.Chance < 100 && move.User.ActiveAbility?.Name == AbilityNames.SereneGrace) ||
move.User.BattleData?.BattleSide.VolatileScripts.Contains<RainbowEffect>() == true)
move.User.BattleSide.VolatileScripts.Contains<RainbowEffect>() == true)
{
return 5;
}
return 0;
}
private static bool HasMoveWithEffect(this IPokemon pokemon, params StringKey[] effect)
private static bool HasMoveWithEffect(this IBattlePokemon pokemon, params StringKey[] effect)
{
return pokemon.Moves.WhereNotNull().Any(move => move.MoveData.SecondaryEffect?.Name is not null &&
effect.Contains(move.MoveData.SecondaryEffect.Name));
}
private static bool Opposes(this IPokemon pokemon, IPokemon target) =>
pokemon.BattleData?.BattleSide != target.BattleData?.BattleSide;
private static bool Opposes(this IBattlePokemon pokemon, IBattlePokemon target) =>
pokemon.BattleSide != target.BattleSide;
public static bool WantsStatusProblem(IPokemon pokemon, StringKey? status)
public static bool WantsStatusProblem(IBattlePokemon pokemon, StringKey? status)
{
if (status is null)
return true;