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