diff options
Diffstat (limited to 'scripts/Entities')
| -rw-r--r-- | scripts/Entities/Actions/ChangeInputStateAction.cs.uid | 1 | ||||
| -rw-r--r-- | scripts/Entities/Actions/SpellAction.cs | 34 | ||||
| -rw-r--r-- | scripts/Entities/Items/ConsumableItem.cs | 2 | ||||
| -rw-r--r-- | scripts/Entities/Items/ScrollConsumable.cs | 96 |
4 files changed, 123 insertions, 10 deletions
diff --git a/scripts/Entities/Actions/ChangeInputStateAction.cs.uid b/scripts/Entities/Actions/ChangeInputStateAction.cs.uid new file mode 100644 index 0000000..c8f6440 --- /dev/null +++ b/scripts/Entities/Actions/ChangeInputStateAction.cs.uid @@ -0,0 +1 @@ +uid://bxlakdvr1lovw diff --git a/scripts/Entities/Actions/SpellAction.cs b/scripts/Entities/Actions/SpellAction.cs index 086a52a..f8819b9 100644 --- a/scripts/Entities/Actions/SpellAction.cs +++ b/scripts/Entities/Actions/SpellAction.cs @@ -1,23 +1,45 @@ using Godot; using TheLegendOfGustav.Entities.Actors; using TheLegendOfGustav.Magic; +using TheLegendOfGustav.Utils; namespace TheLegendOfGustav.Entities.Actions; -public partial class SpellCommand(Actor actor, Vector2I offset, SpellResource spell) : DirectionalAction(actor, offset) +/// <summary> +/// Ação para quando o jogador joga um feitiço. +/// </summary> +public partial class SpellAction : DirectionalAction { - private SpellResource spell = spell; + private SpellResource spell; + + public SpellAction(Actor actor, Vector2I offset, SpellResource spell) : base(actor, offset) + { + this.spell = spell; + + Cost = 5; + } public override bool Perform() { - Actor target = null; + Actor target; - if (GetTarget() is Actor actor) + if (spell.Type == SpellType.Self) + { + target = Actor; + } + else if (GetTarget() is Actor actor) { target = actor; } + else + { + return false; + } - if (spell.Type == SpellType.Ranged && target == null) return false; + if (Grid.Distance(Actor.GridPosition, target.GridPosition) > spell.Range) + { + return false; + } if (Actor.Mp < spell.Cost) { @@ -28,6 +50,8 @@ public partial class SpellCommand(Actor actor, Vector2I offset, SpellResource sp { effect.Apply(Actor, target); } + + Actor.Energy -= Cost; return true; } } diff --git a/scripts/Entities/Items/ConsumableItem.cs b/scripts/Entities/Items/ConsumableItem.cs index 4078bd3..b672b7d 100644 --- a/scripts/Entities/Items/ConsumableItem.cs +++ b/scripts/Entities/Items/ConsumableItem.cs @@ -31,7 +31,7 @@ public abstract partial class ConsumableItem(Vector2I initialPosition, MapData m /// <returns>Se a ação foi realizada ou não.</returns> public abstract bool Activate(ItemAction action); - public void ConsumedBy(Player consumer) + public virtual void ConsumedBy(Player consumer) { Inventory inventory = consumer.Inventory; inventory.RemoveItem(this); diff --git a/scripts/Entities/Items/ScrollConsumable.cs b/scripts/Entities/Items/ScrollConsumable.cs index d260263..de809a0 100644 --- a/scripts/Entities/Items/ScrollConsumable.cs +++ b/scripts/Entities/Items/ScrollConsumable.cs @@ -1,25 +1,113 @@ 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) { - private ScrollConsumableDefinition definition = 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; - MessageLogData.Instance.AddMessage("Foste cuckado"); - ConsumedBy(consumer); + // 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); + } + + /// <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; + } + } }
\ No newline at end of file |
