using Godot;
using TheLegendOfGustav.Map;
using TheLegendOfGustav.Entities.Actors;
using TheLegendOfGustav.Entities;
using TheLegendOfGustav.Entities.Actions;
using TheLegendOfGustav.Utils;
namespace TheLegendOfGustav.Time;
///
/// Gerenciador de turnos, o senhor do tempo.
///
public partial class TurnManager(Map.Map map) : RefCounted
{
public int TurnCount { get; private set; } = 0;
private Map.Map Map { get; set; } = map;
private MapData Map_Data { get => Map.MapData; }
private Player Player { get => Map_Data.Player; }
///
/// As ações do jogador ficam em uma fila e são executadas quando
/// possível dentro das regras do senhor do tempo.
///
private Godot.Collections.Array PlayerActionQueue { get; set; } = [];
///
/// Insere uma ação na fila de ações do jogador.
///
///
public void InsertPlayerAction(Action playerAction)
{
PlayerActionQueue.Add(playerAction);
}
///
/// Computa um turno.
///
/// Um turno segue a seguinte ordem lógica
/// 1. Todos os atores recebem energia com base nas suas velocidades.
/// 2. O jogador performa ações enquanto sua energia permitir
/// 3. Os outros atores performam suas ações enquanto sua energia permitir.
///
public void Tick()
{
// Se o jogador puder agir mas a fila de ações estiver vazia,
// não computamos o turno.
if (PlayerActionQueue.Count == 0 && Player.Energy > 0)
{
return;
}
Vector2I previousPlayerPos = Player.GridPosition;
// Início do turno, o jogador recebe um pouco de energia.
if (Player.Energy <= 0)
{
StartTurn();
}
bool actionResult = true; ;
// Primeiro executamos a ação do jogador, se ele puder.
if (PlayerActionQueue.Count > 0 && Player.Energy > 0)
{
Action action = PlayerActionQueue[0];
PlayerActionQueue.RemoveAt(0);
actionResult = action.Perform();
// TODO: Isto é feio, lembre-me de mudar isto antes da entrega final.
if (action is SpellAction)
{
GD.Print(actionResult);
SignalBus.Instance.EmitSignal(SignalBus.SignalName.PlayerSpellCast, actionResult);
}
}
// Se a ação do jogador for gratuita ou se o jogador ainda possuir energia,
// ele poderá fazer mais um turno sem interrupções.
if (actionResult && Player.Energy <= 0)
{
// Depois computamos os turnos dos outros atores.
HandleEnemyTurns();
Map.UpdateFOV(Player.GridPosition);
}
// Por fim, se o jogador mudou de lugar, atualizamos seu campo de visão.
if (Player.GridPosition != previousPlayerPos)
{
Map.UpdateFOV(Player.GridPosition);
}
}
///
/// Método executado no início do turno.
///
private void StartTurn()
{
TurnCount++;
// Recarregamos a energia de todos os atores.
foreach (Entity entity in Map_Data.Entities)
{
if (entity is Actor actor && actor.IsAlive)
{
actor.OnTurnStart(TurnCount);
}
}
}
///
/// Executa turnos para cada ator no mapa.
///
private void HandleEnemyTurns()
{
foreach (Entity entity in Map_Data.Entities)
{
if (entity is Player) continue;
// Se o ator for um inimigo e estiver vivo, deixamos
// que sua IA faça um turno.
if (entity is Enemy enemy && enemy.IsAlive)
{
// O inimigo poderá fazer quantos turnos sua energia deixar.
while (enemy.Energy > 0)
{
enemy.Soul.Perform();
}
}
}
}
}