From 83b316ad5347fcc4ca687645b816017de6984703 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sat, 11 Jan 2025 14:08:04 +0100 Subject: [PATCH] Support for Bide --- PkmnLib.Dynamic/Models/Battle.cs | 23 +++++++ PkmnLib.Dynamic/ScriptHandling/Script.cs | 4 ++ PkmnLib.Tests/Data/Moves.json | 12 +++- .../Scripts/Moves/Bestow.cs | 25 +++++++ .../PkmnLib.Plugin.Gen7/Scripts/Moves/Bide.cs | 30 ++++++++ .../Scripts/Pokemon/BideEffect.cs | 69 +++++++++++++++++++ 6 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bestow.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bide.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/BideEffect.cs diff --git a/PkmnLib.Dynamic/Models/Battle.cs b/PkmnLib.Dynamic/Models/Battle.cs index 76618ec..e9211bd 100644 --- a/PkmnLib.Dynamic/Models/Battle.cs +++ b/PkmnLib.Dynamic/Models/Battle.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using PkmnLib.Dynamic.Events; using PkmnLib.Dynamic.Libraries; using PkmnLib.Dynamic.Models.BattleFlow; @@ -92,6 +93,8 @@ public interface IBattle : IScriptSource, IDeepCloneable /// void ValidateBattleState(); + bool HasForcedTurn(IPokemon pokemon, [NotNullWhen(true)] out ITurnChoice? choice); + /// /// Checks whether a choice is actually possible. /// @@ -229,11 +232,31 @@ public class BattleImpl : ScriptSource, IBattle HasEnded = true; } + /// + public bool HasForcedTurn(IPokemon pokemon, [NotNullWhen(true)] out ITurnChoice? choice) + { + var battleData = pokemon.BattleData; + if (battleData == null) + { + choice = null; + return false; + } + + ITurnChoice? forcedChoice = null; + pokemon.RunScriptHook( + script => script.ForceTurnSelection(battleData.SideIndex, battleData.Position, ref forcedChoice)); + choice = forcedChoice; + return choice != null; + } + /// public bool CanUse(ITurnChoice choice) { if (!choice.User.IsUsable) return false; + if (HasForcedTurn(choice.User, out var forcedChoice) && choice != forcedChoice) + return false; + if (choice is IMoveChoice moveChoice) { // TODO: Hook to change number of PP needed. diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs index c7ab598..2582f9a 100644 --- a/PkmnLib.Dynamic/ScriptHandling/Script.cs +++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs @@ -83,6 +83,10 @@ public abstract class Script : IDeepCloneable public virtual void PreventMoveSelection(IMoveChoice choice, ref bool prevent) { } + + public virtual void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice) + { + } /// /// This function is ran just before the start of the turn. Everyone has made its choices here, diff --git a/PkmnLib.Tests/Data/Moves.json b/PkmnLib.Tests/Data/Moves.json index 24a7845..54e23d8 100755 --- a/PkmnLib.Tests/Data/Moves.json +++ b/PkmnLib.Tests/Data/Moves.json @@ -810,21 +810,27 @@ "flags": [ "mirror", "ignore-substitute" - ] + ], + "effect": { + "name": "bestow" + } }, { "name": "bide", "type": "normal", "power": 0, "pp": 10, - "accuracy": 0, + "accuracy": 255, "priority": 1, "target": "Self", "category": "physical", "flags": [ "contact", "protect" - ] + ], + "effect": { + "name": "bide" + } }, { "name": "bind", diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bestow.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bestow.cs new file mode 100644 index 0000000..6a3f3fa --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bestow.cs @@ -0,0 +1,25 @@ +using PkmnLib.Dynamic.Models; +using PkmnLib.Dynamic.ScriptHandling; +using PkmnLib.Dynamic.ScriptHandling.Registry; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "bestow")] +public class Bestow : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + var user = move.User; + var userHeldItem = user.HeldItem; + var targetHeldItem = target.HeldItem; + + if (userHeldItem == null || targetHeldItem != null) + { + move.GetHitData(target, hit).Fail(); + return; + } + + _ = target.SetHeldItem(userHeldItem); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bide.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bide.cs new file mode 100644 index 0000000..521e873 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Bide.cs @@ -0,0 +1,30 @@ +using System.Linq; +using PkmnLib.Dynamic.Models; +using PkmnLib.Dynamic.ScriptHandling; +using PkmnLib.Dynamic.ScriptHandling.Registry; +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "bide")] +public class Bide : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + var bideEffect = move.User.Volatile.Get(); + if (bideEffect == null) + { + bideEffect = new BideEffect(move.User); + move.User.Volatile.Add(bideEffect); + } + + bideEffect.Turns += 1; + if (bideEffect.Turns < 2) + return; + var lastPokemon = bideEffect.HitBy.LastOrDefault(x => x.BattleData?.IsOnBattlefield == true); + lastPokemon?.Damage(bideEffect.DamageTaken, DamageSource.MoveDamage); + + RemoveSelf(); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/BideEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/BideEffect.cs new file mode 100644 index 0000000..9a952c5 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/BideEffect.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using PkmnLib.Dynamic.Models; +using PkmnLib.Dynamic.Models.Choices; +using PkmnLib.Dynamic.ScriptHandling; +using PkmnLib.Dynamic.ScriptHandling.Registry; + +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "bide")] +public class BideEffect : Script +{ + private readonly IPokemon? _owner; + public byte Turns; + public uint DamageTaken; + public readonly List HitBy = []; + + private BideEffect() + { + } + + public BideEffect(IPokemon owner) + { + _owner = owner; + } + + /// + public override void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit) + { + HitBy.Add(move.User); + } + + /// + public override void OnDamage(IPokemon pokemon, DamageSource source, uint oldHealth, uint newHealth) + { + DamageTaken += oldHealth - newHealth; + } + + private ITurnChoice? _choice; + + /// + public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice) + { + if (_owner == null) + return; + var ownerBattleData = _owner.BattleData; + if (ownerBattleData == null) + return; + if (ownerBattleData.SideIndex != sideIndex || ownerBattleData.Position != position) + return; + if (_choice != null) + { + choice = _choice; + return; + } + + var bideMove = _owner.Moves.FirstOrDefault(x => x?.MoveData.Name == "bide"); + if (bideMove == null) + { + if (!_owner.Library.StaticLibrary.Moves.TryGet("bide", out var moveData)) + throw new Exception("Move 'bide' not found in move library."); + + bideMove = new LearnedMoveImpl(moveData, MoveLearnMethod.Unknown); + } + + choice = _choice = new MoveChoice(_owner, bideMove, sideIndex, position); + } +} \ No newline at end of file