More abilities, refactor custom triggers to be typed.
All checks were successful
Build / Build (push) Successful in 48s

This commit is contained in:
2025-06-13 11:15:48 +02:00
parent 4326794611
commit 6d71de375e
43 changed files with 630 additions and 196 deletions

View File

@@ -10,21 +10,16 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
public class AuraBreak : Script
{
/// <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;
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;
}
}
}

View File

@@ -21,11 +21,11 @@ public class Comatose : Script
}
/// <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;
}
}
}

View File

@@ -15,17 +15,10 @@ public class DarkAura : Script
if (move.GetHitData(target, hit).Type?.Name == "dark")
{
var auraModifier = 5448f / 4096f;
var parameters = new Dictionary<StringKey, object?>
{
["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));
}

View File

@@ -11,15 +11,11 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
public class EarlyBird : Script
{
/// <inheritdoc />
public override void CustomTrigger(StringKey eventName, IDictionary<StringKey, object?>? 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;
}
}

View File

@@ -16,17 +16,10 @@ public class FairyAura : Script
if (move.GetHitData(target, hit).Type?.Name == "fairy")
{
var auraModifier = 5448f / 4096f;
var parameters = new Dictionary<StringKey, object?>
{
["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));
}

View File

@@ -10,8 +10,8 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
public class GrassPelt : Script
{
/// <inheritdoc />
public override void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
public override void ChangeIncomingMoveDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit,
uint offensiveStat, StatisticSet<uint> statisticSet, Statistic stat, ref uint value)
{
if (move.Battle.TerrainName == ScriptUtils.ResolveName<Terrain.GrassyTerrain>() && stat == Statistic.Defense)
value = value.MultiplyOrMax(1.5f);

View File

@@ -41,11 +41,10 @@ public class IceBody : Script
}
/// <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;
parameters["ignoresHail"] = true;
ignoreHailArgs.Ignore = true;
}
}

View File

@@ -9,12 +9,15 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
public class Infiltrator : Script
{
/// <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) ||
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;
}
}
}

View File

@@ -9,11 +9,10 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
public class LiquidOoze : Script
{
/// <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;
parameters["invert"] = true;
parameters.Invert = true;
}
}

View 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;
}
}

View 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());
}
}

View 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();
}
}
}

View 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;
}
}

View 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);
}
}
}

View File

@@ -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;
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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);
}
}