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