Session shell - Startup venue menu (src/game/ui/start_menu.gd): keyboard + mouse, replaces the live-shift boot and V as the player-facing venue path (V stays a debug bind). - Pause overlay on Esc (src/game/ui/pause_overlay.gd): resume, restart shift, settings, quit to menu. - Settings panel (src/game/ui/settings_panel.gd): mouse look sensitivity and HUD font scale, persisted via Save into user://settings.json and loaded on boot. - Shift review "Run another shift" now returns to the venue menu. - New smoke: src/test/smoke_session_shell.gd (menu -> shift -> pause -> settings -> menu), passes headless and windowed. World and presentation - Skyline backdrop restored with a transparent asset and an alpha-scissor unshaded material; fixes the opaque black slab from the earlier attempt and the unimported texture path. - Steps and landing are a StaticBody3D with per-tread colliders, so the guard and NPCs walk up instead of clipping through. - Marquee Label3D scaled up (72 -> 160) so STEP LINE reads from the sidewalk. - Focus torus and name tag enlarged for 2-3 m readability. - Routine traffic despawns after an idle lifetime (NpcAvatar.ephemeral), so the 16-NPC cap no longer fills with standers; authored events stay for the shift. Docs and tests - PLAN.md test/CI section corrected to the custom headless runner and Gitea Actions. - README controls table, smoke checklist, test count, and status lists refreshed. - TODO/DONE/MEMORY updated; completed sections moved to DONE.md. - Headless suite 77 -> 88 passing (settings round-trip, step colliders, skyline alpha, session-shell UI behavior, traffic lifetime).
54 lines
2.2 KiB
GDScript
54 lines
2.2 KiB
GDScript
extends RefCounted
|
|
## Headless tests for shift-result persistence (Save autoload, user:// JSON).
|
|
|
|
func _save_instance() -> Object:
|
|
var save_script: GDScript = load("res://src/autoload/save.gd")
|
|
return save_script.new()
|
|
|
|
func test_save_round_trip() -> bool:
|
|
var save := _save_instance()
|
|
var review := {
|
|
"scenario_id": "hollywood_building",
|
|
"reputation": {"rating": 82.5, "grade": "B"},
|
|
"spawned_count": 7,
|
|
"completed_at_ms": Time.get_ticks_msec(),
|
|
}
|
|
var err: int = save.save_shift_result(review)
|
|
var a := TestUtils.check_eq(err, OK, "save succeeds")
|
|
var results: Array = save.load_shift_results()
|
|
var b := TestUtils.check(results.size() > 0, "history non-empty after save")
|
|
var last: Dictionary = results[results.size() - 1]
|
|
var c := TestUtils.check_eq(last.get("scenario_id", ""), "hollywood_building", "round-trips scenario id")
|
|
var d := TestUtils.check_eq(float(last.get("reputation", {}).get("rating", 0.0)), 82.5, "round-trips rating")
|
|
save.free()
|
|
return a and b and c and d
|
|
|
|
func test_load_returns_array_when_empty_or_missing() -> bool:
|
|
# The tester home may already contain results; this only asserts shape safety.
|
|
var save := _save_instance()
|
|
var results: Array = save.load_shift_results()
|
|
var ok := TestUtils.check(results is Array, "load_shift_results always returns Array")
|
|
save.free()
|
|
return ok
|
|
|
|
func test_settings_round_trip() -> bool:
|
|
var save := _save_instance()
|
|
var err: int = save.save_settings({"look_sensitivity": 0.0035, "hud_font_scale": 1.25})
|
|
var a := TestUtils.check_eq(err, OK, "save_settings succeeds")
|
|
var loaded: Dictionary = save.load_settings()
|
|
var b := TestUtils.check(is_equal_approx(float(loaded.get("look_sensitivity", 0.0)), 0.0035), "round-trips sensitivity")
|
|
var c := TestUtils.check(is_equal_approx(float(loaded.get("hud_font_scale", 0.0)), 1.25), "round-trips font scale")
|
|
# Restore defaults so later runs are not affected.
|
|
save.save_settings(save.DEFAULT_SETTINGS)
|
|
save.free()
|
|
return a and b and c
|
|
|
|
func test_settings_defaults_present() -> bool:
|
|
var save := _save_instance()
|
|
var defaults: Dictionary = save.load_settings()
|
|
var ok := TestUtils.check(
|
|
defaults.has("look_sensitivity") and defaults.has("hud_font_scale"),
|
|
"settings have documented defaults")
|
|
save.free()
|
|
return ok
|