89 lines
4.1 KiB
C#
89 lines
4.1 KiB
C#
using System.Linq.Expressions;
|
|
using System.Reflection;
|
|
using PkmnLib.Dynamic.AI.Explicit;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
|
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
|
using PkmnLib.Static.Moves;
|
|
|
|
namespace PkmnLib.Plugin.Gen7.AI;
|
|
|
|
public static class ExplicitAIFunctions
|
|
{
|
|
public static void RegisterAIFunctions(ExplicitAIHandlers handlers)
|
|
{
|
|
var baseType = typeof(Script);
|
|
foreach (var type in typeof(ExplicitAIFunctions).Assembly.GetTypes().Where(t => baseType.IsAssignableFrom(t)))
|
|
{
|
|
var attribute = type.GetCustomAttribute<ScriptAttribute>();
|
|
if (attribute == null)
|
|
continue;
|
|
|
|
if (attribute.Category == ScriptCategory.Move)
|
|
{
|
|
var failureMethod = type.GetMethods(BindingFlags.Public | BindingFlags.Static).FirstOrDefault(m =>
|
|
m.GetCustomAttribute<AIMoveFailureFunctionAttribute>() != null);
|
|
if (failureMethod != 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 scoreParam = Expression.Parameter(typeof(int).MakeByRefType(), "score");
|
|
var functionExpression = Expression.Lambda<AIScoreMoveHandler>(
|
|
Expression.Call(null, scoreMethod, optionParam, scoreParam), optionParam, scoreParam).Compile();
|
|
handlers.MoveEffectScore.Add(attribute.Name, functionExpression);
|
|
}
|
|
}
|
|
}
|
|
|
|
handlers.GeneralMoveAgainstTargetScore.Add("predicated_damage", PredictedDamageScore);
|
|
}
|
|
|
|
private static void PredictedDamageScore(MoveOption option, ref int score)
|
|
{
|
|
var target = option.Target;
|
|
if (target == null)
|
|
return;
|
|
if (option.Move.Move.Category == MoveCategory.Status)
|
|
return;
|
|
var damage = option.EstimatedDamage;
|
|
if (target.Volatile.TryGet<SubstituteEffect>(out var substitute))
|
|
{
|
|
var health = substitute.Health;
|
|
score += (int)Math.Min(15.0f * damage / health, 20);
|
|
return;
|
|
}
|
|
|
|
score += (int)Math.Min(15.0f * damage / target.CurrentHealth, 30);
|
|
|
|
if (damage > target.CurrentHealth * 1.1f)
|
|
{
|
|
score += 10;
|
|
if ((option.Move.Move.HasFlag("multi_hit") && target.CurrentHealth == target.MaxHealth &&
|
|
target.ActiveAbility?.Name == "sturdy") || target.HasHeldItem("focus_sash"))
|
|
{
|
|
score += 8;
|
|
}
|
|
}
|
|
}
|
|
} |