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;