diff --git a/src/logic-scenes/boot/boot.gd b/src/logic-scenes/boot/boot.gd index 70ca7deb..5889bd4c 100644 --- a/src/logic-scenes/boot/boot.gd +++ b/src/logic-scenes/boot/boot.gd @@ -1,5 +1,4 @@ extends Node -func _ready() -> void: +func _enter_tree() -> void: Main.normal_boot = true # Tell the system this is a normal game start - \ No newline at end of file diff --git a/src/singletons/global_state.gd b/src/singletons/global_state.gd index c4cfdbf8..67c9cfbc 100644 --- a/src/singletons/global_state.gd +++ b/src/singletons/global_state.gd @@ -3,9 +3,24 @@ class_name GlobalState #region configuration signal settings_changed -var _settings_initialized +signal savegame_changed +var _settings_initialized: bool = false +var _savegame_initialized: bool = false + +var all_ready: bool: + get(): + return _settings_initialized and _savegame_initialized + +func saves_loaded(save: SaveGame): + save_game = save + _savegame_initialized = true + var active_room: Room +var save_game: SaveGame: + set(save): + save_game = save + savegame_changed.emit() signal environment_settings_changed # FIXME find a better way to switch fonts and maybe emit the theme_changed signal! @@ -167,10 +182,10 @@ func load_user_settings(): file.close() var parsed: Dictionary = JSON.parse_string(raw_json) - for kategory in parsed.values(): - for key in kategory.keys(): + for kategory in parsed.keys(): + for key in parsed[kategory].keys(): if key in self: - set(key, parsed[key]) + set(key, parsed[kategory][key]) else: if OS.has_feature("macos"): @@ -224,15 +239,14 @@ func save_settings(): var file := FileAccess.open(user_settings_path, FileAccess.WRITE) file.store_string(JSON.stringify(out_dict)) file.close() - - _settings_initialized = true + settings_changed.emit() func _ready(): + load_user_settings() await get_tree().process_frame music_volume = music_volume - #region focus handling (called staging to avoid name colisions) # CAUTION: scene_reference directly accesses stage list to play sequences. diff --git a/src/singletons/main/main.gd b/src/singletons/main/main.gd index 5805b2d1..ac7a55df 100644 --- a/src/singletons/main/main.gd +++ b/src/singletons/main/main.gd @@ -1,50 +1,49 @@ -extends Node +extends Control var normal_boot : bool = false +@onready var menu_animation: AnimationNodeStateMachinePlayback = %MenuAnimationTree.get("parameters/playback") @export_file(".tscn") var youth_room_path: String @export_file(".tscn") var transition_room_path: String @export_file(".tscn") var adulthood_room_path: String @export_file(".tscn") var ending_path: String -@onready var curtain: Curtain = %Curtain +@onready var curtain: Panel = %Curtain @onready var credits_roll: Control = %CreditsRoll @onready var main_menu: MainMenu = %MainMenu @onready var pause_menu: PauseMenu = %PauseMenu @onready var room_paths := { - State.rooms.NULL: youth_room_path, # Maybe Draven story? - State.rooms.YOUTH: youth_room_path, - State.rooms.TRANSITION: transition_room_path, - State.rooms.ADULTHOOD: adulthood_room_path, - State.rooms.ENDING: ending_path + Room.ids.NULL: youth_room_path, # Maybe Draven story? + Room.ids.YOUTH: youth_room_path, + Room.ids.TRANSITION: transition_room_path, + Room.ids.ADULTHOOD: adulthood_room_path, + Room.ids.ENDING: ending_path } -enum AppState {BOOT, MENU, PLAY, PAUSE, CREDITS} +enum AppState {INIT, LOADING, MENU, PLAY, PAUSE, CREDITS} -var state: AppState = AppState.BOOT: +var state: AppState = AppState.INIT: set(value): - state = value print("main.gd: app_state changing to: %s" % str(state)) - match state: - AppState.BOOT: - credits_roll.hide() - main_menu.hide() - pause_menu.hide() + match value: + AppState.INIT: + pass + AppState.LOADING: + %MenuAnimationTree["parameters/conditions/loading_done"] = false AppState.MENU: - credits_roll.hide() - pause_menu.hide() + menu_animation.travel("loading_menu") await main_menu.execute() AppState.PLAY: - pass + menu_animation.travel("start_game") + await_ui_clear() + hide() AppState.PAUSE: - credits_roll.hide() - main_menu.hide() - pause_menu.appear() + menu_animation.travel("reveal_pause_menu") AppState.CREDITS: - main_menu.hide() - pause_menu.hide() - credits_roll.play() + menu_animation.travel("credits_roll") + + state = value func _enter_tree() -> void: print("main.gd: _enter_tree()") @@ -53,51 +52,80 @@ func _ready() -> void: print("main.gd: _ready()") main_menu.continue_button.pressed.connect(func(): state = AppState.PLAY) main_menu.credits_button.pressed.connect(func(): state = AppState.CREDITS) - - #TODO: Load the last savegame(?) - await %Loading.stop() - + + #await get_tree().process_frame + await await_boot_completed() + if normal_boot: print("main.gd: normal boot (loading last save and showing main menu)") - state = AppState.MENU + call_deferred("start_menu") + else: print("main.gd: direct boot (hiding menus and entering main loop)") state = AppState.PLAY +func start_menu(): + + if Steam.resume_from_steamdeck(): + start_game() + + initialise_room(State.save_game.current_room) + state = AppState.MENU + -func start_game(save: SaveGame) -> void: +func await_boot_completed(): + print("main.gd: Awaiting Boot Completion ...") + while not State.all_ready: + await get_tree().process_frame + print("main.gd: Boot Completed.") + +func await_ui_clear(): + print("main.gd: Awaiting Menu Clear ...") + while not menu_animation.get_current_node() == "start_game": + await get_tree().process_frame + print("main.gd: Menu Cleared.") + +func start_game(save: SaveGame = State.save_game) -> void: print("main.gd: play_game()") - var room_path := room_paths.get(save.current_room, youth_room_path) as String - + initialise_room(save.current_room) + State.active_room.play() state = AppState.PLAY - while room_path: - await _load_room(room_path) - room_path = await State.room.play() - # Ending? Roll credits? +func is_game_active() -> bool: + return state == AppState.PLAY or state == AppState.PAUSE + + +func initialise_room(room_id: Room.ids): + if State.active_room: + if State.active_room.id == room_id: + return + else: + menu_animation.travel("change_savegame") + _load_room(room_paths.get(room_id, youth_room_path) as String) + func _load_room(scene_path: String) -> void: - await curtain.close() - %Loading.play() - - if State.room: - State.room.unload() - State.room.queue_free() - State.room = null - ResourceLoader.load_threaded_request(scene_path, "PackedScene", true) + + await get_tree().create_timer(0.1).timeout + + if State.active_room: + State.active_room.unload() + State.active_room.queue_free() + State.active_room = null + while true: await get_tree().process_frame var load_state := ResourceLoader.load_threaded_get_status(scene_path) match load_state: ResourceLoader.THREAD_LOAD_LOADED: var next_scene := ResourceLoader.load_threaded_get(scene_path) as PackedScene - State.room = next_scene.instantiate() as Room - %Stage.add_child(State.room) + State.active_room = next_scene.instantiate() as Room + %Stage.add_child(State.active_room) await get_tree().process_frame - %Loading.stop() + %MenuAnimationTree["parameters/conditions/loading_done"] = true return ResourceLoader.THREAD_LOAD_FAILED: push_error("Failed to load room.") @@ -109,7 +137,10 @@ func _load_room(scene_path: String) -> void: var last_mode := DisplayServer.WINDOW_MODE_WINDOWED func _unhandled_input(event: InputEvent) -> void: - #if event.is_action_type(): print_debug("Unhandled Input", event) + #if event.is_actionxxx_type(): print_debug("Unhandled Input", event) + + if event.is_action_pressed("ui_pause") and state == AppState.PLAY: + state = AppState.PAUSE if not Engine.is_editor_hint(): if event.is_action_pressed("toggle_fullscreen"): diff --git a/src/ui/menu_main/save_game_list.gd b/src/ui/menu_main/save_game_list.gd index 45378f8b..c139ad00 100644 --- a/src/ui/menu_main/save_game_list.gd +++ b/src/ui/menu_main/save_game_list.gd @@ -19,12 +19,14 @@ func _validate_property(property: Dictionary) -> void: var _tween: Tween = null + func _ready() -> void: _load_games() hide() set_process_input(false) back_button.pressed.connect(cancel) + func _ensure_directory() -> void: var dir := DirAccess.open(State.user_saves_path) @@ -49,8 +51,7 @@ func _load_games(): # Skip invalid/empty saves if save != null and not save.is_empty: saves.append(save) - - _sort_saves() + State.saves_loaded(get_most_recent_save()) _rebuild_buttons() @@ -69,6 +70,7 @@ func _get_slot_number(save: SaveGame) -> int: # Find this save's position in the creation-order list return saves_by_creation.find(save) + 1 + func _rebuild_buttons() -> void: save_buttons = [] for child in list_container.get_children(): @@ -93,6 +95,7 @@ func _rebuild_buttons() -> void: func _on_game_picked(id: int) -> void: _picked.emit(saves[id]) + func _on_delete_requested(id: int) -> void: var save_to_delete := saves[id] var save_path := save_to_delete.file_name @@ -116,23 +119,28 @@ func _on_delete_requested(id: int) -> void: var focus_index := mini(id, save_buttons.size() - 1) save_buttons[focus_index].grab_focus() + func get_most_recent_save() -> SaveGame: _sort_saves() return saves[0] if saves.size() > 0 else SaveGame.new() + func has_more_saves() -> bool: for save in saves: if save != State.save_game: return true return false + func _gui_input(event: InputEvent) -> void: if event.is_action_pressed("ui_cancel"): cancel() + func cancel()->void: _picked.emit(State.save_game) + # This function is called when the user us supposed to choose a slot to load or create a new game. func pick_save_slot() -> SaveGame: await open() @@ -140,6 +148,7 @@ func pick_save_slot() -> SaveGame: await close() return result + # TODO: ugh, godot tweens are the wurst func open() -> void: show() @@ -156,6 +165,7 @@ func open() -> void: _tween.tween_property(self, "modulate", Color.WHITE, 0.5) await _tween.finished + func close() -> void: if _tween != null: _tween.kill()