diff --git a/game/levels/abstract_level.gd b/game/levels/abstract_level.gd index 30e8e95..c153a1b 100644 --- a/game/levels/abstract_level.gd +++ b/game/levels/abstract_level.gd @@ -2,6 +2,10 @@ class_name AbstractLevel extends Node2D +const NEXT_LEVEL_META = "next_level" +const CURRENT_LEVEL_INDEX = "current_level" + + @onready var pause_menu : PauseMenu = $%PauseMenu @onready var game_over_menu : GameOverMenu = $%GameOverMenu @onready var completion_menu : CompletionMenu = $%CompletionMenu @@ -27,3 +31,10 @@ func _on_level_end_entered(body: Node2D) -> void: if body is Player: get_tree().paused = true completion_menu.show() + + var level_index : int = get_tree().get_meta(AbstractLevel.CURRENT_LEVEL_INDEX, 0) + get_tree().remove_meta(AbstractLevel.CURRENT_LEVEL_INDEX) + + if SaveManager.completed_levels <= level_index: + SaveManager.completed_levels = level_index + 1 + SaveManager.save() diff --git a/game/levels/level_1.gd b/game/levels/test_level.gd similarity index 51% rename from game/levels/level_1.gd rename to game/levels/test_level.gd index 98ecccd..b20642a 100644 --- a/game/levels/level_1.gd +++ b/game/levels/test_level.gd @@ -1,2 +1,2 @@ -class_name Level1 +class_name TestLevel extends AbstractLevel diff --git a/game/levels/level_1.gd.uid b/game/levels/test_level.gd.uid similarity index 100% rename from game/levels/level_1.gd.uid rename to game/levels/test_level.gd.uid diff --git a/game/levels/level_1.tscn b/game/levels/test_level.tscn similarity index 99% rename from game/levels/level_1.tscn rename to game/levels/test_level.tscn index 00e78a5..35c8a7a 100644 --- a/game/levels/level_1.tscn +++ b/game/levels/test_level.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=21 format=4 uid="uid://bmn74wc2vophn"] [ext_resource type="PackedScene" uid="uid://mpsu4g2b5h3a" path="res://game/levels/abstract_level.tscn" id="1_15okj"] -[ext_resource type="Script" uid="uid://b04km0lbc108u" path="res://game/levels/level_1.gd" id="2_hj65a"] +[ext_resource type="Script" uid="uid://b04km0lbc108u" path="res://game/levels/test_level.gd" id="2_hj65a"] [ext_resource type="PackedScene" uid="uid://b3kyqvuxmfn8f" path="res://game/objects/fireplace.tscn" id="3_tuvkr"] [ext_resource type="PackedScene" uid="uid://pw8xqtpauy57" path="res://game/objects/tutorial/jump_kbd.tscn" id="4_crrvt"] [ext_resource type="PackedScene" uid="uid://g0n5npbt3hb2" path="res://game/doors/circle_door.tscn" id="4_yrqjd"] @@ -21,7 +21,7 @@ [ext_resource type="PackedScene" uid="uid://cutjutvd8dqqc" path="res://game/characters/black_enemy.tscn" id="19_ohfy3"] [ext_resource type="PackedScene" uid="uid://bv2gahb4wxgb1" path="res://game/characters/white_enemy.tscn" id="20_rt0xo"] -[node name="Level1" instance=ExtResource("1_15okj")] +[node name="TestLevel" instance=ExtResource("1_15okj")] script = ExtResource("2_hj65a") [node name="Door" parent="Doors" index="0" instance=ExtResource("11_g1txn")] diff --git a/game/managers/save_manager.gd b/game/managers/save_manager.gd new file mode 100644 index 0000000..913e98b --- /dev/null +++ b/game/managers/save_manager.gd @@ -0,0 +1,40 @@ +class_name CSaveManager +extends Node + + +@export var save_file_path := "user://save.bin" +@export var save_file_pass := "save_file_data" + + +const CATEGORY_GAME = "game" +const GAME_COMPLETED_LEVELS = "completed_levels" + + +var completed_levels := 0 + +var _save_file: ConfigFile + + +func _ready() -> void: + _save_file = ConfigFile.new() + _load() + + +func save() -> void: + _save_file.set_value(CATEGORY_GAME, GAME_COMPLETED_LEVELS, completed_levels) + _save_file.save_encrypted_pass(save_file_path, save_file_pass) + + + + +func _load() -> void: + if _save_file.load_encrypted_pass(save_file_path, save_file_pass) == OK: + _process_save_file() + + save() + + +func _process_save_file() -> void: + completed_levels = _save_file.get_value( + CATEGORY_GAME, GAME_COMPLETED_LEVELS, completed_levels + ) diff --git a/game/managers/save_manager.gd.uid b/game/managers/save_manager.gd.uid new file mode 100644 index 0000000..fed3cc4 --- /dev/null +++ b/game/managers/save_manager.gd.uid @@ -0,0 +1 @@ +uid://d2vtovrykiwd diff --git a/game/managers/save_manager.tscn b/game/managers/save_manager.tscn new file mode 100644 index 0000000..f7762a5 --- /dev/null +++ b/game/managers/save_manager.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://cuqxyst3qlvmw"] + +[ext_resource type="Script" uid="uid://d2vtovrykiwd" path="res://game/managers/save_manager.gd" id="1_c6jim"] + +[node name="SaveManager" type="Node"] +script = ExtResource("1_c6jim") diff --git a/game/menu/completion_menu.gd b/game/menu/completion_menu.gd index e635bea..dfdbd3b 100644 --- a/game/menu/completion_menu.gd +++ b/game/menu/completion_menu.gd @@ -2,28 +2,39 @@ class_name CompletionMenu extends Control -@export var next_level : PackedScene - - @onready var next_level_button : Button = $%NextLevelButton +@onready var main_menu_button : Button = $%MainMenuButton @onready var focus_timer : Timer = $FocusTimer +func _get_next_level(remove := false) -> PackedScene: + var next_level : PackedScene = get_tree().get_meta(AbstractLevel.NEXT_LEVEL_META, null) + + if remove: + get_tree().remove_meta(AbstractLevel.NEXT_LEVEL_META) + + return next_level + + func _on_next_level_button_pressed() -> void: get_tree().paused = false - - if next_level != null: - get_tree().change_scene_to_packed(next_level) - else: - get_tree().change_scene_to_file("res://game/menu/main_menu.tscn") + get_tree().change_scene_to_packed(_get_next_level(true)) + + +func _on_main_menu_button_pressed() -> void: + get_tree().paused = false + get_tree().change_scene_to_file("res://game/menu/main_menu.tscn") func _on_visibility_changed() -> void: if visible: + next_level_button.visible = _get_next_level() != null if focus_timer != null: focus_timer.start() func _on_focus_timer_timeout() -> void: - if next_level_button != null: - next_level_button.grab_focus() + if next_level_button != null and next_level_button.visible: + next_level_button.grab_focus() + elif main_menu_button != null: + main_menu_button.grab_focus() diff --git a/game/menu/completion_menu.tscn b/game/menu/completion_menu.tscn index 3cd7e78..4305c1b 100644 --- a/game/menu/completion_menu.tscn +++ b/game/menu/completion_menu.tscn @@ -48,9 +48,15 @@ unique_name_in_owner = true layout_mode = 2 text = "Next level" +[node name="MainMenuButton" type="Button" parent="MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Main menu" + [node name="FocusTimer" type="Timer" parent="."] one_shot = true [connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"] [connection signal="pressed" from="MarginContainer/VBoxContainer/NextLevelButton" to="." method="_on_next_level_button_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/MainMenuButton" to="." method="_on_main_menu_button_pressed"] [connection signal="timeout" from="FocusTimer" to="." method="_on_focus_timer_timeout"] diff --git a/game/menu/game_over_menu.gd b/game/menu/game_over_menu.gd index 0a74275..b768f7c 100644 --- a/game/menu/game_over_menu.gd +++ b/game/menu/game_over_menu.gd @@ -2,10 +2,16 @@ class_name GameOverMenu extends Control +@onready var retry_button : Button = $%RetryButton @onready var main_menu_button : Button = $%MainMenuButton @onready var focus_timer : Timer = $FocusTimer +func _on_retry_button_pressed() -> void: + get_tree().paused = false + get_tree().change_scene_to_file(get_tree().current_scene.scene_file_path) + + func _on_main_menu_button_pressed() -> void: get_tree().paused = false get_tree().change_scene_to_file("res://game/menu/main_menu.tscn") @@ -19,4 +25,4 @@ func _on_visibility_changed() -> void: func _on_focus_timer_timeout() -> void: if main_menu_button != null: - main_menu_button.grab_focus() + retry_button.grab_focus() diff --git a/game/menu/game_over_menu.tscn b/game/menu/game_over_menu.tscn index 9f8eb88..a5b24ea 100644 --- a/game/menu/game_over_menu.tscn +++ b/game/menu/game_over_menu.tscn @@ -43,6 +43,11 @@ layout_mode = 2 text = "Game over!" horizontal_alignment = 1 +[node name="RetryButton" type="Button" parent="MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Retry" + [node name="MainMenuButton" type="Button" parent="MarginContainer/VBoxContainer"] unique_name_in_owner = true layout_mode = 2 @@ -52,5 +57,6 @@ text = "Main menu" one_shot = true [connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/RetryButton" to="." method="_on_retry_button_pressed"] [connection signal="pressed" from="MarginContainer/VBoxContainer/MainMenuButton" to="." method="_on_main_menu_button_pressed"] [connection signal="timeout" from="FocusTimer" to="." method="_on_focus_timer_timeout"] diff --git a/game/menu/level_selection.gd b/game/menu/level_selection.gd new file mode 100644 index 0000000..a9d7214 --- /dev/null +++ b/game/menu/level_selection.gd @@ -0,0 +1,37 @@ +class_name LevelSelection +extends Control + + +@export var levels : Array[PackedScene] = [] + + +@onready var grid : GridContainer = $%GridContainer + + +func _ready() -> void: + levels = levels.filter(func(item: PackedScene) -> bool: return item != null) + + for i in range(levels.size()): + var level := levels[i] + var next_level := levels[i+1] if i+1 < levels.size() else null + var disable := SaveManager.completed_levels < i + + var button : Button = Button.new() + button.text = "Level %d" % (i + 1) + button.disabled = disable + button.focus_mode = Control.FOCUS_NONE if disable else Control.FOCUS_ALL + grid.add_child(button) + button.pressed.connect(_on_level_selected.bind(i, level, next_level)) + + if i == 0: + button.grab_focus() + + +func _on_level_selected(index: int, level: PackedScene, next_level: PackedScene) -> void: + get_tree().set_meta(AbstractLevel.CURRENT_LEVEL_INDEX, index) + get_tree().set_meta(AbstractLevel.NEXT_LEVEL_META, next_level) + get_tree().change_scene_to_packed(level) + + +func _on_main_menu_button_pressed() -> void: + get_tree().change_scene_to_file("res://game/menu/main_menu.tscn") diff --git a/game/menu/level_selection.gd.uid b/game/menu/level_selection.gd.uid new file mode 100644 index 0000000..6a5079f --- /dev/null +++ b/game/menu/level_selection.gd.uid @@ -0,0 +1 @@ +uid://47impoi24o7g diff --git a/game/menu/level_selection.tscn b/game/menu/level_selection.tscn new file mode 100644 index 0000000..d00f014 --- /dev/null +++ b/game/menu/level_selection.tscn @@ -0,0 +1,45 @@ +[gd_scene load_steps=4 format=3 uid="uid://cdw72hewwmypn"] + +[ext_resource type="Theme" uid="uid://bh56my8b2htnr" path="res://themes/menu.tres" id="1_5cn0b"] +[ext_resource type="Script" uid="uid://47impoi24o7g" path="res://game/menu/level_selection.gd" id="1_fb5q3"] +[ext_resource type="PackedScene" uid="uid://bmn74wc2vophn" path="res://game/levels/test_level.tscn" id="3_3oqjo"] + +[node name="LevelSelection" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_5cn0b") +script = ExtResource("1_fb5q3") +levels = Array[PackedScene]([ExtResource("3_3oqjo"), ExtResource("3_3oqjo")]) + +[node name="MarginContainer" type="MarginContainer" 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="MarginContainer"] +layout_mode = 2 +alignment = 1 + +[node name="GridContainer" type="GridContainer" parent="MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 6 +columns = 4 + +[node name="Control" type="Control" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="MainMenuButton" type="Button" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Main menu" + +[connection signal="pressed" from="MarginContainer/VBoxContainer/MainMenuButton" to="." method="_on_main_menu_button_pressed"] diff --git a/game/menu/main_menu.gd b/game/menu/main_menu.gd index 7dbcb58..1d54600 100644 --- a/game/menu/main_menu.gd +++ b/game/menu/main_menu.gd @@ -22,7 +22,7 @@ func _setup_neighbors() -> void: func _on_start_button_pressed() -> void: - get_tree().change_scene_to_file("res://game/levels/level_1.tscn") + get_tree().change_scene_to_file("res://game/menu/level_selection.tscn") func _on_options_button_pressed() -> void: diff --git a/project.godot b/project.godot index d111041..70f02a0 100644 --- a/project.godot +++ b/project.godot @@ -20,6 +20,7 @@ config/icon="res://icon.svg" SettingsManager="*res://game/managers/settings_manager.tscn" SoundManager="*res://game/managers/sound_manager.tscn" +SaveManager="*res://game/managers/save_manager.tscn" [debug]