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
|
using Godot;
using TheLegendOfGustav.Entities.Actions;
using TheLegendOfGustav.Entities.Actors;
using TheLegendOfGustav.InputHandling;
using TheLegendOfGustav.Magic;
using TheLegendOfGustav.Map;
using TheLegendOfGustav.Utils;
namespace TheLegendOfGustav.Entities.Items;
/// <summary>
/// Um pergaminho é um item consumível que joga exatamente um feitiço.
/// Feitiços de pergaminhos são gratuitos e não possuem restrições.
/// </summary>
/// <param name="initialPosition">Posição que o item será colocado no mapa.</param>
/// <param name="map">Mapa que o item será colocado.</param>
/// <param name="definition">Definição do item.</param>
public partial class ScrollConsumable(Vector2I initialPosition, MapData map, ScrollConsumableDefinition definition) : ConsumableItem(initialPosition, map, definition)
{
public SpellResource Spell { get; private set; } = definition.Spell;
/// <summary>
/// O pergaminho só pode ser usado uma única vez.
/// Alguns feitiços precisam de um alvo, o que significa que
/// o pergaminho não pode se auto-destruir enquanto o jogador não
/// escolher uma posição válida.
///
/// Esta variável garante que seja impossível de usar o pergaminho enquanto o jogador
/// escolhe um alvo.
/// </summary>
private bool awaitingTermination = false;
private ScrollConsumableDefinition definition = definition;
/// <summary>
/// O godot exige que desconectemos sinais customizados antes de
/// apagar nós.
///
/// Esta variável guarda a expressão lambda que usamos no sinal PlayerSpellCast
/// para ser desconectado em ConsumedBy.
/// </summary>
private SignalBus.PlayerSpellCastEventHandler bindSignal = null;
public override bool Activate(ItemAction action)
{
if (awaitingTermination)
{
return false;
}
// Feitiços de pergaminhos são gratuitos.
Spell.Cost = 0;
Player consumer = action.Player;
// Alguns feitiços precisam de um alvo escolhido pelo jogador.
// Não podemos esperar pelo jogador aqui, a função precisa retornar de forma síncrona.
//
// Então, se o feitiço precisar de um alvo, foi montada uma infraestrutura de sinais
// para garantir que:
// 1. O jogador possa escolher uma localização qualquer.
// 2. Uma ação seja gerada com a posição escolhida e dentro de um inputHandler para entrar
// no ciclo de turnos normalmente.
// 3. O pergaminho seja destruído quando o feitiço for executado com sucesso.
if (Spell.Type == SpellType.Ranged)
{
// Este delegate existe somente para que eu possa desconectar o sinal de quando o feitiço é executado.
// A engine exige que desconectemos sinais customizados antes de apagar um nó.
bindSignal = delegate (bool success) { OnPlayerChoseTarget(success, action.Player); };
// Eu mando dois sinais aqui. um deles avisa para o input handler trocar para o estado de
// escolher posição de feitiço e o outro informa este inputhandler o feitiço deste pergaminho.
SignalBus.Instance.EmitSignal(SignalBus.SignalName.CommandInputHandler, (int)InputHandlers.CastSpell);
SignalBus.Instance.EmitSignal(SignalBus.SignalName.PlayerSpellChooseLocation, Spell);
// Eu também conecto nosso delegate declarado anteriormente para quando o feitiço for executado.
SignalBus.Instance.PlayerSpellCast += bindSignal;
awaitingTermination = true;
return true;
}
return true;
}
public override void ConsumedBy(Player consumer)
{
// De novo, a engine exige que desconectemos o sinal antes de destruir o pergaminho.
if (bindSignal != null)
{
SignalBus.Instance.PlayerSpellCast -= bindSignal;
}
base.ConsumedBy(consumer);
}
public override void _Notification(int what)
{
if (what == NotificationPredelete)
{
if (bindSignal != null)
{
SignalBus.Instance.PlayerSpellCast -= bindSignal;
}
}
base._Notification(what);
}
/// <summary>
/// Este método é executado quando o feitiço deste pergaminho for executado
/// (depois do jogador escolher um alvo.)
/// </summary>
/// <param name="success">Se o feitiço for executado com sucesso.</param>
/// <summary>
/// Este método é executado quando o feitiço deste pergaminho for executado
/// (depois do jogador escolher um alvo.)
/// </summary>
/// <param name="success">Se o feitiço for executado com sucesso.</param>
/// <param name="consumer">Quem ativou o feitiço.</param>
private void OnPlayerChoseTarget(bool success, Player consumer) {
if (success)
{
ConsumedBy(consumer);
}
else
{
// Se o feitiço não for ativado com sucesso,
// podemos reutilizar o pergaminho.
awaitingTermination = false;
}
}
}
|