From a3a9f1800a21ed215e3de751ded865a269c61c44 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Fri, 7 Mar 2025 11:50:59 +0100 Subject: [PATCH] Implements 11 more moves --- .husky/pre-commit | 2 + PkmnLib.Dynamic/Models/Pokemon.cs | 2 +- PkmnLib.Static/Utils/StringKey.cs | 9 +++ PkmnLib.Tests/Data/Moves.json | 60 +++++++++++++++---- .../Scripts/Moves/BaseChargeMove.cs | 27 +++++++++ .../Scripts/Moves/ForceCritical.cs | 11 ++++ .../Scripts/Moves/FreezeShock.cs | 23 +++++++ .../Scripts/Moves/Frustration.cs | 14 +++++ .../Scripts/Moves/FuryCutter.cs | 18 ++++++ .../Scripts/Moves/FusionBolt.cs | 26 ++++++++ .../Scripts/Moves/FusionFlare.cs | 26 ++++++++ .../Scripts/Moves/FutureSight.cs | 20 +++++++ .../Scripts/Moves/GastroAcid.cs | 11 ++++ .../Scripts/Pokemon/BaseChargeEffect.cs | 22 +++++++ .../Scripts/Pokemon/FreezeShockEffect.cs | 4 ++ .../Scripts/Pokemon/FuryCutterEffect.cs | 14 +++++ .../Scripts/Pokemon/FutureSightEffect.cs | 35 +++++++++++ 17 files changed, 311 insertions(+), 13 deletions(-) create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/BaseChargeMove.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ForceCritical.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FreezeShock.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Frustration.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FuryCutter.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FusionBolt.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FusionFlare.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FutureSight.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GastroAcid.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/BaseChargeEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FreezeShockEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FuryCutterEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/FutureSightEffect.cs 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