diff --git a/scripts/save_system.gd b/autoloads/save_system.gd similarity index 60% rename from scripts/save_system.gd rename to autoloads/save_system.gd index 34245f1..68daa8b 100644 --- a/scripts/save_system.gd +++ b/autoloads/save_system.gd @@ -1,4 +1,3 @@ -class_name SaveSystem extends Node @export var save_path: String = "user://savegame.save" @@ -15,18 +14,32 @@ func save_game(): var file := FileAccess.open(save_path, FileAccess.WRITE) file.store_var(save_data) file.close() + print("Game saved to: ", save_path) -func load_game(): +func load_game() -> bool: if not FileAccess.file_exists(save_path): - return + return false var file := FileAccess.open(save_path, FileAccess.READ) var save_data: Dictionary = file.get_var() file.close() if save_data.has("version") and save_data["version"] != version: print("Save file version mismatch. Expected: ", version, ", Found: ", save_data["version"]) - return + return false + print("save data: ", save_data) gm.player_state = save_data["player_state"] - gm.unlock_skills(gm.player_state["unlocked_skills"]) \ No newline at end of file + print("Player state loaded: ", gm.player_state) + var skills: Array[String] = [] + for skill_name in gm.player_state["unlocked_skills"]: + skills.append(skill_name) + + print("Newly unlocked skills: ", skills) + gm.unlock_skills(skills) + print("Game loaded from: ", save_path) + return true + + +func check_save_exists() -> bool: + return FileAccess.file_exists(save_path) diff --git a/scripts/save_system.gd.uid b/autoloads/save_system.gd.uid similarity index 100% rename from scripts/save_system.gd.uid rename to autoloads/save_system.gd.uid diff --git a/project.godot b/project.godot index 225f9fc..43e767d 100644 --- a/project.godot +++ b/project.godot @@ -36,6 +36,7 @@ Console="*res://addons/console/console.gd" AchievementsManager="*res://objects/achievements.tscn" ConfigFileHandler="*res://autoloads/config_file_handler.gd" UiManager="*res://autoloads/ui_manager.gd" +SaveSystem="*res://autoloads/save_system.gd" [debug] diff --git a/scenes/level_village_2.tscn b/scenes/level_village_2.tscn index 7889ed6..db79b6f 100644 --- a/scenes/level_village_2.tscn +++ b/scenes/level_village_2.tscn @@ -75,8 +75,9 @@ player_health = NodePath("../../Brick Player/HealthComponent") current_level = SubResource("Resource_bqjcg") nodes_to_disable = [NodePath("../../Brick Player")] -[node name="Marketplace" parent="UI Layer" index="3" node_paths=PackedStringArray("skill_unlocker")] +[node name="Marketplace" parent="UI Layer" index="3" node_paths=PackedStringArray("skill_unlocker", "components_to_disable")] skill_unlocker = NodePath("../../Brick Player/SkillUnlockerComponent") +components_to_disable = [NodePath("../../Brick Player")] [node name="Global Light" parent="." instance=ExtResource("4_wykfl")] diff --git a/scripts/components/exit_door_component.gd b/scripts/components/exit_door_component.gd index d0b6a4b..96f4cfc 100644 --- a/scripts/components/exit_door_component.gd +++ b/scripts/components/exit_door_component.gd @@ -36,4 +36,4 @@ func on_exit_area_body_entered(_body: Node2D) -> void: func go_to_next_level() -> void: - gm.try_to_go_to_next_level() \ No newline at end of file + gm.on_level_complete() \ No newline at end of file diff --git a/scripts/components/player_death.gd b/scripts/components/player_death.gd index cbca0f3..0e37528 100644 --- a/scripts/components/player_death.gd +++ b/scripts/components/player_death.gd @@ -17,5 +17,5 @@ func _on_health_component_on_death() -> void: effect.scale = Vector2(1.5, 1.5) gm.remove_lives(1) - gm.set_coins(0) + gm.reset_current_session_state() diff --git a/scripts/components/score.gd b/scripts/components/score.gd index 2bc01d5..bd24932 100644 --- a/scripts/components/score.gd +++ b/scripts/components/score.gd @@ -6,10 +6,10 @@ extends Node func _ready(): await get_tree().process_frame - var coins = get_tree().get_nodes_in_group("coins") + var coins := get_tree().get_nodes_in_group("coins") for coin in coins: - coin.connect("collected", on_collected) + coin.collected.connect(on_collected) func on_collected(amount: int, type: CollectableResource.CollectableType) -> void: @@ -17,4 +17,4 @@ func on_collected(amount: int, type: CollectableResource.CollectableType) -> voi return if type != CollectableResource.CollectableType.COIN: return - game_manager.add_coins(amount) + game_manager.current_session_state["coins_collected"] += amount diff --git a/scripts/components/skill_unlocker_component.gd b/scripts/components/skill_unlocker_component.gd index 95c4029..143194a 100644 --- a/scripts/components/skill_unlocker_component.gd +++ b/scripts/components/skill_unlocker_component.gd @@ -6,6 +6,11 @@ extends Node @onready var game_manager: GM = $"/root/GameManager" + +func has_enough_coins(amount: int) -> bool: + return game_manager and game_manager.get_coins() >= amount + + func try_unlock_skill(skill_data: SkillData) -> bool: if not game_manager: return false @@ -13,11 +18,11 @@ func try_unlock_skill(skill_data: SkillData) -> bool: if game_manager.is_skill_unlocked(skill_data.name): return false - if game_manager.get_coins() < skill_data.cost: + if not has_enough_coins(skill_data.cost): return false game_manager.remove_coins(skill_data.cost) - game_manager.unlock_skill(skill_data.name) + game_manager.current_session_state["unlocked_skills"].append(skill_data.name) skill_manager.add_skill(skill_data) return true @@ -30,9 +35,4 @@ func unlock_all_skills() -> void: skills.append(skill.name) game_manager.unlock_skills(skills) - skill_manager.apply_unlocked_skills() - - -func _input(event: InputEvent) -> void: - if event.is_action_pressed("unlock_skills"): - unlock_all_skills() \ No newline at end of file + skill_manager.apply_unlocked_skills() \ No newline at end of file diff --git a/scripts/game_manager.gd b/scripts/game_manager.gd index a6e75db..76d39af 100644 --- a/scripts/game_manager.gd +++ b/scripts/game_manager.gd @@ -9,11 +9,18 @@ extends Node "unlocked_skills": [], "current_level": 0, "unlocked_levels": [], + "completed_levels": [], } var nodes_in_scene := [] +var current_session_state := { + "coins_collected": 0, + "skills_unlocked": [], + } + + func _enter_tree() -> void: get_tree().node_added.connect(on_node_added) get_tree().node_removed.connect(on_node_removed) @@ -64,6 +71,7 @@ func get_kid_nodes() -> Array[CollectableComponent]: func add_coins(amount: int) -> void: player_state["coins"] += amount + player_state["coins"] = max(0, player_state["coins"]) func set_coins(amount: int) -> void: @@ -71,11 +79,20 @@ func set_coins(amount: int) -> void: func get_coins() -> int: - return player_state["coins"] + return player_state["coins"] + current_session_state["coins_collected"] func remove_coins(amount: int) -> void: - player_state["coins"] -= amount + var session_coins = current_session_state["coins_collected"] + + if amount <= session_coins: + current_session_state["coins_collected"] -= amount + else: + var remaining_amount = amount - session_coins + current_session_state["coins_collected"] = 0 + player_state["coins"] = max(0, player_state["coins"] - remaining_amount) + + player_state["coins"] = max(0, player_state["coins"]) func add_lives(amount: int) -> void: @@ -95,7 +112,7 @@ func get_lives() -> int: func is_skill_unlocked(skill_name: String) -> bool: - return skill_name in player_state["unlocked_skills"] + return skill_name in player_state["unlocked_skills"] or skill_name in current_session_state["skills_unlocked"] func unlock_skill(skill_name: String) -> void: @@ -118,6 +135,7 @@ func reset_player_state() -> void: "coins": 0, "lives": 3, "unlocked_skills": [], + "completed_levels": [], } @@ -132,6 +150,19 @@ func try_to_go_to_next_level() -> void: get_tree().change_scene_to_packed(level_scenes[next_level]) + +func mark_level_complete(level_index: int) -> void: + unlock_level(level_index + 1) + if level_index not in player_state["completed_levels"]: player_state["completed_levels"].append(level_index) + + +func reset_current_session_state() -> void: + current_session_state = { + "coins_collected": 0, + "skills_unlocked": [], + } + + func quit_game() -> void: get_tree().quit() @@ -152,9 +183,24 @@ func start_new_game() -> void: func continue_game() -> void: - # todo: load player state from save file + if not SaveSystem.load_game(): + printerr("Failed to load game. Starting a new game instead.") + start_new_game() + return if player_state["current_level"] < level_scenes.size(): get_tree().change_scene_to_packed(level_scenes[player_state["current_level"]]) else: - printerr("No levels unlocked to continue.") \ No newline at end of file + printerr("No levels unlocked to continue.") + + +func on_level_complete() -> void: + var level_index = player_state["current_level"] + mark_level_complete(level_index) + add_coins(current_session_state["coins_collected"]) + for skill_name in current_session_state["skills_unlocked"]: + unlock_skill(skill_name) + + reset_current_session_state() + try_to_go_to_next_level() + SaveSystem.save_game() diff --git a/scripts/ui/game_over_screen.gd b/scripts/ui/game_over_screen.gd index c6806e3..cc19c5b 100644 --- a/scripts/ui/game_over_screen.gd +++ b/scripts/ui/game_over_screen.gd @@ -23,6 +23,7 @@ func on_restart_button_pressed() -> void: return gm.reset_player_state() + gm.reset_current_session_state() get_tree().reload_current_scene() diff --git a/scripts/ui/main_menu.gd b/scripts/ui/main_menu.gd index b6b479c..4d73087 100644 --- a/scripts/ui/main_menu.gd +++ b/scripts/ui/main_menu.gd @@ -27,6 +27,11 @@ func _ready() -> void: if version_label: version_label.text = "v. " + ProjectSettings.get_setting("application/config/version") + if not SaveSystem.check_save_exists() and continue_button: + continue_button.disabled = true + else: + continue_button.disabled = false + func _on_new_game_button_pressed() -> void: if gm: @@ -54,4 +59,4 @@ func _on_settings_button_pressed() -> void: func _on_credits_button_pressed() -> void: if credits_control: - UiManager.push_screen(credits_control) \ No newline at end of file + UiManager.push_screen(credits_control)