feat(client): game session management (#258, D-085)

Per-game save directories under user://saves/<timestamp>-<seed>/.
SessionManager autoload handles new_game(), resume_game(), quit flow.
Main menu scene with New Game / Continue / Quit buttons. Game-id
passed to server subprocess via --game-id flag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 11:45:46 +01:00
co-authored by Claude Opus 4.6
parent a7541aa27b
commit ef135830f2
9 changed files with 451 additions and 3 deletions
+50
View File
@@ -0,0 +1,50 @@
extends Control
## #258: Main menu — New Game / Continue / Quit.
## New Game: generates per-game save directory (D-085), starts game.
## Continue: loads most recent save directory (#554 full loading screen deferred).
const GAME_SCENE := "res://scenes/main.tscn"
const BG_COLOR := Color(0.05, 0.05, 0.08, 1.0)
const TITLE_COLOR := Color("#c8d0e0")
const SUBTITLE_COLOR := Color("#8890a0")
const BTN_NORMAL_COLOR := Color("#e8c547")
const BTN_DISABLED_COLOR := Color("#4a5060")
const FONT_SIZE_TITLE := 36
const FONT_SIZE_SUBTITLE := 14
const FONT_SIZE_BTN := 15
@onready var _new_game_btn: Button = $VBox/NewGameBtn
@onready var _continue_btn: Button = $VBox/ContinueBtn
@onready var _quit_btn: Button = $VBox/QuitBtn
func _ready() -> void:
_new_game_btn.pressed.connect(_on_new_game)
_continue_btn.pressed.connect(_on_continue)
_quit_btn.pressed.connect(_on_quit)
_refresh_continue_state()
func _refresh_continue_state() -> void:
var saves := SessionManager.list_game_dirs()
_continue_btn.disabled = saves.is_empty()
func _on_new_game() -> void:
SessionManager.new_game()
get_tree().change_scene_to_file(GAME_SCENE)
func _on_continue() -> void:
# #554: Full loading screen blocked until server SaveCommand lands.
# For now: automatically load the most recent game directory.
var saves := SessionManager.list_game_dirs()
if saves.is_empty():
return
SessionManager.resume_game(saves[0].game_id)
get_tree().change_scene_to_file(GAME_SCENE)
func _on_quit() -> void:
get_tree().quit()
+13
View File
@@ -121,6 +121,14 @@ func _build_ui() -> void:
close_btn.pressed.connect(close)
_container.add_child(close_btn)
# Quit to Menu button (#258: D-085 session management)
var quit_btn := Button.new()
quit_btn.text = UIStrings.get_text("menu.quit_to_menu")
quit_btn.add_theme_font_size_override("font_size", FONT_SIZE)
quit_btn.add_theme_color_override("font_color", Color("#c87040"))
quit_btn.pressed.connect(_on_quit_to_menu)
_container.add_child(quit_btn)
func _destroy_ui() -> void:
if _container:
@@ -153,6 +161,11 @@ func _draw() -> void:
HORIZONTAL_ALIGNMENT_LEFT, -1, FONT_SIZE + 2, TITLE_COLOR)
func _on_quit_to_menu() -> void:
close()
SessionManager.quit_to_menu(get_tree().root)
static func _format_db(db: float) -> String:
if db <= -40.0:
return "mute"