summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assets/definitions/Items/small_healing_potion.tres2
-rw-r--r--assets/definitions/actor/EntityInspector.tres2
-rw-r--r--assets/definitions/actor/Player.tres2
-rw-r--r--assets/definitions/actor/Shadow.tres2
-rw-r--r--assets/definitions/actor/Skeleton.tres2
-rw-r--r--assets/definitions/actor/morcegao.tres2
-rw-r--r--assets/definitions/tiles/floor.tres2
-rw-r--r--assets/definitions/tiles/wall.tres2
-rw-r--r--scenes/Game.tscn30
-rw-r--r--scenes/Inspector.tscn2
-rw-r--r--scripts/Entities/Actions/Action.cs69
-rw-r--r--scripts/Entities/Actions/Action.cs.uid (renamed from scripts/entities/actions/Action.cs.uid)0
-rw-r--r--scripts/Entities/Actions/BumpAction.cs (renamed from scripts/entities/actions/BumpAction.cs)14
-rw-r--r--scripts/Entities/Actions/BumpAction.cs.uid (renamed from scripts/entities/actions/BumpAction.cs.uid)0
-rw-r--r--scripts/Entities/Actions/DirectionalAction.cs37
-rw-r--r--scripts/Entities/Actions/DirectionalAction.cs.uid (renamed from scripts/entities/actions/DirectionalAction.cs.uid)0
-rw-r--r--scripts/Entities/Actions/DropAction.cs17
-rw-r--r--scripts/Entities/Actions/DropAction.cs.uid (renamed from scripts/entities/actions/DropAction.cs.uid)0
-rw-r--r--scripts/Entities/Actions/ItemAction.cs39
-rw-r--r--scripts/Entities/Actions/ItemAction.cs.uid (renamed from scripts/entities/actions/ItemAction.cs.uid)0
-rw-r--r--scripts/Entities/Actions/MeleeAction.cs (renamed from scripts/entities/actions/MeleeAction.cs)21
-rw-r--r--scripts/Entities/Actions/MeleeAction.cs.uid (renamed from scripts/entities/actions/MeleeAction.cs.uid)0
-rw-r--r--scripts/Entities/Actions/MovementAction.cs (renamed from scripts/entities/actions/MovementAction.cs)10
-rw-r--r--scripts/Entities/Actions/MovementAction.cs.uid (renamed from scripts/entities/actions/MovementAction.cs.uid)0
-rw-r--r--scripts/Entities/Actions/PickUpAction.cs51
-rw-r--r--scripts/Entities/Actions/PickUpAction.cs.uid (renamed from scripts/entities/actions/PickUpAction.cs.uid)0
-rw-r--r--scripts/Entities/Actions/WaitAction.cs (renamed from scripts/entities/actions/WaitAction.cs)8
-rw-r--r--scripts/Entities/Actions/WaitAction.cs.uid (renamed from scripts/entities/actions/WaitAction.cs.uid)0
-rw-r--r--scripts/Entities/Actors/AI/BaseAI.cs54
-rw-r--r--scripts/Entities/Actors/AI/BaseAI.cs.uid (renamed from scripts/entities/actors/AI/BaseAI.cs.uid)0
-rw-r--r--scripts/Entities/Actors/AI/HostileEnemyAI.cs (renamed from scripts/entities/actors/AI/HostileEnemyAI.cs)46
-rw-r--r--scripts/Entities/Actors/AI/HostileEnemyAI.cs.uid (renamed from scripts/entities/actors/AI/HostileEnemyAI.cs.uid)0
-rw-r--r--scripts/Entities/Actors/Actor.cs (renamed from scripts/entities/actors/Actor.cs)192
-rw-r--r--scripts/Entities/Actors/Actor.cs.uid (renamed from scripts/entities/actors/Actor.cs.uid)0
-rw-r--r--scripts/Entities/Actors/ActorDefinition.cs (renamed from scripts/entities/actors/ActorDefinition.cs)4
-rw-r--r--scripts/Entities/Actors/ActorDefinition.cs.uid (renamed from scripts/entities/actors/ActorDefinition.cs.uid)0
-rw-r--r--scripts/Entities/Actors/Enemy.cs (renamed from scripts/entities/actors/Enemy.cs)41
-rw-r--r--scripts/Entities/Actors/Enemy.cs.uid (renamed from scripts/entities/actors/Enemy.cs.uid)0
-rw-r--r--scripts/Entities/Actors/EnemyDefinition.cs (renamed from scripts/entities/actors/EnemyDefinition.cs)6
-rw-r--r--scripts/Entities/Actors/EnemyDefinition.cs.uid (renamed from scripts/entities/actors/EnemyDefinition.cs.uid)0
-rw-r--r--scripts/Entities/Actors/Inventory.cs46
-rw-r--r--scripts/Entities/Actors/Inventory.cs.uid (renamed from scripts/entities/actors/Inventory.cs.uid)0
-rw-r--r--scripts/Entities/Actors/Player.cs (renamed from scripts/entities/actors/Player.cs)19
-rw-r--r--scripts/Entities/Actors/Player.cs.uid (renamed from scripts/entities/actors/Player.cs.uid)0
-rw-r--r--scripts/Entities/Actors/PlayerDefinition.cs (renamed from scripts/entities/actors/PlayerDefinition.cs)5
-rw-r--r--scripts/Entities/Actors/PlayerDefinition.cs.uid (renamed from scripts/entities/actors/PlayerDefinition.cs.uid)0
-rw-r--r--scripts/Entities/Entity.cs (renamed from scripts/entities/Entity.cs)99
-rw-r--r--scripts/Entities/Entity.cs.uid (renamed from scripts/entities/Entity.cs.uid)0
-rw-r--r--scripts/Entities/EntityDefinition.cs (renamed from scripts/entities/EntityDefinition.cs)5
-rw-r--r--scripts/Entities/EntityDefinition.cs.uid (renamed from scripts/entities/EntityDefinition.cs.uid)0
-rw-r--r--scripts/Entities/Items/ConsumableItem.cs (renamed from scripts/entities/items/ConsumableItem.cs)15
-rw-r--r--scripts/Entities/Items/ConsumableItem.cs.uid (renamed from scripts/entities/items/ConsumableItem.cs.uid)0
-rw-r--r--scripts/Entities/Items/ConsumableItemDefinition.cs (renamed from scripts/entities/items/ConsumableItemDefinition.cs)2
-rw-r--r--scripts/Entities/Items/ConsumableItemDefinition.cs.uid (renamed from scripts/entities/items/ConsumableItemDefinition.cs.uid)0
-rw-r--r--scripts/Entities/Items/HealingConsumable.cs30
-rw-r--r--scripts/Entities/Items/HealingConsumable.cs.uid (renamed from scripts/entities/items/HealingConsumable.cs.uid)0
-rw-r--r--scripts/Entities/Items/HealingConsumableDefinition.cs (renamed from scripts/entities/items/HealingConsumableDefinition.cs)9
-rw-r--r--scripts/Entities/Items/HealingConsumableDefinition.cs.uid (renamed from scripts/entities/items/HealingConsumableDefinition.cs.uid)0
-rw-r--r--scripts/GUI/Details.cs43
-rw-r--r--scripts/GUI/Hud.cs17
-rw-r--r--scripts/GUI/InventoryMenu.cs47
-rw-r--r--scripts/GUI/ItemMenuEntry.cs61
-rw-r--r--scripts/GUI/Message.cs45
-rw-r--r--scripts/GUI/MessageLog.cs12
-rw-r--r--scripts/Game.cs59
-rw-r--r--scripts/InputHandling/BaseInputHandler.cs (renamed from scripts/input/BaseInputHandler.cs)19
-rw-r--r--scripts/InputHandling/BaseInputHandler.cs.uid (renamed from scripts/input/BaseInputHandler.cs.uid)0
-rw-r--r--scripts/InputHandling/GameOverInputHandler.cs (renamed from scripts/input/GameOverInputHandler.cs)9
-rw-r--r--scripts/InputHandling/GameOverInputHandler.cs.uid (renamed from scripts/input/GameOverInputHandler.cs.uid)0
-rw-r--r--scripts/InputHandling/InputHandler.cs58
-rw-r--r--scripts/InputHandling/InputHandler.cs.uid (renamed from scripts/input/InputHandler.cs.uid)0
-rw-r--r--scripts/InputHandling/InspectInputHandler.cs (renamed from scripts/input/InspectInputHandler.cs)42
-rw-r--r--scripts/InputHandling/InspectInputHandler.cs.uid (renamed from scripts/input/InspectInputHandler.cs.uid)0
-rw-r--r--scripts/InputHandling/InventoryInputHandler.cs75
-rw-r--r--scripts/InputHandling/InventoryInputHandler.cs.uid (renamed from scripts/input/InventoryInputHandler.cs.uid)0
-rw-r--r--scripts/InputHandling/MainGameInputHandler.cs58
-rw-r--r--scripts/InputHandling/MainGameInputHandler.cs.uid (renamed from scripts/input/MainGameInputHandler.cs.uid)0
-rw-r--r--scripts/InputHandling/PickupInputHandler.cs (renamed from scripts/input/PickupInputHandler.cs)33
-rw-r--r--scripts/InputHandling/PickupInputHandler.cs.uid (renamed from scripts/input/PickupInputHandler.cs.uid)0
-rw-r--r--scripts/Map/DungeonGenerator.cs (renamed from scripts/map/DungeonGenerator.cs)311
-rw-r--r--scripts/Map/DungeonGenerator.cs.uid (renamed from scripts/map/DungeonGenerator.cs.uid)0
-rw-r--r--scripts/Map/FieldOfView.cs (renamed from scripts/map/FieldOfView.cs)51
-rw-r--r--scripts/Map/FieldOfView.cs.uid (renamed from scripts/map/FieldOfView.cs.uid)0
-rw-r--r--scripts/Map/Map.cs98
-rw-r--r--scripts/Map/Map.cs.uid (renamed from scripts/map/Map.cs.uid)0
-rw-r--r--scripts/Map/MapData.cs318
-rw-r--r--scripts/Map/MapData.cs.uid (renamed from scripts/map/MapData.cs.uid)0
-rw-r--r--scripts/Map/MapDivision.cs113
-rw-r--r--scripts/Map/MapDivision.cs.uid (renamed from scripts/map/MapDivision.cs.uid)0
-rw-r--r--scripts/Map/Tile.cs (renamed from scripts/map/Tile.cs)95
-rw-r--r--scripts/Map/Tile.cs.uid (renamed from scripts/map/Tile.cs.uid)0
-rw-r--r--scripts/Map/TileDefinition.cs (renamed from scripts/map/TileDefinition.cs)2
-rw-r--r--scripts/Map/TileDefinition.cs.uid (renamed from scripts/map/TileDefinition.cs.uid)0
-rw-r--r--scripts/Time/TurnManager.cs102
-rw-r--r--scripts/Utils/Grid.cs12
-rw-r--r--scripts/Utils/Inspector.cs (renamed from scripts/entities/actors/Inspector.cs)28
-rw-r--r--scripts/Utils/Inspector.cs.uid (renamed from scripts/entities/actors/Inspector.cs.uid)0
-rw-r--r--scripts/Utils/MessageLogData.cs59
-rw-r--r--scripts/Utils/SignalBus.cs19
-rw-r--r--scripts/entities/actions/Action.cs44
-rw-r--r--scripts/entities/actions/DirectionalAction.cs30
-rw-r--r--scripts/entities/actions/DropAction.cs13
-rw-r--r--scripts/entities/actions/ItemAction.cs17
-rw-r--r--scripts/entities/actions/PickUpAction.cs34
-rw-r--r--scripts/entities/actors/AI/BaseAI.cs41
-rw-r--r--scripts/entities/actors/Inventory.cs39
-rw-r--r--scripts/entities/items/HealingConsumable.cs28
-rw-r--r--scripts/input/InputHandler.cs52
-rw-r--r--scripts/input/InventoryInputHandler.cs65
-rw-r--r--scripts/input/MainGameInputHandler.cs47
-rw-r--r--scripts/map/Map.cs86
-rw-r--r--scripts/map/MapData.cs272
-rw-r--r--scripts/map/MapDivision.cs98
113 files changed, 2011 insertions, 1528 deletions
diff --git a/assets/definitions/Items/small_healing_potion.tres b/assets/definitions/Items/small_healing_potion.tres
index 56d1294..4a75d72 100644
--- a/assets/definitions/Items/small_healing_potion.tres
+++ b/assets/definitions/Items/small_healing_potion.tres
@@ -1,6 +1,6 @@
[gd_resource type="Resource" script_class="HealingConsumableDefinition" load_steps=3 format=3 uid="uid://bm6yx6rwh8bds"]
-[ext_resource type="Script" uid="uid://b3qy4gtjfci14" path="res://scripts/entities/items/HealingConsumableDefinition.cs" id="1_4dl2g"]
+[ext_resource type="Script" uid="uid://b3qy4gtjfci14" path="res://scripts/Entities/Items/HealingConsumableDefinition.cs" id="1_4dl2g"]
[ext_resource type="Texture2D" uid="uid://b7drpdbk4lggb" path="res://assets/sprites/items/small_health_potion.png" id="2_esrbk"]
[resource]
diff --git a/assets/definitions/actor/EntityInspector.tres b/assets/definitions/actor/EntityInspector.tres
index d085b79..8b9b79e 100644
--- a/assets/definitions/actor/EntityInspector.tres
+++ b/assets/definitions/actor/EntityInspector.tres
@@ -1,6 +1,6 @@
[gd_resource type="Resource" script_class="ActorDefinition" load_steps=2 format=3 uid="uid://bq3mbgtvjgrg3"]
-[ext_resource type="Script" uid="uid://crxw1e37xlrrt" path="res://scripts/entities/actors/ActorDefinition.cs" id="1_na4h7"]
+[ext_resource type="Script" uid="uid://crxw1e37xlrrt" path="res://scripts/Entities/Actors/ActorDefinition.cs" id="1_na4h7"]
[resource]
script = ExtResource("1_na4h7")
diff --git a/assets/definitions/actor/Player.tres b/assets/definitions/actor/Player.tres
index 56aa604..36c24c5 100644
--- a/assets/definitions/actor/Player.tres
+++ b/assets/definitions/actor/Player.tres
@@ -1,6 +1,6 @@
[gd_resource type="Resource" script_class="PlayerDefinition" load_steps=4 format=3 uid="uid://7pwmjcgbcyoo"]
-[ext_resource type="Script" uid="uid://bd78nfh1tsjq6" path="res://scripts/entities/actors/PlayerDefinition.cs" id="1_2k33r"]
+[ext_resource type="Script" uid="uid://bd78nfh1tsjq6" path="res://scripts/Entities/Actors/PlayerDefinition.cs" id="1_2k33r"]
[ext_resource type="Texture2D" uid="uid://w0808ug4al66" path="res://assets/sprites/actors/generic_grave.png" id="1_m72ac"]
[ext_resource type="Texture2D" uid="uid://dwky8qc2y602k" path="res://assets/sprites/actors/player.png" id="3_frwfa"]
diff --git a/assets/definitions/actor/Shadow.tres b/assets/definitions/actor/Shadow.tres
index cfa0995..8493618 100644
--- a/assets/definitions/actor/Shadow.tres
+++ b/assets/definitions/actor/Shadow.tres
@@ -1,7 +1,7 @@
[gd_resource type="Resource" script_class="EnemyDefinition" load_steps=4 format=3 uid="uid://dqd714474t4ic"]
[ext_resource type="Texture2D" uid="uid://w0808ug4al66" path="res://assets/sprites/actors/generic_grave.png" id="1_3bs08"]
-[ext_resource type="Script" uid="uid://dkfdm2m2scyks" path="res://scripts/entities/actors/EnemyDefinition.cs" id="1_4jpld"]
+[ext_resource type="Script" uid="uid://dkfdm2m2scyks" path="res://scripts/Entities/Actors/EnemyDefinition.cs" id="1_4jpld"]
[ext_resource type="Texture2D" uid="uid://br8cqtbd6xcej" path="res://assets/sprites/actors/shadow.png" id="3_kvxyn"]
[resource]
diff --git a/assets/definitions/actor/Skeleton.tres b/assets/definitions/actor/Skeleton.tres
index f67bf56..44a2893 100644
--- a/assets/definitions/actor/Skeleton.tres
+++ b/assets/definitions/actor/Skeleton.tres
@@ -1,7 +1,7 @@
[gd_resource type="Resource" script_class="EnemyDefinition" load_steps=4 format=3 uid="uid://gt6xqa737dyt"]
[ext_resource type="Texture2D" uid="uid://w0808ug4al66" path="res://assets/sprites/actors/generic_grave.png" id="1_7fof3"]
-[ext_resource type="Script" uid="uid://dkfdm2m2scyks" path="res://scripts/entities/actors/EnemyDefinition.cs" id="1_m5x88"]
+[ext_resource type="Script" uid="uid://dkfdm2m2scyks" path="res://scripts/Entities/Actors/EnemyDefinition.cs" id="1_m5x88"]
[ext_resource type="Texture2D" uid="uid://dh5sgjdwkps88" path="res://assets/sprites/actors/skeleton.png" id="2_hhidw"]
[resource]
diff --git a/assets/definitions/actor/morcegao.tres b/assets/definitions/actor/morcegao.tres
index 937679f..65c9a6e 100644
--- a/assets/definitions/actor/morcegao.tres
+++ b/assets/definitions/actor/morcegao.tres
@@ -1,7 +1,7 @@
[gd_resource type="Resource" script_class="EnemyDefinition" load_steps=4 format=3 uid="uid://cj0kq4sfft8gh"]
[ext_resource type="Texture2D" uid="uid://w0808ug4al66" path="res://assets/sprites/actors/generic_grave.png" id="1_hdleo"]
-[ext_resource type="Script" uid="uid://dkfdm2m2scyks" path="res://scripts/entities/actors/EnemyDefinition.cs" id="1_m2lyk"]
+[ext_resource type="Script" uid="uid://dkfdm2m2scyks" path="res://scripts/Entities/Actors/EnemyDefinition.cs" id="1_m2lyk"]
[ext_resource type="Texture2D" uid="uid://cddn56s7w2lc2" path="res://assets/sprites/actors/morcegao.png" id="3_601km"]
[resource]
diff --git a/assets/definitions/tiles/floor.tres b/assets/definitions/tiles/floor.tres
index 92a8da7..44b0097 100644
--- a/assets/definitions/tiles/floor.tres
+++ b/assets/definitions/tiles/floor.tres
@@ -1,6 +1,6 @@
[gd_resource type="Resource" script_class="TileDefinition" load_steps=3 format=3 uid="uid://dfrremrf4rn15"]
-[ext_resource type="Script" uid="uid://ba82a33ov6uuo" path="res://scripts/map/TileDefinition.cs" id="1_snxyj"]
+[ext_resource type="Script" uid="uid://ba82a33ov6uuo" path="res://scripts/Map/TileDefinition.cs" id="1_snxyj"]
[ext_resource type="Texture2D" uid="uid://ds13x1dpu0v4s" path="res://assets/sprites/tiles/floor.png" id="1_vvyfi"]
[resource]
diff --git a/assets/definitions/tiles/wall.tres b/assets/definitions/tiles/wall.tres
index a8ba785..a4fa75d 100644
--- a/assets/definitions/tiles/wall.tres
+++ b/assets/definitions/tiles/wall.tres
@@ -1,7 +1,7 @@
[gd_resource type="Resource" script_class="TileDefinition" load_steps=3 format=3 uid="uid://cdluqrbfcu5ng"]
[ext_resource type="Texture2D" uid="uid://s5klxirh5r1w" path="res://assets/sprites/tiles/wall.png" id="1_jkwov"]
-[ext_resource type="Script" uid="uid://ba82a33ov6uuo" path="res://scripts/map/TileDefinition.cs" id="1_ugwtv"]
+[ext_resource type="Script" uid="uid://ba82a33ov6uuo" path="res://scripts/Map/TileDefinition.cs" id="1_ugwtv"]
[resource]
script = ExtResource("1_ugwtv")
diff --git a/scenes/Game.tscn b/scenes/Game.tscn
index 4cb5231..09e9825 100644
--- a/scenes/Game.tscn
+++ b/scenes/Game.tscn
@@ -1,17 +1,17 @@
[gd_scene load_steps=13 format=3 uid="uid://u5h6iqyi8wd0"]
[ext_resource type="Script" uid="uid://dwubb28wt4bhe" path="res://scripts/Game.cs" id="1_cpr0p"]
-[ext_resource type="Script" uid="uid://ejqmdbc0524i" path="res://scripts/input/MainGameInputHandler.cs" id="3_400sg"]
-[ext_resource type="Script" uid="uid://fe2h4is11tnw" path="res://scripts/map/Map.cs" id="3_cpr0p"]
-[ext_resource type="Script" uid="uid://bxt6g7t1uvvia" path="res://scripts/input/InputHandler.cs" id="3_s0nni"]
-[ext_resource type="Script" uid="uid://dwyr067lwqcsj" path="res://scripts/map/DungeonGenerator.cs" id="4_78awf"]
-[ext_resource type="Script" uid="uid://ogqlb3purl6n" path="res://scripts/input/GameOverInputHandler.cs" id="4_g4kob"]
-[ext_resource type="Script" uid="uid://bamlnrj5bm1rd" path="res://scripts/input/InspectInputHandler.cs" id="5_g4kob"]
+[ext_resource type="Script" uid="uid://ejqmdbc0524i" path="res://scripts/InputHandling/MainGameInputHandler.cs" id="3_400sg"]
+[ext_resource type="Script" uid="uid://fe2h4is11tnw" path="res://scripts/Map/Map.cs" id="3_cpr0p"]
+[ext_resource type="Script" uid="uid://bxt6g7t1uvvia" path="res://scripts/InputHandling/InputHandler.cs" id="3_s0nni"]
+[ext_resource type="Script" uid="uid://dwyr067lwqcsj" path="res://scripts/Map/DungeonGenerator.cs" id="4_78awf"]
+[ext_resource type="Script" uid="uid://ogqlb3purl6n" path="res://scripts/InputHandling/GameOverInputHandler.cs" id="4_g4kob"]
+[ext_resource type="Script" uid="uid://bamlnrj5bm1rd" path="res://scripts/InputHandling/InspectInputHandler.cs" id="5_g4kob"]
[ext_resource type="PackedScene" uid="uid://bryqrafavmwj4" path="res://scenes/Details.tscn" id="5_qy1jj"]
-[ext_resource type="Script" uid="uid://bereyrj1s46y5" path="res://scripts/map/FieldOfView.cs" id="5_s0nni"]
+[ext_resource type="Script" uid="uid://bereyrj1s46y5" path="res://scripts/Map/FieldOfView.cs" id="5_s0nni"]
[ext_resource type="PackedScene" uid="uid://b4h4xyr1g8o50" path="res://scenes/GUI/hud.tscn" id="6_aug50"]
-[ext_resource type="Script" uid="uid://dspqgdxg5jji0" path="res://scripts/input/PickupInputHandler.cs" id="10_3xj3m"]
-[ext_resource type="Script" uid="uid://bjcjktvyrdh10" path="res://scripts/input/InventoryInputHandler.cs" id="11_mcffj"]
+[ext_resource type="Script" uid="uid://dspqgdxg5jji0" path="res://scripts/InputHandling/PickupInputHandler.cs" id="10_3xj3m"]
+[ext_resource type="Script" uid="uid://bjcjktvyrdh10" path="res://scripts/InputHandling/InventoryInputHandler.cs" id="11_mcffj"]
[node name="Game" type="Node"]
script = ExtResource("1_cpr0p")
@@ -21,9 +21,7 @@ script = ExtResource("3_cpr0p")
[node name="Generator" type="Node" parent="Map"]
script = ExtResource("4_78awf")
-seed = 989
-useSeed = false
-iterations = 8
+UseSeed = false
[node name="FieldOfView" type="Node" parent="Map"]
script = ExtResource("5_s0nni")
@@ -44,16 +42,16 @@ script = ExtResource("3_400sg")
[node name="GameOverInputHandler" type="Node" parent="InputHandler"]
script = ExtResource("4_g4kob")
-[node name="InspectInputHandler" type="Node" parent="InputHandler" node_paths=PackedStringArray("map")]
+[node name="InspectInputHandler" type="Node" parent="InputHandler" node_paths=PackedStringArray("Map")]
script = ExtResource("5_g4kob")
-map = NodePath("../../Map")
+Map = NodePath("../../Map")
[node name="PickupInputHandler" type="Node" parent="InputHandler"]
script = ExtResource("10_3xj3m")
-[node name="InventoryInputHandler" type="Node" parent="InputHandler" node_paths=PackedStringArray("map")]
+[node name="InventoryInputHandler" type="Node" parent="InputHandler" node_paths=PackedStringArray("Map")]
script = ExtResource("11_mcffj")
-map = NodePath("../../Map")
+Map = NodePath("../../Map")
[node name="Camera2D" type="Camera2D" parent="."]
offset = Vector2(8, 8)
diff --git a/scenes/Inspector.tscn b/scenes/Inspector.tscn
index ad2a062..8b3dcdb 100644
--- a/scenes/Inspector.tscn
+++ b/scenes/Inspector.tscn
@@ -1,7 +1,7 @@
[gd_scene load_steps=3 format=3 uid="uid://dyythuxaio6j4"]
[ext_resource type="Texture2D" uid="uid://cjjxf4rbj8gku" path="res://assets/sprites/inspector.png" id="1_psyxb"]
-[ext_resource type="Script" uid="uid://dxsrtu4b3pi08" path="res://scripts/entities/actors/Inspector.cs" id="2_g62u7"]
+[ext_resource type="Script" uid="uid://dxsrtu4b3pi08" path="res://scripts/Utils/Inspector.cs" id="2_g62u7"]
[node name="Sprite2D" type="Sprite2D"]
texture = ExtResource("1_psyxb")
diff --git a/scripts/Entities/Actions/Action.cs b/scripts/Entities/Actions/Action.cs
new file mode 100644
index 0000000..b2d6a4b
--- /dev/null
+++ b/scripts/Entities/Actions/Action.cs
@@ -0,0 +1,69 @@
+using Godot;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Map;
+
+namespace TheLegendOfGustav.Entities.Actions;
+
+/// <summary>
+/// <c>Action</c> representa uma ação no jogo efetuada por um ator.
+/// Ações são geradas pelo jogador e pela IA, elas regem os atores do jogo.
+/// </summary>
+public abstract partial class Action : RefCounted
+{
+ private Actor actor;
+
+ private int cost;
+
+ public Action(Actor actor)
+ {
+ Actor = actor;
+ // Custo base, subclasses podem sobreescrever isto se quiserem.
+ Cost = 10;
+ }
+
+ /// <summary>
+ /// O ator que realiza a ação.
+ /// </summary>
+ public Actor Actor
+ {
+ get => actor;
+ private set
+ {
+ actor = value;
+ }
+ }
+
+ /// <summary>
+ /// O custo da ação.
+ /// </summary>
+ protected int Cost
+ {
+ get => cost;
+ set
+ {
+ cost = value;
+ }
+ }
+
+ /// <summary>
+ /// É conveniente ter acesso ao mapa dentro de uma ação.
+ /// </summary>
+ protected MapData MapData
+ {
+ get => actor.MapData;
+ }
+
+ /// <summary>
+ /// Método que executa a ação. Subclasses da ação devem implementar este método.
+ /// <example>
+ /// Exemplo:
+ /// <code>
+ /// Action action = new Action(actor);
+ /// /* . . . */
+ /// action.Perform();
+ /// </code>
+ /// </example>
+ /// </summary>
+ /// <returns>Se a ação foi executada ou não.</returns>
+ public abstract bool Perform();
+}
diff --git a/scripts/entities/actions/Action.cs.uid b/scripts/Entities/Actions/Action.cs.uid
index 9523b0a..9523b0a 100644
--- a/scripts/entities/actions/Action.cs.uid
+++ b/scripts/Entities/Actions/Action.cs.uid
diff --git a/scripts/entities/actions/BumpAction.cs b/scripts/Entities/Actions/BumpAction.cs
index fa2605f..f0a0047 100644
--- a/scripts/entities/actions/BumpAction.cs
+++ b/scripts/Entities/Actions/BumpAction.cs
@@ -1,4 +1,7 @@
using Godot;
+using TheLegendOfGustav.Entities.Actors;
+
+namespace TheLegendOfGustav.Entities.Actions;
/// <summary>
/// Ação de "Esbarramento", utilizada principalmente pelo jogador.
@@ -19,11 +22,14 @@ public partial class BumpAction : DirectionalAction
Action action;
// Se houver um ator no destino, crie uma ação de ataque.
- if (GetTarget() != null) {
- action = new MeleeAction(actor, Offset);
- } else {
+ if (GetTarget() != null)
+ {
+ action = new MeleeAction(Actor, Offset);
+ }
+ else
+ {
// Mas se não houver, crie uma ação de movimento.
- action = new MovementAction(actor, Offset);
+ action = new MovementAction(Actor, Offset);
}
// Executa a ação.
diff --git a/scripts/entities/actions/BumpAction.cs.uid b/scripts/Entities/Actions/BumpAction.cs.uid
index f5ce3f8..f5ce3f8 100644
--- a/scripts/entities/actions/BumpAction.cs.uid
+++ b/scripts/Entities/Actions/BumpAction.cs.uid
diff --git a/scripts/Entities/Actions/DirectionalAction.cs b/scripts/Entities/Actions/DirectionalAction.cs
new file mode 100644
index 0000000..e32e9f2
--- /dev/null
+++ b/scripts/Entities/Actions/DirectionalAction.cs
@@ -0,0 +1,37 @@
+using Godot;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Map;
+
+namespace TheLegendOfGustav.Entities.Actions;
+
+/// <summary>
+/// Ação direcionada. Esta ação é acompanhada com um vetor que representa uma
+/// distância tendo como ponto de partida o ator.
+/// </summary>
+public abstract partial class DirectionalAction : Action
+{
+ public DirectionalAction(Actor actor, Vector2I offset) : base(actor)
+ {
+ Offset = offset;
+ }
+
+ /// <summary>
+ /// Direção/distância do ator da ação.
+ /// Seu significado depende da ação que implementará esta classe.
+ /// </summary>
+ public Vector2I Offset { get; private set; }
+
+ /// <summary>
+ /// Coordenada do alvo da ação.
+ /// </summary>
+ public Vector2I Destination { get => Actor.GridPosition + Offset; }
+
+ /// <summary>
+ /// Função que obtém o alvo da ação, se houver.
+ /// </summary>
+ /// <returns>O ator alvo da ação, nulo se não houver.</returns>
+ protected Entity GetTarget()
+ {
+ return MapData.GetBlockingEntityAtPosition(Destination);
+ }
+}
diff --git a/scripts/entities/actions/DirectionalAction.cs.uid b/scripts/Entities/Actions/DirectionalAction.cs.uid
index 901756a..901756a 100644
--- a/scripts/entities/actions/DirectionalAction.cs.uid
+++ b/scripts/Entities/Actions/DirectionalAction.cs.uid
diff --git a/scripts/Entities/Actions/DropAction.cs b/scripts/Entities/Actions/DropAction.cs
new file mode 100644
index 0000000..00ddd7e
--- /dev/null
+++ b/scripts/Entities/Actions/DropAction.cs
@@ -0,0 +1,17 @@
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Entities.Items;
+
+namespace TheLegendOfGustav.Entities.Actions;
+
+public partial class DropAction : ItemAction
+{
+ public DropAction(Player player, ConsumableItem item) : base(player, item)
+ {
+ }
+
+ public override bool Perform()
+ {
+ Player.Inventory.Drop(Item);
+ return true;
+ }
+} \ No newline at end of file
diff --git a/scripts/entities/actions/DropAction.cs.uid b/scripts/Entities/Actions/DropAction.cs.uid
index 98ed82f..98ed82f 100644
--- a/scripts/entities/actions/DropAction.cs.uid
+++ b/scripts/Entities/Actions/DropAction.cs.uid
diff --git a/scripts/Entities/Actions/ItemAction.cs b/scripts/Entities/Actions/ItemAction.cs
new file mode 100644
index 0000000..14c5d93
--- /dev/null
+++ b/scripts/Entities/Actions/ItemAction.cs
@@ -0,0 +1,39 @@
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Entities.Items;
+
+namespace TheLegendOfGustav.Entities.Actions;
+
+public partial class ItemAction : Action
+{
+ private ConsumableItem item;
+ private Player player;
+
+ public ItemAction(Player player, ConsumableItem item) : base(player)
+ {
+ Item = item;
+ Player = player;
+ }
+
+ public Player Player
+ {
+ get => player;
+ private set
+ {
+ player = value;
+ }
+ }
+
+ protected ConsumableItem Item
+ {
+ get => item;
+ set
+ {
+ item = value;
+ }
+ }
+
+ public override bool Perform()
+ {
+ return item.Activate(this);
+ }
+} \ No newline at end of file
diff --git a/scripts/entities/actions/ItemAction.cs.uid b/scripts/Entities/Actions/ItemAction.cs.uid
index c8c8e23..c8c8e23 100644
--- a/scripts/entities/actions/ItemAction.cs.uid
+++ b/scripts/Entities/Actions/ItemAction.cs.uid
diff --git a/scripts/entities/actions/MeleeAction.cs b/scripts/Entities/Actions/MeleeAction.cs
index 09c5cc7..fcd8368 100644
--- a/scripts/entities/actions/MeleeAction.cs
+++ b/scripts/Entities/Actions/MeleeAction.cs
@@ -1,4 +1,8 @@
using Godot;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Utils;
+
+namespace TheLegendOfGustav.Entities.Actions;
/// <summary>
/// Ação de ataque físico. Uma ação direcionada que ataca um alvo.
@@ -18,31 +22,34 @@ public partial class MeleeAction : DirectionalAction
Entity potentialTarget = GetTarget();
// Só podemos atacar atores.
- if (potentialTarget is not Actor) {
+ if (potentialTarget is not TheLegendOfGustav.Entities.Actors.Actor)
+ {
return false;
}
Actor target = (Actor)potentialTarget;
-
// Se não houver um ator na direção, não podemos continuar.
// Isto é uma ação gratuita.
if (target == null) return false;
// não podemos ter dano negativo.
- int damage = actor.Atk - target.Def;
+ int damage = Actor.Atk - target.Def;
- string attackDesc = $"{actor.DisplayName} ataca {target.DisplayName}";
+ string attackDesc = $"{Actor.DisplayName} ataca {target.DisplayName}";
- if (damage > 0) {
+ if (damage > 0)
+ {
attackDesc += $" e remove {damage} de HP.";
target.Hp -= damage;
- } else {
+ }
+ else
+ {
attackDesc += $" mas {target.DisplayName} tem músculos de aço.";
}
MessageLogData.Instance.AddMessage(attackDesc);
- actor.Energy -= cost;
+ Actor.Energy -= Cost;
return true;
}
}
diff --git a/scripts/entities/actions/MeleeAction.cs.uid b/scripts/Entities/Actions/MeleeAction.cs.uid
index bc97619..bc97619 100644
--- a/scripts/entities/actions/MeleeAction.cs.uid
+++ b/scripts/Entities/Actions/MeleeAction.cs.uid
diff --git a/scripts/entities/actions/MovementAction.cs b/scripts/Entities/Actions/MovementAction.cs
index 403ec0a..0ac842c 100644
--- a/scripts/entities/actions/MovementAction.cs
+++ b/scripts/Entities/Actions/MovementAction.cs
@@ -1,4 +1,8 @@
using Godot;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Map;
+
+namespace TheLegendOfGustav.Entities.Actions;
/// <summary>
/// Ação de movimento. Movimenta o ator para a direção de seu Offset.
@@ -12,14 +16,14 @@ public partial class MovementAction : DirectionalAction
public override bool Perform()
{
// Não anda se o destino for um tile sólido.
- if (!Map_Data.GetTile(Destination).IsWalkable) return true;
+ if (!MapData.GetTile(Destination).IsWalkable) return true;
// Não anda se o destino for oculpado por um ator.
// Na maioria dos casos, essa condição nunca é verdadeira.
if (GetTarget() != null) return true;
- actor.Walk(Offset);
- actor.Energy -= cost;
+ Actor.Walk(Offset);
+ Actor.Energy -= Cost;
return true;
}
diff --git a/scripts/entities/actions/MovementAction.cs.uid b/scripts/Entities/Actions/MovementAction.cs.uid
index 07569ef..07569ef 100644
--- a/scripts/entities/actions/MovementAction.cs.uid
+++ b/scripts/Entities/Actions/MovementAction.cs.uid
diff --git a/scripts/Entities/Actions/PickUpAction.cs b/scripts/Entities/Actions/PickUpAction.cs
new file mode 100644
index 0000000..0dbd672
--- /dev/null
+++ b/scripts/Entities/Actions/PickUpAction.cs
@@ -0,0 +1,51 @@
+using Godot;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Utils;
+using TheLegendOfGustav.Entities.Items;
+using TheLegendOfGustav.Map;
+
+namespace TheLegendOfGustav.Entities.Actions;
+
+public partial class PickupAction : DirectionalAction
+{
+ private Player player;
+
+ public PickupAction(Player player, Vector2I offset) : base(player, offset)
+ {
+ Player = player;
+ // Pegar itens requer um tempo menor.
+ Cost = 2;
+ }
+
+ protected Player Player
+ {
+ get => player;
+ private set
+ {
+ player = value;
+ }
+ }
+
+ public override bool Perform()
+ {
+ ConsumableItem item = MapData.GetFirstItemAtPosition(Destination);
+
+ if (item == null)
+ {
+ MessageLogData.Instance.AddMessage("Não tem item aqui.");
+ return false;
+ }
+
+ if (player.Inventory.Items.Count >= player.Inventory.Capacity)
+ {
+ MessageLogData.Instance.AddMessage("Seu inventário está cheio");
+ return false;
+ }
+
+ MapData.RemoveEntity(item);
+ player.Inventory.Add(item);
+
+ player.Energy -= Cost;
+ return true;
+ }
+} \ No newline at end of file
diff --git a/scripts/entities/actions/PickUpAction.cs.uid b/scripts/Entities/Actions/PickUpAction.cs.uid
index 7ca9c72..7ca9c72 100644
--- a/scripts/entities/actions/PickUpAction.cs.uid
+++ b/scripts/Entities/Actions/PickUpAction.cs.uid
diff --git a/scripts/entities/actions/WaitAction.cs b/scripts/Entities/Actions/WaitAction.cs
index c26d884..011703b 100644
--- a/scripts/entities/actions/WaitAction.cs
+++ b/scripts/Entities/Actions/WaitAction.cs
@@ -1,4 +1,6 @@
-using Godot;
+using TheLegendOfGustav.Entities.Actors;
+
+namespace TheLegendOfGustav.Entities.Actions;
/// <summary>
/// Ação da inação. Ação que realiza nada.
@@ -96,9 +98,7 @@ public partial class WaitAction : Action
//@@@@@@@@@@@@*====+==++=*@. @ @@* .@* @ .*#. @ @* #: -@ @@% @ -+ =*%@@= @@: @ @ +. #+ *@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@@======++====+*%@@@@%%%@@@@%*+#@@@@@%#%@@#=+**=. =**+=***==*#*: :%@@@@@@@@@**=+#*==*###*=. -=**+::@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@+==+==++=====++=====++=+++++++++===+++++#% +@@@* :#%= =@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
- actor.Energy -= cost;
+ Actor.Energy -= Cost;
return true;
}
-
-
} \ No newline at end of file
diff --git a/scripts/entities/actions/WaitAction.cs.uid b/scripts/Entities/Actions/WaitAction.cs.uid
index 120c8c1..120c8c1 100644
--- a/scripts/entities/actions/WaitAction.cs.uid
+++ b/scripts/Entities/Actions/WaitAction.cs.uid
diff --git a/scripts/Entities/Actors/AI/BaseAI.cs b/scripts/Entities/Actors/AI/BaseAI.cs
new file mode 100644
index 0000000..bdd1e61
--- /dev/null
+++ b/scripts/Entities/Actors/AI/BaseAI.cs
@@ -0,0 +1,54 @@
+using Godot;
+
+namespace TheLegendOfGustav.Entities.Actors.AI;
+
+/// <summary>
+/// Enum das diferentes IAs disponíveis.
+/// </summary>
+public enum AIType
+{
+ None,
+ DefaultHostile
+};
+
+/// <summary>
+/// base para as IAs do jogo.
+/// </summary>
+public abstract partial class BaseAI : Node
+{
+ /// <summary>
+ /// Corpo controlado pela IA.
+ /// O corpo é a marionete da alma.
+ /// </summary>
+ protected Actor Body { get; set; }
+
+ public override void _Ready()
+ {
+ base._Ready();
+ // Por padrão, a IA é filha do nó de seu corpo.
+ Body = GetParent<Actor>();
+ }
+
+ /// <summary>
+ /// Computa um único turno para o ator controlado.
+ /// Aviso: NPCs não possuem ações gratuitas.
+ /// A IA SEMPRE precisa executar uma ação que custe energia.
+ /// </summary>
+ public abstract void Perform();
+
+ /// <summary>
+ /// Utiliza o pathfinder do mapa para obter um caminho
+ /// da posição atual do ator para um destino qualquer.
+ /// </summary>
+ /// <param name="destination">Destino</param>
+ /// <returns>Vetor com vetores, passo a passo para chegar no destino.</returns>
+ public Godot.Collections.Array<Vector2> GetPathTo(Vector2I destination)
+ {
+ // Arrays do Godot são muito mais confortáveis de manipular, então
+ // eu converto o Array do C# em um array do Godot antes de retornar o caminho.
+ Godot.Collections.Array<Vector2> list = [];
+ Vector2[] path = Body.MapData.Pathfinder.GetPointPath(Body.GridPosition, destination);
+ list.AddRange(path);
+ return list;
+ }
+} \ No newline at end of file
diff --git a/scripts/entities/actors/AI/BaseAI.cs.uid b/scripts/Entities/Actors/AI/BaseAI.cs.uid
index b23724c..b23724c 100644
--- a/scripts/entities/actors/AI/BaseAI.cs.uid
+++ b/scripts/Entities/Actors/AI/BaseAI.cs.uid
diff --git a/scripts/entities/actors/AI/HostileEnemyAI.cs b/scripts/Entities/Actors/AI/HostileEnemyAI.cs
index e7efd26..dbcf98d 100644
--- a/scripts/entities/actors/AI/HostileEnemyAI.cs
+++ b/scripts/Entities/Actors/AI/HostileEnemyAI.cs
@@ -1,4 +1,7 @@
using Godot;
+using TheLegendOfGustav.Entities.Actions;
+
+namespace TheLegendOfGustav.Entities.Actors.AI;
/// <summary>
/// Uma IA simples. Sempre tentará atacar o jogador com ataques corpo a corpo.
@@ -6,16 +9,16 @@ using Godot;
public partial class HostileEnemyAI : BaseAI
{
/// <summary>
- /// Caminho até a última posição conhecida do jogador.
- /// </summary>
- private Godot.Collections.Array<Vector2> path = [];
+ /// Caminho até a última posição conhecida do jogador.
+ /// </summary>
+ private Godot.Collections.Array<Vector2> Path { get; set; } = [];
public override void Perform()
{
// O alvo da IA sempre é o jogador.
- Player target = body.Map_Data.Player;
+ Player target = Body.MapData.Player;
// Vetor que parte do inimigo até o jogador.
- Vector2I offset = target.GridPosition - body.GridPosition;
+ Vector2I offset = target.GridPosition - Body.GridPosition;
// Distância entre o inimigo e o jogador. Leva em consideração somente
// um dos eixos.
int distance = int.Max(int.Abs(offset.X), int.Abs(offset.Y));
@@ -24,18 +27,21 @@ public partial class HostileEnemyAI : BaseAI
Action action;
// Só faz sentido atacar o jogador se o inimigo estiver visível.
- if (body.Map_Data.GetTile(body.GridPosition).IsInView) {
+ if (Body.MapData.GetTile(Body.GridPosition).IsInView)
+ {
// Se o inimigo consegue ver que o jogador está morto,
// IT'S OVER.
- if (!target.IsAlive) {
- action = new WaitAction(body);
+ if (!target.IsAlive)
+ {
+ action = new WaitAction(Body);
action.Perform();
return;
}
// Se estiver do lado do jogador, ataque.
- if (distance <= 1) {
- action = new MeleeAction(body, offset);
+ if (distance <= 1)
+ {
+ action = new MeleeAction(Body, offset);
action.Perform();
// Executada a ação, acabamos nosso turno aqui.
return;
@@ -44,32 +50,34 @@ public partial class HostileEnemyAI : BaseAI
// Se o inimigo estiver visível para o jogador,
// consideramos que ele também consiga ver o jogador.
// Logo, atualizamos o caminho para a posição atual do jogador.
- path = GetPathTo(target.GridPosition);
+ Path = GetPathTo(target.GridPosition);
// O primeiro passo é a posição atual do inimigo, podemos remover.
- path.RemoveAt(0);
+ Path.RemoveAt(0);
}
// Se existir um caminho conhecido para o jogador.
- if (path.Count > 0) {
+ if (Path.Count > 0)
+ {
// Pegamos o próximo passo para o destino.
- Vector2I destination = (Vector2I) path[0];
+ Vector2I destination = (Vector2I)Path[0];
// Se tiver o caminho estiver bloqueado, paramos o nosso turno aqui.
- if (body.Map_Data.GetBlockingEntityAtPosition(destination) != null) {
- action = new WaitAction(body);
+ if (Body.MapData.GetBlockingEntityAtPosition(destination) != null)
+ {
+ action = new WaitAction(Body);
action.Perform();
return;
}
// Caso o contrário, criamos uma nova ação de movimentação e a executamos.
- action = new MovementAction(body, destination - body.GridPosition);
+ action = new MovementAction(Body, destination - Body.GridPosition);
action.Perform();
// Podemos remover o passo do caminho.
- path.RemoveAt(0);
+ Path.RemoveAt(0);
return;
}
// Senão, espere.
- action = new WaitAction(body);
+ action = new WaitAction(Body);
action.Perform();
return;
}
diff --git a/scripts/entities/actors/AI/HostileEnemyAI.cs.uid b/scripts/Entities/Actors/AI/HostileEnemyAI.cs.uid
index 0fa2c32..0fa2c32 100644
--- a/scripts/entities/actors/AI/HostileEnemyAI.cs.uid
+++ b/scripts/Entities/Actors/AI/HostileEnemyAI.cs.uid
diff --git a/scripts/entities/actors/Actor.cs b/scripts/Entities/Actors/Actor.cs
index 1cb1f37..7e6685f 100644
--- a/scripts/entities/actors/Actor.cs
+++ b/scripts/Entities/Actors/Actor.cs
@@ -1,4 +1,8 @@
using Godot;
+using TheLegendOfGustav.Map;
+using TheLegendOfGustav.Utils;
+
+namespace TheLegendOfGustav.Entities.Actors;
/// <summary>
/// A classe de ator define um personagem no jogo.
@@ -6,137 +10,163 @@ using Godot;
[GlobalClass]
public partial class Actor : Entity
{
+ #region Fields
+ private int mp;
+ private int hp;
+
+ private int energy;
+ #endregion
+
+ #region Constructors
+ public Actor(Vector2I initialPosition, MapData map, ActorDefinition definition) : base(initialPosition, map, definition)
+ {
+ SetDefinition(definition);
+ }
+ #endregion
+
+ #region Signals
/// <summary>
- /// Sinal emitido toda vez que o HP mudar.
- /// </summary>
- /// <param name="hp">Novo HP</param>
- /// <param name="maxHp">Quantidade máxima de HP.</param>
+ /// Sinal emitido toda vez que o HP mudar.
+ /// </summary>
+ /// <param name="hp">Novo HP</param>
+ /// <param name="maxHp">Quantidade máxima de HP.</param>
[Signal]
public delegate void HealthChangedEventHandler(int hp, int maxHp);
/// <summary>
- /// Sinal emitido se o ator morrer.
- /// </summary>
+ /// Sinal emitido se o ator morrer.
+ /// </summary>
[Signal]
public delegate void DiedEventHandler();
+ #endregion
-
+ #region Properties
/// <summary>
- /// A definição do ator possui caracterísitcas padrões que definem
- /// o ator em questão.
+ /// Se o ator está vivo.
/// </summary>
- private ActorDefinition definition;
-
- /// <summary>
- /// Se o ator está vivo.
- /// </summary>
public bool IsAlive { get => Hp > 0; }
- private int energy;
/// <summary>
/// Utilizado no sistema de turnos.
/// Enquanto o ator tiver energia, ele poderá realizar turnos.
/// </summary>
- public int Energy
- {
+ public int Energy
+ {
get => energy;
set
{
- if (value > Speed) {
+ if (value > Speed)
+ {
energy = Speed;
- } else {
+ }
+ else
+ {
energy = value;
}
}
}
/// <summary>
- /// Taxa de recarga de energia.
- /// </summary>
- public int Speed { get => definition.Speed; }
-
- /// <summary>
- /// Executado uma vez por turno,
- /// </summary>
- public void RechargeEnergy() {
- Energy += Speed;
- }
+ /// Taxa de recarga de energia.
+ /// </summary>
+ public int Speed { get => Definition.Speed; }
- private int hp;
/// <summary>
- /// HP máximo do ator.
- /// </summary>
+ /// HP máximo do ator.
+ /// </summary>
public int MaxHp { get; private set; }
/// <summary>
- /// HP atual do ator.
- /// </summary>
- public int Hp {
+ /// HP atual do ator.
+ /// </summary>
+ public int Hp
+ {
get => hp;
- set {
+ set
+ {
// Esta propriedade impede que o HP seja maior que o máximo.
hp = int.Clamp(value, 0, MaxHp);
EmitSignal(SignalName.HealthChanged, Hp, MaxHp);
- if (hp <= 0) {
+ if (hp <= 0)
+ {
Die();
}
}
}
- private int mp;
/// <summary>
- /// Máximo de mana do ator.
- /// </summary>
+ /// Máximo de mana do ator.
+ /// </summary>
public int MaxMp { get; private set; }
/// <summary>
- /// Mana atual do ator.
- /// </summary>
- public int Mp {
+ /// Mana atual do ator.
+ /// </summary>
+ public int Mp
+ {
get => mp;
- set {
+ set
+ {
mp = int.Clamp(value, 0, MaxMp);
}
}
/// <summary>
- /// Estatística de ataque
- /// </summary>
+ /// Estatística de ataque
+ /// </summary>
public int Atk { get; private set; }
/// <summary>
- /// Estatística de defesa.
- /// </summary>
+ /// Estatística de defesa.
+ /// </summary>
public int Def { get; private set; }
/// <summary>
- /// Estatística mental.
- /// </summary>
+ /// Estatística mental.
+ /// </summary>
public int Men { get; private set; }
/// <summary>
- /// Move o ator para uma localização. Veja MovementAction.
- /// </summary>
- /// <param name="offset">Vetor que parte da posição do ator até o seu destino.</param>
- public void Walk(Vector2I offset) {
+ /// A definição do ator possui caracterísitcas padrões que definem
+ /// o ator em questão.
+ /// </summary>
+ private ActorDefinition Definition
+ {
+ get;
+ set;
+ }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Executado uma vez por turno,
+ /// </summary>
+ public void RechargeEnergy()
+ {
+ Energy += Speed;
+ }
+
+ /// <summary>
+ /// Move o ator para uma localização. Veja MovementAction.
+ /// </summary>
+ /// <param name="offset">Vetor que parte da posição do ator até o seu destino.</param>
+ public void Walk(Vector2I offset)
+ {
// Cada ator tem um peso no sistema de pathfinding.
// Sempre que ele se mover, removemos seu peso da posição antiga
- Map_Data.UnregisterBlockingEntity(this);
+ MapData.UnregisterBlockingEntity(this);
GridPosition += offset;
// E colocamos na próxima.
- Map_Data.RegisterBlockingEntity(this);
+ MapData.RegisterBlockingEntity(this);
// Este peso influencia o algoritmo de pathfinding.
// Atores evitam caminhos bloqueados. por outros atores.
}
- public Actor(Vector2I initialPosition, MapData map, ActorDefinition definition) : base(initialPosition, map, definition) {
-
- SetDefinition(definition);
- }
/// <summary>
- /// Recupera uma quantidade de HP do ator.
- /// </summary>
- /// <param name="amount">HP para recuperar</param>
- /// <returns>Quanto HP foi realmente recuperado.</returns>
- public int Heal(int amount) {
+ /// Recupera uma quantidade de HP do ator.
+ /// </summary>
+ /// <param name="amount">HP para recuperar</param>
+ /// <returns>Quanto HP foi realmente recuperado.</returns>
+ public int Heal(int amount)
+ {
int neoHp = Hp + amount;
if (neoHp > MaxHp) neoHp = MaxHp;
@@ -147,14 +177,15 @@ public partial class Actor : Entity
}
/// <summary>
- /// Aplica uma definição de NPC para o ator.
- /// Se o ator for um boneco de barro, este método é como um
- /// sopro de vida.
- /// </summary>
- /// <param name="definition">A definição do ator.</param>
- public virtual void SetDefinition(ActorDefinition definition) {
+ /// Aplica uma definição de NPC para o ator.
+ /// Se o ator for um boneco de barro, este método é como um
+ /// sopro de vida.
+ /// </summary>
+ /// <param name="definition">A definição do ator.</param>
+ public virtual void SetDefinition(ActorDefinition definition)
+ {
base.SetDefinition(definition);
- this.definition = definition;
+ Definition = definition;
Type = definition.Type;
@@ -168,7 +199,8 @@ public partial class Actor : Entity
Men = definition.Men;
}
- public virtual void Die() {
+ public virtual void Die()
+ {
//⠀⠀⠀⠀⢠⣤⣤⣤⢠⣤⣤⣤⣤⣄⢀⣠⣤⣤⣄⠀⠀⠀⢀⣠⣤⣤⣄⠀⣤⣤⠀⠀⣠⣤⣤⣤⣤⣤⡄⢠⣤⣤⣤⣄⠀⠀
//⠀⠀⠀⠀⠈⢹⣿⠉⠈⠉⣿⣿⠉⠉⢾⣿⣉⣉⠙⠀⠀⢀⣾⡟⠉⠉⣿⣧⢸⣿⡄⢠⣿⠏⣿⣿⣉⣉⡁⢸⣿⡏⢉⣿⡷⠀
//⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⣿⣿⠀⠀⠈⠿⠿⣿⣿⡀⠀⠸⣿⡇⠀⠀⣾⣿⠀⢿⣿⣸⡿⠀⣿⣿⠿⠿⠇⢸⣿⣿⣿⣿⠀⠀
@@ -197,19 +229,23 @@ public partial class Actor : Entity
string deathMessage;
- if (Map_Data.Player == this) {
+ if (MapData.Player == this)
+ {
deathMessage = "Você morreu!";
- } else {
+ }
+ else
+ {
deathMessage = $"{DisplayName} morreu!";
}
MessageLogData.Instance.AddMessage(deathMessage);
- Texture = definition.deathTexture;
+ Texture = Definition.deathTexture;
BlocksMovement = false;
Type = EntityType.CORPSE;
- DisplayName= $"Restos mortais de {DisplayName}";
- Map_Data.UnregisterBlockingEntity(this);
+ DisplayName = $"Restos mortais de {DisplayName}";
+ MapData.UnregisterBlockingEntity(this);
EmitSignal(SignalName.Died);
}
+ #endregion
} \ No newline at end of file
diff --git a/scripts/entities/actors/Actor.cs.uid b/scripts/Entities/Actors/Actor.cs.uid
index cf29b40..cf29b40 100644
--- a/scripts/entities/actors/Actor.cs.uid
+++ b/scripts/Entities/Actors/Actor.cs.uid
diff --git a/scripts/entities/actors/ActorDefinition.cs b/scripts/Entities/Actors/ActorDefinition.cs
index 540ede0..5bd8073 100644
--- a/scripts/entities/actors/ActorDefinition.cs
+++ b/scripts/Entities/Actors/ActorDefinition.cs
@@ -1,5 +1,7 @@
using Godot;
+namespace TheLegendOfGustav.Entities.Actors;
+
/// <summary>
/// Define de forma genérica as características de um ator.
/// </summary>
@@ -13,7 +15,7 @@ public partial class ActorDefinition : EntityDefinition
[ExportCategory("Mechanics")]
[Export]
- public int Speed { get; set;} = 10;
+ public int Speed { get; set; } = 10;
// Estatísticas padrão do ator.
[ExportCategory("Stats")]
diff --git a/scripts/entities/actors/ActorDefinition.cs.uid b/scripts/Entities/Actors/ActorDefinition.cs.uid
index ddcfe02..ddcfe02 100644
--- a/scripts/entities/actors/ActorDefinition.cs.uid
+++ b/scripts/Entities/Actors/ActorDefinition.cs.uid
diff --git a/scripts/entities/actors/Enemy.cs b/scripts/Entities/Actors/Enemy.cs
index 9c06417..c152a0b 100644
--- a/scripts/entities/actors/Enemy.cs
+++ b/scripts/Entities/Actors/Enemy.cs
@@ -1,14 +1,8 @@
using Godot;
-using System;
+using TheLegendOfGustav.Entities.Actors.AI;
+using TheLegendOfGustav.Map;
-/// <summary>
-/// Enum das diferentes IAs disponíveis.
-/// </summary>
-public enum AIType
-{
- None,
- DefaultHostile
-};
+namespace TheLegendOfGustav.Entities.Actors;
/// <summary>
/// Um inimigo é uma espécie de ator que é
@@ -16,30 +10,32 @@ public enum AIType
/// </summary>
public partial class Enemy : Actor
{
- private EnemyDefinition definition;
+ public Enemy(Vector2I initialPosition, MapData map, EnemyDefinition definition) : base(initialPosition, map, definition)
+ {
+ Definition = definition;
+ SetDefinition(definition);
+ }
+
/// <summary>
/// A alma do ator. Gera ações que são executadas todo turno.
/// </summary>
public BaseAI Soul { get; private set; }
- public Enemy(Vector2I initialPosition, MapData map, EnemyDefinition definition) : base(initialPosition, map, definition)
- {
- this.definition = definition;
- SetDefinition(definition);
- }
+ private EnemyDefinition Definition { get; set; }
/// <summary>
- /// Além de definir as características gerais de um ator,
- /// também define qual IA utilizar.
- /// </summary>
- /// <param name="definition">Definição do inimigo.</param>
+ /// Além de definir as características gerais de um ator,
+ /// também define qual IA utilizar.
+ /// </summary>
+ /// <param name="definition">Definição do inimigo.</param>
public void SetDefinition(EnemyDefinition definition)
{
// Definimos as características do ator.
- base.SetDefinition(definition);
+ base.SetDefinition(Definition);
// Definimos qual IA utilizar.
- switch(definition.AI) {
+ switch (definition.AI)
+ {
case AIType.None:
break;
case AIType.DefaultHostile:
@@ -49,7 +45,8 @@ public partial class Enemy : Actor
}
}
- public override void Die() {
+ public override void Die()
+ {
Soul.QueueFree();
Soul = null;
base.Die();
diff --git a/scripts/entities/actors/Enemy.cs.uid b/scripts/Entities/Actors/Enemy.cs.uid
index 93255b7..93255b7 100644
--- a/scripts/entities/actors/Enemy.cs.uid
+++ b/scripts/Entities/Actors/Enemy.cs.uid
diff --git a/scripts/entities/actors/EnemyDefinition.cs b/scripts/Entities/Actors/EnemyDefinition.cs
index e372e3a..97f8f13 100644
--- a/scripts/entities/actors/EnemyDefinition.cs
+++ b/scripts/Entities/Actors/EnemyDefinition.cs
@@ -1,10 +1,14 @@
using Godot;
+using TheLegendOfGustav.Entities.Actors.AI;
+
+namespace TheLegendOfGustav.Entities.Actors;
/// <summary>
/// Além das configurações do ator, também possui qual IA utilizar.
/// </summary>
[GlobalClass]
-public partial class EnemyDefinition : ActorDefinition {
+public partial class EnemyDefinition : ActorDefinition
+{
[ExportCategory("AI")]
[Export]
public AIType AI;
diff --git a/scripts/entities/actors/EnemyDefinition.cs.uid b/scripts/Entities/Actors/EnemyDefinition.cs.uid
index 1ba03e1..1ba03e1 100644
--- a/scripts/entities/actors/EnemyDefinition.cs.uid
+++ b/scripts/Entities/Actors/EnemyDefinition.cs.uid
diff --git a/scripts/Entities/Actors/Inventory.cs b/scripts/Entities/Actors/Inventory.cs
new file mode 100644
index 0000000..f65dc59
--- /dev/null
+++ b/scripts/Entities/Actors/Inventory.cs
@@ -0,0 +1,46 @@
+using Godot;
+using TheLegendOfGustav.Entities.Items;
+using TheLegendOfGustav.Map;
+using TheLegendOfGustav.Utils;
+
+namespace TheLegendOfGustav.Entities.Actors;
+
+public partial class Inventory(int capacity) : Node
+{
+ private Player Player { get; set; }
+ public int Capacity { get; private set; } = capacity;
+ public Godot.Collections.Array<ConsumableItem> Items { get; private set; } = [];
+
+ public override void _Ready()
+ {
+ base._Ready();
+ Player = GetParent<Player>();
+ }
+
+ public void Drop(ConsumableItem item)
+ {
+ Items.Remove(item);
+
+ MapData data = Player.MapData;
+
+ data.InsertEntity(item);
+ data.EmitSignal(MapData.SignalName.EntityPlaced, item);
+
+ item.MapData = data;
+ item.GridPosition = Player.GridPosition;
+
+ MessageLogData.Instance.AddMessage($"Você descarta {item.DisplayName}.");
+ }
+
+ public void Add(ConsumableItem item)
+ {
+ if (Items.Count >= Capacity) return;
+
+ Items.Add(item);
+ }
+
+ public void RemoveItem(ConsumableItem item)
+ {
+ Items.Remove(item);
+ }
+} \ No newline at end of file
diff --git a/scripts/entities/actors/Inventory.cs.uid b/scripts/Entities/Actors/Inventory.cs.uid
index 05c2beb..05c2beb 100644
--- a/scripts/entities/actors/Inventory.cs.uid
+++ b/scripts/Entities/Actors/Inventory.cs.uid
diff --git a/scripts/entities/actors/Player.cs b/scripts/Entities/Actors/Player.cs
index 71812e8..7fd80d4 100644
--- a/scripts/entities/actors/Player.cs
+++ b/scripts/Entities/Actors/Player.cs
@@ -1,5 +1,7 @@
using Godot;
-using System;
+using TheLegendOfGustav.Map;
+
+namespace TheLegendOfGustav.Entities.Actors;
/// <summary>
/// Classe do jogador. Por enquanto não é diferente do Ator, mas isso pode mudar.
@@ -7,18 +9,19 @@ using System;
[GlobalClass]
public partial class Player : Actor
{
- private PlayerDefinition definition;
- public Inventory inventory;
-
public Player(Vector2I initialPosition, MapData map, PlayerDefinition definition) : base(initialPosition, map, definition)
{
- this.definition = definition;
+ Definition = definition;
SetDefinition(definition);
}
- public void SetDefinition(PlayerDefinition definition) {
- inventory = new(definition.InventoryCapacity);
+ private PlayerDefinition Definition { get; set; }
+ public Inventory Inventory { get; private set; }
+
+ public void SetDefinition(PlayerDefinition definition)
+ {
+ Inventory = new(definition.InventoryCapacity);
- AddChild(inventory);
+ AddChild(Inventory);
}
}
diff --git a/scripts/entities/actors/Player.cs.uid b/scripts/Entities/Actors/Player.cs.uid
index 8229b7f..8229b7f 100644
--- a/scripts/entities/actors/Player.cs.uid
+++ b/scripts/Entities/Actors/Player.cs.uid
diff --git a/scripts/entities/actors/PlayerDefinition.cs b/scripts/Entities/Actors/PlayerDefinition.cs
index aca07e1..58ae6b4 100644
--- a/scripts/entities/actors/PlayerDefinition.cs
+++ b/scripts/Entities/Actors/PlayerDefinition.cs
@@ -1,7 +1,10 @@
using Godot;
+namespace TheLegendOfGustav.Entities.Actors;
+
[GlobalClass]
-public partial class PlayerDefinition : ActorDefinition {
+public partial class PlayerDefinition : ActorDefinition
+{
[ExportCategory("Player Mechanics")]
[Export]
public int InventoryCapacity = 0;
diff --git a/scripts/entities/actors/PlayerDefinition.cs.uid b/scripts/Entities/Actors/PlayerDefinition.cs.uid
index 9d01ab9..9d01ab9 100644
--- a/scripts/entities/actors/PlayerDefinition.cs.uid
+++ b/scripts/Entities/Actors/PlayerDefinition.cs.uid
diff --git a/scripts/entities/Entity.cs b/scripts/Entities/Entity.cs
index 85a3156..412bd7a 100644
--- a/scripts/entities/Entity.cs
+++ b/scripts/Entities/Entity.cs
@@ -1,4 +1,8 @@
using Godot;
+using TheLegendOfGustav.Map;
+using TheLegendOfGustav.Utils;
+
+namespace TheLegendOfGustav.Entities;
/// <summary>
/// Defino aqui que o jogo irá desenhar
@@ -14,22 +18,33 @@ public enum EntityType
/// <summary>
/// Classe para elementos móveis que o jogador pode interagir.
/// </summary>
-public abstract partial class Entity : Sprite2D {
- /// <summary>
- /// A definição da entidade possui caracterísitcas padrões que definem
- /// a entidade em questão.
- /// </summary>
- private EntityDefinition definition;
+public abstract partial class Entity : Sprite2D
+{
+ private Vector2I gridPosition = Vector2I.Zero;
private EntityType type;
+ private bool blocksMovement;
+ private string displayName;
+
+ public Entity(Vector2I initialPosition, MapData map, EntityDefinition definition)
+ {
+ GridPosition = initialPosition;
+ MapData = map;
+ Centered = false;
+
+ SetDefinition(definition);
+ }
+
/// <summary>
- /// Usado para definir a camada da entidade no mapa.
- /// </summary>
- public EntityType Type {
+ /// Usado para definir a camada da entidade no mapa.
+ /// </summary>
+ public EntityType Type
+ {
get => type;
- set {
+ set
+ {
type = value;
- ZIndex = (int) type;
+ ZIndex = (int)type;
}
}
@@ -37,15 +52,16 @@ public abstract partial class Entity : Sprite2D {
/// É conveniente ter acesso ao mapa dentro da entidade. Isto porque ela existe dentro
/// do mapa, então é necessário ter acesso à algumas informações.
/// </summary>
- public MapData Map_Data { get; set; }
+ public MapData MapData { get; set; }
- private Vector2I gridPosition = Vector2I.Zero;
/// <summary>
- /// Posição da entidade no mapa do jogo. Diferentemente de Position, GridPosition tem como formato
- /// os tiles do mapa.
- /// </summary>
- public Vector2I GridPosition {
- set {
+ /// Posição da entidade no mapa do jogo. Diferentemente de Position, GridPosition tem como formato
+ /// os tiles do mapa.
+ /// </summary>
+ public Vector2I GridPosition
+ {
+ set
+ {
gridPosition = value;
// O sistema de coordenadas do Godot é em pixels, mas faz mais sentido para o jogo utilizar coordenadas em tiles.
// Esta propriedade converte um sistema para o outro automaticamente.
@@ -54,28 +70,36 @@ public abstract partial class Entity : Sprite2D {
get => gridPosition;
}
- private bool blocksMovement;
/// <summary>
/// Se a entidade bloqueia movimento (não pode oculpar a mesma célula de outra entidade.)
/// </summary>
- public bool BlocksMovement {
+ public bool BlocksMovement
+ {
get => blocksMovement;
- protected set {
+ protected set
+ {
blocksMovement = value;
}
}
- private string displayName;
/// <summary>
- /// Nome da entidade.
- /// </summary>
- public string DisplayName {
+ /// Nome da entidade.
+ /// </summary>
+ public string DisplayName
+ {
get => displayName;
- protected set {
+ protected set
+ {
displayName = value;
}
}
+ /// <summary>
+ /// A definição da entidade possui caracterísitcas padrões que definem
+ /// a entidade em questão.
+ /// </summary>
+ private EntityDefinition Definition;
+
public override void _Ready()
{
base._Ready();
@@ -84,22 +108,17 @@ public abstract partial class Entity : Sprite2D {
GridPosition = Grid.WorldToGrid(Position);
}
- public Entity(Vector2I initialPosition, MapData map, EntityDefinition definition) {
- GridPosition = initialPosition;
- Map_Data = map;
- Centered = false;
-
- SetDefinition(definition);
- }
+
/// <summary>
- /// Aplica uma definição de NPC para o ator.
- /// Se o ator for um boneco de barro, este método é como um
- /// sopro de vida.
- /// </summary>
- /// <param name="definition">A definição do ator.</param>
- public virtual void SetDefinition(EntityDefinition definition) {
- this.definition = definition;
+ /// Aplica uma definição de NPC para o ator.
+ /// Se o ator for um boneco de barro, este método é como um
+ /// sopro de vida.
+ /// </summary>
+ /// <param name="definition">A definição do ator.</param>
+ public virtual void SetDefinition(EntityDefinition definition)
+ {
+ Definition = definition;
BlocksMovement = definition.blocksMovement;
DisplayName = definition.name;
Type = definition.Type;
diff --git a/scripts/entities/Entity.cs.uid b/scripts/Entities/Entity.cs.uid
index f178d64..f178d64 100644
--- a/scripts/entities/Entity.cs.uid
+++ b/scripts/Entities/Entity.cs.uid
diff --git a/scripts/entities/EntityDefinition.cs b/scripts/Entities/EntityDefinition.cs
index ba7236a..a6080bd 100644
--- a/scripts/entities/EntityDefinition.cs
+++ b/scripts/Entities/EntityDefinition.cs
@@ -1,7 +1,10 @@
using Godot;
+namespace TheLegendOfGustav.Entities;
+
[GlobalClass]
-public partial class EntityDefinition : Resource{
+public partial class EntityDefinition : Resource
+{
[ExportCategory("Entity Visuals")]
// Nome da entidade.
[Export]
diff --git a/scripts/entities/EntityDefinition.cs.uid b/scripts/Entities/EntityDefinition.cs.uid
index 0aed6ab..0aed6ab 100644
--- a/scripts/entities/EntityDefinition.cs.uid
+++ b/scripts/Entities/EntityDefinition.cs.uid
diff --git a/scripts/entities/items/ConsumableItem.cs b/scripts/Entities/Items/ConsumableItem.cs
index 82fab49..f70983a 100644
--- a/scripts/entities/items/ConsumableItem.cs
+++ b/scripts/Entities/Items/ConsumableItem.cs
@@ -1,14 +1,16 @@
using Godot;
+using TheLegendOfGustav.Entities.Actions;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Map;
+
+namespace TheLegendOfGustav.Entities.Items;
/// <summary>
/// Classe para itens consumíveis.
/// Itens consumíveis são itens de uso limitado.
/// </summary>
-public abstract partial class ConsumableItem : Entity
+public abstract partial class ConsumableItem(Vector2I initialPosition, MapData map, EntityDefinition definition) : Entity(initialPosition, map, definition)
{
- public ConsumableItem(Vector2I initialPosition, MapData map, EntityDefinition definition) : base(initialPosition, map, definition)
- {
- }
/// <summary>
/// Gera uma ação onde o ator consome o item.
@@ -29,8 +31,9 @@ public abstract partial class ConsumableItem : Entity
/// <returns>Se a ação foi realizada ou não.</returns>
public abstract bool Activate(ItemAction action);
- public void ConsumedBy(Player consumer) {
- Inventory inventory = consumer.inventory;
+ public void ConsumedBy(Player consumer)
+ {
+ Inventory inventory = consumer.Inventory;
inventory.RemoveItem(this);
QueueFree();
}
diff --git a/scripts/entities/items/ConsumableItem.cs.uid b/scripts/Entities/Items/ConsumableItem.cs.uid
index e6c452a..e6c452a 100644
--- a/scripts/entities/items/ConsumableItem.cs.uid
+++ b/scripts/Entities/Items/ConsumableItem.cs.uid
diff --git a/scripts/entities/items/ConsumableItemDefinition.cs b/scripts/Entities/Items/ConsumableItemDefinition.cs
index 74340d2..9cadc0b 100644
--- a/scripts/entities/items/ConsumableItemDefinition.cs
+++ b/scripts/Entities/Items/ConsumableItemDefinition.cs
@@ -1,5 +1,7 @@
using Godot;
+namespace TheLegendOfGustav.Entities.Items;
+
/// <summary>
/// Esta classe só existe para agrupar seus descendentes.
/// </summary>
diff --git a/scripts/entities/items/ConsumableItemDefinition.cs.uid b/scripts/Entities/Items/ConsumableItemDefinition.cs.uid
index 9ddc0f6..9ddc0f6 100644
--- a/scripts/entities/items/ConsumableItemDefinition.cs.uid
+++ b/scripts/Entities/Items/ConsumableItemDefinition.cs.uid
diff --git a/scripts/Entities/Items/HealingConsumable.cs b/scripts/Entities/Items/HealingConsumable.cs
new file mode 100644
index 0000000..32f76a6
--- /dev/null
+++ b/scripts/Entities/Items/HealingConsumable.cs
@@ -0,0 +1,30 @@
+using Godot;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Utils;
+using TheLegendOfGustav.Entities.Actions;
+using TheLegendOfGustav.Map;
+
+namespace TheLegendOfGustav.Entities.Items;
+
+public partial class HealingConsumable(Vector2I initialPosition, MapData map, HealingConsumableDefinition definition) : ConsumableItem(initialPosition, map, definition)
+{
+ private HealingConsumableDefinition Definition { get; set; } = definition;
+ public float HealingPercentage { get; private set; } = definition.healingPercentage;
+
+ public override bool Activate(ItemAction action)
+ {
+ Player consumer = action.Player;
+ int intendedAmount = (int)(HealingPercentage / 100 * consumer.MaxHp);
+ int recovered = consumer.Heal(intendedAmount);
+
+ // Se não tinha o que curar, a ativação falhou.
+ if (recovered == 0)
+ {
+ MessageLogData.Instance.AddMessage("Você já está saudável.");
+ return false;
+ }
+ MessageLogData.Instance.AddMessage($"Você consome {DisplayName} e recupera {recovered} de HP");
+ ConsumedBy(consumer);
+ return true;
+ }
+} \ No newline at end of file
diff --git a/scripts/entities/items/HealingConsumable.cs.uid b/scripts/Entities/Items/HealingConsumable.cs.uid
index 8f8f942..8f8f942 100644
--- a/scripts/entities/items/HealingConsumable.cs.uid
+++ b/scripts/Entities/Items/HealingConsumable.cs.uid
diff --git a/scripts/entities/items/HealingConsumableDefinition.cs b/scripts/Entities/Items/HealingConsumableDefinition.cs
index 2562e9e..d0e5850 100644
--- a/scripts/entities/items/HealingConsumableDefinition.cs
+++ b/scripts/Entities/Items/HealingConsumableDefinition.cs
@@ -1,10 +1,13 @@
using Godot;
+namespace TheLegendOfGustav.Entities.Items;
+
[GlobalClass]
-public partial class HealingConsumableDefinition : ConsumableItemDefinition {
+public partial class HealingConsumableDefinition : ConsumableItemDefinition
+{
///<summary>
- /// Porcentagem da vida do ator para restaurar.
- ///</summary>
+ /// Porcentagem da vida do ator para restaurar.
+ ///</summary>
[ExportCategory("Item Mechanics")]
[Export]
public float healingPercentage = 10;
diff --git a/scripts/entities/items/HealingConsumableDefinition.cs.uid b/scripts/Entities/Items/HealingConsumableDefinition.cs.uid
index 2fd311d..2fd311d 100644
--- a/scripts/entities/items/HealingConsumableDefinition.cs.uid
+++ b/scripts/Entities/Items/HealingConsumableDefinition.cs.uid
diff --git a/scripts/GUI/Details.cs b/scripts/GUI/Details.cs
index 814d2ac..3c64427 100644
--- a/scripts/GUI/Details.cs
+++ b/scripts/GUI/Details.cs
@@ -1,48 +1,57 @@
using Godot;
-using System;
+using TheLegendOfGustav.Entities;
+using TheLegendOfGustav.Map;
+using TheLegendOfGustav.Utils;
+
+namespace TheLegendOfGustav.GUI;
public partial class Details : CanvasLayer
{
private static readonly LabelSettings lblSettings = GD.Load<LabelSettings>("res://assets/definitions/message_label_settings.tres");
- private Map map;
- private VBoxContainer entityNames;
- private Godot.Collections.Array<Entity> entities = [];
+
+ private Map.Map Map { get; set; }
+ private VBoxContainer EntityNames { get; set; }
+ private Godot.Collections.Array<Entity> Entities { get; set; } = [];
- private Godot.Collections.Array<Label> actorsLabel = [];
+ private Godot.Collections.Array<Label> ActorsLabel { get; set; } = [];
public override void _Ready()
{
base._Ready();
- map = GetParent<Map>();
- entityNames = GetNode<VBoxContainer>("HBoxContainer/PanelContainer/ScrollContainer/Entities");
+ Map = GetParent<Map.Map>();
+ EntityNames = GetNode<VBoxContainer>("HBoxContainer/PanelContainer/ScrollContainer/Entities");
SignalBus.Instance.InspectorMoved += OnInspectorWalk;
SignalBus.Instance.EnterInspectionMode += () => Visible = true;
SignalBus.Instance.ExitInspectionMode += () => Visible = false;
}
- public void OnInspectorWalk(Vector2I pos) {
- MapData mapData = map.Map_Data;
- entities = mapData.GetEntitiesAtPosition(pos);
+ public void OnInspectorWalk(Vector2I pos)
+ {
+ MapData mapData = Map.MapData;
+ Entities = mapData.GetEntitiesAtPosition(pos);
UpdateLabels();
}
- private void UpdateLabels() {
- foreach(Label label in actorsLabel) {
+ private void UpdateLabels()
+ {
+ foreach (Label label in ActorsLabel)
+ {
label.QueueFree();
}
- actorsLabel.Clear();
- foreach (Entity entity in entities) {
+ ActorsLabel.Clear();
+
+ foreach (Entity entity in Entities)
+ {
Label label = new()
{
Text = entity.DisplayName,
LabelSettings = lblSettings
};
- actorsLabel.Add(label);
- entityNames.AddChild(label);
+ ActorsLabel.Add(label);
+ EntityNames.AddChild(label);
}
}
}
- \ No newline at end of file
diff --git a/scripts/GUI/Hud.cs b/scripts/GUI/Hud.cs
index 614fa5e..1149728 100644
--- a/scripts/GUI/Hud.cs
+++ b/scripts/GUI/Hud.cs
@@ -1,17 +1,20 @@
using Godot;
-using System;
+
+namespace TheLegendOfGustav.GUI;
public partial class Hud : Node
{
- private TextureProgressBar hpBar;
+ private TextureProgressBar HpBar { get; set; }
- public override void _Ready() {
+ public override void _Ready()
+ {
base._Ready();
- hpBar = GetNode<TextureProgressBar>("VBoxContainer/InfoBar/Stats/MarginContainer/HBoxContainer/AspectRatioContainer/HPbar");
+ HpBar = GetNode<TextureProgressBar>("VBoxContainer/InfoBar/Stats/MarginContainer/HBoxContainer/AspectRatioContainer/HPbar");
}
- public void OnHealthChanged(int hp, int maxHp) {
- hpBar.Value = hp;
- hpBar.MaxValue = maxHp;
+ public void OnHealthChanged(int hp, int maxHp)
+ {
+ HpBar.Value = hp;
+ HpBar.MaxValue = maxHp;
}
}
diff --git a/scripts/GUI/InventoryMenu.cs b/scripts/GUI/InventoryMenu.cs
index bfb1795..5bac62b 100644
--- a/scripts/GUI/InventoryMenu.cs
+++ b/scripts/GUI/InventoryMenu.cs
@@ -1,52 +1,63 @@
using Godot;
-using System;
+using TheLegendOfGustav.Entities.Items;
+using TheLegendOfGustav.Entities.Actors;
+
+namespace TheLegendOfGustav.GUI;
public partial class InventoryMenu : CanvasLayer
{
private static readonly PackedScene itemMenuEntryScene = GD.Load<PackedScene>("res://scenes/GUI/item_menu_entry.tscn");
+
[Signal]
public delegate void ItemSelectedEventHandler(ConsumableItem item);
[Signal]
public delegate void ItemDropEventHandler(ConsumableItem item);
- private VBoxContainer itemsNode;
+ private VBoxContainer ItemsNode { get; set; }
- public override void _Ready() {
+ public override void _Ready()
+ {
base._Ready();
- itemsNode = GetNode<VBoxContainer>("CenterContainer/PanelContainer/VBoxContainer/Items");
+ ItemsNode = GetNode<VBoxContainer>("CenterContainer/PanelContainer/VBoxContainer/Items");
Hide();
}
- public void OnActivate(ConsumableItem item) {
+ public void OnActivate(ConsumableItem item)
+ {
EmitSignal(SignalName.ItemSelected, item);
}
-
- public void OnDrop(ConsumableItem item) {
+
+ public void OnDrop(ConsumableItem item)
+ {
EmitSignal(SignalName.ItemDrop, item);
}
- private void RegisterItem(int index, ConsumableItem item) {
+ public void Initialize(Inventory inventory)
+ {
+ for (int i = 0; i < inventory.Items.Count; i++)
+ {
+ RegisterItem(i, inventory.Items[i]);
+ }
+
+ Show();
+ }
+
+ private void RegisterItem(int index, ConsumableItem item)
+ {
char? shortcut = null;
// Só terá atalho para as letras do alfabeto.
- if (index < 26) {
+ if (index < 26)
+ {
shortcut = (char)('a' + index);
}
ItemMenuEntry itemEntry = itemMenuEntryScene.Instantiate<ItemMenuEntry>();
- itemsNode.AddChild(itemEntry);
+ ItemsNode.AddChild(itemEntry);
itemEntry.Initialize(item, shortcut);
itemEntry.Activate += OnActivate;
itemEntry.Drop += OnDrop;
}
-
- public void Initialize(Inventory inventory) {
- for (int i = 0; i < inventory.Items.Count; i++) {
- RegisterItem(i, inventory.Items[i]);
- }
-
- Show();
- }
}
diff --git a/scripts/GUI/ItemMenuEntry.cs b/scripts/GUI/ItemMenuEntry.cs
index 449c97b..7ee3fcd 100644
--- a/scripts/GUI/ItemMenuEntry.cs
+++ b/scripts/GUI/ItemMenuEntry.cs
@@ -1,41 +1,48 @@
using Godot;
+using TheLegendOfGustav.Entities.Items;
+
+namespace TheLegendOfGustav.GUI;
public partial class ItemMenuEntry : HBoxContainer
{
- private TextureRect icon;
- private Label shortcutLabel;
- private Label nameLabel;
- private Button activateBtn;
- private Button dropBtn;
-
[Signal]
public delegate void ActivateEventHandler(ConsumableItem Item);
[Signal]
- public delegate void DropEventHandler(ConsumableItem item);
-
- private ConsumableItem item;
+ public delegate void DropEventHandler(ConsumableItem Item);
- public void Initialize(ConsumableItem item, char? shortcut) {
- this.item = item;
- nameLabel.Text = item.DisplayName;
- if (shortcut != null) {
- shortcutLabel.Text = $"{shortcut}";
- } else {
- shortcutLabel.Text = "";
- }
- icon.Texture = item.Texture;
- }
+ private TextureRect Icon { get; set; }
+ private Label ShortcutLabel { get; set; }
+ private Label NameLabel { get; set; }
+ private Button ActivateBtn { get; set; }
+ private Button DropBtn { get; set; }
+ private ConsumableItem Item { get; set; }
- public override void _Ready() {
+ public override void _Ready()
+ {
base._Ready();
- icon = GetNode<TextureRect>("Icon");
- shortcutLabel = GetNode<Label>("Shortcut");
- nameLabel = GetNode<Label>("ItemName");
- activateBtn = GetNode<Button>("ActivateBtn");
- dropBtn = GetNode<Button>("DropButton");
+ Icon = GetNode<TextureRect>("Icon");
+ ShortcutLabel = GetNode<Label>("Shortcut");
+ NameLabel = GetNode<Label>("ItemName");
+ ActivateBtn = GetNode<Button>("ActivateBtn");
+ DropBtn = GetNode<Button>("DropButton");
- activateBtn.Pressed += () => EmitSignal(SignalName.Activate, item);
- dropBtn.Pressed += () => EmitSignal(SignalName.Drop, item);
+ ActivateBtn.Pressed += () => EmitSignal(SignalName.Activate, Item);
+ DropBtn.Pressed += () => EmitSignal(SignalName.Drop, Item);
+ }
+
+ public void Initialize(ConsumableItem item, char? shortcut)
+ {
+ Item = item;
+ NameLabel.Text = item.DisplayName;
+ if (shortcut != null)
+ {
+ ShortcutLabel.Text = $"{shortcut}";
+ }
+ else
+ {
+ ShortcutLabel.Text = "";
+ }
+ Icon.Texture = item.Texture;
}
}
diff --git a/scripts/GUI/Message.cs b/scripts/GUI/Message.cs
index b0472ee..11f3532 100644
--- a/scripts/GUI/Message.cs
+++ b/scripts/GUI/Message.cs
@@ -1,32 +1,49 @@
using Godot;
+namespace TheLegendOfGustav.GUI;
+
public partial class Message : Label
{
- private static LabelSettings baseSettings = GD.Load<LabelSettings>("res://assets/definitions/message_label_settings.tres");
+ private static readonly LabelSettings baseSettings = GD.Load<LabelSettings>("res://assets/definitions/message_label_settings.tres");
+
private string plainText;
- public string PlainText { get => plainText; }
private int count = 1;
- public int Count {
+
+ public Message(string text)
+ {
+ PlainText = text;
+ Text = text;
+ LabelSettings = (LabelSettings)baseSettings.Duplicate();
+ AutowrapMode = TextServer.AutowrapMode.WordSmart;
+ }
+
+ public string PlainText
+ {
+ get => plainText;
+ private set
+ {
+ plainText = value;
+ }
+ }
+ public int Count
+ {
get => count;
- set {
+ set
+ {
count = value;
Text = FullText;
}
}
- public string FullText {
- get {
- if (count > 1) {
+ public string FullText
+ {
+ get
+ {
+ if (count > 1)
+ {
return $"{plainText} ({count})";
}
return plainText;
}
}
-
- public Message(string text) {
- plainText = text;
- Text = text;
- LabelSettings = (LabelSettings) baseSettings.Duplicate();
- AutowrapMode = TextServer.AutowrapMode.WordSmart;
- }
}
diff --git a/scripts/GUI/MessageLog.cs b/scripts/GUI/MessageLog.cs
index d481b19..1fc59b6 100644
--- a/scripts/GUI/MessageLog.cs
+++ b/scripts/GUI/MessageLog.cs
@@ -1,24 +1,28 @@
using Godot;
-using System;
using System.Threading.Tasks;
+using TheLegendOfGustav.Utils;
+
+namespace TheLegendOfGustav.GUI;
public partial class MessageLog : ScrollContainer
{
- private VBoxContainer MessageList;
+ private VBoxContainer MessageList { get; set; }
public override void _Ready()
{
base._Ready();
MessageList = GetNode<VBoxContainer>("MessageList");
- foreach (Message msg in MessageLogData.Instance.Messages) {
+ foreach (Message msg in MessageLogData.Instance.Messages)
+ {
_ = AddMessageAsync(msg);
}
MessageLogData.Instance.messageSent += async (Message msg) => await AddMessageAsync(msg);
}
- private async Task AddMessageAsync(Message message) {
+ private async Task AddMessageAsync(Message message)
+ {
MessageList.AddChild(message);
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
EnsureControlVisible(message);
diff --git a/scripts/Game.cs b/scripts/Game.cs
index b8c6a8b..ff10eea 100644
--- a/scripts/Game.cs
+++ b/scripts/Game.cs
@@ -1,36 +1,45 @@
using Godot;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Entities.Actions;
+using TheLegendOfGustav.InputHandling;
+using TheLegendOfGustav.GUI;
+using TheLegendOfGustav.Time;
+using TheLegendOfGustav.Utils;
+namespace TheLegendOfGustav;
/// <summary>
/// Classe principal do jogo.
/// Lar da lógica central do jogo.
/// </summary>
-public partial class Game : Node {
+public partial class Game : Node
+{
/// <summary>
- /// Definição de um jogador.
- /// </summary>
+ /// Definição de um jogador.
+ /// </summary>
private static readonly PlayerDefinition playerDefinition = GD.Load<PlayerDefinition>("res://assets/definitions/actor/Player.tres");
/// <summary>
- /// O jogo possui o mapa.
- /// </summary>
- private Map Map;
+ /// O jogo possui o mapa.
+ /// </summary>
+ private Map.Map Map { get; set; }
/// <summary>
- /// Objeto para obter input do usuário.
- /// </summary>
- private InputHandler inputHandler;
+ /// Objeto para obter input do usuário.
+ /// </summary>
+ private InputHandler InputHandler { get; set; }
/// <summary>
/// Gerenciador de turnos
/// </summary>
- private TurnManager turnManager;
+ private TurnManager TurnManager { get; set; }
private Hud hud;
- public override void _Ready() {
+ public override void _Ready()
+ {
base._Ready();
- Map = GetNode<Map>("Map");
+ Map = GetNode<Map.Map>("Map");
- inputHandler = GetNode<InputHandler>("InputHandler");
+ InputHandler = GetNode<InputHandler>("InputHandler");
hud = GetNode<Hud>("HUD");
// O jogador é criado pelo jogo.
@@ -38,7 +47,7 @@ public partial class Game : Node {
Camera2D camera = GetNode<Camera2D>("Camera2D");
RemoveChild(camera);
player.HealthChanged += (int hp, int maxHp) => hud.OnHealthChanged(hp, maxHp);
- player.Died += () => inputHandler.SetInputHandler(InputHandlers.GameOver);
+ player.Died += () => InputHandler.SetInputHandler(InputHandlers.GameOver);
player.AddChild(camera);
@@ -46,29 +55,31 @@ public partial class Game : Node {
Map.UpdateFOV(player.GridPosition);
- turnManager = new(Map);
+ TurnManager = new(Map);
MessageLogData.Instance.AddMessage("Boa sorte!");
}
/// <summary>
- /// Método executa aproximadamente 60 vezes por segundo.
- /// </summary>
- /// <param name="delta"></param>
- public override void _PhysicsProcess(double delta) {
+ /// Método executa aproximadamente 60 vezes por segundo.
+ /// </summary>
+ /// <param name="delta"></param>
+ public override void _PhysicsProcess(double delta)
+ {
base._PhysicsProcess(delta);
- Player player = Map.Map_Data.Player;
+ Player player = Map.MapData.Player;
// Pegamos uma ação do usuário
- Action action = inputHandler.GetAction(player);
+ Action action = InputHandler.GetAction(player);
- if (action != null) {
- turnManager.InsertPlayerAction(action);
+ if (action != null)
+ {
+ TurnManager.InsertPlayerAction(action);
}
// Computamos um turno.
- turnManager.Tick();
+ TurnManager.Tick();
}
}
diff --git a/scripts/input/BaseInputHandler.cs b/scripts/InputHandling/BaseInputHandler.cs
index 4e389ed..f3527ca 100644
--- a/scripts/input/BaseInputHandler.cs
+++ b/scripts/InputHandling/BaseInputHandler.cs
@@ -1,4 +1,8 @@
using Godot;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Entities.Actions;
+
+namespace TheLegendOfGustav.InputHandling;
/// <summary>
/// Classe base para obter ações do usuário.
@@ -8,14 +12,15 @@ using Godot;
/// possui somente dois estados: Com jogador vivo e com jogador morto.
/// Mas isto pode aumentar.
/// </summary>
-public abstract partial class BaseInputHandler : Node {
- /// <summary>
- /// Método executado quando o input handler entra em cena;
- /// </summary>
+public abstract partial class BaseInputHandler : Node
+{
+ /// <summary>
+ /// Método executado quando o input handler entra em cena;
+ /// </summary>
public virtual void Enter() { }
- /// <summary>
- /// Método executado quando o input handler sai de cena;
- /// </summary>
+ /// <summary>
+ /// Método executado quando o input handler sai de cena;
+ /// </summary>
public virtual void Exit() { }
/// <summary>
/// Obtém uma ação do usuári conforme input.
diff --git a/scripts/input/BaseInputHandler.cs.uid b/scripts/InputHandling/BaseInputHandler.cs.uid
index deae303..deae303 100644
--- a/scripts/input/BaseInputHandler.cs.uid
+++ b/scripts/InputHandling/BaseInputHandler.cs.uid
diff --git a/scripts/input/GameOverInputHandler.cs b/scripts/InputHandling/GameOverInputHandler.cs
index 5e41daf..e11e98a 100644
--- a/scripts/input/GameOverInputHandler.cs
+++ b/scripts/InputHandling/GameOverInputHandler.cs
@@ -1,10 +1,13 @@
-using Godot;
+using TheLegendOfGustav.Entities.Actions;
+using TheLegendOfGustav.Entities.Actors;
+
+namespace TheLegendOfGustav.InputHandling;
/// <summary>
/// Esquema de controles para quando o jogador está morto.
/// </summary>
-public partial class GameOverInputHandler : BaseInputHandler {
-
+public partial class GameOverInputHandler : BaseInputHandler
+{
// Por enquanto não tem nada.
public override Action GetAction(Player player)
{
diff --git a/scripts/input/GameOverInputHandler.cs.uid b/scripts/InputHandling/GameOverInputHandler.cs.uid
index 14ddfd8..14ddfd8 100644
--- a/scripts/input/GameOverInputHandler.cs.uid
+++ b/scripts/InputHandling/GameOverInputHandler.cs.uid
diff --git a/scripts/InputHandling/InputHandler.cs b/scripts/InputHandling/InputHandler.cs
new file mode 100644
index 0000000..55a17b4
--- /dev/null
+++ b/scripts/InputHandling/InputHandler.cs
@@ -0,0 +1,58 @@
+using Godot;
+using TheLegendOfGustav.Entities.Actions;
+using TheLegendOfGustav.Entities.Actors;
+
+namespace TheLegendOfGustav.InputHandling;
+
+public enum InputHandlers
+{
+ MainGame,
+ GameOver,
+ Inspect,
+ Pickup,
+ Inventory
+}
+
+/// <summary>
+/// Máquina de estado que obtém ações do usuário conforme o estado atual do jogo.
+/// </summary>
+public partial class InputHandler : Node
+{
+ [Export]
+ private InputHandlers StartingInputHandler { get; set; }
+
+ private Godot.Collections.Dictionary<InputHandlers, BaseInputHandler> InputHandlerDict { get; set; } = [];
+
+ private BaseInputHandler SelectedInputHandler { get; set; }
+
+ public override void _Ready()
+ {
+ base._Ready();
+ // Controles para quando o jogador está vivo e jogando normalmente.
+ InputHandlerDict.Add(InputHandlers.MainGame, GetNode<MainGameInputHandler>("MainGameInputHandler"));
+ // Controles para quando o jogador está morto.
+ InputHandlerDict.Add(InputHandlers.GameOver, GetNode<GameOverInputHandler>("GameOverInputHandler"));
+ InputHandlerDict.Add(InputHandlers.Inspect, GetNode<InspectInputHandler>("InspectInputHandler"));
+ InputHandlerDict.Add(InputHandlers.Pickup, GetNode<PickupInputHandler>("PickupInputHandler"));
+ InputHandlerDict.Add(InputHandlers.Inventory, GetNode<InventoryInputHandler>("InventoryInputHandler"));
+
+ SetInputHandler(StartingInputHandler);
+ }
+
+ public Action GetAction(Player player)
+ {
+ return SelectedInputHandler.GetAction(player);
+ }
+
+ /// <summary>
+ /// Define o esquema de controle atual do jogo
+ /// para o estado informado.
+ /// </summary>
+ /// <param name="inputhandler">Estado do jogo.</param>
+ public void SetInputHandler(InputHandlers inputhandler)
+ {
+ SelectedInputHandler?.Exit();
+ SelectedInputHandler = InputHandlerDict[inputhandler];
+ SelectedInputHandler.Enter();
+ }
+}
diff --git a/scripts/input/InputHandler.cs.uid b/scripts/InputHandling/InputHandler.cs.uid
index f61cdba..f61cdba 100644
--- a/scripts/input/InputHandler.cs.uid
+++ b/scripts/InputHandling/InputHandler.cs.uid
diff --git a/scripts/input/InspectInputHandler.cs b/scripts/InputHandling/InspectInputHandler.cs
index ad76c62..90734f9 100644
--- a/scripts/input/InspectInputHandler.cs
+++ b/scripts/InputHandling/InspectInputHandler.cs
@@ -1,4 +1,9 @@
using Godot;
+using TheLegendOfGustav.Entities.Actions;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Utils;
+
+namespace TheLegendOfGustav.InputHandling;
/// <summary>
/// TODO: Esta solução é nojenta e precisa ser retrabalhada.
@@ -7,7 +12,7 @@ public partial class InspectInputHandler : BaseInputHandler
{
private static readonly PackedScene InspectorScene = GD.Load<PackedScene>("res://scenes/Inspector.tscn");
- private readonly Godot.Collections.Dictionary<string, Vector2I> directions = new()
+ private static readonly Godot.Collections.Dictionary<string, Vector2I> directions = new()
{
{"walk-up", Vector2I.Up},
{"walk-down", Vector2I.Down},
@@ -18,38 +23,45 @@ public partial class InspectInputHandler : BaseInputHandler
{"walk-down-right", Vector2I.Down + Vector2I.Right},
{"walk-down-left", Vector2I.Down + Vector2I.Left},
};
+
/// <summary>
- /// Preciso disso
- /// </summary>
+ /// Preciso disso
+ /// </summary>
[Export]
- private Map map;
+ private Map.Map Map { get; set; }
- private Inspector inspector;
+ private Inspector Inspector { get; set; }
- public override void Enter() {
+ public override void Enter()
+ {
SignalBus.Instance.EmitSignal(SignalBus.SignalName.EnterInspectionMode);
- inspector = InspectorScene.Instantiate<Inspector>();
+ Inspector = InspectorScene.Instantiate<Inspector>();
- inspector.GridPosition = map.Map_Data.Player.GridPosition;
+ Inspector.GridPosition = Map.MapData.Player.GridPosition;
- map.AddChild(inspector);
+ Map.AddChild(Inspector);
}
- public override void Exit() {
- inspector.QueueFree();
+ public override void Exit()
+ {
+ Inspector.QueueFree();
SignalBus.Instance.EmitSignal(SignalBus.SignalName.ExitInspectionMode);
}
+
public override Action GetAction(Player player)
{
Action action = null;
- foreach (var direction in directions) {
- if (Input.IsActionJustPressed(direction.Key)) {
- inspector.Walk(direction.Value);
+ foreach (var direction in directions)
+ {
+ if (Input.IsActionJustPressed(direction.Key))
+ {
+ Inspector.Walk(direction.Value);
}
}
- if (Input.IsActionJustPressed("quit")) {
+ if (Input.IsActionJustPressed("quit"))
+ {
GetParent<InputHandler>().SetInputHandler(InputHandlers.MainGame);
}
diff --git a/scripts/input/InspectInputHandler.cs.uid b/scripts/InputHandling/InspectInputHandler.cs.uid
index 20141fa..20141fa 100644
--- a/scripts/input/InspectInputHandler.cs.uid
+++ b/scripts/InputHandling/InspectInputHandler.cs.uid
diff --git a/scripts/InputHandling/InventoryInputHandler.cs b/scripts/InputHandling/InventoryInputHandler.cs
new file mode 100644
index 0000000..390e545
--- /dev/null
+++ b/scripts/InputHandling/InventoryInputHandler.cs
@@ -0,0 +1,75 @@
+using Godot;
+using TheLegendOfGustav.GUI;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Entities.Items;
+using TheLegendOfGustav.Entities.Actions;
+
+namespace TheLegendOfGustav.InputHandling;
+
+public partial class InventoryInputHandler : BaseInputHandler
+{
+ private static readonly PackedScene inventoryScene = GD.Load<PackedScene>("res://scenes/GUI/invetory_menu.tscn");
+
+
+ [Export]
+ private Map.Map Map { get; set; }
+
+ private InventoryMenu InventoryMenu { get; set; }
+ private ConsumableItem ActivationItem { get; set; } = null;
+ private ConsumableItem DropItem { get; set; } = null;
+
+ public override void Enter()
+ {
+ InventoryMenu = inventoryScene.Instantiate<InventoryMenu>();
+ Map.MapData.Player.AddChild(InventoryMenu);
+ InventoryMenu.Initialize(Map.MapData.Player.Inventory);
+ InventoryMenu.ItemSelected += OnItemActivate;
+ InventoryMenu.ItemDrop += OnItemDrop;
+ }
+
+ public override void Exit()
+ {
+ ActivationItem = null;
+ DropItem = null;
+ InventoryMenu.QueueFree();
+ }
+
+ public override Action GetAction(Player player)
+ {
+ Action action = null;
+
+ if (ActivationItem != null)
+ {
+ action = new ItemAction(player, ActivationItem);
+ Close();
+ }
+
+ if (DropItem != null)
+ {
+ action = new DropAction(player, DropItem);
+ Close();
+ }
+
+ if (Input.IsActionJustPressed("quit"))
+ {
+ Close();
+ }
+
+ return action;
+ }
+
+ private void Close()
+ {
+ GetParent<InputHandler>().SetInputHandler(InputHandlers.MainGame);
+ }
+
+ private void OnItemActivate(ConsumableItem item)
+ {
+ ActivationItem = item;
+ }
+
+ private void OnItemDrop(ConsumableItem item)
+ {
+ DropItem = item;
+ }
+} \ No newline at end of file
diff --git a/scripts/input/InventoryInputHandler.cs.uid b/scripts/InputHandling/InventoryInputHandler.cs.uid
index b5d0ed9..b5d0ed9 100644
--- a/scripts/input/InventoryInputHandler.cs.uid
+++ b/scripts/InputHandling/InventoryInputHandler.cs.uid
diff --git a/scripts/InputHandling/MainGameInputHandler.cs b/scripts/InputHandling/MainGameInputHandler.cs
new file mode 100644
index 0000000..fe8e573
--- /dev/null
+++ b/scripts/InputHandling/MainGameInputHandler.cs
@@ -0,0 +1,58 @@
+using Godot;
+using TheLegendOfGustav.Entities.Actions;
+using TheLegendOfGustav.Entities.Actors;
+
+namespace TheLegendOfGustav.InputHandling;
+
+/// <summary>
+/// Esquema de controles principal do jogo.
+/// </summary>
+public partial class MainGameInputHandler : BaseInputHandler
+{
+ 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},
+ };
+
+ public override Action GetAction(Player player)
+ {
+ Action action = null;
+
+ foreach (var direction in directions)
+ {
+ if (Input.IsActionJustPressed(direction.Key))
+ {
+ action = new BumpAction(player, direction.Value);
+ }
+ }
+
+ if (Input.IsActionJustPressed("open-inventory"))
+ {
+ GetParent<InputHandler>().SetInputHandler(InputHandlers.Inventory);
+ }
+
+ if (Input.IsActionJustPressed("pick-item"))
+ {
+ GetParent<InputHandler>().SetInputHandler(InputHandlers.Pickup);
+ }
+
+ if (Input.IsActionJustPressed("inspect"))
+ {
+ GetParent<InputHandler>().SetInputHandler(InputHandlers.Inspect);
+ }
+
+ if (Input.IsActionJustPressed("skip-turn"))
+ {
+ action = new WaitAction(player);
+ }
+
+ return action;
+ }
+}
diff --git a/scripts/input/MainGameInputHandler.cs.uid b/scripts/InputHandling/MainGameInputHandler.cs.uid
index 302a3b5..302a3b5 100644
--- a/scripts/input/MainGameInputHandler.cs.uid
+++ b/scripts/InputHandling/MainGameInputHandler.cs.uid
diff --git a/scripts/input/PickupInputHandler.cs b/scripts/InputHandling/PickupInputHandler.cs
index 8f4f9b2..1396b2b 100644
--- a/scripts/input/PickupInputHandler.cs
+++ b/scripts/InputHandling/PickupInputHandler.cs
@@ -1,9 +1,14 @@
using Godot;
+using TheLegendOfGustav.Entities.Actions;
+using TheLegendOfGustav.Entities.Actors;
+
+namespace TheLegendOfGustav.InputHandling;
/// <summary>
/// Esquema de controles para pegar um item.
/// </summary>
-public partial class PickupInputHandler : BaseInputHandler {
+public partial class PickupInputHandler : BaseInputHandler
+{
private readonly Godot.Collections.Dictionary<string, Vector2I> directions = new()
{
{"walk-up", Vector2I.Up},
@@ -16,26 +21,30 @@ public partial class PickupInputHandler : BaseInputHandler {
{"walk-down-left", Vector2I.Down + Vector2I.Left},
{"skip-turn", Vector2I.Zero}
};
- public override Action GetAction(Player player) {
+
+ public override Action GetAction(Player player)
+ {
Action action = null;
- if (player.IsAlive) {
- foreach (var direction in directions) {
- if (Input.IsActionJustPressed(direction.Key)) {
- action = new PickupAction(player, direction.Value);
- Quit();
- }
- }
-
- if (Input.IsActionJustPressed("quit")) {
+ foreach (var direction in directions)
+ {
+ if (Input.IsActionJustPressed(direction.Key))
+ {
+ action = new PickupAction(player, direction.Value);
Quit();
}
}
+ if (Input.IsActionJustPressed("quit"))
+ {
+ Quit();
+ }
+
return action;
}
- private void Quit() {
+ private void Quit()
+ {
GetParent<InputHandler>().SetInputHandler(InputHandlers.MainGame);
}
}
diff --git a/scripts/input/PickupInputHandler.cs.uid b/scripts/InputHandling/PickupInputHandler.cs.uid
index 139dd25..139dd25 100644
--- a/scripts/input/PickupInputHandler.cs.uid
+++ b/scripts/InputHandling/PickupInputHandler.cs.uid
diff --git a/scripts/map/DungeonGenerator.cs b/scripts/Map/DungeonGenerator.cs
index cb89508..9a44d33 100644
--- a/scripts/map/DungeonGenerator.cs
+++ b/scripts/Map/DungeonGenerator.cs
@@ -1,4 +1,9 @@
using Godot;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Entities;
+using TheLegendOfGustav.Entities.Items;
+
+namespace TheLegendOfGustav.Map;
/// <summary>
/// A classe dungeonGenerator cria exatamente um andar da masmorra.
@@ -6,9 +11,10 @@ using Godot;
/// </summary>
public partial class DungeonGenerator : Node
{
+ #region Fields
/// <summary>
- /// Coleção de todos os inimigos que o gerador tem acesso.
- /// </summary>
+ /// Coleção de todos os inimigos que o gerador tem acesso.
+ /// </summary>
private static readonly Godot.Collections.Array<EnemyDefinition> enemies = [
GD.Load<EnemyDefinition>("res://assets/definitions/actor/Skeleton.tres"),
GD.Load<EnemyDefinition>("res://assets/definitions/actor/morcegao.tres"),
@@ -18,104 +24,80 @@ public partial class DungeonGenerator : Node
private static readonly Godot.Collections.Array<ConsumableItemDefinition> items = [
GD.Load<HealingConsumableDefinition>("res://assets/definitions/Items/small_healing_potion.tres")
];
+ #endregion
+ #region Properties
/// <summary>
/// Dimensões do mapa a ser criado.
/// </summary>
[ExportCategory("Dimension")]
[Export]
- private int width = 80;
+ private int Width { get; set; } = 80;
[Export]
- private int height = 60;
+ private int Height { get; set; } = 60;
/// <summary>
/// Gerador de números aleatórios
/// </summary>
[ExportCategory("RNG")]
- private RandomNumberGenerator rng = new();
+ private RandomNumberGenerator Rng { get; set; } = new();
/// <summary>
- /// Qual seed utilizar.
- /// </summary>
+ /// Qual seed utilizar.
+ /// </summary>
[Export]
- private ulong seed;
+ private ulong Seed { get; set; }
/// <summary>
- /// Se será utilizada a nossa seed ou a seed padrão da classe RandomNumberGenerator.
- /// </summary>
+ /// Se será utilizada a nossa seed ou a seed padrão da classe RandomNumberGenerator.
+ /// </summary>
[Export]
- private bool useSeed = true;
+ private bool UseSeed { get; set; } = true;
/// <summary>
- /// Quantas iterações do algoritmo chamar.
- /// </summary>
+ /// Quantas iterações do algoritmo chamar.
+ /// </summary>
[Export]
- private int iterations = 3;
+ private int Iterations { get; set; } = 3;
/// <summary>
- /// Quantidade máxima de inimigos por sala.
- /// </summary>
+ /// Quantidade máxima de inimigos por sala.
+ /// </summary>
[ExportCategory("Monster RNG")]
[Export]
- private int maxMonsterPerRoom = 2;
+ private int MaxMonsterPerRoom { get; set; } = 2;
/// <summary>
- /// Quantidade máxima de itens por sala.
- /// </summary>
+ /// Quantidade máxima de itens por sala.
+ /// </summary>
[ExportCategory("Loot RNG")]
[Export]
- private int maxItemsPerRoom = 2;
+ private int MaxItemsPerRoom { get; set; } = 2;
+ #endregion
+ #region Methods
public override void _Ready()
{
base._Ready();
- if (useSeed) {
- rng.Seed = seed;
- }
- }
-
- /// <summary>
- /// Transforma o tile da posição especificada em chão.
- /// </summary>
- /// <param name="data">o mapa</param>
- /// <param name="pos">posição para colocar o chão.</param>
- private static void CarveTile(MapData data, Vector2I pos)
- {
- Tile tile = data.GetTile(pos);
- if (tile == null) return;
-
- tile.SetDefinition(MapData.floorDefinition);
- }
-
- /// <summary>
- /// Preenche uma área retangular com chão.
- /// </summary>
- /// <param name="data">O mapa</param>
- /// <param name="room">Área para preencher com chão</param>
- private static void CarveRoom(MapData data, Rect2I room)
- {
- for (int y = room.Position.Y; y < room.End.Y; y++)
+ if (UseSeed)
{
- for (int x = room.Position.X; x < room.End.X; x++)
- {
- CarveTile(data, new Vector2I(x, y));
- }
+ Rng.Seed = Seed;
}
}
/// <summary>
- /// Gera um andar da masmorra.
- /// Inimigos são colocados conforme configurações.
- /// O jogador é colocado na primeira sala gerada.
- /// </summary>
- /// <param name="player">Jogador.</param>
- /// <returns>O mapa gerado.</returns>
+ /// Gera um andar da masmorra.
+ /// Inimigos são colocados conforme configurações.
+ /// O jogador é colocado na primeira sala gerada.
+ /// </summary>
+ /// <param name="player">Jogador.</param>
+ /// <returns>O mapa gerado.</returns>
public MapData GenerateDungeon(Player player)
{
- MapData data = new MapData(width, height, player);
+ MapData data = new MapData(Width, Height, player);
// Divisão mestre que engloba o mapa inteiro.
- MapDivision root = new MapDivision(0, 0, width, height);
+ MapDivision root = new MapDivision(0, 0, Width, Height);
// Chama o algoritmo para dividir o mapa.
- root.Split(iterations, rng);
+ root.Split(Iterations, Rng);
bool first = true;
@@ -123,16 +105,16 @@ public partial class DungeonGenerator : Node
TunnelDivisions(data, root);
// Cria as salas com base nas divisões geradas.
- foreach(MapDivision division in root.GetLeaves())
+ foreach (MapDivision division in root.GetLeaves())
{
Rect2I room = new(division.Position, division.Size);
-
+
// A sala não pode oculpar a divisão inteira, senão não haveriam paredes.
room = room.GrowIndividual(
- -rng.RandiRange(1, 2),
- -rng.RandiRange(1, 2),
- -rng.RandiRange(1, 2),
- -rng.RandiRange(1, 2)
+ -Rng.RandiRange(1, 2),
+ -Rng.RandiRange(1, 2),
+ -Rng.RandiRange(1, 2),
+ -Rng.RandiRange(1, 2)
);
// De fato cria a sala.
@@ -153,120 +135,167 @@ public partial class DungeonGenerator : Node
}
/// <summary>
- /// Popula uma sala com inimigos.
- /// </summary>
- /// <param name="data">O mapa</param>
- /// <param name="room">A sala.</param>
- private void PlaceEntities(MapData data, Rect2I room) {
+ /// Transforma o tile da posição especificada em chão.
+ /// </summary>
+ /// <param name="data">o mapa</param>
+ /// <param name="pos">posição para colocar o chão.</param>
+ private static void CarveTile(MapData data, Vector2I pos)
+ {
+ Tile tile = data.GetTile(pos);
+ if (tile == null) return;
+
+ tile.SetDefinition(MapData.floorDefinition);
+ }
+
+ /// <summary>
+ /// Preenche uma área retangular com chão.
+ /// </summary>
+ /// <param name="data">O mapa</param>
+ /// <param name="room">Área para preencher com chão</param>
+ private static void CarveRoom(MapData data, Rect2I room)
+ {
+ for (int y = room.Position.Y; y < room.End.Y; y++)
+ {
+ for (int x = room.Position.X; x < room.End.X; x++)
+ {
+ CarveTile(data, new Vector2I(x, y));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Preenche uma linha horizontal com chão.
+ /// </summary>
+ /// <param name="data">O mapa</param>
+ /// <param name="y">Eixo y do corredor.</param>
+ /// <param name="xBegin">Início do corredor</param>
+ /// <param name="xEnd">Final do corredor.</param>
+ private static void HorizontalCorridor(MapData data, int y, int xBegin, int xEnd)
+ {
+ int begin = (xBegin < xEnd) ? xBegin : xEnd;
+ int end = (xEnd > xBegin) ? xEnd : xBegin;
+ for (int i = begin; i <= end; i++)
+ {
+ CarveTile(data, new Vector2I(i, y));
+ }
+ }
+
+ /// <summary>
+ /// Cria recursivamente corredores entre o centro de cada divisão do mapa.
+ /// </summary>
+ /// <param name="data">O mapa</param>
+ /// <param name="root">Divisão mestre.</param>
+ private static void TunnelDivisions(MapData data, MapDivision root)
+ {
+ if (root.IsLeaf)
+ {
+ return;
+ }
+
+ TunnelBetween(data, root.Right.Center, root.Left.Center);
+ TunnelDivisions(data, root.Left);
+ TunnelDivisions(data, root.Right);
+ }
+
+ /// <summary>
+ /// Preenche uma linha vertical com chão.
+ /// </summary>
+ /// <param name="data">O mapa.</param>
+ /// <param name="x">Eixo x do corredor.</param>
+ /// <param name="yBegin">Início do corredor</param>
+ /// <param name="yEnd">Final do corredor.</param>
+ private static void VerticalCorridor(MapData data, int x, int yBegin, int yEnd)
+ {
+ int begin = (yBegin < yEnd) ? yBegin : yEnd;
+ int end = (yEnd > yBegin) ? yEnd : yBegin;
+ for (int i = begin; i <= end; i++)
+ {
+ CarveTile(data, new Vector2I(x, i));
+ }
+ }
+
+ /// <summary>
+ /// Cria corredores vertical e horizontal para unir dois pontos no mapa.
+ /// </summary>
+ /// <param name="data">O mapa</param>
+ /// <param name="start">Ponto inicial</param>
+ /// <param name="end">Ponto final.</param>
+ private static void TunnelBetween(MapData data, Vector2I start, Vector2I end)
+ {
+ HorizontalCorridor(data, start.Y, start.X, end.X);
+ VerticalCorridor(data, end.X, start.Y, end.Y);
+ }
+
+ /// <summary>
+ /// Popula uma sala com inimigos.
+ /// </summary>
+ /// <param name="data">O mapa</param>
+ /// <param name="room">A sala.</param>
+ private void PlaceEntities(MapData data, Rect2I room)
+ {
// Define quantos monstros serão colocados na sala
- int monsterAmount = rng.RandiRange(0, maxMonsterPerRoom);
+ int monsterAmount = Rng.RandiRange(0, MaxMonsterPerRoom);
// Define quantos itens serão colocados na sala.
- int itemAmount = rng.RandiRange(0, maxItemsPerRoom);
+ int itemAmount = Rng.RandiRange(0, MaxItemsPerRoom);
- for (int i = 0; i < monsterAmount; i++) {
+ for (int i = 0; i < monsterAmount; i++)
+ {
// Escolhe um lugar aleatório na sala.
Vector2I position = new(
- rng.RandiRange(room.Position.X, room.End.X - 1),
- rng.RandiRange(room.Position.Y, room.End.Y - 1)
+ Rng.RandiRange(room.Position.X, room.End.X - 1),
+ Rng.RandiRange(room.Position.Y, room.End.Y - 1)
);
// Só podemos colocar um ator por ponto no espaço.
bool canPlace = true;
- foreach (Entity entity in data.Entities) {
- if (entity.GridPosition == position) {
+ foreach (Entity entity in data.Entities)
+ {
+ if (entity.GridPosition == position)
+ {
canPlace = false;
break;
}
}
// Se possível, criamos um inimigo aleatório na posição escolhida.
- if (canPlace) {
+ if (canPlace)
+ {
EnemyDefinition definition = enemies.PickRandom();
Enemy enemy = new(position, data, definition);
data.InsertEntity(enemy);
}
}
- for (int i = 0; i < itemAmount; i++) {
+ for (int i = 0; i < itemAmount; i++)
+ {
// Escolhe um lugar aleatório na sala.
Vector2I position = new(
- rng.RandiRange(room.Position.X, room.End.X - 1),
- rng.RandiRange(room.Position.Y, room.End.Y - 1)
+ Rng.RandiRange(room.Position.X, room.End.X - 1),
+ Rng.RandiRange(room.Position.Y, room.End.Y - 1)
);
// Só podemos colocar um ator por ponto no espaço.
bool canPlace = true;
- foreach (Entity entity in data.Entities) {
- if (entity.GridPosition == position) {
+ foreach (Entity entity in data.Entities)
+ {
+ if (entity.GridPosition == position)
+ {
canPlace = false;
break;
}
}
// Se possível, criamos um inimigo aleatório na posição escolhida.
- if (canPlace) {
+ if (canPlace)
+ {
ConsumableItemDefinition definition = items.PickRandom();
- if (definition is HealingConsumableDefinition hcDefinition) {
+ if (definition is HealingConsumableDefinition hcDefinition)
+ {
HealingConsumable item = new(position, data, hcDefinition);
data.InsertEntity(item);
}
}
}
}
-
- /// <summary>
- /// Preenche uma linha horizontal com chão.
- /// </summary>
- /// <param name="data">O mapa</param>
- /// <param name="y">Eixo y do corredor.</param>
- /// <param name="xBegin">Início do corredor</param>
- /// <param name="xEnd">Final do corredor.</param>
- private static void HorizontalCorridor(MapData data, int y, int xBegin, int xEnd) {
- int begin = (xBegin < xEnd) ? xBegin : xEnd;
- int end = (xEnd > xBegin) ? xEnd : xBegin;
- for (int i = begin; i <= end; i++) {
- CarveTile(data, new Vector2I(i, y));
- }
- }
-
- /// <summary>
- /// Preenche uma linha vertical com chão.
- /// </summary>
- /// <param name="data">O mapa.</param>
- /// <param name="x">Eixo x do corredor.</param>
- /// <param name="yBegin">Início do corredor</param>
- /// <param name="yEnd">Final do corredor.</param>
- private static void VerticalCorridor(MapData data, int x, int yBegin, int yEnd) {
- int begin = (yBegin < yEnd) ? yBegin : yEnd;
- int end = (yEnd > yBegin) ? yEnd : yBegin;
- for (int i = begin; i <= end; i++) {
- CarveTile(data, new Vector2I(x, i));
- }
- }
-
- /// <summary>
- /// Cria corredores vertical e horizontal para unir dois pontos no mapa.
- /// </summary>
- /// <param name="data">O mapa</param>
- /// <param name="start">Ponto inicial</param>
- /// <param name="end">Ponto final.</param>
- private static void TunnelBetween(MapData data, Vector2I start, Vector2I end) {
- HorizontalCorridor(data, start.Y, start.X, end.X);
- VerticalCorridor(data, end.X, start.Y, end.Y);
- }
-
- /// <summary>
- /// Cria recursivamente corredores entre o centro de cada divisão do mapa.
- /// </summary>
- /// <param name="data">O mapa</param>
- /// <param name="root">Divisão mestre.</param>
- private static void TunnelDivisions(MapData data, MapDivision root) {
- if (root.IsLeaf) {
- return;
- }
-
- TunnelBetween(data, root.Right.Center, root.Left.Center);
- TunnelDivisions(data, root.Left);
- TunnelDivisions(data, root.Right);
- }
+ #endregion
}
diff --git a/scripts/map/DungeonGenerator.cs.uid b/scripts/Map/DungeonGenerator.cs.uid
index 15aeef6..15aeef6 100644
--- a/scripts/map/DungeonGenerator.cs.uid
+++ b/scripts/Map/DungeonGenerator.cs.uid
diff --git a/scripts/map/FieldOfView.cs b/scripts/Map/FieldOfView.cs
index bdcd206..40df320 100644
--- a/scripts/map/FieldOfView.cs
+++ b/scripts/Map/FieldOfView.cs
@@ -1,21 +1,26 @@
using Godot;
+namespace TheLegendOfGustav.Map;
+
// Copiado e adaptado deste cara aqui: https://www.roguebasin.com/index.php?title=C%2B%2B_shadowcasting_implementation e deste também https://selinadev.github.io/08-rogueliketutorial-04/
// Eu não vou mentir, li como o algoritmo funciona, mas mesmo assim não entendi.
-public partial class FieldOfView : Node {
+public partial class FieldOfView : Node
+{
private Godot.Collections.Array<Tile> fov = [];
- private static int[,] multipliers = new int[4,8]{
+ private static readonly int[,] multipliers = new int[4, 8]{
{1, 0, 0, -1, -1, 0, 0, 1},
{0, 1, -1, 0, 0, -1, 1, 0},
{0, 1, 1, 0, 0, -1, -1, 0},
{1, 0, 0, 1, -1, 0, 0, -1}
};
- private void CastLight(MapData data, Vector2I pos, int radius, int row, float startSlope, float endSlope, int xx, int xy, int yx, int yy) {
- if (startSlope < endSlope) {
+ private void CastLight(MapData data, Vector2I pos, int radius, int row, float startSlope, float endSlope, int xx, int xy, int yx, int yy)
+ {
+ if (startSlope < endSlope)
+ {
return;
}
@@ -40,56 +45,70 @@ public partial class FieldOfView : Node {
int sax = dx * xx + dy * xy;
int say = dx * yx + dy * yy;
- if ((sax < 0 && int.Abs(sax) > pos.X) || (say < 0 && int.Abs(say) > pos.Y)) {
+ if ((sax < 0 && int.Abs(sax) > pos.X) || (say < 0 && int.Abs(say) > pos.Y))
+ {
continue;
}
int ax = pos.X + sax;
int ay = pos.Y + say;
- if (ax >= data.Width || ay >= data.Height) {
+ if (ax >= data.Width || ay >= data.Height)
+ {
continue;
}
Tile currentTile = data.GetTile(ax, ay);
int radius2 = radius * radius;
- if ((dx * dx + dy * dy) < radius2) {
+ if ((dx * dx + dy * dy) < radius2)
+ {
currentTile.IsInView = true;
fov.Add(currentTile);
}
- if (blocked) {
- if (!currentTile.IsTransparent) {
+ if (blocked)
+ {
+ if (!currentTile.IsTransparent)
+ {
nextStartSlope = rSlope;
continue;
- } else {
+ }
+ else
+ {
blocked = false;
startSlope = nextStartSlope;
}
- } else if (!currentTile.IsTransparent) {
+ }
+ else if (!currentTile.IsTransparent)
+ {
blocked = true;
nextStartSlope = rSlope;
CastLight(data, pos, radius, i + 1, startSlope, lSlope, xx, xy, yx, yy);
}
}
- if (blocked) {
+ if (blocked)
+ {
break;
}
}
}
- private void ClearFOV() {
- foreach (Tile tile in fov) {
+ private void ClearFOV()
+ {
+ foreach (Tile tile in fov)
+ {
tile.IsInView = false;
}
fov.Clear();
}
- public void UpdateFOV(MapData data, Vector2I position, int radius) {
+ public void UpdateFOV(MapData data, Vector2I position, int radius)
+ {
ClearFOV();
Tile start = data.GetTile(position);
start.IsInView = true;
fov.Add(start);
- for (int i = 0; i < 8; i++) {
+ for (int i = 0; i < 8; i++)
+ {
CastLight(data, position, radius, 1, 1.0f, 0.0f, multipliers[0, i], multipliers[1, i], multipliers[2, i], multipliers[3, i]);
}
}
diff --git a/scripts/map/FieldOfView.cs.uid b/scripts/Map/FieldOfView.cs.uid
index a173ff3..a173ff3 100644
--- a/scripts/map/FieldOfView.cs.uid
+++ b/scripts/Map/FieldOfView.cs.uid
diff --git a/scripts/Map/Map.cs b/scripts/Map/Map.cs
new file mode 100644
index 0000000..9117bc5
--- /dev/null
+++ b/scripts/Map/Map.cs
@@ -0,0 +1,98 @@
+using Godot;
+using TheLegendOfGustav.Entities;
+using TheLegendOfGustav.Entities.Actors;
+
+namespace TheLegendOfGustav.Map;
+
+/// <summary>
+/// A parte visual do mapa.
+/// </summary>
+public partial class Map : Node2D
+{
+ /// <summary>
+ /// Dados do mapa.
+ /// </summary>
+ public MapData MapData { get; private set; }
+
+ /// <summary>
+ /// raio de alcance da visão do jogador.
+ /// </summary>
+ [Export]
+ private int FovRadius { get; set; } = 12;
+
+ /// <summary>
+ /// Gerador de mapas.
+ /// </summary>
+ private DungeonGenerator Generator { get; set; }
+
+ FieldOfView FieldOfView { get; set; }
+
+ private Node2D TilesNode { get; set; }
+ private Node2D EntitiesNode { get; set; }
+
+ public override void _Ready()
+ {
+ base._Ready();
+ // Começamos obtendo nós relevantes para o mapa.
+ Generator = GetNode<DungeonGenerator>("Generator");
+ FieldOfView = GetNode<FieldOfView>("FieldOfView");
+ TilesNode = GetNode<Node2D>("Tiles");
+ EntitiesNode = GetNode<Node2D>("Entities");
+ }
+
+ /// <summary>
+ /// Cria um andar da masmorra utilizando o gerador de mapa.
+ /// </summary>
+ /// <param name="player">O gerador de mapas precisa do jogador.</param>
+ public void Generate(Player player)
+ {
+ MapData = Generator.GenerateDungeon(player);
+
+ MapData.EntityPlaced += OnEntityPlaced;
+
+ PlaceTiles();
+ PlaceEntities();
+ }
+
+ /// <summary>
+ /// Atualiza o campo de visão do mapa com base em uma coordenada.
+ /// </summary>
+ /// <param name="pos">Centro de visão, normalmente é a posição do jogador.</param>
+ public void UpdateFOV(Vector2I pos)
+ {
+ FieldOfView.UpdateFOV(MapData, pos, FovRadius);
+ // Esconde ou revela entidades com base no campo de visão.
+ foreach (Entity entity in MapData.Entities)
+ {
+ entity.Visible = MapData.GetTile(entity.GridPosition).IsInView;
+ }
+ }
+
+
+ /// <summary>
+ /// Coloca todos os tiles do mapa no mundo do jogo.
+ /// </summary>
+ private void PlaceTiles()
+ {
+ foreach (Tile tile in MapData.Tiles)
+ {
+ TilesNode.AddChild(tile);
+ }
+ }
+
+ /// <summary>
+ /// Coloca todas as entidades do mapa no mundo do jogo.
+ /// </summary>
+ private void PlaceEntities()
+ {
+ foreach (Entity entity in MapData.Entities)
+ {
+ EntitiesNode.AddChild(entity);
+ }
+ }
+
+ private void OnEntityPlaced(Entity entity)
+ {
+ EntitiesNode.AddChild(entity);
+ }
+}
diff --git a/scripts/map/Map.cs.uid b/scripts/Map/Map.cs.uid
index 7306888..7306888 100644
--- a/scripts/map/Map.cs.uid
+++ b/scripts/Map/Map.cs.uid
diff --git a/scripts/Map/MapData.cs b/scripts/Map/MapData.cs
new file mode 100644
index 0000000..3365380
--- /dev/null
+++ b/scripts/Map/MapData.cs
@@ -0,0 +1,318 @@
+using Godot;
+using TheLegendOfGustav.Entities;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Entities.Items;
+
+namespace TheLegendOfGustav.Map;
+
+/// <summary>
+/// Classe que cuida dos dados e da parte lógica do mapa.
+/// O mapa é o cenário onde as ações do jogo ocorrem.
+/// Mais especificamente, o mapa é um único andar da masmorra.
+/// </summary>
+public partial class MapData : RefCounted
+{
+ #region Fields
+ public static readonly TileDefinition wallDefinition = GD.Load<TileDefinition>("res://assets/definitions/tiles/wall.tres");
+ public static readonly TileDefinition floorDefinition = GD.Load<TileDefinition>("res://assets/definitions/tiles/floor.tres");
+ /// <summary>
+ /// Peso do ator no pathfinder.
+ /// A IA irá evitar de passar por espaços com peso alto.
+ /// </summary>
+ private static readonly float EntityWeight = 10.0f;
+ #endregion
+
+ #region Constructor
+ public MapData(int width, int height, Player player)
+ {
+ Width = width;
+ Height = height;
+
+ Player = player;
+ // Como o jogador é criado antes do mapa, precisamos
+ // atualizá-lo com o novo mapa.
+ player.MapData = this;
+ InsertEntity(player);
+
+ SetupTiles();
+ }
+ #endregion
+
+ #region Signals
+ [Signal]
+ public delegate void EntityPlacedEventHandler(Entity entity);
+ #endregion
+
+ #region Properties
+ /// <summary>
+ /// Largura do mapa.
+ /// </summary>
+ public int Width { get; private set; }
+ /// <summary>
+ /// Altura do mapa.
+ /// </summary>
+ public int Height { get; private set; }
+
+ /// <summary>
+ /// Os tiles que compõem o mapa.
+ /// </summary>
+ public Godot.Collections.Array<Tile> Tiles { get; private set; } = [];
+
+ /// <summary>
+ /// O jogador é especial e por isso o mapa faz questão de rastreá-lo.
+ /// </summary>
+ public Player Player { get; set; }
+ /// <summary>
+ /// Lista de todas as entidades dentro do mapa.
+ /// </summary>
+ public Godot.Collections.Array<Entity> Entities { get; private set; } = [];
+
+ /// <summary>
+ /// Lista de todos os itens dentro do mapa.
+ /// </summary>
+ public Godot.Collections.Array<ConsumableItem> Items
+ {
+ get
+ {
+ Godot.Collections.Array<ConsumableItem> list = [];
+ foreach (Entity entity in Entities)
+ {
+ if (entity is ConsumableItem item)
+ {
+ list.Add(item);
+ }
+ }
+ return list;
+ }
+ }
+ /// <summary>
+ /// Objeto do Godot que utiliza do algoritmo A* para calcular
+ /// caminhos e rotas.
+ /// </summary>
+ public AStarGrid2D Pathfinder { get; private set; }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Inicializa o pathfinder;
+ /// </summary>
+ public void SetupPathfinding()
+ {
+ Pathfinder = new AStarGrid2D
+ {
+ //A região é o mapa inteiro.
+ Region = new Rect2I(0, 0, Width, Height)
+ };
+
+ // Atualiza o pathfinder para a região definida.
+ Pathfinder.Update();
+
+ // Define quais pontos do mapa são passáveis ou não.
+ for (int y = 0; y < Height; y++)
+ {
+ for (int x = 0; x < Width; x++)
+ {
+ Vector2I pos = new Vector2I(x, y);
+ Tile tile = GetTile(pos);
+ // Pontos sólidos são impossíveis de passar.
+ Pathfinder.SetPointSolid(pos, !tile.IsWalkable);
+ }
+ }
+
+ // Registra todos os atores em cena.
+ foreach (Entity entity in Entities)
+ {
+ if (entity.BlocksMovement)
+ {
+ RegisterBlockingEntity(entity);
+ }
+ }
+
+ }
+
+ /// <summary>
+ /// Define um peso na posição de uma entidade para que a IA evite de passar por lá.
+ /// Ênfase em evitar. Se o único caminho para o destino estiver bloqueado
+ /// por uma entidade, o jogo tentará andar mesmo assim.
+ /// </summary>
+ /// <param name="entity">A entidade em questão.</param>
+ public void RegisterBlockingEntity(Entity entity)
+ {
+ Pathfinder.SetPointWeightScale(entity.GridPosition, EntityWeight);
+ }
+
+ /// <summary>
+ /// Remove o peso na posição de uma entidade.
+ /// Quando uma entidade move sua posição, devemos tirar o peso de sua posição anterior.
+ /// </summary>
+ /// <param name="entity">A entidade em questão.</param>
+ public void UnregisterBlockingEntity(Entity entity)
+ {
+ Pathfinder.SetPointWeightScale(entity.GridPosition, 0);
+ }
+
+ /// <summary>
+ /// Registra uma entidade no mapa. A existência de uma entidade não é considerada se ela não
+ /// estiver registrada no mapa.
+ /// </summary>
+ /// <param name="entity">A entidade em questão</param>
+ public void InsertEntity(Entity entity)
+ {
+ Entities.Add(entity);
+ }
+
+ /// <summary>
+ /// Obtém o tile na posição desejada.
+ /// </summary>
+ /// <param name="pos">Vetor posição</param>
+ /// <returns>O tile na posição, nulo se for fora do mapa.</returns>
+ public Tile GetTile(Vector2I pos)
+ {
+ int index = GridToIndex(pos);
+
+ if (index < 0) return null;
+
+ return Tiles[index];
+ }
+
+ /// <summary>
+ /// Obtém o tile na posição desejada.
+ /// </summary>
+ /// <param name="x">x da coordenada</param>
+ /// <param name="y">y da coordenada</param>
+ /// <returns>O tile na posição, nulo se for fora do mapa.</returns>
+ public Tile GetTile(int x, int y)
+ {
+ return GetTile(new Vector2I(x, y));
+ }
+ /// <summary>
+ /// Obtém a entidade na posição especificada.
+ /// </summary>
+ /// <param name="pos">Vetor posição</param>
+ /// <returns>A entidade na posição especificada, nulo se não houver.</returns>
+ public Entity GetBlockingEntityAtPosition(Vector2I pos)
+ {
+ foreach (Entity entity in Entities)
+ {
+ if (entity.GridPosition == pos && entity.BlocksMovement)
+ {
+ return entity;
+ }
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Obtém o primeiro item na posição especificada.
+ /// </summary>
+ /// <param name="pos">Posição</param>
+ /// <returns>O primeiro item na posição, nulo se não houver.</returns>
+ public ConsumableItem GetFirstItemAtPosition(Vector2I pos)
+ {
+ foreach (ConsumableItem item in Items)
+ {
+ if (item.GridPosition == pos)
+ {
+ return item;
+ }
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Remove uma entidade do mapa sem dar free.
+ /// </summary>
+ /// <param name="entity">A entidade para remover</param>
+ public void RemoveEntity(Entity entity)
+ {
+ // Eu removo a entidade do nó de entidades.
+ entity.GetParent().RemoveChild(entity);
+ // Eu removo a entidade da lista de entidades do mapa.
+ Entities.Remove(entity);
+ }
+
+ /// <summary>
+ /// Obtém todas as entidades na posição especificada.
+ /// É possível haver mais de uma entidade na mesma posição se uma delas não bloquear movimento.
+ /// </summary>
+ /// <param name="pos">Vetor posição</param>
+ /// <returns>Lista com todas as entidades na posição especificada.</returns>
+ public Godot.Collections.Array<Entity> GetEntitiesAtPosition(Vector2I pos)
+ {
+ Godot.Collections.Array<Entity> ZOfZero = [];
+ Godot.Collections.Array<Entity> ZOfOne = [];
+ Godot.Collections.Array<Entity> ZOfTwo = [];
+
+ // Pego todos os atores
+ foreach (Entity entity in Entities)
+ {
+ if (entity.GridPosition == pos)
+ {
+ switch (entity.ZIndex)
+ {
+ case 0:
+ ZOfZero.Add(entity);
+ break;
+ case 1:
+ ZOfOne.Add(entity);
+ break;
+ case 2:
+ ZOfTwo.Add(entity);
+ break;
+ }
+ }
+ }
+
+ // Retorno os atores ordenados por ZIndex.
+ return ZOfZero + ZOfOne + ZOfTwo;
+ }
+
+ /// <summary>
+ /// Cria novos Tiles até preencher as dimensões do mapa.
+ /// É importante que estes tiles sejam paredes, o gerador de mapas
+ /// não cria paredes por conta própria.
+ /// </summary>
+ private void SetupTiles()
+ {
+ for (int i = 0; i < Height; i++)
+ {
+ for (int j = 0; j < Width; j++)
+ {
+ Tiles.Add(new Tile(new Vector2I(j, i), wallDefinition));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Converte uma coordenada em um índice para acessar a lista de tiles.
+ /// </summary>
+ /// <param name="pos">Vetor posição</param>
+ /// <returns>Índice na lista de tiles. -1 se estiver fora do mapa.</returns>
+ private int GridToIndex(Vector2I pos)
+ {
+ if (!IsInBounds(pos)) return -1;
+
+ return pos.Y * Width + pos.X;
+ }
+
+ /// <summary>
+ /// Se uma coordenada está dentro da área do mapa.
+ /// </summary>
+ /// <param name="pos">Vetor posição</param>
+ /// <returns>Se o vetor está dentro do mapa.</returns>
+ private bool IsInBounds(Vector2I pos)
+ {
+ if (pos.X < 0 || pos.Y < 0)
+ {
+ return false;
+ }
+ if (pos.X >= Width || pos.Y >= Height)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ #endregion
+}
diff --git a/scripts/map/MapData.cs.uid b/scripts/Map/MapData.cs.uid
index 6c226e7..6c226e7 100644
--- a/scripts/map/MapData.cs.uid
+++ b/scripts/Map/MapData.cs.uid
diff --git a/scripts/Map/MapDivision.cs b/scripts/Map/MapDivision.cs
new file mode 100644
index 0000000..e50b331
--- /dev/null
+++ b/scripts/Map/MapDivision.cs
@@ -0,0 +1,113 @@
+using Godot;
+
+namespace TheLegendOfGustav.Map;
+
+/// <summary>
+/// Classe utilizada pelo gerador de mapas.
+/// Uma divisão é uma região retangular de espaço que pode
+/// conter dentro de si duas regiões menores *ou* uma sala.
+/// Uma divisão é uma árvore binária que possui espaço para salas em suas folhas.
+/// </summary>
+public partial class MapDivision : RefCounted
+{
+ public MapDivision(Vector2I position, Vector2I size)
+ {
+ Position = position;
+ Size = size;
+ }
+
+ public MapDivision(Vector2I position, int width, int height)
+ {
+ Position = position;
+ Size = new(width, height);
+ }
+
+ public MapDivision(int x, int y, int width, int height)
+ {
+ Position = new(x, y);
+ Size = new(width, height);
+ }
+
+ /// <summary>
+ /// Região retangular da divisão.
+ /// </summary>
+ public Vector2I Position { get; set; }
+ public Vector2I Size { get; set; }
+
+ public Vector2I Center
+ {
+ get => new(Position.X + Size.X / 2, Position.Y + Size.Y / 2);
+ }
+
+ /// <summary>
+ /// Filhos da árvore
+ /// </summary>
+ public MapDivision Left { get; private set; }
+ public MapDivision Right { get; private set; }
+
+ /// <summary>
+ /// Se a divisão atual for uma folha.
+ /// As folhas representam salas.
+ /// </summary>
+ public bool IsLeaf
+ {
+ get => Left == null && Right == null;
+ }
+
+ /// <summary>
+ /// É conveniente ter acesso à todas as folhas da árvore.
+ /// </summary>
+ /// <returns>Lista com todas as folhas da árvore.</returns>
+ public Godot.Collections.Array<MapDivision> GetLeaves()
+ {
+ if (IsLeaf)
+ {
+ Godot.Collections.Array<MapDivision> list = [];
+ list.Add(this);
+ return list;
+ }
+ return Left.GetLeaves() + Right.GetLeaves();
+ }
+
+ /// <summary>
+ /// Algoritmo para gerar as divisões.
+ /// O mapa começa com uma única divisão que oculpa sua extensão completa.
+ /// Depois disso, ela se dividirá recursivamente n vezes.
+ /// As divisões nas folhas representam espaços onde pode gerar uma sala.
+ /// </summary>
+ /// <param name="iterations">Número de iterações</param>
+ /// <param name="rng">Gerador de números</param>
+ public void Split(int iterations, RandomNumberGenerator rng)
+ {
+ float SplitRatio = rng.RandfRange(0.35f, 0.65f);
+ bool horizontalSplit = Size.X <= Size.Y;
+
+ // Eu defini um limite mínimo de 4 de altura e 4 de largura para divisões.
+
+ if (horizontalSplit)
+ {
+ int leftHeight = (int)(Size.Y * SplitRatio);
+ if (leftHeight > 4 && Size.Y - leftHeight > 4)
+ {
+ Left = new MapDivision(Position, Size.X, leftHeight);
+ Right = new MapDivision(Position.X, Position.Y + leftHeight, Size.X, Size.Y - leftHeight);
+ }
+ }
+ else
+ {
+ int leftWidth = (int)(Size.Y * SplitRatio);
+
+ if (leftWidth > 4 && Size.Y - leftWidth > 4)
+ {
+ Left = new MapDivision(Position, leftWidth, Size.Y);
+ Right = new MapDivision(Position.X + leftWidth, Position.Y, Size.X - leftWidth, Size.Y);
+ }
+ }
+
+ if (iterations > 1)
+ {
+ Left?.Split(iterations - 1, rng);
+ Right?.Split(iterations - 1, rng);
+ }
+ }
+} \ No newline at end of file
diff --git a/scripts/map/MapDivision.cs.uid b/scripts/Map/MapDivision.cs.uid
index fdd53a4..fdd53a4 100644
--- a/scripts/map/MapDivision.cs.uid
+++ b/scripts/Map/MapDivision.cs.uid
diff --git a/scripts/map/Tile.cs b/scripts/Map/Tile.cs
index 39f7486..e721fca 100644
--- a/scripts/map/Tile.cs
+++ b/scripts/Map/Tile.cs
@@ -1,5 +1,7 @@
using Godot;
-using System;
+using TheLegendOfGustav.Utils;
+
+namespace TheLegendOfGustav.Map;
/// <summary>
/// O mundo do jogo é composto por Tiles.
@@ -9,70 +11,77 @@ using System;
/// </summary>
public partial class Tile : Sprite2D
{
- /// <summary>
- /// A definição do tile carrega seus valores padrão.
- /// </summary>
- private TileDefinition definition;
+ private bool isExplored = false;
+ private bool isInView = false;
+
+ public Tile(Vector2I pos, TileDefinition definition)
+ {
+ // Tile herda da classe Sprite2D.
+ // Por padrão, a posição do Sprite2D é no centro de sua textura.
+ // Para o jogo, faz mais sentido que a posição seja no
+ // canto superior esquerdo.
+ Centered = false;
+ // Tiles começam invisíveis porque não foram vistos pelo jogador.
+ Visible = false;
+ Position = Grid.GridToWorld(pos);
+ SetDefinition(definition);
+ }
/// <summary>
- /// Determina se atores podem andar em cima do Tile.
- /// </summary>
+ /// Determina se atores podem andar em cima do Tile.
+ /// </summary>
public bool IsWalkable { get; private set; }
/// <summary>
- /// Determina se o tile bloqueia visão.
- /// </summary>
+ /// Determina se o tile bloqueia visão.
+ /// </summary>
public bool IsTransparent { get; private set; }
- private bool isExplored = false;
/// <summary>
- /// Se o jogador já viu este tile antes.
- /// Tiles não descobertos são invisíveis.
- /// </summary>
- public bool IsExplored {
- get => this.isExplored;
- set {
+ /// A definição do tile carrega seus valores padrão.
+ /// </summary>
+ private TileDefinition Definition { get; set; }
+ /// <summary>
+ /// Se o jogador já viu este tile antes.
+ /// Tiles não descobertos são invisíveis.
+ /// </summary>
+ public bool IsExplored
+ {
+ get => isExplored;
+ set
+ {
isExplored = value;
- if (IsExplored && !Visible) {
+ if (IsExplored && !Visible)
+ {
Visible = true;
}
}
}
- private bool isInView = false;
/// <summary>
- /// Se o jogador vê o tile neste exato momento.
- /// Elementos neste tile estão dentro do campo de visão do jogador.
- /// </summary>
- public bool IsInView {
+ /// Se o jogador vê o tile neste exato momento.
+ /// Elementos neste tile estão dentro do campo de visão do jogador.
+ /// </summary>
+ public bool IsInView
+ {
get => isInView;
- set {
+ set
+ {
isInView = value;
- Modulate = isInView ? definition.LitColor : definition.DarkColor;
- if (IsInView && !IsExplored) {
+ Modulate = isInView ? Definition.LitColor : Definition.DarkColor;
+ if (IsInView && !IsExplored)
+ {
IsExplored = true;
}
}
}
- public Tile(Vector2I pos, TileDefinition definition)
- {
- // Tile herda da classe Sprite2D.
- // Por padrão, a posição do Sprite2D é no centro de sua textura.
- // Para o jogo, faz mais sentido que a posição seja no
- // canto superior esquerdo.
- Centered = false;
- // Tiles começam invisíveis porque não foram vistos pelo jogador.
- Visible = false;
- Position = Grid.GridToWorld(pos);
- SetDefinition(definition);
- }
-
/// <summary>
- /// Define as características do tile.
- /// </summary>
- /// <param name="definition">Definição do tile.</param>
- public void SetDefinition(TileDefinition definition) {
- this.definition = definition;
+ /// Define as características do tile.
+ /// </summary>
+ /// <param name="definition">Definição do tile.</param>
+ public void SetDefinition(TileDefinition definition)
+ {
+ Definition = definition;
Modulate = definition.DarkColor;
Texture = definition.Texture;
IsWalkable = definition.IsWalkable;
diff --git a/scripts/map/Tile.cs.uid b/scripts/Map/Tile.cs.uid
index 9fa3c81..9fa3c81 100644
--- a/scripts/map/Tile.cs.uid
+++ b/scripts/Map/Tile.cs.uid
diff --git a/scripts/map/TileDefinition.cs b/scripts/Map/TileDefinition.cs
index 84e5cc1..e843b52 100644
--- a/scripts/map/TileDefinition.cs
+++ b/scripts/Map/TileDefinition.cs
@@ -1,5 +1,7 @@
using Godot;
+namespace TheLegendOfGustav.Map;
+
/// <summary>
/// Define as características de um tile.
/// </summary>
diff --git a/scripts/map/TileDefinition.cs.uid b/scripts/Map/TileDefinition.cs.uid
index 14f2903..14f2903 100644
--- a/scripts/map/TileDefinition.cs.uid
+++ b/scripts/Map/TileDefinition.cs.uid
diff --git a/scripts/Time/TurnManager.cs b/scripts/Time/TurnManager.cs
index 01efc18..9713fea 100644
--- a/scripts/Time/TurnManager.cs
+++ b/scripts/Time/TurnManager.cs
@@ -1,86 +1,98 @@
using Godot;
+using TheLegendOfGustav.Map;
+using TheLegendOfGustav.Entities.Actors;
+using TheLegendOfGustav.Entities;
+using TheLegendOfGustav.Entities.Actions;
+
+namespace TheLegendOfGustav.Time;
/// <summary>
/// Gerenciador de turnos, o senhor do tempo.
/// </summary>
-public partial class TurnManager : RefCounted
+public partial class TurnManager(Map.Map map) : RefCounted
{
public int TurnCount { get; private set; } = 0;
- private Map map;
- private MapData Map_Data { get => map.Map_Data; }
+ private Map.Map Map { get; set; } = map;
+ private MapData Map_Data { get => Map.MapData; }
private Player Player { get => Map_Data.Player; }
/// <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 = [];
-
- public TurnManager(Map map) {
- this.map = map;
- }
+ /// 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 { get; set; } = [];
/// <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);
+ /// 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() {
+ /// 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) {
+ 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) {
+ if (Player.Energy <= 0)
+ {
StartTurn();
}
- bool actionResult = true;;
+ 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);
+ if (PlayerActionQueue.Count > 0 && Player.Energy > 0)
+ {
+ Action action = PlayerActionQueue[0];
+ PlayerActionQueue.RemoveAt(0);
actionResult = action.Perform();
}
// 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) {
+ if (actionResult && Player.Energy <= 0)
+ {
// Depois computamos os turnos dos outros atores.
HandleEnemyTurns();
- map.UpdateFOV(Player.GridPosition);
+ 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);
+ if (Player.GridPosition != previousPlayerPos)
+ {
+ Map.UpdateFOV(Player.GridPosition);
}
}
/// <summary>
- /// Método executado no início do turno.
- /// </summary>
- private void StartTurn() {
+ /// 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) {
+ foreach (Entity entity in Map_Data.Entities)
+ {
+ if (entity is Actor actor && actor.IsAlive)
+ {
actor.RechargeEnergy();
}
}
@@ -89,14 +101,18 @@ public partial class TurnManager : RefCounted
/// <summary>
/// Executa turnos para cada ator no mapa.
/// </summary>
- private void HandleEnemyTurns() {
- foreach (Entity entity in Map_Data.Entities) {
+ 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) {
+ if (entity is Enemy enemy && enemy.IsAlive)
+ {
// O inimigo poderá fazer quantos turnos sua energia deixar.
- while (enemy.Energy > 0) {
+ while (enemy.Energy > 0)
+ {
enemy.Soul.Perform();
}
}
diff --git a/scripts/Utils/Grid.cs b/scripts/Utils/Grid.cs
index 271f559..b744c60 100644
--- a/scripts/Utils/Grid.cs
+++ b/scripts/Utils/Grid.cs
@@ -1,5 +1,6 @@
using Godot;
-using System;
+
+namespace TheLegendOfGustav.Utils;
/// <summary>
/// Classe utilitária para converter coordenadas da malha dos tiles
@@ -7,14 +8,17 @@ using System;
/// Esta classe é necessária porque o Godot trata posições em pixels,
/// mas faz mais sentido tratarmos as posições em tiles.
/// </summary>
-public abstract partial class Grid : GodotObject {
+public abstract partial class Grid : GodotObject
+{
public static readonly Vector2I tileSize = new(16, 16);
- public static Vector2I WorldToGrid(Vector2 coord) {
+ public static Vector2I WorldToGrid(Vector2 coord)
+ {
return (Vector2I)(coord / tileSize);
}
- public static Vector2 GridToWorld(Vector2I coord) {
+ public static Vector2 GridToWorld(Vector2I coord)
+ {
return coord * tileSize;
}
}
diff --git a/scripts/entities/actors/Inspector.cs b/scripts/Utils/Inspector.cs
index e340543..7fb12ff 100644
--- a/scripts/entities/actors/Inspector.cs
+++ b/scripts/Utils/Inspector.cs
@@ -1,17 +1,23 @@
using Godot;
+namespace TheLegendOfGustav.Utils;
+
/// <summary>
/// Isto é uma abominação
/// </summary>
public partial class Inspector : Sprite2D
{
private Vector2I gridPosition = Vector2I.Zero;
+
+
/// <summary>
- /// Posição do inspetor no espaço. Diferentemente de Position, GridPosition tem como formato
- /// os tiles do mapa.
- /// </summary>
- public Vector2I GridPosition {
- set {
+ /// Posição do inspetor no espaço. Diferentemente de Position, GridPosition tem como formato
+ /// os tiles do mapa.
+ /// </summary>
+ public Vector2I GridPosition
+ {
+ set
+ {
gridPosition = value;
// O sistema de coordenadas do Godot é em pixels, mas faz mais sentido para o jogo utilizar coordenadas em tiles.
// Esta propriedade converte um sistema para o outro automaticamente.
@@ -20,7 +26,8 @@ public partial class Inspector : Sprite2D
get => gridPosition;
}
- public override void _Ready() {
+ public override void _Ready()
+ {
base._Ready();
Camera2D camera = GetNode<Camera2D>("Camera2D");
camera.Enabled = true;
@@ -30,10 +37,11 @@ public partial class Inspector : Sprite2D
}
/// <summary>
- /// O Inspetor não faz parte do mapa.
- /// </summary>
- /// <param name="offset"></param>
- public void Walk(Vector2I offset) {
+ /// O Inspetor não faz parte do mapa.
+ /// </summary>
+ /// <param name="offset"></param>
+ public void Walk(Vector2I offset)
+ {
GridPosition += offset;
SignalBus.Instance.EmitSignal(SignalBus.SignalName.InspectorMoved, GridPosition);
}
diff --git a/scripts/entities/actors/Inspector.cs.uid b/scripts/Utils/Inspector.cs.uid
index ca411e4..ca411e4 100644
--- a/scripts/entities/actors/Inspector.cs.uid
+++ b/scripts/Utils/Inspector.cs.uid
diff --git a/scripts/Utils/MessageLogData.cs b/scripts/Utils/MessageLogData.cs
index e8f0f09..46d2c3e 100644
--- a/scripts/Utils/MessageLogData.cs
+++ b/scripts/Utils/MessageLogData.cs
@@ -1,25 +1,30 @@
using Godot;
+using TheLegendOfGustav.GUI;
+
+namespace TheLegendOfGustav.Utils;
public partial class MessageLogData : Node
{
+ /// <summary>
+ /// Acionado sempre que uma mensagem for adicionada para o log.
+ /// </summary>
+ /// <param name="text">Mensagem.</param>
+ [Signal]
+ public delegate void messageSentEventHandler(Message message);
+
public static MessageLogData Instance { get; private set; }
- private Godot.Collections.Array<Message> messages = [];
- public Godot.Collections.Array<Message> Messages {get => messages;}
-private Message LastMessage {
- get {
- if (messages.Count <= 0) {
+ public Godot.Collections.Array<Message> Messages { get; private set; } = [];
+
+ private Message LastMessage
+ {
+ get
+ {
+ if (Messages.Count <= 0)
+ {
return null;
}
- return messages[^1];
- }
- }
-
- public void ClearMessages() {
- for (int i = messages.Count - 1; i >= 0; i--) {
- Message message = messages[i];
- messages.RemoveAt(i);
- message.QueueFree();
+ return Messages[^1];
}
}
@@ -29,22 +34,26 @@ private Message LastMessage {
Instance = this;
}
- public void AddMessage(string text) {
- if (LastMessage != null && LastMessage.PlainText == text) {
+ public void ClearMessages()
+ {
+ for (int i = Messages.Count - 1; i >= 0; i--)
+ {
+ Message message = Messages[i];
+ Messages.RemoveAt(i);
+ message.QueueFree();
+ }
+ }
+
+ public void AddMessage(string text)
+ {
+ if (LastMessage != null && LastMessage.PlainText == text)
+ {
LastMessage.Count++;
return;
}
Message message = new(text);
- messages.Add(message);
+ Messages.Add(message);
EmitSignal(SignalName.messageSent, message);
}
-
- /// <summary>
- /// Acionado sempre que uma mensagem for adicionada para o log.
- /// </summary>
- /// <param name="text">Mensagem.</param>
- [Signal]
- public delegate void messageSentEventHandler(Message message);
-
} \ No newline at end of file
diff --git a/scripts/Utils/SignalBus.cs b/scripts/Utils/SignalBus.cs
index edeb09b..a2aa6ca 100644
--- a/scripts/Utils/SignalBus.cs
+++ b/scripts/Utils/SignalBus.cs
@@ -1,5 +1,6 @@
using Godot;
-using System;
+
+namespace TheLegendOfGustav.Utils;
/// <summary>
/// Objeto global com sinais, fortes sinais.
@@ -7,16 +8,10 @@ using System;
public partial class SignalBus : Node
{
/// <summary>
- /// Pois é.
- /// </summary>
+ /// Pois é.
+ /// </summary>
public static SignalBus Instance { get; private set; }
- public override void _Ready()
- {
- base._Ready();
- Instance = this;
- }
-
[Signal]
public delegate void InspectorMovedEventHandler(Vector2I pos);
@@ -24,4 +19,10 @@ public partial class SignalBus : Node
public delegate void EnterInspectionModeEventHandler();
[Signal]
public delegate void ExitInspectionModeEventHandler();
+
+ public override void _Ready()
+ {
+ base._Ready();
+ Instance = this;
+ }
}
diff --git a/scripts/entities/actions/Action.cs b/scripts/entities/actions/Action.cs
deleted file mode 100644
index 9dab5a4..0000000
--- a/scripts/entities/actions/Action.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using Godot;
-
-
-/// <summary>
-/// <c>Action</c> representa uma ação no jogo efetuada por um ator.
-/// Ações são geradas pelo jogador e pela IA, elas regem os atores do jogo.
-/// </summary>
-public abstract partial class Action : RefCounted {
- /// <summary>
- /// O ator que realiza a ação.
- /// </summary>
- protected Actor actor;
- public Actor ThisActor { get => actor; }
-
- // O custo da ação.
- protected int cost;
-
- public Action(Actor actor) {
- this.actor = actor;
- // Custo base, subclasses podem sobreescrever isto se quiserem.
- cost = 10;
- }
-
- /// <summary>
- /// Método que executa a ação. Subclasses da ação devem implementar este método.
- /// <example>
- /// Exemplo:
- /// <code>
- /// Action action = new Action(actor);
- /// /* . . . */
- /// action.Perform();
- /// </code>
- /// </example>
- /// </summary>
- /// <returns>Se a ação foi executada ou não.</returns>
- public abstract bool Perform();
-
- /// <summary>
- /// É conveniente ter acesso ao mapa dentro de uma ação.
- /// </summary>
- protected MapData Map_Data {
- get => actor.Map_Data;
- }
-}
diff --git a/scripts/entities/actions/DirectionalAction.cs b/scripts/entities/actions/DirectionalAction.cs
deleted file mode 100644
index 9c7a915..0000000
--- a/scripts/entities/actions/DirectionalAction.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using Godot;
-
-/// <summary>
-/// Ação direcionada. Esta ação é acompanhada com um vetor que representa uma
-/// distância tendo como ponto de partida o ator.
-/// </summary>
-public abstract partial class DirectionalAction : Action
-{
- /// <summary>
- /// Direção/distância do ator da ação.
- /// Seu significado depende da ação que implementará esta classe.
- /// </summary>
- public Vector2I Offset { get; private set; }
- /// <summary>
- /// Coordenada do alvo da ação.
- /// </summary>
- public Vector2I Destination { get => actor.GridPosition + Offset; }
- public DirectionalAction(Actor actor, Vector2I offset) : base(actor)
- {
- Offset = offset;
- }
-
- /// <summary>
- /// Função que obtém o alvo da ação, se houver.
- /// </summary>
- /// <returns>O ator alvo da ação, nulo se não houver.</returns>
- protected Entity GetTarget() {
- return Map_Data.GetBlockingEntityAtPosition(Destination);
- }
-}
diff --git a/scripts/entities/actions/DropAction.cs b/scripts/entities/actions/DropAction.cs
deleted file mode 100644
index e5bd929..0000000
--- a/scripts/entities/actions/DropAction.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Godot;
-
-public partial class DropAction : ItemAction
-{
- public DropAction(Player player, ConsumableItem item) : base(player, item)
- {
- }
-
- public override bool Perform() {
- player.inventory.Drop(item);
- return true;
- }
-} \ No newline at end of file
diff --git a/scripts/entities/actions/ItemAction.cs b/scripts/entities/actions/ItemAction.cs
deleted file mode 100644
index d5247df..0000000
--- a/scripts/entities/actions/ItemAction.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Godot;
-
-public partial class ItemAction : Action
-{
- protected ConsumableItem item;
- protected Player player;
- public ItemAction(Player player, ConsumableItem item) : base(player)
- {
- this.item = item;
- this.player = player;
- }
-
- public override bool Perform()
- {
- return item.Activate(this);
- }
-} \ No newline at end of file
diff --git a/scripts/entities/actions/PickUpAction.cs b/scripts/entities/actions/PickUpAction.cs
deleted file mode 100644
index b772bb7..0000000
--- a/scripts/entities/actions/PickUpAction.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Godot;
-
-public partial class PickupAction : DirectionalAction
-{
- protected Player player;
-
- public PickupAction(Player player, Vector2I offset) : base(player, offset)
- {
- this.player = player;
- // Pegar itens requer um tempo menor.
- cost = 2;
- }
-
- public override bool Perform()
- {
- ConsumableItem item = Map_Data.GetFirstItemAtPosition(Destination);
-
- if (item == null) {
- MessageLogData.Instance.AddMessage("Não tem item aqui.");
- return false;
- }
-
- if (player.inventory.Items.Count >= player.inventory.Capacity) {
- MessageLogData.Instance.AddMessage("Seu inventário está cheio");
- return false;
- }
-
- Map_Data.RemoveEntity(item);
- player.inventory.Add(item);
-
- player.Energy -= cost;
- return true;
- }
-} \ No newline at end of file
diff --git a/scripts/entities/actors/AI/BaseAI.cs b/scripts/entities/actors/AI/BaseAI.cs
deleted file mode 100644
index 733a61a..0000000
--- a/scripts/entities/actors/AI/BaseAI.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Godot;
-
-/// <summary>
-/// base para as IAs do jogo.
-/// </summary>
-public abstract partial class BaseAI : Node {
- /// <summary>
- /// Corpo controlado pela IA.
- /// O corpo é a marionete da alma.
- /// </summary>
- protected Actor body;
-
- public override void _Ready()
- {
- base._Ready();
- // Por padrão, a IA é filha do nó de seu corpo.
- body = GetParent<Actor>();
- }
-
- /// <summary>
- /// Computa um único turno para o ator controlado.
- /// Aviso: NPCs não possuem ações gratuitas.
- /// A IA SEMPRE precisa executar uma ação que custe energia.
- /// </summary>
- public abstract void Perform();
-
- /// <summary>
- /// Utiliza o pathfinder do mapa para obter um caminho
- /// da posição atual do ator para um destino qualquer.
- /// </summary>
- /// <param name="destination">Destino</param>
- /// <returns>Vetor com vetores, passo a passo para chegar no destino.</returns>
- public Godot.Collections.Array<Vector2> GetPathTo(Vector2I destination) {
- // Arrays do Godot são muito mais confortáveis de manipular, então
- // eu converto o Array do C# em um array do Godot antes de retornar o caminho.
- Godot.Collections.Array<Vector2> list = [];
- Vector2[] path = body.Map_Data.Pathfinder.GetPointPath(body.GridPosition, destination);
- list.AddRange(path);
- return list;
- }
-} \ No newline at end of file
diff --git a/scripts/entities/actors/Inventory.cs b/scripts/entities/actors/Inventory.cs
deleted file mode 100644
index f1cff2a..0000000
--- a/scripts/entities/actors/Inventory.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using Godot;
-
-public partial class Inventory : Node {
- private Player player;
-
- public Godot.Collections.Array<ConsumableItem> Items { get; private set; } = [];
-
- public int Capacity { get; private set; }
-
- public Inventory(int capacity) {
- Capacity = capacity;
- }
-
- public override void _Ready() {
- base._Ready();
- player = GetParent<Player>();
- }
-
- public void Drop(ConsumableItem item) {
- Items.Remove(item);
- MapData data = player.Map_Data;
- data.InsertEntity(item);
- data.EmitSignal(MapData.SignalName.EntityPlaced, item);
- item.Map_Data = data;
- item.GridPosition = player.GridPosition;
-
- MessageLogData.Instance.AddMessage($"Você descarta {item.DisplayName}.");
- }
-
- public void Add(ConsumableItem item) {
- if (Items.Count >= Capacity) return;
-
- Items.Add(item);
- }
-
- public void RemoveItem(ConsumableItem item) {
- Items.Remove(item);
- }
-} \ No newline at end of file
diff --git a/scripts/entities/items/HealingConsumable.cs b/scripts/entities/items/HealingConsumable.cs
deleted file mode 100644
index 2104693..0000000
--- a/scripts/entities/items/HealingConsumable.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Godot;
-
-public partial class HealingConsumable : ConsumableItem
-{
- private HealingConsumableDefinition definition;
- public float HealingPercentage { get; private set; }
- public HealingConsumable(Vector2I initialPosition, MapData map, HealingConsumableDefinition definition) : base(initialPosition, map, definition)
- {
- this.definition = definition;
- HealingPercentage = definition.healingPercentage;
- }
-
- public override bool Activate(ItemAction action)
- {
- Player consumer = (Player) action.ThisActor;
- int intendedAmount = (int)(HealingPercentage / 100 * consumer.MaxHp);
- int recovered = consumer.Heal(intendedAmount);
-
- // Se não tinha o que curar, a ativação falhou.
- if (recovered == 0) {
- MessageLogData.Instance.AddMessage("Você já está saudável.");
- return false;
- }
- MessageLogData.Instance.AddMessage($"Você consome {DisplayName} e recupera {recovered} de HP");
- ConsumedBy(consumer);
- return true;
- }
-} \ No newline at end of file
diff --git a/scripts/input/InputHandler.cs b/scripts/input/InputHandler.cs
deleted file mode 100644
index 67f4abf..0000000
--- a/scripts/input/InputHandler.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using Godot;
-
-public enum InputHandlers
-{
- MainGame,
- GameOver,
- Inspect,
- Pickup,
- Inventory
-}
-
-/// <summary>
-/// Máquina de estado que obtém ações do usuário conforme o estado atual do jogo.
-/// </summary>
-public partial class InputHandler : Node
-{
- private Godot.Collections.Dictionary<InputHandlers, BaseInputHandler> inputHandlers = [];
-
- [Export]
- private InputHandlers startingInputHandler;
-
- private BaseInputHandler selectedInputHandler;
-
- public override void _Ready()
- {
- base._Ready();
- // Controles para quando o jogador está vivo e jogando normalmente.
- inputHandlers.Add(InputHandlers.MainGame, GetNode<MainGameInputHandler>("MainGameInputHandler"));
- // Controles para quando o jogador está morto.
- inputHandlers.Add(InputHandlers.GameOver, GetNode<GameOverInputHandler>("GameOverInputHandler"));
- inputHandlers.Add(InputHandlers.Inspect, GetNode<InspectInputHandler>("InspectInputHandler"));
- inputHandlers.Add(InputHandlers.Pickup, GetNode<PickupInputHandler>("PickupInputHandler"));
- inputHandlers.Add(InputHandlers.Inventory, GetNode<InventoryInputHandler>("InventoryInputHandler"));
-
- SetInputHandler(startingInputHandler);
- }
-
- public Action GetAction(Player player) {
- return selectedInputHandler.GetAction(player);
- }
-
- /// <summary>
- /// Define o esquema de controle atual do jogo
- /// para o estado informado.
- /// </summary>
- /// <param name="inputhandler">Estado do jogo.</param>
- public void SetInputHandler(InputHandlers inputhandler) {
- selectedInputHandler?.Exit();
- selectedInputHandler = inputHandlers[inputhandler];
- selectedInputHandler.Enter();
- }
-}
diff --git a/scripts/input/InventoryInputHandler.cs b/scripts/input/InventoryInputHandler.cs
deleted file mode 100644
index 98f8576..0000000
--- a/scripts/input/InventoryInputHandler.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using Godot;
-
-public partial class InventoryInputHandler : BaseInputHandler
-{
- private static readonly PackedScene inventoryScene = GD.Load<PackedScene>("res://scenes/GUI/invetory_menu.tscn");
-
- private InventoryMenu inventoryMenu;
-
- ConsumableItem activationItem = null;
- ConsumableItem dropItem = null;
-
- [Export]
- private Map map;
-
- public override void Enter() {
- inventoryMenu = inventoryScene.Instantiate<InventoryMenu>();
- map.Map_Data.Player.AddChild(inventoryMenu);
- inventoryMenu.Initialize(map.Map_Data.Player.inventory);
- inventoryMenu.ItemSelected += OnItemActivate;
- inventoryMenu.ItemDrop += OnItemDrop;
- }
-
- public override void Exit() {
- activationItem = null;
- dropItem = null;
- inventoryMenu.QueueFree();
- }
-
- public override Action GetAction(Player player)
- {
- Action action = null;
-
- if (activationItem != null) {
- action = new ItemAction(player, activationItem);
- Close();
- }
-
- if (dropItem != null) {
- action = new DropAction(player, dropItem);
- Close();
- }
-
- if (Input.IsActionJustPressed("quit")) {
- Close();
- }
-
- return action;
- }
-
- private void Close() {
- GetParent<InputHandler>().SetInputHandler(InputHandlers.MainGame);
- }
-
- private void ActivateItem() {
-
- }
-
- private void OnItemActivate(ConsumableItem item) {
- activationItem = item;
- }
-
- private void OnItemDrop(ConsumableItem item) {
- dropItem = item;
- }
-} \ No newline at end of file
diff --git a/scripts/input/MainGameInputHandler.cs b/scripts/input/MainGameInputHandler.cs
deleted file mode 100644
index 6bda004..0000000
--- a/scripts/input/MainGameInputHandler.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using Godot;
-
-/// <summary>
-/// Esquema de controles principal do jogo.
-/// </summary>
-public partial class MainGameInputHandler : BaseInputHandler {
- 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},
- };
- public override Action GetAction(Player player) {
- Action action = null;
-
- if (player.IsAlive) {
- foreach (var direction in directions) {
- if (Input.IsActionJustPressed(direction.Key)) {
- action = new BumpAction(player, direction.Value);
- }
- }
-
- if (Input.IsActionJustPressed("open-inventory")) {
- GetParent<InputHandler>().SetInputHandler(InputHandlers.Inventory);
- }
-
- if (Input.IsActionJustPressed("pick-item")) {
- GetParent<InputHandler>().SetInputHandler(InputHandlers.Pickup);
- }
-
- if (Input.IsActionJustPressed("inspect")) {
- GetParent<InputHandler>().SetInputHandler(InputHandlers.Inspect);
- }
-
- if (Input.IsActionJustPressed("skip-turn")) {
- action = new WaitAction(player);
- }
- }
-
- return action;
- }
-}
diff --git a/scripts/map/Map.cs b/scripts/map/Map.cs
deleted file mode 100644
index 148bda6..0000000
--- a/scripts/map/Map.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using Godot;
-
-/// <summary>
-/// A parte visual do mapa.
-/// </summary>
-public partial class Map : Node2D
-{
- /// <summary>
- /// Dados do mapa.
- /// </summary>
- public MapData Map_Data { get; private set; }
-
- /// <summary>
- /// raio de alcance da visão do jogador.
- /// </summary>
- [Export]
- private int fovRadius = 12;
-
- /// <summary>
- /// Gerador de mapas.
- /// </summary>
- private DungeonGenerator generator;
-
- FieldOfView fieldOfView;
-
- private Node2D tilesNode;
- private Node2D entitiesNode;
-
- public override void _Ready()
- {
- base._Ready();
- // Começamos obtendo nós relevantes para o mapa.
- generator = GetNode<DungeonGenerator>("Generator");
- fieldOfView = GetNode<FieldOfView>("FieldOfView");
- tilesNode = GetNode<Node2D>("Tiles");
- entitiesNode = GetNode<Node2D>("Entities");
- }
-
- /// <summary>
- /// Coloca todos os tiles do mapa no mundo do jogo.
- /// </summary>
- private void PlaceTiles() {
- foreach (Tile tile in Map_Data.Tiles) {
- tilesNode.AddChild(tile);
- }
- }
-
- /// <summary>
- /// Coloca todas as entidades do mapa no mundo do jogo.
- /// </summary>
- private void PlaceEntities() {
- foreach (Entity entity in Map_Data.Entities) {
- entitiesNode.AddChild(entity);
- }
- }
-
- /// <summary>
- /// Cria um andar da masmorra utilizando o gerador de mapa.
- /// </summary>
- /// <param name="player">O gerador de mapas precisa do jogador.</param>
- public void Generate(Player player)
- {
- Map_Data = generator.GenerateDungeon(player);
-
- Map_Data.EntityPlaced += OnEntityPlaced;
-
- PlaceTiles();
- PlaceEntities();
- }
-
- /// <summary>
- /// Atualiza o campo de visão do mapa com base em uma coordenada.
- /// </summary>
- /// <param name="pos">Centro de visão, normalmente é a posição do jogador.</param>
- public void UpdateFOV(Vector2I pos) {
- fieldOfView.UpdateFOV(Map_Data, pos, fovRadius);
- // Esconde ou revela entidades com base no campo de visão.
- foreach (Entity entity in Map_Data.Entities) {
- entity.Visible = Map_Data.GetTile(entity.GridPosition).IsInView;
- }
- }
-
- private void OnEntityPlaced(Entity entity) {
- entitiesNode.AddChild(entity);
- }
-}
diff --git a/scripts/map/MapData.cs b/scripts/map/MapData.cs
deleted file mode 100644
index f3e193e..0000000
--- a/scripts/map/MapData.cs
+++ /dev/null
@@ -1,272 +0,0 @@
-using Godot;
-
-/// <summary>
-/// Classe que cuida dos dados e da parte lógica do mapa.
-/// O mapa é o cenário onde as ações do jogo ocorrem.
-/// Mais especificamente, o mapa é um único andar da masmorra.
-/// </summary>
-public partial class MapData : RefCounted
-{
- public static readonly TileDefinition wallDefinition = GD.Load<TileDefinition>("res://assets/definitions/tiles/wall.tres");
- public static readonly TileDefinition floorDefinition = GD.Load<TileDefinition>("res://assets/definitions/tiles/floor.tres");
-
- [Signal]
- public delegate void EntityPlacedEventHandler(Entity entity);
-
- /// <summary>
- /// Largura do mapa.
- /// </summary>
- public int Width { get; private set; }
- /// <summary>
- /// Altura do mapa.
- /// </summary>
- public int Height { get; private set; }
-
- /// <summary>
- /// Os tiles que compõem o mapa.
- /// </summary>
- public Godot.Collections.Array<Tile> Tiles { get; private set; } = [];
-
- /// <summary>
- /// O jogador é especial e por isso o mapa faz questão de rastreá-lo.
- /// </summary>
- public Player Player { get; set; }
- /// <summary>
- /// Lista de todos os atores dentro do mapa.
- /// </summary>
- public Godot.Collections.Array<Entity> Entities { get; private set; } = [];
-
- public Godot.Collections.Array<ConsumableItem> Items {
- get {
- Godot.Collections.Array<ConsumableItem> list = [];
- foreach (Entity entity in Entities) {
- if (entity is ConsumableItem item) {
- list.Add(item);
- }
- }
- return list;
- }
- }
-
- private AStarGrid2D pathfinder;
- /// <summary>
- /// Objeto do Godot que utiliza do algoritmo A* para calcular
- /// caminhos e rotas.
- /// </summary>
- public AStarGrid2D Pathfinder { get => pathfinder; }
- /// <summary>
- /// Peso do ator no pathfinder.
- /// A IA irá evitar de passar por espaços com peso alto.
- /// </summary>
- private static readonly float EntityWeight = 10.0f;
-
- /// <summary>
- /// Inicializa o pathfinder;
- /// </summary>
- public void SetupPathfinding() {
- pathfinder = new AStarGrid2D
- {
- //A região é o mapa inteiro.
- Region = new Rect2I(0, 0, Width, Height)
- };
-
- // Atualiza o pathfinder para a região definida.
- pathfinder.Update();
-
- // Define quais pontos do mapa são passáveis ou não.
- for (int y = 0; y < Height; y++) {
- for (int x = 0; x < Width; x++) {
- Vector2I pos = new Vector2I(x, y);
- Tile tile = GetTile(pos);
- // Pontos sólidos são impossíveis de passar.
- pathfinder.SetPointSolid(pos, !tile.IsWalkable);
- }
- }
-
- // Registra todos os atores em cena.
- foreach (Entity entity in Entities) {
- if (entity.BlocksMovement) {
- RegisterBlockingEntity(entity);
- }
- }
-
- }
-
- /// <summary>
- /// Define um peso na posição de uma entidade para que a IA evite de passar por lá.
- /// Ênfase em evitar. Se o único caminho para o destino estiver bloqueado
- /// por uma entidade, o jogo tentará andar mesmo assim.
- /// </summary>
- /// <param name="entity">A entidade em questão.</param>
- public void RegisterBlockingEntity(Entity entity) {
- pathfinder.SetPointWeightScale(entity.GridPosition, EntityWeight);
- }
-
- /// <summary>
- /// Remove o peso na posição de uma entidade.
- /// Quando uma entidade move sua posição, devemos tirar o peso de sua posição anterior.
- /// </summary>
- /// <param name="entity">A entidade em questão.</param>
- public void UnregisterBlockingEntity(Entity entity) {
- pathfinder.SetPointWeightScale(entity.GridPosition, 0);
- }
-
- public MapData(int width, int height, Player player) {
- Width = width;
- Height = height;
-
- Player = player;
- // Como o jogador é criado antes do mapa, precisamos
- // atualizá-lo com o novo mapa.
- player.Map_Data = this;
- InsertEntity(player);
-
- SetupTiles();
- }
-
- /// <summary>
- /// Cria novos Tiles até preencher as dimensões do mapa.
- /// É importante que estes tiles sejam paredes, o gerador de mapas
- /// não cria paredes por conta própria.
- /// </summary>
- private void SetupTiles() {
- for (int i = 0; i < Height; i++)
- {
- for (int j = 0; j < Width; j++)
- {
- Tiles.Add(new Tile(new Vector2I(j, i), wallDefinition));
- }
- }
- }
-
- /// <summary>
- /// Registra uma entidade no mapa. A existência de uma entidade não é considerada se ela não
- /// estiver registrada no mapa.
- /// </summary>
- /// <param name="entity">A entidade em questão</param>
- public void InsertEntity(Entity entity) {
- Entities.Add(entity);
- }
-
- /// <summary>
- /// Converte uma coordenada em um índice para acessar a lista de tiles.
- /// </summary>
- /// <param name="pos">Vetor posição</param>
- /// <returns>Índice na lista de tiles. -1 se estiver fora do mapa.</returns>
- private int GridToIndex(Vector2I pos) {
- if (!IsInBounds(pos)) return -1;
-
- return pos.Y * Width + pos.X;
- }
-
- /// <summary>
- /// Se uma coordenada está dentro da área do mapa.
- /// </summary>
- /// <param name="pos">Vetor posição</param>
- /// <returns>Se o vetor está dentro do mapa.</returns>
- private bool IsInBounds(Vector2I pos) {
- if (pos.X < 0 || pos.Y < 0) {
- return false;
- }
- if (pos.X >= Width || pos.Y >= Height) {
- return false;
- }
-
- return true;
- }
-
- /// <summary>
- /// Obtém o tile na posição desejada.
- /// </summary>
- /// <param name="pos">Vetor posição</param>
- /// <returns>O tile na posição, nulo se for fora do mapa.</returns>
- public Tile GetTile(Vector2I pos) {
- int index = GridToIndex(pos);
-
- if (index < 0) return null;
-
- return Tiles[index];
- }
-
- /// <summary>
- /// Obtém o tile na posição desejada.
- /// </summary>
- /// <param name="x">x da coordenada</param>
- /// <param name="y">y da coordenada</param>
- /// <returns>O tile na posição, nulo se for fora do mapa.</returns>
- public Tile GetTile(int x, int y) {
- return GetTile(new Vector2I(x, y));
- }
-
- /// <summary>
- /// Obtém a entidade na posição especificada.
- /// </summary>
- /// <param name="pos">Vetor posição</param>
- /// <returns>A entidade na posição especificada, nulo se não houver.</returns>
- public Entity GetBlockingEntityAtPosition(Vector2I pos) {
- foreach (Entity entity in Entities) {
- if (entity.GridPosition == pos && entity.BlocksMovement) {
- return entity;
- }
- }
- return null;
- }
-
- /// <summary>
- /// Obtém o primeiro item na posição especificada.
- /// </summary>
- /// <param name="pos">Posição</param>
- /// <returns>O primeiro item na posição, nulo se não houver.</returns>
- public ConsumableItem GetFirstItemAtPosition(Vector2I pos) {
- foreach (ConsumableItem item in Items) {
- if (item.GridPosition == pos) {
- return item;
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Remove uma entidade do mapa sem dar free.
- /// </summary>
- /// <param name="entity">A entidade para remover</param>
- public void RemoveEntity(Entity entity) {
- // Eu removo a entidade do nó de entidades.
- entity.GetParent().RemoveChild(entity);
- // Eu removo a entidade da lista de entidades do mapa.
- Entities.Remove(entity);
- }
-
- /// <summary>
- /// Obtém todas as entidades na posição especificada.
- /// É possível haver mais de uma entidade na mesma posição se uma delas não bloquear movimento.
- /// </summary>
- /// <param name="pos">Vetor posição</param>
- /// <returns>Lista com todas as entidades na posição especificada.</returns>
- public Godot.Collections.Array<Entity> GetEntitiesAtPosition(Vector2I pos) {
- Godot.Collections.Array<Entity> ZOfZero = [];
- Godot.Collections.Array<Entity> ZOfOne = [];
- Godot.Collections.Array<Entity> ZOfTwo = [];
-
- // Pego todos os atores
- foreach (Entity entity in Entities) {
- if (entity.GridPosition == pos) {
- switch (entity.ZIndex) {
- case 0:
- ZOfZero.Add(entity);
- break;
- case 1:
- ZOfOne.Add(entity);
- break;
- case 2:
- ZOfTwo.Add(entity);
- break;
- }
- }
- }
-
- // Retorno os atores ordenados por ZIndex.
- return ZOfZero + ZOfOne + ZOfTwo;
- }
-}
diff --git a/scripts/map/MapDivision.cs b/scripts/map/MapDivision.cs
deleted file mode 100644
index 3273775..0000000
--- a/scripts/map/MapDivision.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using Godot;
-
-/// <summary>
-/// Classe utilizada pelo gerador de mapas.
-/// Uma divisão é uma região retangular de espaço que pode
-/// conter dentro de si duas regiões menores *ou* uma sala.
-/// Uma divisão é uma árvore binária que possui espaço para salas em suas folhas.
-/// </summary>
-public partial class MapDivision : RefCounted {
- /// <summary>
- /// Região retangular da divisão.
- /// </summary>
- public Vector2I Position { get; set; }
- public Vector2I Size { get; set; }
-
- public Vector2I Center {
- get => new(Position.X + Size.X/2, Position.Y + Size.Y/2);
- }
-
- /// <summary>
- /// Filhos da árvore
- /// </summary>
- private MapDivision left;
- public MapDivision Left { get => this.left; }
- private MapDivision right;
- public MapDivision Right { get => this.right; }
-
- /// <summary>
- /// Se a divisão atual for uma folha.
- /// As folhas representam salas.
- /// </summary>
- public bool IsLeaf {
- get => left == null && right == null;
- }
-
- public MapDivision(Vector2I position, Vector2I size) {
- Position = position;
- Size = size;
- }
-
- public MapDivision(Vector2I position, int width, int height) {
- Position = position;
- Size = new(width, height);
- }
-
- public MapDivision(int x, int y, int width, int height) {
- Position = new(x, y);
- Size = new(width, height);
- }
-
- /// <summary>
- /// É conveniente ter acesso à todas as folhas da árvore.
- /// </summary>
- /// <returns>Lista com todas as folhas da árvore.</returns>
- public Godot.Collections.Array<MapDivision> GetLeaves() {
- if (IsLeaf) {
- Godot.Collections.Array<MapDivision> list = [];
- list.Add(this);
- return list;
- }
- return left.GetLeaves() + right.GetLeaves();
- }
-
- /// <summary>
- /// Algoritmo para gerar as divisões.
- /// O mapa começa com uma única divisão que oculpa sua extensão completa.
- /// Depois disso, ela se dividirá recursivamente n vezes.
- /// As divisões nas folhas representam espaços onde pode gerar uma sala.
- /// </summary>
- /// <param name="iterations">Número de iterações</param>
- /// <param name="rng">Gerador de números</param>
- public void Split(int iterations, RandomNumberGenerator rng) {
- float SplitRatio = rng.RandfRange(0.35f, 0.65f);
- bool horizontalSplit = Size.X <= Size.Y;
-
- // Eu defini um limite mínimo de 4 de altura e 4 de largura para divisões.
-
- if (horizontalSplit) {
- int leftHeight = (int) (Size.Y * SplitRatio);
- if (leftHeight > 4 && Size.Y - leftHeight > 4) {
- left = new MapDivision(Position, Size.X, leftHeight);
- right = new MapDivision(Position.X, Position.Y + leftHeight, Size.X, Size.Y - leftHeight);
- }
- } else {
- int leftWidth = (int) (Size.Y * SplitRatio);
-
- if (leftWidth > 4 && Size.Y - leftWidth > 4) {
- left = new MapDivision(Position, leftWidth, Size.Y);
- right = new MapDivision(Position.X + leftWidth, Position.Y, Size.X - leftWidth, Size.Y);
- }
- }
-
- if (iterations > 1) {
- left?.Split(iterations - 1, rng);
- right?.Split(iterations - 1, rng);
- }
- }
-} \ No newline at end of file