From 1f5a320090362f280ae3fa06331f7bd4edb9f807 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Mon, 30 Dec 2024 11:43:04 +0100 Subject: [PATCH] Implements switching Pokemon and fleeing from battle --- PkmnLib.Dynamic/Libraries/MiscLibrary.cs | 2 + .../Models/BattleFlow/TurnRunner.cs | 70 ++++++++++++++++++- PkmnLib.Dynamic/Models/BattleSide.cs | 19 +++++ .../Models/Choices/SwitchChoice.cs | 6 +- .../Libraries/Gen7MiscLibrary.cs | 28 ++++++++ 5 files changed, 123 insertions(+), 2 deletions(-) diff --git a/PkmnLib.Dynamic/Libraries/MiscLibrary.cs b/PkmnLib.Dynamic/Libraries/MiscLibrary.cs index 8971efe..c08f971 100644 --- a/PkmnLib.Dynamic/Libraries/MiscLibrary.cs +++ b/PkmnLib.Dynamic/Libraries/MiscLibrary.cs @@ -19,4 +19,6 @@ public interface IMiscLibrary /// Gets the current time of day for the battle. /// TimeOfDay GetTimeOfDay(); + + bool CanFlee(IBattle battle, IFleeChoice fleeChoice); } \ No newline at end of file diff --git a/PkmnLib.Dynamic/Models/BattleFlow/TurnRunner.cs b/PkmnLib.Dynamic/Models/BattleFlow/TurnRunner.cs index ca8cf58..b0d6290 100644 --- a/PkmnLib.Dynamic/Models/BattleFlow/TurnRunner.cs +++ b/PkmnLib.Dynamic/Models/BattleFlow/TurnRunner.cs @@ -84,7 +84,75 @@ public static class TurnRunner case IMoveChoice moveChoice: MoveTurnExecutor.ExecuteMoveChoice(battle, moveChoice); break; - // TODO: Implement other choice types + case ISwitchChoice switchChoice: + ExecuteSwitchChoice(battle, switchChoice); + break; + case IFleeChoice fleeChoice: + ExecuteFleeChoice(battle, fleeChoice); + break; + // TODO: Implement item choice types } } + + private static void ExecuteSwitchChoice(IBattle battle, ISwitchChoice fleeChoice) + { + var user = fleeChoice.User; + var battleData = user.BattleData; + if (battleData == null) + return; + var preventSwitch = false; + fleeChoice.RunScriptHook(script => script.PreventSelfSwitch(fleeChoice, ref preventSwitch)); + if (preventSwitch) + return; + foreach (var side in battle.Sides) + { + if (side.Index == battleData.SideIndex) + continue; + foreach (var pokemon in side.Pokemon.WhereNotNull()) + { + pokemon.RunScriptHook(script => script.PreventOpponentSwitch(fleeChoice, ref preventSwitch)); + if (preventSwitch) + return; + } + } + user.Volatile.Clear(); + var userSide = battle.Sides[battleData.SideIndex]; + userSide.SwapPokemon(battleData.Position, fleeChoice.SwitchTo); + } + + + private static void ExecuteFleeChoice(IBattle battle, IFleeChoice fleeChoice) + { + var user = fleeChoice.User; + var battleData = user.BattleData; + if (battleData == null) + return; + if (!battle.CanFlee) + return; + + var preventFlee = false; + fleeChoice.RunScriptHook(script => script.PreventSelfRunAway(fleeChoice, ref preventFlee)); + if (preventFlee) + return; + + foreach (var side in battle.Sides) + { + if (side.Index == battleData.SideIndex) + continue; + foreach (var pokemon in side.Pokemon.WhereNotNull()) + { + pokemon.RunScriptHook(script => script.PreventOpponentRunAway(fleeChoice, ref preventFlee)); + if (preventFlee) + return; + } + } + + if (!battle.Library.MiscLibrary.CanFlee(battle, fleeChoice)) + return; + + var userSide = battle.Sides[battleData.SideIndex]; + userSide.MarkAsFled(); + battle.ValidateBattleState(); + } + } \ No newline at end of file diff --git a/PkmnLib.Dynamic/Models/BattleSide.cs b/PkmnLib.Dynamic/Models/BattleSide.cs index a0da422..dd6cb03 100644 --- a/PkmnLib.Dynamic/Models/BattleSide.cs +++ b/PkmnLib.Dynamic/Models/BattleSide.cs @@ -110,6 +110,16 @@ public interface IBattleSide : IScriptSource, IDeepCloneable /// Checks whether the side has been defeated. /// bool IsDefeated(); + + /// + /// The number of times this side has attempted to flee. + /// + uint FleeAttempts { get; } + + /// + /// Registers a flee attempt for this side. + /// + void RegisterFleeAttempt(); /// /// Mark the side as fled. @@ -267,6 +277,15 @@ public class BattleSideImpl : ScriptSource, IBattleSide return _fillablePositions.All(fillable => !fillable); } + /// + public uint FleeAttempts { get; private set; } + + /// + public void RegisterFleeAttempt() + { + FleeAttempts++; + } + /// public void MarkAsFled() => HasFledBattle = true; diff --git a/PkmnLib.Dynamic/Models/Choices/SwitchChoice.cs b/PkmnLib.Dynamic/Models/Choices/SwitchChoice.cs index c0e2181..02bfdad 100644 --- a/PkmnLib.Dynamic/Models/Choices/SwitchChoice.cs +++ b/PkmnLib.Dynamic/Models/Choices/SwitchChoice.cs @@ -7,7 +7,10 @@ namespace PkmnLib.Dynamic.Models.Choices; /// public interface ISwitchChoice : ITurnChoice { - + /// + /// The Pokémon to switch to. + /// + IPokemon SwitchTo { get; } } /// @@ -18,6 +21,7 @@ public class SwitchChoice : TurnChoice, ISwitchChoice SwitchTo = switchTo; } + /// public IPokemon SwitchTo { get; } /// diff --git a/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7MiscLibrary.cs b/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7MiscLibrary.cs index 072a650..5733d87 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7MiscLibrary.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7MiscLibrary.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using PkmnLib.Dynamic; using PkmnLib.Dynamic.Libraries; using PkmnLib.Dynamic.Models; @@ -35,4 +36,31 @@ public class Gen7MiscLibrary : IMiscLibrary _ => TimeOfDay.Night, }; } + + /// + public bool CanFlee(IBattle battle, IFleeChoice fleeChoice) + { + var user = fleeChoice.User; + var battleData = user.BattleData; + if (battleData == null) + return false; + var opponentSide = battle.Sides[battleData.SideIndex == 0 ? 1 : 0]; + var opponent = opponentSide.Pokemon.FirstOrDefault(x => x is not null); + if (opponent == null) + return true; + + var userSpeed = user.FlatStats.Speed; + var opponentSpeed = opponent.FlatStats.Speed; + // If the player's active Pokémon's Speed is greater than or equal to the wild Pokémon's Speed, fleeing will + // always succeed + if (userSpeed >= opponentSpeed) + return true; + + var userSide = battle.Sides[battleData.SideIndex]; + + userSide.RegisterFleeAttempt(); + var fleeChance = ((userSpeed * 32) / (opponentSpeed / 4) + (30 * userSide.FleeAttempts)) / 256; + var random = battle.Random.GetInt(0, 100); + return random < fleeChance; + } } \ No newline at end of file