Implement basic AI check for whether a move would fail

This commit is contained in:
Deukhoofd 2025-07-11 17:13:11 +02:00
parent a3a4993407
commit 364d4b9080
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
3 changed files with 56 additions and 8 deletions

View File

@ -5,4 +5,9 @@ namespace PkmnLib.Plugin.Gen7.AI;
[AttributeUsage(AttributeTargets.Method), MeansImplicitUse] [AttributeUsage(AttributeTargets.Method), MeansImplicitUse]
public class AIMoveScoreFunctionAttribute : Attribute public class AIMoveScoreFunctionAttribute : Attribute
{ {
}
[AttributeUsage(AttributeTargets.Method), MeansImplicitUse]
public class AIMoveFailureFunctionAttribute : Attribute
{
} }

View File

@ -20,18 +20,37 @@ public static class ExplicitAIFunctions
if (attribute.Category == ScriptCategory.Move) if (attribute.Category == ScriptCategory.Move)
{ {
// Check if the move type has a static function in the following format: var failureMethod = type.GetMethods(BindingFlags.Public | BindingFlags.Static).FirstOrDefault(m =>
// public static void AIMoveEffectScore(MoveOption option, ref int score); m.GetCustomAttribute<AIMoveFailureFunctionAttribute>() != null);
var method = type.GetMethods(BindingFlags.Public | BindingFlags.Static).FirstOrDefault(m => if (failureMethod != null)
m.GetCustomAttribute<AIMoveScoreFunctionAttribute>() != null && m.ReturnType == typeof(void) &&
m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType == typeof(MoveOption) &&
m.GetParameters()[1].ParameterType == typeof(int).MakeByRefType());
if (method != null)
{ {
if (failureMethod.ReturnType != typeof(bool) || failureMethod.GetParameters().Length != 1 ||
failureMethod.GetParameters()[0].ParameterType != typeof(MoveOption))
{
throw new InvalidOperationException(
$"Method {failureMethod.Name} in {type.Name} must return bool and take a single MoveOption parameter.");
}
var optionParam = Expression.Parameter(typeof(MoveOption), "option");
var functionExpression = Expression.Lambda<AIBoolHandler>(
Expression.Call(null, failureMethod, optionParam), optionParam).Compile();
handlers.MoveFailureCheck.Add(attribute.Name, functionExpression);
}
var scoreMethod = type.GetMethods(BindingFlags.Public | BindingFlags.Static).FirstOrDefault(m =>
m.GetCustomAttribute<AIMoveScoreFunctionAttribute>() != null);
if (scoreMethod != null)
{
if (scoreMethod.ReturnType != typeof(void) || scoreMethod.GetParameters().Length != 2 ||
scoreMethod.GetParameters()[0].ParameterType != typeof(MoveOption) ||
scoreMethod.GetParameters()[1].ParameterType != typeof(int).MakeByRefType())
{
throw new InvalidOperationException(
$"Method {scoreMethod.Name} in {type.Name} must return void and take a MoveOption and an int by reference parameter.");
}
var optionParam = Expression.Parameter(typeof(MoveOption), "option"); var optionParam = Expression.Parameter(typeof(MoveOption), "option");
var scoreParam = Expression.Parameter(typeof(int).MakeByRefType(), "score"); var scoreParam = Expression.Parameter(typeof(int).MakeByRefType(), "score");
var functionExpression = Expression.Lambda<AIScoreMoveHandler>( var functionExpression = Expression.Lambda<AIScoreMoveHandler>(
Expression.Call(null, method, optionParam, scoreParam), optionParam, scoreParam).Compile(); Expression.Call(null, scoreMethod, optionParam, scoreParam), optionParam, scoreParam).Compile();
handlers.MoveEffectScore.Add(attribute.Name, functionExpression); handlers.MoveEffectScore.Add(attribute.Name, functionExpression);
} }
} }

View File

@ -35,6 +35,9 @@ public abstract class ChangeUserStats : Script, IScriptOnInitialize, IScriptOnSe
move.User.ChangeStatBoost(_stat, _amount, true, false); move.User.ChangeStatBoost(_stat, _amount, true, false);
} }
protected static bool WouldMoveFail(MoveOption option, Statistic stat) =>
option.Move.User.StatBoost.GetStatistic(stat) == StatBoostStatisticSet.MaxStatBoost;
protected static void GetMoveEffectScore(MoveOption option, Statistic stat, ref int score) protected static void GetMoveEffectScore(MoveOption option, Statistic stat, ref int score)
{ {
if (option.Move.Move.SecondaryEffect == null || if (option.Move.Move.SecondaryEffect == null ||
@ -56,6 +59,9 @@ public class ChangeUserAttack : ChangeUserStats
{ {
} }
[AIMoveFailureFunction]
public static bool AIMoveWillFail(MoveOption option) => WouldMoveFail(option, Statistic.Attack);
[AIMoveScoreFunction] [AIMoveScoreFunction]
public static void AIMoveEffectScore(MoveOption option, ref int score) => public static void AIMoveEffectScore(MoveOption option, ref int score) =>
GetMoveEffectScore(option, Statistic.Attack, ref score); GetMoveEffectScore(option, Statistic.Attack, ref score);
@ -68,6 +74,9 @@ public class ChangeUserDefense : ChangeUserStats
{ {
} }
[AIMoveFailureFunction]
public static bool AIMoveWillFail(MoveOption option) => WouldMoveFail(option, Statistic.Defense);
[AIMoveScoreFunction] [AIMoveScoreFunction]
public static void AIMoveEffectScore(MoveOption option, ref int score) => public static void AIMoveEffectScore(MoveOption option, ref int score) =>
GetMoveEffectScore(option, Statistic.Defense, ref score); GetMoveEffectScore(option, Statistic.Defense, ref score);
@ -80,6 +89,9 @@ public class ChangeUserSpecialAttack : ChangeUserStats
{ {
} }
[AIMoveFailureFunction]
public static bool AIMoveWillFail(MoveOption option) => WouldMoveFail(option, Statistic.SpecialAttack);
[AIMoveScoreFunction] [AIMoveScoreFunction]
public static void AIMoveEffectScore(MoveOption option, ref int score) => public static void AIMoveEffectScore(MoveOption option, ref int score) =>
GetMoveEffectScore(option, Statistic.SpecialAttack, ref score); GetMoveEffectScore(option, Statistic.SpecialAttack, ref score);
@ -92,6 +104,9 @@ public class ChangeUserSpecialDefense : ChangeUserStats
{ {
} }
[AIMoveFailureFunction]
public static bool AIMoveWillFail(MoveOption option) => WouldMoveFail(option, Statistic.SpecialDefense);
[AIMoveScoreFunction] [AIMoveScoreFunction]
public static void AIMoveEffectScore(MoveOption option, ref int score) => public static void AIMoveEffectScore(MoveOption option, ref int score) =>
GetMoveEffectScore(option, Statistic.SpecialDefense, ref score); GetMoveEffectScore(option, Statistic.SpecialDefense, ref score);
@ -104,6 +119,9 @@ public class ChangeUserSpeed : ChangeUserStats
{ {
} }
[AIMoveFailureFunction]
public static bool AIMoveWillFail(MoveOption option) => WouldMoveFail(option, Statistic.Speed);
[AIMoveScoreFunction] [AIMoveScoreFunction]
public static void AIMoveEffectScore(MoveOption option, ref int score) => public static void AIMoveEffectScore(MoveOption option, ref int score) =>
GetMoveEffectScore(option, Statistic.Speed, ref score); GetMoveEffectScore(option, Statistic.Speed, ref score);
@ -116,6 +134,9 @@ public class ChangeUserAccuracy : ChangeUserStats
{ {
} }
[AIMoveFailureFunction]
public static bool AIMoveWillFail(MoveOption option) => WouldMoveFail(option, Statistic.Accuracy);
[AIMoveScoreFunction] [AIMoveScoreFunction]
public static void AIMoveEffectScore(MoveOption option, ref int score) => public static void AIMoveEffectScore(MoveOption option, ref int score) =>
GetMoveEffectScore(option, Statistic.Accuracy, ref score); GetMoveEffectScore(option, Statistic.Accuracy, ref score);
@ -128,6 +149,9 @@ public class ChangeUserEvasion : ChangeUserStats
{ {
} }
[AIMoveFailureFunction]
public static bool AIMoveWillFail(MoveOption option) => WouldMoveFail(option, Statistic.Evasion);
[AIMoveScoreFunction] [AIMoveScoreFunction]
public static void AIMoveEffectScore(MoveOption option, ref int score) => public static void AIMoveEffectScore(MoveOption option, ref int score) =>
GetMoveEffectScore(option, Statistic.Evasion, ref score); GetMoveEffectScore(option, Statistic.Evasion, ref score);