diff --git a/PkmnLib.Dynamic/Events/CaptureAttemptEvent.cs b/PkmnLib.Dynamic/Events/CaptureAttemptEvent.cs
new file mode 100644
index 0000000..e92e06c
--- /dev/null
+++ b/PkmnLib.Dynamic/Events/CaptureAttemptEvent.cs
@@ -0,0 +1,19 @@
+using PkmnLib.Dynamic.Libraries;
+using PkmnLib.Dynamic.Models;
+
+namespace PkmnLib.Dynamic.Events;
+
+public class CaptureAttemptEvent : IEventData
+{
+ public CaptureAttemptEvent(IPokemon target, CaptureResult result)
+ {
+ Target = target;
+ Result = result;
+ }
+
+ public IPokemon Target { get; init; }
+ public CaptureResult Result { get; init; }
+
+ ///
+ public EventBatchId BatchId { get; init; }
+}
\ No newline at end of file
diff --git a/PkmnLib.Dynamic/Libraries/CaptureLibrary.cs b/PkmnLib.Dynamic/Libraries/CaptureLibrary.cs
new file mode 100644
index 0000000..c8341f0
--- /dev/null
+++ b/PkmnLib.Dynamic/Libraries/CaptureLibrary.cs
@@ -0,0 +1,25 @@
+using PkmnLib.Dynamic.Models;
+using PkmnLib.Static;
+
+namespace PkmnLib.Dynamic.Libraries;
+
+public record struct CaptureResult
+{
+ public CaptureResult(bool IsCaught, int Shakes, bool CriticalCapture)
+ {
+ this.IsCaught = IsCaught;
+ this.Shakes = Shakes;
+ this.CriticalCapture = CriticalCapture;
+ }
+
+ public bool IsCaught { get; init; }
+ public int Shakes { get; init; }
+ public bool CriticalCapture { get; init; }
+
+ public static CaptureResult Failed => new CaptureResult(false, 0, false);
+}
+
+public interface ICaptureLibrary
+{
+ CaptureResult TryCapture(IPokemon target, IItem captureItem, IBattleRandom random);
+}
\ No newline at end of file
diff --git a/PkmnLib.Dynamic/Libraries/DynamicLibrary.cs b/PkmnLib.Dynamic/Libraries/DynamicLibrary.cs
index 0e1f1e0..1bb237b 100644
--- a/PkmnLib.Dynamic/Libraries/DynamicLibrary.cs
+++ b/PkmnLib.Dynamic/Libraries/DynamicLibrary.cs
@@ -32,6 +32,11 @@ public interface IDynamicLibrary
///
IMiscLibrary MiscLibrary { get; }
+ ///
+ /// The capture library deals with the calculation of the capture rate of a Pokémon.
+ ///
+ ICaptureLibrary CaptureLibrary { get; }
+
///
/// A holder of the script types that can be resolved by this library.
///
@@ -58,19 +63,22 @@ public class DynamicLibraryImpl : IDynamicLibrary
throw new InvalidOperationException("Stat calculator not found in plugins.");
if (registry.MiscLibrary is null)
throw new InvalidOperationException("Misc library not found in plugins.");
+ if (registry.CaptureLibrary is null)
+ throw new InvalidOperationException("Capture library not found in plugins.");
var scriptResolver = new ScriptResolver(registry.ScriptTypes, registry.ItemScriptTypes);
return new DynamicLibraryImpl(staticLibrary, registry.BattleStatCalculator,
- registry.DamageCalculator, registry.MiscLibrary, scriptResolver);
+ registry.DamageCalculator, registry.MiscLibrary, registry.CaptureLibrary, scriptResolver);
}
private DynamicLibraryImpl(IStaticLibrary staticLibrary, IBattleStatCalculator statCalculator,
- IDamageCalculator damageCalculator, IMiscLibrary miscLibrary, ScriptResolver scriptResolver)
+ IDamageCalculator damageCalculator, IMiscLibrary miscLibrary, ICaptureLibrary captureLibrary, ScriptResolver scriptResolver)
{
StaticLibrary = staticLibrary;
StatCalculator = statCalculator;
DamageCalculator = damageCalculator;
MiscLibrary = miscLibrary;
ScriptResolver = scriptResolver;
+ CaptureLibrary = captureLibrary;
}
///
@@ -84,7 +92,10 @@ public class DynamicLibraryImpl : IDynamicLibrary
///
public IMiscLibrary MiscLibrary { get; }
-
+
+ ///
+ public ICaptureLibrary CaptureLibrary { get; }
+
///
public ScriptResolver ScriptResolver { get; }
}
\ No newline at end of file
diff --git a/PkmnLib.Dynamic/Models/Battle.cs b/PkmnLib.Dynamic/Models/Battle.cs
index 3924d28..84c2d7a 100644
--- a/PkmnLib.Dynamic/Models/Battle.cs
+++ b/PkmnLib.Dynamic/Models/Battle.cs
@@ -3,6 +3,7 @@ using PkmnLib.Dynamic.Libraries;
using PkmnLib.Dynamic.Models.BattleFlow;
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Dynamic.ScriptHandling;
+using PkmnLib.Static;
using PkmnLib.Static.Utils;
namespace PkmnLib.Dynamic.Models;
@@ -116,6 +117,8 @@ public interface IBattle : IScriptSource, IDeepCloneable
/// for a single turn. The outer list is ordered from oldest to newest turn.
///
IReadOnlyList> PreviousTurnChoices { get; }
+
+ CaptureResult AttempCapture(byte sideIndex, byte position, IItem item);
}
///
@@ -335,6 +338,24 @@ public class BattleImpl : ScriptSource, IBattle
///
public IReadOnlyList> PreviousTurnChoices => _previousTurnChoices;
+ ///
+ public CaptureResult AttempCapture(byte sideIndex, byte position, IItem item)
+ {
+ var target = GetPokemon(sideIndex, position);
+ if (target is not { IsUsable: true })
+ return CaptureResult.Failed;
+
+ var attemptCapture = Library.CaptureLibrary.TryCapture(target, item, Random);
+ if (attemptCapture.IsCaught)
+ {
+ target.MarkAsCaught();
+ var side = Sides[target.BattleData!.SideIndex];
+ side.ForceClearPokemonFromField(target.BattleData.Position);
+ }
+ EventHook.Invoke(new CaptureAttemptEvent(target, attemptCapture));
+ return attemptCapture;
+ }
+
///
public override int ScriptCount => 2;
diff --git a/PkmnLib.Dynamic/Models/BattleSide.cs b/PkmnLib.Dynamic/Models/BattleSide.cs
index dd6cb03..335f740 100644
--- a/PkmnLib.Dynamic/Models/BattleSide.cs
+++ b/PkmnLib.Dynamic/Models/BattleSide.cs
@@ -217,6 +217,13 @@ public class BattleSideImpl : ScriptSource, IBattleSide
///
public void ForceClearPokemonFromField(byte index)
{
+ var pokemon = _pokemon[index];
+ if (pokemon is not null)
+ {
+ pokemon.RunScriptHook(script => script.OnRemove());
+ pokemon.SetOnBattlefield(false);
+ }
+
_pokemon[index] = null;
}
diff --git a/PkmnLib.Dynamic/Models/Pokemon.cs b/PkmnLib.Dynamic/Models/Pokemon.cs
index 9780b5d..a35330a 100644
--- a/PkmnLib.Dynamic/Models/Pokemon.cs
+++ b/PkmnLib.Dynamic/Models/Pokemon.cs
@@ -201,6 +201,8 @@ public interface IPokemon : IScriptSource, IDeepCloneable
/// Whether or not this Pokemon was caught this battle.
///
bool IsCaught { get; }
+
+ public void MarkAsCaught();
///
/// The script for the held item.
@@ -632,6 +634,12 @@ public class PokemonImpl : ScriptSource, IPokemon
///
public bool IsCaught { get; private set; }
+ ///
+ public void MarkAsCaught()
+ {
+ IsCaught = true;
+ }
+
///
public ScriptContainer HeldItemTriggerScript { get; } = new();
diff --git a/PkmnLib.Dynamic/ScriptHandling/ItemScript.cs b/PkmnLib.Dynamic/ScriptHandling/ItemScript.cs
index be0920d..996cdc3 100644
--- a/PkmnLib.Dynamic/ScriptHandling/ItemScript.cs
+++ b/PkmnLib.Dynamic/ScriptHandling/ItemScript.cs
@@ -1,10 +1,19 @@
using PkmnLib.Dynamic.Models;
+using PkmnLib.Static;
using PkmnLib.Static.Utils;
namespace PkmnLib.Dynamic.ScriptHandling;
public abstract class ItemScript : IDeepCloneable
{
+ protected ItemScript(IItem item)
+ {
+ Item = item;
+ }
+
+ protected IItem Item { get; private set; }
+
+
///
/// Initializes the script with the given parameters for a specific item
///
diff --git a/PkmnLib.Dynamic/ScriptHandling/PokeballScript.cs b/PkmnLib.Dynamic/ScriptHandling/PokeballScript.cs
new file mode 100644
index 0000000..5af4a0f
--- /dev/null
+++ b/PkmnLib.Dynamic/ScriptHandling/PokeballScript.cs
@@ -0,0 +1,24 @@
+using PkmnLib.Dynamic.Models;
+using PkmnLib.Static;
+
+namespace PkmnLib.Dynamic.ScriptHandling;
+
+public abstract class PokeballScript : ItemScript
+{
+ ///
+ protected PokeballScript(IItem item) : base(item)
+ {
+ }
+
+ public abstract byte GetCatchRate(IPokemon target);
+
+ ///
+ public override void OnUseWithTarget(IPokemon target)
+ {
+ var battleData = target.BattleData;
+ if (battleData == null)
+ return;
+
+ battleData.Battle.AttempCapture(battleData.SideIndex, battleData.Position, Item);
+ }
+}
\ No newline at end of file
diff --git a/PkmnLib.Dynamic/ScriptHandling/Registry/ScriptRegistry.cs b/PkmnLib.Dynamic/ScriptHandling/Registry/ScriptRegistry.cs
index 8c22b63..bce904b 100644
--- a/PkmnLib.Dynamic/ScriptHandling/Registry/ScriptRegistry.cs
+++ b/PkmnLib.Dynamic/ScriptHandling/Registry/ScriptRegistry.cs
@@ -2,6 +2,7 @@ using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using PkmnLib.Dynamic.Libraries;
+using PkmnLib.Static;
using PkmnLib.Static.Utils;
namespace PkmnLib.Dynamic.ScriptHandling.Registry;
@@ -13,10 +14,11 @@ namespace PkmnLib.Dynamic.ScriptHandling.Registry;
public class ScriptRegistry
{
private readonly Dictionary<(ScriptCategory category, StringKey name), Func