diff --git a/PkmnLib.Dynamic/Models/Pokemon.cs b/PkmnLib.Dynamic/Models/Pokemon.cs
index 5fb8cbb..ed95315 100644
--- a/PkmnLib.Dynamic/Models/Pokemon.cs
+++ b/PkmnLib.Dynamic/Models/Pokemon.cs
@@ -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.
diff --git a/PkmnLib.Dynamic/ScriptHandling/ICustomTriggerArgs.cs b/PkmnLib.Dynamic/ScriptHandling/ICustomTriggerArgs.cs
new file mode 100644
index 0000000..3b55ec5
--- /dev/null
+++ b/PkmnLib.Dynamic/ScriptHandling/ICustomTriggerArgs.cs
@@ -0,0 +1,126 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Linq.Expressions;
+using PkmnLib.Static.Utils;
+
+namespace PkmnLib.Dynamic.ScriptHandling;
+
+///
+/// Interface for custom trigger arguments.
+///
+public interface ICustomTriggerArgs;
+
+///
+/// Static helper methods for working with .
+///
+/// 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.
+///
+public static class CustomTriggerArgsHelpers
+{
+ private record TypeFuncs(
+ Dictionary> Getters,
+ Dictionary> Setters);
+
+ private static readonly Dictionary 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>(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>(assign, parameter, valueParameter)
+ .Compile();
+ });
+
+ TypeFuncInfo[type] = new TypeFuncs(getters, setters);
+ }
+
+ private static bool TryGetGetter(Type type, StringKey key,
+ [NotNullWhen(true)] out Func? 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? setter)
+ {
+ if (!TypeFuncInfo.ContainsKey(type))
+ PopulateForType(type);
+ return TypeFuncInfo[type].Setters.TryGetValue(key, out setter);
+ }
+
+ public static T 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}.");
+ 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(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);
+ }
+}
\ No newline at end of file
diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs
index b1eaf40..ca026d8 100644
--- a/PkmnLib.Dynamic/ScriptHandling/Script.cs
+++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs
@@ -356,6 +356,23 @@ public abstract class Script : IDeepCloneable
{
}
+ ///
+ /// This function allows a script to change the offensive stat value of an incoming move.
+ ///
+ public virtual void ChangeIncomingMoveOffensiveStatValue(IExecutingMove executingMove, IPokemon target,
+ byte hitNumber, uint defensiveStat, StatisticSet targetStats, Statistic offensive, ref uint offensiveStat)
+ {
+ }
+
+ ///
+ /// This function allows a script to change the defensive stat value of an incoming move.
+ ///
+ public virtual void ChangeIncomingMoveDefensiveStatValue(IExecutingMove executingMove, IPokemon target,
+ byte hitNumber, uint origOffensiveStat, StatisticSet targetStats, Statistic defensive,
+ ref uint defensiveStat)
+ {
+ }
+
///
/// 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.
///
- ///
- /// 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.
///
- public virtual void CustomTrigger(StringKey eventName, IDictionary? parameters)
+ public virtual void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
{
}
diff --git a/Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Abilities/MegaLauncherTests.cs b/Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Abilities/MegaLauncherTests.cs
new file mode 100644
index 0000000..3a5d326
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Abilities/MegaLauncherTests.cs
@@ -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();
+ var target = Substitute.For();
+ var healPercent = 0.5f;
+ var user = Substitute.For();
+ 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(amount => healedAmount = amount));
+ target.BoostedStats.Returns(new StatisticSet(100, 100, 100, 100, 100, 100));
+
+ // Act
+ healPercentScript.OnInitialize(new Dictionary
+ {
+ { "healPercent", healPercent },
+ });
+ healPercentScript.OnSecondaryEffect(move, target, 0);
+
+ // Assert
+ await Assert.That(healedAmount).IsEqualTo((uint)(target.BoostedStats.Hp * healPercent * 1.5f));
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc b/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc
index 858099f..3beb10c 100755
--- a/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc
+++ b/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc
@@ -342,15 +342,33 @@
"magic_bounce": {
"effect": "magic_bounce"
},
- "magic_guard": {},
- "magician": {},
- "magma_armor": {},
- "magnet_pull": {},
- "marvel_scale": {},
- "mega_launcher": {},
- "merciless": {},
- "minus": {},
- "misty_surge": {},
+ "magic_guard": {
+ "effect": "magic_guard"
+ },
+ "magician": {
+ "effect": "magician"
+ },
+ "magma_armor": {
+ "effect": "magma_armor"
+ },
+ "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": {},
"moody": {},
"motor_drive": {},
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Libraries/Battling/Gen7DamageCalculator.cs b/Plugins/PkmnLib.Plugin.Gen7/Libraries/Battling/Gen7DamageCalculator.cs
index 57508e0..5b2f5d4 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Libraries/Battling/Gen7DamageCalculator.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Libraries/Battling/Gen7DamageCalculator.cs
@@ -136,6 +136,10 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
defensiveStat, targetStats, offensive, ref offensiveStat));
executingMove.RunScriptHook(script => script.ChangeDefensiveStatValue(executingMove, target, hitNumber,
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;
executingMove.RunScriptHook(script =>
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/AuraBreak.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/AuraBreak.cs
index f19b45c..967da63 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/AuraBreak.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/AuraBreak.cs
@@ -10,21 +10,16 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
public class AuraBreak : Script
{
///
- public override void CustomTrigger(StringKey eventName, IDictionary? 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;
- if (!parameters.TryGetValue("aura_type", out var auraTypeObj) || auraTypeObj is not string auraType)
- return;
-
- if (auraType is "dark" or "fairy")
+ var typeName = auraArgs.Move.UseMove.MoveType.Name;
+ if (typeName == "dark" || typeName == "fairy")
{
- if (parameters.TryGetValue("modifier", out var modifierObj) && modifierObj is float)
- {
- // Reverse the aura effect by reducing power by 25%
- parameters["modifier"] = 0.75f;
- }
+ // Reverse the aura effect by reducing power by 25%
+ auraArgs.AuraEffect *= 0.75f;
}
}
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Comatose.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Comatose.cs
index ad41dac..92dd45d 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Comatose.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Comatose.cs
@@ -21,11 +21,11 @@ public class Comatose : Script
}
///
- public override void CustomTrigger(StringKey eventName, IDictionary? 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;
}
}
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/DarkAura.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/DarkAura.cs
index 2cd6d9f..82cefb3 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/DarkAura.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/DarkAura.cs
@@ -15,17 +15,10 @@ public class DarkAura : Script
if (move.GetHitData(target, hit).Type?.Name == "dark")
{
var auraModifier = 5448f / 4096f;
- var parameters = new Dictionary
- {
- ["aura_type"] = "dark",
- ["modifier"] = auraModifier,
- };
+ var args = new CustomTriggers.ModifyAuraEffectArgs(move, target, hit, auraModifier);
move.Battle.Sides.SelectMany(side => side.Pokemon).WhereNotNull()
- .RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyAuraEffect, parameters));
- if (parameters.TryGetValue("modifier", out var modObj) && modObj is float modValue)
- {
- auraModifier = modValue;
- }
+ .RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyAuraEffect, args));
+ auraModifier = args.AuraEffect;
modifier *= auraModifier;
move.Battle.EventHook.Invoke(new AbilityTriggerEvent(move.User));
}
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/EarlyBird.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/EarlyBird.cs
index c8be63f..4a7bab6 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/EarlyBird.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/EarlyBird.cs
@@ -11,15 +11,11 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
public class EarlyBird : Script
{
///
- public override void CustomTrigger(StringKey eventName, IDictionary? parameters)
+ public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
{
if (eventName != CustomTriggers.ModifySleepTurns)
return;
- if (parameters == null)
- return;
- if (parameters.TryGetValue("turns", out var turnsObj) && turnsObj is int turns)
- {
- parameters["turns"] = turns / 2;
- }
+ if (args is CustomTriggers.ModifySleepTurnsArgs sleepArgs)
+ sleepArgs.Turns /= 2;
}
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/FairyAura.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/FairyAura.cs
index fb08238..d1fb2f0 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/FairyAura.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/FairyAura.cs
@@ -16,17 +16,10 @@ public class FairyAura : Script
if (move.GetHitData(target, hit).Type?.Name == "fairy")
{
var auraModifier = 5448f / 4096f;
- var parameters = new Dictionary
- {
- ["aura_type"] = "fairy",
- ["modifier"] = auraModifier,
- };
+ var args = new CustomTriggers.ModifyAuraEffectArgs(move, target, hit, auraModifier);
move.Battle.Sides.SelectMany(side => side.Pokemon).WhereNotNull()
- .RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyAuraEffect, parameters));
- if (parameters.TryGetValue("modifier", out var modObj) && modObj is float modValue)
- {
- auraModifier = modValue;
- }
+ .RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyAuraEffect, args));
+ auraModifier = args.AuraEffect;
modifier *= auraModifier;
move.Battle.EventHook.Invoke(new AbilityTriggerEvent(move.User));
}
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/GrassPelt.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/GrassPelt.cs
index 1e3ecd0..2d72de5 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/GrassPelt.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/GrassPelt.cs
@@ -10,8 +10,8 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
public class GrassPelt : Script
{
///
- public override void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
- ImmutableStatisticSet targetStats, Statistic stat, ref uint value)
+ public override void ChangeIncomingMoveDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit,
+ uint offensiveStat, StatisticSet statisticSet, Statistic stat, ref uint value)
{
if (move.Battle.TerrainName == ScriptUtils.ResolveName() && stat == Statistic.Defense)
value = value.MultiplyOrMax(1.5f);
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/IceBody.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/IceBody.cs
index 67fbc60..235e67f 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/IceBody.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/IceBody.cs
@@ -41,11 +41,10 @@ public class IceBody : Script
}
///
- public override void CustomTrigger(StringKey eventName, IDictionary? 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;
-
- parameters["ignoresHail"] = true;
+ ignoreHailArgs.Ignore = true;
}
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Infiltrator.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Infiltrator.cs
index 9069bab..979bb6b 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Infiltrator.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Infiltrator.cs
@@ -9,12 +9,15 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
public class Infiltrator : Script
{
///
- public override void CustomTrigger(StringKey eventName, IDictionary? parameters)
+ public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
{
- if ((eventName != CustomTriggers.BypassProtection && eventName != CustomTriggers.BypassSubstitute) ||
- parameters is null)
- return;
-
- parameters["bypass"] = true;
+ if (eventName == CustomTriggers.BypassSubstitute && args is CustomTriggers.BypassSubstituteArgs bypassArgs)
+ {
+ bypassArgs.Bypass = true;
+ }
+ else if (eventName == CustomTriggers.BypassProtection && args is CustomTriggers.BypassProtectionArgs customArgs)
+ {
+ customArgs.Bypass = true;
+ }
}
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/LiquidOoze.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/LiquidOoze.cs
index 1902f59..47e9f29 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/LiquidOoze.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/LiquidOoze.cs
@@ -9,11 +9,10 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
public class LiquidOoze : Script
{
///
- public override void CustomTrigger(StringKey eventName, IDictionary? 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;
-
- parameters["invert"] = true;
+ parameters.Invert = true;
}
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagicGuard.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagicGuard.cs
new file mode 100644
index 0000000..5a150e8
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagicGuard.cs
@@ -0,0 +1,25 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Magic Guard is an ability that prevents all damage except from direct attacks.
+///
+/// Bulbapedia - Magic Guard
+///
+[Script(ScriptCategory.Ability, "magic_guard")]
+public class MagicGuard : Script
+{
+ ///
+ 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;
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Magician.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Magician.cs
new file mode 100644
index 0000000..47956f3
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Magician.cs
@@ -0,0 +1,25 @@
+using PkmnLib.Static.Moves;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Magician is an ability that steals the held item of a Pokémon it hits with a move.
+///
+/// Bulbapedia - Magician
+///
+[Script(ScriptCategory.Ability, "magician")]
+public class Magician : Script
+{
+ ///
+ 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());
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagmaArmor.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagmaArmor.cs
new file mode 100644
index 0000000..949865a
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagmaArmor.cs
@@ -0,0 +1,31 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Magma Armor is an ability that prevents the Pokémon from being frozen.
+///
+/// Bulbapedia - Magma Armor
+///
+[Script(ScriptCategory.Ability, "magma_armor")]
+public class MagmaArmor : Script
+{
+ ///
+ public override void PreventStatusChange(IPokemon pokemon, StringKey status, bool selfInflicted,
+ ref bool preventStatus)
+ {
+ if (status == ScriptUtils.ResolveName())
+ {
+ pokemon.BattleData?.Battle.EventHook.Invoke(new AbilityTriggerEvent(pokemon));
+ preventStatus = true;
+ }
+ }
+
+ ///
+ public override void OnSwitchIn(IPokemon pokemon, byte position)
+ {
+ if (pokemon.HasStatus(ScriptUtils.ResolveName()))
+ {
+ pokemon.BattleData?.Battle.EventHook.Invoke(new AbilityTriggerEvent(pokemon));
+ pokemon.ClearStatus();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagnetPull.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagnetPull.cs
new file mode 100644
index 0000000..38c31c4
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MagnetPull.cs
@@ -0,0 +1,24 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Magnet Pull is an ability that prevents Steel-type Pokémon from fleeing or switching out.
+///
+/// Bulbapedia - Magnet Pull
+///
+[Script(ScriptCategory.Ability, "magnet_pull")]
+public class MagnetPull : Script
+{
+ ///
+ public override void PreventOpponentRunAway(IFleeChoice choice, ref bool prevent)
+ {
+ if (choice.User.Types.Any(x => x.Name == "steel"))
+ prevent = true;
+ }
+
+ ///
+ public override void PreventOpponentSwitch(ISwitchChoice choice, ref bool prevent)
+ {
+ if (choice.User.Types.Any(x => x.Name == "steel"))
+ prevent = true;
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MarvelScale.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MarvelScale.cs
new file mode 100644
index 0000000..5c0f9de
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MarvelScale.cs
@@ -0,0 +1,20 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Marvel Scale is an ability that increases the Pokémon's Defense by 50% when it has a status condition.
+///
+/// Bulbapedia - Marvel Scale
+///
+[Script(ScriptCategory.Ability, "marvel_scale")]
+public class MarvelScale : Script
+{
+ ///
+ public override void ChangeIncomingMoveDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit,
+ uint offensiveStat, StatisticSet statisticSet, Statistic stat, ref uint value)
+ {
+ if (!target.StatusScript.IsEmpty && stat == Statistic.Defense)
+ {
+ value = value.MultiplyOrMax(1.5f);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MegaLauncher.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MegaLauncher.cs
new file mode 100644
index 0000000..c28879d
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MegaLauncher.cs
@@ -0,0 +1,29 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Mega Launcher is an ability that boosts the power of aura and pulse moves.
+///
+/// Bulbapedia - Mega Launcher
+///
+[Script(ScriptCategory.Ability, "mega_launcher")]
+public class MegaLauncher : Script
+{
+ ///
+ public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
+ {
+ if (move.UseMove.HasFlag("pulse"))
+ {
+ basePower = basePower.MultiplyOrMax(1.5f);
+ }
+ }
+
+ ///
+ 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;
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Merciless.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Merciless.cs
new file mode 100644
index 0000000..7dc6f50
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Merciless.cs
@@ -0,0 +1,22 @@
+using PkmnLib.Plugin.Gen7.Scripts.Status;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Merciless is an ability that always results in critical hits against poisoned targets.
+///
+/// Bulbapedia - Merciless
+///
+[Script(ScriptCategory.Ability, "merciless")]
+public class Merciless : Script
+{
+ ///
+ public override void ChangeCriticalStage(IExecutingMove move, IPokemon target, byte hit, ref byte stage)
+ {
+ if (target.StatusScript.Script?.Name == ScriptUtils.ResolveName() ||
+ target.StatusScript.Script?.Name == ScriptUtils.ResolveName())
+ {
+ stage = 100;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Minus.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Minus.cs
new file mode 100644
index 0000000..5d42f24
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Minus.cs
@@ -0,0 +1,23 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Minus is an ability that boosts Special Attack if another ally has Plus or Minus.
+///
+/// Bulbapedia - Minus
+///
+[Script(ScriptCategory.Ability, "minus")]
+public class Minus : Script
+{
+ ///
+ public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
+ ImmutableStatisticSet 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MistySurge.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MistySurge.cs
new file mode 100644
index 0000000..8fab36f
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MistySurge.cs
@@ -0,0 +1,28 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Misty Surge is an ability that creates Misty Terrain when the Pokémon enters battle.
+///
+/// Bulbapedia - Misty Surge
+///
+[Script(ScriptCategory.Ability, "misty_surge")]
+public class MistySurge : Script
+{
+ ///
+ public override void OnSwitchIn(IPokemon pokemon, byte position)
+ {
+ if (pokemon.BattleData?.Battle is null)
+ return;
+
+ var terrainName = ScriptUtils.ResolveName();
+ 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);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs
index be64c6c..477f58d 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs
@@ -4,29 +4,108 @@ public static class CustomTriggers
{
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 record IgnoreHailArgs(IPokemon Pokemon) : ICustomTriggerArgs
+ {
+ public bool Ignore { get; set; } = false;
+ }
+
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 record ReflectNumberOfTurnsArgs(IExecutingMove Move, int Duration) : ICustomTriggerArgs
+ {
+ public int Duration { get; set; } = Duration;
+ }
+
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 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 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 record BypassChargeMoveArgs(IExecutingMove Move, bool Bypass) : ICustomTriggerArgs
+ {
+ public bool Bypass { get; set; } = Bypass;
+ }
+
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 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 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 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;
+ }
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/MoveVolatile/BypassSleepVolatile.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/MoveVolatile/BypassSleepVolatile.cs
index d021f25..9c4ac2a 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/MoveVolatile/BypassSleepVolatile.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/MoveVolatile/BypassSleepVolatile.cs
@@ -6,9 +6,9 @@ namespace PkmnLib.Plugin.Gen7.Scripts.MoveVolatile;
public class BypassSleepVolatile : Script
{
///
- public override void CustomTrigger(StringKey eventName, IDictionary? parameters)
+ public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
{
- if (eventName == CustomTriggers.BypassSleep && parameters != null)
- parameters["bypass_sleep"] = true;
+ if (eventName == CustomTriggers.BypassSleep && args is CustomTriggers.BypassSleepArgs bypassArgs)
+ bypassArgs.Bypass = true;
}
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/AuroraVeil.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/AuroraVeil.cs
index b5809de..0e8c381 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/AuroraVeil.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/AuroraVeil.cs
@@ -40,13 +40,9 @@ public class AuroraVeil : Script
var side = battle.Sides[move.User.BattleData!.SideIndex];
- var numberOfTurns = 5;
- var dict = new Dictionary
- {
- { "duration", numberOfTurns },
- };
- move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.AuroraVeilDuration, dict));
- numberOfTurns = (int)dict.GetOrDefault("duration", numberOfTurns)!;
+ var args = new CustomTriggers.AuroraVeilDurationArgs(move, 5);
+ move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.AuroraVeilDuration, args));
+ var numberOfTurns = args.Duration;
var script = side.VolatileScripts.StackOrAdd(ScriptUtils.ResolveName(), () =>
{
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BellyDrum.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BellyDrum.cs
index aba839c..3fbaa5b 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BellyDrum.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BellyDrum.cs
@@ -13,7 +13,7 @@ public class BellyDrum : Script
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.
target.ChangeStatBoost(Statistic.Attack, 12, true, false);
}
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bind.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bind.cs
index 2b7a311..492bb47 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bind.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bind.cs
@@ -9,13 +9,11 @@ public class Bind : Script
///
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
- var bindTurnsParameters = new Dictionary { { "bind_number_of_turns", 5 } };
- move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BindNumberOfTurns, bindTurnsParameters));
- var bindDamageParameters = new Dictionary { { "bind_percent_of_max_health", 1f / 8f } };
- move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BindPercentOfMaxHealth, bindDamageParameters));
+ var args = new CustomTriggers.ModifyBindArgs(move);
+ move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyBind, args));
- var bindTurns = bindTurnsParameters.GetOrDefault("bind_number_of_turns", 5) as int? ?? 5;
- var bindDamage = bindDamageParameters.GetOrDefault("bind_percent_of_max_health", 1f / 8f) as float? ?? 1f / 8f;
+ var bindTurns = args.Duration;
+ var bindDamage = args.DamagePercent;
var bindEffect = new BindEffect(target, bindTurns, bindDamage);
target.Volatile.Add(bindEffect);
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ChargeMove.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ChargeMove.cs
index d225498..3fafa87 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ChargeMove.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ChargeMove.cs
@@ -7,13 +7,10 @@ public class ChargeMove : Script
{
public override void PreventMove(IExecutingMove move, ref bool prevent)
{
- var bypassCharge = false;
- var parameters = new Dictionary
- {
- { "move", move },
- { "bypassCharge", bypassCharge },
- };
- move.RunScriptHook(script => script.CustomTrigger(CustomTriggers.BypassChargeMove, parameters));
+ var args = new CustomTriggers.BypassChargeMoveArgs(move, false);
+ move.RunScriptHook(script => script.CustomTrigger(CustomTriggers.BypassChargeMove, args));
+ if (args.Bypass)
+ return;
var chargeMoveEffect = move.User.Volatile.Get();
if (chargeMoveEffect != null && chargeMoveEffect.MoveName == move.UseMove.Name)
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Curse.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Curse.cs
index 39ae511..3478291 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Curse.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Curse.cs
@@ -16,7 +16,7 @@ public class Curse : Script
return;
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)
return;
target.Volatile.Add(new GhostCurseEffect(target));
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Drain.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Drain.cs
index 9a9474f..fa16e75 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Drain.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Drain.cs
@@ -23,21 +23,15 @@ public class Drain : Script
if (move.User.HasHeldItem("big_root"))
healed = (uint)(healed * 1.3f);
var invert = false;
- var parameters = new Dictionary
- {
- { "user", user },
- { "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;
+
+ var args = new CustomTriggers.ModifyDrainArgs(move, target, hit, damage, healed, invert);
+ target.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyDrain, args));
+ invert = args.Invert;
+ healed = args.Healed;
if (invert)
{
- user.Damage(damage, DamageSource.Misc);
+ user.Damage(healed, DamageSource.Misc);
}
else
{
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DreamEater.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DreamEater.cs
index 32202cc..721bd11 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DreamEater.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DreamEater.cs
@@ -19,22 +19,14 @@ public class DreamEater : Script
if (move.User.HasHeldItem("big_root"))
healed = (uint)(healed * 1.3f);
- var invert = false;
- var parameters = new Dictionary
- {
- { "user", user },
- { "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;
+ var args = new CustomTriggers.ModifyDrainArgs(move, target, hit, damage, healed, false);
+ target.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyDrain, args));
+ var invert = args.Invert;
+ healed = args.Healed;
if (invert)
{
- user.Damage(damage, DamageSource.Misc);
+ user.Damage(healed, DamageSource.Misc);
}
else
{
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/HealPercent.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/HealPercent.cs
index 4e6480d..8230286 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/HealPercent.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/HealPercent.cs
@@ -19,6 +19,10 @@ public class HealPercent : Script
///
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));
}
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/LightScreen.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/LightScreen.cs
index a8a6016..16fa1cb 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/LightScreen.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/LightScreen.cs
@@ -13,13 +13,9 @@ public class LightScreen : Script
if (battleData == null)
return;
- var turns = 5;
- var dict = new Dictionary
- {
- { "duration", turns },
- };
- move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.LightScreenNumberOfTurns, dict));
- turns = (int)dict.GetOrDefault("duration", turns)!;
+ var args = new CustomTriggers.LightScreenNumberOfTurnsArgs(move, 5);
+ move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.LightScreenNumberOfTurns, args));
+ var turns = args.Duration;
battleData.BattleSide.VolatileScripts.StackOrAdd(ScriptUtils.ResolveName(),
() => new LightScreenEffect(turns));
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Reflect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Reflect.cs
index 5780b5c..5058b75 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Reflect.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Reflect.cs
@@ -12,12 +12,10 @@ public class Reflect : Script
if (battleData is null)
return;
var numberOfTurns = 5;
- var dict = new Dictionary
- {
- { "duration", numberOfTurns },
- };
- move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ReflectNumberOfTurns, dict));
- numberOfTurns = (int)dict.GetOrDefault("duration", numberOfTurns)!;
+
+ var args = new CustomTriggers.ReflectNumberOfTurnsArgs(move, numberOfTurns);
+ move.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ReflectNumberOfTurns, args));
+ numberOfTurns = args.Duration;
battleData.BattleSide.VolatileScripts.Add(new Side.ReflectEffect(numberOfTurns));
}
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Whirlpool.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Whirlpool.cs
index a456b51..af9b84b 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Whirlpool.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Whirlpool.cs
@@ -14,14 +14,10 @@ public class Whirlpool : Script
{
var turns = move.Battle.Random.GetInt(4, 6);
var damagePercent = 0.125f;
- var parameters = new Dictionary
- {
- { "number_of_turns", turns },
- { "damage_percent", 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;
+ var args = new CustomTriggers.WhirlpoolArgs(move, target, hit, turns, damagePercent);
+ move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.Whirlpool, args));
+ turns = args.Turns;
+ damagePercent = args.DamagePercent;
whirlpoolEffect.AddTargetedPokemon(target, turns, damagePercent);
}
}
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DestinyBondEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DestinyBondEffect.cs
index 4866b47..5e5b5c5 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DestinyBondEffect.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/DestinyBondEffect.cs
@@ -10,7 +10,7 @@ public class DestinyBondEffect : Script
{
if (pokemon.BattleData?.Battle.ChoiceQueue?.LastRanChoice is not IMoveChoice lastChoice)
return;
- lastChoice.User.Damage(lastChoice.User.BoostedStats.Hp * 10, DamageSource.Misc);
+ lastChoice.User.Damage(lastChoice.User.BoostedStats.Hp * 10, DamageSource.Misc, forceDamage: true);
}
}
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ProtectionEffectScript.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ProtectionEffectScript.cs
index e2fc3ee..d8d7a89 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ProtectionEffectScript.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ProtectionEffectScript.cs
@@ -11,17 +11,9 @@ public class ProtectionEffectScript : Script
if (!executingMove.UseMove.HasFlag("protect"))
return;
- var bypass = false;
- var parameters = new Dictionary
- {
- { "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)
+ var args = new CustomTriggers.BypassProtectionArgs(executingMove, target, hitIndex, false);
+ executingMove.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BypassProtection, args));
+ if (args.Bypass)
return;
block = true;
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SubstituteEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SubstituteEffect.cs
index 7890e1c..e5de7fc 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SubstituteEffect.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SubstituteEffect.cs
@@ -11,17 +11,9 @@ public class SubstituteEffect(uint health) : Script
if (executingMove.UseMove.HasFlag("ignore-substitute"))
return;
- var bypass = false;
- var parameters = new Dictionary
- {
- { "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)
+ var args = new CustomTriggers.BypassSubstituteArgs(executingMove, target, hitIndex, false);
+ executingMove.User.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BypassSubstitute, args));
+ if (args.Bypass)
return;
block = true;
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Sleep.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Sleep.cs
index e2c0d7c..eeb8376 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Sleep.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Sleep.cs
@@ -22,13 +22,9 @@ public class Sleep : Script
{
// 1-3 turns of sleep
Turns = battleData.Battle.Random.GetInt(1, 4);
- source.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifySleepTurns,
- new Dictionary
- {
- { "pokemon", pokemon },
- { "turns", Turns },
- }));
- Turns = Math.Max(1, Turns);
+ var args = new CustomTriggers.ModifySleepTurnsArgs(pokemon, Turns);
+ source.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifySleepTurns, args));
+ Turns = Math.Max(1, args.Turns);
}
}
@@ -45,14 +41,9 @@ public class Sleep : Script
if (move.UseMove.HasFlag("usable_while_asleep"))
return;
- var bypass = false;
- var pars = new Dictionary
- {
- { "bypass_sleep", bypass },
- };
- move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BypassSleep, pars));
- bypass = pars.GetValueOrDefault("bypass_sleep", false) as bool? ?? false;
- if (bypass)
+ var args = new CustomTriggers.BypassSleepArgs(move, false);
+ move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.BypassSleep, args));
+ if (args.Bypass)
return;
prevent = true;
}
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Hail.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Hail.cs
index f5a93c2..2aa53a4 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Hail.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Hail.cs
@@ -29,12 +29,9 @@ public class Hail : Script, ILimitedTurnsScript
continue;
if (pokemon.Types.Contains(iceType))
continue;
- var ignoresHail = false;
- pokemon.RunScriptHook(x => x.CustomTrigger(CustomTriggers.IgnoreHail, new Dictionary
- {
- { "ignoresHail", ignoresHail },
- }));
- if (ignoresHail)
+ var args = new CustomTriggers.IgnoreHailArgs(pokemon);
+ pokemon.RunScriptHook(x => x.CustomTrigger(CustomTriggers.IgnoreHail, args));
+ if (args.Ignore)
continue;
var maxHealth = pokemon.BoostedStats.Hp;
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/HarshSunlight.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/HarshSunlight.cs
index a83e0e4..db0d8c7 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/HarshSunlight.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/HarshSunlight.cs
@@ -24,17 +24,15 @@ public class HarshSunlight : Script
}
///
- public override void CustomTrigger(StringKey eventName, IDictionary? parameters)
+ public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
{
- if (eventName == CustomTriggers.BypassChargeMove)
+ if (eventName != CustomTriggers.BypassChargeMove)
+ return;
+ if (args is not CustomTriggers.BypassChargeMoveArgs bypassArgs)
+ return;
+ if (bypassArgs.Move.UseMove.Name == "solar_beam" || bypassArgs.Move.UseMove.Name == "solar_blade")
{
- if (parameters == null || !parameters.TryGetValue("move", out var moveObj) ||
- moveObj is not IExecutingMove move)
- return;
- if (move.UseMove.Name == "solar_beam" || move.UseMove.Name == "solar_blade")
- {
- parameters["bypassCharge"] = true;
- }
+ bypassArgs.Bypass = true;
}
}