More abilities, refactor custom triggers to be typed.
All checks were successful
Build / Build (push) Successful in 48s
All checks were successful
Build / Build (push) Successful in 48s
This commit is contained in:
@@ -1023,6 +1023,8 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||
this.RunScriptHook(script => script.ChangeIncomingDamage(this, source, ref dmg));
|
||||
damage = dmg;
|
||||
}
|
||||
if (damage == 0)
|
||||
return;
|
||||
|
||||
// If the damage is more than the current health, we cap it at the current health, to prevent
|
||||
// underflow.
|
||||
|
||||
126
PkmnLib.Dynamic/ScriptHandling/ICustomTriggerArgs.cs
Normal file
126
PkmnLib.Dynamic/ScriptHandling/ICustomTriggerArgs.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq.Expressions;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Dynamic.ScriptHandling;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for custom trigger arguments.
|
||||
/// </summary>
|
||||
public interface ICustomTriggerArgs;
|
||||
|
||||
/// <summary>
|
||||
/// Static helper methods for working with <see cref="ICustomTriggerArgs"/>.
|
||||
///
|
||||
/// This class provides methods to get and set properties on custom trigger arguments. This allows for plugins and scripts
|
||||
/// to modify the behavior of custom triggers without needing to know the specific types of the arguments, which means
|
||||
/// they do not need to reference each other directly.
|
||||
/// </summary>
|
||||
public static class CustomTriggerArgsHelpers
|
||||
{
|
||||
private record TypeFuncs(
|
||||
Dictionary<StringKey, Func<ICustomTriggerArgs, object?>> Getters,
|
||||
Dictionary<StringKey, Action<ICustomTriggerArgs, object?>> Setters);
|
||||
|
||||
private static readonly Dictionary<Type, TypeFuncs> TypeFuncInfo = new();
|
||||
|
||||
private static void PopulateForType(Type type)
|
||||
{
|
||||
if (TypeFuncInfo.ContainsKey(type))
|
||||
return;
|
||||
|
||||
var getters = type.GetProperties().Where(p => p.CanRead && p.GetIndexParameters().Length == 0).ToDictionary(
|
||||
p => new StringKey(p.Name), p =>
|
||||
{
|
||||
var parameter = Expression.Parameter(typeof(ICustomTriggerArgs), "args");
|
||||
var typeCast = Expression.Convert(parameter, type);
|
||||
var propertyAccess = Expression.Property(typeCast, p);
|
||||
var convert = Expression.Convert(propertyAccess, typeof(object));
|
||||
return Expression.Lambda<Func<ICustomTriggerArgs, object?>>(convert, parameter).Compile();
|
||||
});
|
||||
var setters = type.GetProperties().Where(p => p.CanWrite && p.GetIndexParameters().Length == 0).ToDictionary(
|
||||
p => new StringKey(p.Name), p =>
|
||||
{
|
||||
var parameter = Expression.Parameter(typeof(ICustomTriggerArgs), "args");
|
||||
var typeCast = Expression.Convert(parameter, type);
|
||||
var valueParameter = Expression.Parameter(typeof(object), "value");
|
||||
var propertyAccess = Expression.Property(typeCast, p);
|
||||
var convert = Expression.Convert(valueParameter, p.PropertyType);
|
||||
var assign = Expression.Assign(propertyAccess, convert);
|
||||
return Expression.Lambda<Action<ICustomTriggerArgs, object?>>(assign, parameter, valueParameter)
|
||||
.Compile();
|
||||
});
|
||||
|
||||
TypeFuncInfo[type] = new TypeFuncs(getters, setters);
|
||||
}
|
||||
|
||||
private static bool TryGetGetter(Type type, StringKey key,
|
||||
[NotNullWhen(true)] out Func<ICustomTriggerArgs, object?>? getter)
|
||||
{
|
||||
if (!TypeFuncInfo.ContainsKey(type))
|
||||
PopulateForType(type);
|
||||
return TypeFuncInfo[type].Getters.TryGetValue(key, out getter);
|
||||
}
|
||||
|
||||
private static bool TryGetSetter(Type type, StringKey key,
|
||||
[NotNullWhen(true)] out Action<ICustomTriggerArgs, object?>? setter)
|
||||
{
|
||||
if (!TypeFuncInfo.ContainsKey(type))
|
||||
PopulateForType(type);
|
||||
return TypeFuncInfo[type].Setters.TryGetValue(key, out setter);
|
||||
}
|
||||
|
||||
public static T Get<T>(this ICustomTriggerArgs args, StringKey key)
|
||||
{
|
||||
if (args is null)
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
var type = args.GetType();
|
||||
if (!TryGetGetter(type, key, out var getter))
|
||||
throw new KeyNotFoundException($"Key '{key}' not found in {type.Name}.");
|
||||
var value = getter(args);
|
||||
if (value is T typedValue)
|
||||
return typedValue;
|
||||
throw new InvalidCastException(
|
||||
$"Value for key '{key}' in {type.Name} is of type {value?.GetType().Name}, expected {typeof(T).Name}.");
|
||||
}
|
||||
|
||||
public static object? Get(this ICustomTriggerArgs args, StringKey key)
|
||||
{
|
||||
if (args is null)
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
var type = args.GetType();
|
||||
if (!TryGetGetter(type, key, out var getter))
|
||||
throw new KeyNotFoundException($"Key '{key}' not found in {type.Name}.");
|
||||
return getter(args);
|
||||
}
|
||||
|
||||
public static bool TryGet<T>(this ICustomTriggerArgs args, StringKey key, [NotNullWhen(true)] out T? value)
|
||||
{
|
||||
if (args is null)
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
var type = args.GetType();
|
||||
if (!TryGetGetter(type, key, out var getter))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
var result = getter(args);
|
||||
if (result is T typedValue)
|
||||
{
|
||||
value = typedValue;
|
||||
return true;
|
||||
}
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void Set(this ICustomTriggerArgs args, StringKey key, object? value)
|
||||
{
|
||||
if (args is null)
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
var type = args.GetType();
|
||||
if (!TryGetSetter(type, key, out var setter))
|
||||
throw new KeyNotFoundException($"Key '{key}' not found in {type.Name}.");
|
||||
setter(args, value);
|
||||
}
|
||||
}
|
||||
@@ -356,6 +356,23 @@ public abstract class Script : IDeepCloneable
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function allows a script to change the offensive stat value of an incoming move.
|
||||
/// </summary>
|
||||
public virtual void ChangeIncomingMoveOffensiveStatValue(IExecutingMove executingMove, IPokemon target,
|
||||
byte hitNumber, uint defensiveStat, StatisticSet<uint> targetStats, Statistic offensive, ref uint offensiveStat)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function allows a script to change the defensive stat value of an incoming move.
|
||||
/// </summary>
|
||||
public virtual void ChangeIncomingMoveDefensiveStatValue(IExecutingMove executingMove, IPokemon target,
|
||||
byte hitNumber, uint origOffensiveStat, StatisticSet<uint> targetStats, Statistic defensive,
|
||||
ref uint defensiveStat)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
@@ -633,10 +650,10 @@ public abstract class Script : IDeepCloneable
|
||||
/// The name of the event that is triggered. This should be unique for each different event. Overriding scripts
|
||||
/// should validate the event name is one they should handle.
|
||||
/// </param>
|
||||
/// <param name="parameters">
|
||||
/// The parameters that are passed to the event. This can be null if no parameters are passed.
|
||||
/// <param name="args">
|
||||
/// The parameters that are passed to the event.
|
||||
/// </param>
|
||||
public virtual void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
|
||||
public virtual void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user