Lots more work on implementing battling
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using PkmnLib.Dynamic.Events;
|
||||
using PkmnLib.Dynamic.Libraries;
|
||||
using PkmnLib.Dynamic.Models.BattleFlow;
|
||||
using PkmnLib.Dynamic.Models.Choices;
|
||||
using PkmnLib.Dynamic.ScriptHandling;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Dynamic.Models;
|
||||
|
||||
@@ -69,12 +71,12 @@ public interface IBattle : IScriptSource
|
||||
/// <summary>
|
||||
/// A queue of the yet to be executed choices in a turn.
|
||||
/// </summary>
|
||||
BattleChoiceQueue ChoiceQueue { get; }
|
||||
BattleChoiceQueue? ChoiceQueue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get a Pokemon on the battlefield, on a specific side and an index on that side.
|
||||
/// </summary>
|
||||
IPokemon GetPokemon(byte side, byte position);
|
||||
IPokemon? GetPokemon(byte side, byte position);
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a slot on the battlefield can still be filled. If no party is responsible
|
||||
@@ -92,20 +94,231 @@ public interface IBattle : IScriptSource
|
||||
/// <summary>
|
||||
/// Checks whether a choice is actually possible.
|
||||
/// </summary>
|
||||
void CanUse(ITurnChoice choice);
|
||||
|
||||
bool CanUse(ITurnChoice choice);
|
||||
|
||||
/// <summary>
|
||||
/// Try and set the choice for the battle. If the choice is not valid, this returns false.
|
||||
/// </summary>
|
||||
bool TrySetChoice(ITurnChoice choice);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current weather for the battle. If null is passed, this clears the weather.
|
||||
/// </summary>
|
||||
void SetWeather(string? weatherName);
|
||||
|
||||
void SetWeather(StringKey? weatherName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current weather of the battle. If no weather is present, this returns null.
|
||||
/// </summary>
|
||||
string? WeatherName { get; }
|
||||
StringKey? WeatherName { get; }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBattle"/>
|
||||
public class BattleImpl : ScriptSource, IBattle
|
||||
{
|
||||
/// <inheritdoc cref="BattleImpl"/>
|
||||
public BattleImpl(IDynamicLibrary library, IReadOnlyList<IBattleParty> parties, bool canFlee, byte numberOfSides,
|
||||
byte positionsPerSide)
|
||||
{
|
||||
Library = library;
|
||||
Parties = parties;
|
||||
CanFlee = canFlee;
|
||||
NumberOfSides = numberOfSides;
|
||||
PositionsPerSide = positionsPerSide;
|
||||
var sides = new IBattleSide[numberOfSides];
|
||||
for (byte i = 0; i < numberOfSides; i++)
|
||||
sides[i] = new BattleSideImpl(i, positionsPerSide, this);
|
||||
Sides = sides;
|
||||
Random = new BattleRandomImpl();
|
||||
EventHook = new EventHook();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDynamicLibrary Library { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<IBattleParty> Parties { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanFlee { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte NumberOfSides { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte PositionsPerSide { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<IBattleSide> Sides { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IBattleRandom Random { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasEnded { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public BattleResult? Result { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public EventHook EventHook { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint CurrentTurnNumber { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public BattleChoiceQueue? ChoiceQueue { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPokemon? GetPokemon(byte side, byte position) => Sides[side].Pokemon[position];
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanSlotBeFilled(byte side, byte position) => Parties.Any(x => x.IsResponsibleForIndex(new ResponsibleIndex(side, position)) && x.HasPokemonNotInField());
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ValidateBattleState()
|
||||
{
|
||||
if (HasEnded)
|
||||
return;
|
||||
var survivingSideExists = false;
|
||||
IBattleSide? survivingSide = null;
|
||||
foreach (var side in Sides)
|
||||
{
|
||||
if (side.HasFledBattle)
|
||||
{
|
||||
Result = BattleResult.Inconclusive;
|
||||
HasEnded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!side.IsDefeated())
|
||||
{
|
||||
// If we already found a surviving side, the battle is not over yet
|
||||
if (survivingSideExists)
|
||||
return;
|
||||
survivingSideExists = true;
|
||||
survivingSide = side;
|
||||
}
|
||||
}
|
||||
|
||||
// If every side is defeated, the battle is a draw
|
||||
if (!survivingSideExists)
|
||||
{
|
||||
Result = BattleResult.Inconclusive;
|
||||
HasEnded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If only one side is left, that side has won
|
||||
Result = BattleResult.Conclusive(survivingSide!.Index);
|
||||
HasEnded = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanUse(ITurnChoice choice)
|
||||
{
|
||||
if (choice.User.IsUsable)
|
||||
return false;
|
||||
if (choice is IMoveChoice moveChoice)
|
||||
{
|
||||
// TODO: Hook to change number of PP needed.
|
||||
if (moveChoice.UsedMove.CurrentPp < 1)
|
||||
return false;
|
||||
// TODO: Validate target
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TrySetChoice(ITurnChoice choice)
|
||||
{
|
||||
if (!CanUse(choice))
|
||||
return false;
|
||||
if (choice.User.BattleData?.IsOnBattlefield != true)
|
||||
return false;
|
||||
var side = Sides[choice.User.BattleData!.SideIndex];
|
||||
side.SetChoice(choice.User.BattleData!.Position, choice);
|
||||
CheckChoicesSetAndRun();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CheckChoicesSetAndRun()
|
||||
{
|
||||
foreach (var side in Sides)
|
||||
{
|
||||
if (!side.AllChoicesSet)
|
||||
return;
|
||||
if (!side.AllPositionsFilled())
|
||||
return;
|
||||
}
|
||||
|
||||
var choices = new ITurnChoice[NumberOfSides * PositionsPerSide];
|
||||
for (var index = 0; index < Sides.Count; index++)
|
||||
{
|
||||
var side = Sides[index];
|
||||
for (byte i = 0; i < PositionsPerSide; i++)
|
||||
{
|
||||
var choice = side.SetChoices[i];
|
||||
if (choice is null)
|
||||
throw new InvalidOperationException("Choice is null.");
|
||||
if (choice is IMoveChoice moveChoice)
|
||||
{
|
||||
var priority = moveChoice.UsedMove.MoveData.Priority;
|
||||
choice.RunScriptHook(script => script.ChangePriority(moveChoice, ref priority));
|
||||
moveChoice.Priority = priority;
|
||||
}
|
||||
var speed = choice.User.BoostedStats.Speed;
|
||||
choice.RunScriptHook(script => script.ChangeSpeed(choice, ref speed));
|
||||
choice.Speed = speed;
|
||||
|
||||
choice.RandomValue = (uint)Random.GetInt();
|
||||
choices[index * PositionsPerSide + i] = choice;
|
||||
|
||||
choices[index * PositionsPerSide + i] = choice;
|
||||
}
|
||||
side.ResetChoices();
|
||||
}
|
||||
CurrentTurnNumber += 1;
|
||||
ChoiceQueue = new BattleChoiceQueue(choices);
|
||||
|
||||
this.RunTurn();
|
||||
|
||||
ChoiceQueue = null;
|
||||
EventHook.Invoke(new EndTurnEvent());
|
||||
}
|
||||
|
||||
private readonly ScriptContainer _weatherScript = new();
|
||||
/// <inheritdoc />
|
||||
public void SetWeather(StringKey? weatherName)
|
||||
{
|
||||
if (weatherName.HasValue)
|
||||
{
|
||||
if (!Library.ScriptResolver.TryResolve(ScriptCategory.Weather, weatherName.Value, out var script))
|
||||
throw new InvalidOperationException($"Weather script {weatherName} not found.");
|
||||
_weatherScript.Set(script);
|
||||
script.OnInitialize(Library, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
_weatherScript.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private IScriptSet Volatile { get; } = new ScriptSet();
|
||||
|
||||
/// <inheritdoc />
|
||||
public StringKey? WeatherName => _weatherScript.Script?.Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int ScriptCount => 2;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts)
|
||||
{
|
||||
scripts.Add(_weatherScript );
|
||||
scripts.Add(Volatile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void CollectScripts(List<IEnumerable<ScriptContainer>> scripts) => GetOwnScripts(scripts);
|
||||
}
|
||||
Reference in New Issue
Block a user