From cd6095455abd9e2d596be03b7e22d5d9a52cd245 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sun, 15 Jun 2025 13:20:58 +0200 Subject: [PATCH] More abilities --- PkmnLib.Dynamic/Models/Pokemon.cs | 23 +++++++---- PkmnLib.Static/Utils/NumericHelpers.cs | 18 ++++++++ .../PkmnLib.Plugin.Gen7/Data/Abilities.jsonc | 41 ++++++++++++++----- .../Scripts/Abilities/Harvest.cs | 2 +- .../Scripts/Abilities/Magician.cs | 2 +- .../Scripts/Abilities/Pickpocket.cs | 2 +- .../Scripts/Abilities/Triage.cs | 20 +++++++++ .../Scripts/Abilities/Truant.cs | 18 ++++++++ .../Scripts/Abilities/Unaware.cs | 24 +++++++++++ .../Scripts/Abilities/Unburden.cs | 19 +++++++++ .../Scripts/Abilities/Unnerve.cs | 12 ++++++ .../Scripts/Abilities/VictoryStar.cs | 17 ++++++++ .../Scripts/Abilities/VitalSpirit.cs | 20 +++++++++ .../Scripts/Abilities/VoltAbsorb.cs | 20 +++++++++ .../Scripts/Abilities/WaterAbsorb.cs | 20 +++++++++ .../Scripts/Moves/Bestow.cs | 2 +- .../Scripts/Moves/BugBite.cs | 2 +- .../Scripts/Moves/Covet.cs | 2 +- .../Scripts/Moves/Recycle.cs | 2 +- .../Scripts/Moves/Switcheroo.cs | 4 +- .../Scripts/Pokemon/TruantEffect.cs | 10 +++++ 21 files changed, 251 insertions(+), 29 deletions(-) create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Triage.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Truant.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unaware.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unburden.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unnerve.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VictoryStar.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VitalSpirit.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VoltAbsorb.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/WaterAbsorb.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/TruantEffect.cs diff --git a/PkmnLib.Dynamic/Models/Pokemon.cs b/PkmnLib.Dynamic/Models/Pokemon.cs index c14c59a..78b0112 100644 --- a/PkmnLib.Dynamic/Models/Pokemon.cs +++ b/PkmnLib.Dynamic/Models/Pokemon.cs @@ -235,7 +235,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable /// Changes the held item of the Pokemon. Returns the previously held item. /// [MustUseReturnValue] - IItem? SetHeldItem(IItem? item); + IItem? ForceSetHeldItem(IItem? item); /// /// Removes the held item from the Pokemon. Returns the previously held item. @@ -243,6 +243,8 @@ public interface IPokemon : IScriptSource, IDeepCloneable [MustUseReturnValue] IItem? RemoveHeldItem(); + bool HasItemBeenRemovedForBattle { get; } + /// /// Removes the held item from the Pokemon for the duration of the battle. Returns the previously held item. /// @@ -261,7 +263,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable /// /// Restores the held item of a Pokémon if it was temporarily removed. /// - void RestoreStolenHeldItem(); + void RestoreRemovedHeldItem(); /// /// Makes the Pokemon uses its held item. Returns whether the item was consumed. @@ -788,7 +790,7 @@ public class PokemonImpl : ScriptSource, IPokemon public bool HasHeldItem(StringKey itemName) => HeldItem?.Name == itemName; /// - public IItem? SetHeldItem(IItem? item) + public IItem? ForceSetHeldItem(IItem? item) { var previous = HeldItem; HeldItem = item; @@ -812,12 +814,15 @@ public class PokemonImpl : ScriptSource, IPokemon return previous; } - private IItem? _stolenHeldItem; + /// + public bool HasItemBeenRemovedForBattle => _removedHeldItem is not null; + + private IItem? _removedHeldItem; /// public IItem? RemoveHeldItemForBattle() { - return _stolenHeldItem = RemoveHeldItem(); + return _removedHeldItem = RemoveHeldItem(); } /// @@ -840,10 +845,10 @@ public class PokemonImpl : ScriptSource, IPokemon } /// - public void RestoreStolenHeldItem() + public void RestoreRemovedHeldItem() { - _ = SetHeldItem(_stolenHeldItem); - _stolenHeldItem = null; + _ = ForceSetHeldItem(_removedHeldItem); + _removedHeldItem = null; } /// @@ -863,7 +868,7 @@ public class PokemonImpl : ScriptSource, IPokemon BattleData.MarkItemAsConsumed(HeldItem); } - UseItem(SetHeldItem(null)!); + UseItem(ForceSetHeldItem(null)!); return true; } diff --git a/PkmnLib.Static/Utils/NumericHelpers.cs b/PkmnLib.Static/Utils/NumericHelpers.cs index 638f1e0..bb7edf3 100644 --- a/PkmnLib.Static/Utils/NumericHelpers.cs +++ b/PkmnLib.Static/Utils/NumericHelpers.cs @@ -82,4 +82,22 @@ public static class NumericHelpers var result = value * multiplier; return result > uint.MaxValue ? uint.MaxValue : (uint)result; } + + /// + /// Multiplies two values. If this overflows, returns . + /// + public static int MultiplyOrMax(this int value, int multiplier) + { + var result = (long)value * multiplier; + return result > int.MaxValue ? int.MaxValue : (int)result; + } + + /// + /// Multiplies two values. If this overflows, returns . + /// + public static int MultiplyOrMax(this int value, float multiplier) + { + var result = value * multiplier; + return result > int.MaxValue ? int.MaxValue : (int)result; + } } \ 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 acfdc04..3918a6d 100755 --- a/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc +++ b/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc @@ -695,18 +695,37 @@ "cant_be_copied" ] }, - "triage": {}, - "truant": { - "canBeChanged": false + "triage": { + "effect": "triage" + }, + "truant": { + "canBeChanged": false, + "effect": "truant" + }, + "turboblaze": { + "effect": "teravolt" + }, + "unaware": { + "effect": "unaware" + }, + "unburden": { + "effect": "unburden" + }, + "unnerve": { + "effect": "unnerve" + }, + "victory_star": { + "effect": "victory_star" + }, + "vital_spirit": { + "effect": "vital_spirit" + }, + "volt_absorb": { + "effect": "volt_absorb" + }, + "water_absorb": { + "effect": "water_absorb" }, - "turboblaze": {}, - "unaware": {}, - "unburden": {}, - "unnerve": {}, - "victory_star": {}, - "vital_spirit": {}, - "volt_absorb": {}, - "water_absorb": {}, "water_bubble": {}, "water_compaction": {}, "water_veil": {}, diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Harvest.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Harvest.cs index 5615003..e34d3d2 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Harvest.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Harvest.cs @@ -33,7 +33,7 @@ public class Harvest : Script if (battle.WeatherName != ScriptUtils.ResolveName() && rng.GetInt(1) != 0) return; battle.EventHook.Invoke(new AbilityTriggerEvent(_pokemon)); - _ = _pokemon.SetHeldItem(consumedBerry); + _ = _pokemon.ForceSetHeldItem(consumedBerry); } } } \ 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 index 4839fc3..ee987bf 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Magician.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Magician.cs @@ -23,6 +23,6 @@ public class Magician : Script return; move.Battle.EventHook.Invoke(new AbilityTriggerEvent(move.User)); - _ = move.User.SetHeldItem(item); + _ = move.User.ForceSetHeldItem(item); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Pickpocket.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Pickpocket.cs index 9d3e58a..2aa764b 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Pickpocket.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Pickpocket.cs @@ -15,6 +15,6 @@ public class Pickpocket : Script !move.User.TryStealHeldItem(out var item)) return; move.Battle.EventHook.Invoke(new AbilityTriggerEvent(target)); - _ = target.SetHeldItem(item); + _ = target.ForceSetHeldItem(item); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Triage.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Triage.cs new file mode 100644 index 0000000..663cd52 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Triage.cs @@ -0,0 +1,20 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Triage is an ability that gives priority to healing moves. +/// +/// Bulbapedia - Triage +/// +[Script(ScriptCategory.Ability, "triage")] +public class Triage : Script +{ + /// + public override void ChangePriority(IMoveChoice choice, ref sbyte priority) + { + if (!choice.ChosenMove.MoveData.HasFlag("heal")) + return; + if (priority == sbyte.MaxValue) + return; + priority++; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Truant.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Truant.cs new file mode 100644 index 0000000..aa66f0a --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Truant.cs @@ -0,0 +1,18 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Truant is an ability that causes the Pokémon to only be able to move every other turn. +/// +/// Bulbapedia - Truant +/// +[Script(ScriptCategory.Ability, "truant")] +public class Truant : Script +{ + /// + public override void OnAfterMove(IExecutingMove move) + { + move.User.Volatile.Add(new TruantEffect(move.User)); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unaware.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unaware.cs new file mode 100644 index 0000000..803fd67 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unaware.cs @@ -0,0 +1,24 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Unaware is an ability that ignores the opposing Pokémon's stat changes when attacking or defending. +/// +/// Bulbapedia - Unaware +/// +[Script(ScriptCategory.Ability, "unaware")] +public class Unaware : Script +{ + /// + public override void ChangeIncomingMoveOffensiveStatValue(IExecutingMove executingMove, IPokemon target, + byte hitNumber, uint defensiveStat, StatisticSet targetStats, Statistic offensive, ref uint offensiveStat) + { + offensiveStat = executingMove.User.FlatStats.GetStatistic(offensive); + } + + /// + public override void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat, + ImmutableStatisticSet targetStats, Statistic stat, ref uint value) + { + value = target.FlatStats.GetStatistic(stat); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unburden.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unburden.cs new file mode 100644 index 0000000..84809aa --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unburden.cs @@ -0,0 +1,19 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Unburden is an ability that doubles the Pokémon's Speed if it loses its held item. +/// +/// Bulbapedia - Unburden +/// +[Script(ScriptCategory.Ability, "unburden")] +public class Unburden : Script +{ + /// + public override void ChangeSpeed(ITurnChoice choice, ref uint speed) + { + if (choice.User.HasItemBeenRemovedForBattle) + { + speed = speed.MultiplyOrMax(2); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unnerve.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unnerve.cs new file mode 100644 index 0000000..d197173 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Unnerve.cs @@ -0,0 +1,12 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Unnerve is an ability that prevents opposing Pokémon from using their held Berries. +/// +/// Bulbapedia - Unnerve +/// +[Script(ScriptCategory.Ability, "unnerve")] +public class Unnerve : Script +{ + // TODO: Implement Unnerve ability logic +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VictoryStar.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VictoryStar.cs new file mode 100644 index 0000000..ba1c929 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VictoryStar.cs @@ -0,0 +1,17 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Victory Star is an ability that boosts the accuracy of the Pokémon and its allies. +/// +/// Bulbapedia - Victory Star +/// +[Script(ScriptCategory.Ability, "victory_star")] +public class VictoryStar : Script +{ + /// + public override void ChangeAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, + ref int modifiedAccuracy) + { + modifiedAccuracy = modifiedAccuracy.MultiplyOrMax(1.1f); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VitalSpirit.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VitalSpirit.cs new file mode 100644 index 0000000..07c121e --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VitalSpirit.cs @@ -0,0 +1,20 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Vital Spirit is an ability that prevents the Pokémon from falling asleep. +/// +/// Bulbapedia - Vital Spirit +/// +[Script(ScriptCategory.Ability, "vital_spirit")] +public class VitalSpirit : Script +{ + /// + public override void PreventStatusChange(IPokemon pokemon, StringKey status, bool selfInflicted, + ref bool preventStatus) + { + if (status == ScriptUtils.ResolveName() && !selfInflicted) + { + preventStatus = true; + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VoltAbsorb.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VoltAbsorb.cs new file mode 100644 index 0000000..4b68e79 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/VoltAbsorb.cs @@ -0,0 +1,20 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Volt Absorb is an ability that heals the Pokémon when hit by an Electric-type move. +/// +/// Bulbapedia - Volt Absorb +/// +[Script(ScriptCategory.Ability, "volt_absorb")] +public class VoltAbsorb : Script +{ + /// + public override void IsInvulnerableToMove(IExecutingMove move, IPokemon target, ref bool invulnerable) + { + if (move.GetHitData(target, 0).Type?.Name != "electric") + return; + invulnerable = true; + target.BattleData?.Battle.EventHook.Invoke(new AbilityTriggerEvent(target)); + target.Heal(target.MaxHealth / 4); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/WaterAbsorb.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/WaterAbsorb.cs new file mode 100644 index 0000000..d041bb5 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/WaterAbsorb.cs @@ -0,0 +1,20 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Water Absorb is an ability that heals the Pokémon when hit by a Water-type move. +/// +/// Bulbapedia - Water Absorb +/// +[Script(ScriptCategory.Ability, "water_absorb")] +public class WaterAbsorb : Script +{ + /// + public override void IsInvulnerableToMove(IExecutingMove move, IPokemon target, ref bool invulnerable) + { + if (move.GetHitData(target, 0).Type?.Name != "water") + return; + invulnerable = true; + target.BattleData?.Battle.EventHook.Invoke(new AbilityTriggerEvent(target)); + target.Heal(target.MaxHealth / 4); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bestow.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bestow.cs index fb4aad3..a31e4f4 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bestow.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bestow.cs @@ -16,6 +16,6 @@ public class Bestow : Script return; } - _ = target.SetHeldItem(userHeldItem); + _ = target.ForceSetHeldItem(userHeldItem); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BugBite.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BugBite.cs index 01e3efe..e617e7f 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BugBite.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BugBite.cs @@ -19,7 +19,7 @@ public class BugBite : Script return; } - _ = target.SetHeldItem(null); + _ = target.ForceSetHeldItem(null); targetHeldItem.RunItemScript(battleData.Battle.Library.ScriptResolver, user, move.Battle.EventHook); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Covet.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Covet.cs index 0cc61c2..4df7f5d 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Covet.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Covet.cs @@ -10,6 +10,6 @@ public class Covet : Script return; if (!move.User.TryStealHeldItem(out var item)) return; - _ = move.User.SetHeldItem(item); + _ = move.User.ForceSetHeldItem(item); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Recycle.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Recycle.cs index 92fed20..16b3b27 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Recycle.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Recycle.cs @@ -20,6 +20,6 @@ public class Recycle : Script move.GetHitData(target, hit).Fail(); return; } - _ = move.User.SetHeldItem(lastItem); + _ = move.User.ForceSetHeldItem(lastItem); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Switcheroo.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Switcheroo.cs index 66170af..ec22ef1 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Switcheroo.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Switcheroo.cs @@ -18,7 +18,7 @@ public class Switcheroo : Script targetHeldItem = target.RemoveHeldItem(); userHeldItem = move.User.RemoveHeldItem(); - _ = target.SetHeldItem(userHeldItem); - _ = move.User.SetHeldItem(targetHeldItem); + _ = target.ForceSetHeldItem(userHeldItem); + _ = move.User.ForceSetHeldItem(targetHeldItem); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/TruantEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/TruantEffect.cs new file mode 100644 index 0000000..ac27728 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/TruantEffect.cs @@ -0,0 +1,10 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +public class TruantEffect(IPokemon owner) : Script +{ + /// + public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice) + { + choice = new PassChoice(owner); + } +} \ No newline at end of file