From bc913fd25421c5c2a8d3225dbc24fa3f6df9cac1 Mon Sep 17 00:00:00 2001 From: Thomas Metten Date: Tue, 5 Nov 2024 00:09:44 +0100 Subject: [PATCH 1/7] feat: add basic homebase scene --- assets/UI/energy_orb.png.import | 6 +-- assets/UI/scroll_base.png.import | 6 +-- assets/UI/tilemap.png.import | 6 +-- assets/UI/tilemap_packed.png.import | 6 +-- scripts/enemy.gd | 1 + scripts/scenes/home_base.gd | 13 ++++++ scripts/scenes/home_base.tscn | 67 +++++++++++++++++++++++++++++ scripts/scenes/main.gd | 10 ++++- scripts/scenes/main_menu.gd | 3 +- 9 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 scripts/scenes/home_base.gd create mode 100644 scripts/scenes/home_base.tscn diff --git a/assets/UI/energy_orb.png.import b/assets/UI/energy_orb.png.import index c61b1c5..2d3a3a2 100644 --- a/assets/UI/energy_orb.png.import +++ b/assets/UI/energy_orb.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://dyx76tn885md3" -path="res://.godot/imported/energy_orb.png-2cbe75b8af4388a02af2b14d7f4c3fa2.ctex" +path="res://.godot/imported/energy_orb.png-8af805f3f4e2d178d867da22867062b3.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/UI/energy_orb.png" -dest_files=["res://.godot/imported/energy_orb.png-2cbe75b8af4388a02af2b14d7f4c3fa2.ctex"] +source_file="res://assets/ui/energy_orb.png" +dest_files=["res://.godot/imported/energy_orb.png-8af805f3f4e2d178d867da22867062b3.ctex"] [params] diff --git a/assets/UI/scroll_base.png.import b/assets/UI/scroll_base.png.import index 9e486d7..ed51b06 100644 --- a/assets/UI/scroll_base.png.import +++ b/assets/UI/scroll_base.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://df58t5bfmj70a" -path="res://.godot/imported/scroll_base.png-693a0b068424563664384c6eb0209196.ctex" +path="res://.godot/imported/scroll_base.png-791e7b490ba08675e2067537e744575f.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/UI/scroll_base.png" -dest_files=["res://.godot/imported/scroll_base.png-693a0b068424563664384c6eb0209196.ctex"] +source_file="res://assets/ui/scroll_base.png" +dest_files=["res://.godot/imported/scroll_base.png-791e7b490ba08675e2067537e744575f.ctex"] [params] diff --git a/assets/UI/tilemap.png.import b/assets/UI/tilemap.png.import index ac09591..71dc670 100644 --- a/assets/UI/tilemap.png.import +++ b/assets/UI/tilemap.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://dmvsmadpgt07f" -path="res://.godot/imported/tilemap.png-083a8bbebe37cff77c4a1b9df77bc47f.ctex" +path="res://.godot/imported/tilemap.png-cc54adafb85d770760bc0268fc94ca9e.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/UI/tilemap.png" -dest_files=["res://.godot/imported/tilemap.png-083a8bbebe37cff77c4a1b9df77bc47f.ctex"] +source_file="res://assets/ui/tilemap.png" +dest_files=["res://.godot/imported/tilemap.png-cc54adafb85d770760bc0268fc94ca9e.ctex"] [params] diff --git a/assets/UI/tilemap_packed.png.import b/assets/UI/tilemap_packed.png.import index 450a71e..73156e4 100644 --- a/assets/UI/tilemap_packed.png.import +++ b/assets/UI/tilemap_packed.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://b27cwck0oyrc" -path="res://.godot/imported/tilemap_packed.png-42b0b930e1378971414a5562b18c2f23.ctex" +path="res://.godot/imported/tilemap_packed.png-de9d7af106cbb7c482b9af8055d029ba.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/UI/tilemap_packed.png" -dest_files=["res://.godot/imported/tilemap_packed.png-42b0b930e1378971414a5562b18c2f23.ctex"] +source_file="res://assets/ui/tilemap_packed.png" +dest_files=["res://.godot/imported/tilemap_packed.png-de9d7af106cbb7c482b9af8055d029ba.ctex"] [params] diff --git a/scripts/enemy.gd b/scripts/enemy.gd index e2c7645..de7e5a4 100644 --- a/scripts/enemy.gd +++ b/scripts/enemy.gd @@ -14,6 +14,7 @@ func take_damage(amount: int) -> void: update_health_display() if health <= 0: get_node("/root/Main").combat_won() + func take_turn() -> void: print("Enemy turn") diff --git a/scripts/scenes/home_base.gd b/scripts/scenes/home_base.gd new file mode 100644 index 0000000..0fa8e7e --- /dev/null +++ b/scripts/scenes/home_base.gd @@ -0,0 +1,13 @@ +extends Control + +func _on_adventure_button_pressed() -> void: + pass # Go to run tree + +func _on_decoder_button_pressed() -> void: + pass # Go to decoder + +func _on_stone_button_pressed() -> void: + pass # Go to stone powers and progress + +func _on_loadout_button_pressed() -> void: + pass # Go to deck viewer and loadout diff --git a/scripts/scenes/home_base.tscn b/scripts/scenes/home_base.tscn new file mode 100644 index 0000000..f62dded --- /dev/null +++ b/scripts/scenes/home_base.tscn @@ -0,0 +1,67 @@ +[gd_scene load_steps=3 format=3 uid="uid://bnk32gyp3bsir"] + +[ext_resource type="Script" path="res://scripts/scenes/home_base.gd" id="1_4udhq"] +[ext_resource type="Script" path="res://scripts/managers/pause_manager.gd" id="2_4gf2c"] + +[node name="HomeBase" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_4udhq") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Label" type="Label" parent="VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 105 +text = "Home base" +horizontal_alignment = 1 + +[node name="GridContainer" type="GridContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +theme_override_constants/h_separation = 40 +theme_override_constants/v_separation = 40 +columns = 2 + +[node name="AdventureButton" type="Button" parent="VBoxContainer/GridContainer"] +custom_minimum_size = Vector2(400, 400) +layout_mode = 2 +theme_override_font_sizes/font_size = 87 +text = "Go on an adventure" + +[node name="DecoderButton" type="Button" parent="VBoxContainer/GridContainer"] +custom_minimum_size = Vector2(400, 400) +layout_mode = 2 +theme_override_font_sizes/font_size = 87 +text = "Decode cards" + +[node name="StoneButton" type="Button" parent="VBoxContainer/GridContainer"] +custom_minimum_size = Vector2(400, 400) +layout_mode = 2 +theme_override_font_sizes/font_size = 87 +text = "Magical stone" + +[node name="LoadoutButton" type="Button" parent="VBoxContainer/GridContainer"] +custom_minimum_size = Vector2(400, 400) +layout_mode = 2 +theme_override_font_sizes/font_size = 87 +text = "Deck and inventory" + +[node name="PauseManager" type="Node" parent="."] +script = ExtResource("2_4gf2c") + +[connection signal="pressed" from="VBoxContainer/GridContainer/AdventureButton" to="." method="_on_adventure_button_pressed"] +[connection signal="pressed" from="VBoxContainer/GridContainer/DecoderButton" to="." method="_on_decoder_button_pressed"] +[connection signal="pressed" from="VBoxContainer/GridContainer/StoneButton" to="." method="_on_stone_button_pressed"] +[connection signal="pressed" from="VBoxContainer/GridContainer/LoadoutButton" to="." method="_on_loadout_button_pressed"] diff --git a/scripts/scenes/main.gd b/scripts/scenes/main.gd index a6e9012..c18e16c 100644 --- a/scripts/scenes/main.gd +++ b/scripts/scenes/main.gd @@ -3,6 +3,7 @@ extends Node @onready var combat_scene = preload("res://scenes/combat.tscn") @onready var menu_scene = preload("res://scenes/menus/main_menu.tscn") +@onready var home_base_scene = preload("res://scripts/scenes/home_base.tscn") var current_scene: Node = null @@ -30,4 +31,11 @@ func start_combat(): func combat_won(): print("Combat won") - show_main_menu() + start_home_base() + +func start_home_base(): + if current_scene: + current_scene.queue_free() + current_scene = home_base_scene.instantiate() + add_child(current_scene) + diff --git a/scripts/scenes/main_menu.gd b/scripts/scenes/main_menu.gd index f126dab..e0d6eb3 100644 --- a/scripts/scenes/main_menu.gd +++ b/scripts/scenes/main_menu.gd @@ -2,6 +2,7 @@ extends Control const OPTIONS_SCENE = preload("res://scenes/menus/options_menu.tscn") +const HOME_BASE_SCENE = preload("res://scripts/scenes/home_base.tscn") func _ready(): $CenterContainer/VBoxContainer/StartButton.pressed.connect(_on_start_pressed) @@ -30,7 +31,7 @@ func _on_quit_pressed(): func _on_continue_button_pressed() -> void: print("continue pressed") GameState.load_game() - start_combat() + get_node("/root/Main").start_home_base() func _on_options_pressed() -> void: var options = OPTIONS_SCENE.instantiate() From 28a472329f8188089353cebf5ffae405d5c4e399 Mon Sep 17 00:00:00 2001 From: Tom Metten Date: Tue, 5 Nov 2024 22:33:36 +0100 Subject: [PATCH 2/7] feat(WIP): add adventure system --- assets/UI/energy_orb.png.import | 6 +- assets/UI/scroll_base.png.import | 6 +- assets/UI/tilemap.png.import | 6 +- assets/UI/tilemap_packed.png.import | 6 +- project.godot | 1 + .../adventures/ingame_tutorial/0_combat.tres | 12 ++ .../adventures/ingame_tutorial/1_combat.tres | 13 ++ .../adventures/ingame_tutorial/2a_combat.tres | 12 ++ .../adventures/ingame_tutorial/2b_event.tres | 10 ++ .../adventures/ingame_tutorial/3_combat.tres | 12 ++ .../ingame_tutorial/adventure_map.tres | 9 ++ .../adventures/ingame_tutorial/finish.tres | 9 ++ .../adventures/ingame_tutorial/start.tres | 10 ++ scenes/adventure_map.tscn | 30 ++++ scenes/encounter_node.tscn | 38 +++++ scripts/data/adventure_map_data.gd | 5 + scripts/data/combat_encounter_node_data.gd | 4 + scripts/data/encounter_node_data.gd | 5 + scripts/data/enemy_data.gd | 6 + scripts/data/event_encounter_node_data.gd | 4 + scripts/data/finish_encounter_node_data.gd | 4 + scripts/data/start_encounter_node_data.gd | 5 + scripts/managers/adventure_manager.gd | 147 ++++++++++++++++++ scripts/managers/decoder_manager.gd | 1 - scripts/managers/hand_manager.gd | 1 - scripts/scenes/encounter_node.gd | 64 ++++++++ scripts/scenes/main_menu.gd | 12 +- scripts/systems/adventure_system.gd | 136 ++++++++++++++++ scripts/utils/circle.gd | 8 + 29 files changed, 565 insertions(+), 17 deletions(-) create mode 100644 resources/adventures/ingame_tutorial/0_combat.tres create mode 100644 resources/adventures/ingame_tutorial/1_combat.tres create mode 100644 resources/adventures/ingame_tutorial/2a_combat.tres create mode 100644 resources/adventures/ingame_tutorial/2b_event.tres create mode 100644 resources/adventures/ingame_tutorial/3_combat.tres create mode 100644 resources/adventures/ingame_tutorial/adventure_map.tres create mode 100644 resources/adventures/ingame_tutorial/finish.tres create mode 100644 resources/adventures/ingame_tutorial/start.tres create mode 100644 scenes/adventure_map.tscn create mode 100644 scenes/encounter_node.tscn create mode 100644 scripts/data/adventure_map_data.gd create mode 100644 scripts/data/combat_encounter_node_data.gd create mode 100644 scripts/data/encounter_node_data.gd create mode 100644 scripts/data/enemy_data.gd create mode 100644 scripts/data/event_encounter_node_data.gd create mode 100644 scripts/data/finish_encounter_node_data.gd create mode 100644 scripts/data/start_encounter_node_data.gd create mode 100644 scripts/managers/adventure_manager.gd create mode 100644 scripts/scenes/encounter_node.gd create mode 100644 scripts/systems/adventure_system.gd create mode 100644 scripts/utils/circle.gd diff --git a/assets/UI/energy_orb.png.import b/assets/UI/energy_orb.png.import index 2d3a3a2..c61b1c5 100644 --- a/assets/UI/energy_orb.png.import +++ b/assets/UI/energy_orb.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://dyx76tn885md3" -path="res://.godot/imported/energy_orb.png-8af805f3f4e2d178d867da22867062b3.ctex" +path="res://.godot/imported/energy_orb.png-2cbe75b8af4388a02af2b14d7f4c3fa2.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/ui/energy_orb.png" -dest_files=["res://.godot/imported/energy_orb.png-8af805f3f4e2d178d867da22867062b3.ctex"] +source_file="res://assets/UI/energy_orb.png" +dest_files=["res://.godot/imported/energy_orb.png-2cbe75b8af4388a02af2b14d7f4c3fa2.ctex"] [params] diff --git a/assets/UI/scroll_base.png.import b/assets/UI/scroll_base.png.import index ed51b06..9e486d7 100644 --- a/assets/UI/scroll_base.png.import +++ b/assets/UI/scroll_base.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://df58t5bfmj70a" -path="res://.godot/imported/scroll_base.png-791e7b490ba08675e2067537e744575f.ctex" +path="res://.godot/imported/scroll_base.png-693a0b068424563664384c6eb0209196.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/ui/scroll_base.png" -dest_files=["res://.godot/imported/scroll_base.png-791e7b490ba08675e2067537e744575f.ctex"] +source_file="res://assets/UI/scroll_base.png" +dest_files=["res://.godot/imported/scroll_base.png-693a0b068424563664384c6eb0209196.ctex"] [params] diff --git a/assets/UI/tilemap.png.import b/assets/UI/tilemap.png.import index 71dc670..ac09591 100644 --- a/assets/UI/tilemap.png.import +++ b/assets/UI/tilemap.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://dmvsmadpgt07f" -path="res://.godot/imported/tilemap.png-cc54adafb85d770760bc0268fc94ca9e.ctex" +path="res://.godot/imported/tilemap.png-083a8bbebe37cff77c4a1b9df77bc47f.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/ui/tilemap.png" -dest_files=["res://.godot/imported/tilemap.png-cc54adafb85d770760bc0268fc94ca9e.ctex"] +source_file="res://assets/UI/tilemap.png" +dest_files=["res://.godot/imported/tilemap.png-083a8bbebe37cff77c4a1b9df77bc47f.ctex"] [params] diff --git a/assets/UI/tilemap_packed.png.import b/assets/UI/tilemap_packed.png.import index 73156e4..450a71e 100644 --- a/assets/UI/tilemap_packed.png.import +++ b/assets/UI/tilemap_packed.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://b27cwck0oyrc" -path="res://.godot/imported/tilemap_packed.png-de9d7af106cbb7c482b9af8055d029ba.ctex" +path="res://.godot/imported/tilemap_packed.png-42b0b930e1378971414a5562b18c2f23.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/ui/tilemap_packed.png" -dest_files=["res://.godot/imported/tilemap_packed.png-de9d7af106cbb7c482b9af8055d029ba.ctex"] +source_file="res://assets/UI/tilemap_packed.png" +dest_files=["res://.godot/imported/tilemap_packed.png-42b0b930e1378971414a5562b18c2f23.ctex"] [params] diff --git a/project.godot b/project.godot index e82a811..3db71b7 100644 --- a/project.godot +++ b/project.godot @@ -23,6 +23,7 @@ config/features=PackedStringArray("4.3", "GL Compatibility") ConfigManager="*res://scripts/managers/config_manager.gd" GameState="*res://scripts/autoload/game_state.gd" +AdventureSystem="*res://scripts/systems/adventure_system.gd" [display] diff --git a/resources/adventures/ingame_tutorial/0_combat.tres b/resources/adventures/ingame_tutorial/0_combat.tres new file mode 100644 index 0000000..024c5d5 --- /dev/null +++ b/resources/adventures/ingame_tutorial/0_combat.tres @@ -0,0 +1,12 @@ +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=5 format=3 uid="uid://dsohl2qco44tx"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_w06wc"] +[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_7oxnr"] +[ext_resource type="Resource" uid="uid://b61wo0msir67x" path="res://resources/adventures/ingame_tutorial/1_combat.tres" id="2_lvvpg"] +[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_1bpls"] + +[resource] +script = ExtResource("3_1bpls") +enemies = Array[ExtResource("2_7oxnr")]([]) +childNodes = Array[ExtResource("1_w06wc")]([ExtResource("2_lvvpg")]) +completed = false diff --git a/resources/adventures/ingame_tutorial/1_combat.tres b/resources/adventures/ingame_tutorial/1_combat.tres new file mode 100644 index 0000000..9fdf100 --- /dev/null +++ b/resources/adventures/ingame_tutorial/1_combat.tres @@ -0,0 +1,13 @@ +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://b61wo0msir67x"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_q1hg8"] +[ext_resource type="Resource" uid="uid://hixs5ub5k4we" path="res://resources/adventures/ingame_tutorial/2a_combat.tres" id="2_km8iu"] +[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_t0t55"] +[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_s4ptf"] +[ext_resource type="Resource" uid="uid://cxl62hprsbcw1" path="res://resources/adventures/ingame_tutorial/2b_event.tres" id="3_yhy31"] + +[resource] +script = ExtResource("3_s4ptf") +enemies = Array[ExtResource("2_t0t55")]([]) +childNodes = Array[ExtResource("1_q1hg8")]([ExtResource("2_km8iu"), ExtResource("3_yhy31")]) +completed = false diff --git a/resources/adventures/ingame_tutorial/2a_combat.tres b/resources/adventures/ingame_tutorial/2a_combat.tres new file mode 100644 index 0000000..0c67ad8 --- /dev/null +++ b/resources/adventures/ingame_tutorial/2a_combat.tres @@ -0,0 +1,12 @@ +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=5 format=3 uid="uid://hixs5ub5k4we"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_eejxk"] +[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_dr5jw"] +[ext_resource type="Resource" uid="uid://xpw7xp6klhla" path="res://resources/adventures/ingame_tutorial/3_combat.tres" id="2_fxa05"] +[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_qyrbs"] + +[resource] +script = ExtResource("3_qyrbs") +enemies = Array[ExtResource("2_dr5jw")]([]) +childNodes = Array[ExtResource("1_eejxk")]([ExtResource("2_fxa05")]) +completed = false diff --git a/resources/adventures/ingame_tutorial/2b_event.tres b/resources/adventures/ingame_tutorial/2b_event.tres new file mode 100644 index 0000000..49d4ca5 --- /dev/null +++ b/resources/adventures/ingame_tutorial/2b_event.tres @@ -0,0 +1,10 @@ +[gd_resource type="Resource" script_class="EventEncounterNodeData" load_steps=4 format=3 uid="uid://cxl62hprsbcw1"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_xxh5h"] +[ext_resource type="Script" path="res://scripts/data/event_encounter_node_data.gd" id="2_811ok"] +[ext_resource type="Resource" uid="uid://xpw7xp6klhla" path="res://resources/adventures/ingame_tutorial/3_combat.tres" id="2_n5lnf"] + +[resource] +script = ExtResource("2_811ok") +childNodes = Array[ExtResource("1_xxh5h")]([ExtResource("2_n5lnf")]) +completed = false diff --git a/resources/adventures/ingame_tutorial/3_combat.tres b/resources/adventures/ingame_tutorial/3_combat.tres new file mode 100644 index 0000000..fdc9f71 --- /dev/null +++ b/resources/adventures/ingame_tutorial/3_combat.tres @@ -0,0 +1,12 @@ +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=5 format=3 uid="uid://xpw7xp6klhla"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_o4v54"] +[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_y3pue"] +[ext_resource type="Resource" uid="uid://h7ww7yma5ddj" path="res://resources/adventures/ingame_tutorial/finish.tres" id="2_yb1tn"] +[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_kx66a"] + +[resource] +script = ExtResource("3_kx66a") +enemies = Array[ExtResource("2_y3pue")]([]) +childNodes = Array[ExtResource("1_o4v54")]([ExtResource("2_yb1tn")]) +completed = false diff --git a/resources/adventures/ingame_tutorial/adventure_map.tres b/resources/adventures/ingame_tutorial/adventure_map.tres new file mode 100644 index 0000000..5b7a295 --- /dev/null +++ b/resources/adventures/ingame_tutorial/adventure_map.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="AdventureMapData" load_steps=3 format=3 uid="uid://cq1h50d6agd3j"] + +[ext_resource type="Resource" uid="uid://cldmpsek0wxvd" path="res://resources/adventures/ingame_tutorial/start.tres" id="1_l4584"] +[ext_resource type="Script" path="res://scripts/data/adventure_map_data.gd" id="2_ic3nw"] + +[resource] +script = ExtResource("2_ic3nw") +rootEncounterNode = ExtResource("1_l4584") +auto_start = true diff --git a/resources/adventures/ingame_tutorial/finish.tres b/resources/adventures/ingame_tutorial/finish.tres new file mode 100644 index 0000000..b537727 --- /dev/null +++ b/resources/adventures/ingame_tutorial/finish.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="FinishEncounterNodeData" load_steps=3 format=3 uid="uid://h7ww7yma5ddj"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_h5xn1"] +[ext_resource type="Script" path="res://scripts/data/finish_encounter_node_data.gd" id="2_isi6o"] + +[resource] +script = ExtResource("2_isi6o") +childNodes = Array[ExtResource("1_h5xn1")]([]) +completed = false diff --git a/resources/adventures/ingame_tutorial/start.tres b/resources/adventures/ingame_tutorial/start.tres new file mode 100644 index 0000000..2552376 --- /dev/null +++ b/resources/adventures/ingame_tutorial/start.tres @@ -0,0 +1,10 @@ +[gd_resource type="Resource" script_class="StartEncounterNodeData" load_steps=4 format=3 uid="uid://cldmpsek0wxvd"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_72qi4"] +[ext_resource type="Script" path="res://scripts/data/start_encounter_node_data.gd" id="2_r3cwx"] +[ext_resource type="Resource" uid="uid://dsohl2qco44tx" path="res://resources/adventures/ingame_tutorial/0_combat.tres" id="2_y55f8"] + +[resource] +script = ExtResource("2_r3cwx") +childNodes = Array[ExtResource("1_72qi4")]([ExtResource("2_y55f8")]) +completed = false diff --git a/scenes/adventure_map.tscn b/scenes/adventure_map.tscn new file mode 100644 index 0000000..5e53fe9 --- /dev/null +++ b/scenes/adventure_map.tscn @@ -0,0 +1,30 @@ +[gd_scene load_steps=2 format=3 uid="uid://ffxbow25onke"] + +[ext_resource type="Script" path="res://scripts/managers/adventure_manager.gd" id="1_ryxis"] + +[node name="AdventureMap" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ScrollContainer" type="ScrollContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +horizontal_scroll_mode = 2 +vertical_scroll_mode = 0 + +[node name="AdventureStages" type="HBoxContainer" parent="ScrollContainer"] +layout_mode = 2 + +[node name="LinesContainer" type="Node2D" parent="ScrollContainer"] +z_index = -1 + +[node name="AdventureManager" type="Node" parent="."] +script = ExtResource("1_ryxis") diff --git a/scenes/encounter_node.tscn b/scenes/encounter_node.tscn new file mode 100644 index 0000000..24eb7b8 --- /dev/null +++ b/scenes/encounter_node.tscn @@ -0,0 +1,38 @@ +[gd_scene load_steps=3 format=3 uid="uid://bo2u58bqms64c"] + +[ext_resource type="Script" path="res://scripts/scenes/encounter_node.gd" id="1_6g2l7"] +[ext_resource type="Script" path="res://scripts/utils/circle.gd" id="2_kg7rm"] + +[node name="EncounterNode" type="Control"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_6g2l7") + +[node name="CenterContainer" type="CenterContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"] +layout_mode = 2 +theme_override_constants/separation = 8 +alignment = 1 + +[node name="Circle" type="ColorRect" parent="CenterContainer/VBoxContainer"] +custom_minimum_size = Vector2(40, 40) +layout_mode = 2 +script = ExtResource("2_kg7rm") + +[node name="Label" type="Label" parent="CenterContainer/VBoxContainer"] +layout_mode = 2 +text = "Encounter" +horizontal_alignment = 1 +vertical_alignment = 1 diff --git a/scripts/data/adventure_map_data.gd b/scripts/data/adventure_map_data.gd new file mode 100644 index 0000000..3f6ec6f --- /dev/null +++ b/scripts/data/adventure_map_data.gd @@ -0,0 +1,5 @@ +extends Resource +class_name AdventureMapData + +@export var rootEncounterNode : StartEncounterNodeData +@export var auto_start: bool = false diff --git a/scripts/data/combat_encounter_node_data.gd b/scripts/data/combat_encounter_node_data.gd new file mode 100644 index 0000000..ff30437 --- /dev/null +++ b/scripts/data/combat_encounter_node_data.gd @@ -0,0 +1,4 @@ +extends EncounterNodeData +class_name CombatEncounterNodeData + +@export var enemies: Array[EnemyData] diff --git a/scripts/data/encounter_node_data.gd b/scripts/data/encounter_node_data.gd new file mode 100644 index 0000000..7f7ca9e --- /dev/null +++ b/scripts/data/encounter_node_data.gd @@ -0,0 +1,5 @@ +extends Resource +class_name EncounterNodeData + +@export var childNodes: Array[EncounterNodeData] = [] +@export var completed: bool = false diff --git a/scripts/data/enemy_data.gd b/scripts/data/enemy_data.gd new file mode 100644 index 0000000..cbffce7 --- /dev/null +++ b/scripts/data/enemy_data.gd @@ -0,0 +1,6 @@ +extends Resource +class_name EnemyData + +@export var enemy_scene: PackedScene +@export var hitpoints: int +@export var abilities: Array = [] diff --git a/scripts/data/event_encounter_node_data.gd b/scripts/data/event_encounter_node_data.gd new file mode 100644 index 0000000..f07668f --- /dev/null +++ b/scripts/data/event_encounter_node_data.gd @@ -0,0 +1,4 @@ +extends EncounterNodeData +class_name EventEncounterNodeData + +@export var eventScene: PackedScene diff --git a/scripts/data/finish_encounter_node_data.gd b/scripts/data/finish_encounter_node_data.gd new file mode 100644 index 0000000..3c9a807 --- /dev/null +++ b/scripts/data/finish_encounter_node_data.gd @@ -0,0 +1,4 @@ +extends EncounterNodeData +class_name FinishEncounterNodeData + +@export var finishScene: PackedScene diff --git a/scripts/data/start_encounter_node_data.gd b/scripts/data/start_encounter_node_data.gd new file mode 100644 index 0000000..ff3b6a6 --- /dev/null +++ b/scripts/data/start_encounter_node_data.gd @@ -0,0 +1,5 @@ +extends EncounterNodeData +class_name StartEncounterNodeData + +func _init() -> void: + completed = true diff --git a/scripts/managers/adventure_manager.gd b/scripts/managers/adventure_manager.gd new file mode 100644 index 0000000..aa331e3 --- /dev/null +++ b/scripts/managers/adventure_manager.gd @@ -0,0 +1,147 @@ +extends Node + +@export var adventure: AdventureMapData +@onready var adventureStagesContainer: HBoxContainer = $"../ScrollContainer/HBoxContainer/AdventureStages" +@onready var lines_container: Node2D = $"../ScrollContainer/HBoxContainer/LinesContainer" + +const NODE_SCENE = preload("res://scenes/encounter_node.tscn") +@export var COLUMN_SPACING = 200 +@export var NODE_SPACING = 120 +@export var LINE_COLOR = Color("6b7280") +@export var LINE_WIDTH = 3.0 +@export var DASH_LENGTH: float = 10.0 +@export var GAP_LENGTH: float = 10.0 + +func init_adventure_from_resource(adventureResource: AdventureMapData) -> void: + adventure = adventureResource + drawMap() + +func drawMap() -> void: + # Clear existing nodes and lines + for child in adventureStagesContainer.get_children(): + child.queue_free() + for child in lines_container.get_children(): + child.queue_free() + + if not adventure or not adventure.rootEncounterNode: + return + + # Create stages (columns) based on depth + var stages = [] + _gather_stages(adventure.rootEncounterNode, 0, stages) + + # Create containers for each stage + for stage_nodes in stages: + var stage_container = VBoxContainer.new() + stage_container.custom_minimum_size.x = COLUMN_SPACING + stage_container.alignment = BoxContainer.ALIGNMENT_CENTER + stage_container.add_theme_constant_override("separation", NODE_SPACING) + adventureStagesContainer.add_child(stage_container) + + # Add nodes to the stage + for node_data in stage_nodes: + var encounter_node = _create_encounter_node(node_data) + stage_container.add_child(encounter_node) + + # Draw all connections after nodes are positioned + await get_tree().process_frame + _draw_all_connections() + +func _gather_stages(node: EncounterNodeData, depth: int, stages: Array) -> void: + # Ensure we have an array for this depth + while stages.size() <= depth: + stages.append([]) + + # Add node to its stage + stages[depth].append(node) + + # Process child nodes + for child in node.childNodes: + _gather_stages(child, depth + 1, stages) + +func _create_encounter_node(node_data: EncounterNodeData) -> Control: + var node_instance = NODE_SCENE.instantiate() + node_instance.setup(node_data) + + # Node type specific setup + if node_data is StartEncounterNodeData: + node_instance.set_start_node() + elif node_data is FinishEncounterNodeData: + node_instance.set_finish_node() + + if node_data.completed: + node_instance.set_completed() + + return node_instance + +func _draw_all_connections() -> void: + var stages = adventureStagesContainer.get_children() + + for stage_idx in range(stages.size() - 1): # Stop before last stage + var current_stage = stages[stage_idx] + var next_stage = stages[stage_idx + 1] + + for from_node in current_stage.get_children(): + var from_data = from_node.get_node_data() + + for child_data in from_data.childNodes: + for to_node in next_stage.get_children(): + if to_node.get_node_data() == child_data: + _draw_connection(from_node, to_node) + +func _draw_connection(from_node: Control, to_node: Control) -> void: + var line = Line2D.new() + line.default_color = LINE_COLOR + line.width = LINE_WIDTH + line.begin_cap_mode = Line2D.LINE_CAP_ROUND + line.end_cap_mode = Line2D.LINE_CAP_ROUND + + # Get the circles' centers in local coordinates relative to their common parent + var from_circle = from_node.get_node("CenterContainer/VBoxContainer/Circle") + var to_circle = to_node.get_node("CenterContainer/VBoxContainer/Circle") + + var from_center = from_circle.get_global_rect().get_center() + var to_center = to_circle.get_global_rect().get_center() + + # Convert points to lines_container's local coordinates + from_center = lines_container.to_local(from_center) + to_center = lines_container.to_local(to_center) + + # Calculate control points for curve + var control_offset = Vector2((to_center.x - from_center.x) * 0.5, 0) + + # Create dotted line points + var points = _create_dotted_curve_points(from_center, to_center, control_offset) + + line.points = points + lines_container.add_child(line) + +func _create_dotted_curve_points(from: Vector2, to: Vector2, control_offset: Vector2) -> PackedVector2Array: + var points := PackedVector2Array() + var curve = Curve2D.new() + + # Add points to create a bezier curve + curve.add_point(from, Vector2.ZERO, control_offset) + curve.add_point(to, -control_offset, Vector2.ZERO) + + # Settings for dash pattern + var dash_length := DASH_LENGTH + var gap_length := GAP_LENGTH + var curve_length = curve.get_baked_length() + + # Always start with the first point + points.append(from) + + # Calculate how many complete dash-gap pairs we need + var segment_length = dash_length + gap_length + var num_complete_segments = floor((curve_length - dash_length) / segment_length) + + # Create the middle dots + for i in range(num_complete_segments): + var pos = (i + 1) * segment_length + points.append(curve.sample_baked(pos)) + + # Always end with the last point + points.append(to) + + return points diff --git a/scripts/managers/decoder_manager.gd b/scripts/managers/decoder_manager.gd index cf62733..cae87a2 100644 --- a/scripts/managers/decoder_manager.gd +++ b/scripts/managers/decoder_manager.gd @@ -1,4 +1,3 @@ -# decoder_manager.gd extends Node signal aspect_decoded(card_id: String, aspect: String) diff --git a/scripts/managers/hand_manager.gd b/scripts/managers/hand_manager.gd index 53ec7ca..f5dee89 100644 --- a/scripts/managers/hand_manager.gd +++ b/scripts/managers/hand_manager.gd @@ -1,4 +1,3 @@ -# hand_manager.gd extends Node const CARD_SCENE = preload("res://scenes/card.tscn") diff --git a/scripts/scenes/encounter_node.gd b/scripts/scenes/encounter_node.gd new file mode 100644 index 0000000..e2986b5 --- /dev/null +++ b/scripts/scenes/encounter_node.gd @@ -0,0 +1,64 @@ +extends Control + +signal node_clicked(node_data: EncounterNodeData) + +var node_data: EncounterNodeData + +@onready var circle: ColorRect = $CenterContainer/VBoxContainer/Circle +@onready var label: Label = $CenterContainer/VBoxContainer/Label + +const BASE_NODE_COLOR := Color("4a4a4a") +const START_NODE_COLOR := Color("1a531a") +const FINISH_NODE_COLOR := Color("531a1a") +const COMPLETED_COLOR := Color("4a821a") +const HOVER_TINT := Color(1.2, 1.2, 1.2, 1) + +func _ready() -> void: + custom_minimum_size = Vector2(100, 100) + mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + + # Setup mouse handling + gui_input.connect(_on_gui_input) + mouse_entered.connect(_on_mouse_entered) + mouse_exited.connect(_on_mouse_exited) + +func setup(data: EncounterNodeData) -> void: + node_data = data + update_appearance() + +func get_node_data() -> EncounterNodeData: + return node_data + +func set_start_node() -> void: + circle.color = START_NODE_COLOR + label.text = "Start" + +func set_finish_node() -> void: + circle.color = FINISH_NODE_COLOR + label.text = "Final" + +func set_completed() -> void: + circle.color = COMPLETED_COLOR + +func update_appearance() -> void: + if node_data.completed: + set_completed() + + if node_data is StartEncounterNodeData: + set_start_node() + elif node_data is FinishEncounterNodeData: + set_finish_node() + else: + label.text = "Encounter" + circle.color = BASE_NODE_COLOR + +func _on_gui_input(event: InputEvent) -> void: + if event is InputEventMouseButton: + if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: + node_clicked.emit(node_data) + +func _on_mouse_entered() -> void: + modulate = HOVER_TINT + +func _on_mouse_exited() -> void: + modulate = Color.WHITE diff --git a/scripts/scenes/main_menu.gd b/scripts/scenes/main_menu.gd index e0d6eb3..4393a27 100644 --- a/scripts/scenes/main_menu.gd +++ b/scripts/scenes/main_menu.gd @@ -18,10 +18,16 @@ func _on_start_pressed(): disable_menu() GameState.start_new_run() var new_game_cutscene = load("res://resources/cutscenes/new_game/new_game_cutscene.tres") - $CutsceneSystem.play_cutscene(new_game_cutscene, start_combat) + $CutsceneSystem.play_cutscene(new_game_cutscene, start_ingame_tutorial) + +func start_ingame_tutorial() -> void: + var ingame_tutorial_adventure = load("res://resources/adventures/ingame_tutorial/adventure_map.tres") + AdventureSystem.start_adventure(ingame_tutorial_adventure) + AdventureSystem.adventure_completed.connect(_on_ingame_tutorial_completed) + +func _on_ingame_tutorial_completed(adventure: AdventureMapData): + get_node("/root/Main").start_home_base() -func start_combat() -> void: - get_node("/root/Main").start_combat() func _on_quit_pressed(): print("quit pressed") diff --git a/scripts/systems/adventure_system.gd b/scripts/systems/adventure_system.gd new file mode 100644 index 0000000..97fb049 --- /dev/null +++ b/scripts/systems/adventure_system.gd @@ -0,0 +1,136 @@ +# adventure_system.gd +extends Node + +signal adventure_started(adventure_data: AdventureMapData) +signal encounter_selected(node: EncounterNodeData) +signal encounter_completed(node: EncounterNodeData) +signal adventure_completed(adventure_data: AdventureMapData) + +const COMBAT_SCENE = preload("res://scenes/combat.tscn") +const ADVENTURE_MAP_SCENE = preload("res://scenes/adventure_map.tscn") + +var current_adventure: AdventureMapData +var current_node: EncounterNodeData +var current_map_instance: Node + +func start_adventure(adventure_data: AdventureMapData) -> void: + current_adventure = adventure_data + current_node = null + + # Emit signal for any listeners + adventure_started.emit(adventure_data) + + # Create and show the adventure map + if current_map_instance: + current_map_instance.queue_free() + + current_map_instance = ADVENTURE_MAP_SCENE.instantiate() + get_tree().root.add_child(current_map_instance) + + # Initialize the map with our adventure data + current_map_instance.init_adventure(adventure_data) + + # Check if we should auto-start an encounter + if adventure_data.auto_start and adventure_data.rootEncounterNode.childNodes.size() > 0: + # Hide the map immediately if we're auto-starting + current_map_instance.hide() + _auto_start_first_encounter(adventure_data.rootEncounterNode) + else: + current_map_instance.show() + +func _auto_start_first_encounter(start_node: StartEncounterNodeData) -> void: + # Get the first available child node + if start_node.childNodes.size() > 0: + var first_encounter = start_node.childNodes[0] + select_encounter(first_encounter) + +func select_encounter(encounter_node: EncounterNodeData) -> void: + if not can_select_encounter(encounter_node): + return + + current_node = encounter_node + encounter_selected.emit(encounter_node) + + # Handle different encounter types + if encounter_node is CombatEncounterNodeData: + start_combat(encounter_node) + elif encounter_node is EventEncounterNodeData: + start_event(encounter_node) + elif encounter_node is FinishEncounterNodeData: + handle_finish_encounter(encounter_node) + +func can_select_encounter(node: EncounterNodeData) -> bool: + # Can't select already completed encounters + if node.completed: + return false + + # Must have a completed parent (unless it's the start node) + if not node is StartEncounterNodeData: + var has_completed_parent = false + for stage in current_map_instance.get_all_nodes(): + for potential_parent in stage: + if potential_parent.childNodes.has(node) and potential_parent.completed: + has_completed_parent = true + break + if not has_completed_parent: + return false + + return true + +func complete_current_encounter() -> void: + if current_node: + current_node.completed = true + encounter_completed.emit(current_node) + current_map_instance.update_map() + adventure_completed.emit(current_adventure) + # Save progress if needed + # GameState.save_game() + +func start_combat(combat_node: CombatEncounterNodeData) -> void: + var combat_scene = COMBAT_SCENE.instantiate() + + # Get the enemy container + var enemy_container = combat_scene.get_node("Enemy").get_parent() + # Remove the default enemy + enemy_container.remove_child(combat_scene.get_node("Enemy")) + + # Create an HBoxContainer for multiple enemies + var enemies_container = HBoxContainer.new() + enemies_container.alignment = BoxContainer.ALIGNMENT_CENTER + enemies_container.add_theme_constant_override("separation", 20) + enemy_container.add_child(enemies_container) + + # Add each enemy from the combat node + for enemy_data in combat_node.enemies: + var enemy_instance = enemy_data.enemy_scene.instantiate() + enemies_container.add_child(enemy_instance) + # Setup enemy with its data if needed + if enemy_instance.has_method("setup"): + enemy_instance.setup(enemy_data) + + get_tree().root.add_child(combat_scene) + current_map_instance.hide() + +func start_event(event_node: EventEncounterNodeData) -> void: + var event_scene = event_node.eventScene.instantiate() + # Setup event scene with any necessary data from event_node + get_tree().root.add_child(event_scene) + current_map_instance.hide() + +func handle_finish_encounter(finish_node: FinishEncounterNodeData) -> void: + var finish_scene = finish_node.finishScene.instantiate() + get_tree().root.add_child(finish_scene) + current_map_instance.hide() + +func return_to_map() -> void: + current_map_instance.show() + +# Utility functions for other systems to use +func get_current_adventure() -> AdventureMapData: + return current_adventure + +func get_current_node() -> EncounterNodeData: + return current_node + +func is_node_available(node: EncounterNodeData) -> bool: + return can_select_encounter(node) diff --git a/scripts/utils/circle.gd b/scripts/utils/circle.gd new file mode 100644 index 0000000..174bb52 --- /dev/null +++ b/scripts/utils/circle.gd @@ -0,0 +1,8 @@ +extends ColorRect + +func _draw() -> void: + draw_circle(size / 2, size.x / 2, color) + +func _ready() -> void: + # Make the ColorRect transparent, we'll only show the circle + color = Color.TRANSPARENT From d9ea4ad2aeae86c030b1c99e1c026804626901f6 Mon Sep 17 00:00:00 2001 From: Thomas Metten Date: Wed, 6 Nov 2024 00:41:14 +0100 Subject: [PATCH 3/7] feat(WIP): refactor combat and add cultist enemy to adventure --- assets/UI/energy_orb.png.import | 6 +- assets/UI/scroll_base.png.import | 6 +- assets/UI/tilemap.png.import | 6 +- assets/UI/tilemap_packed.png.import | 6 +- project.godot | 2 + .../adventures/ingame_tutorial/0_combat.tres | 5 +- .../adventures/ingame_tutorial/1_combat.tres | 5 +- .../adventures/ingame_tutorial/2a_combat.tres | 5 +- .../adventures/ingame_tutorial/3_combat.tres | 5 +- .../adventures/ingame_tutorial/start.tres | 2 +- resources/enemies/cultist.tres | 10 ++ scenes/adventure_map.tscn | 2 +- scenes/combat.tscn | 63 +++++-------- scenes/encounter_node.tscn | 2 +- scenes/enemies/cultist.tscn | 16 ++++ scenes/menus/main_menu.tscn | 10 +- scripts/data/enemy_data.gd | 2 +- scripts/decode_ui.gd | 5 - scripts/enemy.gd | 17 +++- scripts/managers/adventure_manager.gd | 6 +- scripts/managers/combat_manager.gd | 10 +- scripts/scenes/card.gd | 22 +++-- scripts/scenes/combat.gd | 12 +++ scripts/scenes/encounter_node.gd | 21 ++++- scripts/scenes/main_menu.gd | 2 +- scripts/systems/adventure_system.gd | 91 +++++++++++-------- scripts/systems/background_system.gd | 26 +++--- 27 files changed, 209 insertions(+), 156 deletions(-) create mode 100644 resources/enemies/cultist.tres create mode 100644 scenes/enemies/cultist.tscn diff --git a/assets/UI/energy_orb.png.import b/assets/UI/energy_orb.png.import index c61b1c5..2d3a3a2 100644 --- a/assets/UI/energy_orb.png.import +++ b/assets/UI/energy_orb.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://dyx76tn885md3" -path="res://.godot/imported/energy_orb.png-2cbe75b8af4388a02af2b14d7f4c3fa2.ctex" +path="res://.godot/imported/energy_orb.png-8af805f3f4e2d178d867da22867062b3.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/UI/energy_orb.png" -dest_files=["res://.godot/imported/energy_orb.png-2cbe75b8af4388a02af2b14d7f4c3fa2.ctex"] +source_file="res://assets/ui/energy_orb.png" +dest_files=["res://.godot/imported/energy_orb.png-8af805f3f4e2d178d867da22867062b3.ctex"] [params] diff --git a/assets/UI/scroll_base.png.import b/assets/UI/scroll_base.png.import index 9e486d7..ed51b06 100644 --- a/assets/UI/scroll_base.png.import +++ b/assets/UI/scroll_base.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://df58t5bfmj70a" -path="res://.godot/imported/scroll_base.png-693a0b068424563664384c6eb0209196.ctex" +path="res://.godot/imported/scroll_base.png-791e7b490ba08675e2067537e744575f.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/UI/scroll_base.png" -dest_files=["res://.godot/imported/scroll_base.png-693a0b068424563664384c6eb0209196.ctex"] +source_file="res://assets/ui/scroll_base.png" +dest_files=["res://.godot/imported/scroll_base.png-791e7b490ba08675e2067537e744575f.ctex"] [params] diff --git a/assets/UI/tilemap.png.import b/assets/UI/tilemap.png.import index ac09591..71dc670 100644 --- a/assets/UI/tilemap.png.import +++ b/assets/UI/tilemap.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://dmvsmadpgt07f" -path="res://.godot/imported/tilemap.png-083a8bbebe37cff77c4a1b9df77bc47f.ctex" +path="res://.godot/imported/tilemap.png-cc54adafb85d770760bc0268fc94ca9e.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/UI/tilemap.png" -dest_files=["res://.godot/imported/tilemap.png-083a8bbebe37cff77c4a1b9df77bc47f.ctex"] +source_file="res://assets/ui/tilemap.png" +dest_files=["res://.godot/imported/tilemap.png-cc54adafb85d770760bc0268fc94ca9e.ctex"] [params] diff --git a/assets/UI/tilemap_packed.png.import b/assets/UI/tilemap_packed.png.import index 450a71e..73156e4 100644 --- a/assets/UI/tilemap_packed.png.import +++ b/assets/UI/tilemap_packed.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://b27cwck0oyrc" -path="res://.godot/imported/tilemap_packed.png-42b0b930e1378971414a5562b18c2f23.ctex" +path="res://.godot/imported/tilemap_packed.png-de9d7af106cbb7c482b9af8055d029ba.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/UI/tilemap_packed.png" -dest_files=["res://.godot/imported/tilemap_packed.png-42b0b930e1378971414a5562b18c2f23.ctex"] +source_file="res://assets/ui/tilemap_packed.png" +dest_files=["res://.godot/imported/tilemap_packed.png-de9d7af106cbb7c482b9af8055d029ba.ctex"] [params] diff --git a/project.godot b/project.godot index 3db71b7..3471470 100644 --- a/project.godot +++ b/project.godot @@ -24,6 +24,8 @@ config/features=PackedStringArray("4.3", "GL Compatibility") ConfigManager="*res://scripts/managers/config_manager.gd" GameState="*res://scripts/autoload/game_state.gd" AdventureSystem="*res://scripts/systems/adventure_system.gd" +BackgroundSystem="*res://scripts/systems/background_system.gd" +CutsceneSystem="*res://scripts/systems/cutscene_system.gd" [display] diff --git a/resources/adventures/ingame_tutorial/0_combat.tres b/resources/adventures/ingame_tutorial/0_combat.tres index 024c5d5..c7f7521 100644 --- a/resources/adventures/ingame_tutorial/0_combat.tres +++ b/resources/adventures/ingame_tutorial/0_combat.tres @@ -1,12 +1,13 @@ -[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=5 format=3 uid="uid://dsohl2qco44tx"] +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://dsohl2qco44tx"] [ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_w06wc"] [ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_7oxnr"] [ext_resource type="Resource" uid="uid://b61wo0msir67x" path="res://resources/adventures/ingame_tutorial/1_combat.tres" id="2_lvvpg"] [ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_1bpls"] +[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="4_sukwl"] [resource] script = ExtResource("3_1bpls") -enemies = Array[ExtResource("2_7oxnr")]([]) +enemies = Array[ExtResource("2_7oxnr")]([ExtResource("4_sukwl")]) childNodes = Array[ExtResource("1_w06wc")]([ExtResource("2_lvvpg")]) completed = false diff --git a/resources/adventures/ingame_tutorial/1_combat.tres b/resources/adventures/ingame_tutorial/1_combat.tres index 9fdf100..7490d6e 100644 --- a/resources/adventures/ingame_tutorial/1_combat.tres +++ b/resources/adventures/ingame_tutorial/1_combat.tres @@ -1,13 +1,14 @@ -[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://b61wo0msir67x"] +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=7 format=3 uid="uid://b61wo0msir67x"] [ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_q1hg8"] [ext_resource type="Resource" uid="uid://hixs5ub5k4we" path="res://resources/adventures/ingame_tutorial/2a_combat.tres" id="2_km8iu"] [ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_t0t55"] [ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_s4ptf"] [ext_resource type="Resource" uid="uid://cxl62hprsbcw1" path="res://resources/adventures/ingame_tutorial/2b_event.tres" id="3_yhy31"] +[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="5_ad11l"] [resource] script = ExtResource("3_s4ptf") -enemies = Array[ExtResource("2_t0t55")]([]) +enemies = Array[ExtResource("2_t0t55")]([ExtResource("5_ad11l"), ExtResource("5_ad11l")]) childNodes = Array[ExtResource("1_q1hg8")]([ExtResource("2_km8iu"), ExtResource("3_yhy31")]) completed = false diff --git a/resources/adventures/ingame_tutorial/2a_combat.tres b/resources/adventures/ingame_tutorial/2a_combat.tres index 0c67ad8..26e325d 100644 --- a/resources/adventures/ingame_tutorial/2a_combat.tres +++ b/resources/adventures/ingame_tutorial/2a_combat.tres @@ -1,12 +1,13 @@ -[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=5 format=3 uid="uid://hixs5ub5k4we"] +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://hixs5ub5k4we"] [ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_eejxk"] [ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_dr5jw"] [ext_resource type="Resource" uid="uid://xpw7xp6klhla" path="res://resources/adventures/ingame_tutorial/3_combat.tres" id="2_fxa05"] [ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_qyrbs"] +[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="4_cwl26"] [resource] script = ExtResource("3_qyrbs") -enemies = Array[ExtResource("2_dr5jw")]([]) +enemies = Array[ExtResource("2_dr5jw")]([ExtResource("4_cwl26"), ExtResource("4_cwl26"), ExtResource("4_cwl26")]) childNodes = Array[ExtResource("1_eejxk")]([ExtResource("2_fxa05")]) completed = false diff --git a/resources/adventures/ingame_tutorial/3_combat.tres b/resources/adventures/ingame_tutorial/3_combat.tres index fdc9f71..f73e701 100644 --- a/resources/adventures/ingame_tutorial/3_combat.tres +++ b/resources/adventures/ingame_tutorial/3_combat.tres @@ -1,12 +1,13 @@ -[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=5 format=3 uid="uid://xpw7xp6klhla"] +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://xpw7xp6klhla"] [ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_o4v54"] [ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_y3pue"] [ext_resource type="Resource" uid="uid://h7ww7yma5ddj" path="res://resources/adventures/ingame_tutorial/finish.tres" id="2_yb1tn"] [ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_kx66a"] +[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="4_hhtf0"] [resource] script = ExtResource("3_kx66a") -enemies = Array[ExtResource("2_y3pue")]([]) +enemies = Array[ExtResource("2_y3pue")]([ExtResource("4_hhtf0"), ExtResource("4_hhtf0"), ExtResource("4_hhtf0"), ExtResource("4_hhtf0")]) childNodes = Array[ExtResource("1_o4v54")]([ExtResource("2_yb1tn")]) completed = false diff --git a/resources/adventures/ingame_tutorial/start.tres b/resources/adventures/ingame_tutorial/start.tres index 2552376..eab3341 100644 --- a/resources/adventures/ingame_tutorial/start.tres +++ b/resources/adventures/ingame_tutorial/start.tres @@ -7,4 +7,4 @@ [resource] script = ExtResource("2_r3cwx") childNodes = Array[ExtResource("1_72qi4")]([ExtResource("2_y55f8")]) -completed = false +completed = true diff --git a/resources/enemies/cultist.tres b/resources/enemies/cultist.tres new file mode 100644 index 0000000..9f3761a --- /dev/null +++ b/resources/enemies/cultist.tres @@ -0,0 +1,10 @@ +[gd_resource type="Resource" script_class="EnemyData" load_steps=3 format=3 uid="uid://bjkxuf2s1608i"] + +[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="1_g2v6o"] +[ext_resource type="PackedScene" uid="uid://cbtac01eivwim" path="res://scenes/enemies/cultist.tscn" id="1_uchoq"] + +[resource] +script = ExtResource("1_g2v6o") +enemy_scene = ExtResource("1_uchoq") +health = 10 +abilities = [] diff --git a/scenes/adventure_map.tscn b/scenes/adventure_map.tscn index 5e53fe9..a8e4b1d 100644 --- a/scenes/adventure_map.tscn +++ b/scenes/adventure_map.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=2 format=3 uid="uid://ffxbow25onke"] +[gd_scene load_steps=2 format=3 uid="uid://b7vx0syoj8olw"] [ext_resource type="Script" path="res://scripts/managers/adventure_manager.gd" id="1_ryxis"] diff --git a/scenes/combat.tscn b/scenes/combat.tscn index 947ac23..c2ceaf3 100644 --- a/scenes/combat.tscn +++ b/scenes/combat.tscn @@ -1,14 +1,10 @@ -[gd_scene load_steps=11 format=3 uid="uid://c8frb1w88o0u0"] +[gd_scene load_steps=7 format=3 uid="uid://b3l57gnlln63g"] [ext_resource type="Script" path="res://scripts/scenes/combat.gd" id="1_n5yyl"] [ext_resource type="Script" path="res://scripts/managers/combat_manager.gd" id="2_305ab"] [ext_resource type="Script" path="res://scripts/managers/hand_manager.gd" id="3_bdk6y"] -[ext_resource type="Script" path="res://scripts/managers/decoder_manager.gd" id="4_vgm5a"] [ext_resource type="Script" path="res://scripts/managers/pause_manager.gd" id="5_pl43j"] -[ext_resource type="Script" path="res://scripts/systems/background_system.gd" id="6_l1wfx"] [ext_resource type="Script" path="res://scripts/player.gd" id="7_dwwk6"] -[ext_resource type="Script" path="res://scripts/enemy.gd" id="8_grmhu"] -[ext_resource type="PackedScene" uid="uid://duyk27ghemr8s" path="res://scenes/decode_ui.tscn" id="9_1iax2"] [ext_resource type="Texture2D" uid="uid://dyx76tn885md3" path="res://assets/ui/energy_orb.png" id="9_e0ys4"] [node name="Combat" type="Node2D"] @@ -20,14 +16,29 @@ script = ExtResource("2_305ab") [node name="HandManager" type="Node" parent="."] script = ExtResource("3_bdk6y") -[node name="DecoderManager" type="Node" parent="."] -script = ExtResource("4_vgm5a") - [node name="PauseManager" type="Node" parent="."] script = ExtResource("5_pl43j") -[node name="BackgroundSystem" type="Node" parent="."] -script = ExtResource("6_l1wfx") +[node name="Player" type="Node2D" parent="."] +position = Vector2(300, 300) +script = ExtResource("7_dwwk6") + +[node name="Visual" type="ColorRect" parent="Player"] +offset_right = 100.0 +offset_bottom = 100.0 +color = Color(0, 1, 0, 1) + +[node name="HealthLabel" type="Label" parent="Player"] +offset_right = 40.0 +offset_bottom = 23.0 +theme_override_font_sizes/font_size = 70 +text = "40/40" + +[node name="EnemiesContainer" type="HBoxContainer" parent="."] +offset_right = 40.0 +offset_bottom = 40.0 +theme_override_constants/separation = 20 +alignment = 1 [node name="UI" type="CanvasLayer" parent="."] @@ -105,35 +116,3 @@ theme_override_font_sizes/font_size = 140 text = "3" horizontal_alignment = 1 vertical_alignment = 1 - -[node name="DecodeUI" parent="UI" instance=ExtResource("9_1iax2")] - -[node name="Player" type="Node2D" parent="."] -position = Vector2(300, 300) -script = ExtResource("7_dwwk6") - -[node name="Visual" type="ColorRect" parent="Player"] -offset_right = 100.0 -offset_bottom = 100.0 -color = Color(0, 1, 0, 1) - -[node name="HealthLabel" type="Label" parent="Player"] -offset_right = 40.0 -offset_bottom = 23.0 -theme_override_font_sizes/font_size = 70 -text = "40/40" - -[node name="Enemy" type="Node2D" parent="."] -position = Vector2(700, 300) -script = ExtResource("8_grmhu") - -[node name="Visual" type="ColorRect" parent="Enemy"] -offset_right = 100.0 -offset_bottom = 100.0 -color = Color(1, 0, 0, 1) - -[node name="HealthLabel" type="Label" parent="Enemy"] -offset_right = 40.0 -offset_bottom = 23.0 -theme_override_font_sizes/font_size = 70 -text = "30/30" diff --git a/scenes/encounter_node.tscn b/scenes/encounter_node.tscn index 24eb7b8..72bcb78 100644 --- a/scenes/encounter_node.tscn +++ b/scenes/encounter_node.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=3 format=3 uid="uid://bo2u58bqms64c"] +[gd_scene load_steps=3 format=3 uid="uid://cidwq7l0x54g7"] [ext_resource type="Script" path="res://scripts/scenes/encounter_node.gd" id="1_6g2l7"] [ext_resource type="Script" path="res://scripts/utils/circle.gd" id="2_kg7rm"] diff --git a/scenes/enemies/cultist.tscn b/scenes/enemies/cultist.tscn new file mode 100644 index 0000000..f50a4dd --- /dev/null +++ b/scenes/enemies/cultist.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=2 format=3 uid="uid://cbtac01eivwim"] + +[ext_resource type="Script" path="res://scripts/enemy.gd" id="1_c11yq"] + +[node name="Cultist" type="Node2D"] +script = ExtResource("1_c11yq") + +[node name="ColorRect" type="ColorRect" parent="."] +custom_minimum_size = Vector2(100, 200) +offset_right = 40.0 +offset_bottom = 40.0 +color = Color(0.394879, 0.000106898, 0.394876, 1) + +[node name="HealthLabel" type="Label" parent="."] +offset_right = 40.0 +offset_bottom = 23.0 diff --git a/scenes/menus/main_menu.tscn b/scenes/menus/main_menu.tscn index c273259..c00e41b 100644 --- a/scenes/menus/main_menu.tscn +++ b/scenes/menus/main_menu.tscn @@ -1,8 +1,6 @@ -[gd_scene load_steps=4 format=3 uid="uid://bxru22786wp6n"] +[gd_scene load_steps=2 format=3 uid="uid://bxru22786wp6n"] [ext_resource type="Script" path="res://scripts/scenes/main_menu.gd" id="1_prvtn"] -[ext_resource type="Script" path="res://scripts/systems/background_system.gd" id="2_bpmgo"] -[ext_resource type="Script" path="res://scripts/systems/cutscene_system.gd" id="3_fo7iw"] [node name="MainMenu" type="CenterContainer"] anchors_preset = 15 @@ -53,9 +51,3 @@ custom_minimum_size = Vector2(400, 100) layout_mode = 2 theme_override_font_sizes/font_size = 32 text = "Quit" - -[node name="BackgroundSystem" type="Node" parent="."] -script = ExtResource("2_bpmgo") - -[node name="CutsceneSystem" type="Node" parent="."] -script = ExtResource("3_fo7iw") diff --git a/scripts/data/enemy_data.gd b/scripts/data/enemy_data.gd index cbffce7..22c87b1 100644 --- a/scripts/data/enemy_data.gd +++ b/scripts/data/enemy_data.gd @@ -2,5 +2,5 @@ extends Resource class_name EnemyData @export var enemy_scene: PackedScene -@export var hitpoints: int +@export var health: int @export var abilities: Array = [] diff --git a/scripts/decode_ui.gd b/scripts/decode_ui.gd index 0b02341..c2c6ba3 100644 --- a/scripts/decode_ui.gd +++ b/scripts/decode_ui.gd @@ -16,11 +16,6 @@ const COSTS = { func _ready() -> void: hide() - update_points() - -func update_points() -> void: - var decoder = get_node("/root/Main/Combat/DecoderManager") - decode_points_label.text = "Decode Points: %d" % decoder.available_points func show_for_card(card: Node) -> void: # Clear previous options diff --git a/scripts/enemy.gd b/scripts/enemy.gd index de7e5a4..be13518 100644 --- a/scripts/enemy.gd +++ b/scripts/enemy.gd @@ -2,7 +2,6 @@ extends Node var health: int = 30 var max_health: int = 30 -var intent_damage: int = 6 @onready var health_label: Label = $HealthLabel @@ -13,13 +12,21 @@ func take_damage(amount: int) -> void: health -= amount update_health_display() if health <= 0: - get_node("/root/Main").combat_won() - + queue_free() func take_turn() -> void: print("Enemy turn") - var player = get_node("../Player") - player.take_damage(intent_damage) + var player = get_node("/root/Combat/Player") + if player: + # This will be replaced with actual ability usage + pass func update_health_display() -> void: health_label.text = str(health) + "/" + str(max_health) + +func setup(enemy_data: EnemyData) -> void: + if not is_node_ready(): + await ready + health = enemy_data.health + max_health = enemy_data.health + update_health_display() diff --git a/scripts/managers/adventure_manager.gd b/scripts/managers/adventure_manager.gd index aa331e3..9b83226 100644 --- a/scripts/managers/adventure_manager.gd +++ b/scripts/managers/adventure_manager.gd @@ -1,8 +1,8 @@ extends Node @export var adventure: AdventureMapData -@onready var adventureStagesContainer: HBoxContainer = $"../ScrollContainer/HBoxContainer/AdventureStages" -@onready var lines_container: Node2D = $"../ScrollContainer/HBoxContainer/LinesContainer" +@onready var adventureStagesContainer: HBoxContainer = $"../ScrollContainer/AdventureStages" +@onready var lines_container: Node2D = $"../ScrollContainer/LinesContainer" const NODE_SCENE = preload("res://scenes/encounter_node.tscn") @export var COLUMN_SPACING = 200 @@ -12,7 +12,7 @@ const NODE_SCENE = preload("res://scenes/encounter_node.tscn") @export var DASH_LENGTH: float = 10.0 @export var GAP_LENGTH: float = 10.0 -func init_adventure_from_resource(adventureResource: AdventureMapData) -> void: +func init_adventure(adventureResource: AdventureMapData) -> void: adventure = adventureResource drawMap() diff --git a/scripts/managers/combat_manager.gd b/scripts/managers/combat_manager.gd index 9e87606..a157026 100644 --- a/scripts/managers/combat_manager.gd +++ b/scripts/managers/combat_manager.gd @@ -5,7 +5,7 @@ var max_energy: int = 3 var is_player_turn: bool = true @onready var hand_manager: Node = $"../HandManager" -@onready var enemy: Node = $"../Enemy" +@onready var enemies_container: Node = $"../EnemiesContainer" @onready var energy_label: Label = $"../UI/HUD/EnergyMarginContainer/EnergyTexture/EnergyLabel" func _ready() -> void: @@ -22,8 +22,12 @@ func start_turn() -> void: func end_turn() -> void: hand_manager.discard_hand() is_player_turn = false - # Enemy turn - enemy.take_turn() + + # Enemy turns + for enemy in enemies_container.get_children(): + if enemy.has_method("take_turn"): + enemy.take_turn() + # Back to player start_turn() diff --git a/scripts/scenes/card.gd b/scripts/scenes/card.gd index 06b879a..93b58fc 100644 --- a/scripts/scenes/card.gd +++ b/scripts/scenes/card.gd @@ -1,4 +1,3 @@ -# card.gd extends Control signal card_played(card) @@ -81,12 +80,12 @@ func _gui_input(event: InputEvent) -> void: if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed: show_decode_menu() elif event.button_index == MOUSE_BUTTON_LEFT and event.pressed: - var combat_manager = get_node("/root/Main/Combat/CombatManager") - if combat_manager.can_play_card(card_data.energy_cost): + var combat_manager = get_node("/root/Combat/CombatManager") + if combat_manager and combat_manager.can_play_card(card_data.energy_cost): play_card() func play_card() -> void: - var combat_manager = get_node("/root/Main/Combat/CombatManager") + var combat_manager = get_node("/root/Combat/CombatManager") combat_manager.spend_energy(card_data.energy_cost) execute_effect() card_played.emit(self) @@ -95,11 +94,16 @@ func play_card() -> void: func execute_effect() -> void: match card_data.card_type: "attack": - var enemy = get_node("/root/Main/Combat/Enemy") - enemy.take_damage(get_effect_value()) + var enemies_container = get_node("/root/Combat/EnemiesContainer") + if enemies_container and enemies_container.get_child_count() > 0: + # For now, just target the first enemy + # You might want to implement target selection later + var enemy = enemies_container.get_child(0) + enemy.take_damage(get_effect_value()) "block": - var player = get_node("/root/Main/Combat/Player") - player.gain_block(get_effect_value()) + var player = get_node("/root/Combat/Player") + if player: + player.gain_block(get_effect_value()) func show_decode_menu() -> void: var popup = PopupMenu.new() @@ -124,7 +128,7 @@ func show_decode_menu() -> void: popup.popup() func _on_decode_option_selected(id: int) -> void: - var decoder = get_node("/root/Main/Combat/DecoderManager") + var decoder = get_node("/root/Combat/DecoderManager") match id: 0: decoder.decode_aspect(self, "cost") 1: decoder.decode_aspect(self, "type") diff --git a/scripts/scenes/combat.gd b/scripts/scenes/combat.gd index 97ccef2..ec5b2bc 100644 --- a/scripts/scenes/combat.gd +++ b/scripts/scenes/combat.gd @@ -32,6 +32,18 @@ func _ready() -> void: energy_container.anchor_right = 1.0 $UI/HUD/EndTurnMarginContainer/EndTurnButton.pressed.connect(_on_end_turn_pressed) + + # Connect to enemy death or victory condition + $EnemiesContainer.child_exiting_tree.connect(_check_combat_state) + +func _check_combat_state(_node) -> void: + # If all enemies are dead + if $EnemiesContainer.get_child_count() <= 1: # <= 1 because the signal fires before the node is fully removed + _on_combat_won() + +func _on_combat_won() -> void: + AdventureSystem.complete_current_encounter() + queue_free() func _on_end_turn_pressed() -> void: combat_manager.end_turn() diff --git a/scripts/scenes/encounter_node.gd b/scripts/scenes/encounter_node.gd index e2986b5..92e3ed1 100644 --- a/scripts/scenes/encounter_node.gd +++ b/scripts/scenes/encounter_node.gd @@ -21,36 +21,49 @@ func _ready() -> void: gui_input.connect(_on_gui_input) mouse_entered.connect(_on_mouse_entered) mouse_exited.connect(_on_mouse_exited) + + # If we were set up before _ready, update appearance now + if node_data: + update_appearance() func setup(data: EncounterNodeData) -> void: node_data = data - update_appearance() + if is_node_ready(): + update_appearance() func get_node_data() -> EncounterNodeData: return node_data func set_start_node() -> void: + if not is_node_ready(): + await ready circle.color = START_NODE_COLOR label.text = "Start" func set_finish_node() -> void: + if not is_node_ready(): + await ready circle.color = FINISH_NODE_COLOR label.text = "Final" func set_completed() -> void: + if not is_node_ready(): + await ready circle.color = COMPLETED_COLOR func update_appearance() -> void: + if not is_node_ready(): + await ready + if node_data.completed: set_completed() - - if node_data is StartEncounterNodeData: + elif node_data is StartEncounterNodeData: set_start_node() elif node_data is FinishEncounterNodeData: set_finish_node() else: - label.text = "Encounter" circle.color = BASE_NODE_COLOR + label.text = "Encounter" func _on_gui_input(event: InputEvent) -> void: if event is InputEventMouseButton: diff --git a/scripts/scenes/main_menu.gd b/scripts/scenes/main_menu.gd index 4393a27..7dc400b 100644 --- a/scripts/scenes/main_menu.gd +++ b/scripts/scenes/main_menu.gd @@ -18,7 +18,7 @@ func _on_start_pressed(): disable_menu() GameState.start_new_run() var new_game_cutscene = load("res://resources/cutscenes/new_game/new_game_cutscene.tres") - $CutsceneSystem.play_cutscene(new_game_cutscene, start_ingame_tutorial) + CutsceneSystem.play_cutscene(new_game_cutscene, start_ingame_tutorial) func start_ingame_tutorial() -> void: var ingame_tutorial_adventure = load("res://resources/adventures/ingame_tutorial/adventure_map.tres") diff --git a/scripts/systems/adventure_system.gd b/scripts/systems/adventure_system.gd index 97fb049..c273ea7 100644 --- a/scripts/systems/adventure_system.gd +++ b/scripts/systems/adventure_system.gd @@ -1,4 +1,3 @@ -# adventure_system.gd extends Node signal adventure_started(adventure_data: AdventureMapData) @@ -11,32 +10,23 @@ const ADVENTURE_MAP_SCENE = preload("res://scenes/adventure_map.tscn") var current_adventure: AdventureMapData var current_node: EncounterNodeData -var current_map_instance: Node +var current_combat_scene: Node func start_adventure(adventure_data: AdventureMapData) -> void: current_adventure = adventure_data current_node = null - # Emit signal for any listeners adventure_started.emit(adventure_data) - # Create and show the adventure map - if current_map_instance: - current_map_instance.queue_free() - - current_map_instance = ADVENTURE_MAP_SCENE.instantiate() - get_tree().root.add_child(current_map_instance) - - # Initialize the map with our adventure data - current_map_instance.init_adventure(adventure_data) - - # Check if we should auto-start an encounter if adventure_data.auto_start and adventure_data.rootEncounterNode.childNodes.size() > 0: - # Hide the map immediately if we're auto-starting - current_map_instance.hide() _auto_start_first_encounter(adventure_data.rootEncounterNode) else: - current_map_instance.show() + show_map() + +func show_map() -> void: + var map_instance = ADVENTURE_MAP_SCENE.instantiate() + get_tree().root.add_child(map_instance) + map_instance.get_node("AdventureManager").init_adventure(current_adventure) func _auto_start_first_encounter(start_node: StartEncounterNodeData) -> void: # Get the first available child node @@ -67,38 +57,57 @@ func can_select_encounter(node: EncounterNodeData) -> bool: # Must have a completed parent (unless it's the start node) if not node is StartEncounterNodeData: var has_completed_parent = false - for stage in current_map_instance.get_all_nodes(): - for potential_parent in stage: - if potential_parent.childNodes.has(node) and potential_parent.completed: - has_completed_parent = true - break + # Check all parent nodes directly + for potential_parent in _get_all_parent_nodes(node): + if potential_parent.completed: + has_completed_parent = true + break if not has_completed_parent: return false return true +func _get_all_parent_nodes(node: EncounterNodeData) -> Array[EncounterNodeData]: + var parents: Array[EncounterNodeData] = [] + + # Start from the root and traverse the tree + _find_parents_recursive(current_adventure.rootEncounterNode, node, parents) + + return parents + +func _find_parents_recursive(current: EncounterNodeData, target: EncounterNodeData, parents: Array[EncounterNodeData]) -> bool: + # Check if any of this node's children is our target + for child in current.childNodes: + if child == target: + parents.append(current) + return true + # Recursively check this child's children + if _find_parents_recursive(child, target, parents): + return true + + return false + func complete_current_encounter() -> void: if current_node: current_node.completed = true encounter_completed.emit(current_node) - current_map_instance.update_map() - adventure_completed.emit(current_adventure) - # Save progress if needed - # GameState.save_game() + + if current_node is FinishEncounterNodeData: + adventure_completed.emit(current_adventure) + else: + # If we have a current combat/event scene, remove it + if current_combat_scene: + current_combat_scene.queue_free() + current_combat_scene = null + + await get_tree().process_frame + show_map() func start_combat(combat_node: CombatEncounterNodeData) -> void: var combat_scene = COMBAT_SCENE.instantiate() - # Get the enemy container - var enemy_container = combat_scene.get_node("Enemy").get_parent() - # Remove the default enemy - enemy_container.remove_child(combat_scene.get_node("Enemy")) - - # Create an HBoxContainer for multiple enemies - var enemies_container = HBoxContainer.new() - enemies_container.alignment = BoxContainer.ALIGNMENT_CENTER - enemies_container.add_theme_constant_override("separation", 20) - enemy_container.add_child(enemies_container) + # Get the enemies container + var enemies_container = combat_scene.get_node("EnemiesContainer") # Add each enemy from the combat node for enemy_data in combat_node.enemies: @@ -109,21 +118,23 @@ func start_combat(combat_node: CombatEncounterNodeData) -> void: enemy_instance.setup(enemy_data) get_tree().root.add_child(combat_scene) - current_map_instance.hide() + current_combat_scene = combat_scene func start_event(event_node: EventEncounterNodeData) -> void: var event_scene = event_node.eventScene.instantiate() # Setup event scene with any necessary data from event_node get_tree().root.add_child(event_scene) - current_map_instance.hide() + current_combat_scene = event_scene func handle_finish_encounter(finish_node: FinishEncounterNodeData) -> void: var finish_scene = finish_node.finishScene.instantiate() get_tree().root.add_child(finish_scene) - current_map_instance.hide() + current_combat_scene = finish_scene func return_to_map() -> void: - current_map_instance.show() + # Only return to map if we aren't finished + if not (current_node is FinishEncounterNodeData): + show_map() # Utility functions for other systems to use func get_current_adventure() -> AdventureMapData: diff --git a/scripts/systems/background_system.gd b/scripts/systems/background_system.gd index 1c4c3d6..82f13f6 100644 --- a/scripts/systems/background_system.gd +++ b/scripts/systems/background_system.gd @@ -11,15 +11,6 @@ func setup_background(): # Create a TextureRect node as the background var background = TextureRect.new() - # Set it to fill the entire viewport - background.set_anchors_preset(Control.PRESET_FULL_RECT) - - # Make sure it stays behind other elements - background.z_index = -1 - - # Ignore mouse input so controls underneath work - background.mouse_filter = Control.MOUSE_FILTER_IGNORE - # Get the parent scene name directly var current_scene = get_parent().name print("Current scene: ", current_scene) # Debug print @@ -29,9 +20,22 @@ func setup_background(): background.texture = MAIN_MENU_BG elif current_scene == "Combat": background.texture = COMBAT_BG + else: + background = ColorRect.new() + background.color = Color.AQUAMARINE - # Set the stretch mode to cover the entire area while maintaining aspect ratio - background.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED + if background is TextureRect: + # Set the stretch mode to cover the entire area while maintaining aspect ratio + background.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED + + # Set it to fill the entire viewport + background.set_anchors_preset(Control.PRESET_FULL_RECT) + + # Make sure it stays behind other elements + background.z_index = -1 + + # Ignore mouse input so controls underneath work + background.mouse_filter = Control.MOUSE_FILTER_IGNORE # Add the background as the first child add_child(background) From 444f55ed2dac0efb0883d872ca7dea3bdc7effd9 Mon Sep 17 00:00:00 2001 From: Thomas Metten Date: Wed, 6 Nov 2024 14:44:12 +0100 Subject: [PATCH 4/7] feat: add complete adventure loop --- project.godot | 4 + repomix-output.txt | 3137 +++++++++++++++++ .../adventures/ingame_tutorial/0_combat.tres | 4 +- scenes/adventure_map.tscn | 3 + scenes/combat.tscn | 9 +- scenes/encounter_node.tscn | 2 +- {scripts/scenes => scenes}/home_base.tscn | 8 +- scripts/data/encounter_node_data.gd | 1 + scripts/enemy.gd | 2 +- scripts/managers/adventure_manager.gd | 3 +- scripts/managers/pause_manager.gd | 35 +- scripts/pause_menu.gd | 11 +- scripts/scenes/card.gd | 17 +- scripts/scenes/main.gd | 62 +- scripts/scenes/main_menu.gd | 16 +- scripts/systems/adventure_system.gd | 142 +- scripts/systems/background_system.gd | 62 +- 17 files changed, 3382 insertions(+), 136 deletions(-) create mode 100644 repomix-output.txt rename {scripts/scenes => scenes}/home_base.tscn (95%) diff --git a/project.godot b/project.godot index 3471470..99a4fd2 100644 --- a/project.godot +++ b/project.godot @@ -42,6 +42,10 @@ enabled=PackedStringArray() config/itch_username="" config/itch_project_name="" +[global_group] + +encounter_nodes="All currently present encounter nodes" + [maaacks_options_menus] disable_plugin_dialogues=true diff --git a/repomix-output.txt b/repomix-output.txt new file mode 100644 index 0000000..0f53870 --- /dev/null +++ b/repomix-output.txt @@ -0,0 +1,3137 @@ +This file is a merged representation of the entire codebase, combining all repository files into a single document. +Generated by Repomix on: 2024-11-06T12:35:33.109Z + +================================================================ +File Summary +================================================================ + +Purpose: +-------- +This file contains a packed representation of the entire repository's contents. +It is designed to be easily consumable by AI systems for analysis, code review, +or other automated processes. + +File Format: +------------ +The content is organized as follows: +1. This summary section +2. Repository information +3. Repository structure +4. Multiple file entries, each consisting of: + a. A separator line (================) + b. The file path (File: path/to/file) + c. Another separator line + d. The full contents of the file + e. A blank line + +Usage Guidelines: +----------------- +- This file should be treated as read-only. Any changes should be made to the + original repository files, not this packed version. +- When processing this file, use the file path to distinguish + between different files in the repository. +- Be aware that this file may contain sensitive information. Handle it with + the same level of security as you would the original repository. + +Notes: +------ +- Some files may have been excluded based on .gitignore rules and Repomix's + configuration. +- Binary files are not included in this packed representation. Please refer to + the Repository Structure section for a complete list of file paths, including + binary files. + +Additional Info: +---------------- + +For more information about Repomix, visit: https://github.com/yamadashy/repomix + +================================================================ +Repository Structure +================================================================ +assets/ + audio/ + new_game_narrative.mp3.import + backgrounds/ + main_menu_bg.webp.import + ui/ + tilemap.tres +resources/ + adventures/ + ingame_tutorial/ + 0_combat.tres + 1_combat.tres + 2a_combat.tres + 2b_event.tres + 3_combat.tres + adventure_map.tres + finish.tres + start.tres + cards/ + attack_card.tres + block_card.tres + cutscenes/ + new_game/ + new_game_cutscene.tres + enemies/ + cultist.tres +scenes/ + enemies/ + cultist.tscn + menus/ + main_menu.tscn + options_menu.tscn + adventure_map.tscn + card.tscn + combat.tscn + cutscene.tscn + decode_ui.tscn + encounter_node.tscn + main.tscn + pause_menu.tscn +scripts/ + autoload/ + game_state.gd + data/ + adventure_map_data.gd + card_data.gd + combat_encounter_node_data.gd + cutscene_data.gd + cutscene_frame_data.gd + encounter_node_data.gd + enemy_data.gd + event_encounter_node_data.gd + finish_encounter_node_data.gd + start_encounter_node_data.gd + managers/ + adventure_manager.gd + combat_manager.gd + config_manager.gd + decoder_manager.gd + hand_manager.gd + pause_manager.gd + ui_scale_manager.gd + scenes/ + card.gd + combat.gd + cutscene_scene.gd + encounter_node.gd + home_base.gd + home_base.tscn + main_menu.gd + main.gd + options_menu.gd + systems/ + adventure_system.gd + background_system.gd + cutscene_system.gd + utils/ + circle.gd + decode_ui.gd + enemy.gd + pause_menu.gd + player.gd +.gitignore +ATTRIBUTION.md +LICENSE +project.godot +README.md + +================================================================ +Repository Files +================================================================ + +================ +File: assets/audio/new_game_narrative.mp3.import +================ +[remap] + +importer="mp3" +type="AudioStreamMP3" +uid="uid://b6miq63f23cyk" +path="res://.godot/imported/new_game_narrative.mp3-d3f83ef65f4ffbf7b4ee75adcce85e39.mp3str" + +[deps] + +source_file="res://assets/audio/new_game_narrative.mp3" +dest_files=["res://.godot/imported/new_game_narrative.mp3-d3f83ef65f4ffbf7b4ee75adcce85e39.mp3str"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 + +================ +File: assets/backgrounds/main_menu_bg.webp.import +================ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c4a4pijnc7ymf" +path="res://.godot/imported/main_menu_bg.webp-226249c542a90a329d6cdc5258220c3a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/backgrounds/main_menu_bg.webp" +dest_files=["res://.godot/imported/main_menu_bg.webp-226249c542a90a329d6cdc5258220c3a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 + +================ +File: assets/ui/tilemap.tres +================ +[gd_resource type="TileMapPattern" format=3 uid="uid://bekwf75e1n4no"] + +[resource] + +================ +File: resources/adventures/ingame_tutorial/0_combat.tres +================ +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://dsohl2qco44tx"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_w06wc"] +[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_7oxnr"] +[ext_resource type="Resource" uid="uid://b61wo0msir67x" path="res://resources/adventures/ingame_tutorial/1_combat.tres" id="2_lvvpg"] +[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_1bpls"] +[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="4_sukwl"] + +[resource] +script = ExtResource("3_1bpls") +enemies = Array[ExtResource("2_7oxnr")]([ExtResource("4_sukwl")]) +childNodes = Array[ExtResource("1_w06wc")]([ExtResource("2_lvvpg")]) +completed = false + +================ +File: resources/adventures/ingame_tutorial/1_combat.tres +================ +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=7 format=3 uid="uid://b61wo0msir67x"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_q1hg8"] +[ext_resource type="Resource" uid="uid://hixs5ub5k4we" path="res://resources/adventures/ingame_tutorial/2a_combat.tres" id="2_km8iu"] +[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_t0t55"] +[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_s4ptf"] +[ext_resource type="Resource" uid="uid://cxl62hprsbcw1" path="res://resources/adventures/ingame_tutorial/2b_event.tres" id="3_yhy31"] +[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="5_ad11l"] + +[resource] +script = ExtResource("3_s4ptf") +enemies = Array[ExtResource("2_t0t55")]([ExtResource("5_ad11l"), ExtResource("5_ad11l")]) +childNodes = Array[ExtResource("1_q1hg8")]([ExtResource("2_km8iu"), ExtResource("3_yhy31")]) +completed = false + +================ +File: resources/adventures/ingame_tutorial/2a_combat.tres +================ +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://hixs5ub5k4we"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_eejxk"] +[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_dr5jw"] +[ext_resource type="Resource" uid="uid://xpw7xp6klhla" path="res://resources/adventures/ingame_tutorial/3_combat.tres" id="2_fxa05"] +[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_qyrbs"] +[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="4_cwl26"] + +[resource] +script = ExtResource("3_qyrbs") +enemies = Array[ExtResource("2_dr5jw")]([ExtResource("4_cwl26"), ExtResource("4_cwl26"), ExtResource("4_cwl26")]) +childNodes = Array[ExtResource("1_eejxk")]([ExtResource("2_fxa05")]) +completed = false + +================ +File: resources/adventures/ingame_tutorial/2b_event.tres +================ +[gd_resource type="Resource" script_class="EventEncounterNodeData" load_steps=4 format=3 uid="uid://cxl62hprsbcw1"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_xxh5h"] +[ext_resource type="Script" path="res://scripts/data/event_encounter_node_data.gd" id="2_811ok"] +[ext_resource type="Resource" uid="uid://xpw7xp6klhla" path="res://resources/adventures/ingame_tutorial/3_combat.tres" id="2_n5lnf"] + +[resource] +script = ExtResource("2_811ok") +childNodes = Array[ExtResource("1_xxh5h")]([ExtResource("2_n5lnf")]) +completed = false + +================ +File: resources/adventures/ingame_tutorial/3_combat.tres +================ +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://xpw7xp6klhla"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_o4v54"] +[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_y3pue"] +[ext_resource type="Resource" uid="uid://h7ww7yma5ddj" path="res://resources/adventures/ingame_tutorial/finish.tres" id="2_yb1tn"] +[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_kx66a"] +[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="4_hhtf0"] + +[resource] +script = ExtResource("3_kx66a") +enemies = Array[ExtResource("2_y3pue")]([ExtResource("4_hhtf0"), ExtResource("4_hhtf0"), ExtResource("4_hhtf0"), ExtResource("4_hhtf0")]) +childNodes = Array[ExtResource("1_o4v54")]([ExtResource("2_yb1tn")]) +completed = false + +================ +File: resources/adventures/ingame_tutorial/adventure_map.tres +================ +[gd_resource type="Resource" script_class="AdventureMapData" load_steps=3 format=3 uid="uid://cq1h50d6agd3j"] + +[ext_resource type="Resource" uid="uid://cldmpsek0wxvd" path="res://resources/adventures/ingame_tutorial/start.tres" id="1_l4584"] +[ext_resource type="Script" path="res://scripts/data/adventure_map_data.gd" id="2_ic3nw"] + +[resource] +script = ExtResource("2_ic3nw") +rootEncounterNode = ExtResource("1_l4584") +auto_start = true + +================ +File: resources/adventures/ingame_tutorial/finish.tres +================ +[gd_resource type="Resource" script_class="FinishEncounterNodeData" load_steps=3 format=3 uid="uid://h7ww7yma5ddj"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_h5xn1"] +[ext_resource type="Script" path="res://scripts/data/finish_encounter_node_data.gd" id="2_isi6o"] + +[resource] +script = ExtResource("2_isi6o") +childNodes = Array[ExtResource("1_h5xn1")]([]) +completed = false + +================ +File: resources/adventures/ingame_tutorial/start.tres +================ +[gd_resource type="Resource" script_class="StartEncounterNodeData" load_steps=4 format=3 uid="uid://cldmpsek0wxvd"] + +[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_72qi4"] +[ext_resource type="Script" path="res://scripts/data/start_encounter_node_data.gd" id="2_r3cwx"] +[ext_resource type="Resource" uid="uid://dsohl2qco44tx" path="res://resources/adventures/ingame_tutorial/0_combat.tres" id="2_y55f8"] + +[resource] +script = ExtResource("2_r3cwx") +childNodes = Array[ExtResource("1_72qi4")]([ExtResource("2_y55f8")]) +completed = true + +================ +File: resources/cards/attack_card.tres +================ +[gd_resource type="Resource" script_class="CardData" load_steps=2 format=3 uid="uid://i1h80xcboga2"] + +[ext_resource type="Script" path="res://scripts/data/card_data.gd" id="1_qhkig"] + +[resource] +script = ExtResource("1_qhkig") +name = "Strike" +energy_cost = 1 +card_type = "attack" +effect_value = 6 +effect_description = "Deal 6 damage" + +================ +File: resources/cards/block_card.tres +================ +[gd_resource type="Resource" script_class="CardData" load_steps=2 format=3 uid="uid://dwoxx76qsbfys"] + +[ext_resource type="Script" path="res://scripts/data/card_data.gd" id="1_vpyxy"] + +[resource] +script = ExtResource("1_vpyxy") +name = "Block" +energy_cost = 1 +card_type = "block" +effect_value = 5 +effect_description = "Gain 5 block" + +================ +File: resources/cutscenes/new_game/new_game_cutscene.tres +================ +[gd_resource type="Resource" script_class="CutsceneData" load_steps=10 format=3 uid="uid://cmss538sh2l7r"] + +[ext_resource type="Script" path="res://scripts/data/cutscene_data.gd" id="1_b1qtf"] +[ext_resource type="AudioStream" uid="uid://b6miq63f23cyk" path="res://assets/audio/new_game_narrative.mp3" id="1_mx5nb"] +[ext_resource type="Script" path="res://scripts/data/cutscene_frame_data.gd" id="2_ireuw"] +[ext_resource type="Texture2D" uid="uid://wv57ymcqwumg" path="res://assets/cutscene_images/new_game/new_game_0_stone.png" id="3_sptdj"] +[ext_resource type="Texture2D" uid="uid://x7r1vbh43dhe" path="res://assets/cutscene_images/new_game/new_game_1_marketplace.png" id="4_x3eri"] +[ext_resource type="Texture2D" uid="uid://qoukk6jbsbu0" path="res://assets/cutscene_images/new_game/new_game_2_rocky_outcrop.png" id="5_7v7wn"] + +[sub_resource type="Resource" id="Resource_dxda3"] +script = ExtResource("2_ireuw") +timestamp = 0.0 +image = ExtResource("3_sptdj") + +[sub_resource type="Resource" id="Resource_gcroa"] +script = ExtResource("2_ireuw") +timestamp = 6.0 +image = ExtResource("4_x3eri") + +[sub_resource type="Resource" id="Resource_5aa0k"] +script = ExtResource("2_ireuw") +timestamp = 22.0 +image = ExtResource("5_7v7wn") + +[resource] +resource_name = "New Game" +script = ExtResource("1_b1qtf") +audio_track = ExtResource("1_mx5nb") +frames = Array[ExtResource("2_ireuw")]([SubResource("Resource_dxda3"), SubResource("Resource_gcroa"), SubResource("Resource_5aa0k")]) + +================ +File: resources/enemies/cultist.tres +================ +[gd_resource type="Resource" script_class="EnemyData" load_steps=3 format=3 uid="uid://bjkxuf2s1608i"] + +[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="1_g2v6o"] +[ext_resource type="PackedScene" uid="uid://cbtac01eivwim" path="res://scenes/enemies/cultist.tscn" id="1_uchoq"] + +[resource] +script = ExtResource("1_g2v6o") +enemy_scene = ExtResource("1_uchoq") +health = 10 +abilities = [] + +================ +File: scenes/enemies/cultist.tscn +================ +[gd_scene load_steps=2 format=3 uid="uid://cbtac01eivwim"] + +[ext_resource type="Script" path="res://scripts/enemy.gd" id="1_c11yq"] + +[node name="Cultist" type="Node2D"] +script = ExtResource("1_c11yq") + +[node name="ColorRect" type="ColorRect" parent="."] +custom_minimum_size = Vector2(100, 200) +offset_right = 40.0 +offset_bottom = 40.0 +color = Color(0.394879, 0.000106898, 0.394876, 1) + +[node name="HealthLabel" type="Label" parent="."] +offset_right = 40.0 +offset_bottom = 23.0 + +================ +File: scenes/menus/main_menu.tscn +================ +[gd_scene load_steps=2 format=3 uid="uid://bxru22786wp6n"] + +[ext_resource type="Script" path="res://scripts/scenes/main_menu.gd" id="1_prvtn"] + +[node name="MainMenu" type="CenterContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_prvtn") + +[node name="CenterContainer" type="CenterContainer" parent="."] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"] +layout_mode = 2 +theme_override_constants/separation = 20 + +[node name="Title" type="Label" parent="CenterContainer/VBoxContainer"] +layout_mode = 2 +theme_override_colors/font_shadow_color = Color(0, 0, 0, 0.5) +theme_override_constants/shadow_offset_x = 2 +theme_override_constants/shadow_offset_y = 2 +theme_override_constants/outline_size = 2 +theme_override_font_sizes/font_size = 48 +text = "Card Game" +horizontal_alignment = 1 + +[node name="ContinueButton" type="Button" parent="CenterContainer/VBoxContainer"] +custom_minimum_size = Vector2(400, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +disabled = true +text = "Continue Game" + +[node name="StartButton" type="Button" parent="CenterContainer/VBoxContainer"] +custom_minimum_size = Vector2(400, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "New Game" + +[node name="Options" type="Button" parent="CenterContainer/VBoxContainer"] +custom_minimum_size = Vector2(400, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "Options" + +[node name="QuitButton" type="Button" parent="CenterContainer/VBoxContainer"] +custom_minimum_size = Vector2(400, 100) +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "Quit" + +================ +File: scenes/menus/options_menu.tscn +================ +[gd_scene load_steps=2 format=3 uid="uid://umnex4p7msdu"] + +[ext_resource type="Script" path="res://scripts/scenes/options_menu.gd" id="1_script"] + +[node name="OptionsMenu" type="CenterContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +mouse_filter = 2 +script = ExtResource("1_script") + +[node name="ColorRect" type="ColorRect" parent="."] +layout_mode = 2 +color = Color(0, 0, 0, 0.5) + +[node name="CenterContainer" type="CenterContainer" parent="."] +layout_mode = 2 + +[node name="PanelContainer" type="PanelContainer" parent="CenterContainer"] +custom_minimum_size = Vector2(800, 600) +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/PanelContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="TabContainer" type="TabContainer" parent="CenterContainer/PanelContainer/MarginContainer"] +layout_mode = 2 +current_tab = 0 + +[node name="Video" type="VBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer"] +layout_mode = 2 +theme_override_constants/separation = 20 +metadata/_tab_index = 0 + +[node name="FullscreenLabel" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Video"] +layout_mode = 2 +theme_override_font_sizes/font_size = 24 +text = "Fullscreen" + +[node name="FullscreenToggle" type="CheckButton" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Video"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_font_sizes/font_size = 24 + +[node name="ResolutionLabel" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Video"] +layout_mode = 2 +theme_override_font_sizes/font_size = 24 +text = "Resolution (Windowed Mode)" + +[node name="ResolutionOptionButton" type="OptionButton" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Video"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_font_sizes/font_size = 24 + +[node name="Audio" type="VBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer"] +visible = false +layout_mode = 2 +theme_override_constants/separation = 20 +metadata/_tab_index = 1 + +[node name="MuteLabel" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio"] +layout_mode = 2 +theme_override_font_sizes/font_size = 24 +text = "Mute Audio" + +[node name="MuteToggle" type="CheckButton" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_font_sizes/font_size = 24 + +[node name="VolumeContainer" type="HBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio"] +layout_mode = 2 + +[node name="VolumeLabel" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio/VolumeContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 24 +text = "Master Volume" + +[node name="VolumeLabelValue" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio/VolumeContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_font_sizes/font_size = 24 +text = "100%" + +[node name="VolumeSlider" type="HSlider" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="BackButton" type="Button" parent="CenterContainer/PanelContainer/MarginContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 8 +theme_override_font_sizes/font_size = 24 +text = "Back" + +================ +File: scenes/adventure_map.tscn +================ +[gd_scene load_steps=2 format=3 uid="uid://b7vx0syoj8olw"] + +[ext_resource type="Script" path="res://scripts/managers/adventure_manager.gd" id="1_ryxis"] + +[node name="AdventureMap" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ScrollContainer" type="ScrollContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +horizontal_scroll_mode = 2 +vertical_scroll_mode = 0 + +[node name="AdventureStages" type="HBoxContainer" parent="ScrollContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="LinesContainer" type="Node2D" parent="ScrollContainer"] +z_index = -1 + +[node name="AdventureManager" type="Node" parent="."] +script = ExtResource("1_ryxis") + +================ +File: scenes/card.tscn +================ +[gd_scene load_steps=3 format=3 uid="uid://cpp37o7600nho"] + +[ext_resource type="Script" path="res://scripts/scenes/card.gd" id="1_p7luq"] +[ext_resource type="Texture2D" uid="uid://df58t5bfmj70a" path="res://assets/ui/scroll_base.png" id="2_mugk4"] + +[node name="Card" type="Control"] +custom_minimum_size = Vector2(200, 300) +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = -1720.0 +offset_bottom = -780.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_p7luq") + +[node name="Sprite2D" type="Sprite2D" parent="."] +position = Vector2(1, 1) +scale = Vector2(0.995, 0.996805) +texture = ExtResource("2_mugk4") +centered = false + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 0 +offset_left = 34.0 +offset_top = 67.0 +offset_right = 238.0 +offset_bottom = 314.0 +scale = Vector2(0.7, 0.78) +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 10 +theme_override_constants/margin_bottom = 10 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 + +[node name="TopBar" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="CostLabel" type="Label" parent="MarginContainer/VBoxContainer/TopBar"] +layout_mode = 2 +size_flags_horizontal = 0 +text = "666" + +[node name="NameLabel" type="Label" parent="MarginContainer/VBoxContainer/TopBar"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Unleash all demons" + +[node name="EffectLabel" type="Label" parent="MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(180, 200) +layout_mode = 2 +size_flags_vertical = 3 +text = "Unleash all demons, with the power of THE DEBUG!" +horizontal_alignment = 1 +vertical_alignment = 2 +autowrap_mode = 2 + +================ +File: scenes/combat.tscn +================ +[gd_scene load_steps=7 format=3 uid="uid://b3l57gnlln63g"] + +[ext_resource type="Script" path="res://scripts/scenes/combat.gd" id="1_n5yyl"] +[ext_resource type="Script" path="res://scripts/managers/combat_manager.gd" id="2_305ab"] +[ext_resource type="Script" path="res://scripts/managers/hand_manager.gd" id="3_bdk6y"] +[ext_resource type="Script" path="res://scripts/managers/pause_manager.gd" id="5_pl43j"] +[ext_resource type="Script" path="res://scripts/player.gd" id="7_dwwk6"] +[ext_resource type="Texture2D" uid="uid://dyx76tn885md3" path="res://assets/ui/energy_orb.png" id="9_e0ys4"] + +[node name="Combat" type="Node2D"] +script = ExtResource("1_n5yyl") + +[node name="CombatManager" type="Node" parent="."] +script = ExtResource("2_305ab") + +[node name="HandManager" type="Node" parent="."] +script = ExtResource("3_bdk6y") + +[node name="PauseManager" type="Node" parent="."] +script = ExtResource("5_pl43j") + +[node name="Player" type="Node2D" parent="."] +position = Vector2(300, 300) +script = ExtResource("7_dwwk6") + +[node name="Visual" type="ColorRect" parent="Player"] +offset_right = 100.0 +offset_bottom = 100.0 +color = Color(0, 1, 0, 1) + +[node name="HealthLabel" type="Label" parent="Player"] +offset_right = 40.0 +offset_bottom = 23.0 +theme_override_font_sizes/font_size = 70 +text = "40/40" + +[node name="EnemiesContainer" type="HBoxContainer" parent="."] +offset_right = 40.0 +offset_bottom = 40.0 +theme_override_constants/separation = 20 +alignment = 1 + +[node name="UI" type="CanvasLayer" parent="."] + +[node name="HUD" type="Control" parent="UI"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 + +[node name="HandContainer" type="HBoxContainer" parent="UI/HUD"] +custom_minimum_size = Vector2(0, 300) +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -576.0 +offset_top = -300.0 +offset_right = 576.0 +grow_horizontal = 2 +grow_vertical = 0 +alignment = 1 + +[node name="EndTurnMarginContainer" type="MarginContainer" parent="UI/HUD"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -300.0 +offset_top = -150.0 +offset_right = -50.0 +offset_bottom = -50.0 +grow_horizontal = 0 +grow_vertical = 0 + +[node name="EndTurnButton" type="Button" parent="UI/HUD/EndTurnMarginContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 70 +text = "End turn" + +[node name="EnergyMarginContainer" type="MarginContainer" parent="UI/HUD"] +custom_minimum_size = Vector2(200, 200) +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -250.0 +offset_top = 50.0 +offset_right = -50.0 +offset_bottom = 250.0 +grow_horizontal = 0 + +[node name="EnergyTexture" type="TextureRect" parent="UI/HUD/EnergyMarginContainer"] +custom_minimum_size = Vector2(200, 200) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +texture = ExtResource("9_e0ys4") +expand_mode = 1 + +[node name="EnergyLabel" type="Label" parent="UI/HUD/EnergyMarginContainer/EnergyTexture"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/font_size = 140 +text = "3" +horizontal_alignment = 1 +vertical_alignment = 1 + +================ +File: scenes/cutscene.tscn +================ +[gd_scene load_steps=2 format=3 uid="uid://1bxh8on3pygv"] + +[ext_resource type="Script" path="res://scripts/scenes/cutscene_scene.gd" id="1_6o0s5"] + +[node name="Cutscene" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_6o0s5") + +[node name="TextureRect" type="TextureRect" parent="."] +z_index = 1000 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="AudioStreamPlayer" type="AudioStreamPlayer2D" parent="."] + +================ +File: scenes/decode_ui.tscn +================ +[gd_scene load_steps=2 format=3 uid="uid://duyk27ghemr8s"] + +[ext_resource type="Script" path="res://scripts/decode_ui.gd" id="1_d4e5u"] + +[node name="DecodeUI" type="PanelContainer"] +process_mode = 3 +visible = false +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -100.0 +offset_top = -75.0 +offset_right = 100.0 +offset_bottom = 75.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_d4e5u") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 10 +theme_override_constants/margin_bottom = 10 + +[node name="VBox" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="DecodePointsLabel" type="Label" parent="MarginContainer/VBox"] +layout_mode = 2 +text = "Decode Points: 0" +horizontal_alignment = 1 + +[node name="OptionsContainer" type="VBoxContainer" parent="MarginContainer/VBox"] +layout_mode = 2 +theme_override_constants/separation = 5 + +[node name="CloseButton" type="Button" parent="MarginContainer/VBox"] +layout_mode = 2 +text = "Close" + +[connection signal="pressed" from="MarginContainer/VBox/CloseButton" to="." method="hide"] + +================ +File: scenes/encounter_node.tscn +================ +[gd_scene load_steps=3 format=3 uid="uid://cidwq7l0x54g7"] + +[ext_resource type="Script" path="res://scripts/scenes/encounter_node.gd" id="1_6g2l7"] +[ext_resource type="Script" path="res://scripts/utils/circle.gd" id="2_kg7rm"] + +[node name="EncounterNode" type="Control"] +custom_minimum_size = Vector2(100, 100) +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_6g2l7") + +[node name="CenterContainer" type="CenterContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"] +layout_mode = 2 +theme_override_constants/separation = 8 +alignment = 1 + +[node name="Circle" type="ColorRect" parent="CenterContainer/VBoxContainer"] +custom_minimum_size = Vector2(40, 40) +layout_mode = 2 +script = ExtResource("2_kg7rm") + +[node name="Label" type="Label" parent="CenterContainer/VBoxContainer"] +layout_mode = 2 +text = "Encounter" +horizontal_alignment = 1 +vertical_alignment = 1 + +================ +File: scenes/main.tscn +================ +[gd_scene load_steps=2 format=3 uid="uid://ddx65fdpgkycb"] + +[ext_resource type="Script" path="res://scripts/scenes/main.gd" id="1_fc67n"] + +[node name="Main" type="Node"] +script = ExtResource("1_fc67n") + +================ +File: scenes/pause_menu.tscn +================ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://scripts/pause_menu.gd" id="1_p7luq"] + +[node name="PauseMenu" type="Control"] +process_mode = 3 +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_p7luq") + +[node name="ColorRect" type="ColorRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 0.5) +mouse_filter = 1 + +[node name="PanelContainer" type="PanelContainer" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -100.0 +offset_top = -68.0 +offset_right = 100.0 +offset_bottom = 68.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 + +[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 16 +theme_override_constants/margin_top = 16 +theme_override_constants/margin_right = 16 +theme_override_constants/margin_bottom = 16 +mouse_filter = 1 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"] +layout_mode = 2 +theme_override_constants/separation = 16 +mouse_filter = 1 + +[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "PAUSED" +horizontal_alignment = 1 + +[node name="ResumeButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(200, 50) +layout_mode = 2 +size_flags_horizontal = 4 +mouse_default_cursor_shape = 2 +text = "Resume" + +[node name="QuitButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(200, 50) +layout_mode = 2 +size_flags_horizontal = 4 +mouse_default_cursor_shape = 2 +text = "Quit to Menu" + +================ +File: scripts/autoload/game_state.gd +================ +# game_state.gd +extends Node + +# Persistent game progress +var total_runs: int = 0 +var current_run: int = 0 + +# Card knowledge system +var decoded_aspects: Dictionary = { + # Structure: + # "res://resources/cards/attack_card.tres": { + # "cost": true, + # "type": true, + # "value": false, + # "description": false + # } +} + +# Current run state +var current_deck: Array = [] +var current_gold: int = 100 +var current_hp: int = 40 +var current_max_hp: int = 40 +var decode_points: int = 0 + +# Save data structure +const SAVE_PATH = "user://gamestate.save" + +func _ready() -> void: + load_game() + +func save_game() -> void: + var save_data = { + "meta_progress": { + "total_runs": total_runs, + "decoded_aspects": decoded_aspects, + }, + "current_run": { + "run_number": current_run, + "current_deck": current_deck, + "current_gold": current_gold, + "current_hp": current_hp, + "current_max_hp": current_max_hp, + "decode_points": decode_points + } + } + + var save_file = FileAccess.open(SAVE_PATH, FileAccess.WRITE) + var json_string = JSON.stringify(save_data) + save_file.store_string(json_string) + +func load_game() -> void: + if not FileAccess.file_exists(SAVE_PATH): + return + + var save_file = FileAccess.open(SAVE_PATH, FileAccess.READ) + var json_string = save_file.get_as_text() + + var json = JSON.new() + var parse_result = json.parse(json_string) + if parse_result == OK: + var save_data = json.get_data() + + # Load meta progress + if "meta_progress" in save_data: + total_runs = save_data["meta_progress"].get("total_runs", 0) + decoded_aspects = save_data["meta_progress"].get("decoded_aspects", {}) + + # Load current run + if "current_run" in save_data: + current_run = save_data["current_run"].get("run_number", 0) + current_deck = save_data["current_run"].get("current_deck", []) + current_gold = save_data["current_run"].get("current_gold", 100) + current_hp = save_data["current_run"].get("current_hp", 40) + current_max_hp = save_data["current_run"].get("current_max_hp", 40) + decode_points = save_data["current_run"].get("decode_points", 0) + +# Run management +func start_new_run() -> void: + current_run = total_runs + 1 + current_gold = 100 + current_hp = 40 + current_max_hp = 40 + decode_points = 0 + current_deck.clear() + setup_starting_deck() + save_game() + +func end_run(victory: bool) -> void: + if victory: + total_runs += 1 + save_game() + +# Card knowledge management +func is_aspect_decoded(card_path: String, aspect: String) -> bool: + if not decoded_aspects.has(card_path): + return false + return decoded_aspects[card_path].get(aspect, false) + +func decode_aspect(card_path: String, aspect: String) -> void: + if not decoded_aspects.has(card_path): + decoded_aspects[card_path] = {} + decoded_aspects[card_path][aspect] = true + save_game() + +func is_card_fully_decoded(card_path: String) -> bool: + if not decoded_aspects.has(card_path): + return false + var aspects = decoded_aspects[card_path] + return aspects.get("cost", false) and \ + aspects.get("type", false) and \ + aspects.get("value", false) and \ + aspects.get("description", false) + +# Deck management +func setup_starting_deck() -> void: + # Load starting cards + for i in range(5): + current_deck.append("res://resources/cards/attack_card.tres") + current_deck.append("res://resources/cards/block_card.tres") + +# Resource management +func add_decode_points(amount: int) -> void: + decode_points += amount + save_game() + +func spend_decode_points(amount: int) -> bool: + if decode_points >= amount: + decode_points -= amount + save_game() + return true + return false + +func add_gold(amount: int) -> void: + current_gold += amount + save_game() + +func spend_gold(amount: int) -> bool: + if current_gold >= amount: + current_gold -= amount + save_game() + return true + return false + +# Health management +func heal(amount: int) -> void: + current_hp = min(current_hp + amount, current_max_hp) + save_game() + +func take_damage(amount: int) -> void: + current_hp = max(current_hp - amount, 0) + save_game() + +func increase_max_hp(amount: int) -> void: + current_max_hp += amount + current_hp += amount + save_game() + +================ +File: scripts/data/adventure_map_data.gd +================ +extends Resource +class_name AdventureMapData + +@export var rootEncounterNode : StartEncounterNodeData +@export var auto_start: bool = false + +================ +File: scripts/data/card_data.gd +================ +extends Resource +class_name CardData + +@export var name: String +@export var energy_cost: int +@export var card_type: String # "attack" or "block" +@export var effect_value: int +@export var effect_description: String + +================ +File: scripts/data/combat_encounter_node_data.gd +================ +extends EncounterNodeData +class_name CombatEncounterNodeData + +@export var enemies: Array[EnemyData] + +================ +File: scripts/data/cutscene_data.gd +================ +class_name CutsceneData +extends Resource + +@export var audio_track: AudioStream +@export var frames: Array[CutsceneFrameData] + +func _init(): + frames = [] + +================ +File: scripts/data/cutscene_frame_data.gd +================ +class_name CutsceneFrameData +extends Resource + +@export var timestamp: float +@export var image: Texture2D + +================ +File: scripts/data/encounter_node_data.gd +================ +extends Resource +class_name EncounterNodeData + +@export var childNodes: Array[EncounterNodeData] = [] +@export var completed: bool = false + +================ +File: scripts/data/enemy_data.gd +================ +extends Resource +class_name EnemyData + +@export var enemy_scene: PackedScene +@export var health: int +@export var abilities: Array = [] + +================ +File: scripts/data/event_encounter_node_data.gd +================ +extends EncounterNodeData +class_name EventEncounterNodeData + +@export var eventScene: PackedScene + +================ +File: scripts/data/finish_encounter_node_data.gd +================ +extends EncounterNodeData +class_name FinishEncounterNodeData + +@export var finishScene: PackedScene + +================ +File: scripts/data/start_encounter_node_data.gd +================ +extends EncounterNodeData +class_name StartEncounterNodeData + +func _init() -> void: + completed = true + +================ +File: scripts/managers/adventure_manager.gd +================ +extends Node + +@export var adventure: AdventureMapData +@onready var adventureStagesContainer: HBoxContainer = $"../ScrollContainer/AdventureStages" +@onready var lines_container: Node2D = $"../ScrollContainer/LinesContainer" + +const NODE_SCENE = preload("res://scenes/encounter_node.tscn") +@export var COLUMN_SPACING = 200 +@export var NODE_SPACING = 120 +@export var LINE_COLOR = Color("6b7280") +@export var LINE_WIDTH = 3.0 +@export var DASH_LENGTH: float = 10.0 +@export var GAP_LENGTH: float = 10.0 + +func init_adventure(adventureResource: AdventureMapData) -> void: + adventure = adventureResource + drawMap() + +func drawMap() -> void: + # Clear existing nodes and lines + for child in adventureStagesContainer.get_children(): + child.queue_free() + for child in lines_container.get_children(): + child.queue_free() + + if not adventure or not adventure.rootEncounterNode: + return + + # Create stages (columns) based on depth + var stages = [] + _gather_stages(adventure.rootEncounterNode, 0, stages) + + # Create containers for each stage + for stage_nodes in stages: + var stage_container = VBoxContainer.new() + stage_container.custom_minimum_size.x = COLUMN_SPACING + stage_container.alignment = BoxContainer.ALIGNMENT_CENTER + stage_container.add_theme_constant_override("separation", NODE_SPACING) + adventureStagesContainer.add_child(stage_container) + + # Add nodes to the stage + for node_data in stage_nodes: + var encounter_node = _create_encounter_node(node_data) + stage_container.add_child(encounter_node) + + # Draw all connections after nodes are positioned + await get_tree().process_frame + _draw_all_connections() + +func _gather_stages(node: EncounterNodeData, depth: int, stages: Array) -> void: + # Ensure we have an array for this depth + while stages.size() <= depth: + stages.append([]) + + # Add node to its stage + stages[depth].append(node) + + # Process child nodes + for child in node.childNodes: + _gather_stages(child, depth + 1, stages) + +func _create_encounter_node(node_data: EncounterNodeData) -> Control: + var node_instance = NODE_SCENE.instantiate() + node_instance.setup(node_data) + + # Node type specific setup + if node_data is StartEncounterNodeData: + node_instance.set_start_node() + elif node_data is FinishEncounterNodeData: + node_instance.set_finish_node() + + if node_data.completed: + node_instance.set_completed() + + return node_instance + +func _draw_all_connections() -> void: + var stages = adventureStagesContainer.get_children() + + for stage_idx in range(stages.size() - 1): # Stop before last stage + var current_stage = stages[stage_idx] + var next_stage = stages[stage_idx + 1] + + for from_node in current_stage.get_children(): + var from_data = from_node.get_node_data() + + for child_data in from_data.childNodes: + for to_node in next_stage.get_children(): + if to_node.get_node_data() == child_data: + _draw_connection(from_node, to_node) + +func _draw_connection(from_node: Control, to_node: Control) -> void: + var line = Line2D.new() + line.default_color = LINE_COLOR + line.width = LINE_WIDTH + line.begin_cap_mode = Line2D.LINE_CAP_ROUND + line.end_cap_mode = Line2D.LINE_CAP_ROUND + + # Get the circles' centers in local coordinates relative to their common parent + var from_circle = from_node.get_node("CenterContainer/VBoxContainer/Circle") + var to_circle = to_node.get_node("CenterContainer/VBoxContainer/Circle") + + var from_center = from_circle.get_global_rect().get_center() + var to_center = to_circle.get_global_rect().get_center() + + # Convert points to lines_container's local coordinates + from_center = lines_container.to_local(from_center) + to_center = lines_container.to_local(to_center) + + # Calculate control points for curve + var control_offset = Vector2((to_center.x - from_center.x) * 0.5, 0) + + # Create dotted line points + var points = _create_dotted_curve_points(from_center, to_center, control_offset) + + line.points = points + lines_container.add_child(line) + +func _create_dotted_curve_points(from: Vector2, to: Vector2, control_offset: Vector2) -> PackedVector2Array: + var points := PackedVector2Array() + var curve = Curve2D.new() + + # Add points to create a bezier curve + curve.add_point(from, Vector2.ZERO, control_offset) + curve.add_point(to, -control_offset, Vector2.ZERO) + + # Settings for dash pattern + var dash_length := DASH_LENGTH + var gap_length := GAP_LENGTH + var curve_length = curve.get_baked_length() + + # Always start with the first point + points.append(from) + + # Calculate how many complete dash-gap pairs we need + var segment_length = dash_length + gap_length + var num_complete_segments = floor((curve_length - dash_length) / segment_length) + + # Create the middle dots + for i in range(num_complete_segments): + var pos = (i + 1) * segment_length + points.append(curve.sample_baked(pos)) + + # Always end with the last point + points.append(to) + + return points + +================ +File: scripts/managers/combat_manager.gd +================ +extends Node + +var current_energy: int = 3 +var max_energy: int = 3 +var is_player_turn: bool = true + +@onready var hand_manager: Node = $"../HandManager" +@onready var enemies_container: Node = $"../EnemiesContainer" +@onready var energy_label: Label = $"../UI/HUD/EnergyMarginContainer/EnergyTexture/EnergyLabel" + +func _ready() -> void: + await hand_manager.ready + start_turn() + +func start_turn() -> void: + current_energy = max_energy + energy_label.text = str(current_energy) + hand_manager.draw_cards(5) + is_player_turn = true + print("Player turn") + +func end_turn() -> void: + hand_manager.discard_hand() + is_player_turn = false + + # Enemy turns + for enemy in enemies_container.get_children(): + if enemy.has_method("take_turn"): + enemy.take_turn() + + # Back to player + start_turn() + +func can_play_card(card_cost: int) -> bool: + return is_player_turn and current_energy >= card_cost + +func spend_energy(amount: int) -> void: + current_energy -= amount + energy_label.text = str(current_energy) + +================ +File: scripts/managers/config_manager.gd +================ +extends Node + +const CONFIG_PATH = "user://config.cfg" + +signal resolution_changed(new_resolution: Vector2) +signal window_mode_changed(is_fullscreen: bool) + +var config = { + "video": { + "fullscreen": false, + "resolution_index": 3 # Default to 1920x1080 + }, + "audio": { + "master_volume": 0, + "muted": false + } +} + +var resolutions = [ + Vector2i(1280, 720), + Vector2i(1366, 768), + Vector2i(1600, 900), + Vector2i(1920, 1080), + Vector2i(2560, 1440), + Vector2i(3840, 2160) +] + +func _ready() -> void: + load_config() + call_deferred("apply_config") + +func save_config() -> void: + var config_file = FileAccess.open(CONFIG_PATH, FileAccess.WRITE) + var json_string = JSON.stringify(config) + config_file.store_string(json_string) + +func load_config() -> void: + if not FileAccess.file_exists(CONFIG_PATH): + # Get current window state for initial config + config.video.fullscreen = DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN + # Find closest resolution match for current window size + var current_size = DisplayServer.window_get_size() + var closest_index = 0 + var smallest_diff = Vector2.INF + for i in range(resolutions.size()): + var diff = (resolutions[i] - Vector2i(current_size)).length() + if diff < smallest_diff.length(): + smallest_diff = Vector2(diff, diff) + closest_index = i + config.video.resolution_index = closest_index + + # Get current audio state + config.audio.master_volume = AudioServer.get_bus_volume_db(0) + config.audio.muted = AudioServer.is_bus_mute(0) + + save_config() + return + + var config_file = FileAccess.open(CONFIG_PATH, FileAccess.READ) + var json_string = config_file.get_as_text() + + var json = JSON.new() + var parse_result = json.parse(json_string) + if parse_result == OK: + var loaded_config = json.get_data() + if typeof(loaded_config) == TYPE_DICTIONARY: + if "video" in loaded_config: + config.video = loaded_config.video + if "audio" in loaded_config: + config.audio = loaded_config.audio + +func update_video_settings(fullscreen: bool, resolution_index: int) -> void: + var mode_changed = config.video.fullscreen != fullscreen + var res_changed = config.video.resolution_index != resolution_index + + config.video.fullscreen = fullscreen + config.video.resolution_index = resolution_index + apply_config() + save_config() + + if mode_changed: + window_mode_changed.emit(fullscreen) + if res_changed: + resolution_changed.emit(Vector2(resolutions[resolution_index])) + +func apply_config() -> void: + if config.video.fullscreen: + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) + else: + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + var resolution = resolutions[config.video.resolution_index] + DisplayServer.window_set_size(resolution) + + # Center the window + var screen_size = DisplayServer.screen_get_size() + var window_pos = (screen_size - resolution) / 2 + DisplayServer.window_set_position(window_pos) + + # Apply audio settings + AudioServer.set_bus_volume_db(0, config.audio.master_volume) + AudioServer.set_bus_mute(0, config.audio.muted) + +func update_audio_settings(volume: float, muted: bool) -> void: + config.audio.master_volume = volume + config.audio.muted = muted + save_config() + +================ +File: scripts/managers/decoder_manager.gd +================ +extends Node + +signal aspect_decoded(card_id: String, aspect: String) +signal points_changed(new_amount: int) + +var available_points: int = 10 # Start with some points for testing + +const DECODE_COSTS = { + "cost": 2, + "type": 3, + "value": 4, + "description": 5 +} + +func _ready() -> void: + # Update UI on startup + points_changed.emit(available_points) + +func can_decode_aspect(aspect: String) -> bool: + if not DECODE_COSTS.has(aspect): + return false + return available_points >= DECODE_COSTS[aspect] + +func decode_aspect(card: Node, aspect: String) -> void: + if not can_decode_aspect(aspect): + return + + var cost = DECODE_COSTS[aspect] + available_points -= cost + points_changed.emit(available_points) + + # Store the decoded information + var card_id = card.card_data.resource_path + GameState.decoded_aspects[card_id] = GameState.decoded_aspects.get(card_id, {}) + GameState.decoded_aspects[card_id][aspect] = true + + # Update card display + card.reveal_aspect(aspect) + aspect_decoded.emit(card_id, aspect) + + # Update decode UI if it's visible + var decode_ui = get_node("/root/Main/Combat/UI/DecodeUI") + if decode_ui and decode_ui.visible: + decode_ui.update_points() + +func add_points(amount: int) -> void: + available_points += amount + points_changed.emit(available_points) + GameState.save_game() + +================ +File: scripts/managers/hand_manager.gd +================ +extends Node + +const CARD_SCENE = preload("res://scenes/card.tscn") + +var draw_pile: Array = [] +var hand: Array = [] +var discard_pile: Array = [] + +@onready var hand_container = $"../UI/HUD/HandContainer" + +func _ready() -> void: + setup_starting_deck() + +func setup_starting_deck() -> void: + # Add basic cards + for i in range(5): + var attack_card = load("res://resources/cards/attack_card.tres") + var block_card = load("res://resources/cards/block_card.tres") + draw_pile.append(attack_card) + draw_pile.append(block_card) + draw_pile.shuffle() + +func draw_cards(amount: int) -> void: + for i in range(amount): + if draw_pile.size() == 0: + print("Draw pile empty") + reshuffle_discard() + if draw_pile.size() == 0: + return + var card_data = draw_pile.pop_front() + create_card(card_data) + +func create_card(card_data: Resource) -> void: + var card_instance = CARD_SCENE.instantiate() + hand_container.add_child(card_instance) + + # Load decode state from GameState + var decoded_aspects = {} + if card_data.resource_path in GameState.decoded_aspects: + decoded_aspects = GameState.decoded_aspects[card_data.resource_path].duplicate() + + # Initialize card with both data and decode state + card_instance.setup(card_data, decoded_aspects) + card_instance.connect("card_played", _on_card_played, CONNECT_ONE_SHOT) + hand.append(card_instance) + +func discard_hand() -> void: + for card in hand: + discard_pile.append(card.card_data) + card.queue_free() + hand.clear() + +func reshuffle_discard() -> void: + print("Reshuffling discard") + draw_pile.append_array(discard_pile) + discard_pile.clear() + draw_pile.shuffle() + +func _on_card_played(card: Variant) -> void: + hand.remove_at(hand.find(card)) + discard_pile.append(card.card_data) + +================ +File: scripts/managers/pause_manager.gd +================ +extends Node + +const PAUSE_MENU = preload("res://scenes/pause_menu.tscn") + +var pause_menu: Control = null +var is_paused: bool = false + +func _ready() -> void: + setup_pause_menu() + process_mode = Node.PROCESS_MODE_ALWAYS + +func setup_pause_menu() -> void: + if pause_menu == null: + pause_menu = PAUSE_MENU.instantiate() + add_child(pause_menu) + var ui_scale_manager = get_node("/root/Main/UIScaleManager") + ui_scale_manager.register_ui_root(pause_menu) + pause_menu.resume_pressed.connect(_on_resume) + pause_menu.quit_pressed.connect(_on_quit) + pause_menu.hide() + +func _unhandled_input(event: InputEvent) -> void: + if event.is_action_pressed("ui_cancel"): # ESC key + toggle_pause() + +func toggle_pause() -> void: + is_paused = !is_paused + get_tree().paused = is_paused + + if is_paused: + pause_menu.open() + else: + pause_menu.close() + +func _on_resume() -> void: + toggle_pause() + +func _on_quit() -> void: + print("Pause Menu: Handling quit to main menu") + # Unpause the game + is_paused = false + get_tree().paused = false + pause_menu.hide() + + # Let Main handle the scene transition + var main = get_node("/root/Main") + if main: + main.show_main_menu() + else: + print("ERROR: Couldn't find Main node!") + +================ +File: scripts/managers/ui_scale_manager.gd +================ +extends Node + +var current_scale: float = 1.0 +const BASE_RESOLUTION := Vector2(1920, 1080) + +func _ready() -> void: + # Connect to signals + get_tree().root.connect("size_changed", _on_viewport_size_changed) + ConfigManager.resolution_changed.connect(_on_resolution_changed) + ConfigManager.window_mode_changed.connect(_on_window_mode_changed) + + # Initial setup + call_deferred("_do_initial_setup") + +func _do_initial_setup() -> void: + await get_tree().process_frame + _update_ui_scale() + +func _on_viewport_size_changed() -> void: + _update_ui_scale() + +func _on_resolution_changed(_new_resolution: Vector2) -> void: + _update_ui_scale() + +func _on_window_mode_changed(_is_fullscreen: bool) -> void: + _update_ui_scale() + +func register_ui_root(control: Control) -> void: + control.pivot_offset = control.size / 2 + _setup_control(control) + _update_ui_scale() + +func _update_ui_scale() -> void: + var viewport_size := DisplayServer.window_get_size() + var scale_x := viewport_size.x / float(BASE_RESOLUTION.x) + var scale_y := viewport_size.y / float(BASE_RESOLUTION.y) + current_scale = min(scale_x, scale_y) + + _scale_ui_recursive(get_tree().root) + +func _scale_ui_recursive(node: Node) -> void: + if node is Control: + _setup_control(node) + + for child in node.get_children(): + _scale_ui_recursive(child) + +func _setup_control(control: Control) -> void: + if control is CenterContainer: + var viewport_size = DisplayServer.window_get_size() + control.custom_minimum_size = Vector2.ZERO + control.anchor_right = 1.0 + control.anchor_bottom = 1.0 + control.offset_right = 0 + control.offset_bottom = 0 + control.position = Vector2.ZERO + control.size = viewport_size + + # Update children anchoring + for child in control.get_children(): + if child is Control: + child.anchor_left = 0.5 + child.anchor_right = 0.5 + child.anchor_top = 0.5 + child.anchor_bottom = 0.5 + child.position = -child.size / 2 + +================ +File: scripts/scenes/card.gd +================ +extends Control + +signal card_played(card) + +var card_data: Resource +var decoded_aspects: Dictionary = {} + +@onready var cost_label: Label = $MarginContainer/VBoxContainer/TopBar/CostLabel +@onready var name_label: Label = $MarginContainer/VBoxContainer/TopBar/NameLabel +@onready var effect_label: Label = $MarginContainer/VBoxContainer/EffectLabel + +# Constants for unread display +const UNKNOWN_COST = "?" +const UNKNOWN_NAME = "???" +const UNKNOWN_EFFECT = "????????????" + +func _ready() -> void: + # Set fixed size for cards + custom_minimum_size = Vector2(200, 300) + size = Vector2(200, 300) + + # Center card contents + $MarginContainer.anchor_right = 1.0 + $MarginContainer.anchor_bottom = 1.0 + $MarginContainer.offset_right = 0 + $MarginContainer.offset_bottom = 0 + + # Update card display + update_display() + +func update_display() -> void: + if not is_node_ready(): + await ready + # Cost display + if is_aspect_decoded("cost"): + cost_label.text = str(card_data.energy_cost) + else: + cost_label.text = UNKNOWN_COST + + # Name display + if is_aspect_decoded("name"): + name_label.text = card_data.name + else: + name_label.text = UNKNOWN_NAME + + # Effect display + if is_aspect_decoded("description"): + effect_label.text = card_data.effect_description + else: + effect_label.text = UNKNOWN_EFFECT + +func is_aspect_decoded(aspect: String) -> bool: + return decoded_aspects.get(aspect, false) + +func setup(data: Resource, starting_decoded_aspects: Dictionary = {}) -> void: + card_data = data + decoded_aspects = starting_decoded_aspects + +func reveal_aspect(aspect: String) -> void: + print("Revealing " + aspect) + decoded_aspects[aspect] = true + # Save to GameState + if not card_data.resource_path in GameState.decoded_aspects: + GameState.decoded_aspects[card_data.resource_path] = {} + GameState.decoded_aspects[card_data.resource_path][aspect] = true + update_display() + +func get_effect_value() -> int: + if is_aspect_decoded("value"): + return card_data.effect_value + else: + # Random value for unread cards + match card_data.card_type: + "attack": return randi_range(4, 8) + "block": return randi_range(4, 8) + _: return card_data.effect_value + +func _gui_input(event: InputEvent) -> void: + if event is InputEventMouseButton: + if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed: + show_decode_menu() + elif event.button_index == MOUSE_BUTTON_LEFT and event.pressed: + var combat_scene = get_node("/root/Main/Combat") + var combat_manager = combat_scene.get_node("CombatManager") + if combat_manager and combat_manager.can_play_card(card_data.energy_cost): + play_card() + +func play_card() -> void: + var combat_scene = get_node("/root/Main/Combat") + var combat_manager = combat_scene.get_node("CombatManager") + combat_manager.spend_energy(card_data.energy_cost) + execute_effect() + card_played.emit(self) + queue_free() + +func execute_effect() -> void: + var combat_scene = get_node("/root/Main/Combat") + + match card_data.card_type: + "attack": + var enemies_container = combat_scene.get_node("EnemiesContainer") + if enemies_container and enemies_container.get_child_count() > 0: + # For now, just target the first enemy + # You might want to implement target selection later + var enemy = enemies_container.get_child(0) + enemy.take_damage(get_effect_value()) + "block": + var player = combat_scene.get_node("Player") + if player: + player.gain_block(get_effect_value()) + +func show_decode_menu() -> void: + var popup = PopupMenu.new() + add_child(popup) + + # Add decode options + if not is_aspect_decoded("cost"): + popup.add_item("Decode Cost (2 points)", 0) + if not is_aspect_decoded("type"): + popup.add_item("Decode Type (3 points)", 1) + if not is_aspect_decoded("value"): + popup.add_item("Decode Value (4 points)", 2) + if not is_aspect_decoded("description"): + popup.add_item("Decode Description (5 points)", 3) + + if popup.item_count == 0: + popup.add_item("Fully Decoded", -1) + popup.set_item_disabled(0, true) + + popup.id_pressed.connect(_on_decode_option_selected) + popup.position = get_global_mouse_position() + popup.popup() + +func _on_decode_option_selected(id: int) -> void: + var combat_scene = get_node("/root/Main/Combat") + var decoder = combat_scene.get_node("DecoderManager") + match id: + 0: decoder.decode_aspect(self, "cost") + 1: decoder.decode_aspect(self, "type") + 2: decoder.decode_aspect(self, "value") + 3: decoder.decode_aspect(self, "description") + +================ +File: scripts/scenes/combat.gd +================ +extends Node2D + +@onready var combat_manager = $CombatManager +@onready var hud = $UI/HUD + +func _ready() -> void: + # Set up HUD to fill screen + hud.anchor_right = 1.0 + hud.anchor_bottom = 1.0 + hud.offset_right = 0 + hud.offset_bottom = 0 + + # Center the hand container + var hand_container = $UI/HUD/HandContainer + hand_container.custom_minimum_size.y = 300 + hand_container.anchor_left = 0.5 + hand_container.anchor_right = 0.5 + hand_container.anchor_top = 1.0 + hand_container.anchor_bottom = 1.0 + hand_container.offset_top = -300 + + # Position end turn button + var end_turn_container = $UI/HUD/EndTurnMarginContainer + end_turn_container.anchor_left = 1.0 + end_turn_container.anchor_right = 1.0 + end_turn_container.anchor_top = 1.0 + end_turn_container.anchor_bottom = 1.0 + + # Position energy display + var energy_container = $UI/HUD/EnergyMarginContainer + energy_container.anchor_left = 1.0 + energy_container.anchor_right = 1.0 + + $UI/HUD/EndTurnMarginContainer/EndTurnButton.pressed.connect(_on_end_turn_pressed) + + # Connect to enemy death or victory condition + $EnemiesContainer.child_exiting_tree.connect(_check_combat_state) + +func _check_combat_state(_node) -> void: + # If all enemies are dead + if $EnemiesContainer.get_child_count() <= 1: # <= 1 because the signal fires before the node is fully removed + _on_combat_won() + +func _on_combat_won() -> void: + AdventureSystem.complete_current_encounter() + queue_free() + +func _on_end_turn_pressed() -> void: + combat_manager.end_turn() + +================ +File: scripts/scenes/cutscene_scene.gd +================ +class_name CutsceneScene +extends Control + +signal cutscene_finished + +@onready var audio_player: AudioStreamPlayer2D = $AudioStreamPlayer +@onready var image_display: TextureRect = $TextureRect + +var current_cutscene: CutsceneData +var current_frame_index: int = 0 +var exit_callback: Callable + +func _ready() -> void: + audio_player.connect("finished", _on_audio_finished) + +func start_cutscene(cutscene: CutsceneData, callback: Callable) -> void: + if not cutscene: + push_error("Invalid cutscene resource") + return + + print("Cutscene: " + cutscene.resource_name) + + current_cutscene = cutscene + current_frame_index = 0 + exit_callback = callback + + # Setup audio + audio_player.stream = cutscene.audio_track + audio_player.play() + + # Show first frame + if cutscene.frames.size() > 0: + image_display.texture = cutscene.frames[0].image + + # Start checking timestamps + set_process(true) + +func _process(_delta: float) -> void: + if not current_cutscene or current_frame_index >= current_cutscene.frames.size(): + return + + var current_time = audio_player.get_playback_position() + var next_frame = current_cutscene.frames[current_frame_index] + + if current_time >= next_frame.timestamp: + image_display.texture = next_frame.image + current_frame_index += 1 + +func _on_audio_finished() -> void: + set_process(false) + emit_signal("cutscene_finished") + + if exit_callback != null: + exit_callback.call() + + self.queue_free() + +func _unhandled_input(event: InputEvent) -> void: + if event.is_action_pressed("ui_cancel"): + skip_cutscene() + +func skip_cutscene() -> void: + audio_player.stop() + _on_audio_finished() + +================ +File: scripts/scenes/encounter_node.gd +================ +extends Control + +signal node_clicked(node_data: EncounterNodeData) + +var node_data: EncounterNodeData + +@onready var circle: ColorRect = $CenterContainer/VBoxContainer/Circle +@onready var label: Label = $CenterContainer/VBoxContainer/Label + +const BASE_NODE_COLOR := Color("4a4a4a") +const START_NODE_COLOR := Color("1a531a") +const FINISH_NODE_COLOR := Color("531a1a") +const COMPLETED_COLOR := Color("4a821a") +const HOVER_TINT := Color(1.2, 1.2, 1.2, 1) + +func _ready() -> void: + custom_minimum_size = Vector2(100, 100) + mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + + # Setup mouse handling + gui_input.connect(_on_gui_input) + mouse_entered.connect(_on_mouse_entered) + mouse_exited.connect(_on_mouse_exited) + + # If we were set up before _ready, update appearance now + if node_data: + update_appearance() + +func setup(data: EncounterNodeData) -> void: + node_data = data + if is_node_ready(): + update_appearance() + +func get_node_data() -> EncounterNodeData: + return node_data + +func set_start_node() -> void: + if not is_node_ready(): + await ready + circle.color = START_NODE_COLOR + label.text = "Start" + +func set_finish_node() -> void: + if not is_node_ready(): + await ready + circle.color = FINISH_NODE_COLOR + label.text = "Final" + +func set_completed() -> void: + if not is_node_ready(): + await ready + circle.color = COMPLETED_COLOR + +func update_appearance() -> void: + if not is_node_ready(): + await ready + + if node_data.completed: + set_completed() + elif node_data is StartEncounterNodeData: + set_start_node() + elif node_data is FinishEncounterNodeData: + set_finish_node() + else: + circle.color = BASE_NODE_COLOR + label.text = "Encounter" + +func _on_gui_input(event: InputEvent) -> void: + if event is InputEventMouseButton: + if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: + node_clicked.emit(node_data) + +func _on_mouse_entered() -> void: + modulate = HOVER_TINT + +func _on_mouse_exited() -> void: + modulate = Color.WHITE + +================ +File: scripts/scenes/home_base.gd +================ +extends Control + +func _on_adventure_button_pressed() -> void: + pass # Go to run tree + +func _on_decoder_button_pressed() -> void: + pass # Go to decoder + +func _on_stone_button_pressed() -> void: + pass # Go to stone powers and progress + +func _on_loadout_button_pressed() -> void: + pass # Go to deck viewer and loadout + +================ +File: scripts/scenes/home_base.tscn +================ +[gd_scene load_steps=3 format=3 uid="uid://bnk32gyp3bsir"] + +[ext_resource type="Script" path="res://scripts/scenes/home_base.gd" id="1_4udhq"] +[ext_resource type="Script" path="res://scripts/managers/pause_manager.gd" id="2_4gf2c"] + +[node name="HomeBase" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_4udhq") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Label" type="Label" parent="VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 105 +text = "Home base" +horizontal_alignment = 1 + +[node name="GridContainer" type="GridContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +theme_override_constants/h_separation = 40 +theme_override_constants/v_separation = 40 +columns = 2 + +[node name="AdventureButton" type="Button" parent="VBoxContainer/GridContainer"] +custom_minimum_size = Vector2(400, 400) +layout_mode = 2 +theme_override_font_sizes/font_size = 87 +text = "Go on an adventure" + +[node name="DecoderButton" type="Button" parent="VBoxContainer/GridContainer"] +custom_minimum_size = Vector2(400, 400) +layout_mode = 2 +theme_override_font_sizes/font_size = 87 +text = "Decode cards" + +[node name="StoneButton" type="Button" parent="VBoxContainer/GridContainer"] +custom_minimum_size = Vector2(400, 400) +layout_mode = 2 +theme_override_font_sizes/font_size = 87 +text = "Magical stone" + +[node name="LoadoutButton" type="Button" parent="VBoxContainer/GridContainer"] +custom_minimum_size = Vector2(400, 400) +layout_mode = 2 +theme_override_font_sizes/font_size = 87 +text = "Deck and inventory" + +[node name="PauseManager" type="Node" parent="."] +script = ExtResource("2_4gf2c") + +[connection signal="pressed" from="VBoxContainer/GridContainer/AdventureButton" to="." method="_on_adventure_button_pressed"] +[connection signal="pressed" from="VBoxContainer/GridContainer/DecoderButton" to="." method="_on_decoder_button_pressed"] +[connection signal="pressed" from="VBoxContainer/GridContainer/StoneButton" to="." method="_on_stone_button_pressed"] +[connection signal="pressed" from="VBoxContainer/GridContainer/LoadoutButton" to="." method="_on_loadout_button_pressed"] + +================ +File: scripts/scenes/main_menu.gd +================ +# main_menu.gd +extends Control + +const OPTIONS_SCENE = preload("res://scenes/menus/options_menu.tscn") + +func _ready(): + $CenterContainer/VBoxContainer/StartButton.pressed.connect(_on_start_pressed) + $CenterContainer/VBoxContainer/ContinueButton.pressed.connect(_on_continue_button_pressed) + $CenterContainer/VBoxContainer/Options.pressed.connect(_on_options_pressed) + $CenterContainer/VBoxContainer/QuitButton.pressed.connect(_on_quit_pressed) + # Enable menu when it's first shown + enable_menu() + +func _on_start_pressed(): + print("start pressed") + # Disable menu interaction before starting cutscene + disable_menu() + # Tell the main scene to handle the new game sequence + get_node("/root/Main").handle_new_game() + +func _on_quit_pressed(): + print("quit pressed") + GameState.save_game() + get_tree().quit() + +func _on_continue_button_pressed() -> void: + print("continue pressed") + GameState.load_game() + get_node("/root/Main").start_home_base() + +func _on_options_pressed() -> void: + var options = OPTIONS_SCENE.instantiate() + add_child(options) + # Hide the main menu buttons while in options + $CenterContainer.hide() + # Connect to know when to show the buttons again + options.options_closed.connect(_on_options_closed) + +func _on_options_closed() -> void: + $CenterContainer.show() + +func disable_menu() -> void: + # Disable all buttons + for button in $CenterContainer/VBoxContainer.get_children(): + if button is Button: + button.disabled = true + + # Make menu non-interactive + mouse_filter = Control.MOUSE_FILTER_IGNORE + + # Optional: fade the menu visually + modulate = Color(1, 1, 1, 0.5) + +func enable_menu() -> void: + # Enable all buttons that should be enabled + for button in $CenterContainer/VBoxContainer.get_children(): + if button is Button: + # Skip buttons that should remain disabled (like Options or Continue if not available) + if button.name == "ContinueButton" and not has_save_game(): + continue + button.disabled = false + + # Make menu interactive again + mouse_filter = Control.MOUSE_FILTER_STOP + + # Restore full opacity + modulate = Color(1, 1, 1, 1) + + # Focus the Start button + $CenterContainer/VBoxContainer/StartButton.grab_focus() + +func has_save_game() -> bool: + return FileAccess.file_exists("user://gamestate.save") + +================ +File: scripts/scenes/main.gd +================ +extends Node + +@onready var combat_scene = preload("res://scenes/combat.tscn") +@onready var menu_scene = preload("res://scenes/menus/main_menu.tscn") +@onready var home_base_scene = preload("res://scripts/scenes/home_base.tscn") + +var current_scene: Node = null + +func _ready(): + var ui_scale_manager = Node.new() + ui_scale_manager.name = "UIScaleManager" + ui_scale_manager.set_script(load("res://scripts/managers/ui_scale_manager.gd")) + add_child(ui_scale_manager) + # Ensure config is applied before showing the menu + await get_tree().create_timer(0.1).timeout + show_main_menu() + +func cleanup_current_scene() -> void: + print("Main: Cleaning up current scene") + AdventureSystem.cleanup() + if current_scene: + remove_child(current_scene) + current_scene.queue_free() + current_scene = null + +func transition_to_scene(new_scene: Node) -> void: + print("Transitioning to new scene: ", new_scene.name) + cleanup_current_scene() + + print("Adding new scene: ", new_scene.name) + current_scene = new_scene + add_child(current_scene) + +func show_main_menu() -> void: + print("Showing main menu") + var new_menu = menu_scene.instantiate() + call_deferred("transition_to_scene", new_menu) + +func start_combat() -> void: + var new_combat = combat_scene.instantiate() + call_deferred("transition_to_scene", new_combat) + +func start_home_base() -> void: + var new_home = home_base_scene.instantiate() + call_deferred("transition_to_scene", new_home) + +func handle_new_game() -> void: + GameState.start_new_run() + var new_game_cutscene = load("res://resources/cutscenes/new_game/new_game_cutscene.tres") + cleanup_current_scene() + + # Start the cutscene + CutsceneSystem.play_cutscene(new_game_cutscene, func(): + # After cutscene, start tutorial + var ingame_tutorial_adventure = load("res://resources/adventures/ingame_tutorial/adventure_map.tres") + AdventureSystem.start_adventure(ingame_tutorial_adventure) + # Connect to adventure completion if not already connected + if not AdventureSystem.adventure_completed.is_connected(_on_tutorial_completed): + AdventureSystem.adventure_completed.connect(_on_tutorial_completed) + ) + +func _on_tutorial_completed(_adventure: AdventureMapData) -> void: + start_home_base() + +================ +File: scripts/scenes/options_menu.gd +================ +# options_menu.gd +extends Control + +signal options_closed + +# Scene References +@onready var fullscreen_toggle = %FullscreenToggle +@onready var resolution_dropdown = %ResolutionOptionButton +@onready var master_volume_slider = %VolumeSlider +@onready var mute_toggle = %MuteToggle +@onready var back_button = %BackButton +@onready var volume_label = %VolumeLabelValue + +# Constants for volume conversion +const MIN_DB = -80 +const MAX_DB = 0 + +func _ready(): + setup_controls() + load_settings() + connect_signals() + +func setup_controls(): + # Setup resolution dropdown + for resolution in ConfigManager.resolutions: + resolution_dropdown.add_item("%dx%d" % [resolution.x, resolution.y]) + + # Setup volume slider with percentage values + master_volume_slider.min_value = 0 + master_volume_slider.max_value = 100 + master_volume_slider.step = 1 + + # Load initial values + fullscreen_toggle.button_pressed = ConfigManager.config.video.fullscreen + resolution_dropdown.selected = ConfigManager.config.video.resolution_index + # Convert from dB to percentage for display + master_volume_slider.value = db_to_percent(ConfigManager.config.audio.master_volume) + mute_toggle.button_pressed = ConfigManager.config.audio.muted + + # Update volume display + _update_volume_label(master_volume_slider.value) + + # Update resolution dropdown state based on fullscreen + _update_resolution_dropdown_state() + +func connect_signals(): + print("Connecting signals...") # Debug print + fullscreen_toggle.pressed.connect(_on_fullscreen_toggled) + resolution_dropdown.item_selected.connect(_on_resolution_selected) + master_volume_slider.value_changed.connect(_on_volume_changed) + mute_toggle.toggled.connect(_on_mute_toggled) + back_button.pressed.connect(_on_back_pressed) + +# Convert decibel value to percentage (0-100) +func db_to_percent(db: float) -> float: + if db <= MIN_DB: + return 0.0 + if db >= MAX_DB: + return 100.0 + return ((db - MIN_DB) / (MAX_DB - MIN_DB)) * 100.0 + +# Convert percentage (0-100) to decibel value +func percent_to_db(percent: float) -> float: + return lerp(MIN_DB, MAX_DB, percent / 100.0) + +func _update_volume_label(percent: float) -> void: + volume_label.text = "%d%%" % percent + +func _on_volume_changed(value: float): + print("Volume changed to ", value, "%") + _update_volume_label(value) + var db_value = percent_to_db(value) + AudioServer.set_bus_volume_db(0, db_value) + save_settings() + +func _update_resolution_dropdown_state(): + if fullscreen_toggle.button_pressed: + # Get current screen resolution + var screen_size = DisplayServer.screen_get_size() + resolution_dropdown.disabled = true + + # Find and select the closest matching resolution + var closest_index = 0 + var smallest_diff = Vector2.INF + for i in range(ConfigManager.resolutions.size()): + var diff = (ConfigManager.resolutions[i] - screen_size).length() + if diff < smallest_diff.length(): + smallest_diff = Vector2(diff, diff) + closest_index = i + + resolution_dropdown.selected = closest_index + DisplayServer.window_set_size(screen_size) + else: + resolution_dropdown.disabled = false + +func _on_fullscreen_toggled(): + print("Fullscreen toggled: ", fullscreen_toggle.button_pressed) # Debug print + if fullscreen_toggle.button_pressed: + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) + else: + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + + _update_resolution_dropdown_state() + save_settings() + +func _on_resolution_selected(index: int): + print("Resolution selected: ", index) # Debug print + if index >= 0 and index < ConfigManager.resolutions.size() and not fullscreen_toggle.button_pressed: + var resolution = ConfigManager.resolutions[index] + DisplayServer.window_set_size(resolution) + save_settings() + +func _on_mute_toggled(button_pressed: bool): + print("Mute toggled: ", button_pressed) # Debug print + AudioServer.set_bus_mute(0, button_pressed) + save_settings() + +func _on_back_pressed(): + print("Back pressed") # Debug print + save_settings() + options_closed.emit() + queue_free() + +func save_settings(): + ConfigManager.update_video_settings( + fullscreen_toggle.button_pressed, + resolution_dropdown.selected + ) + ConfigManager.update_audio_settings( + percent_to_db(master_volume_slider.value), + mute_toggle.button_pressed + ) + +func load_settings(): + fullscreen_toggle.button_pressed = ConfigManager.config.video.fullscreen + resolution_dropdown.selected = ConfigManager.config.video.resolution_index + master_volume_slider.value = db_to_percent(ConfigManager.config.audio.master_volume) + _update_volume_label(master_volume_slider.value) + mute_toggle.button_pressed = ConfigManager.config.audio.muted + + # Apply the settings + if ConfigManager.config.video.fullscreen: + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) + else: + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + _on_resolution_selected(ConfigManager.config.video.resolution_index) + + AudioServer.set_bus_volume_db(0, ConfigManager.config.audio.master_volume) + AudioServer.set_bus_mute(0, ConfigManager.config.audio.muted) + +================ +File: scripts/systems/adventure_system.gd +================ +extends Node + +signal adventure_started(adventure_data: AdventureMapData) +signal encounter_selected(node: EncounterNodeData) +signal encounter_completed(node: EncounterNodeData) +signal adventure_completed(adventure_data: AdventureMapData) + +const COMBAT_SCENE = preload("res://scenes/combat.tscn") +const ADVENTURE_MAP_SCENE = preload("res://scenes/adventure_map.tscn") + +var current_adventure: AdventureMapData +var current_node: EncounterNodeData +var current_scene: Node +var map_instance: Node + +func cleanup() -> void: + print("Adventure System: Cleaning up...") + if is_instance_valid(current_scene): + print("Removing current scene") + if current_scene.get_parent(): + current_scene.get_parent().call_deferred("remove_child", current_scene) + current_scene.call_deferred("queue_free") + current_scene = null + + if is_instance_valid(map_instance): + print("Removing map instance") + if map_instance.get_parent(): + map_instance.get_parent().call_deferred("remove_child", map_instance) + map_instance.call_deferred("queue_free") + map_instance = null + + current_node = null + +func start_adventure(adventure_data: AdventureMapData) -> void: + print("Starting new adventure") + cleanup() # Clean up any existing adventure first + current_adventure = adventure_data + current_node = null + + adventure_started.emit(adventure_data) + + if adventure_data.auto_start and adventure_data.rootEncounterNode.childNodes.size() > 0: + _auto_start_first_encounter(adventure_data.rootEncounterNode) + else: + show_map() + +func show_map() -> void: + if current_scene or map_instance: + cleanup() + await get_tree().process_frame + + print("Creating new adventure map") + map_instance = ADVENTURE_MAP_SCENE.instantiate() + var main = get_node("/root/Main") + main.call_deferred("add_child", map_instance) + + await get_tree().process_frame + var adventure_manager = map_instance.get_node("AdventureManager") + if adventure_manager: + print("Initializing adventure map") + adventure_manager.init_adventure(current_adventure) + else: + push_error("AdventureManager not found in map instance") + +func _auto_start_first_encounter(start_node: StartEncounterNodeData) -> void: + if start_node.childNodes.size() > 0: + var first_encounter = start_node.childNodes[0] + select_encounter(first_encounter) + +func select_encounter(encounter_node: EncounterNodeData) -> void: + if not can_select_encounter(encounter_node): + return + + print("Selecting encounter: ", encounter_node.get_class()) + current_node = encounter_node + encounter_selected.emit(encounter_node) + + if is_instance_valid(map_instance): + if map_instance.get_parent(): + map_instance.get_parent().call_deferred("remove_child", map_instance) + map_instance.call_deferred("queue_free") + map_instance = null + + if encounter_node is CombatEncounterNodeData: + start_combat(encounter_node) + elif encounter_node is EventEncounterNodeData: + start_event(encounter_node) + elif encounter_node is FinishEncounterNodeData: + handle_finish_encounter(encounter_node) + +func can_select_encounter(node: EncounterNodeData) -> bool: + if node.completed: + return false + + if not node is StartEncounterNodeData: + var has_completed_parent = false + for potential_parent in _get_all_parent_nodes(node): + if potential_parent.completed: + has_completed_parent = true + break + if not has_completed_parent: + return false + + return true + +func _get_all_parent_nodes(node: EncounterNodeData) -> Array[EncounterNodeData]: + var parents: Array[EncounterNodeData] = [] + _find_parents_recursive(current_adventure.rootEncounterNode, node, parents) + return parents + +func _find_parents_recursive(current: EncounterNodeData, target: EncounterNodeData, parents: Array[EncounterNodeData]) -> bool: + for child in current.childNodes: + if child == target: + parents.append(current) + return true + if _find_parents_recursive(child, target, parents): + parents.append(current) + return true + return false + +func complete_current_encounter() -> void: + if not current_node: + return + + print("Completing encounter: ", current_node.get_class()) + current_node.completed = true + encounter_completed.emit(current_node) + + if current_node is FinishEncounterNodeData: + adventure_completed.emit(current_adventure) + else: + call_deferred("_handle_encounter_completion") + +func _handle_encounter_completion() -> void: + if is_instance_valid(current_scene): + if current_scene.get_parent(): + current_scene.get_parent().call_deferred("remove_child", current_scene) + current_scene.call_deferred("queue_free") + current_scene = null + + await get_tree().process_frame + print("Showing map after encounter completion") + show_map() + +func start_combat(combat_node: CombatEncounterNodeData) -> void: + print("Starting combat encounter") + var main = get_node("/root/Main") + current_scene = COMBAT_SCENE.instantiate() + + var enemies_container = current_scene.get_node("EnemiesContainer") + + for enemy_data in combat_node.enemies: + var enemy_instance = enemy_data.enemy_scene.instantiate() + enemies_container.add_child(enemy_instance) + if enemy_instance.has_method("setup"): + enemy_instance.setup(enemy_data) + + main.call_deferred("add_child", current_scene) + +func start_event(event_node: EventEncounterNodeData) -> void: + print("Starting event encounter") + if event_node.eventScene: + var main = get_node("/root/Main") + current_scene = event_node.eventScene.instantiate() + main.call_deferred("add_child", current_scene) + else: + print("Warning: Event node has no scene assigned") + complete_current_encounter() + +func handle_finish_encounter(finish_node: FinishEncounterNodeData) -> void: + print("Starting finish encounter") + if finish_node.finishScene: + var main = get_node("/root/Main") + current_scene = finish_node.finishScene.instantiate() + main.call_deferred("add_child", current_scene) + else: + print("Completing adventure without finish scene") + complete_current_encounter() + +func return_to_map() -> void: + if not (current_node is FinishEncounterNodeData): + if is_instance_valid(current_scene): + if current_scene.get_parent(): + current_scene.get_parent().call_deferred("remove_child", current_scene) + current_scene.call_deferred("queue_free") + current_scene = null + show_map() + +func get_current_adventure() -> AdventureMapData: + return current_adventure + +func get_current_node() -> EncounterNodeData: + return current_node + +func is_node_available(node: EncounterNodeData) -> bool: + return can_select_encounter(node) + +================ +File: scripts/systems/background_system.gd +================ +extends Node + +# Preload the background textures +const MAIN_MENU_BG = preload("res://assets/backgrounds/main_menu_bg.webp") +const COMBAT_BG = preload("res://assets/backgrounds/combat_bg.png") + +var current_background: Control + +func cleanup() -> void: + if current_background: + if current_background.get_parent(): + current_background.get_parent().remove_child(current_background) + current_background.queue_free() + current_background = null + print("Background System: Cleaned up current background") + +func setup_for_scene(scene_type: String) -> void: + print("Background System: Setting up background for ", scene_type) + cleanup() + + var background: Control + + match scene_type: + "MainMenu": + background = TextureRect.new() + background.texture = MAIN_MENU_BG + "Combat": + background = TextureRect.new() + background.texture = COMBAT_BG + _: + background = ColorRect.new() + background.color = Color.WHITE + + if background is TextureRect: + background.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED + + background.set_anchors_preset(Control.PRESET_FULL_RECT) + background.z_index = -1 + background.mouse_filter = Control.MOUSE_FILTER_IGNORE + + add_child(background) + current_background = background + +================ +File: scripts/systems/cutscene_system.gd +================ +extends Node + +const CUTSCENE_SCENE_PATH = "res://scenes/cutscene.tscn" + +func play_cutscene(cutscene: CutsceneData, callback: Callable) -> void: + if not cutscene: + push_error("Invalid cutscene resource") + return + + var cutscene_scene = load(CUTSCENE_SCENE_PATH).instantiate() as CutsceneScene + get_tree().root.add_child(cutscene_scene) + cutscene_scene.start_cutscene(cutscene, callback) + +================ +File: scripts/utils/circle.gd +================ +extends ColorRect + +func _draw() -> void: + draw_circle(size / 2, size.x / 2, color) + +func _ready() -> void: + # Make the ColorRect transparent, we'll only show the circle + color = Color.TRANSPARENT + +================ +File: scripts/decode_ui.gd +================ +# decode_ui.gd +extends PanelContainer + +signal aspect_selected(aspect: String) + +@onready var decode_points_label = $MarginContainer/VBox/DecodePointsLabel +@onready var options_container = $MarginContainer/VBox/OptionsContainer + +# Decode costs for each aspect +const COSTS = { + "cost": 2, + "type": 3, + "value": 4, + "description": 5 +} + +func _ready() -> void: + hide() + +func show_for_card(card: Node) -> void: + # Clear previous options + for child in options_container.get_children(): + child.queue_free() + + # Add new options based on card's decode state + for aspect in COSTS: + if not card.is_aspect_decoded(aspect): + var button = Button.new() + button.text = "Decode %s (%d points)" % [aspect.capitalize(), COSTS[aspect]] + + var decoder = get_node("/root/Main/Combat/DecoderManager") + button.disabled = decoder.available_points < COSTS[aspect] + + button.pressed.connect(func(): + aspect_selected.emit(aspect) + hide() + ) + options_container.add_child(button) + + if options_container.get_child_count() == 0: + var label = Label.new() + label.text = "Card fully decoded!" + options_container.add_child(label) + + # Show the popup + show() + # Center in viewport + position = get_viewport_rect().size / 2 - size / 2 + +================ +File: scripts/enemy.gd +================ +extends Node + +var health: int = 30 +var max_health: int = 30 + +@onready var health_label: Label = $HealthLabel + +func _ready() -> void: + update_health_display() + +func take_damage(amount: int) -> void: + health -= amount + update_health_display() + if health <= 0: + queue_free() + +func take_turn() -> void: + print("Enemy turn") + var player = get_node("/root/Main/Combat/Player") + if player: + # This will be replaced with actual ability usage + pass + +func update_health_display() -> void: + health_label.text = str(health) + "/" + str(max_health) + +func setup(enemy_data: EnemyData) -> void: + if not is_node_ready(): + await ready + health = enemy_data.health + max_health = enemy_data.health + update_health_display() + +================ +File: scripts/pause_menu.gd +================ +extends Control + +signal resume_pressed +signal quit_pressed + +func _ready() -> void: + hide() + # Connect button signals + var resume_button = $PanelContainer/MarginContainer/VBoxContainer/ResumeButton + var quit_button = $PanelContainer/MarginContainer/VBoxContainer/QuitButton + + if not resume_button.pressed.is_connected(_on_resume_pressed): + resume_button.pressed.connect(_on_resume_pressed) + if not quit_button.pressed.is_connected(_on_quit_pressed): + quit_button.pressed.connect(_on_quit_pressed) + +func _on_resume_pressed() -> void: + resume_pressed.emit() + +func _on_quit_pressed() -> void: + quit_pressed.emit() + +func open() -> void: + show() + $PanelContainer/MarginContainer/VBoxContainer/ResumeButton.grab_focus() + +func close() -> void: + hide() + +================ +File: scripts/player.gd +================ +extends Node2D + +var health: int = 40 +var max_health: int = 40 +var block: int = 0 + +@onready var health_label = $HealthLabel + +func _ready() -> void: + update_health_display() + +func take_damage(amount: int) -> void: + # First reduce block + if block > 0: + var blocked = min(block, amount) + amount -= blocked + block -= blocked + + # Then reduce health + if amount > 0: + health -= amount + print("Player took ", amount, " damage. Health: ", health) + + update_health_display() + + if health <= 0: + die() + +func gain_block(amount: int) -> void: + block += amount + print("Player gained ", amount, " block. Total block: ", block) + update_health_display() + +func update_health_display() -> void: + var block_text = " [" + str(block) + "]" if block > 0 else "" + health_label.text = str(health) + "/" + str(max_health) + block_text + +func die() -> void: + print("Game Over!") + # Handle game over logic + +================ +File: .gitignore +================ +# Godot 4+ specific ignores +.godot/ + +# Godot-specific ignores +.import/ +export.cfg +export_presets.cfg + +# Imported translations (automatically generated from CSV files) +*.translation + +# Mono-specific ignores +.mono/ +data_*/ +mono_crash.*.json + +# Mac-specific ignores +.DS_Store + +================ +File: ATTRIBUTION.md +================ +# Attribution +## Collaborators + + +### Role +Person 1 +Person 2 +[Person w/ Link]() + + +## Sourced / Unaffiliated +### Asset Type +#### Use Case +Author: [Name]() +Source: [Domain : webpage.html]() +License: [License]() + + +## Tools +#### Godot +Author: [Juan Linietsky, Ariel Manzur, and contributors](https://godotengine.org/contact) +Source: [godotengine.org](https://godotengine.org/) +License: [MIT License](https://github.com/godotengine/godot/blob/master/LICENSE.txt) + +#### Git +Author: [Linus Torvalds](https://github.com/torvalds) +Source: [git-scm.com](https://git-scm.com/downloads) +License: [GNU General Public License version 2](https://opensource.org/licenses/GPL-2.0) + +#### Godot Options Menus +Author: [Marek Belski and contributors](https://github.com/Maaack/Godot-Options-Menus/graphs/contributors) +Source: [github: Godot-Options-Menus](https://github.com/Maaack/Godot-Options-Menus) +License: [MIT License](LICENSE.txt) + +================ +File: LICENSE +================ +MIT License + +Copyright (c) 2024-present Pixel Pilgrims Studio + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +================ +File: project.godot +================ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[ai_assistant_hub] + +base_url="http://127.0.0.1:11434" +llm_api="ollama_api" + +[application] + +config/name="New Game Project" +run/main_scene="res://scenes/main.tscn" +config/features=PackedStringArray("4.3", "GL Compatibility") + +[autoload] + +ConfigManager="*res://scripts/managers/config_manager.gd" +GameState="*res://scripts/autoload/game_state.gd" +AdventureSystem="*res://scripts/systems/adventure_system.gd" +BackgroundSystem="*res://scripts/systems/background_system.gd" +CutsceneSystem="*res://scripts/systems/cutscene_system.gd" + +[display] + +window/size/viewport_width=1920 +window/size/viewport_height=1080 +window/size/resizable=false + +[editor_plugins] + +enabled=PackedStringArray() + +[github_to_itch] + +config/itch_username="" +config/itch_project_name="" + +[maaacks_options_menus] + +disable_plugin_dialogues=true +copy_path="res://scenes" + +[rendering] + +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" + +================ +File: README.md +================ +# game-off-2024 diff --git a/resources/adventures/ingame_tutorial/0_combat.tres b/resources/adventures/ingame_tutorial/0_combat.tres index c7f7521..776def1 100644 --- a/resources/adventures/ingame_tutorial/0_combat.tres +++ b/resources/adventures/ingame_tutorial/0_combat.tres @@ -1,5 +1,6 @@ -[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://dsohl2qco44tx"] +[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=7 format=3 uid="uid://dsohl2qco44tx"] +[ext_resource type="Texture2D" uid="uid://dxq4fe4rkpgs6" path="res://assets/backgrounds/combat_bg.png" id="1_msf4q"] [ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_w06wc"] [ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_7oxnr"] [ext_resource type="Resource" uid="uid://b61wo0msir67x" path="res://resources/adventures/ingame_tutorial/1_combat.tres" id="2_lvvpg"] @@ -11,3 +12,4 @@ script = ExtResource("3_1bpls") enemies = Array[ExtResource("2_7oxnr")]([ExtResource("4_sukwl")]) childNodes = Array[ExtResource("1_w06wc")]([ExtResource("2_lvvpg")]) completed = false +background = ExtResource("1_msf4q") diff --git a/scenes/adventure_map.tscn b/scenes/adventure_map.tscn index a8e4b1d..10ce532 100644 --- a/scenes/adventure_map.tscn +++ b/scenes/adventure_map.tscn @@ -9,6 +9,8 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 [node name="ScrollContainer" type="ScrollContainer" parent="."] layout_mode = 1 @@ -22,6 +24,7 @@ vertical_scroll_mode = 0 [node name="AdventureStages" type="HBoxContainer" parent="ScrollContainer"] layout_mode = 2 +size_flags_vertical = 3 [node name="LinesContainer" type="Node2D" parent="ScrollContainer"] z_index = -1 diff --git a/scenes/combat.tscn b/scenes/combat.tscn index c2ceaf3..7a5f5a8 100644 --- a/scenes/combat.tscn +++ b/scenes/combat.tscn @@ -20,7 +20,7 @@ script = ExtResource("3_bdk6y") script = ExtResource("5_pl43j") [node name="Player" type="Node2D" parent="."] -position = Vector2(300, 300) +position = Vector2(300, 420) script = ExtResource("7_dwwk6") [node name="Visual" type="ColorRect" parent="Player"] @@ -34,11 +34,8 @@ offset_bottom = 23.0 theme_override_font_sizes/font_size = 70 text = "40/40" -[node name="EnemiesContainer" type="HBoxContainer" parent="."] -offset_right = 40.0 -offset_bottom = 40.0 -theme_override_constants/separation = 20 -alignment = 1 +[node name="EnemiesContainer" type="Node2D" parent="."] +position = Vector2(1200, 420) [node name="UI" type="CanvasLayer" parent="."] diff --git a/scenes/encounter_node.tscn b/scenes/encounter_node.tscn index 72bcb78..25cc227 100644 --- a/scenes/encounter_node.tscn +++ b/scenes/encounter_node.tscn @@ -3,7 +3,7 @@ [ext_resource type="Script" path="res://scripts/scenes/encounter_node.gd" id="1_6g2l7"] [ext_resource type="Script" path="res://scripts/utils/circle.gd" id="2_kg7rm"] -[node name="EncounterNode" type="Control"] +[node name="EncounterNode" type="Control" groups=["encounter_nodes"]] custom_minimum_size = Vector2(100, 100) layout_mode = 3 anchors_preset = 15 diff --git a/scripts/scenes/home_base.tscn b/scenes/home_base.tscn similarity index 95% rename from scripts/scenes/home_base.tscn rename to scenes/home_base.tscn index f62dded..8d622a1 100644 --- a/scripts/scenes/home_base.tscn +++ b/scenes/home_base.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=3 format=3 uid="uid://bnk32gyp3bsir"] -[ext_resource type="Script" path="res://scripts/scenes/home_base.gd" id="1_4udhq"] -[ext_resource type="Script" path="res://scripts/managers/pause_manager.gd" id="2_4gf2c"] +[ext_resource type="Script" path="res://scripts/scenes/home_base.gd" id="1_07xob"] +[ext_resource type="Script" path="res://scripts/managers/pause_manager.gd" id="2_5cg8f"] [node name="HomeBase" type="Control"] layout_mode = 3 @@ -10,7 +10,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -script = ExtResource("1_4udhq") +script = ExtResource("1_07xob") [node name="VBoxContainer" type="VBoxContainer" parent="."] layout_mode = 1 @@ -59,7 +59,7 @@ theme_override_font_sizes/font_size = 87 text = "Deck and inventory" [node name="PauseManager" type="Node" parent="."] -script = ExtResource("2_4gf2c") +script = ExtResource("2_5cg8f") [connection signal="pressed" from="VBoxContainer/GridContainer/AdventureButton" to="." method="_on_adventure_button_pressed"] [connection signal="pressed" from="VBoxContainer/GridContainer/DecoderButton" to="." method="_on_decoder_button_pressed"] diff --git a/scripts/data/encounter_node_data.gd b/scripts/data/encounter_node_data.gd index 7f7ca9e..90a2def 100644 --- a/scripts/data/encounter_node_data.gd +++ b/scripts/data/encounter_node_data.gd @@ -3,3 +3,4 @@ class_name EncounterNodeData @export var childNodes: Array[EncounterNodeData] = [] @export var completed: bool = false +@export var background: Resource diff --git a/scripts/enemy.gd b/scripts/enemy.gd index be13518..2c12d3f 100644 --- a/scripts/enemy.gd +++ b/scripts/enemy.gd @@ -16,7 +16,7 @@ func take_damage(amount: int) -> void: func take_turn() -> void: print("Enemy turn") - var player = get_node("/root/Combat/Player") + var player = get_node("/root/Main/Combat/Player") if player: # This will be replaced with actual ability usage pass diff --git a/scripts/managers/adventure_manager.gd b/scripts/managers/adventure_manager.gd index 9b83226..fb51681 100644 --- a/scripts/managers/adventure_manager.gd +++ b/scripts/managers/adventure_manager.gd @@ -12,8 +12,9 @@ const NODE_SCENE = preload("res://scenes/encounter_node.tscn") @export var DASH_LENGTH: float = 10.0 @export var GAP_LENGTH: float = 10.0 -func init_adventure(adventureResource: AdventureMapData) -> void: +func init_adventure(adventureResource: AdventureMapData): adventure = adventureResource + BackgroundSystem.setup_background(null) drawMap() func drawMap() -> void: diff --git a/scripts/managers/pause_manager.gd b/scripts/managers/pause_manager.gd index 22d49b3..0abced2 100644 --- a/scripts/managers/pause_manager.gd +++ b/scripts/managers/pause_manager.gd @@ -2,20 +2,22 @@ extends Node const PAUSE_MENU = preload("res://scenes/pause_menu.tscn") -var pause_menu: Control +var pause_menu: Control = null var is_paused: bool = false func _ready() -> void: - pause_menu = PAUSE_MENU.instantiate() - add_child(pause_menu) - # Register with UI scaling system - var ui_scale_manager = get_node("/root/Main/UIScaleManager") - ui_scale_manager.register_ui_root(pause_menu) + setup_pause_menu() process_mode = Node.PROCESS_MODE_ALWAYS - pause_menu = PAUSE_MENU.instantiate() - add_child(pause_menu) - pause_menu.resume_pressed.connect(_on_resume) - pause_menu.quit_pressed.connect(_on_quit) + +func setup_pause_menu() -> void: + if pause_menu == null: + pause_menu = PAUSE_MENU.instantiate() + add_child(pause_menu) + var ui_scale_manager = get_node("/root/Main/UIScaleManager") + ui_scale_manager.register_ui_root(pause_menu) + pause_menu.resume_pressed.connect(_on_resume) + pause_menu.quit_pressed.connect(_on_quit) + pause_menu.hide() func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("ui_cancel"): # ESC key @@ -34,6 +36,15 @@ func _on_resume() -> void: toggle_pause() func _on_quit() -> void: - # You might want to confirm before quitting + print("Pause Menu: Handling quit to main menu") + # Unpause the game + is_paused = false get_tree().paused = false - get_node("/root/Main").show_main_menu() + pause_menu.hide() + + # Let Main handle the scene transition + var main = get_node("/root/Main") + if main: + main.show_main_menu() + else: + print("ERROR: Couldn't find Main node!") diff --git a/scripts/pause_menu.gd b/scripts/pause_menu.gd index 152e2d0..9b2aa71 100644 --- a/scripts/pause_menu.gd +++ b/scripts/pause_menu.gd @@ -5,12 +5,17 @@ signal quit_pressed func _ready() -> void: hide() - $PanelContainer/MarginContainer/VBoxContainer/ResumeButton.pressed.connect(_on_resume_pressed) - $PanelContainer/MarginContainer/VBoxContainer/QuitButton.pressed.connect(_on_quit_pressed) + # Connect button signals + var resume_button = $PanelContainer/MarginContainer/VBoxContainer/ResumeButton + var quit_button = $PanelContainer/MarginContainer/VBoxContainer/QuitButton + + if not resume_button.pressed.is_connected(_on_resume_pressed): + resume_button.pressed.connect(_on_resume_pressed) + if not quit_button.pressed.is_connected(_on_quit_pressed): + quit_button.pressed.connect(_on_quit_pressed) func _on_resume_pressed() -> void: resume_pressed.emit() - hide() func _on_quit_pressed() -> void: quit_pressed.emit() diff --git a/scripts/scenes/card.gd b/scripts/scenes/card.gd index 93b58fc..1843280 100644 --- a/scripts/scenes/card.gd +++ b/scripts/scenes/card.gd @@ -28,7 +28,7 @@ func _ready() -> void: # Update card display update_display() -func update_display() -> void: +func update_display() -> void: if not is_node_ready(): await ready # Cost display @@ -80,28 +80,32 @@ func _gui_input(event: InputEvent) -> void: if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed: show_decode_menu() elif event.button_index == MOUSE_BUTTON_LEFT and event.pressed: - var combat_manager = get_node("/root/Combat/CombatManager") + var combat_scene = get_node("/root/Main/Combat") + var combat_manager = combat_scene.get_node("CombatManager") if combat_manager and combat_manager.can_play_card(card_data.energy_cost): play_card() func play_card() -> void: - var combat_manager = get_node("/root/Combat/CombatManager") + var combat_scene = get_node("/root/Main/Combat") + var combat_manager = combat_scene.get_node("CombatManager") combat_manager.spend_energy(card_data.energy_cost) execute_effect() card_played.emit(self) queue_free() func execute_effect() -> void: + var combat_scene = get_node("/root/Main/Combat") + match card_data.card_type: "attack": - var enemies_container = get_node("/root/Combat/EnemiesContainer") + var enemies_container = combat_scene.get_node("EnemiesContainer") if enemies_container and enemies_container.get_child_count() > 0: # For now, just target the first enemy # You might want to implement target selection later var enemy = enemies_container.get_child(0) enemy.take_damage(get_effect_value()) "block": - var player = get_node("/root/Combat/Player") + var player = combat_scene.get_node("Player") if player: player.gain_block(get_effect_value()) @@ -128,7 +132,8 @@ func show_decode_menu() -> void: popup.popup() func _on_decode_option_selected(id: int) -> void: - var decoder = get_node("/root/Combat/DecoderManager") + var combat_scene = get_node("/root/Main/Combat") + var decoder = combat_scene.get_node("DecoderManager") match id: 0: decoder.decode_aspect(self, "cost") 1: decoder.decode_aspect(self, "type") diff --git a/scripts/scenes/main.gd b/scripts/scenes/main.gd index c18e16c..2d4cc73 100644 --- a/scripts/scenes/main.gd +++ b/scripts/scenes/main.gd @@ -1,41 +1,63 @@ -# main.gd extends Node @onready var combat_scene = preload("res://scenes/combat.tscn") @onready var menu_scene = preload("res://scenes/menus/main_menu.tscn") -@onready var home_base_scene = preload("res://scripts/scenes/home_base.tscn") +@onready var home_base_scene = preload("res://scenes/home_base.tscn") var current_scene: Node = null func _ready(): var ui_scale_manager = Node.new() - ui_scale_manager.name = "UIScaleManager" # Named for easy reference + ui_scale_manager.name = "UIScaleManager" ui_scale_manager.set_script(load("res://scripts/managers/ui_scale_manager.gd")) add_child(ui_scale_manager) # Ensure config is applied before showing the menu await get_tree().create_timer(0.1).timeout show_main_menu() -func show_main_menu(): +func cleanup_current_scene() -> void: + print("Main: Cleaning up current scene") + AdventureSystem.cleanup() if current_scene: + remove_child(current_scene) current_scene.queue_free() - current_scene = menu_scene.instantiate() - add_child(current_scene) - # The menu will enable itself in its _ready() function + current_scene = null -func start_combat(): - if current_scene: - current_scene.queue_free() - current_scene = combat_scene.instantiate() - add_child(current_scene) - -func combat_won(): - print("Combat won") - start_home_base() +func transition_to_scene(new_scene: Node) -> void: + print("Transitioning to new scene: ", new_scene.name) + cleanup_current_scene() -func start_home_base(): - if current_scene: - current_scene.queue_free() - current_scene = home_base_scene.instantiate() + print("Adding new scene: ", new_scene.name) + current_scene = new_scene add_child(current_scene) + +func show_main_menu() -> void: + print("Showing main menu") + var new_menu = menu_scene.instantiate() + call_deferred("transition_to_scene", new_menu) + +func start_combat() -> void: + var new_combat = combat_scene.instantiate() + call_deferred("transition_to_scene", new_combat) + +func start_home_base() -> void: + var new_home = home_base_scene.instantiate() + call_deferred("transition_to_scene", new_home) + +func handle_new_game() -> void: + GameState.start_new_run() + var new_game_cutscene = load("res://resources/cutscenes/new_game/new_game_cutscene.tres") + cleanup_current_scene() + # Start the cutscene + CutsceneSystem.play_cutscene(new_game_cutscene, func(): + # After cutscene, start tutorial + var ingame_tutorial_adventure = load("res://resources/adventures/ingame_tutorial/adventure_map.tres") + AdventureSystem.start_adventure(ingame_tutorial_adventure) + # Connect to adventure completion if not already connected + if not AdventureSystem.adventure_completed.is_connected(_on_tutorial_completed): + AdventureSystem.adventure_completed.connect(_on_tutorial_completed) + ) + +func _on_tutorial_completed(_adventure: AdventureMapData) -> void: + start_home_base() diff --git a/scripts/scenes/main_menu.gd b/scripts/scenes/main_menu.gd index 7dc400b..f001c96 100644 --- a/scripts/scenes/main_menu.gd +++ b/scripts/scenes/main_menu.gd @@ -2,7 +2,6 @@ extends Control const OPTIONS_SCENE = preload("res://scenes/menus/options_menu.tscn") -const HOME_BASE_SCENE = preload("res://scripts/scenes/home_base.tscn") func _ready(): $CenterContainer/VBoxContainer/StartButton.pressed.connect(_on_start_pressed) @@ -11,23 +10,14 @@ func _ready(): $CenterContainer/VBoxContainer/QuitButton.pressed.connect(_on_quit_pressed) # Enable menu when it's first shown enable_menu() + BackgroundSystem.setup_for_scene(name) func _on_start_pressed(): print("start pressed") # Disable menu interaction before starting cutscene disable_menu() - GameState.start_new_run() - var new_game_cutscene = load("res://resources/cutscenes/new_game/new_game_cutscene.tres") - CutsceneSystem.play_cutscene(new_game_cutscene, start_ingame_tutorial) - -func start_ingame_tutorial() -> void: - var ingame_tutorial_adventure = load("res://resources/adventures/ingame_tutorial/adventure_map.tres") - AdventureSystem.start_adventure(ingame_tutorial_adventure) - AdventureSystem.adventure_completed.connect(_on_ingame_tutorial_completed) - -func _on_ingame_tutorial_completed(adventure: AdventureMapData): - get_node("/root/Main").start_home_base() - + # Tell the main scene to handle the new game sequence + get_node("/root/Main").handle_new_game() func _on_quit_pressed(): print("quit pressed") diff --git a/scripts/systems/adventure_system.gd b/scripts/systems/adventure_system.gd index c273ea7..455b1be 100644 --- a/scripts/systems/adventure_system.gd +++ b/scripts/systems/adventure_system.gd @@ -10,9 +10,30 @@ const ADVENTURE_MAP_SCENE = preload("res://scenes/adventure_map.tscn") var current_adventure: AdventureMapData var current_node: EncounterNodeData -var current_combat_scene: Node +var current_scene: Node +var map_instance: Node + +func cleanup() -> void: + print("Adventure System: Cleaning up...") + if is_instance_valid(current_scene): + print("Removing current scene") + if current_scene.get_parent(): + current_scene.get_parent().call_deferred("remove_child", current_scene) + current_scene.call_deferred("queue_free") + current_scene = null + + if is_instance_valid(map_instance): + print("Removing map instance") + if map_instance.get_parent(): + map_instance.get_parent().call_deferred("remove_child", map_instance) + map_instance.call_deferred("queue_free") + map_instance = null + + current_node = null func start_adventure(adventure_data: AdventureMapData) -> void: + print("Starting new adventure") + cleanup() # Clean up any existing adventure first current_adventure = adventure_data current_node = null @@ -24,12 +45,27 @@ func start_adventure(adventure_data: AdventureMapData) -> void: show_map() func show_map() -> void: - var map_instance = ADVENTURE_MAP_SCENE.instantiate() - get_tree().root.add_child(map_instance) - map_instance.get_node("AdventureManager").init_adventure(current_adventure) + if current_scene or map_instance: + cleanup() + await get_tree().process_frame + + print("Creating new adventure map") + map_instance = ADVENTURE_MAP_SCENE.instantiate() + var main = get_node("/root/Main") + main.call_deferred("add_child", map_instance) + + await get_tree().process_frame + var adventure_manager = map_instance.get_node("AdventureManager") + if adventure_manager: + print("Initializing adventure map") + adventure_manager.init_adventure(current_adventure) + var encounter_nodes = get_tree().get_nodes_in_group("encounter_nodes") + for node in encounter_nodes: + node.connect('node_clicked', select_encounter) + else: + push_error("AdventureManager not found in map instance") func _auto_start_first_encounter(start_node: StartEncounterNodeData) -> void: - # Get the first available child node if start_node.childNodes.size() > 0: var first_encounter = start_node.childNodes[0] select_encounter(first_encounter) @@ -38,10 +74,16 @@ func select_encounter(encounter_node: EncounterNodeData) -> void: if not can_select_encounter(encounter_node): return + print("Selecting encounter: ", encounter_node.get_class()) current_node = encounter_node encounter_selected.emit(encounter_node) - # Handle different encounter types + if is_instance_valid(map_instance): + if map_instance.get_parent(): + map_instance.get_parent().call_deferred("remove_child", map_instance) + map_instance.call_deferred("queue_free") + map_instance = null + if encounter_node is CombatEncounterNodeData: start_combat(encounter_node) elif encounter_node is EventEncounterNodeData: @@ -50,14 +92,11 @@ func select_encounter(encounter_node: EncounterNodeData) -> void: handle_finish_encounter(encounter_node) func can_select_encounter(node: EncounterNodeData) -> bool: - # Can't select already completed encounters if node.completed: return false - # Must have a completed parent (unless it's the start node) if not node is StartEncounterNodeData: var has_completed_parent = false - # Check all parent nodes directly for potential_parent in _get_all_parent_nodes(node): if potential_parent.completed: has_completed_parent = true @@ -69,74 +108,89 @@ func can_select_encounter(node: EncounterNodeData) -> bool: func _get_all_parent_nodes(node: EncounterNodeData) -> Array[EncounterNodeData]: var parents: Array[EncounterNodeData] = [] - - # Start from the root and traverse the tree _find_parents_recursive(current_adventure.rootEncounterNode, node, parents) - return parents func _find_parents_recursive(current: EncounterNodeData, target: EncounterNodeData, parents: Array[EncounterNodeData]) -> bool: - # Check if any of this node's children is our target for child in current.childNodes: if child == target: parents.append(current) return true - # Recursively check this child's children if _find_parents_recursive(child, target, parents): + parents.append(current) return true - return false func complete_current_encounter() -> void: - if current_node: - current_node.completed = true - encounter_completed.emit(current_node) + if not current_node: + return - if current_node is FinishEncounterNodeData: - adventure_completed.emit(current_adventure) - else: - # If we have a current combat/event scene, remove it - if current_combat_scene: - current_combat_scene.queue_free() - current_combat_scene = null - - await get_tree().process_frame - show_map() + print("Completing encounter: ", current_node.get_class()) + current_node.completed = true + encounter_completed.emit(current_node) + + if current_node is FinishEncounterNodeData: + adventure_completed.emit(current_adventure) + else: + call_deferred("_handle_encounter_completion") + +func _handle_encounter_completion() -> void: + if is_instance_valid(current_scene): + if current_scene.get_parent(): + current_scene.get_parent().call_deferred("remove_child", current_scene) + current_scene.call_deferred("queue_free") + current_scene = null + + await get_tree().process_frame + print("Showing map after encounter completion") + show_map() func start_combat(combat_node: CombatEncounterNodeData) -> void: - var combat_scene = COMBAT_SCENE.instantiate() + print("Starting combat encounter") + BackgroundSystem.setup_background(combat_node.background) + var main = get_node("/root/Main") + current_scene = COMBAT_SCENE.instantiate() - # Get the enemies container - var enemies_container = combat_scene.get_node("EnemiesContainer") + var enemies_container = current_scene.get_node("EnemiesContainer") - # Add each enemy from the combat node for enemy_data in combat_node.enemies: var enemy_instance = enemy_data.enemy_scene.instantiate() enemies_container.add_child(enemy_instance) - # Setup enemy with its data if needed if enemy_instance.has_method("setup"): enemy_instance.setup(enemy_data) - get_tree().root.add_child(combat_scene) - current_combat_scene = combat_scene + main.call_deferred("add_child", current_scene) func start_event(event_node: EventEncounterNodeData) -> void: - var event_scene = event_node.eventScene.instantiate() - # Setup event scene with any necessary data from event_node - get_tree().root.add_child(event_scene) - current_combat_scene = event_scene + print("Starting event encounter") + BackgroundSystem.setup_background(event_node.background) + if event_node.eventScene: + var main = get_node("/root/Main") + current_scene = event_node.eventScene.instantiate() + main.call_deferred("add_child", current_scene) + else: + print("Warning: Event node has no scene assigned") + complete_current_encounter() func handle_finish_encounter(finish_node: FinishEncounterNodeData) -> void: - var finish_scene = finish_node.finishScene.instantiate() - get_tree().root.add_child(finish_scene) - current_combat_scene = finish_scene + print("Starting finish encounter") + if finish_node.finishScene: + var main = get_node("/root/Main") + current_scene = finish_node.finishScene.instantiate() + main.call_deferred("add_child", current_scene) + else: + print("Completing adventure without finish scene") + complete_current_encounter() func return_to_map() -> void: - # Only return to map if we aren't finished if not (current_node is FinishEncounterNodeData): + if is_instance_valid(current_scene): + if current_scene.get_parent(): + current_scene.get_parent().call_deferred("remove_child", current_scene) + current_scene.call_deferred("queue_free") + current_scene = null show_map() -# Utility functions for other systems to use func get_current_adventure() -> AdventureMapData: return current_adventure diff --git a/scripts/systems/background_system.gd b/scripts/systems/background_system.gd index 82f13f6..87eda64 100644 --- a/scripts/systems/background_system.gd +++ b/scripts/systems/background_system.gd @@ -4,39 +4,53 @@ extends Node const MAIN_MENU_BG = preload("res://assets/backgrounds/main_menu_bg.webp") const COMBAT_BG = preload("res://assets/backgrounds/combat_bg.png") -func _ready(): - setup_background() +var current_background: Control -func setup_background(): - # Create a TextureRect node as the background - var background = TextureRect.new() - - # Get the parent scene name directly - var current_scene = get_parent().name - print("Current scene: ", current_scene) # Debug print - - # Set the texture based on the current scene - if current_scene == "MainMenu": - background.texture = MAIN_MENU_BG - elif current_scene == "Combat": - background.texture = COMBAT_BG +func setup_for_scene(scene_type: String) -> void: + print("Background System: Setting up background for ", scene_type) + + var background: Control + + match scene_type: + "MainMenu": + background = TextureRect.new() + background.texture = MAIN_MENU_BG + "Combat": + background = TextureRect.new() + background.texture = COMBAT_BG + _: + background = ColorRect.new() + background.color = Color.DARK_TURQUOISE + + if background is TextureRect: + background.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED + + background.set_anchors_preset(Control.PRESET_FULL_RECT) + background.z_index = -1 + background.mouse_filter = Control.MOUSE_FILTER_IGNORE + + add_child(background) + current_background = background + +func setup_background(backgroundResource: Resource) -> void: + print("Background System: Setting up background for encounter") + + var background: Control + + var encounterBackground = backgroundResource + if encounterBackground: + background = TextureRect.new() + background.texture = encounterBackground else: background = ColorRect.new() - background.color = Color.AQUAMARINE + background.color = Color.DARK_TURQUOISE if background is TextureRect: - # Set the stretch mode to cover the entire area while maintaining aspect ratio background.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED - # Set it to fill the entire viewport background.set_anchors_preset(Control.PRESET_FULL_RECT) - - # Make sure it stays behind other elements background.z_index = -1 - - # Ignore mouse input so controls underneath work background.mouse_filter = Control.MOUSE_FILTER_IGNORE - # Add the background as the first child add_child(background) - move_child(background, 0) + current_background = background From 65b49ad2be01d6b02fedc0bc434d5cfe5e535eff Mon Sep 17 00:00:00 2001 From: Thomas Metten Date: Wed, 6 Nov 2024 14:53:48 +0100 Subject: [PATCH 5/7] chore: remove repomix output --- repomix-output.txt | 3137 -------------------------------------------- 1 file changed, 3137 deletions(-) delete mode 100644 repomix-output.txt diff --git a/repomix-output.txt b/repomix-output.txt deleted file mode 100644 index 0f53870..0000000 --- a/repomix-output.txt +++ /dev/null @@ -1,3137 +0,0 @@ -This file is a merged representation of the entire codebase, combining all repository files into a single document. -Generated by Repomix on: 2024-11-06T12:35:33.109Z - -================================================================ -File Summary -================================================================ - -Purpose: --------- -This file contains a packed representation of the entire repository's contents. -It is designed to be easily consumable by AI systems for analysis, code review, -or other automated processes. - -File Format: ------------- -The content is organized as follows: -1. This summary section -2. Repository information -3. Repository structure -4. Multiple file entries, each consisting of: - a. A separator line (================) - b. The file path (File: path/to/file) - c. Another separator line - d. The full contents of the file - e. A blank line - -Usage Guidelines: ------------------ -- This file should be treated as read-only. Any changes should be made to the - original repository files, not this packed version. -- When processing this file, use the file path to distinguish - between different files in the repository. -- Be aware that this file may contain sensitive information. Handle it with - the same level of security as you would the original repository. - -Notes: ------- -- Some files may have been excluded based on .gitignore rules and Repomix's - configuration. -- Binary files are not included in this packed representation. Please refer to - the Repository Structure section for a complete list of file paths, including - binary files. - -Additional Info: ----------------- - -For more information about Repomix, visit: https://github.com/yamadashy/repomix - -================================================================ -Repository Structure -================================================================ -assets/ - audio/ - new_game_narrative.mp3.import - backgrounds/ - main_menu_bg.webp.import - ui/ - tilemap.tres -resources/ - adventures/ - ingame_tutorial/ - 0_combat.tres - 1_combat.tres - 2a_combat.tres - 2b_event.tres - 3_combat.tres - adventure_map.tres - finish.tres - start.tres - cards/ - attack_card.tres - block_card.tres - cutscenes/ - new_game/ - new_game_cutscene.tres - enemies/ - cultist.tres -scenes/ - enemies/ - cultist.tscn - menus/ - main_menu.tscn - options_menu.tscn - adventure_map.tscn - card.tscn - combat.tscn - cutscene.tscn - decode_ui.tscn - encounter_node.tscn - main.tscn - pause_menu.tscn -scripts/ - autoload/ - game_state.gd - data/ - adventure_map_data.gd - card_data.gd - combat_encounter_node_data.gd - cutscene_data.gd - cutscene_frame_data.gd - encounter_node_data.gd - enemy_data.gd - event_encounter_node_data.gd - finish_encounter_node_data.gd - start_encounter_node_data.gd - managers/ - adventure_manager.gd - combat_manager.gd - config_manager.gd - decoder_manager.gd - hand_manager.gd - pause_manager.gd - ui_scale_manager.gd - scenes/ - card.gd - combat.gd - cutscene_scene.gd - encounter_node.gd - home_base.gd - home_base.tscn - main_menu.gd - main.gd - options_menu.gd - systems/ - adventure_system.gd - background_system.gd - cutscene_system.gd - utils/ - circle.gd - decode_ui.gd - enemy.gd - pause_menu.gd - player.gd -.gitignore -ATTRIBUTION.md -LICENSE -project.godot -README.md - -================================================================ -Repository Files -================================================================ - -================ -File: assets/audio/new_game_narrative.mp3.import -================ -[remap] - -importer="mp3" -type="AudioStreamMP3" -uid="uid://b6miq63f23cyk" -path="res://.godot/imported/new_game_narrative.mp3-d3f83ef65f4ffbf7b4ee75adcce85e39.mp3str" - -[deps] - -source_file="res://assets/audio/new_game_narrative.mp3" -dest_files=["res://.godot/imported/new_game_narrative.mp3-d3f83ef65f4ffbf7b4ee75adcce85e39.mp3str"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 - -================ -File: assets/backgrounds/main_menu_bg.webp.import -================ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c4a4pijnc7ymf" -path="res://.godot/imported/main_menu_bg.webp-226249c542a90a329d6cdc5258220c3a.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://assets/backgrounds/main_menu_bg.webp" -dest_files=["res://.godot/imported/main_menu_bg.webp-226249c542a90a329d6cdc5258220c3a.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 - -================ -File: assets/ui/tilemap.tres -================ -[gd_resource type="TileMapPattern" format=3 uid="uid://bekwf75e1n4no"] - -[resource] - -================ -File: resources/adventures/ingame_tutorial/0_combat.tres -================ -[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://dsohl2qco44tx"] - -[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_w06wc"] -[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_7oxnr"] -[ext_resource type="Resource" uid="uid://b61wo0msir67x" path="res://resources/adventures/ingame_tutorial/1_combat.tres" id="2_lvvpg"] -[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_1bpls"] -[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="4_sukwl"] - -[resource] -script = ExtResource("3_1bpls") -enemies = Array[ExtResource("2_7oxnr")]([ExtResource("4_sukwl")]) -childNodes = Array[ExtResource("1_w06wc")]([ExtResource("2_lvvpg")]) -completed = false - -================ -File: resources/adventures/ingame_tutorial/1_combat.tres -================ -[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=7 format=3 uid="uid://b61wo0msir67x"] - -[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_q1hg8"] -[ext_resource type="Resource" uid="uid://hixs5ub5k4we" path="res://resources/adventures/ingame_tutorial/2a_combat.tres" id="2_km8iu"] -[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_t0t55"] -[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_s4ptf"] -[ext_resource type="Resource" uid="uid://cxl62hprsbcw1" path="res://resources/adventures/ingame_tutorial/2b_event.tres" id="3_yhy31"] -[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="5_ad11l"] - -[resource] -script = ExtResource("3_s4ptf") -enemies = Array[ExtResource("2_t0t55")]([ExtResource("5_ad11l"), ExtResource("5_ad11l")]) -childNodes = Array[ExtResource("1_q1hg8")]([ExtResource("2_km8iu"), ExtResource("3_yhy31")]) -completed = false - -================ -File: resources/adventures/ingame_tutorial/2a_combat.tres -================ -[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://hixs5ub5k4we"] - -[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_eejxk"] -[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_dr5jw"] -[ext_resource type="Resource" uid="uid://xpw7xp6klhla" path="res://resources/adventures/ingame_tutorial/3_combat.tres" id="2_fxa05"] -[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_qyrbs"] -[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="4_cwl26"] - -[resource] -script = ExtResource("3_qyrbs") -enemies = Array[ExtResource("2_dr5jw")]([ExtResource("4_cwl26"), ExtResource("4_cwl26"), ExtResource("4_cwl26")]) -childNodes = Array[ExtResource("1_eejxk")]([ExtResource("2_fxa05")]) -completed = false - -================ -File: resources/adventures/ingame_tutorial/2b_event.tres -================ -[gd_resource type="Resource" script_class="EventEncounterNodeData" load_steps=4 format=3 uid="uid://cxl62hprsbcw1"] - -[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_xxh5h"] -[ext_resource type="Script" path="res://scripts/data/event_encounter_node_data.gd" id="2_811ok"] -[ext_resource type="Resource" uid="uid://xpw7xp6klhla" path="res://resources/adventures/ingame_tutorial/3_combat.tres" id="2_n5lnf"] - -[resource] -script = ExtResource("2_811ok") -childNodes = Array[ExtResource("1_xxh5h")]([ExtResource("2_n5lnf")]) -completed = false - -================ -File: resources/adventures/ingame_tutorial/3_combat.tres -================ -[gd_resource type="Resource" script_class="CombatEncounterNodeData" load_steps=6 format=3 uid="uid://xpw7xp6klhla"] - -[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_o4v54"] -[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="2_y3pue"] -[ext_resource type="Resource" uid="uid://h7ww7yma5ddj" path="res://resources/adventures/ingame_tutorial/finish.tres" id="2_yb1tn"] -[ext_resource type="Script" path="res://scripts/data/combat_encounter_node_data.gd" id="3_kx66a"] -[ext_resource type="Resource" uid="uid://bjkxuf2s1608i" path="res://resources/enemies/cultist.tres" id="4_hhtf0"] - -[resource] -script = ExtResource("3_kx66a") -enemies = Array[ExtResource("2_y3pue")]([ExtResource("4_hhtf0"), ExtResource("4_hhtf0"), ExtResource("4_hhtf0"), ExtResource("4_hhtf0")]) -childNodes = Array[ExtResource("1_o4v54")]([ExtResource("2_yb1tn")]) -completed = false - -================ -File: resources/adventures/ingame_tutorial/adventure_map.tres -================ -[gd_resource type="Resource" script_class="AdventureMapData" load_steps=3 format=3 uid="uid://cq1h50d6agd3j"] - -[ext_resource type="Resource" uid="uid://cldmpsek0wxvd" path="res://resources/adventures/ingame_tutorial/start.tres" id="1_l4584"] -[ext_resource type="Script" path="res://scripts/data/adventure_map_data.gd" id="2_ic3nw"] - -[resource] -script = ExtResource("2_ic3nw") -rootEncounterNode = ExtResource("1_l4584") -auto_start = true - -================ -File: resources/adventures/ingame_tutorial/finish.tres -================ -[gd_resource type="Resource" script_class="FinishEncounterNodeData" load_steps=3 format=3 uid="uid://h7ww7yma5ddj"] - -[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_h5xn1"] -[ext_resource type="Script" path="res://scripts/data/finish_encounter_node_data.gd" id="2_isi6o"] - -[resource] -script = ExtResource("2_isi6o") -childNodes = Array[ExtResource("1_h5xn1")]([]) -completed = false - -================ -File: resources/adventures/ingame_tutorial/start.tres -================ -[gd_resource type="Resource" script_class="StartEncounterNodeData" load_steps=4 format=3 uid="uid://cldmpsek0wxvd"] - -[ext_resource type="Script" path="res://scripts/data/encounter_node_data.gd" id="1_72qi4"] -[ext_resource type="Script" path="res://scripts/data/start_encounter_node_data.gd" id="2_r3cwx"] -[ext_resource type="Resource" uid="uid://dsohl2qco44tx" path="res://resources/adventures/ingame_tutorial/0_combat.tres" id="2_y55f8"] - -[resource] -script = ExtResource("2_r3cwx") -childNodes = Array[ExtResource("1_72qi4")]([ExtResource("2_y55f8")]) -completed = true - -================ -File: resources/cards/attack_card.tres -================ -[gd_resource type="Resource" script_class="CardData" load_steps=2 format=3 uid="uid://i1h80xcboga2"] - -[ext_resource type="Script" path="res://scripts/data/card_data.gd" id="1_qhkig"] - -[resource] -script = ExtResource("1_qhkig") -name = "Strike" -energy_cost = 1 -card_type = "attack" -effect_value = 6 -effect_description = "Deal 6 damage" - -================ -File: resources/cards/block_card.tres -================ -[gd_resource type="Resource" script_class="CardData" load_steps=2 format=3 uid="uid://dwoxx76qsbfys"] - -[ext_resource type="Script" path="res://scripts/data/card_data.gd" id="1_vpyxy"] - -[resource] -script = ExtResource("1_vpyxy") -name = "Block" -energy_cost = 1 -card_type = "block" -effect_value = 5 -effect_description = "Gain 5 block" - -================ -File: resources/cutscenes/new_game/new_game_cutscene.tres -================ -[gd_resource type="Resource" script_class="CutsceneData" load_steps=10 format=3 uid="uid://cmss538sh2l7r"] - -[ext_resource type="Script" path="res://scripts/data/cutscene_data.gd" id="1_b1qtf"] -[ext_resource type="AudioStream" uid="uid://b6miq63f23cyk" path="res://assets/audio/new_game_narrative.mp3" id="1_mx5nb"] -[ext_resource type="Script" path="res://scripts/data/cutscene_frame_data.gd" id="2_ireuw"] -[ext_resource type="Texture2D" uid="uid://wv57ymcqwumg" path="res://assets/cutscene_images/new_game/new_game_0_stone.png" id="3_sptdj"] -[ext_resource type="Texture2D" uid="uid://x7r1vbh43dhe" path="res://assets/cutscene_images/new_game/new_game_1_marketplace.png" id="4_x3eri"] -[ext_resource type="Texture2D" uid="uid://qoukk6jbsbu0" path="res://assets/cutscene_images/new_game/new_game_2_rocky_outcrop.png" id="5_7v7wn"] - -[sub_resource type="Resource" id="Resource_dxda3"] -script = ExtResource("2_ireuw") -timestamp = 0.0 -image = ExtResource("3_sptdj") - -[sub_resource type="Resource" id="Resource_gcroa"] -script = ExtResource("2_ireuw") -timestamp = 6.0 -image = ExtResource("4_x3eri") - -[sub_resource type="Resource" id="Resource_5aa0k"] -script = ExtResource("2_ireuw") -timestamp = 22.0 -image = ExtResource("5_7v7wn") - -[resource] -resource_name = "New Game" -script = ExtResource("1_b1qtf") -audio_track = ExtResource("1_mx5nb") -frames = Array[ExtResource("2_ireuw")]([SubResource("Resource_dxda3"), SubResource("Resource_gcroa"), SubResource("Resource_5aa0k")]) - -================ -File: resources/enemies/cultist.tres -================ -[gd_resource type="Resource" script_class="EnemyData" load_steps=3 format=3 uid="uid://bjkxuf2s1608i"] - -[ext_resource type="Script" path="res://scripts/data/enemy_data.gd" id="1_g2v6o"] -[ext_resource type="PackedScene" uid="uid://cbtac01eivwim" path="res://scenes/enemies/cultist.tscn" id="1_uchoq"] - -[resource] -script = ExtResource("1_g2v6o") -enemy_scene = ExtResource("1_uchoq") -health = 10 -abilities = [] - -================ -File: scenes/enemies/cultist.tscn -================ -[gd_scene load_steps=2 format=3 uid="uid://cbtac01eivwim"] - -[ext_resource type="Script" path="res://scripts/enemy.gd" id="1_c11yq"] - -[node name="Cultist" type="Node2D"] -script = ExtResource("1_c11yq") - -[node name="ColorRect" type="ColorRect" parent="."] -custom_minimum_size = Vector2(100, 200) -offset_right = 40.0 -offset_bottom = 40.0 -color = Color(0.394879, 0.000106898, 0.394876, 1) - -[node name="HealthLabel" type="Label" parent="."] -offset_right = 40.0 -offset_bottom = 23.0 - -================ -File: scenes/menus/main_menu.tscn -================ -[gd_scene load_steps=2 format=3 uid="uid://bxru22786wp6n"] - -[ext_resource type="Script" path="res://scripts/scenes/main_menu.gd" id="1_prvtn"] - -[node name="MainMenu" type="CenterContainer"] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_prvtn") - -[node name="CenterContainer" type="CenterContainer" parent="."] -layout_mode = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"] -layout_mode = 2 -theme_override_constants/separation = 20 - -[node name="Title" type="Label" parent="CenterContainer/VBoxContainer"] -layout_mode = 2 -theme_override_colors/font_shadow_color = Color(0, 0, 0, 0.5) -theme_override_constants/shadow_offset_x = 2 -theme_override_constants/shadow_offset_y = 2 -theme_override_constants/outline_size = 2 -theme_override_font_sizes/font_size = 48 -text = "Card Game" -horizontal_alignment = 1 - -[node name="ContinueButton" type="Button" parent="CenterContainer/VBoxContainer"] -custom_minimum_size = Vector2(400, 100) -layout_mode = 2 -theme_override_font_sizes/font_size = 32 -disabled = true -text = "Continue Game" - -[node name="StartButton" type="Button" parent="CenterContainer/VBoxContainer"] -custom_minimum_size = Vector2(400, 100) -layout_mode = 2 -theme_override_font_sizes/font_size = 32 -text = "New Game" - -[node name="Options" type="Button" parent="CenterContainer/VBoxContainer"] -custom_minimum_size = Vector2(400, 100) -layout_mode = 2 -theme_override_font_sizes/font_size = 32 -text = "Options" - -[node name="QuitButton" type="Button" parent="CenterContainer/VBoxContainer"] -custom_minimum_size = Vector2(400, 100) -layout_mode = 2 -theme_override_font_sizes/font_size = 32 -text = "Quit" - -================ -File: scenes/menus/options_menu.tscn -================ -[gd_scene load_steps=2 format=3 uid="uid://umnex4p7msdu"] - -[ext_resource type="Script" path="res://scripts/scenes/options_menu.gd" id="1_script"] - -[node name="OptionsMenu" type="CenterContainer"] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -mouse_filter = 2 -script = ExtResource("1_script") - -[node name="ColorRect" type="ColorRect" parent="."] -layout_mode = 2 -color = Color(0, 0, 0, 0.5) - -[node name="CenterContainer" type="CenterContainer" parent="."] -layout_mode = 2 - -[node name="PanelContainer" type="PanelContainer" parent="CenterContainer"] -custom_minimum_size = Vector2(800, 600) -layout_mode = 2 - -[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/PanelContainer"] -layout_mode = 2 -theme_override_constants/margin_left = 20 -theme_override_constants/margin_top = 20 -theme_override_constants/margin_right = 20 -theme_override_constants/margin_bottom = 20 - -[node name="TabContainer" type="TabContainer" parent="CenterContainer/PanelContainer/MarginContainer"] -layout_mode = 2 -current_tab = 0 - -[node name="Video" type="VBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer"] -layout_mode = 2 -theme_override_constants/separation = 20 -metadata/_tab_index = 0 - -[node name="FullscreenLabel" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Video"] -layout_mode = 2 -theme_override_font_sizes/font_size = 24 -text = "Fullscreen" - -[node name="FullscreenToggle" type="CheckButton" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Video"] -unique_name_in_owner = true -layout_mode = 2 -theme_override_font_sizes/font_size = 24 - -[node name="ResolutionLabel" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Video"] -layout_mode = 2 -theme_override_font_sizes/font_size = 24 -text = "Resolution (Windowed Mode)" - -[node name="ResolutionOptionButton" type="OptionButton" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Video"] -unique_name_in_owner = true -layout_mode = 2 -theme_override_font_sizes/font_size = 24 - -[node name="Audio" type="VBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer"] -visible = false -layout_mode = 2 -theme_override_constants/separation = 20 -metadata/_tab_index = 1 - -[node name="MuteLabel" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio"] -layout_mode = 2 -theme_override_font_sizes/font_size = 24 -text = "Mute Audio" - -[node name="MuteToggle" type="CheckButton" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio"] -unique_name_in_owner = true -layout_mode = 2 -theme_override_font_sizes/font_size = 24 - -[node name="VolumeContainer" type="HBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio"] -layout_mode = 2 - -[node name="VolumeLabel" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio/VolumeContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_font_sizes/font_size = 24 -text = "Master Volume" - -[node name="VolumeLabelValue" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio/VolumeContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_override_font_sizes/font_size = 24 -text = "100%" - -[node name="VolumeSlider" type="HSlider" parent="CenterContainer/PanelContainer/MarginContainer/TabContainer/Audio"] -unique_name_in_owner = true -layout_mode = 2 - -[node name="BackButton" type="Button" parent="CenterContainer/PanelContainer/MarginContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 8 -theme_override_font_sizes/font_size = 24 -text = "Back" - -================ -File: scenes/adventure_map.tscn -================ -[gd_scene load_steps=2 format=3 uid="uid://b7vx0syoj8olw"] - -[ext_resource type="Script" path="res://scripts/managers/adventure_manager.gd" id="1_ryxis"] - -[node name="AdventureMap" type="Control"] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 - -[node name="ScrollContainer" type="ScrollContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -horizontal_scroll_mode = 2 -vertical_scroll_mode = 0 - -[node name="AdventureStages" type="HBoxContainer" parent="ScrollContainer"] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="LinesContainer" type="Node2D" parent="ScrollContainer"] -z_index = -1 - -[node name="AdventureManager" type="Node" parent="."] -script = ExtResource("1_ryxis") - -================ -File: scenes/card.tscn -================ -[gd_scene load_steps=3 format=3 uid="uid://cpp37o7600nho"] - -[ext_resource type="Script" path="res://scripts/scenes/card.gd" id="1_p7luq"] -[ext_resource type="Texture2D" uid="uid://df58t5bfmj70a" path="res://assets/ui/scroll_base.png" id="2_mugk4"] - -[node name="Card" type="Control"] -custom_minimum_size = Vector2(200, 300) -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_right = -1720.0 -offset_bottom = -780.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_p7luq") - -[node name="Sprite2D" type="Sprite2D" parent="."] -position = Vector2(1, 1) -scale = Vector2(0.995, 0.996805) -texture = ExtResource("2_mugk4") -centered = false - -[node name="MarginContainer" type="MarginContainer" parent="."] -layout_mode = 0 -offset_left = 34.0 -offset_top = 67.0 -offset_right = 238.0 -offset_bottom = 314.0 -scale = Vector2(0.7, 0.78) -theme_override_constants/margin_left = 10 -theme_override_constants/margin_top = 10 -theme_override_constants/margin_right = 10 -theme_override_constants/margin_bottom = 10 - -[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] -layout_mode = 2 - -[node name="TopBar" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] -layout_mode = 2 - -[node name="CostLabel" type="Label" parent="MarginContainer/VBoxContainer/TopBar"] -layout_mode = 2 -size_flags_horizontal = 0 -text = "666" - -[node name="NameLabel" type="Label" parent="MarginContainer/VBoxContainer/TopBar"] -layout_mode = 2 -size_flags_horizontal = 3 -text = "Unleash all demons" - -[node name="EffectLabel" type="Label" parent="MarginContainer/VBoxContainer"] -custom_minimum_size = Vector2(180, 200) -layout_mode = 2 -size_flags_vertical = 3 -text = "Unleash all demons, with the power of THE DEBUG!" -horizontal_alignment = 1 -vertical_alignment = 2 -autowrap_mode = 2 - -================ -File: scenes/combat.tscn -================ -[gd_scene load_steps=7 format=3 uid="uid://b3l57gnlln63g"] - -[ext_resource type="Script" path="res://scripts/scenes/combat.gd" id="1_n5yyl"] -[ext_resource type="Script" path="res://scripts/managers/combat_manager.gd" id="2_305ab"] -[ext_resource type="Script" path="res://scripts/managers/hand_manager.gd" id="3_bdk6y"] -[ext_resource type="Script" path="res://scripts/managers/pause_manager.gd" id="5_pl43j"] -[ext_resource type="Script" path="res://scripts/player.gd" id="7_dwwk6"] -[ext_resource type="Texture2D" uid="uid://dyx76tn885md3" path="res://assets/ui/energy_orb.png" id="9_e0ys4"] - -[node name="Combat" type="Node2D"] -script = ExtResource("1_n5yyl") - -[node name="CombatManager" type="Node" parent="."] -script = ExtResource("2_305ab") - -[node name="HandManager" type="Node" parent="."] -script = ExtResource("3_bdk6y") - -[node name="PauseManager" type="Node" parent="."] -script = ExtResource("5_pl43j") - -[node name="Player" type="Node2D" parent="."] -position = Vector2(300, 300) -script = ExtResource("7_dwwk6") - -[node name="Visual" type="ColorRect" parent="Player"] -offset_right = 100.0 -offset_bottom = 100.0 -color = Color(0, 1, 0, 1) - -[node name="HealthLabel" type="Label" parent="Player"] -offset_right = 40.0 -offset_bottom = 23.0 -theme_override_font_sizes/font_size = 70 -text = "40/40" - -[node name="EnemiesContainer" type="HBoxContainer" parent="."] -offset_right = 40.0 -offset_bottom = 40.0 -theme_override_constants/separation = 20 -alignment = 1 - -[node name="UI" type="CanvasLayer" parent="."] - -[node name="HUD" type="Control" parent="UI"] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 1 - -[node name="HandContainer" type="HBoxContainer" parent="UI/HUD"] -custom_minimum_size = Vector2(0, 300) -layout_mode = 1 -anchors_preset = 7 -anchor_left = 0.5 -anchor_top = 1.0 -anchor_right = 0.5 -anchor_bottom = 1.0 -offset_left = -576.0 -offset_top = -300.0 -offset_right = 576.0 -grow_horizontal = 2 -grow_vertical = 0 -alignment = 1 - -[node name="EndTurnMarginContainer" type="MarginContainer" parent="UI/HUD"] -layout_mode = 1 -anchors_preset = 3 -anchor_left = 1.0 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -300.0 -offset_top = -150.0 -offset_right = -50.0 -offset_bottom = -50.0 -grow_horizontal = 0 -grow_vertical = 0 - -[node name="EndTurnButton" type="Button" parent="UI/HUD/EndTurnMarginContainer"] -layout_mode = 2 -theme_override_font_sizes/font_size = 70 -text = "End turn" - -[node name="EnergyMarginContainer" type="MarginContainer" parent="UI/HUD"] -custom_minimum_size = Vector2(200, 200) -layout_mode = 1 -anchors_preset = 1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -250.0 -offset_top = 50.0 -offset_right = -50.0 -offset_bottom = 250.0 -grow_horizontal = 0 - -[node name="EnergyTexture" type="TextureRect" parent="UI/HUD/EnergyMarginContainer"] -custom_minimum_size = Vector2(200, 200) -layout_mode = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 -texture = ExtResource("9_e0ys4") -expand_mode = 1 - -[node name="EnergyLabel" type="Label" parent="UI/HUD/EnergyMarginContainer/EnergyTexture"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_font_sizes/font_size = 140 -text = "3" -horizontal_alignment = 1 -vertical_alignment = 1 - -================ -File: scenes/cutscene.tscn -================ -[gd_scene load_steps=2 format=3 uid="uid://1bxh8on3pygv"] - -[ext_resource type="Script" path="res://scripts/scenes/cutscene_scene.gd" id="1_6o0s5"] - -[node name="Cutscene" type="Control"] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_6o0s5") - -[node name="TextureRect" type="TextureRect" parent="."] -z_index = 1000 -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="AudioStreamPlayer" type="AudioStreamPlayer2D" parent="."] - -================ -File: scenes/decode_ui.tscn -================ -[gd_scene load_steps=2 format=3 uid="uid://duyk27ghemr8s"] - -[ext_resource type="Script" path="res://scripts/decode_ui.gd" id="1_d4e5u"] - -[node name="DecodeUI" type="PanelContainer"] -process_mode = 3 -visible = false -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -100.0 -offset_top = -75.0 -offset_right = 100.0 -offset_bottom = 75.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_d4e5u") - -[node name="MarginContainer" type="MarginContainer" parent="."] -layout_mode = 2 -theme_override_constants/margin_left = 10 -theme_override_constants/margin_top = 10 -theme_override_constants/margin_right = 10 -theme_override_constants/margin_bottom = 10 - -[node name="VBox" type="VBoxContainer" parent="MarginContainer"] -layout_mode = 2 -theme_override_constants/separation = 10 - -[node name="DecodePointsLabel" type="Label" parent="MarginContainer/VBox"] -layout_mode = 2 -text = "Decode Points: 0" -horizontal_alignment = 1 - -[node name="OptionsContainer" type="VBoxContainer" parent="MarginContainer/VBox"] -layout_mode = 2 -theme_override_constants/separation = 5 - -[node name="CloseButton" type="Button" parent="MarginContainer/VBox"] -layout_mode = 2 -text = "Close" - -[connection signal="pressed" from="MarginContainer/VBox/CloseButton" to="." method="hide"] - -================ -File: scenes/encounter_node.tscn -================ -[gd_scene load_steps=3 format=3 uid="uid://cidwq7l0x54g7"] - -[ext_resource type="Script" path="res://scripts/scenes/encounter_node.gd" id="1_6g2l7"] -[ext_resource type="Script" path="res://scripts/utils/circle.gd" id="2_kg7rm"] - -[node name="EncounterNode" type="Control"] -custom_minimum_size = Vector2(100, 100) -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_6g2l7") - -[node name="CenterContainer" type="CenterContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"] -layout_mode = 2 -theme_override_constants/separation = 8 -alignment = 1 - -[node name="Circle" type="ColorRect" parent="CenterContainer/VBoxContainer"] -custom_minimum_size = Vector2(40, 40) -layout_mode = 2 -script = ExtResource("2_kg7rm") - -[node name="Label" type="Label" parent="CenterContainer/VBoxContainer"] -layout_mode = 2 -text = "Encounter" -horizontal_alignment = 1 -vertical_alignment = 1 - -================ -File: scenes/main.tscn -================ -[gd_scene load_steps=2 format=3 uid="uid://ddx65fdpgkycb"] - -[ext_resource type="Script" path="res://scripts/scenes/main.gd" id="1_fc67n"] - -[node name="Main" type="Node"] -script = ExtResource("1_fc67n") - -================ -File: scenes/pause_menu.tscn -================ -[gd_scene load_steps=2 format=3] - -[ext_resource type="Script" path="res://scripts/pause_menu.gd" id="1_p7luq"] - -[node name="PauseMenu" type="Control"] -process_mode = 3 -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_p7luq") - -[node name="ColorRect" type="ColorRect" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -color = Color(0, 0, 0, 0.5) -mouse_filter = 1 - -[node name="PanelContainer" type="PanelContainer" parent="."] -layout_mode = 1 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -100.0 -offset_top = -68.0 -offset_right = 100.0 -offset_bottom = 68.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 1 - -[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"] -layout_mode = 2 -theme_override_constants/margin_left = 16 -theme_override_constants/margin_top = 16 -theme_override_constants/margin_right = 16 -theme_override_constants/margin_bottom = 16 -mouse_filter = 1 - -[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"] -layout_mode = 2 -theme_override_constants/separation = 16 -mouse_filter = 1 - -[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 -theme_override_font_sizes/font_size = 32 -text = "PAUSED" -horizontal_alignment = 1 - -[node name="ResumeButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"] -custom_minimum_size = Vector2(200, 50) -layout_mode = 2 -size_flags_horizontal = 4 -mouse_default_cursor_shape = 2 -text = "Resume" - -[node name="QuitButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"] -custom_minimum_size = Vector2(200, 50) -layout_mode = 2 -size_flags_horizontal = 4 -mouse_default_cursor_shape = 2 -text = "Quit to Menu" - -================ -File: scripts/autoload/game_state.gd -================ -# game_state.gd -extends Node - -# Persistent game progress -var total_runs: int = 0 -var current_run: int = 0 - -# Card knowledge system -var decoded_aspects: Dictionary = { - # Structure: - # "res://resources/cards/attack_card.tres": { - # "cost": true, - # "type": true, - # "value": false, - # "description": false - # } -} - -# Current run state -var current_deck: Array = [] -var current_gold: int = 100 -var current_hp: int = 40 -var current_max_hp: int = 40 -var decode_points: int = 0 - -# Save data structure -const SAVE_PATH = "user://gamestate.save" - -func _ready() -> void: - load_game() - -func save_game() -> void: - var save_data = { - "meta_progress": { - "total_runs": total_runs, - "decoded_aspects": decoded_aspects, - }, - "current_run": { - "run_number": current_run, - "current_deck": current_deck, - "current_gold": current_gold, - "current_hp": current_hp, - "current_max_hp": current_max_hp, - "decode_points": decode_points - } - } - - var save_file = FileAccess.open(SAVE_PATH, FileAccess.WRITE) - var json_string = JSON.stringify(save_data) - save_file.store_string(json_string) - -func load_game() -> void: - if not FileAccess.file_exists(SAVE_PATH): - return - - var save_file = FileAccess.open(SAVE_PATH, FileAccess.READ) - var json_string = save_file.get_as_text() - - var json = JSON.new() - var parse_result = json.parse(json_string) - if parse_result == OK: - var save_data = json.get_data() - - # Load meta progress - if "meta_progress" in save_data: - total_runs = save_data["meta_progress"].get("total_runs", 0) - decoded_aspects = save_data["meta_progress"].get("decoded_aspects", {}) - - # Load current run - if "current_run" in save_data: - current_run = save_data["current_run"].get("run_number", 0) - current_deck = save_data["current_run"].get("current_deck", []) - current_gold = save_data["current_run"].get("current_gold", 100) - current_hp = save_data["current_run"].get("current_hp", 40) - current_max_hp = save_data["current_run"].get("current_max_hp", 40) - decode_points = save_data["current_run"].get("decode_points", 0) - -# Run management -func start_new_run() -> void: - current_run = total_runs + 1 - current_gold = 100 - current_hp = 40 - current_max_hp = 40 - decode_points = 0 - current_deck.clear() - setup_starting_deck() - save_game() - -func end_run(victory: bool) -> void: - if victory: - total_runs += 1 - save_game() - -# Card knowledge management -func is_aspect_decoded(card_path: String, aspect: String) -> bool: - if not decoded_aspects.has(card_path): - return false - return decoded_aspects[card_path].get(aspect, false) - -func decode_aspect(card_path: String, aspect: String) -> void: - if not decoded_aspects.has(card_path): - decoded_aspects[card_path] = {} - decoded_aspects[card_path][aspect] = true - save_game() - -func is_card_fully_decoded(card_path: String) -> bool: - if not decoded_aspects.has(card_path): - return false - var aspects = decoded_aspects[card_path] - return aspects.get("cost", false) and \ - aspects.get("type", false) and \ - aspects.get("value", false) and \ - aspects.get("description", false) - -# Deck management -func setup_starting_deck() -> void: - # Load starting cards - for i in range(5): - current_deck.append("res://resources/cards/attack_card.tres") - current_deck.append("res://resources/cards/block_card.tres") - -# Resource management -func add_decode_points(amount: int) -> void: - decode_points += amount - save_game() - -func spend_decode_points(amount: int) -> bool: - if decode_points >= amount: - decode_points -= amount - save_game() - return true - return false - -func add_gold(amount: int) -> void: - current_gold += amount - save_game() - -func spend_gold(amount: int) -> bool: - if current_gold >= amount: - current_gold -= amount - save_game() - return true - return false - -# Health management -func heal(amount: int) -> void: - current_hp = min(current_hp + amount, current_max_hp) - save_game() - -func take_damage(amount: int) -> void: - current_hp = max(current_hp - amount, 0) - save_game() - -func increase_max_hp(amount: int) -> void: - current_max_hp += amount - current_hp += amount - save_game() - -================ -File: scripts/data/adventure_map_data.gd -================ -extends Resource -class_name AdventureMapData - -@export var rootEncounterNode : StartEncounterNodeData -@export var auto_start: bool = false - -================ -File: scripts/data/card_data.gd -================ -extends Resource -class_name CardData - -@export var name: String -@export var energy_cost: int -@export var card_type: String # "attack" or "block" -@export var effect_value: int -@export var effect_description: String - -================ -File: scripts/data/combat_encounter_node_data.gd -================ -extends EncounterNodeData -class_name CombatEncounterNodeData - -@export var enemies: Array[EnemyData] - -================ -File: scripts/data/cutscene_data.gd -================ -class_name CutsceneData -extends Resource - -@export var audio_track: AudioStream -@export var frames: Array[CutsceneFrameData] - -func _init(): - frames = [] - -================ -File: scripts/data/cutscene_frame_data.gd -================ -class_name CutsceneFrameData -extends Resource - -@export var timestamp: float -@export var image: Texture2D - -================ -File: scripts/data/encounter_node_data.gd -================ -extends Resource -class_name EncounterNodeData - -@export var childNodes: Array[EncounterNodeData] = [] -@export var completed: bool = false - -================ -File: scripts/data/enemy_data.gd -================ -extends Resource -class_name EnemyData - -@export var enemy_scene: PackedScene -@export var health: int -@export var abilities: Array = [] - -================ -File: scripts/data/event_encounter_node_data.gd -================ -extends EncounterNodeData -class_name EventEncounterNodeData - -@export var eventScene: PackedScene - -================ -File: scripts/data/finish_encounter_node_data.gd -================ -extends EncounterNodeData -class_name FinishEncounterNodeData - -@export var finishScene: PackedScene - -================ -File: scripts/data/start_encounter_node_data.gd -================ -extends EncounterNodeData -class_name StartEncounterNodeData - -func _init() -> void: - completed = true - -================ -File: scripts/managers/adventure_manager.gd -================ -extends Node - -@export var adventure: AdventureMapData -@onready var adventureStagesContainer: HBoxContainer = $"../ScrollContainer/AdventureStages" -@onready var lines_container: Node2D = $"../ScrollContainer/LinesContainer" - -const NODE_SCENE = preload("res://scenes/encounter_node.tscn") -@export var COLUMN_SPACING = 200 -@export var NODE_SPACING = 120 -@export var LINE_COLOR = Color("6b7280") -@export var LINE_WIDTH = 3.0 -@export var DASH_LENGTH: float = 10.0 -@export var GAP_LENGTH: float = 10.0 - -func init_adventure(adventureResource: AdventureMapData) -> void: - adventure = adventureResource - drawMap() - -func drawMap() -> void: - # Clear existing nodes and lines - for child in adventureStagesContainer.get_children(): - child.queue_free() - for child in lines_container.get_children(): - child.queue_free() - - if not adventure or not adventure.rootEncounterNode: - return - - # Create stages (columns) based on depth - var stages = [] - _gather_stages(adventure.rootEncounterNode, 0, stages) - - # Create containers for each stage - for stage_nodes in stages: - var stage_container = VBoxContainer.new() - stage_container.custom_minimum_size.x = COLUMN_SPACING - stage_container.alignment = BoxContainer.ALIGNMENT_CENTER - stage_container.add_theme_constant_override("separation", NODE_SPACING) - adventureStagesContainer.add_child(stage_container) - - # Add nodes to the stage - for node_data in stage_nodes: - var encounter_node = _create_encounter_node(node_data) - stage_container.add_child(encounter_node) - - # Draw all connections after nodes are positioned - await get_tree().process_frame - _draw_all_connections() - -func _gather_stages(node: EncounterNodeData, depth: int, stages: Array) -> void: - # Ensure we have an array for this depth - while stages.size() <= depth: - stages.append([]) - - # Add node to its stage - stages[depth].append(node) - - # Process child nodes - for child in node.childNodes: - _gather_stages(child, depth + 1, stages) - -func _create_encounter_node(node_data: EncounterNodeData) -> Control: - var node_instance = NODE_SCENE.instantiate() - node_instance.setup(node_data) - - # Node type specific setup - if node_data is StartEncounterNodeData: - node_instance.set_start_node() - elif node_data is FinishEncounterNodeData: - node_instance.set_finish_node() - - if node_data.completed: - node_instance.set_completed() - - return node_instance - -func _draw_all_connections() -> void: - var stages = adventureStagesContainer.get_children() - - for stage_idx in range(stages.size() - 1): # Stop before last stage - var current_stage = stages[stage_idx] - var next_stage = stages[stage_idx + 1] - - for from_node in current_stage.get_children(): - var from_data = from_node.get_node_data() - - for child_data in from_data.childNodes: - for to_node in next_stage.get_children(): - if to_node.get_node_data() == child_data: - _draw_connection(from_node, to_node) - -func _draw_connection(from_node: Control, to_node: Control) -> void: - var line = Line2D.new() - line.default_color = LINE_COLOR - line.width = LINE_WIDTH - line.begin_cap_mode = Line2D.LINE_CAP_ROUND - line.end_cap_mode = Line2D.LINE_CAP_ROUND - - # Get the circles' centers in local coordinates relative to their common parent - var from_circle = from_node.get_node("CenterContainer/VBoxContainer/Circle") - var to_circle = to_node.get_node("CenterContainer/VBoxContainer/Circle") - - var from_center = from_circle.get_global_rect().get_center() - var to_center = to_circle.get_global_rect().get_center() - - # Convert points to lines_container's local coordinates - from_center = lines_container.to_local(from_center) - to_center = lines_container.to_local(to_center) - - # Calculate control points for curve - var control_offset = Vector2((to_center.x - from_center.x) * 0.5, 0) - - # Create dotted line points - var points = _create_dotted_curve_points(from_center, to_center, control_offset) - - line.points = points - lines_container.add_child(line) - -func _create_dotted_curve_points(from: Vector2, to: Vector2, control_offset: Vector2) -> PackedVector2Array: - var points := PackedVector2Array() - var curve = Curve2D.new() - - # Add points to create a bezier curve - curve.add_point(from, Vector2.ZERO, control_offset) - curve.add_point(to, -control_offset, Vector2.ZERO) - - # Settings for dash pattern - var dash_length := DASH_LENGTH - var gap_length := GAP_LENGTH - var curve_length = curve.get_baked_length() - - # Always start with the first point - points.append(from) - - # Calculate how many complete dash-gap pairs we need - var segment_length = dash_length + gap_length - var num_complete_segments = floor((curve_length - dash_length) / segment_length) - - # Create the middle dots - for i in range(num_complete_segments): - var pos = (i + 1) * segment_length - points.append(curve.sample_baked(pos)) - - # Always end with the last point - points.append(to) - - return points - -================ -File: scripts/managers/combat_manager.gd -================ -extends Node - -var current_energy: int = 3 -var max_energy: int = 3 -var is_player_turn: bool = true - -@onready var hand_manager: Node = $"../HandManager" -@onready var enemies_container: Node = $"../EnemiesContainer" -@onready var energy_label: Label = $"../UI/HUD/EnergyMarginContainer/EnergyTexture/EnergyLabel" - -func _ready() -> void: - await hand_manager.ready - start_turn() - -func start_turn() -> void: - current_energy = max_energy - energy_label.text = str(current_energy) - hand_manager.draw_cards(5) - is_player_turn = true - print("Player turn") - -func end_turn() -> void: - hand_manager.discard_hand() - is_player_turn = false - - # Enemy turns - for enemy in enemies_container.get_children(): - if enemy.has_method("take_turn"): - enemy.take_turn() - - # Back to player - start_turn() - -func can_play_card(card_cost: int) -> bool: - return is_player_turn and current_energy >= card_cost - -func spend_energy(amount: int) -> void: - current_energy -= amount - energy_label.text = str(current_energy) - -================ -File: scripts/managers/config_manager.gd -================ -extends Node - -const CONFIG_PATH = "user://config.cfg" - -signal resolution_changed(new_resolution: Vector2) -signal window_mode_changed(is_fullscreen: bool) - -var config = { - "video": { - "fullscreen": false, - "resolution_index": 3 # Default to 1920x1080 - }, - "audio": { - "master_volume": 0, - "muted": false - } -} - -var resolutions = [ - Vector2i(1280, 720), - Vector2i(1366, 768), - Vector2i(1600, 900), - Vector2i(1920, 1080), - Vector2i(2560, 1440), - Vector2i(3840, 2160) -] - -func _ready() -> void: - load_config() - call_deferred("apply_config") - -func save_config() -> void: - var config_file = FileAccess.open(CONFIG_PATH, FileAccess.WRITE) - var json_string = JSON.stringify(config) - config_file.store_string(json_string) - -func load_config() -> void: - if not FileAccess.file_exists(CONFIG_PATH): - # Get current window state for initial config - config.video.fullscreen = DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN - # Find closest resolution match for current window size - var current_size = DisplayServer.window_get_size() - var closest_index = 0 - var smallest_diff = Vector2.INF - for i in range(resolutions.size()): - var diff = (resolutions[i] - Vector2i(current_size)).length() - if diff < smallest_diff.length(): - smallest_diff = Vector2(diff, diff) - closest_index = i - config.video.resolution_index = closest_index - - # Get current audio state - config.audio.master_volume = AudioServer.get_bus_volume_db(0) - config.audio.muted = AudioServer.is_bus_mute(0) - - save_config() - return - - var config_file = FileAccess.open(CONFIG_PATH, FileAccess.READ) - var json_string = config_file.get_as_text() - - var json = JSON.new() - var parse_result = json.parse(json_string) - if parse_result == OK: - var loaded_config = json.get_data() - if typeof(loaded_config) == TYPE_DICTIONARY: - if "video" in loaded_config: - config.video = loaded_config.video - if "audio" in loaded_config: - config.audio = loaded_config.audio - -func update_video_settings(fullscreen: bool, resolution_index: int) -> void: - var mode_changed = config.video.fullscreen != fullscreen - var res_changed = config.video.resolution_index != resolution_index - - config.video.fullscreen = fullscreen - config.video.resolution_index = resolution_index - apply_config() - save_config() - - if mode_changed: - window_mode_changed.emit(fullscreen) - if res_changed: - resolution_changed.emit(Vector2(resolutions[resolution_index])) - -func apply_config() -> void: - if config.video.fullscreen: - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) - else: - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) - var resolution = resolutions[config.video.resolution_index] - DisplayServer.window_set_size(resolution) - - # Center the window - var screen_size = DisplayServer.screen_get_size() - var window_pos = (screen_size - resolution) / 2 - DisplayServer.window_set_position(window_pos) - - # Apply audio settings - AudioServer.set_bus_volume_db(0, config.audio.master_volume) - AudioServer.set_bus_mute(0, config.audio.muted) - -func update_audio_settings(volume: float, muted: bool) -> void: - config.audio.master_volume = volume - config.audio.muted = muted - save_config() - -================ -File: scripts/managers/decoder_manager.gd -================ -extends Node - -signal aspect_decoded(card_id: String, aspect: String) -signal points_changed(new_amount: int) - -var available_points: int = 10 # Start with some points for testing - -const DECODE_COSTS = { - "cost": 2, - "type": 3, - "value": 4, - "description": 5 -} - -func _ready() -> void: - # Update UI on startup - points_changed.emit(available_points) - -func can_decode_aspect(aspect: String) -> bool: - if not DECODE_COSTS.has(aspect): - return false - return available_points >= DECODE_COSTS[aspect] - -func decode_aspect(card: Node, aspect: String) -> void: - if not can_decode_aspect(aspect): - return - - var cost = DECODE_COSTS[aspect] - available_points -= cost - points_changed.emit(available_points) - - # Store the decoded information - var card_id = card.card_data.resource_path - GameState.decoded_aspects[card_id] = GameState.decoded_aspects.get(card_id, {}) - GameState.decoded_aspects[card_id][aspect] = true - - # Update card display - card.reveal_aspect(aspect) - aspect_decoded.emit(card_id, aspect) - - # Update decode UI if it's visible - var decode_ui = get_node("/root/Main/Combat/UI/DecodeUI") - if decode_ui and decode_ui.visible: - decode_ui.update_points() - -func add_points(amount: int) -> void: - available_points += amount - points_changed.emit(available_points) - GameState.save_game() - -================ -File: scripts/managers/hand_manager.gd -================ -extends Node - -const CARD_SCENE = preload("res://scenes/card.tscn") - -var draw_pile: Array = [] -var hand: Array = [] -var discard_pile: Array = [] - -@onready var hand_container = $"../UI/HUD/HandContainer" - -func _ready() -> void: - setup_starting_deck() - -func setup_starting_deck() -> void: - # Add basic cards - for i in range(5): - var attack_card = load("res://resources/cards/attack_card.tres") - var block_card = load("res://resources/cards/block_card.tres") - draw_pile.append(attack_card) - draw_pile.append(block_card) - draw_pile.shuffle() - -func draw_cards(amount: int) -> void: - for i in range(amount): - if draw_pile.size() == 0: - print("Draw pile empty") - reshuffle_discard() - if draw_pile.size() == 0: - return - var card_data = draw_pile.pop_front() - create_card(card_data) - -func create_card(card_data: Resource) -> void: - var card_instance = CARD_SCENE.instantiate() - hand_container.add_child(card_instance) - - # Load decode state from GameState - var decoded_aspects = {} - if card_data.resource_path in GameState.decoded_aspects: - decoded_aspects = GameState.decoded_aspects[card_data.resource_path].duplicate() - - # Initialize card with both data and decode state - card_instance.setup(card_data, decoded_aspects) - card_instance.connect("card_played", _on_card_played, CONNECT_ONE_SHOT) - hand.append(card_instance) - -func discard_hand() -> void: - for card in hand: - discard_pile.append(card.card_data) - card.queue_free() - hand.clear() - -func reshuffle_discard() -> void: - print("Reshuffling discard") - draw_pile.append_array(discard_pile) - discard_pile.clear() - draw_pile.shuffle() - -func _on_card_played(card: Variant) -> void: - hand.remove_at(hand.find(card)) - discard_pile.append(card.card_data) - -================ -File: scripts/managers/pause_manager.gd -================ -extends Node - -const PAUSE_MENU = preload("res://scenes/pause_menu.tscn") - -var pause_menu: Control = null -var is_paused: bool = false - -func _ready() -> void: - setup_pause_menu() - process_mode = Node.PROCESS_MODE_ALWAYS - -func setup_pause_menu() -> void: - if pause_menu == null: - pause_menu = PAUSE_MENU.instantiate() - add_child(pause_menu) - var ui_scale_manager = get_node("/root/Main/UIScaleManager") - ui_scale_manager.register_ui_root(pause_menu) - pause_menu.resume_pressed.connect(_on_resume) - pause_menu.quit_pressed.connect(_on_quit) - pause_menu.hide() - -func _unhandled_input(event: InputEvent) -> void: - if event.is_action_pressed("ui_cancel"): # ESC key - toggle_pause() - -func toggle_pause() -> void: - is_paused = !is_paused - get_tree().paused = is_paused - - if is_paused: - pause_menu.open() - else: - pause_menu.close() - -func _on_resume() -> void: - toggle_pause() - -func _on_quit() -> void: - print("Pause Menu: Handling quit to main menu") - # Unpause the game - is_paused = false - get_tree().paused = false - pause_menu.hide() - - # Let Main handle the scene transition - var main = get_node("/root/Main") - if main: - main.show_main_menu() - else: - print("ERROR: Couldn't find Main node!") - -================ -File: scripts/managers/ui_scale_manager.gd -================ -extends Node - -var current_scale: float = 1.0 -const BASE_RESOLUTION := Vector2(1920, 1080) - -func _ready() -> void: - # Connect to signals - get_tree().root.connect("size_changed", _on_viewport_size_changed) - ConfigManager.resolution_changed.connect(_on_resolution_changed) - ConfigManager.window_mode_changed.connect(_on_window_mode_changed) - - # Initial setup - call_deferred("_do_initial_setup") - -func _do_initial_setup() -> void: - await get_tree().process_frame - _update_ui_scale() - -func _on_viewport_size_changed() -> void: - _update_ui_scale() - -func _on_resolution_changed(_new_resolution: Vector2) -> void: - _update_ui_scale() - -func _on_window_mode_changed(_is_fullscreen: bool) -> void: - _update_ui_scale() - -func register_ui_root(control: Control) -> void: - control.pivot_offset = control.size / 2 - _setup_control(control) - _update_ui_scale() - -func _update_ui_scale() -> void: - var viewport_size := DisplayServer.window_get_size() - var scale_x := viewport_size.x / float(BASE_RESOLUTION.x) - var scale_y := viewport_size.y / float(BASE_RESOLUTION.y) - current_scale = min(scale_x, scale_y) - - _scale_ui_recursive(get_tree().root) - -func _scale_ui_recursive(node: Node) -> void: - if node is Control: - _setup_control(node) - - for child in node.get_children(): - _scale_ui_recursive(child) - -func _setup_control(control: Control) -> void: - if control is CenterContainer: - var viewport_size = DisplayServer.window_get_size() - control.custom_minimum_size = Vector2.ZERO - control.anchor_right = 1.0 - control.anchor_bottom = 1.0 - control.offset_right = 0 - control.offset_bottom = 0 - control.position = Vector2.ZERO - control.size = viewport_size - - # Update children anchoring - for child in control.get_children(): - if child is Control: - child.anchor_left = 0.5 - child.anchor_right = 0.5 - child.anchor_top = 0.5 - child.anchor_bottom = 0.5 - child.position = -child.size / 2 - -================ -File: scripts/scenes/card.gd -================ -extends Control - -signal card_played(card) - -var card_data: Resource -var decoded_aspects: Dictionary = {} - -@onready var cost_label: Label = $MarginContainer/VBoxContainer/TopBar/CostLabel -@onready var name_label: Label = $MarginContainer/VBoxContainer/TopBar/NameLabel -@onready var effect_label: Label = $MarginContainer/VBoxContainer/EffectLabel - -# Constants for unread display -const UNKNOWN_COST = "?" -const UNKNOWN_NAME = "???" -const UNKNOWN_EFFECT = "????????????" - -func _ready() -> void: - # Set fixed size for cards - custom_minimum_size = Vector2(200, 300) - size = Vector2(200, 300) - - # Center card contents - $MarginContainer.anchor_right = 1.0 - $MarginContainer.anchor_bottom = 1.0 - $MarginContainer.offset_right = 0 - $MarginContainer.offset_bottom = 0 - - # Update card display - update_display() - -func update_display() -> void: - if not is_node_ready(): - await ready - # Cost display - if is_aspect_decoded("cost"): - cost_label.text = str(card_data.energy_cost) - else: - cost_label.text = UNKNOWN_COST - - # Name display - if is_aspect_decoded("name"): - name_label.text = card_data.name - else: - name_label.text = UNKNOWN_NAME - - # Effect display - if is_aspect_decoded("description"): - effect_label.text = card_data.effect_description - else: - effect_label.text = UNKNOWN_EFFECT - -func is_aspect_decoded(aspect: String) -> bool: - return decoded_aspects.get(aspect, false) - -func setup(data: Resource, starting_decoded_aspects: Dictionary = {}) -> void: - card_data = data - decoded_aspects = starting_decoded_aspects - -func reveal_aspect(aspect: String) -> void: - print("Revealing " + aspect) - decoded_aspects[aspect] = true - # Save to GameState - if not card_data.resource_path in GameState.decoded_aspects: - GameState.decoded_aspects[card_data.resource_path] = {} - GameState.decoded_aspects[card_data.resource_path][aspect] = true - update_display() - -func get_effect_value() -> int: - if is_aspect_decoded("value"): - return card_data.effect_value - else: - # Random value for unread cards - match card_data.card_type: - "attack": return randi_range(4, 8) - "block": return randi_range(4, 8) - _: return card_data.effect_value - -func _gui_input(event: InputEvent) -> void: - if event is InputEventMouseButton: - if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed: - show_decode_menu() - elif event.button_index == MOUSE_BUTTON_LEFT and event.pressed: - var combat_scene = get_node("/root/Main/Combat") - var combat_manager = combat_scene.get_node("CombatManager") - if combat_manager and combat_manager.can_play_card(card_data.energy_cost): - play_card() - -func play_card() -> void: - var combat_scene = get_node("/root/Main/Combat") - var combat_manager = combat_scene.get_node("CombatManager") - combat_manager.spend_energy(card_data.energy_cost) - execute_effect() - card_played.emit(self) - queue_free() - -func execute_effect() -> void: - var combat_scene = get_node("/root/Main/Combat") - - match card_data.card_type: - "attack": - var enemies_container = combat_scene.get_node("EnemiesContainer") - if enemies_container and enemies_container.get_child_count() > 0: - # For now, just target the first enemy - # You might want to implement target selection later - var enemy = enemies_container.get_child(0) - enemy.take_damage(get_effect_value()) - "block": - var player = combat_scene.get_node("Player") - if player: - player.gain_block(get_effect_value()) - -func show_decode_menu() -> void: - var popup = PopupMenu.new() - add_child(popup) - - # Add decode options - if not is_aspect_decoded("cost"): - popup.add_item("Decode Cost (2 points)", 0) - if not is_aspect_decoded("type"): - popup.add_item("Decode Type (3 points)", 1) - if not is_aspect_decoded("value"): - popup.add_item("Decode Value (4 points)", 2) - if not is_aspect_decoded("description"): - popup.add_item("Decode Description (5 points)", 3) - - if popup.item_count == 0: - popup.add_item("Fully Decoded", -1) - popup.set_item_disabled(0, true) - - popup.id_pressed.connect(_on_decode_option_selected) - popup.position = get_global_mouse_position() - popup.popup() - -func _on_decode_option_selected(id: int) -> void: - var combat_scene = get_node("/root/Main/Combat") - var decoder = combat_scene.get_node("DecoderManager") - match id: - 0: decoder.decode_aspect(self, "cost") - 1: decoder.decode_aspect(self, "type") - 2: decoder.decode_aspect(self, "value") - 3: decoder.decode_aspect(self, "description") - -================ -File: scripts/scenes/combat.gd -================ -extends Node2D - -@onready var combat_manager = $CombatManager -@onready var hud = $UI/HUD - -func _ready() -> void: - # Set up HUD to fill screen - hud.anchor_right = 1.0 - hud.anchor_bottom = 1.0 - hud.offset_right = 0 - hud.offset_bottom = 0 - - # Center the hand container - var hand_container = $UI/HUD/HandContainer - hand_container.custom_minimum_size.y = 300 - hand_container.anchor_left = 0.5 - hand_container.anchor_right = 0.5 - hand_container.anchor_top = 1.0 - hand_container.anchor_bottom = 1.0 - hand_container.offset_top = -300 - - # Position end turn button - var end_turn_container = $UI/HUD/EndTurnMarginContainer - end_turn_container.anchor_left = 1.0 - end_turn_container.anchor_right = 1.0 - end_turn_container.anchor_top = 1.0 - end_turn_container.anchor_bottom = 1.0 - - # Position energy display - var energy_container = $UI/HUD/EnergyMarginContainer - energy_container.anchor_left = 1.0 - energy_container.anchor_right = 1.0 - - $UI/HUD/EndTurnMarginContainer/EndTurnButton.pressed.connect(_on_end_turn_pressed) - - # Connect to enemy death or victory condition - $EnemiesContainer.child_exiting_tree.connect(_check_combat_state) - -func _check_combat_state(_node) -> void: - # If all enemies are dead - if $EnemiesContainer.get_child_count() <= 1: # <= 1 because the signal fires before the node is fully removed - _on_combat_won() - -func _on_combat_won() -> void: - AdventureSystem.complete_current_encounter() - queue_free() - -func _on_end_turn_pressed() -> void: - combat_manager.end_turn() - -================ -File: scripts/scenes/cutscene_scene.gd -================ -class_name CutsceneScene -extends Control - -signal cutscene_finished - -@onready var audio_player: AudioStreamPlayer2D = $AudioStreamPlayer -@onready var image_display: TextureRect = $TextureRect - -var current_cutscene: CutsceneData -var current_frame_index: int = 0 -var exit_callback: Callable - -func _ready() -> void: - audio_player.connect("finished", _on_audio_finished) - -func start_cutscene(cutscene: CutsceneData, callback: Callable) -> void: - if not cutscene: - push_error("Invalid cutscene resource") - return - - print("Cutscene: " + cutscene.resource_name) - - current_cutscene = cutscene - current_frame_index = 0 - exit_callback = callback - - # Setup audio - audio_player.stream = cutscene.audio_track - audio_player.play() - - # Show first frame - if cutscene.frames.size() > 0: - image_display.texture = cutscene.frames[0].image - - # Start checking timestamps - set_process(true) - -func _process(_delta: float) -> void: - if not current_cutscene or current_frame_index >= current_cutscene.frames.size(): - return - - var current_time = audio_player.get_playback_position() - var next_frame = current_cutscene.frames[current_frame_index] - - if current_time >= next_frame.timestamp: - image_display.texture = next_frame.image - current_frame_index += 1 - -func _on_audio_finished() -> void: - set_process(false) - emit_signal("cutscene_finished") - - if exit_callback != null: - exit_callback.call() - - self.queue_free() - -func _unhandled_input(event: InputEvent) -> void: - if event.is_action_pressed("ui_cancel"): - skip_cutscene() - -func skip_cutscene() -> void: - audio_player.stop() - _on_audio_finished() - -================ -File: scripts/scenes/encounter_node.gd -================ -extends Control - -signal node_clicked(node_data: EncounterNodeData) - -var node_data: EncounterNodeData - -@onready var circle: ColorRect = $CenterContainer/VBoxContainer/Circle -@onready var label: Label = $CenterContainer/VBoxContainer/Label - -const BASE_NODE_COLOR := Color("4a4a4a") -const START_NODE_COLOR := Color("1a531a") -const FINISH_NODE_COLOR := Color("531a1a") -const COMPLETED_COLOR := Color("4a821a") -const HOVER_TINT := Color(1.2, 1.2, 1.2, 1) - -func _ready() -> void: - custom_minimum_size = Vector2(100, 100) - mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND - - # Setup mouse handling - gui_input.connect(_on_gui_input) - mouse_entered.connect(_on_mouse_entered) - mouse_exited.connect(_on_mouse_exited) - - # If we were set up before _ready, update appearance now - if node_data: - update_appearance() - -func setup(data: EncounterNodeData) -> void: - node_data = data - if is_node_ready(): - update_appearance() - -func get_node_data() -> EncounterNodeData: - return node_data - -func set_start_node() -> void: - if not is_node_ready(): - await ready - circle.color = START_NODE_COLOR - label.text = "Start" - -func set_finish_node() -> void: - if not is_node_ready(): - await ready - circle.color = FINISH_NODE_COLOR - label.text = "Final" - -func set_completed() -> void: - if not is_node_ready(): - await ready - circle.color = COMPLETED_COLOR - -func update_appearance() -> void: - if not is_node_ready(): - await ready - - if node_data.completed: - set_completed() - elif node_data is StartEncounterNodeData: - set_start_node() - elif node_data is FinishEncounterNodeData: - set_finish_node() - else: - circle.color = BASE_NODE_COLOR - label.text = "Encounter" - -func _on_gui_input(event: InputEvent) -> void: - if event is InputEventMouseButton: - if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: - node_clicked.emit(node_data) - -func _on_mouse_entered() -> void: - modulate = HOVER_TINT - -func _on_mouse_exited() -> void: - modulate = Color.WHITE - -================ -File: scripts/scenes/home_base.gd -================ -extends Control - -func _on_adventure_button_pressed() -> void: - pass # Go to run tree - -func _on_decoder_button_pressed() -> void: - pass # Go to decoder - -func _on_stone_button_pressed() -> void: - pass # Go to stone powers and progress - -func _on_loadout_button_pressed() -> void: - pass # Go to deck viewer and loadout - -================ -File: scripts/scenes/home_base.tscn -================ -[gd_scene load_steps=3 format=3 uid="uid://bnk32gyp3bsir"] - -[ext_resource type="Script" path="res://scripts/scenes/home_base.gd" id="1_4udhq"] -[ext_resource type="Script" path="res://scripts/managers/pause_manager.gd" id="2_4gf2c"] - -[node name="HomeBase" type="Control"] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_4udhq") - -[node name="VBoxContainer" type="VBoxContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="Label" type="Label" parent="VBoxContainer"] -layout_mode = 2 -theme_override_font_sizes/font_size = 105 -text = "Home base" -horizontal_alignment = 1 - -[node name="GridContainer" type="GridContainer" parent="VBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 -theme_override_constants/h_separation = 40 -theme_override_constants/v_separation = 40 -columns = 2 - -[node name="AdventureButton" type="Button" parent="VBoxContainer/GridContainer"] -custom_minimum_size = Vector2(400, 400) -layout_mode = 2 -theme_override_font_sizes/font_size = 87 -text = "Go on an adventure" - -[node name="DecoderButton" type="Button" parent="VBoxContainer/GridContainer"] -custom_minimum_size = Vector2(400, 400) -layout_mode = 2 -theme_override_font_sizes/font_size = 87 -text = "Decode cards" - -[node name="StoneButton" type="Button" parent="VBoxContainer/GridContainer"] -custom_minimum_size = Vector2(400, 400) -layout_mode = 2 -theme_override_font_sizes/font_size = 87 -text = "Magical stone" - -[node name="LoadoutButton" type="Button" parent="VBoxContainer/GridContainer"] -custom_minimum_size = Vector2(400, 400) -layout_mode = 2 -theme_override_font_sizes/font_size = 87 -text = "Deck and inventory" - -[node name="PauseManager" type="Node" parent="."] -script = ExtResource("2_4gf2c") - -[connection signal="pressed" from="VBoxContainer/GridContainer/AdventureButton" to="." method="_on_adventure_button_pressed"] -[connection signal="pressed" from="VBoxContainer/GridContainer/DecoderButton" to="." method="_on_decoder_button_pressed"] -[connection signal="pressed" from="VBoxContainer/GridContainer/StoneButton" to="." method="_on_stone_button_pressed"] -[connection signal="pressed" from="VBoxContainer/GridContainer/LoadoutButton" to="." method="_on_loadout_button_pressed"] - -================ -File: scripts/scenes/main_menu.gd -================ -# main_menu.gd -extends Control - -const OPTIONS_SCENE = preload("res://scenes/menus/options_menu.tscn") - -func _ready(): - $CenterContainer/VBoxContainer/StartButton.pressed.connect(_on_start_pressed) - $CenterContainer/VBoxContainer/ContinueButton.pressed.connect(_on_continue_button_pressed) - $CenterContainer/VBoxContainer/Options.pressed.connect(_on_options_pressed) - $CenterContainer/VBoxContainer/QuitButton.pressed.connect(_on_quit_pressed) - # Enable menu when it's first shown - enable_menu() - -func _on_start_pressed(): - print("start pressed") - # Disable menu interaction before starting cutscene - disable_menu() - # Tell the main scene to handle the new game sequence - get_node("/root/Main").handle_new_game() - -func _on_quit_pressed(): - print("quit pressed") - GameState.save_game() - get_tree().quit() - -func _on_continue_button_pressed() -> void: - print("continue pressed") - GameState.load_game() - get_node("/root/Main").start_home_base() - -func _on_options_pressed() -> void: - var options = OPTIONS_SCENE.instantiate() - add_child(options) - # Hide the main menu buttons while in options - $CenterContainer.hide() - # Connect to know when to show the buttons again - options.options_closed.connect(_on_options_closed) - -func _on_options_closed() -> void: - $CenterContainer.show() - -func disable_menu() -> void: - # Disable all buttons - for button in $CenterContainer/VBoxContainer.get_children(): - if button is Button: - button.disabled = true - - # Make menu non-interactive - mouse_filter = Control.MOUSE_FILTER_IGNORE - - # Optional: fade the menu visually - modulate = Color(1, 1, 1, 0.5) - -func enable_menu() -> void: - # Enable all buttons that should be enabled - for button in $CenterContainer/VBoxContainer.get_children(): - if button is Button: - # Skip buttons that should remain disabled (like Options or Continue if not available) - if button.name == "ContinueButton" and not has_save_game(): - continue - button.disabled = false - - # Make menu interactive again - mouse_filter = Control.MOUSE_FILTER_STOP - - # Restore full opacity - modulate = Color(1, 1, 1, 1) - - # Focus the Start button - $CenterContainer/VBoxContainer/StartButton.grab_focus() - -func has_save_game() -> bool: - return FileAccess.file_exists("user://gamestate.save") - -================ -File: scripts/scenes/main.gd -================ -extends Node - -@onready var combat_scene = preload("res://scenes/combat.tscn") -@onready var menu_scene = preload("res://scenes/menus/main_menu.tscn") -@onready var home_base_scene = preload("res://scripts/scenes/home_base.tscn") - -var current_scene: Node = null - -func _ready(): - var ui_scale_manager = Node.new() - ui_scale_manager.name = "UIScaleManager" - ui_scale_manager.set_script(load("res://scripts/managers/ui_scale_manager.gd")) - add_child(ui_scale_manager) - # Ensure config is applied before showing the menu - await get_tree().create_timer(0.1).timeout - show_main_menu() - -func cleanup_current_scene() -> void: - print("Main: Cleaning up current scene") - AdventureSystem.cleanup() - if current_scene: - remove_child(current_scene) - current_scene.queue_free() - current_scene = null - -func transition_to_scene(new_scene: Node) -> void: - print("Transitioning to new scene: ", new_scene.name) - cleanup_current_scene() - - print("Adding new scene: ", new_scene.name) - current_scene = new_scene - add_child(current_scene) - -func show_main_menu() -> void: - print("Showing main menu") - var new_menu = menu_scene.instantiate() - call_deferred("transition_to_scene", new_menu) - -func start_combat() -> void: - var new_combat = combat_scene.instantiate() - call_deferred("transition_to_scene", new_combat) - -func start_home_base() -> void: - var new_home = home_base_scene.instantiate() - call_deferred("transition_to_scene", new_home) - -func handle_new_game() -> void: - GameState.start_new_run() - var new_game_cutscene = load("res://resources/cutscenes/new_game/new_game_cutscene.tres") - cleanup_current_scene() - - # Start the cutscene - CutsceneSystem.play_cutscene(new_game_cutscene, func(): - # After cutscene, start tutorial - var ingame_tutorial_adventure = load("res://resources/adventures/ingame_tutorial/adventure_map.tres") - AdventureSystem.start_adventure(ingame_tutorial_adventure) - # Connect to adventure completion if not already connected - if not AdventureSystem.adventure_completed.is_connected(_on_tutorial_completed): - AdventureSystem.adventure_completed.connect(_on_tutorial_completed) - ) - -func _on_tutorial_completed(_adventure: AdventureMapData) -> void: - start_home_base() - -================ -File: scripts/scenes/options_menu.gd -================ -# options_menu.gd -extends Control - -signal options_closed - -# Scene References -@onready var fullscreen_toggle = %FullscreenToggle -@onready var resolution_dropdown = %ResolutionOptionButton -@onready var master_volume_slider = %VolumeSlider -@onready var mute_toggle = %MuteToggle -@onready var back_button = %BackButton -@onready var volume_label = %VolumeLabelValue - -# Constants for volume conversion -const MIN_DB = -80 -const MAX_DB = 0 - -func _ready(): - setup_controls() - load_settings() - connect_signals() - -func setup_controls(): - # Setup resolution dropdown - for resolution in ConfigManager.resolutions: - resolution_dropdown.add_item("%dx%d" % [resolution.x, resolution.y]) - - # Setup volume slider with percentage values - master_volume_slider.min_value = 0 - master_volume_slider.max_value = 100 - master_volume_slider.step = 1 - - # Load initial values - fullscreen_toggle.button_pressed = ConfigManager.config.video.fullscreen - resolution_dropdown.selected = ConfigManager.config.video.resolution_index - # Convert from dB to percentage for display - master_volume_slider.value = db_to_percent(ConfigManager.config.audio.master_volume) - mute_toggle.button_pressed = ConfigManager.config.audio.muted - - # Update volume display - _update_volume_label(master_volume_slider.value) - - # Update resolution dropdown state based on fullscreen - _update_resolution_dropdown_state() - -func connect_signals(): - print("Connecting signals...") # Debug print - fullscreen_toggle.pressed.connect(_on_fullscreen_toggled) - resolution_dropdown.item_selected.connect(_on_resolution_selected) - master_volume_slider.value_changed.connect(_on_volume_changed) - mute_toggle.toggled.connect(_on_mute_toggled) - back_button.pressed.connect(_on_back_pressed) - -# Convert decibel value to percentage (0-100) -func db_to_percent(db: float) -> float: - if db <= MIN_DB: - return 0.0 - if db >= MAX_DB: - return 100.0 - return ((db - MIN_DB) / (MAX_DB - MIN_DB)) * 100.0 - -# Convert percentage (0-100) to decibel value -func percent_to_db(percent: float) -> float: - return lerp(MIN_DB, MAX_DB, percent / 100.0) - -func _update_volume_label(percent: float) -> void: - volume_label.text = "%d%%" % percent - -func _on_volume_changed(value: float): - print("Volume changed to ", value, "%") - _update_volume_label(value) - var db_value = percent_to_db(value) - AudioServer.set_bus_volume_db(0, db_value) - save_settings() - -func _update_resolution_dropdown_state(): - if fullscreen_toggle.button_pressed: - # Get current screen resolution - var screen_size = DisplayServer.screen_get_size() - resolution_dropdown.disabled = true - - # Find and select the closest matching resolution - var closest_index = 0 - var smallest_diff = Vector2.INF - for i in range(ConfigManager.resolutions.size()): - var diff = (ConfigManager.resolutions[i] - screen_size).length() - if diff < smallest_diff.length(): - smallest_diff = Vector2(diff, diff) - closest_index = i - - resolution_dropdown.selected = closest_index - DisplayServer.window_set_size(screen_size) - else: - resolution_dropdown.disabled = false - -func _on_fullscreen_toggled(): - print("Fullscreen toggled: ", fullscreen_toggle.button_pressed) # Debug print - if fullscreen_toggle.button_pressed: - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) - else: - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) - - _update_resolution_dropdown_state() - save_settings() - -func _on_resolution_selected(index: int): - print("Resolution selected: ", index) # Debug print - if index >= 0 and index < ConfigManager.resolutions.size() and not fullscreen_toggle.button_pressed: - var resolution = ConfigManager.resolutions[index] - DisplayServer.window_set_size(resolution) - save_settings() - -func _on_mute_toggled(button_pressed: bool): - print("Mute toggled: ", button_pressed) # Debug print - AudioServer.set_bus_mute(0, button_pressed) - save_settings() - -func _on_back_pressed(): - print("Back pressed") # Debug print - save_settings() - options_closed.emit() - queue_free() - -func save_settings(): - ConfigManager.update_video_settings( - fullscreen_toggle.button_pressed, - resolution_dropdown.selected - ) - ConfigManager.update_audio_settings( - percent_to_db(master_volume_slider.value), - mute_toggle.button_pressed - ) - -func load_settings(): - fullscreen_toggle.button_pressed = ConfigManager.config.video.fullscreen - resolution_dropdown.selected = ConfigManager.config.video.resolution_index - master_volume_slider.value = db_to_percent(ConfigManager.config.audio.master_volume) - _update_volume_label(master_volume_slider.value) - mute_toggle.button_pressed = ConfigManager.config.audio.muted - - # Apply the settings - if ConfigManager.config.video.fullscreen: - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) - else: - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) - _on_resolution_selected(ConfigManager.config.video.resolution_index) - - AudioServer.set_bus_volume_db(0, ConfigManager.config.audio.master_volume) - AudioServer.set_bus_mute(0, ConfigManager.config.audio.muted) - -================ -File: scripts/systems/adventure_system.gd -================ -extends Node - -signal adventure_started(adventure_data: AdventureMapData) -signal encounter_selected(node: EncounterNodeData) -signal encounter_completed(node: EncounterNodeData) -signal adventure_completed(adventure_data: AdventureMapData) - -const COMBAT_SCENE = preload("res://scenes/combat.tscn") -const ADVENTURE_MAP_SCENE = preload("res://scenes/adventure_map.tscn") - -var current_adventure: AdventureMapData -var current_node: EncounterNodeData -var current_scene: Node -var map_instance: Node - -func cleanup() -> void: - print("Adventure System: Cleaning up...") - if is_instance_valid(current_scene): - print("Removing current scene") - if current_scene.get_parent(): - current_scene.get_parent().call_deferred("remove_child", current_scene) - current_scene.call_deferred("queue_free") - current_scene = null - - if is_instance_valid(map_instance): - print("Removing map instance") - if map_instance.get_parent(): - map_instance.get_parent().call_deferred("remove_child", map_instance) - map_instance.call_deferred("queue_free") - map_instance = null - - current_node = null - -func start_adventure(adventure_data: AdventureMapData) -> void: - print("Starting new adventure") - cleanup() # Clean up any existing adventure first - current_adventure = adventure_data - current_node = null - - adventure_started.emit(adventure_data) - - if adventure_data.auto_start and adventure_data.rootEncounterNode.childNodes.size() > 0: - _auto_start_first_encounter(adventure_data.rootEncounterNode) - else: - show_map() - -func show_map() -> void: - if current_scene or map_instance: - cleanup() - await get_tree().process_frame - - print("Creating new adventure map") - map_instance = ADVENTURE_MAP_SCENE.instantiate() - var main = get_node("/root/Main") - main.call_deferred("add_child", map_instance) - - await get_tree().process_frame - var adventure_manager = map_instance.get_node("AdventureManager") - if adventure_manager: - print("Initializing adventure map") - adventure_manager.init_adventure(current_adventure) - else: - push_error("AdventureManager not found in map instance") - -func _auto_start_first_encounter(start_node: StartEncounterNodeData) -> void: - if start_node.childNodes.size() > 0: - var first_encounter = start_node.childNodes[0] - select_encounter(first_encounter) - -func select_encounter(encounter_node: EncounterNodeData) -> void: - if not can_select_encounter(encounter_node): - return - - print("Selecting encounter: ", encounter_node.get_class()) - current_node = encounter_node - encounter_selected.emit(encounter_node) - - if is_instance_valid(map_instance): - if map_instance.get_parent(): - map_instance.get_parent().call_deferred("remove_child", map_instance) - map_instance.call_deferred("queue_free") - map_instance = null - - if encounter_node is CombatEncounterNodeData: - start_combat(encounter_node) - elif encounter_node is EventEncounterNodeData: - start_event(encounter_node) - elif encounter_node is FinishEncounterNodeData: - handle_finish_encounter(encounter_node) - -func can_select_encounter(node: EncounterNodeData) -> bool: - if node.completed: - return false - - if not node is StartEncounterNodeData: - var has_completed_parent = false - for potential_parent in _get_all_parent_nodes(node): - if potential_parent.completed: - has_completed_parent = true - break - if not has_completed_parent: - return false - - return true - -func _get_all_parent_nodes(node: EncounterNodeData) -> Array[EncounterNodeData]: - var parents: Array[EncounterNodeData] = [] - _find_parents_recursive(current_adventure.rootEncounterNode, node, parents) - return parents - -func _find_parents_recursive(current: EncounterNodeData, target: EncounterNodeData, parents: Array[EncounterNodeData]) -> bool: - for child in current.childNodes: - if child == target: - parents.append(current) - return true - if _find_parents_recursive(child, target, parents): - parents.append(current) - return true - return false - -func complete_current_encounter() -> void: - if not current_node: - return - - print("Completing encounter: ", current_node.get_class()) - current_node.completed = true - encounter_completed.emit(current_node) - - if current_node is FinishEncounterNodeData: - adventure_completed.emit(current_adventure) - else: - call_deferred("_handle_encounter_completion") - -func _handle_encounter_completion() -> void: - if is_instance_valid(current_scene): - if current_scene.get_parent(): - current_scene.get_parent().call_deferred("remove_child", current_scene) - current_scene.call_deferred("queue_free") - current_scene = null - - await get_tree().process_frame - print("Showing map after encounter completion") - show_map() - -func start_combat(combat_node: CombatEncounterNodeData) -> void: - print("Starting combat encounter") - var main = get_node("/root/Main") - current_scene = COMBAT_SCENE.instantiate() - - var enemies_container = current_scene.get_node("EnemiesContainer") - - for enemy_data in combat_node.enemies: - var enemy_instance = enemy_data.enemy_scene.instantiate() - enemies_container.add_child(enemy_instance) - if enemy_instance.has_method("setup"): - enemy_instance.setup(enemy_data) - - main.call_deferred("add_child", current_scene) - -func start_event(event_node: EventEncounterNodeData) -> void: - print("Starting event encounter") - if event_node.eventScene: - var main = get_node("/root/Main") - current_scene = event_node.eventScene.instantiate() - main.call_deferred("add_child", current_scene) - else: - print("Warning: Event node has no scene assigned") - complete_current_encounter() - -func handle_finish_encounter(finish_node: FinishEncounterNodeData) -> void: - print("Starting finish encounter") - if finish_node.finishScene: - var main = get_node("/root/Main") - current_scene = finish_node.finishScene.instantiate() - main.call_deferred("add_child", current_scene) - else: - print("Completing adventure without finish scene") - complete_current_encounter() - -func return_to_map() -> void: - if not (current_node is FinishEncounterNodeData): - if is_instance_valid(current_scene): - if current_scene.get_parent(): - current_scene.get_parent().call_deferred("remove_child", current_scene) - current_scene.call_deferred("queue_free") - current_scene = null - show_map() - -func get_current_adventure() -> AdventureMapData: - return current_adventure - -func get_current_node() -> EncounterNodeData: - return current_node - -func is_node_available(node: EncounterNodeData) -> bool: - return can_select_encounter(node) - -================ -File: scripts/systems/background_system.gd -================ -extends Node - -# Preload the background textures -const MAIN_MENU_BG = preload("res://assets/backgrounds/main_menu_bg.webp") -const COMBAT_BG = preload("res://assets/backgrounds/combat_bg.png") - -var current_background: Control - -func cleanup() -> void: - if current_background: - if current_background.get_parent(): - current_background.get_parent().remove_child(current_background) - current_background.queue_free() - current_background = null - print("Background System: Cleaned up current background") - -func setup_for_scene(scene_type: String) -> void: - print("Background System: Setting up background for ", scene_type) - cleanup() - - var background: Control - - match scene_type: - "MainMenu": - background = TextureRect.new() - background.texture = MAIN_MENU_BG - "Combat": - background = TextureRect.new() - background.texture = COMBAT_BG - _: - background = ColorRect.new() - background.color = Color.WHITE - - if background is TextureRect: - background.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED - - background.set_anchors_preset(Control.PRESET_FULL_RECT) - background.z_index = -1 - background.mouse_filter = Control.MOUSE_FILTER_IGNORE - - add_child(background) - current_background = background - -================ -File: scripts/systems/cutscene_system.gd -================ -extends Node - -const CUTSCENE_SCENE_PATH = "res://scenes/cutscene.tscn" - -func play_cutscene(cutscene: CutsceneData, callback: Callable) -> void: - if not cutscene: - push_error("Invalid cutscene resource") - return - - var cutscene_scene = load(CUTSCENE_SCENE_PATH).instantiate() as CutsceneScene - get_tree().root.add_child(cutscene_scene) - cutscene_scene.start_cutscene(cutscene, callback) - -================ -File: scripts/utils/circle.gd -================ -extends ColorRect - -func _draw() -> void: - draw_circle(size / 2, size.x / 2, color) - -func _ready() -> void: - # Make the ColorRect transparent, we'll only show the circle - color = Color.TRANSPARENT - -================ -File: scripts/decode_ui.gd -================ -# decode_ui.gd -extends PanelContainer - -signal aspect_selected(aspect: String) - -@onready var decode_points_label = $MarginContainer/VBox/DecodePointsLabel -@onready var options_container = $MarginContainer/VBox/OptionsContainer - -# Decode costs for each aspect -const COSTS = { - "cost": 2, - "type": 3, - "value": 4, - "description": 5 -} - -func _ready() -> void: - hide() - -func show_for_card(card: Node) -> void: - # Clear previous options - for child in options_container.get_children(): - child.queue_free() - - # Add new options based on card's decode state - for aspect in COSTS: - if not card.is_aspect_decoded(aspect): - var button = Button.new() - button.text = "Decode %s (%d points)" % [aspect.capitalize(), COSTS[aspect]] - - var decoder = get_node("/root/Main/Combat/DecoderManager") - button.disabled = decoder.available_points < COSTS[aspect] - - button.pressed.connect(func(): - aspect_selected.emit(aspect) - hide() - ) - options_container.add_child(button) - - if options_container.get_child_count() == 0: - var label = Label.new() - label.text = "Card fully decoded!" - options_container.add_child(label) - - # Show the popup - show() - # Center in viewport - position = get_viewport_rect().size / 2 - size / 2 - -================ -File: scripts/enemy.gd -================ -extends Node - -var health: int = 30 -var max_health: int = 30 - -@onready var health_label: Label = $HealthLabel - -func _ready() -> void: - update_health_display() - -func take_damage(amount: int) -> void: - health -= amount - update_health_display() - if health <= 0: - queue_free() - -func take_turn() -> void: - print("Enemy turn") - var player = get_node("/root/Main/Combat/Player") - if player: - # This will be replaced with actual ability usage - pass - -func update_health_display() -> void: - health_label.text = str(health) + "/" + str(max_health) - -func setup(enemy_data: EnemyData) -> void: - if not is_node_ready(): - await ready - health = enemy_data.health - max_health = enemy_data.health - update_health_display() - -================ -File: scripts/pause_menu.gd -================ -extends Control - -signal resume_pressed -signal quit_pressed - -func _ready() -> void: - hide() - # Connect button signals - var resume_button = $PanelContainer/MarginContainer/VBoxContainer/ResumeButton - var quit_button = $PanelContainer/MarginContainer/VBoxContainer/QuitButton - - if not resume_button.pressed.is_connected(_on_resume_pressed): - resume_button.pressed.connect(_on_resume_pressed) - if not quit_button.pressed.is_connected(_on_quit_pressed): - quit_button.pressed.connect(_on_quit_pressed) - -func _on_resume_pressed() -> void: - resume_pressed.emit() - -func _on_quit_pressed() -> void: - quit_pressed.emit() - -func open() -> void: - show() - $PanelContainer/MarginContainer/VBoxContainer/ResumeButton.grab_focus() - -func close() -> void: - hide() - -================ -File: scripts/player.gd -================ -extends Node2D - -var health: int = 40 -var max_health: int = 40 -var block: int = 0 - -@onready var health_label = $HealthLabel - -func _ready() -> void: - update_health_display() - -func take_damage(amount: int) -> void: - # First reduce block - if block > 0: - var blocked = min(block, amount) - amount -= blocked - block -= blocked - - # Then reduce health - if amount > 0: - health -= amount - print("Player took ", amount, " damage. Health: ", health) - - update_health_display() - - if health <= 0: - die() - -func gain_block(amount: int) -> void: - block += amount - print("Player gained ", amount, " block. Total block: ", block) - update_health_display() - -func update_health_display() -> void: - var block_text = " [" + str(block) + "]" if block > 0 else "" - health_label.text = str(health) + "/" + str(max_health) + block_text - -func die() -> void: - print("Game Over!") - # Handle game over logic - -================ -File: .gitignore -================ -# Godot 4+ specific ignores -.godot/ - -# Godot-specific ignores -.import/ -export.cfg -export_presets.cfg - -# Imported translations (automatically generated from CSV files) -*.translation - -# Mono-specific ignores -.mono/ -data_*/ -mono_crash.*.json - -# Mac-specific ignores -.DS_Store - -================ -File: ATTRIBUTION.md -================ -# Attribution -## Collaborators - - -### Role -Person 1 -Person 2 -[Person w/ Link]() - - -## Sourced / Unaffiliated -### Asset Type -#### Use Case -Author: [Name]() -Source: [Domain : webpage.html]() -License: [License]() - - -## Tools -#### Godot -Author: [Juan Linietsky, Ariel Manzur, and contributors](https://godotengine.org/contact) -Source: [godotengine.org](https://godotengine.org/) -License: [MIT License](https://github.com/godotengine/godot/blob/master/LICENSE.txt) - -#### Git -Author: [Linus Torvalds](https://github.com/torvalds) -Source: [git-scm.com](https://git-scm.com/downloads) -License: [GNU General Public License version 2](https://opensource.org/licenses/GPL-2.0) - -#### Godot Options Menus -Author: [Marek Belski and contributors](https://github.com/Maaack/Godot-Options-Menus/graphs/contributors) -Source: [github: Godot-Options-Menus](https://github.com/Maaack/Godot-Options-Menus) -License: [MIT License](LICENSE.txt) - -================ -File: LICENSE -================ -MIT License - -Copyright (c) 2024-present Pixel Pilgrims Studio - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -================ -File: project.godot -================ -; Engine configuration file. -; It's best edited using the editor UI and not directly, -; since the parameters that go here are not all obvious. -; -; Format: -; [section] ; section goes between [] -; param=value ; assign values to parameters - -config_version=5 - -[ai_assistant_hub] - -base_url="http://127.0.0.1:11434" -llm_api="ollama_api" - -[application] - -config/name="New Game Project" -run/main_scene="res://scenes/main.tscn" -config/features=PackedStringArray("4.3", "GL Compatibility") - -[autoload] - -ConfigManager="*res://scripts/managers/config_manager.gd" -GameState="*res://scripts/autoload/game_state.gd" -AdventureSystem="*res://scripts/systems/adventure_system.gd" -BackgroundSystem="*res://scripts/systems/background_system.gd" -CutsceneSystem="*res://scripts/systems/cutscene_system.gd" - -[display] - -window/size/viewport_width=1920 -window/size/viewport_height=1080 -window/size/resizable=false - -[editor_plugins] - -enabled=PackedStringArray() - -[github_to_itch] - -config/itch_username="" -config/itch_project_name="" - -[maaacks_options_menus] - -disable_plugin_dialogues=true -copy_path="res://scenes" - -[rendering] - -renderer/rendering_method="gl_compatibility" -renderer/rendering_method.mobile="gl_compatibility" - -================ -File: README.md -================ -# game-off-2024 From d40c7d7be87beb3626d5ae4e7efdb92899ac66c1 Mon Sep 17 00:00:00 2001 From: Thomas Metten Date: Wed, 6 Nov 2024 14:55:05 +0100 Subject: [PATCH 6/7] chore: ignore repomix output --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3b5b4ad..7759743 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,7 @@ data_*/ mono_crash.*.json # Mac-specific ignores -.DS_Store \ No newline at end of file +.DS_Store + +# Repomix-specific ignores +repomix-output.txt \ No newline at end of file From 9ea0d5a3c61a666f5a9292877a4918e8ba6f48df Mon Sep 17 00:00:00 2001 From: Thomas Metten Date: Wed, 6 Nov 2024 16:47:05 +0100 Subject: [PATCH 7/7] fix: revert deletion of update_points method --- scripts/decode_ui.gd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/decode_ui.gd b/scripts/decode_ui.gd index c2c6ba3..0b02341 100644 --- a/scripts/decode_ui.gd +++ b/scripts/decode_ui.gd @@ -16,6 +16,11 @@ const COSTS = { func _ready() -> void: hide() + update_points() + +func update_points() -> void: + var decoder = get_node("/root/Main/Combat/DecoderManager") + decode_points_label.text = "Decode Points: %d" % decoder.available_points func show_for_card(card: Node) -> void: # Clear previous options