diff --git a/PkmnLib.Dynamic/Models/Battle.cs b/PkmnLib.Dynamic/Models/Battle.cs
index 7acf24c..ba7db5a 100644
--- a/PkmnLib.Dynamic/Models/Battle.cs
+++ b/PkmnLib.Dynamic/Models/Battle.cs
@@ -106,9 +106,16 @@ public interface IBattle : IScriptSource, IDeepCloneable
bool TrySetChoice(ITurnChoice choice);
///
- /// Sets the current weather for the battle. If null is passed, this clears the weather.
+ /// The script that handles the current weather of the battle.
///
- void SetWeather(StringKey? weatherName);
+ IReadOnlyScriptContainer WeatherScript { get; }
+
+ ///
+ /// Sets the current weather for the battle. If null is passed, this clears the weather.
+ /// A duration can be passed to set the duration of the weather in turns. This duration can be modified by
+ /// other scripts before the weather is set through the script hook.
+ ///
+ bool SetWeather(StringKey? weatherName, int duration);
public IScriptSet Volatile { get; }
@@ -343,28 +350,37 @@ public class BattleImpl : ScriptSource, IBattle
EventHook.Invoke(new EndTurnEvent());
}
- private readonly ScriptContainer _weatherScript = new();
+ private ScriptContainer _weatherScript = new();
+ public IReadOnlyScriptContainer WeatherScript => _weatherScript;
///
- public void SetWeather(StringKey? weatherName)
+ public bool SetWeather(StringKey? weatherName, int duration)
{
if (weatherName.HasValue)
{
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)
+ {
+ this.RunScriptHook(x => x.ChangeWeatherDuration(weatherName.Value, ref duration));
+ weatherScript.SetTurns(duration);
+ }
+
_weatherScript.Set(script);
}
else
{
_weatherScript.Clear();
}
+ return true;
// TODO: Trigger weather change script hooks
}
public IScriptSet Volatile { get; } = new ScriptSet();
///
- public StringKey? WeatherName => _weatherScript.Script?.Name;
+ public StringKey? WeatherName => WeatherScript.Script?.Name;
private readonly ScriptContainer _terrainScript = new();
@@ -415,7 +431,7 @@ public class BattleImpl : ScriptSource, IBattle
///
public override void GetOwnScripts(List> scripts)
{
- scripts.Add(_weatherScript);
+ scripts.Add(WeatherScript);
scripts.Add(_terrainScript);
scripts.Add(Volatile);
}
diff --git a/PkmnLib.Dynamic/Models/Pokemon.cs b/PkmnLib.Dynamic/Models/Pokemon.cs
index 320082e..f02e0b0 100644
--- a/PkmnLib.Dynamic/Models/Pokemon.cs
+++ b/PkmnLib.Dynamic/Models/Pokemon.cs
@@ -814,6 +814,7 @@ public class PokemonImpl : ScriptSource, IPokemon
public void SuppressAbility()
{
AbilitySuppressed = true;
+ AbilityScript.Clear();
}
///
@@ -1130,6 +1131,15 @@ public class PokemonImpl : ScriptSource, IPokemon
public void ChangeAbility(IAbility ability)
{
OverrideAbility = ability;
+ if (Library.ScriptResolver.TryResolve(ScriptCategory.Ability, ability.Name, ability.Parameters,
+ out var abilityScript))
+ {
+ AbilityScript.Set(abilityScript);
+ }
+ else
+ {
+ AbilityScript.Clear();
+ }
}
///
diff --git a/PkmnLib.Dynamic/ScriptHandling/IWeatherScript.cs b/PkmnLib.Dynamic/ScriptHandling/IWeatherScript.cs
new file mode 100644
index 0000000..a701b30
--- /dev/null
+++ b/PkmnLib.Dynamic/ScriptHandling/IWeatherScript.cs
@@ -0,0 +1,6 @@
+namespace PkmnLib.Dynamic.ScriptHandling;
+
+public interface IWeatherScript
+{
+ 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 1f4ca91..4377dce 100644
--- a/PkmnLib.Dynamic/ScriptHandling/Script.cs
+++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs
@@ -559,4 +559,8 @@ public abstract class Script : IDeepCloneable
ref int modifiedAccuracy)
{
}
+
+ public virtual void ChangeWeatherDuration(StringKey weatherName, ref int duration)
+ {
+ }
}
\ No newline at end of file
diff --git a/PkmnLib.Dynamic/ScriptHandling/ScriptContainer.cs b/PkmnLib.Dynamic/ScriptHandling/ScriptContainer.cs
index 7592b8a..ad9f2e8 100644
--- a/PkmnLib.Dynamic/ScriptHandling/ScriptContainer.cs
+++ b/PkmnLib.Dynamic/ScriptHandling/ScriptContainer.cs
@@ -4,11 +4,24 @@ using PkmnLib.Static.Utils;
namespace PkmnLib.Dynamic.ScriptHandling;
+public interface IReadOnlyScriptContainer : IEnumerable, IDeepCloneable
+{
+ ///
+ /// Whether this container is empty.
+ ///
+ public bool IsEmpty { get; }
+
+ ///
+ /// The script in this container.
+ ///
+ public Script? Script { get; }
+}
+
///
/// A holder class for a script. This is used so we can cache a list of these, and iterate over them, even when
/// the underlying script changes.
///
-public class ScriptContainer : IEnumerable, IDeepCloneable
+public class ScriptContainer : IReadOnlyScriptContainer
{
///
public ScriptContainer()
@@ -21,15 +34,11 @@ public class ScriptContainer : IEnumerable, IDeepCloneable
Script = script;
}
- ///
- /// Whether this container is empty.
- ///
+ ///
[MemberNotNullWhen(false, nameof(ScriptHandling.Script))]
public bool IsEmpty => Script is null;
- ///
- /// The script in this container.
- ///
+ ///
public Script? Script { get; private set; }
///
diff --git a/PkmnLib.Tests/Data/Moves.json b/PkmnLib.Tests/Data/Moves.json
index 89ddf51..48201d3 100755
--- a/PkmnLib.Tests/Data/Moves.json
+++ b/PkmnLib.Tests/Data/Moves.json
@@ -4766,7 +4766,13 @@
"priority": 0,
"target": "All",
"category": "status",
- "flags": []
+ "flags": [],
+ "effect": {
+ "name": "set_weather",
+ "parameters": {
+ "weather": "hail"
+ }
+ }
},
{
"name": "hammer_arm",
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SetWeather.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SetWeather.cs
new file mode 100644
index 0000000..d42332c
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/SetWeather.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using PkmnLib.Static.Utils;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "set_weather")]
+public class SetWeather : Script
+{
+ private string _weather = null!;
+ private int _defaultTurns = 5;
+
+ ///
+ public override void OnInitialize(IReadOnlyDictionary? parameters)
+ {
+ if (!parameters!.TryGetValue("weather", out var weather) || weather is null)
+ {
+ throw new Exception("Weather not provided.");
+ }
+ _weather = weather!.ToString();
+ if (parameters.TryGetValue("turns", out var turns) && turns is int turn)
+ {
+ _defaultTurns = turn;
+ }
+ }
+
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ target.BattleData?.Battle.SetWeather(_weather, _defaultTurns);
+ }
+}
\ 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 83b383e..fbdc70b 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Hail.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Hail.cs
@@ -6,20 +6,15 @@ using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Weather;
[Script(ScriptCategory.Weather, "hail")]
-public class Hail : Script
+public class Hail : Script, IWeatherScript
{
- public static void RegisterHailIgnoreAbility(StringKey abilityName)
- {
- _hailIgnoreAbilities.Add(abilityName);
- }
+ private int? _duration;
- private static readonly HashSet _hailIgnoreAbilities =
- [
- "ice_body",
- "magic_guard",
- "overcoat",
- "snow_cloak",
- ];
+ ///
+ public void SetTurns(int turns)
+ {
+ _duration = turns;
+ }
///
public override void OnEndTurn(IBattle battle)
@@ -36,14 +31,22 @@ public class Hail : Script
continue;
if (pokemon.Types.Contains(iceType))
continue;
- if (pokemon.ActiveAbility != null && _hailIgnoreAbilities.Contains(pokemon.ActiveAbility.Name))
+ var ignoresHail = false;
+ pokemon.RunScriptHook(x => x.CustomTrigger("ignores_hail", new Dictionary()
+ {
+ { "ignoresHail", ignoresHail },
+ }));
+ if (ignoresHail)
continue;
var maxHealth = pokemon.BoostedStats.Hp;
var damage = maxHealth / 16;
- // TODO: Consider Safety Goggles. Handle it inside the Damage method?
pokemon.Damage(damage, DamageSource.Weather, new EventBatchId());
}
}
+
+ _duration--;
+ if (_duration <= 0)
+ RemoveSelf();
}
}
\ No newline at end of file