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:
parent
4326794611
commit
6d71de375e
@ -1023,6 +1023,8 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||||||
this.RunScriptHook(script => script.ChangeIncomingDamage(this, source, ref dmg));
|
this.RunScriptHook(script => script.ChangeIncomingDamage(this, source, ref dmg));
|
||||||
damage = 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
|
// If the damage is more than the current health, we cap it at the current health, to prevent
|
||||||
// underflow.
|
// 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>
|
/// <summary>
|
||||||
/// 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.
|
||||||
@ -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
|
/// 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.
|
/// should validate the event name is one they should handle.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="parameters">
|
/// <param name="args">
|
||||||
/// The parameters that are passed to the event. This can be null if no parameters are passed.
|
/// The parameters that are passed to the event.
|
||||||
/// </param>
|
/// </param>
|
||||||
public virtual void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
|
public virtual void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
using PkmnLib.Dynamic.Models;
|
||||||
|
using PkmnLib.Dynamic.ScriptHandling;
|
||||||
|
using PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
||||||
|
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||||
|
using PkmnLib.Static;
|
||||||
|
using PkmnLib.Static.Moves;
|
||||||
|
using PkmnLib.Static.Utils;
|
||||||
|
|
||||||
|
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Abilities;
|
||||||
|
|
||||||
|
public class MegaLauncherTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public async Task ChangeHealPercent_HealPercentIncreasesForAuraMoves()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var move = Substitute.For<IExecutingMove>();
|
||||||
|
var target = Substitute.For<IPokemon>();
|
||||||
|
var healPercent = 0.5f;
|
||||||
|
var user = Substitute.For<IPokemon>();
|
||||||
|
move.User.Returns(user);
|
||||||
|
move.UseMove.Category.Returns(MoveCategory.Special);
|
||||||
|
move.UseMove.HasFlag("pulse").Returns(true);
|
||||||
|
var megaLauncher = new ScriptContainer(new MegaLauncher());
|
||||||
|
move.User.AbilityScript.Returns(megaLauncher);
|
||||||
|
move.GetScripts().Returns(new ScriptIterator([megaLauncher]));
|
||||||
|
var healPercentScript = new HealPercent();
|
||||||
|
uint healedAmount = 0;
|
||||||
|
target.Heal(Arg.Do<uint>(amount => healedAmount = amount));
|
||||||
|
target.BoostedStats.Returns(new StatisticSet<uint>(100, 100, 100, 100, 100, 100));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
healPercentScript.OnInitialize(new Dictionary<StringKey, object?>
|
||||||
|
{
|
||||||
|
{ "healPercent", healPercent },
|
||||||
|
});
|
||||||
|
healPercentScript.OnSecondaryEffect(move, target, 0);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
await Assert.That(healedAmount).IsEqualTo((uint)(target.BoostedStats.Hp * healPercent * 1.5f));
|
||||||
|
}
|
||||||
|
}
|
@ -342,15 +342,33 @@
|
|||||||
"magic_bounce": {
|
"magic_bounce": {
|
||||||
"effect": "magic_bounce"
|
"effect": "magic_bounce"
|
||||||
},
|
},
|
||||||
"magic_guard": {},
|
"magic_guard": {
|
||||||
"magician": {},
|
"effect": "magic_guard"
|
||||||
"magma_armor": {},
|
},
|
||||||
"magnet_pull": {},
|
"magician": {
|
||||||
"marvel_scale": {},
|
"effect": "magician"
|
||||||
"mega_launcher": {},
|
},
|
||||||
"merciless": {},
|
"magma_armor": {
|
||||||
"minus": {},
|
"effect": "magma_armor"
|
||||||
"misty_surge": {},
|
},
|
||||||
|
"magnet_pull": {
|
||||||
|
"effect": "magnet_pull"
|
||||||
|
},
|
||||||
|
"marvel_scale": {
|
||||||
|
"effect": "marvel_scale"
|
||||||
|
},
|
||||||
|
"mega_launcher": {
|
||||||
|
"effect": "mega_launcher"
|
||||||
|
},
|
||||||
|
"merciless": {
|
||||||
|
"effect": "merciless"
|
||||||
|
},
|
||||||
|
"minus": {
|
||||||
|
"effect": "minus"
|
||||||
|
},
|
||||||
|
"misty_surge": {
|
||||||
|
"effect": "misty_surge"
|
||||||
|
},
|
||||||
"mold_breaker": {},
|
"mold_breaker": {},
|
||||||
"moody": {},
|
"moody": {},
|
||||||
"motor_drive": {},
|
"motor_drive": {},
|
||||||
|
@ -136,6 +136,10 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
|
|||||||
defensiveStat, targetStats, offensive, ref offensiveStat));
|
defensiveStat, targetStats, offensive, ref offensiveStat));
|
||||||
executingMove.RunScriptHook(script => script.ChangeDefensiveStatValue(executingMove, target, hitNumber,
|
executingMove.RunScriptHook(script => script.ChangeDefensiveStatValue(executingMove, target, hitNumber,
|
||||||
origOffensiveStat, targetStats, defensive, ref defensiveStat));
|
origOffensiveStat, targetStats, defensive, ref defensiveStat));
|
||||||
|
target.RunScriptHook(script => script.ChangeIncomingMoveOffensiveStatValue(executingMove, target, hitNumber,
|
||||||
|
defensiveStat, targetStats, offensive, ref offensiveStat));
|
||||||
|
target.RunScriptHook(script => script.ChangeIncomingMoveDefensiveStatValue(executingMove, target, hitNumber,
|
||||||
|
origOffensiveStat, targetStats, defensive, ref defensiveStat));
|
||||||
|
|
||||||
var modifier = (float)offensiveStat / defensiveStat;
|
var modifier = (float)offensiveStat / defensiveStat;
|
||||||
executingMove.RunScriptHook(script =>
|
executingMove.RunScriptHook(script =>
|
||||||
|
@ -10,21 +10,16 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
|||||||
public class AuraBreak : Script
|
public class AuraBreak : Script
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
|
public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
|
||||||
{
|
{
|
||||||
if (eventName != CustomTriggers.ModifyAuraEffect || parameters == null)
|
if (eventName != CustomTriggers.ModifyAuraEffect || args is not CustomTriggers.ModifyAuraEffectArgs auraArgs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!parameters.TryGetValue("aura_type", out var auraTypeObj) || auraTypeObj is not string auraType)
|
var typeName = auraArgs.Move.UseMove.MoveType.Name;
|
||||||
return;
|
if (typeName == "dark" || typeName == "fairy")
|
||||||
|
|
||||||
if (auraType is "dark" or "fairy")
|
|
||||||
{
|
|
||||||
if (parameters.TryGetValue("modifier", out var modifierObj) && modifierObj is float)
|
|
||||||
{
|
{
|
||||||
// Reverse the aura effect by reducing power by 25%
|
// Reverse the aura effect by reducing power by 25%
|
||||||
parameters["modifier"] = 0.75f;
|
auraArgs.AuraEffect *= 0.75f;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,11 +21,11 @@ public class Comatose : Script
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
|
public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
|
||||||
{
|
{
|
||||||
if (eventName == CustomTriggers.BypassSleep && parameters != null)
|
if (eventName == CustomTriggers.BypassSleep && args is CustomTriggers.BypassSleepArgs bypassArgs)
|
||||||
{
|
{
|
||||||
parameters["bypass_sleep"] = true;
|
bypassArgs.Bypass = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,17 +15,10 @@ public class DarkAura : Script
|
|||||||
if (move.GetHitData(target, hit).Type?.Name == "dark")
|
if (move.GetHitData(target, hit).Type?.Name == "dark")
|
||||||
{
|
{
|
||||||
var auraModifier = 5448f / 4096f;
|
var auraModifier = 5448f / 4096f;
|
||||||
var parameters = new Dictionary<StringKey, object?>
|
var args = new CustomTriggers.ModifyAuraEffectArgs(move, target, hit, auraModifier);
|
||||||
{
|
|
||||||
["aura_type"] = "dark",
|
|
||||||
["modifier"] = auraModifier,
|
|
||||||
};
|
|
||||||
move.Battle.Sides.SelectMany(side => side.Pokemon).WhereNotNull()
|
move.Battle.Sides.SelectMany(side => side.Pokemon).WhereNotNull()
|
||||||
.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyAuraEffect, parameters));
|
.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyAuraEffect, args));
|
||||||
if (parameters.TryGetValue("modifier", out var modObj) && modObj is float modValue)
|
auraModifier = args.AuraEffect;
|
||||||
{
|
|
||||||
auraModifier = modValue;
|
|
||||||
}
|
|
||||||
modifier *= auraModifier;
|
modifier *= auraModifier;
|
||||||
move.Battle.EventHook.Invoke(new AbilityTriggerEvent(move.User));
|
move.Battle.EventHook.Invoke(new AbilityTriggerEvent(move.User));
|
||||||
}
|
}
|
||||||
|
@ -11,15 +11,11 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
|||||||
public class EarlyBird : Script
|
public class EarlyBird : Script
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
|
public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
|
||||||
{
|
{
|
||||||
if (eventName != CustomTriggers.ModifySleepTurns)
|
if (eventName != CustomTriggers.ModifySleepTurns)
|
||||||
return;
|
return;
|
||||||
if (parameters == null)
|
if (args is CustomTriggers.ModifySleepTurnsArgs sleepArgs)
|
||||||
return;
|
sleepArgs.Turns /= 2;
|
||||||
if (parameters.TryGetValue("turns", out var turnsObj) && turnsObj is int turns)
|
|
||||||
{
|
|
||||||
parameters["turns"] = turns / 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,17 +16,10 @@ public class FairyAura : Script
|
|||||||
if (move.GetHitData(target, hit).Type?.Name == "fairy")
|
if (move.GetHitData(target, hit).Type?.Name == "fairy")
|
||||||
{
|
{
|
||||||
var auraModifier = 5448f / 4096f;
|
var auraModifier = 5448f / 4096f;
|
||||||
var parameters = new Dictionary<StringKey, object?>
|
var args = new CustomTriggers.ModifyAuraEffectArgs(move, target, hit, auraModifier);
|
||||||
{
|
|
||||||
["aura_type"] = "fairy",
|
|
||||||
["modifier"] = auraModifier,
|
|
||||||
};
|
|
||||||
move.Battle.Sides.SelectMany(side => side.Pokemon).WhereNotNull()
|
move.Battle.Sides.SelectMany(side => side.Pokemon).WhereNotNull()
|
||||||
.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyAuraEffect, parameters));
|
.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyAuraEffect, args));
|
||||||
if (parameters.TryGetValue("modifier", out var modObj) && modObj is float modValue)
|
auraModifier = args.AuraEffect;
|
||||||
{
|
|
||||||
auraModifier = modValue;
|
|
||||||
}
|
|
||||||
modifier *= auraModifier;
|
modifier *= auraModifier;
|
||||||
move.Battle.EventHook.Invoke(new AbilityTriggerEvent(move.User));
|
move.Battle.EventHook.Invoke(new AbilityTriggerEvent(move.User));
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
|||||||
public class GrassPelt : Script
|
public class GrassPelt : Script
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
|
public override void ChangeIncomingMoveDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit,
|
||||||
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
|
uint offensiveStat, StatisticSet<uint> statisticSet, Statistic stat, ref uint value)
|
||||||
{
|
{
|
||||||
if (move.Battle.TerrainName == ScriptUtils.ResolveName<Terrain.GrassyTerrain>() && stat == Statistic.Defense)
|
if (move.Battle.TerrainName == ScriptUtils.ResolveName<Terrain.GrassyTerrain>() && stat == Statistic.Defense)
|
||||||
value = value.MultiplyOrMax(1.5f);
|
value = value.MultiplyOrMax(1.5f);
|
||||||
|
@ -41,11 +41,10 @@ public class IceBody : Script
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
|
public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
|
||||||
{
|
{
|
||||||
if (eventName != CustomTriggers.IgnoreHail || parameters is null)
|
if (eventName != CustomTriggers.IgnoreHail || args is not CustomTriggers.IgnoreHailArgs ignoreHailArgs)
|
||||||
return;
|
return;
|
||||||
|
ignoreHailArgs.Ignore = true;
|
||||||
parameters["ignoresHail"] = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,12 +9,15 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
|||||||
public class Infiltrator : Script
|
public class Infiltrator : Script
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
|
public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
|
||||||
{
|
{
|
||||||
if ((eventName != CustomTriggers.BypassProtection && eventName != CustomTriggers.BypassSubstitute) ||
|
if (eventName == CustomTriggers.BypassSubstitute && args is CustomTriggers.BypassSubstituteArgs bypassArgs)
|
||||||
parameters is null)
|
{
|
||||||
return;
|
bypassArgs.Bypass = true;
|
||||||
|
}
|
||||||
parameters["bypass"] = true;
|
else if (eventName == CustomTriggers.BypassProtection && args is CustomTriggers.BypassProtectionArgs customArgs)
|
||||||
|
{
|
||||||
|
customArgs.Bypass = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,11 +9,10 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
|||||||
public class LiquidOoze : Script
|
public class LiquidOoze : Script
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
|
public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
|
||||||
{
|
{
|
||||||
if (eventName != CustomTriggers.ModifyDrain || parameters is null)
|
if (eventName != CustomTriggers.ModifyDrain || args is not CustomTriggers.ModifyDrainArgs parameters)
|
||||||
return;
|
return;
|
||||||
|
parameters.Invert = true;
|
||||||
parameters["invert"] = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
25
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagicGuard.cs
Normal file
25
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagicGuard.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Magic Guard is an ability that prevents all damage except from direct attacks.
|
||||||
|
///
|
||||||
|
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Magic_Guard_(Ability)">Bulbapedia - Magic Guard</see>
|
||||||
|
/// </summary>
|
||||||
|
[Script(ScriptCategory.Ability, "magic_guard")]
|
||||||
|
public class MagicGuard : Script
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void ChangeIncomingDamage(IPokemon pokemon, DamageSource source, ref uint damage)
|
||||||
|
{
|
||||||
|
// Magic Guard doesn't work if the Pokémon is not in battle.
|
||||||
|
if (pokemon.BattleData is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (source is DamageSource.MoveDamage or DamageSource.Struggle or DamageSource.FormChange)
|
||||||
|
{
|
||||||
|
// If the damage is from a move, struggle, or form change, we don't prevent it.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
damage = 0;
|
||||||
|
}
|
||||||
|
}
|
25
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Magician.cs
Normal file
25
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Magician.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using PkmnLib.Static.Moves;
|
||||||
|
|
||||||
|
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Magician is an ability that steals the held item of a Pokémon it hits with a move.
|
||||||
|
///
|
||||||
|
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Magician_(Ability)">Bulbapedia - Magician</see>
|
||||||
|
/// </summary>
|
||||||
|
[Script(ScriptCategory.Ability, "magician")]
|
||||||
|
public class Magician : Script
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||||
|
{
|
||||||
|
if (move.UseMove.Category is MoveCategory.Status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (move.User.HeldItem is not null || target.HeldItem is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
move.Battle.EventHook.Invoke(new AbilityTriggerEvent(move.User));
|
||||||
|
_ = move.User.SetHeldItem(target.RemoveHeldItemForBattle());
|
||||||
|
}
|
||||||
|
}
|
31
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagmaArmor.cs
Normal file
31
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagmaArmor.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Magma Armor is an ability that prevents the Pokémon from being frozen.
|
||||||
|
///
|
||||||
|
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Magma_Armor_(Ability)">Bulbapedia - Magma Armor</see>
|
||||||
|
/// </summary>
|
||||||
|
[Script(ScriptCategory.Ability, "magma_armor")]
|
||||||
|
public class MagmaArmor : Script
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void PreventStatusChange(IPokemon pokemon, StringKey status, bool selfInflicted,
|
||||||
|
ref bool preventStatus)
|
||||||
|
{
|
||||||
|
if (status == ScriptUtils.ResolveName<Status.Frozen>())
|
||||||
|
{
|
||||||
|
pokemon.BattleData?.Battle.EventHook.Invoke(new AbilityTriggerEvent(pokemon));
|
||||||
|
preventStatus = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnSwitchIn(IPokemon pokemon, byte position)
|
||||||
|
{
|
||||||
|
if (pokemon.HasStatus(ScriptUtils.ResolveName<Status.Frozen>()))
|
||||||
|
{
|
||||||
|
pokemon.BattleData?.Battle.EventHook.Invoke(new AbilityTriggerEvent(pokemon));
|
||||||
|
pokemon.ClearStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagnetPull.cs
Normal file
24
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagnetPull.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Magnet Pull is an ability that prevents Steel-type Pokémon from fleeing or switching out.
|
||||||
|
///
|
||||||
|
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Magnet_Pull_(Ability)">Bulbapedia - Magnet Pull</see>
|
||||||
|
/// </summary>
|
||||||
|
[Script(ScriptCategory.Ability, "magnet_pull")]
|
||||||
|
public class MagnetPull : Script
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void PreventOpponentRunAway(IFleeChoice choice, ref bool prevent)
|
||||||
|
{
|
||||||
|
if (choice.User.Types.Any(x => x.Name == "steel"))
|
||||||
|
prevent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void PreventOpponentSwitch(ISwitchChoice choice, ref bool prevent)
|
||||||
|
{
|
||||||
|
if (choice.User.Types.Any(x => x.Name == "steel"))
|
||||||
|
prevent = true;
|
||||||
|
}
|
||||||
|
}
|
20
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MarvelScale.cs
Normal file
20
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MarvelScale.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marvel Scale is an ability that increases the Pokémon's Defense by 50% when it has a status condition.
|
||||||
|
///
|
||||||
|
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Marvel_Scale_(Ability)">Bulbapedia - Marvel Scale</see>
|
||||||
|
/// </summary>
|
||||||
|
[Script(ScriptCategory.Ability, "marvel_scale")]
|
||||||
|
public class MarvelScale : Script
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void ChangeIncomingMoveDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit,
|
||||||
|
uint offensiveStat, StatisticSet<uint> statisticSet, Statistic stat, ref uint value)
|
||||||
|
{
|
||||||
|
if (!target.StatusScript.IsEmpty && stat == Statistic.Defense)
|
||||||
|
{
|
||||||
|
value = value.MultiplyOrMax(1.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mega Launcher is an ability that boosts the power of aura and pulse moves.
|
||||||
|
///
|
||||||
|
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Mega_Launcher_(Ability)">Bulbapedia - Mega Launcher</see>
|
||||||
|
/// </summary>
|
||||||
|
[Script(ScriptCategory.Ability, "mega_launcher")]
|
||||||
|
public class MegaLauncher : Script
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
|
||||||
|
{
|
||||||
|
if (move.UseMove.HasFlag("pulse"))
|
||||||
|
{
|
||||||
|
basePower = basePower.MultiplyOrMax(1.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
|
||||||
|
{
|
||||||
|
if (eventName != CustomTriggers.ModifyHealPercent || args is not CustomTriggers.ModifyHealPercentArgs healArgs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (healArgs.Move.UseMove.HasFlag("pulse"))
|
||||||
|
healArgs.HealPercent *= 1.5f;
|
||||||
|
}
|
||||||
|
}
|
22
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Merciless.cs
Normal file
22
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Merciless.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using PkmnLib.Plugin.Gen7.Scripts.Status;
|
||||||
|
|
||||||
|
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Merciless is an ability that always results in critical hits against poisoned targets.
|
||||||
|
///
|
||||||
|
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Merciless_(Ability)">Bulbapedia - Merciless</see>
|
||||||
|
/// </summary>
|
||||||
|
[Script(ScriptCategory.Ability, "merciless")]
|
||||||
|
public class Merciless : Script
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void ChangeCriticalStage(IExecutingMove move, IPokemon target, byte hit, ref byte stage)
|
||||||
|
{
|
||||||
|
if (target.StatusScript.Script?.Name == ScriptUtils.ResolveName<Poisoned>() ||
|
||||||
|
target.StatusScript.Script?.Name == ScriptUtils.ResolveName<BadlyPoisoned>())
|
||||||
|
{
|
||||||
|
stage = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Minus.cs
Normal file
23
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Minus.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minus is an ability that boosts Special Attack if another ally has Plus or Minus.
|
||||||
|
///
|
||||||
|
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Minus_(Ability)">Bulbapedia - Minus</see>
|
||||||
|
/// </summary>
|
||||||
|
[Script(ScriptCategory.Ability, "minus")]
|
||||||
|
public class Minus : Script
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
|
||||||
|
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
|
||||||
|
{
|
||||||
|
var battleData = move.User.BattleData;
|
||||||
|
if (battleData is null)
|
||||||
|
return;
|
||||||
|
if (battleData.BattleSide.Pokemon.WhereNotNull().Any(x => x.IsUsable && x.ActiveAbility?.Name == "plus"))
|
||||||
|
{
|
||||||
|
value = value.MultiplyOrMax(1.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MistySurge.cs
Normal file
28
Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MistySurge.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Misty Surge is an ability that creates Misty Terrain when the Pokémon enters battle.
|
||||||
|
///
|
||||||
|
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Misty_Surge_(Ability)">Bulbapedia - Misty Surge</see>
|
||||||
|
/// </summary>
|
||||||
|
[Script(ScriptCategory.Ability, "misty_surge")]
|
||||||
|
public class MistySurge : Script
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnSwitchIn(IPokemon pokemon, byte position)
|
||||||
|
{
|
||||||
|
if (pokemon.BattleData?.Battle is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var terrainName = ScriptUtils.ResolveName<Terrain.MistyTerrain>();
|
||||||
|
if (pokemon.BattleData.Battle.TerrainName == terrainName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
EventBatchId batchId = new();
|
||||||
|
pokemon.BattleData.Battle.EventHook.Invoke(new AbilityTriggerEvent(pokemon)
|
||||||
|
{
|
||||||
|
BatchId = batchId,
|
||||||
|
});
|
||||||
|
pokemon.BattleData.Battle.SetTerrain(terrainName, batchId);
|
||||||
|
}
|
||||||
|
}
|
@ -4,29 +4,108 @@ public static class CustomTriggers
|
|||||||
{
|
{
|
||||||
public static readonly StringKey AuroraVeilDuration = "aurora_veil_duration";
|
public static readonly StringKey AuroraVeilDuration = "aurora_veil_duration";
|
||||||
|
|
||||||
public static readonly StringKey BindNumberOfTurns = "bind_number_of_turns";
|
public record AuroraVeilDurationArgs(IExecutingMove Move, int Duration) : ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public int Duration { get; set; } = Duration;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey BindPercentOfMaxHealth = "bind_percent_of_max_health";
|
public static readonly StringKey ModifyBind = "modify_bind";
|
||||||
|
|
||||||
|
public record ModifyBindArgs(IExecutingMove Move) : ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public int Duration { get; set; } = 5;
|
||||||
|
public float DamagePercent { get; set; } = 1f / 8f;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey IgnoreHail = "ignores_hail";
|
public static readonly StringKey IgnoreHail = "ignores_hail";
|
||||||
|
|
||||||
|
public record IgnoreHailArgs(IPokemon Pokemon) : ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public bool Ignore { get; set; } = false;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey LightScreenNumberOfTurns = "light_screen_number_of_turns";
|
public static readonly StringKey LightScreenNumberOfTurns = "light_screen_number_of_turns";
|
||||||
|
|
||||||
|
public record LightScreenNumberOfTurnsArgs(IExecutingMove Move, int Duration) : ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public int Duration { get; set; } = Duration;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey ReflectNumberOfTurns = "reflect_number_of_turns";
|
public static readonly StringKey ReflectNumberOfTurns = "reflect_number_of_turns";
|
||||||
|
|
||||||
|
public record ReflectNumberOfTurnsArgs(IExecutingMove Move, int Duration) : ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public int Duration { get; set; } = Duration;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey BypassSleep = "bypass_sleep";
|
public static readonly StringKey BypassSleep = "bypass_sleep";
|
||||||
|
|
||||||
|
public record BypassSleepArgs(IExecutingMove Move, bool Bypass) : ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public bool Bypass { get; set; } = Bypass;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey Whirlpool = "whirlpool";
|
public static readonly StringKey Whirlpool = "whirlpool";
|
||||||
|
|
||||||
|
public record WhirlpoolArgs(IExecutingMove Move, IPokemon Target, byte HitIndex, int Turns, float DamagePercent)
|
||||||
|
: ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public int Turns { get; set; } = Turns;
|
||||||
|
public float DamagePercent { get; set; } = DamagePercent;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey ModifyAuraEffect = "modify_aura_effect";
|
public static readonly StringKey ModifyAuraEffect = "modify_aura_effect";
|
||||||
|
|
||||||
|
public record ModifyAuraEffectArgs(IExecutingMove Move, IPokemon Target, byte HitIndex, float AuraEffect)
|
||||||
|
: ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public float AuraEffect { get; set; } = AuraEffect;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey BypassChargeMove = "bypass_charge_move";
|
public static readonly StringKey BypassChargeMove = "bypass_charge_move";
|
||||||
|
|
||||||
|
public record BypassChargeMoveArgs(IExecutingMove Move, bool Bypass) : ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public bool Bypass { get; set; } = Bypass;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey ModifySleepTurns = "modify_sleep_turns";
|
public static readonly StringKey ModifySleepTurns = "modify_sleep_turns";
|
||||||
|
|
||||||
|
public record ModifySleepTurnsArgs(IPokemon Pokemon, int Turns) : ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public int Turns { get; set; } = Turns;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey BypassProtection = "bypass_protection";
|
public static readonly StringKey BypassProtection = "bypass_protection";
|
||||||
|
|
||||||
|
public record BypassProtectionArgs(IExecutingMove Move, IPokemon Target, byte HitIndex, bool Bypass)
|
||||||
|
: ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public bool Bypass { get; set; } = Bypass;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey BypassSubstitute = "bypass_subsitute";
|
public static readonly StringKey BypassSubstitute = "bypass_subsitute";
|
||||||
|
|
||||||
|
public record BypassSubstituteArgs(IExecutingMove Move, IPokemon Target, byte HitIndex, bool Bypass)
|
||||||
|
: ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public bool Bypass { get; set; } = Bypass;
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StringKey ModifyDrain = "modify_drain";
|
public static readonly StringKey ModifyDrain = "modify_drain";
|
||||||
|
|
||||||
|
public record ModifyDrainArgs(IExecutingMove Move, IPokemon Target, byte Hit, uint Damage, uint Healed, bool Invert)
|
||||||
|
: ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public uint Healed { get; set; } = Healed;
|
||||||
|
public bool Invert { get; set; } = Invert;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StringKey ModifyHealPercent = "modify_heal_percent";
|
||||||
|
|
||||||
|
public record ModifyHealPercentArgs(IExecutingMove Move, IPokemon Target, byte Hit, float HealPercent)
|
||||||
|
: ICustomTriggerArgs
|
||||||
|
{
|
||||||
|
public float HealPercent { get; set; } = HealPercent;
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,9 +6,9 @@ namespace PkmnLib.Plugin.Gen7.Scripts.MoveVolatile;
|
|||||||
public class BypassSleepVolatile : Script
|
public class BypassSleepVolatile : Script
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
|
public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
|
||||||
{
|
{
|
||||||
if (eventName == CustomTriggers.BypassSleep && parameters != null)
|
if (eventName == CustomTriggers.BypassSleep && args is CustomTriggers.BypassSleepArgs bypassArgs)
|
||||||
parameters["bypass_sleep"] = true;
|
bypassArgs.Bypass = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -40,13 +40,9 @@ public class AuroraVeil : Script
|
|||||||
|
|
||||||
var side = battle.Sides[move.User.BattleData!.SideIndex];
|
var side = battle.Sides[move.User.BattleData!.SideIndex];
|
||||||
|
|
||||||
var numberOfTurns = 5;
|
var args = new CustomTriggers.AuroraVeilDurationArgs(move, 5);
|
||||||
var dict = new Dictionary<StringKey, object?>
|
move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.AuroraVeilDuration, args));
|
||||||
{
|
var numberOfTurns = args.Duration;
|
||||||
{ "duration", numberOfTurns },
|
|
||||||
};
|
|
||||||
move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.AuroraVeilDuration, dict));
|
|
||||||
numberOfTurns = (int)dict.GetOrDefault("duration", numberOfTurns)!;
|
|
||||||
|
|
||||||
var script = side.VolatileScripts.StackOrAdd(ScriptUtils.ResolveName<AuroraVeilEffect>(), () =>
|
var script = side.VolatileScripts.StackOrAdd(ScriptUtils.ResolveName<AuroraVeilEffect>(), () =>
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,7 @@ public class BellyDrum : Script
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
target.Damage(maxHealthHalved, DamageSource.Misc);
|
target.Damage(maxHealthHalved, DamageSource.Misc, forceDamage: true);
|
||||||
// Raising the user's Attack by 12 stages should always set it to +6.
|
// Raising the user's Attack by 12 stages should always set it to +6.
|
||||||
target.ChangeStatBoost(Statistic.Attack, 12, true, false);
|
target.ChangeStatBoost(Statistic.Attack, 12, true, false);
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,11 @@ public class Bind : Script
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||||
{
|
{
|
||||||
var bindTurnsParameters = new Dictionary<StringKey, object?> { { "bind_number_of_turns", 5 } };
|
var args = new CustomTriggers.ModifyBindArgs(move);
|
||||||
move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BindNumberOfTurns, bindTurnsParameters));
|
move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyBind, args));
|
||||||
var bindDamageParameters = new Dictionary<StringKey, object?> { { "bind_percent_of_max_health", 1f / 8f } };
|
|
||||||
move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BindPercentOfMaxHealth, bindDamageParameters));
|
|
||||||
|
|
||||||
var bindTurns = bindTurnsParameters.GetOrDefault("bind_number_of_turns", 5) as int? ?? 5;
|
var bindTurns = args.Duration;
|
||||||
var bindDamage = bindDamageParameters.GetOrDefault("bind_percent_of_max_health", 1f / 8f) as float? ?? 1f / 8f;
|
var bindDamage = args.DamagePercent;
|
||||||
|
|
||||||
var bindEffect = new BindEffect(target, bindTurns, bindDamage);
|
var bindEffect = new BindEffect(target, bindTurns, bindDamage);
|
||||||
target.Volatile.Add(bindEffect);
|
target.Volatile.Add(bindEffect);
|
||||||
|
@ -7,13 +7,10 @@ public class ChargeMove : Script
|
|||||||
{
|
{
|
||||||
public override void PreventMove(IExecutingMove move, ref bool prevent)
|
public override void PreventMove(IExecutingMove move, ref bool prevent)
|
||||||
{
|
{
|
||||||
var bypassCharge = false;
|
var args = new CustomTriggers.BypassChargeMoveArgs(move, false);
|
||||||
var parameters = new Dictionary<StringKey, object?>
|
move.RunScriptHook(script => script.CustomTrigger(CustomTriggers.BypassChargeMove, args));
|
||||||
{
|
if (args.Bypass)
|
||||||
{ "move", move },
|
return;
|
||||||
{ "bypassCharge", bypassCharge },
|
|
||||||
};
|
|
||||||
move.RunScriptHook(script => script.CustomTrigger(CustomTriggers.BypassChargeMove, parameters));
|
|
||||||
|
|
||||||
var chargeMoveEffect = move.User.Volatile.Get<ChargeMoveEffect>();
|
var chargeMoveEffect = move.User.Volatile.Get<ChargeMoveEffect>();
|
||||||
if (chargeMoveEffect != null && chargeMoveEffect.MoveName == move.UseMove.Name)
|
if (chargeMoveEffect != null && chargeMoveEffect.MoveName == move.UseMove.Name)
|
||||||
|
@ -16,7 +16,7 @@ public class Curse : Script
|
|||||||
return;
|
return;
|
||||||
if (move.User.Types.Contains(ghostType))
|
if (move.User.Types.Contains(ghostType))
|
||||||
{
|
{
|
||||||
move.User.Damage(move.User.CurrentHealth / 2, DamageSource.Misc);
|
move.User.Damage(move.User.CurrentHealth / 2, DamageSource.Misc, forceDamage: true);
|
||||||
if (move.User.CurrentHealth == 0)
|
if (move.User.CurrentHealth == 0)
|
||||||
return;
|
return;
|
||||||
target.Volatile.Add(new GhostCurseEffect(target));
|
target.Volatile.Add(new GhostCurseEffect(target));
|
||||||
|
@ -23,21 +23,15 @@ public class Drain : Script
|
|||||||
if (move.User.HasHeldItem("big_root"))
|
if (move.User.HasHeldItem("big_root"))
|
||||||
healed = (uint)(healed * 1.3f);
|
healed = (uint)(healed * 1.3f);
|
||||||
var invert = false;
|
var invert = false;
|
||||||
var parameters = new Dictionary<StringKey, object?>
|
|
||||||
{
|
var args = new CustomTriggers.ModifyDrainArgs(move, target, hit, damage, healed, invert);
|
||||||
{ "user", user },
|
target.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyDrain, args));
|
||||||
{ "target", target },
|
invert = args.Invert;
|
||||||
{ "damage", damage },
|
healed = args.Healed;
|
||||||
{ "healed", healed },
|
|
||||||
{ "invert", invert },
|
|
||||||
};
|
|
||||||
target.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyDrain, parameters));
|
|
||||||
if (parameters.TryGetValue("invert", out var invertObj) && invertObj is bool invertBool)
|
|
||||||
invert = invertBool;
|
|
||||||
|
|
||||||
if (invert)
|
if (invert)
|
||||||
{
|
{
|
||||||
user.Damage(damage, DamageSource.Misc);
|
user.Damage(healed, DamageSource.Misc);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -19,22 +19,14 @@ public class DreamEater : Script
|
|||||||
if (move.User.HasHeldItem("big_root"))
|
if (move.User.HasHeldItem("big_root"))
|
||||||
healed = (uint)(healed * 1.3f);
|
healed = (uint)(healed * 1.3f);
|
||||||
|
|
||||||
var invert = false;
|
var args = new CustomTriggers.ModifyDrainArgs(move, target, hit, damage, healed, false);
|
||||||
var parameters = new Dictionary<StringKey, object?>
|
target.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyDrain, args));
|
||||||
{
|
var invert = args.Invert;
|
||||||
{ "user", user },
|
healed = args.Healed;
|
||||||
{ "target", target },
|
|
||||||
{ "damage", damage },
|
|
||||||
{ "healed", healed },
|
|
||||||
{ "invert", invert },
|
|
||||||
};
|
|
||||||
target.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyDrain, parameters));
|
|
||||||
if (parameters.TryGetValue("invert", out var invertObj) && invertObj is bool invertBool)
|
|
||||||
invert = invertBool;
|
|
||||||
|
|
||||||
if (invert)
|
if (invert)
|
||||||
{
|
{
|
||||||
user.Damage(damage, DamageSource.Misc);
|
user.Damage(healed, DamageSource.Misc);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -19,6 +19,10 @@ public class HealPercent : Script
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||||
{
|
{
|
||||||
target.Heal((uint)(move.User.BoostedStats.Hp * _healPercent));
|
var args = new CustomTriggers.ModifyHealPercentArgs(move, target, hit, _healPercent);
|
||||||
|
move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyHealPercent, args));
|
||||||
|
var healPercent = args.HealPercent;
|
||||||
|
|
||||||
|
target.Heal(target.BoostedStats.Hp.MultiplyOrMax(healPercent));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,13 +13,9 @@ public class LightScreen : Script
|
|||||||
if (battleData == null)
|
if (battleData == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var turns = 5;
|
var args = new CustomTriggers.LightScreenNumberOfTurnsArgs(move, 5);
|
||||||
var dict = new Dictionary<StringKey, object?>
|
move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.LightScreenNumberOfTurns, args));
|
||||||
{
|
var turns = args.Duration;
|
||||||
{ "duration", turns },
|
|
||||||
};
|
|
||||||
move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.LightScreenNumberOfTurns, dict));
|
|
||||||
turns = (int)dict.GetOrDefault("duration", turns)!;
|
|
||||||
|
|
||||||
battleData.BattleSide.VolatileScripts.StackOrAdd(ScriptUtils.ResolveName<LightScreenEffect>(),
|
battleData.BattleSide.VolatileScripts.StackOrAdd(ScriptUtils.ResolveName<LightScreenEffect>(),
|
||||||
() => new LightScreenEffect(turns));
|
() => new LightScreenEffect(turns));
|
||||||
|
@ -12,12 +12,10 @@ public class Reflect : Script
|
|||||||
if (battleData is null)
|
if (battleData is null)
|
||||||
return;
|
return;
|
||||||
var numberOfTurns = 5;
|
var numberOfTurns = 5;
|
||||||
var dict = new Dictionary<StringKey, object?>
|
|
||||||
{
|
var args = new CustomTriggers.ReflectNumberOfTurnsArgs(move, numberOfTurns);
|
||||||
{ "duration", numberOfTurns },
|
move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ReflectNumberOfTurns, args));
|
||||||
};
|
numberOfTurns = args.Duration;
|
||||||
move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ReflectNumberOfTurns, dict));
|
|
||||||
numberOfTurns = (int)dict.GetOrDefault("duration", numberOfTurns)!;
|
|
||||||
|
|
||||||
battleData.BattleSide.VolatileScripts.Add(new Side.ReflectEffect(numberOfTurns));
|
battleData.BattleSide.VolatileScripts.Add(new Side.ReflectEffect(numberOfTurns));
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,10 @@ public class Whirlpool : Script
|
|||||||
{
|
{
|
||||||
var turns = move.Battle.Random.GetInt(4, 6);
|
var turns = move.Battle.Random.GetInt(4, 6);
|
||||||
var damagePercent = 0.125f;
|
var damagePercent = 0.125f;
|
||||||
var parameters = new Dictionary<StringKey, object?>
|
var args = new CustomTriggers.WhirlpoolArgs(move, target, hit, turns, damagePercent);
|
||||||
{
|
move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.Whirlpool, args));
|
||||||
{ "number_of_turns", turns },
|
turns = args.Turns;
|
||||||
{ "damage_percent", damagePercent },
|
damagePercent = args.DamagePercent;
|
||||||
};
|
|
||||||
move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.Whirlpool, parameters));
|
|
||||||
turns = parameters.GetValueOrDefault("number_of_turns", turns) as int? ?? turns;
|
|
||||||
damagePercent = parameters.GetValueOrDefault("damage_percent", damagePercent) as float? ?? damagePercent;
|
|
||||||
whirlpoolEffect.AddTargetedPokemon(target, turns, damagePercent);
|
whirlpoolEffect.AddTargetedPokemon(target, turns, damagePercent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ public class DestinyBondEffect : Script
|
|||||||
{
|
{
|
||||||
if (pokemon.BattleData?.Battle.ChoiceQueue?.LastRanChoice is not IMoveChoice lastChoice)
|
if (pokemon.BattleData?.Battle.ChoiceQueue?.LastRanChoice is not IMoveChoice lastChoice)
|
||||||
return;
|
return;
|
||||||
lastChoice.User.Damage(lastChoice.User.BoostedStats.Hp * 10, DamageSource.Misc);
|
lastChoice.User.Damage(lastChoice.User.BoostedStats.Hp * 10, DamageSource.Misc, forceDamage: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,17 +11,9 @@ public class ProtectionEffectScript : Script
|
|||||||
|
|
||||||
if (!executingMove.UseMove.HasFlag("protect"))
|
if (!executingMove.UseMove.HasFlag("protect"))
|
||||||
return;
|
return;
|
||||||
var bypass = false;
|
var args = new CustomTriggers.BypassProtectionArgs(executingMove, target, hitIndex, false);
|
||||||
var parameters = new Dictionary<StringKey, object?>
|
executingMove.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BypassProtection, args));
|
||||||
{
|
if (args.Bypass)
|
||||||
{ "target", target },
|
|
||||||
{ "move", executingMove },
|
|
||||||
{ "hitIndex", hitIndex },
|
|
||||||
{ "bypass", bypass },
|
|
||||||
};
|
|
||||||
executingMove.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BypassProtection, parameters));
|
|
||||||
bypass = parameters.GetValueOrDefault("bypass", false) as bool? ?? false;
|
|
||||||
if (bypass)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
block = true;
|
block = true;
|
||||||
|
@ -11,17 +11,9 @@ public class SubstituteEffect(uint health) : Script
|
|||||||
if (executingMove.UseMove.HasFlag("ignore-substitute"))
|
if (executingMove.UseMove.HasFlag("ignore-substitute"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var bypass = false;
|
var args = new CustomTriggers.BypassSubstituteArgs(executingMove, target, hitIndex, false);
|
||||||
var parameters = new Dictionary<StringKey, object?>
|
executingMove.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BypassSubstitute, args));
|
||||||
{
|
if (args.Bypass)
|
||||||
{ "target", target },
|
|
||||||
{ "move", executingMove },
|
|
||||||
{ "hitIndex", hitIndex },
|
|
||||||
{ "bypass", bypass },
|
|
||||||
};
|
|
||||||
executingMove.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BypassProtection, parameters));
|
|
||||||
bypass = parameters.GetValueOrDefault("bypass", false) as bool? ?? false;
|
|
||||||
if (bypass)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
block = true;
|
block = true;
|
||||||
|
@ -22,13 +22,9 @@ public class Sleep : Script
|
|||||||
{
|
{
|
||||||
// 1-3 turns of sleep
|
// 1-3 turns of sleep
|
||||||
Turns = battleData.Battle.Random.GetInt(1, 4);
|
Turns = battleData.Battle.Random.GetInt(1, 4);
|
||||||
source.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifySleepTurns,
|
var args = new CustomTriggers.ModifySleepTurnsArgs(pokemon, Turns);
|
||||||
new Dictionary<StringKey, object?>
|
source.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifySleepTurns, args));
|
||||||
{
|
Turns = Math.Max(1, args.Turns);
|
||||||
{ "pokemon", pokemon },
|
|
||||||
{ "turns", Turns },
|
|
||||||
}));
|
|
||||||
Turns = Math.Max(1, Turns);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,14 +41,9 @@ public class Sleep : Script
|
|||||||
if (move.UseMove.HasFlag("usable_while_asleep"))
|
if (move.UseMove.HasFlag("usable_while_asleep"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var bypass = false;
|
var args = new CustomTriggers.BypassSleepArgs(move, false);
|
||||||
var pars = new Dictionary<StringKey, object?>
|
move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BypassSleep, args));
|
||||||
{
|
if (args.Bypass)
|
||||||
{ "bypass_sleep", bypass },
|
|
||||||
};
|
|
||||||
move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BypassSleep, pars));
|
|
||||||
bypass = pars.GetValueOrDefault("bypass_sleep", false) as bool? ?? false;
|
|
||||||
if (bypass)
|
|
||||||
return;
|
return;
|
||||||
prevent = true;
|
prevent = true;
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,9 @@ public class Hail : Script, ILimitedTurnsScript
|
|||||||
continue;
|
continue;
|
||||||
if (pokemon.Types.Contains(iceType))
|
if (pokemon.Types.Contains(iceType))
|
||||||
continue;
|
continue;
|
||||||
var ignoresHail = false;
|
var args = new CustomTriggers.IgnoreHailArgs(pokemon);
|
||||||
pokemon.RunScriptHook(x => x.CustomTrigger(CustomTriggers.IgnoreHail, new Dictionary<StringKey, object?>
|
pokemon.RunScriptHook(x => x.CustomTrigger(CustomTriggers.IgnoreHail, args));
|
||||||
{
|
if (args.Ignore)
|
||||||
{ "ignoresHail", ignoresHail },
|
|
||||||
}));
|
|
||||||
if (ignoresHail)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var maxHealth = pokemon.BoostedStats.Hp;
|
var maxHealth = pokemon.BoostedStats.Hp;
|
||||||
|
@ -24,17 +24,15 @@ public class HarshSunlight : Script
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? parameters)
|
public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
|
||||||
{
|
{
|
||||||
if (eventName == CustomTriggers.BypassChargeMove)
|
if (eventName != CustomTriggers.BypassChargeMove)
|
||||||
{
|
|
||||||
if (parameters == null || !parameters.TryGetValue("move", out var moveObj) ||
|
|
||||||
moveObj is not IExecutingMove move)
|
|
||||||
return;
|
return;
|
||||||
if (move.UseMove.Name == "solar_beam" || move.UseMove.Name == "solar_blade")
|
if (args is not CustomTriggers.BypassChargeMoveArgs bypassArgs)
|
||||||
|
return;
|
||||||
|
if (bypassArgs.Move.UseMove.Name == "solar_beam" || bypassArgs.Move.UseMove.Name == "solar_blade")
|
||||||
{
|
{
|
||||||
parameters["bypassCharge"] = true;
|
bypassArgs.Bypass = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user