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
|
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>
/// <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;
}
}
}
|