From 1579d46671e2f87ebe036a8d3181a2d386624deb Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Mon, 9 Jun 2025 15:24:37 +0200 Subject: [PATCH] More abilities --- .../Events/DisplaySpeciesChangeEvent.cs | 21 +++++++ PkmnLib.Dynamic/Models/Pokemon.cs | 53 ++++++++++-------- PkmnLib.Dynamic/ScriptHandling/Script.cs | 8 +++ .../PkmnLib.Plugin.Gen7/Data/Abilities.jsonc | 48 ++++++++++++---- .../Scripts/Abilities/HeavyMetal.cs | 16 ++++++ .../Scripts/Abilities/HoneyGather.cs | 12 ++++ .../Scripts/Abilities/HugePower.cs | 20 +++++++ .../Scripts/Abilities/Hustle.cs | 31 +++++++++++ .../Scripts/Abilities/Hydration.cs | 40 ++++++++++++++ .../Scripts/Abilities/HyperCutter.cs | 22 ++++++++ .../Scripts/Abilities/IceBody.cs | 51 +++++++++++++++++ .../Scripts/Abilities/Illuminate.cs | 12 ++++ .../Scripts/Abilities/Illusion.cs | 55 +++++++++++++++++++ .../Scripts/Abilities/Immunity.cs | 21 +++++++ .../Scripts/Abilities/Imposter.cs | 12 ++++ .../Scripts/Abilities/Infiltrator.cs | 20 +++++++ .../Scripts/CustomTriggers.cs | 4 ++ .../Scripts/Moves/Autotomize.cs | 21 ++++--- .../Scripts/Moves/BatonPass.cs | 3 + .../Scripts/Pokemon/AutotomizeEffect.cs | 21 +++++++ .../Scripts/Pokemon/ProtectionEffectScript.cs | 12 ++++ .../Scripts/Pokemon/SubstituteEffect.cs | 13 +++++ .../Scripts/Utils/BatonPassException.cs | 5 ++ 23 files changed, 480 insertions(+), 41 deletions(-) create mode 100644 PkmnLib.Dynamic/Events/DisplaySpeciesChangeEvent.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HeavyMetal.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HoneyGather.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HugePower.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Hustle.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Hydration.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HyperCutter.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/IceBody.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Illuminate.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Illusion.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Immunity.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Imposter.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Infiltrator.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/AutotomizeEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Utils/BatonPassException.cs diff --git a/PkmnLib.Dynamic/Events/DisplaySpeciesChangeEvent.cs b/PkmnLib.Dynamic/Events/DisplaySpeciesChangeEvent.cs new file mode 100644 index 0000000..ff53918 --- /dev/null +++ b/PkmnLib.Dynamic/Events/DisplaySpeciesChangeEvent.cs @@ -0,0 +1,21 @@ +using PkmnLib.Dynamic.Events; +using PkmnLib.Static.Species; + +namespace PkmnLib.Dynamic.Models; + +public class DisplaySpeciesChangeEvent : IEventData +{ + public IPokemon Pokemon { get; } + public ISpecies? Species { get; } + public IForm? Form { get; } + + public DisplaySpeciesChangeEvent(IPokemon pokemon, ISpecies? species, IForm? form) + { + Pokemon = pokemon; + Species = species; + Form = form; + } + + /// + public EventBatchId BatchId { get; init; } +} \ No newline at end of file diff --git a/PkmnLib.Dynamic/Models/Pokemon.cs b/PkmnLib.Dynamic/Models/Pokemon.cs index 76386ef..09b0ea1 100644 --- a/PkmnLib.Dynamic/Models/Pokemon.cs +++ b/PkmnLib.Dynamic/Models/Pokemon.cs @@ -41,6 +41,11 @@ public interface IPokemon : IScriptSource, IDeepCloneable /// IForm? DisplayForm { get; } + /// + /// Sets the display species and form of the Pokemon. This is used for abilities like Illusion. + /// + void SetDisplaySpecies(ISpecies? species, IForm? form); + /// /// The current level of the Pokemon. /// @@ -90,14 +95,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable /// /// The weight of the Pokemon in kilograms. /// - float WeightInKg { get; set; } - - /// - /// Sets the weight of the Pokémon in kilograms. Returns whether the weight was changed. - /// - /// The new weight in kilograms - /// - public bool ChangeWeightInKgBy(float weightInKg); + float WeightInKg { get; } /// /// The height of the Pokémon in meters. @@ -511,7 +509,6 @@ public class PokemonImpl : ScriptSource, IPokemon Types = form.Types.ToList(); Experience = library.StaticLibrary.GrowthRates.CalculateExperience(species.GrowthRate, level); - WeightInKg = form.Weight; HeightInMeters = form.Height; Happiness = species.BaseHappiness; Volatile = new ScriptSet(this); @@ -546,7 +543,6 @@ public class PokemonImpl : ScriptSource, IPokemon } CurrentHealth = serializedPokemon.CurrentHealth; - WeightInKg = form.Weight; HeightInMeters = form.Height; Happiness = serializedPokemon.Happiness; IndividualValues = serializedPokemon.IndividualValues.ToIndividualValueStatisticSet(); @@ -599,6 +595,18 @@ public class PokemonImpl : ScriptSource, IPokemon /// public IForm? DisplayForm { get; set; } + /// + public void SetDisplaySpecies(ISpecies? species, IForm? form) + { + DisplaySpecies = species; + DisplayForm = form; + + BattleData?.Battle.EventHook.Invoke(new DisplaySpeciesChangeEvent(this, species, form) + { + BatchId = new EventBatchId(), + }); + } + /// public LevelInt Level { get; private set; } @@ -660,18 +668,18 @@ public class PokemonImpl : ScriptSource, IPokemon public uint CurrentHealth { get; private set; } /// - public float WeightInKg { get; set; } - - /// - public bool ChangeWeightInKgBy(float weightInKg) + public float WeightInKg { - if (WeightInKg <= 0.1f) - return false; - var newWeight = WeightInKg + weightInKg; - if (newWeight <= 0.1f) - newWeight = 0.1f; - WeightInKg = newWeight; - return true; + get + { + var weight = Form.Weight; + if (BattleData is not null) + // ReSharper disable once AccessToModifiedClosure + this.RunScriptHook(script => script.ModifyWeight(ref weight)); + if (weight < 0.1f) + weight = 0.1f; + return weight; + } } /// @@ -958,7 +966,6 @@ public class PokemonImpl : ScriptSource, IPokemon Form = form; Types = form.Types.ToList(); - WeightInKg = form.Weight; HeightInMeters = form.Height; var newAbility = Form.GetAbility(AbilityIndex); @@ -1216,7 +1223,6 @@ public class PokemonImpl : ScriptSource, IPokemon if (!onBattleField) { Volatile.Clear(); - WeightInKg = Form.Weight; HeightInMeters = Form.Height; Types = Form.Types; OverrideAbility = null; @@ -1241,7 +1247,6 @@ public class PokemonImpl : ScriptSource, IPokemon var battleData = BattleData; BattleData = null; Volatile.Clear(); - WeightInKg = Form.Weight; HeightInMeters = Form.Height; Types = Form.Types; OverrideAbility = null; diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs index 6db2c4e..d335b94 100644 --- a/PkmnLib.Dynamic/ScriptHandling/Script.cs +++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs @@ -761,4 +761,12 @@ public abstract class Script : IDeepCloneable public virtual void OnWeatherChange(IBattle battle, StringKey? weatherName, StringKey? oldWeatherName) { } + + /// + /// Modifies the weight of a Pokemon. + /// + /// The weight in kilograms + public virtual void ModifyWeight(ref float weight) + { + } } \ 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 03d7e4e..75d4d21 100755 --- a/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc +++ b/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc @@ -184,6 +184,9 @@ "effect": "flash_fire" }, "flower_gift": { + "flags": [ + "cant_be_copied" + ], "effect": "flower_gift" }, "flower_veil": { @@ -193,6 +196,9 @@ "effect": "fluffy" }, "forecast": { + "flags": [ + "cant_be_copied" + ], "effect": "forecast" }, "forewarn": { @@ -240,26 +246,48 @@ "heatproof": { "effect": "heatproof" }, - "heavy_metal": {}, - "honey_gather": {}, - "huge_power": {}, - "hustle": {}, - "hydration": {}, - "hyper_cutter": {}, - "ice_body": {}, - "illuminate": {}, + "heavy_metal": { + "effect": "heavy_metal" + }, + "honey_gather": { + "effect": "honey_gather" + }, + "huge_power": { + "effect": "huge_power" + }, + "hustle": { + "effect": "hustle" + }, + "hydration": { + "effect": "hydration" + }, + "hyper_cutter": { + "effect": "hyper_cutter" + }, + "ice_body": { + "effect": "ice_body" + }, + "illuminate": { + "effect": "illuminate" + }, "illusion": { + "effect": "illusion", "flags": [ "cant_be_copied" ] }, - "immunity": {}, + "immunity": { + "effect": "immunity" + }, "imposter": { + "effect": "imposter", "flags": [ "cant_be_copied" ] }, - "infiltrator": {}, + "infiltrator": { + "effect": "infiltrator" + }, "innards_out": {}, "inner_focus": {}, "insomnia": {}, diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HeavyMetal.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HeavyMetal.cs new file mode 100644 index 0000000..5f8ad17 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HeavyMetal.cs @@ -0,0 +1,16 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Heavy Metal is an ability that doubles the Pokémon's weight. +/// +/// Bulbapedia - Heavy Metal +/// +[Script(ScriptCategory.Ability, "heavy_metal")] +public class HeavyMetal : Script +{ + /// + public override void ModifyWeight(ref float weight) + { + weight *= 2f; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HoneyGather.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HoneyGather.cs new file mode 100644 index 0000000..c2f6627 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HoneyGather.cs @@ -0,0 +1,12 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Honey Gather is an ability that may allow the Pokémon to collect Honey after battle. +/// +/// Bulbapedia - Honey Gather +/// +[Script(ScriptCategory.Ability, "honey_gather")] +public class HoneyGather : Script +{ + // No Effect in battle +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HugePower.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HugePower.cs new file mode 100644 index 0000000..82c76dc --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HugePower.cs @@ -0,0 +1,20 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Huge Power is an ability that doubles the Pokémon's Attack stat. +/// +/// Bulbapedia - Huge Power +/// +[Script(ScriptCategory.Ability, "huge_power")] +public class HugePower : Script +{ + /// + public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat, + ImmutableStatisticSet targetStats, Statistic stat, ref uint value) + { + if (stat == Statistic.Attack) + { + value = value.MultiplyOrMax(2); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Hustle.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Hustle.cs new file mode 100644 index 0000000..c6a7219 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Hustle.cs @@ -0,0 +1,31 @@ +using PkmnLib.Static.Moves; + +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Hustle is an ability that increases the Pokémon's Attack by 50% but lowers the accuracy of its physical moves. +/// +/// Bulbapedia - Hustle +/// +[Script(ScriptCategory.Ability, "hustle")] +public class Hustle : Script +{ + /// + public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat, + ImmutableStatisticSet targetStats, Statistic stat, ref uint value) + { + if (stat != Statistic.Attack) + return; + value = value.MultiplyOrMax(1.5f); + } + + /// + public override void ChangeAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, + ref int modifiedAccuracy) + { + if (executingMove.UseMove.Category == MoveCategory.Physical) + { + modifiedAccuracy = (int)(modifiedAccuracy * 0.8f); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Hydration.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Hydration.cs new file mode 100644 index 0000000..490f1f5 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Hydration.cs @@ -0,0 +1,40 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Hydration is an ability that heals status conditions if it is raining at the end of the turn. +/// +/// Bulbapedia - Hydration +/// +[Script(ScriptCategory.Ability, "hydration")] +public class Hydration : Script +{ + private IPokemon? _pokemon; + + /// + public override void OnAddedToParent(IScriptSource source) + { + if (source is not IPokemon pokemon) + throw new InvalidOperationException("Hydration can only be added to a Pokemon script source."); + _pokemon = pokemon; + } + + /// + public override void OnEndTurn(IBattle battle) + { + if (_pokemon is null) + return; + + if (battle.WeatherName != ScriptUtils.ResolveName()) + return; + if (_pokemon.StatusScript.IsEmpty) + return; + + EventBatchId batchId = new(); + + battle.EventHook.Invoke(new AbilityTriggerEvent(_pokemon) + { + BatchId = batchId, + }); + _pokemon.ClearStatus(batchId); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HyperCutter.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HyperCutter.cs new file mode 100644 index 0000000..e1003b7 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/HyperCutter.cs @@ -0,0 +1,22 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Hyper Cutter is an ability that prevents the Pokémon's Attack stat from being lowered by other Pokémon. +/// +/// Bulbapedia - Hyper Cutter +/// +[Script(ScriptCategory.Ability, "hyper_cutter")] +public class HyperCutter : Script +{ + /// + public override void PreventStatBoostChange(IPokemon target, Statistic stat, sbyte amount, bool selfInflicted, + ref bool prevent) + { + if (stat != Statistic.Attack) + return; + + // Prevent the Attack stat from being lowered by any means + if (amount < 0 && !selfInflicted) + prevent = true; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/IceBody.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/IceBody.cs new file mode 100644 index 0000000..67fbc60 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/IceBody.cs @@ -0,0 +1,51 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Ice Body is an ability that heals the Pokémon for 1/16 of its maximum HP each turn during hail. +/// +/// Bulbapedia - Ice Body +/// +[Script(ScriptCategory.Ability, "ice_body")] +public class IceBody : Script +{ + private IPokemon? _pokemon; + + /// + public override void OnAddedToParent(IScriptSource source) + { + if (source is not IPokemon pokemon) + throw new InvalidOperationException("Ice Body can only be added to a Pokemon script source."); + _pokemon = pokemon; + } + + /// + public override void OnEndTurn(IBattle battle) + { + if (_pokemon is null) + return; + + // Check if the weather is hail + if (battle.WeatherName != ScriptUtils.ResolveName()) + return; + + // Heal the Pokémon for 1/16 of its maximum HP + EventBatchId batchId = new(); + var healAmount = _pokemon.MaxHealth / 16; + _pokemon.Heal(healAmount, true, batchId); + + // Trigger the ability event + battle.EventHook.Invoke(new AbilityTriggerEvent(_pokemon) + { + BatchId = batchId, + }); + } + + /// + public override void CustomTrigger(StringKey eventName, IDictionary? parameters) + { + if (eventName != CustomTriggers.IgnoreHail || parameters is null) + return; + + parameters["ignoresHail"] = true; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Illuminate.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Illuminate.cs new file mode 100644 index 0000000..4a1f9e4 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Illuminate.cs @@ -0,0 +1,12 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Illuminate is an ability that increases the wild encounter rate when the Pokémon is leading the party. +/// +/// Bulbapedia - Illuminate +/// +[Script(ScriptCategory.Ability, "illuminate")] +public class Illuminate : Script +{ + // No effect in battle. +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Illusion.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Illusion.cs new file mode 100644 index 0000000..2685511 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Illusion.cs @@ -0,0 +1,55 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Illusion is an ability that disguises the Pokémon as the last non-fainted Pokémon in the party until it takes damage. +/// +/// Bulbapedia - Illusion +/// +[Script(ScriptCategory.Ability, "illusion")] +public class Illusion : Script +{ + private IPokemon? _pokemon; + + /// + public override void OnAddedToParent(IScriptSource source) + { + if (source is not IPokemon pokemon) + throw new InvalidOperationException("Illusion can only be added to a Pokemon script source."); + _pokemon = pokemon; + } + + /// + public override void OnSwitchIn(IPokemon pokemon, byte position) + { + var battleData = pokemon.BattleData; + if (battleData is null) + return; + var lastNonFaintedPokemon = battleData.Battle.Parties.FirstOrDefault(p => p.Party.Any(pkmn => pkmn == pokemon)) + ?.Party.WhereNotNull().FirstOrDefault(x => x.IsUsable); + if (lastNonFaintedPokemon is null || lastNonFaintedPokemon == pokemon) + return; + + pokemon.SetDisplaySpecies(lastNonFaintedPokemon.Species, lastNonFaintedPokemon.Form); + } + + /// + public override void OnRemove() + { + if (_pokemon is null) + return; + + _pokemon.SetDisplaySpecies(null, null); + _pokemon.BattleData?.Battle.EventHook.Invoke(new AbilityTriggerEvent(_pokemon)); + } + + /// + public override void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit) + { + if (_pokemon?.BattleData?.Battle is null) + return; + + // Remove the illusion when the Pokémon takes damage + _pokemon.SetDisplaySpecies(null, null); + _pokemon.BattleData.Battle.EventHook.Invoke(new AbilityTriggerEvent(_pokemon)); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Immunity.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Immunity.cs new file mode 100644 index 0000000..eb84141 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Immunity.cs @@ -0,0 +1,21 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Immunity is an ability that prevents the Pokémon from being poisoned. +/// +/// Bulbapedia - Immunity +/// +[Script(ScriptCategory.Ability, "immunity")] +public class Immunity : Script +{ + /// + public override void PreventStatusChange(IPokemon pokemon, StringKey status, bool selfInflicted, + ref bool preventStatus) + { + if (status == ScriptUtils.ResolveName() || + status == ScriptUtils.ResolveName()) + { + preventStatus = true; + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Imposter.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Imposter.cs new file mode 100644 index 0000000..9fdb34a --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Imposter.cs @@ -0,0 +1,12 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Imposter is an ability that transforms the Pokémon into its opponent upon entering battle. +/// +/// Bulbapedia - Imposter +/// +[Script(ScriptCategory.Ability, "imposter")] +public class Imposter : Script +{ + // TODO: Implement Imposter effect. +} \ 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 new file mode 100644 index 0000000..9069bab --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Infiltrator.cs @@ -0,0 +1,20 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Infiltrator is an ability that allows the Pokémon's moves to ignore the opposing side's barriers and substitutes. +/// +/// Bulbapedia - Infiltrator +/// +[Script(ScriptCategory.Ability, "infiltrator")] +public class Infiltrator : Script +{ + /// + public override void CustomTrigger(StringKey eventName, IDictionary? parameters) + { + if ((eventName != CustomTriggers.BypassProtection && eventName != CustomTriggers.BypassSubstitute) || + parameters is null) + return; + + parameters["bypass"] = true; + } +} \ 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 f39cfd0..132bf9b 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs @@ -23,4 +23,8 @@ public static class CustomTriggers public static readonly StringKey BypassChargeMove = "bypass_charge_move"; public static readonly StringKey ModifySleepTurns = "modify_sleep_turns"; + + public static readonly StringKey BypassProtection = "bypass_protection"; + + public static readonly StringKey BypassSubstitute = "bypass_subsitute"; } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Autotomize.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Autotomize.cs index f2cc786..f8e3034 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Autotomize.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Autotomize.cs @@ -1,3 +1,5 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + namespace PkmnLib.Plugin.Gen7.Scripts.Moves; /// @@ -21,13 +23,18 @@ public class Autotomize : Script public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) { var user = move.User; - if (user.ChangeStatBoost(Statistic.Speed, 2, true, false) && user.ChangeWeightInKgBy(-100.0f)) + + var existingEffect = user.Volatile.Get(); + var stacks = existingEffect?.Stacks ?? 0; + + if (!user.ChangeStatBoost(Statistic.Speed, 2, true, false) || !(user.WeightInKg - 100f * stacks >= 0.1f)) + return; + + user.Volatile.StackOrAdd(ScriptUtils.ResolveName(), () => new AutotomizeEffect()); + var battle = user.BattleData?.Battle; + battle?.EventHook.Invoke(new DialogEvent("pokemon_became_nimble", new Dictionary { - var battle = user.BattleData?.Battle; - battle?.EventHook.Invoke(new DialogEvent("pokemon_became_nimble", new Dictionary - { - { "pokemon", user }, - })); - } + { "pokemon", user }, + })); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BatonPass.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BatonPass.cs index 4d440b6..7a2d51d 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BatonPass.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BatonPass.cs @@ -1,3 +1,4 @@ +using PkmnLib.Plugin.Gen7.Scripts.Utils; using PkmnLib.Static.Utils; namespace PkmnLib.Plugin.Gen7.Scripts.Moves; @@ -36,6 +37,8 @@ public class BatonPass : Script foreach (var script in volatileScripts) { + if (script is not IBatonPassException) + return; toSwitch.Volatile.Add(script); } } diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/AutotomizeEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/AutotomizeEffect.cs new file mode 100644 index 0000000..396b9e7 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/AutotomizeEffect.cs @@ -0,0 +1,21 @@ +using PkmnLib.Plugin.Gen7.Scripts.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "autotomize")] +public class AutotomizeEffect : Script, IBatonPassException +{ + public int Stacks { get; private set; } = 1; + + /// + public override void Stack() + { + Stacks++; + } + + /// + public override void ModifyWeight(ref float weight) + { + weight -= 100f * Stacks; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ProtectionEffectScript.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ProtectionEffectScript.cs index e8efba2..e2fc3ee 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ProtectionEffectScript.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ProtectionEffectScript.cs @@ -11,6 +11,18 @@ 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) + return; block = true; } diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SubstituteEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SubstituteEffect.cs index d4bd91b..7890e1c 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SubstituteEffect.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SubstituteEffect.cs @@ -11,6 +11,19 @@ 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) + return; + block = true; var damage = executingMove.GetHitData(target, hitIndex).Damage; if (damage >= _health) diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Utils/BatonPassException.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Utils/BatonPassException.cs new file mode 100644 index 0000000..3d236de --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Utils/BatonPassException.cs @@ -0,0 +1,5 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Utils; + +public interface IBatonPassException +{ +} \ No newline at end of file