summaryrefslogtreecommitdiff
path: root/scripts/Time/TurnManager.cs
blob: 3d55fbb9ff8a0e36b0bf9a39ae778b47a0e7c46d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using Godot;
using TheLegendOfGustav.Map;
using TheLegendOfGustav.Entities.Actors;
using TheLegendOfGustav.Entities;
using TheLegendOfGustav.Entities.Actions;
using TheLegendOfGustav.Utils;

namespace TheLegendOfGustav.Time;

/// <summary>
/// Gerenciador de turnos, o senhor do tempo.
/// </summary>
public partial class TurnManager(Map.Map map) : RefCounted
{
	private Map.Map map = map;
	/// <summary>
	/// As ações do jogador ficam em uma fila e são executadas quando
	/// possível dentro das regras do senhor do tempo.
	/// </summary>
	private Godot.Collections.Array<Action> playerActionQueue = [];

	/// <summary>
    /// A quantidade de turnos que se passaram.
    /// </summary>
	public int TurnCount { get; private set; } = 0;

	private MapData Map_Data { get => map.MapData; }
	private Player Player { get => Map_Data.Player; }

	/// <summary>
	/// Insere uma ação na fila de ações do jogador.
	/// </summary>
	/// <param name="playerAction"></param>
	public void InsertPlayerAction(Action playerAction)
	{
		playerActionQueue.Add(playerAction);
	}

	/// <summary>
	/// 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.
	/// </summary>
	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);
		}
	}

	/// <summary>
	/// Método executado no início do turno.
	/// </summary>
	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);
			}
		}
	}

	/// <summary>
	/// Executa turnos para cada ator no mapa.
	/// </summary>
	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();
				}
			}
		}
	}
}