diff --git a/.husky/pre-commit b/.husky/pre-commit
index 6da139d..2108782 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -5,3 +5,5 @@ STAGED_CS=`git diff --cached --name-only --diff-filter=ACMR --pickaxe-regex "*.c
dotnet husky run --group pre-commit --args $STAGED_CS
+# amend the commit with the staged files
+git add `git diff --cached --name-only --diff-filter=ACMR --pickaxe-regex "*.cs"`
\ No newline at end of file
diff --git a/PkmnLib.Dynamic/Models/Pokemon.cs b/PkmnLib.Dynamic/Models/Pokemon.cs
index fa42388..575643d 100644
--- a/PkmnLib.Dynamic/Models/Pokemon.cs
+++ b/PkmnLib.Dynamic/Models/Pokemon.cs
@@ -813,7 +813,7 @@ public class PokemonImpl : ScriptSource, IPokemon
///
public void SuppressAbility()
{
- OverrideAbility = null;
+ AbilitySuppressed = true;
}
///
diff --git a/PkmnLib.Static/Utils/StringKey.cs b/PkmnLib.Static/Utils/StringKey.cs
index c566aa6..0ef5ef8 100644
--- a/PkmnLib.Static/Utils/StringKey.cs
+++ b/PkmnLib.Static/Utils/StringKey.cs
@@ -35,6 +35,15 @@ public readonly record struct StringKey
///
public bool Equals(StringKey other) => string.Equals(_key, other._key, StringComparison.InvariantCultureIgnoreCase);
+ ///
+ public bool Equals(string other) => string.Equals(_key, other, StringComparison.InvariantCultureIgnoreCase);
+
///
public override int GetHashCode() => StringComparer.InvariantCultureIgnoreCase.GetHashCode(_key);
+
+ ///
+ public static bool operator ==(StringKey left, string right) => left.Equals(right);
+
+ ///
+ public static bool operator !=(StringKey left, string right) => !(left == right);
}
\ No newline at end of file
diff --git a/PkmnLib.Tests/Data/Moves.json b/PkmnLib.Tests/Data/Moves.json
index 20b5019..4778e59 100755
--- a/PkmnLib.Tests/Data/Moves.json
+++ b/PkmnLib.Tests/Data/Moves.json
@@ -4136,7 +4136,10 @@
"charge",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "freeze_shock"
+ }
},
{
"name": "frenzy_plant",
@@ -4152,7 +4155,10 @@
"protect",
"mirror",
"nonskybattle"
- ]
+ ],
+ "effect": {
+ "name": "requires_recharge"
+ }
},
{
"name": "frost_breath",
@@ -4166,7 +4172,10 @@
"flags": [
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "force_critical"
+ }
},
{
"name": "frustration",
@@ -4181,7 +4190,10 @@
"contact",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "frustration"
+ }
},
{
"name": "fury_attack",
@@ -4196,7 +4208,10 @@
"contact",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "2_5_hit_move"
+ }
},
{
"name": "fury_cutter",
@@ -4211,7 +4226,10 @@
"contact",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "fury_cutter"
+ }
},
{
"name": "fury_swipes",
@@ -4226,7 +4244,10 @@
"contact",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "2_5_hit_move"
+ }
},
{
"name": "fusion_bolt",
@@ -4240,7 +4261,10 @@
"flags": [
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "fusion_bolt"
+ }
},
{
"name": "fusion_flare",
@@ -4255,7 +4279,10 @@
"protect",
"mirror",
"defrost"
- ]
+ ],
+ "effect": {
+ "name": "fusion_flare"
+ }
},
{
"name": "future_sight",
@@ -4266,7 +4293,10 @@
"priority": 0,
"target": "Any",
"category": "special",
- "flags": []
+ "flags": [],
+ "effect": {
+ "name": "future_sight"
+ }
},
{
"name": "gastro_acid",
@@ -4281,7 +4311,10 @@
"protect",
"reflectable",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "gastro_acid"
+ }
},
{
"name": "gear_grind",
@@ -4296,7 +4329,10 @@
"contact",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "2_hit_move"
+ }
},
{
"name": "gear_up",
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BaseChargeMove.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BaseChargeMove.cs
new file mode 100644
index 0000000..ff0d57a
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BaseChargeMove.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+public abstract class BaseChargeMove : Script where TVolatile : Script
+{
+ public abstract TVolatile CreateVolatile(IPokemon user);
+
+ public override void PreventMove(IExecutingMove move, ref bool prevent)
+ {
+ if (move.User.Volatile.Contains())
+ return;
+
+ move.User.Volatile.Add(CreateVolatile(move.User));
+ move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("began_charging", 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/ForceCritical.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ForceCritical.cs
new file mode 100644
index 0000000..e75f9be
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ForceCritical.cs
@@ -0,0 +1,11 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "force_critical")]
+public class ForceCritical : Script
+{
+ ///
+ public override void ChangeCriticalStage(IExecutingMove move, IPokemon target, byte hit, ref byte stage)
+ {
+ stage = 100;
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FreezeShock.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FreezeShock.cs
new file mode 100644
index 0000000..727fec4
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FreezeShock.cs
@@ -0,0 +1,23 @@
+using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "freeze_shock")]
+public class FreezeShock : BaseChargeMove
+{
+ ///
+ public override FreezeShockEffect CreateVolatile(IPokemon user) => new(user);
+
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ var battleData = target.BattleData;
+ if (battleData == null)
+ return;
+
+ if (battleData.Battle.Random.EffectChance(30, move, target, hit))
+ {
+ target.SetStatus("paralyzed");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Frustration.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Frustration.cs
new file mode 100644
index 0000000..733f98d
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Frustration.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "frustration")]
+public class Frustration : Script
+{
+ ///
+ public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref byte basePower)
+ {
+ var friendship = move.User.Happiness;
+ basePower = Math.Min((byte)1, (byte)((255 - friendship) * 2 / 5));
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FuryCutter.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FuryCutter.cs
new file mode 100644
index 0000000..eb50c30
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FuryCutter.cs
@@ -0,0 +1,18 @@
+using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "fury_cutter")]
+public class FuryCutter : Script
+{
+ ///
+ public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref byte basePower)
+ {
+ var userEffect = move.User.Volatile.Get();
+ if (userEffect == null)
+ return;
+
+ userEffect.TurnCount++;
+ basePower = (byte)(basePower * (userEffect.TurnCount + 1));
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FusionBolt.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FusionBolt.cs
new file mode 100644
index 0000000..b0ab36d
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FusionBolt.cs
@@ -0,0 +1,26 @@
+using System.Linq;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "fusion_bolt")]
+public class FusionBolt : Script
+{
+ ///
+ public override void ChangeDamageModifier(IExecutingMove move, IPokemon target, byte hit, ref float modifier)
+ {
+ var battleData = target.BattleData;
+ if (battleData == null)
+ return;
+
+ // Grab the choices for the current turn, that have been executed before this move.
+ var choice = battleData.Battle.PreviousTurnChoices.Last().TakeWhile(x => x != move.MoveChoice)
+ // Of these, find the move choice that used Fusion Flare.
+ .OfType().FirstOrDefault(x => x.ChosenMove.MoveData.Name == "fusion_flare");
+
+ // If Fusion Flare was used, Fusion Bolt's power is doubled.
+ if (choice != null)
+ {
+ modifier *= 2;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FusionFlare.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FusionFlare.cs
new file mode 100644
index 0000000..e6ee3c4
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FusionFlare.cs
@@ -0,0 +1,26 @@
+using System.Linq;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "fusion_flare")]
+public class FusionFlare : Script
+{
+ ///
+ public override void ChangeDamageModifier(IExecutingMove move, IPokemon target, byte hit, ref float modifier)
+ {
+ var battleData = target.BattleData;
+ if (battleData == null)
+ return;
+
+ // Grab the choices for the current turn, that have been executed before this move.
+ var choice = battleData.Battle.PreviousTurnChoices.Last().TakeWhile(x => x != move.MoveChoice)
+ // Of these, find the move choice that used Fusion Bolt.
+ .OfType().FirstOrDefault(x => x.ChosenMove.MoveData.Name == "fusion_bolt");
+
+ // If Fusion Bolt was used, Fusion Flare's power is doubled.
+ if (choice != null)
+ {
+ modifier *= 2;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FutureSight.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FutureSight.cs
new file mode 100644
index 0000000..90a2325
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FutureSight.cs
@@ -0,0 +1,20 @@
+using System;
+using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+using PkmnLib.Static.Utils;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "future_sight")]
+public class FutureSight : Script
+{
+ ///
+ public override void StopBeforeMove(IExecutingMove move, ref bool prevent)
+ {
+ var battleData = move.User.BattleData;
+ if (battleData == null)
+ return;
+ battleData.Battle.Volatile.Add(new FutureSightEffect(move.MoveChoice));
+
+ prevent = true;
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GastroAcid.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GastroAcid.cs
new file mode 100644
index 0000000..4655746
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GastroAcid.cs
@@ -0,0 +1,11 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "gastro_acid")]
+public class GastroAcid : Script
+{
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ target.SuppressAbility();
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/BaseChargeEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/BaseChargeEffect.cs
new file mode 100644
index 0000000..a4b9c0e
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/BaseChargeEffect.cs
@@ -0,0 +1,22 @@
+using PkmnLib.Plugin.Gen7.Scripts.Utils;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+public abstract class BaseChargeEffect : Script
+{
+ private readonly IPokemon _owner;
+ private readonly string _moveName;
+
+ public BaseChargeEffect(IPokemon owner, string moveName)
+ {
+ _owner = owner;
+ _moveName = moveName;
+ }
+
+ ///
+ public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice)
+ {
+ var opposingSideIndex = (byte)(_owner.BattleData?.SideIndex == 0 ? 1 : 0);
+ choice = TurnChoiceHelper.CreateMoveChoice(_owner, _moveName, opposingSideIndex, position);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FreezeShockEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FreezeShockEffect.cs
new file mode 100644
index 0000000..83b5660
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FreezeShockEffect.cs
@@ -0,0 +1,4 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+[Script(ScriptCategory.Pokemon, "freeze_shock")]
+public class FreezeShockEffect(IPokemon owner) : BaseChargeEffect(owner, "freeze_shock");
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FuryCutterEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FuryCutterEffect.cs
new file mode 100644
index 0000000..98a745c
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FuryCutterEffect.cs
@@ -0,0 +1,14 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+[Script(ScriptCategory.Pokemon, "fury_cutter")]
+public class FuryCutterEffect : Script
+{
+ public int TurnCount { get; set; }
+
+ ///
+ public override void OnBeforeMove(IExecutingMove move)
+ {
+ if (move.UseMove.Name != "fury_cutter")
+ RemoveSelf();
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FutureSightEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FutureSightEffect.cs
new file mode 100644
index 0000000..4655557
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FutureSightEffect.cs
@@ -0,0 +1,35 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+[Script(ScriptCategory.Pokemon, "future_sight")]
+public class FutureSightEffect : Script
+{
+ private int _turnsLeft = 3;
+ private readonly IMoveChoice _moveChoice;
+
+ public FutureSightEffect(IMoveChoice moveChoice)
+ {
+ _moveChoice = moveChoice;
+ }
+
+ ///
+ public override void OnEndTurn(IBattle battle)
+ {
+ _turnsLeft -= 1;
+ if (_turnsLeft <= 0)
+ {
+ var target = battle.GetPokemon(_moveChoice.TargetSide, _moveChoice.TargetPosition);
+ if (target is not { IsUsable: true })
+ {
+ battle.EventHook.Invoke(new DialogEvent("move_failed"));
+ return;
+ }
+ var damageCalculator = battle.Library.DamageCalculator;
+ var executingMove = new ExecutingMoveImpl([target], 1, _moveChoice.ChosenMove,
+ _moveChoice.ChosenMove.MoveData, _moveChoice);
+ var hitData = executingMove.GetHitData(target, 0);
+ var damage = damageCalculator.GetDamage(executingMove, target, 1, hitData);
+
+ target.Damage(damage, DamageSource.Misc);
+ }
+ }
+}
\ No newline at end of file