From 292c303fc068e518db227444f6fa8fdfca6d2d30 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Mon, 5 May 2025 11:36:59 +0200 Subject: [PATCH] More moves implemented --- PkmnLib.Dynamic/Models/Battle.cs | 4 +- .../Models/BattleFlow/MoveTurnExecutor.cs | 11 +- PkmnLib.Dynamic/Models/Choices/MoveChoice.cs | 1 + PkmnLib.Dynamic/Models/Pokemon.cs | 8 + .../ScriptHandling/ILimitedTurnsScript.cs | 12 ++ .../ScriptHandling/IWeatherScript.cs | 12 -- PkmnLib.Dynamic/ScriptHandling/Script.cs | 4 + PkmnLib.Dynamic/ScriptHandling/ScriptSet.cs | 2 + PkmnLib.Tests/Data/Moves.jsonc | 202 +++++++++++++++--- .../Scripts/Battle/SnatchEffect.cs | 62 ++++++ .../Scripts/CustomTriggers.cs | 2 + .../MoveVolatile/BypassSleepVolatile.cs | 15 ++ .../Scripts/Moves/ChargeMove.cs | 17 ++ .../Scripts/Moves/DragonRage.cs | 11 - .../Scripts/Moves/Gravity.cs | 4 +- .../Scripts/Moves/GuardSplit.cs | 4 +- .../Scripts/Moves/PowerSplit.cs | 4 +- .../PkmnLib.Plugin.Gen7/Scripts/Moves/Rest.cs | 4 +- .../Scripts/Moves/SkullBash.cs | 24 +++ .../Scripts/Moves/SkyAttack.cs | 27 +++ .../Scripts/Moves/SkyDrop.cs | 28 +++ .../Scripts/Moves/SleepTalk.cs | 34 +++ .../Scripts/Moves/SmackDown.cs | 30 +++ .../Scripts/Moves/SmellingSalts.cs | 25 +++ .../Scripts/Moves/Snatch.cs | 15 ++ .../Scripts/Moves/Snore.cs | 20 ++ .../PkmnLib.Plugin.Gen7/Scripts/Moves/Soak.cs | 22 ++ .../Scripts/Moves/SparklingAria.cs | 12 ++ .../Scripts/Moves/SpectralThief.cs | 22 ++ .../Scripts/Moves/SpeedSwap.cs | 19 ++ .../Scripts/Moves/SpiderWeb.cs | 15 ++ .../Scripts/Moves/StaticDamage.cs | 35 +++ .../Scripts/Pokemon/ChargeSkyDropEffect.cs | 35 +++ .../Scripts/Pokemon/SkullbashEffect.cs | 20 ++ .../Scripts/Pokemon/SkyAttackEffect.cs | 20 ++ .../Scripts/Pokemon/SmackDownEffect.cs | 21 ++ .../Scripts/Pokemon/SpiderWebEffect.cs | 29 +++ .../Scripts/Status/Sleep.cs | 52 +++++ .../Scripts/Weather/Hail.cs | 2 +- 39 files changed, 818 insertions(+), 68 deletions(-) create mode 100644 PkmnLib.Dynamic/ScriptHandling/ILimitedTurnsScript.cs delete mode 100644 PkmnLib.Dynamic/ScriptHandling/IWeatherScript.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/SnatchEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/MoveVolatile/BypassSleepVolatile.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ChargeMove.cs delete mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DragonRage.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkullBash.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkyAttack.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkyDrop.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SleepTalk.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SmackDown.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SmellingSalts.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Snatch.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Snore.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Soak.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SparklingAria.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpectralThief.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpeedSwap.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpiderWeb.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/StaticDamage.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeSkyDropEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SkullbashEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SkyAttackEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SmackDownEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SpiderWebEffect.cs diff --git a/PkmnLib.Dynamic/Models/Battle.cs b/PkmnLib.Dynamic/Models/Battle.cs index c792161..9e54516 100644 --- a/PkmnLib.Dynamic/Models/Battle.cs +++ b/PkmnLib.Dynamic/Models/Battle.cs @@ -383,13 +383,14 @@ public class BattleImpl : ScriptSource, IBattle if (!Library.ScriptResolver.TryResolve(ScriptCategory.Weather, weatherName.Value, null, out var script)) throw new InvalidOperationException($"Weather script {weatherName} not found."); - if (script is IWeatherScript weatherScript) + if (script is ILimitedTurnsScript weatherScript) { this.RunScriptHook(x => x.ChangeWeatherDuration(weatherName.Value, ref duration)); weatherScript.SetTurns(duration); } _weatherScript.Set(script); + script.OnAddedToParent(this); } else { @@ -415,6 +416,7 @@ public class BattleImpl : ScriptSource, IBattle if (!Library.ScriptResolver.TryResolve(ScriptCategory.Terrain, terrainName.Value, null, out var script)) throw new InvalidOperationException($"Terrain script {terrainName} not found."); _terrainScript.Set(script); + script.OnAddedToParent(this); } else { diff --git a/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs b/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs index a1f4821..8574696 100644 --- a/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs +++ b/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs @@ -7,7 +7,7 @@ using PkmnLib.Static.Utils; namespace PkmnLib.Dynamic.Models.BattleFlow; -internal static class MoveTurnExecutor +public static class MoveTurnExecutor { internal static void ExecuteMoveChoice(IBattle battle, IMoveChoice moveChoice) { @@ -30,6 +30,7 @@ internal static class MoveTurnExecutor secondaryEffect.Parameters, out var script)) { moveChoice.Script.Set(script); + script.OnAddedToParent(moveChoice); } else { @@ -77,16 +78,20 @@ internal static class MoveTurnExecutor // TODO: fail handling return; } + ExecuteMove(executingMove); + } + public static void ExecuteMove(IExecutingMove executingMove) + { var stopped = false; executingMove.RunScriptHook(x => x.StopBeforeMove(executingMove, ref stopped)); if (stopped) return; executingMove.RunScriptHook(x => x.OnBeforeMove(executingMove)); - foreach (var target in targets.WhereNotNull()) + foreach (var target in executingMove.Targets.WhereNotNull()) { - ExecuteMoveChoiceForTarget(battle, executingMove, target); + ExecuteMoveChoiceForTarget(executingMove.Battle, executingMove, target); } executingMove.RunScriptHook(x => x.OnAfterMove(executingMove)); } diff --git a/PkmnLib.Dynamic/Models/Choices/MoveChoice.cs b/PkmnLib.Dynamic/Models/Choices/MoveChoice.cs index 8d7dd53..f82c724 100644 --- a/PkmnLib.Dynamic/Models/Choices/MoveChoice.cs +++ b/PkmnLib.Dynamic/Models/Choices/MoveChoice.cs @@ -64,6 +64,7 @@ public class MoveChoice : TurnChoice, IMoveChoice secondaryEffect.Parameters, out var script)) { Script.Set(script); + script.OnAddedToParent(this); } } } diff --git a/PkmnLib.Dynamic/Models/Pokemon.cs b/PkmnLib.Dynamic/Models/Pokemon.cs index cc14e3f..d158e9a 100644 --- a/PkmnLib.Dynamic/Models/Pokemon.cs +++ b/PkmnLib.Dynamic/Models/Pokemon.cs @@ -553,6 +553,7 @@ public class PokemonImpl : ScriptSource, IPokemon out var statusScript)) throw new KeyNotFoundException($"Status script {serializedPokemon.Status} not found"); StatusScript.Set(statusScript); + statusScript.OnAddedToParent(this); } } @@ -926,6 +927,7 @@ public class PokemonImpl : ScriptSource, IPokemon out var abilityScript)) { AbilityScript.Set(abilityScript); + abilityScript.OnAddedToParent(this); } else { @@ -1099,12 +1101,17 @@ public class PokemonImpl : ScriptSource, IPokemon { if (!Library.ScriptResolver.TryResolve(ScriptCategory.Status, status, null, out var statusScript)) throw new KeyNotFoundException($"Status script {status} not found"); + + if (!StatusScript.IsEmpty) + return false; + var preventStatus = false; this.RunScriptHook(script => script.PreventStatusChange(this, status, ref preventStatus)); if (preventStatus) return false; StatusScript.Set(statusScript); + statusScript.OnAddedToParent(this); return true; } @@ -1191,6 +1198,7 @@ public class PokemonImpl : ScriptSource, IPokemon out var abilityScript)) { AbilityScript.Set(abilityScript); + abilityScript.OnAddedToParent(this); } else { diff --git a/PkmnLib.Dynamic/ScriptHandling/ILimitedTurnsScript.cs b/PkmnLib.Dynamic/ScriptHandling/ILimitedTurnsScript.cs new file mode 100644 index 0000000..91022c2 --- /dev/null +++ b/PkmnLib.Dynamic/ScriptHandling/ILimitedTurnsScript.cs @@ -0,0 +1,12 @@ +namespace PkmnLib.Dynamic.ScriptHandling; + +/// +/// Helper interface for scripts that have a limited number of turns. +/// +public interface ILimitedTurnsScript +{ + /// + /// Sets the number of turns the script will last. + /// + public void SetTurns(int turns); +} \ No newline at end of file diff --git a/PkmnLib.Dynamic/ScriptHandling/IWeatherScript.cs b/PkmnLib.Dynamic/ScriptHandling/IWeatherScript.cs deleted file mode 100644 index 4d2e9da..0000000 --- a/PkmnLib.Dynamic/ScriptHandling/IWeatherScript.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace PkmnLib.Dynamic.ScriptHandling; - -/// -/// Helper interface for weather scripts. -/// -public interface IWeatherScript -{ - /// - /// Sets the number of turns the weather will last. - /// - public void SetTurns(int turns); -} \ No newline at end of file diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs index bb8a4c3..43f9759 100644 --- a/PkmnLib.Dynamic/ScriptHandling/Script.cs +++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs @@ -97,6 +97,10 @@ public abstract class Script : IDeepCloneable { } + public virtual void OnAddedToParent(IScriptSource source) + { + } + /// /// Override to customize whether the move can be selected at all. /// diff --git a/PkmnLib.Dynamic/ScriptHandling/ScriptSet.cs b/PkmnLib.Dynamic/ScriptHandling/ScriptSet.cs index c6912cc..7781123 100644 --- a/PkmnLib.Dynamic/ScriptHandling/ScriptSet.cs +++ b/PkmnLib.Dynamic/ScriptHandling/ScriptSet.cs @@ -117,6 +117,7 @@ public class ScriptSet : IScriptSet script.OnRemoveEvent += s => Remove(s.Name); var container = new ScriptContainer(script); _scripts.Add(container); + script.OnAddedToParent(_source); return container; } @@ -136,6 +137,7 @@ public class ScriptSet : IScriptSet script.OnRemoveEvent += s => Remove(s.Name); var container = new ScriptContainer(script); _scripts.Add(container); + script.OnAddedToParent(_source); return container; } diff --git a/PkmnLib.Tests/Data/Moves.jsonc b/PkmnLib.Tests/Data/Moves.jsonc index bf08e71..e46fc9f 100755 --- a/PkmnLib.Tests/Data/Moves.jsonc +++ b/PkmnLib.Tests/Data/Moves.jsonc @@ -2749,7 +2749,10 @@ "mirror" ], "effect": { - "name": "dragon_rage" + "name": "static_damage", + "parameters": { + "damage": 40 + } } }, { @@ -9980,7 +9983,10 @@ "charge", "protect", "mirror" - ] + ], + "effect": { + "name": "skull_bash" + } }, { "name": "sky_attack", @@ -9996,7 +10002,10 @@ "protect", "mirror", "distance" - ] + ], + "effect": { + "name": "sky_attack" + } }, { "name": "sky_drop", @@ -10014,7 +10023,10 @@ "mirror", "gravity", "distance" - ] + ], + "effect": { + "name": "sky_drop" + } }, { "name": "sky_uppercut", @@ -10032,6 +10044,7 @@ "punch", "hit_flying" ] + // No secondary effect }, { "name": "slack_off", @@ -10045,7 +10058,13 @@ "flags": [ "snatch", "heal" - ] + ], + "effect": { + "name": "heal_percent", + "parameters": { + "healPercent": 0.5 + } + } }, { "name": "slam", @@ -10062,6 +10081,7 @@ "mirror", "nonskybattle" ] + // No secondary effect }, { "name": "slash", @@ -10076,7 +10096,10 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "increased_critical_stage" + } }, { "name": "sleep_powder", @@ -10092,7 +10115,13 @@ "reflectable", "mirror", "powder" - ] + ], + "effect": { + "name": "set_status", + "parameters": { + "status": "sleep" + } + } }, { "name": "sleep_talk", @@ -10103,7 +10132,12 @@ "priority": 0, "target": "Self", "category": "status", - "flags": [] + "flags": [ + "usable_while_asleep" + ], + "effect": { + "name": "sleep_talk" + } }, { "name": "sludge", @@ -10117,7 +10151,14 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "set_status", + "chance": 30, + "parameters": { + "status": "poisoned" + } + } }, { "name": "sludge_bomb", @@ -10132,7 +10173,14 @@ "protect", "mirror", "ballistics" - ] + ], + "effect": { + "name": "set_status", + "chance": 30, + "parameters": { + "status": "poisoned" + } + } }, { "name": "sludge_wave", @@ -10146,7 +10194,14 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "set_status", + "chance": 10, + "parameters": { + "status": "poisoned" + } + } }, { "name": "smack_down", @@ -10162,14 +10217,17 @@ "mirror", "nonskybattle", "hit_flying" - ] + ], + "effect": { + "name": "smack_down" + } }, { "name": "smart_strike", "type": "steel", "power": 70, "pp": 10, - "accuracy": 0, + "accuracy": 255, "priority": 0, "target": "Any", "category": "physical", @@ -10178,6 +10236,7 @@ "protect", "mirror" ] + // No secondary effect }, { "name": "smelling_salts", @@ -10192,7 +10251,10 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "smelling_salts" + } }, { "name": "smog", @@ -10206,7 +10268,14 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "set_status", + "chance": 40, + "parameters": { + "status": "poisoned" + } + } }, { "name": "smokescreen", @@ -10221,7 +10290,13 @@ "protect", "reflectable", "mirror" - ] + ], + "effect": { + "name": "change_target_accuracy", + "parameters": { + "amount": -1 + } + } }, { "name": "snarl", @@ -10237,7 +10312,13 @@ "mirror", "sound", "ignore-substitute" - ] + ], + "effect": { + "name": "change_target_special_attack", + "parameters": { + "amount": -1 + } + } }, { "name": "snatch", @@ -10250,7 +10331,10 @@ "category": "status", "flags": [ "ignore-substitute" - ] + ], + "effect": { + "name": "snatch" + } }, { "name": "snore", @@ -10265,8 +10349,13 @@ "protect", "mirror", "sound", - "ignore-substitute" - ] + "ignore-substitute", + "usable_while_asleep" + ], + "effect": { + "name": "snore", + "chance": 30 + } }, { "name": "soak", @@ -10281,7 +10370,10 @@ "protect", "reflectable", "mirror" - ] + ], + "effect": { + "name": "soak" + } }, { "name": "soft_boiled", @@ -10295,7 +10387,13 @@ "flags": [ "snatch", "heal" - ] + ], + "effect": { + "name": "heal_percent", + "parameters": { + "healPercent": 0.5 + } + } }, { "name": "solar_beam", @@ -10310,7 +10408,10 @@ "charge", "protect", "mirror" - ] + ], + "effect": { + "name": "charge_move" + } }, { "name": "solar_blade", @@ -10326,7 +10427,10 @@ "charge", "protect", "mirror" - ] + ], + "effect": { + "name": "charge_move" + } }, { "name": "sonic_boom", @@ -10340,7 +10444,13 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "static_damage", + "parameters": { + "damage": 20 + } + } }, { "name": "soul_stealing_7_star_strike", @@ -10354,6 +10464,7 @@ "flags": [ "contact" ] + // No secondary effect }, { "name": "spacial_rend", @@ -10367,7 +10478,10 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "increased_critical_stage" + } }, { "name": "spark", @@ -10382,7 +10496,14 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "set_status", + "chance": 30, + "parameters": { + "status": "paralyzed" + } + } }, { "name": "sparkling_aria", @@ -10398,7 +10519,10 @@ "mirror", "sound", "ignore-substitute" - ] + ], + "effect": { + "name": "sparkling_aria" + } }, { "name": "spectral_thief", @@ -10414,7 +10538,10 @@ "protect", "mirror", "ignore-substitute" - ] + ], + "effect": { + "name": "spectral_thief" + } }, { "name": "speed_swap", @@ -10429,14 +10556,17 @@ "protect", "mirror", "ignore-substitute" - ] + ], + "effect": { + "name": "speed_swap" + } }, { "name": "spider_web", "type": "bug", "power": 0, "pp": 10, - "accuracy": 0, + "accuracy": 255, "priority": 0, "target": "Any", "category": "status", @@ -10444,7 +10574,10 @@ "protect", "reflectable", "mirror" - ] + ], + "effect": { + "name": "spider_web" + } }, { "name": "spike_cannon", @@ -10458,7 +10591,10 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "2_5_hit_move" + } }, { "name": "spikes", diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/SnatchEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/SnatchEffect.cs new file mode 100644 index 0000000..e3634f2 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/SnatchEffect.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using PkmnLib.Dynamic.Models.BattleFlow; + +namespace PkmnLib.Plugin.Gen7.Scripts.Battle; + +[Script(ScriptCategory.Battle, "snatch_effect")] +public class SnatchEffect : Script +{ + private Queue _snatchers = new(); + + public void AddSnatcher(IPokemon snatcher) + { + if (_snatchers.Contains(snatcher)) + { + _snatchers = new Queue(_snatchers.Where(s => s != snatcher)); + } + _snatchers.Enqueue(snatcher); + } + + private bool TryGetSnatcher([NotNullWhen(true)] out IPokemon? snatcher) + { + while (_snatchers.Any()) + { + var s = _snatchers.Dequeue(); + if (s.BattleData != null && s.IsUsable) + { + snatcher = s; + return true; + } + } + snatcher = null; + return false; + } + + /// + public override void StopBeforeMove(IExecutingMove move, ref bool stop) + { + if (move.UseMove.HasFlag("snatch") && TryGetSnatcher(out var snatcher)) + { + stop = true; + var battleData = snatcher.BattleData; + if (battleData == null) + return; + + var moveChoice = new MoveChoice(snatcher, move.MoveChoice.ChosenMove, battleData.SideIndex, + battleData.Position); + var executingMove = new ExecutingMoveImpl([snatcher], move.NumberOfHits, move.ChosenMove, move.UseMove, + moveChoice, move.Battle); + // ExecuteMove will once again call StopBeforeMove, which will try to pass the move to the next snatcher + // if one exists. + MoveTurnExecutor.ExecuteMove(executingMove); + } + } + + /// + public override void OnEndTurn(IBattle battle) + { + RemoveSelf(); + } +} \ 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 75227d2..f2163c7 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs @@ -15,4 +15,6 @@ public static class CustomTriggers public static readonly StringKey LightScreenNumberOfTurns = "light_screen_number_of_turns"; public static readonly StringKey ReflectNumberOfTurns = "reflect_number_of_turns"; + + public static readonly StringKey BypassSleep = "bypass_sleep"; } \ 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 new file mode 100644 index 0000000..ec7f023 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/MoveVolatile/BypassSleepVolatile.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using PkmnLib.Static.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.MoveVolatile; + +[Script(ScriptCategory.MoveVolatile, "bypass_sleep")] +public class BypassSleepVolatile : Script +{ + /// + public override void CustomTrigger(StringKey eventName, IDictionary? parameters) + { + if (eventName == CustomTriggers.BypassSleep && parameters != null) + parameters["bypass_sleep"] = true; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ChargeMove.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ChargeMove.cs new file mode 100644 index 0000000..90f9db2 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ChargeMove.cs @@ -0,0 +1,17 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "charge_move")] +public class ChargeMove : Script +{ + public override void PreventMove(IExecutingMove move, ref bool prevent) + { + var chargeMoveEffect = move.User.Volatile.Get(); + if (chargeMoveEffect != null && chargeMoveEffect.MoveName == move.UseMove.Name) + return; + prevent = true; + move.User.Volatile.Add(new ChargeMoveEffect(move.UseMove.Name, move.User, move.MoveChoice.TargetSide, + move.MoveChoice.TargetPosition)); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DragonRage.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DragonRage.cs deleted file mode 100644 index bcf045e..0000000 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/DragonRage.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace PkmnLib.Plugin.Gen7.Scripts.Moves; - -[Script(ScriptCategory.Move, "dragon_rage")] -public class DragonRage : Script -{ - /// - public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) - { - damage = 40; - } -} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Gravity.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Gravity.cs index 7019ae6..ab599ff 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Gravity.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Gravity.cs @@ -30,7 +30,9 @@ public class Gravity : Script var flyEffect = ScriptUtils.ResolveName(); if (pokemon.Volatile.Contains(flyEffect)) pokemon.Volatile.Remove(flyEffect); - // TODO: Sky Drop + var skyDropEffect = ScriptUtils.ResolveName(); + if (pokemon.Volatile.Contains(skyDropEffect)) + pokemon.Volatile.Remove(skyDropEffect); } } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GuardSplit.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GuardSplit.cs index 308e854..0ebb9bc 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GuardSplit.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GuardSplit.cs @@ -24,7 +24,7 @@ public class GuardSplit : Script userStats.SetStatistic(Statistic.SpecialDefense, newSpecialDefense); targetStats.SetStatistic(Statistic.Defense, newDefense); targetStats.SetStatistic(Statistic.SpecialDefense, newSpecialDefense); - user.RecalculateFlatStats(); - target.RecalculateFlatStats(); + user.RecalculateBoostedStats(); + target.RecalculateBoostedStats(); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerSplit.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerSplit.cs index 822e201..9121939 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerSplit.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerSplit.cs @@ -24,7 +24,7 @@ public class PowerSplit : Script userStats.SetStatistic(Statistic.SpecialAttack, newSpecialAttack); targetStats.SetStatistic(Statistic.Attack, newAttack); targetStats.SetStatistic(Statistic.SpecialAttack, newSpecialAttack); - user.RecalculateFlatStats(); - target.RecalculateFlatStats(); + user.RecalculateBoostedStats(); + target.RecalculateBoostedStats(); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Rest.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Rest.cs index 6151576..cd634fe 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Rest.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Rest.cs @@ -18,7 +18,7 @@ public class Rest : Script move.GetHitData(target, hit).Fail(); return; } - move.User.SetStatus(ScriptUtils.ResolveName()); - ((Sleep)move.User.StatusScript.Script!).Turns = 2; + if (move.User.SetStatus(ScriptUtils.ResolveName()) && move.User.StatusScript.Script is Sleep sleep) + sleep.Turns = 2; } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkullBash.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkullBash.cs new file mode 100644 index 0000000..db0070c --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkullBash.cs @@ -0,0 +1,24 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; +using PkmnLib.Static; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "skull_bash")] +public class SkullBash : Script +{ + /// + public override void PreventMove(IExecutingMove move, ref bool prevent) + { + if (move.User.Volatile.Contains()) + return; + move.User.ChangeStatBoost(Statistic.Defense, 1, true); + move.User.Volatile.Add(new SkullBashEffect(move.User)); + prevent = true; + } + + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + move.User.Volatile.Remove(); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkyAttack.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkyAttack.cs new file mode 100644 index 0000000..a4b3c11 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkyAttack.cs @@ -0,0 +1,27 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; +using PkmnLib.Static; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "sky_attack")] +public class SkyAttack : Script +{ + /// + public override void PreventMove(IExecutingMove move, ref bool prevent) + { + if (move.User.Volatile.Contains()) + return; + move.User.Volatile.Add(new SkyAttackEffect(move.User)); + prevent = true; + } + + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + move.User.Volatile.Remove(); + if (move.Battle.Random.EffectChance(30, move, target, hit)) + { + target.Volatile.Add(new FlinchEffect()); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkyDrop.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkyDrop.cs new file mode 100644 index 0000000..087e53f --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SkyDrop.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "sky_drop")] +public class SkyDrop : Script +{ + /// + public override void PreventMove(IExecutingMove move, ref bool prevent) + { + if (move.User.Volatile.Contains()) + return; + + move.User.Volatile.Add(new ChargeSkyDropEffect(move.User)); + move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("sky_drop_charge", new Dictionary + { + { "user", move.User }, + })); + prevent = true; + } + + /// + public override void OnBeforeMove(IExecutingMove move) + { + move.User.Volatile.Remove(ScriptUtils.ResolveName()); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SleepTalk.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SleepTalk.cs new file mode 100644 index 0000000..391d40b --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SleepTalk.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using PkmnLib.Plugin.Gen7.Scripts.MoveVolatile; +using PkmnLib.Plugin.Gen7.Scripts.Utils; +using PkmnLib.Static.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "sleep_talk")] +public class SleepTalk : Script +{ + /// + public override void ChangeMove(IMoveChoice choice, ref StringKey moveName) + { + if (!choice.User.HasStatus(ScriptUtils.ResolveName())) + { + choice.Fail(); + return; + } + var battleData = choice.User.BattleData; + if (battleData == null) + return; + + var moves = choice.User.Moves.WhereNotNull().Where(x => x.MoveData.CanCopyMove()) + .Where(x => x != choice.ChosenMove).OrderBy(_ => battleData.Battle.Random.GetInt()).FirstOrDefault(); + if (moves == null) + { + choice.Fail(); + return; + } + moveName = moves.MoveData.Name; + choice.Volatile.Add(new BypassSleepVolatile()); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SmackDown.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SmackDown.cs new file mode 100644 index 0000000..228e073 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SmackDown.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; +using PkmnLib.Static.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "smack_down")] +public class SmackDown : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + var battleData = target.BattleData; + if (battleData == null) + return; + + target.Volatile.Add(new SmackDownEffect()); + + var chargeBounceEffect = ScriptUtils.ResolveName(); + if (target.Volatile.Contains(chargeBounceEffect)) + target.Volatile.Remove(chargeBounceEffect); + var flyEffect = ScriptUtils.ResolveName(); + if (target.Volatile.Contains(flyEffect)) + target.Volatile.Remove(flyEffect); + var skyDropEffect = ScriptUtils.ResolveName(); + if (target.Volatile.Contains(skyDropEffect)) + target.Volatile.Remove(skyDropEffect); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SmellingSalts.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SmellingSalts.cs new file mode 100644 index 0000000..8308ef7 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SmellingSalts.cs @@ -0,0 +1,25 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "smelling_salts")] +public class SmellingSalts : Script +{ + /// + public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) + { + // If the target is paralyzed, double the damage + if (target.HasStatus(ScriptUtils.ResolveName())) + { + damage *= 2; + } + } + + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + // If the target is paralyzed, remove the paralysis + if (target.HasStatus(ScriptUtils.ResolveName())) + { + target.ClearStatus(); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Snatch.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Snatch.cs new file mode 100644 index 0000000..307101d --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Snatch.cs @@ -0,0 +1,15 @@ +using PkmnLib.Plugin.Gen7.Scripts.Battle; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "snatch")] +public class Snatch : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + move.Battle.Volatile.Add(new SnatchEffect()); + var snatchEffect = move.Battle.Volatile.Get(); + snatchEffect?.AddSnatcher(target); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Snore.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Snore.cs new file mode 100644 index 0000000..15e6b56 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Snore.cs @@ -0,0 +1,20 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "snore")] +public class Snore : Script +{ + /// + public override void FailMove(IExecutingMove move, ref bool fail) + { + if (!move.User.HasStatus(ScriptUtils.ResolveName())) + fail = true; + } + + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + target.Volatile.Add(new FlinchEffect()); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Soak.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Soak.cs new file mode 100644 index 0000000..349db9e --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Soak.cs @@ -0,0 +1,22 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "soak")] +public class Soak : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + if (target.ActiveAbility?.Name == "multitype") + { + move.GetHitData(target, hit).Fail(); + return; + } + + var typeLibrary = move.Battle.Library.StaticLibrary.Types; + // If water type is not found, we can't do anything. + if (!typeLibrary.TryGetTypeIdentifier("water", out var waterType)) + return; + + target.SetTypes([waterType]); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SparklingAria.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SparklingAria.cs new file mode 100644 index 0000000..76f70d6 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SparklingAria.cs @@ -0,0 +1,12 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "sparkling_aria")] +public class SparklingAria : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + if (target.HasStatus(ScriptUtils.ResolveName())) + target.ClearStatus(); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpectralThief.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpectralThief.cs new file mode 100644 index 0000000..e80df21 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpectralThief.cs @@ -0,0 +1,22 @@ +using System.Linq; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "spectral_thief")] +public class SpectralThief : Script +{ + /// + public override void OnBeforeHit(IExecutingMove move, IPokemon target, byte hitIndex) + { + var positiveStats = target.StatBoost.Where(x => x.value > 0).ToArray(); + if (positiveStats.Length > 0) + { + EventBatchId batchId = new(); + foreach (var positiveStat in positiveStats) + { + move.User.ChangeStatBoost(positiveStat.statistic, positiveStat.value, true, batchId); + target.ChangeStatBoost(positiveStat.statistic, (sbyte)-positiveStat.value, true, batchId); + } + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpeedSwap.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpeedSwap.cs new file mode 100644 index 0000000..52cca31 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpeedSwap.cs @@ -0,0 +1,19 @@ +using PkmnLib.Static; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "speed_swap")] +public class SpeedSwap : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + var userSpeed = move.User.FlatStats.Speed; + var targetSpeed = target.FlatStats.Speed; + + move.User.FlatStats.SetStatistic(Statistic.Speed, targetSpeed); + target.FlatStats.SetStatistic(Statistic.Speed, userSpeed); + move.User.RecalculateBoostedStats(); + target.RecalculateBoostedStats(); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpiderWeb.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpiderWeb.cs new file mode 100644 index 0000000..74debda --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SpiderWeb.cs @@ -0,0 +1,15 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "spider_web")] +public class SpiderWeb : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + move.User.Volatile.Add(new SpiderWebEffect()); + var effect = move.User.Volatile.Get(); + effect?.AddTarget(target); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/StaticDamage.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/StaticDamage.cs new file mode 100644 index 0000000..db6a203 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/StaticDamage.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using PkmnLib.Static.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "static_damage")] +public class StaticDamage : Script +{ + private uint Damage { get; set; } + + /// + public override void OnInitialize(IReadOnlyDictionary? parameters) + { + if (parameters == null) + throw new Exception("Parameters cannot be null for StaticDamage script."); + if (parameters.TryGetValue("damage", out var damage)) + { + if (damage is int d) + Damage = (uint)d; + else + throw new Exception($"Invalid damage value: {damage}"); + } + else + { + throw new Exception("Missing required parameter: damage"); + } + } + + /// + public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) + { + damage = Damage; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeSkyDropEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeSkyDropEffect.cs new file mode 100644 index 0000000..37ebcf3 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeSkyDropEffect.cs @@ -0,0 +1,35 @@ +using PkmnLib.Plugin.Gen7.Scripts.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "charge_sky_drop")] +public class ChargeSkyDropEffect : Script +{ + private readonly IPokemon _owner; + + public ChargeSkyDropEffect(IPokemon owner) + { + _owner = owner; + } + + /// + public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice) + { + var opposingSideIndex = (byte)(_owner.BattleData?.SideIndex == 0 ? 1 : 0); + choice = TurnChoiceHelper.CreateMoveChoice(_owner, "sky_drop", opposingSideIndex, position); + } + + /// + public override void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block) + { + if (!executingMove.UseMove.HasFlag("hit_flying")) + block = true; + } + + /// + public override void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) + { + if (!move.UseMove.HasFlag("effective_against_fly")) + damage *= 2; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SkullbashEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SkullbashEffect.cs new file mode 100644 index 0000000..3e296fd --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SkullbashEffect.cs @@ -0,0 +1,20 @@ +using PkmnLib.Plugin.Gen7.Scripts.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "skull_bash")] +public class SkullBashEffect : Script +{ + private readonly IPokemon _owner; + + public SkullBashEffect(IPokemon owner) + { + _owner = owner; + } + + /// + public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice) + { + choice = TurnChoiceHelper.CreateMoveChoice(_owner, "skull_bash", sideIndex, position); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SkyAttackEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SkyAttackEffect.cs new file mode 100644 index 0000000..b9eeb5b --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SkyAttackEffect.cs @@ -0,0 +1,20 @@ +using PkmnLib.Plugin.Gen7.Scripts.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "sky_attack")] +public class SkyAttackEffect : Script +{ + private readonly IPokemon _owner; + + public SkyAttackEffect(IPokemon owner) + { + _owner = owner; + } + + /// + public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice) + { + choice = TurnChoiceHelper.CreateMoveChoice(_owner, "sky_attack", sideIndex, position); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SmackDownEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SmackDownEffect.cs new file mode 100644 index 0000000..273d683 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SmackDownEffect.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using PkmnLib.Static; +using PkmnLib.Static.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "smack_down")] +public class SmackDownEffect : Script +{ + /// + public override void ChangeTypesForIncomingMove(IExecutingMove executingMove, IPokemon target, byte hitIndex, + IList types) + { + var typeLibrary = target.Library.StaticLibrary.Types; + + if (executingMove.UseMove.MoveType.Name != "ground") + return; + // Remove all types that are immune to ground moves + types.RemoveAll(x => typeLibrary.GetSingleEffectiveness(executingMove.UseMove.MoveType, x) == 0); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SpiderWebEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SpiderWebEffect.cs new file mode 100644 index 0000000..748007e --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SpiderWebEffect.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; + +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "spider_web_effect")] +public class SpiderWebEffect : Script +{ + private HashSet _targets = new(); + + /// + public override void PreventOpponentRunAway(IFleeChoice choice, ref bool prevent) + { + if (_targets.Contains(choice.User)) + prevent = true; + } + + /// + public override void PreventOpponentSwitch(ISwitchChoice choice, ref bool prevent) + { + if (_targets.Contains(choice.User)) + prevent = true; + } + + public void AddTarget(IPokemon target) + { + _targets.Add(target); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Sleep.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Sleep.cs index 2ac5990..4ba78af 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Sleep.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Sleep.cs @@ -1,7 +1,59 @@ +using System; +using System.Collections.Generic; +using PkmnLib.Static.Utils; + namespace PkmnLib.Plugin.Gen7.Scripts.Status; [Script(ScriptCategory.Status, "sleep")] public class Sleep : Script { public int Turns { get; set; } + private IPokemon? _owner; + + /// + public override void OnAddedToParent(IScriptSource source) + { + // Rare case where the script is added again. Can happen for example when baton pass is used. + if (Turns != 0) + return; + if (source is not IPokemon pokemon) + throw new InvalidOperationException("Sleep script can only be added to a Pokemon."); + _owner = pokemon; + var battleData = pokemon.BattleData; + if (battleData != null) + { + // 1-3 turns of sleep + Turns = battleData.Battle.Random.GetInt(1, 4); + } + } + + /// + public override void PreventMove(IExecutingMove move, ref bool prevent) + { + Turns--; + if (Turns <= 0) + { + RemoveSelf(); + move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("pokemon_woke_up", + new Dictionary + { + { "pokemon", move.User }, + })); + return; + } + + 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) + return; + prevent = true; + } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Hail.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Hail.cs index 1fcce71..ddd4b44 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Hail.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Hail.cs @@ -6,7 +6,7 @@ using PkmnLib.Static.Utils; namespace PkmnLib.Plugin.Gen7.Scripts.Weather; [Script(ScriptCategory.Weather, "hail")] -public class Hail : Script, IWeatherScript +public class Hail : Script, ILimitedTurnsScript { private int? _duration;