Implements switching Pokemon and fleeing from battle

This commit is contained in:
Deukhoofd 2024-12-30 11:43:04 +01:00
parent 9bdd584b54
commit 1f5a320090
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
5 changed files with 123 additions and 2 deletions

View File

@ -19,4 +19,6 @@ public interface IMiscLibrary
/// Gets the current time of day for the battle.
/// </summary>
TimeOfDay GetTimeOfDay();
bool CanFlee(IBattle battle, IFleeChoice fleeChoice);
}

View File

@ -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();
}
}

View File

@ -111,6 +111,16 @@ public interface IBattleSide : IScriptSource, IDeepCloneable
/// </summary>
bool IsDefeated();
/// <summary>
/// The number of times this side has attempted to flee.
/// </summary>
uint FleeAttempts { get; }
/// <summary>
/// Registers a flee attempt for this side.
/// </summary>
void RegisterFleeAttempt();
/// <summary>
/// Mark the side as fled.
/// </summary>
@ -267,6 +277,15 @@ public class BattleSideImpl : ScriptSource, IBattleSide
return _fillablePositions.All(fillable => !fillable);
}
/// <inheritdoc />
public uint FleeAttempts { get; private set; }
/// <inheritdoc />
public void RegisterFleeAttempt()
{
FleeAttempts++;
}
/// <inheritdoc />
public void MarkAsFled() => HasFledBattle = true;

View File

@ -7,7 +7,10 @@ namespace PkmnLib.Dynamic.Models.Choices;
/// </summary>
public interface ISwitchChoice : ITurnChoice
{
/// <summary>
/// The Pokémon to switch to.
/// </summary>
IPokemon SwitchTo { get; }
}
/// <inheritdoc cref="ISwitchChoice"/>
@ -18,6 +21,7 @@ public class SwitchChoice : TurnChoice, ISwitchChoice
SwitchTo = switchTo;
}
/// <inheritdoc />
public IPokemon SwitchTo { get; }
/// <inheritdoc />

View File

@ -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,
};
}
/// <inheritdoc />
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;
}
}