diff options
| author | Matheus <matheus.guedes.mg.m@gmail.com> | 2025-09-14 00:14:25 -0300 |
|---|---|---|
| committer | Matheus <matheus.guedes.mg.m@gmail.com> | 2025-09-14 00:14:25 -0300 |
| commit | 5958d9c071915ab71aea5a5c08d79e88024f6c58 (patch) | |
| tree | 8ab992a1ae055ea501faa48f3fdc778061945ba0 /scripts | |
| parent | 539fd4820f37aa54df8878091db9680d89ee027a (diff) | |
The newer scolls: groundrim.
Diffstat (limited to 'scripts')
| -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 | ||||
| -rw-r--r-- | scripts/InputHandling/CastSpellInputHandler.cs | 93 | ||||
| -rw-r--r-- | scripts/InputHandling/CastSpellInputHandler.cs.uid | 1 | ||||
| -rw-r--r-- | scripts/InputHandling/InputHandler.cs | 12 | ||||
| -rw-r--r-- | scripts/Magic/DamageEffect.cs | 3 | ||||
| -rw-r--r-- | scripts/Time/TurnManager.cs | 8 | ||||
| -rw-r--r-- | scripts/Utils/SignalBus.cs | 11 |
10 files changed, 250 insertions, 11 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 diff --git a/scripts/InputHandling/CastSpellInputHandler.cs b/scripts/InputHandling/CastSpellInputHandler.cs new file mode 100644 index 0000000..1c5d005 --- /dev/null +++ b/scripts/InputHandling/CastSpellInputHandler.cs @@ -0,0 +1,93 @@ +using Godot; +using TheLegendOfGustav.Entities.Actors; +using TheLegendOfGustav.Entities.Actions; +using TheLegendOfGustav.Utils; + +using TheLegendOfGustav.InputHandling; +using TheLegendOfGustav.Map; +using TheLegendOfGustav.Magic; + +namespace TheLengendOfGustav.InputHandling; + +/// <summary> +/// Esquema de controles para quando o jogador precisar escolher um alvo +/// de feitiço. +/// +/// É bem similar ao InspectorInputHandler. +/// </summary> +public partial class CastSpellInputHandler : BaseInputHandler +{ + private static readonly PackedScene inspectorScene = GD.Load<PackedScene>("res://scenes/Inspector.tscn"); + + private static readonly Godot.Collections.Dictionary<string, Vector2I> directions = new() + { + {"walk-up", Vector2I.Up}, + {"walk-down", Vector2I.Down}, + {"walk-left", Vector2I.Left}, + {"walk-right", Vector2I.Right}, + {"walk-up-right", Vector2I.Up + Vector2I.Right}, + {"walk-up-left", Vector2I.Up + Vector2I.Left}, + {"walk-down-right", Vector2I.Down + Vector2I.Right}, + {"walk-down-left", Vector2I.Down + Vector2I.Left}, + }; + + private Inspector inspector = null; + private SpellResource selectedSpell = null; + [Export] + private Map map; + + public override void _Ready() + { + base._Ready(); + + // O jogador informa qual feitiço será usado. + SignalBus.Instance.PlayerSpellChooseLocation += (SpellResource spell) => selectedSpell = spell; + } + + public override void Enter() + { + inspector = inspectorScene.Instantiate<Inspector>(); + + inspector.GridPosition = map.MapData.Player.GridPosition; + + map.AddChild(inspector); + } + + public override void Exit() + { + selectedSpell = null; + inspector.QueueFree(); + } + + + public override Action GetAction(Player player) + { + Action action = null; + + foreach (var direction in directions) + { + if (Input.IsActionJustPressed(direction.Key)) + { + inspector.Walk(direction.Value); + } + } + + if (selectedSpell != null && Input.IsActionJustPressed("ui_accept")) + { + action = new SpellAction(player, inspector.GridPosition - player.GridPosition, selectedSpell); + + GetParent<InputHandler>().SetInputHandler(InputHandlers.MainGame); + } + + // Se o jogador cancelar a seleção, + // Mandamos um sinal avisando que o feitiço não foi executado com sucesso. + // Pergaminhos usam esta informação. + if (Input.IsActionJustPressed("quit")) + { + SignalBus.Instance.EmitSignal(SignalBus.SignalName.PlayerSpellCast, false); + GetParent<InputHandler>().SetInputHandler(InputHandlers.MainGame); + } + + return action; + } +}
\ No newline at end of file diff --git a/scripts/InputHandling/CastSpellInputHandler.cs.uid b/scripts/InputHandling/CastSpellInputHandler.cs.uid new file mode 100644 index 0000000..26ed706 --- /dev/null +++ b/scripts/InputHandling/CastSpellInputHandler.cs.uid @@ -0,0 +1 @@ +uid://ceck6d5tjpgwf diff --git a/scripts/InputHandling/InputHandler.cs b/scripts/InputHandling/InputHandler.cs index 55a17b4..a1e9b03 100644 --- a/scripts/InputHandling/InputHandler.cs +++ b/scripts/InputHandling/InputHandler.cs @@ -1,6 +1,8 @@ using Godot; using TheLegendOfGustav.Entities.Actions; using TheLegendOfGustav.Entities.Actors; +using TheLegendOfGustav.Utils; +using TheLengendOfGustav.InputHandling; namespace TheLegendOfGustav.InputHandling; @@ -10,7 +12,8 @@ public enum InputHandlers GameOver, Inspect, Pickup, - Inventory + Inventory, + CastSpell } /// <summary> @@ -32,11 +35,18 @@ public partial class InputHandler : Node InputHandlerDict.Add(InputHandlers.MainGame, GetNode<MainGameInputHandler>("MainGameInputHandler")); // Controles para quando o jogador está morto. InputHandlerDict.Add(InputHandlers.GameOver, GetNode<GameOverInputHandler>("GameOverInputHandler")); + // Controles para observar o cenário InputHandlerDict.Add(InputHandlers.Inspect, GetNode<InspectInputHandler>("InspectInputHandler")); + // Controles para pegar um item do chão. InputHandlerDict.Add(InputHandlers.Pickup, GetNode<PickupInputHandler>("PickupInputHandler")); + // Controles para quando o inventário for aberto. InputHandlerDict.Add(InputHandlers.Inventory, GetNode<InventoryInputHandler>("InventoryInputHandler")); + // Controles para quando o jogador precisar escolher um alvo de feitiço. + InputHandlerDict.Add(InputHandlers.CastSpell, GetNode<CastSpellInputHandler>("CastSpellInputHandler")); SetInputHandler(StartingInputHandler); + + SignalBus.Instance.CommandInputHandler += SetInputHandler; } public Action GetAction(Player player) diff --git a/scripts/Magic/DamageEffect.cs b/scripts/Magic/DamageEffect.cs index abd2cca..0b2b3d6 100644 --- a/scripts/Magic/DamageEffect.cs +++ b/scripts/Magic/DamageEffect.cs @@ -1,5 +1,6 @@ using Godot; using TheLegendOfGustav.Entities.Actors; +using TheLegendOfGustav.Utils; namespace TheLegendOfGustav.Magic; @@ -13,6 +14,8 @@ public partial class DamageEffect : SpellEffect { int damageDealt = Damage - target.Men; + MessageLogData.Instance.AddMessage($"{caster.DisplayName} aplica {damageDealt} de dano mágico em {target.DisplayName}"); + target.Hp -= damageDealt; } }
\ No newline at end of file diff --git a/scripts/Time/TurnManager.cs b/scripts/Time/TurnManager.cs index 9713fea..3937aa0 100644 --- a/scripts/Time/TurnManager.cs +++ b/scripts/Time/TurnManager.cs @@ -3,6 +3,7 @@ using TheLegendOfGustav.Map; using TheLegendOfGustav.Entities.Actors; using TheLegendOfGustav.Entities; using TheLegendOfGustav.Entities.Actions; +using TheLegendOfGustav.Utils; namespace TheLegendOfGustav.Time; @@ -64,6 +65,13 @@ public partial class TurnManager(Map.Map map) : RefCounted 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, diff --git a/scripts/Utils/SignalBus.cs b/scripts/Utils/SignalBus.cs index a2aa6ca..bf97c2d 100644 --- a/scripts/Utils/SignalBus.cs +++ b/scripts/Utils/SignalBus.cs @@ -1,4 +1,7 @@ +using System; using Godot; +using TheLegendOfGustav.InputHandling; +using TheLegendOfGustav.Magic; namespace TheLegendOfGustav.Utils; @@ -20,6 +23,14 @@ public partial class SignalBus : Node [Signal] public delegate void ExitInspectionModeEventHandler(); + [Signal] + public delegate void PlayerSpellChooseLocationEventHandler(SpellResource spell); + [Signal] + public delegate void PlayerSpellCastEventHandler(bool success); + + [Signal] + public delegate void CommandInputHandlerEventHandler(InputHandlers state); + public override void _Ready() { base._Ready(); |
