Arcade/kiosk name flow, free cursor in menu and portable Linux export

Every game over unlocks the profile and frees the name, so the menu always asks
for a new player name: the field is empty at every boot, restart or game over and
the game does not start without a name (no anonymous scores). The player identity
is derived from the entered name (deterministic FNV-1a, 16 hex chars valid for the
public API) and the profile keeps the best score per name. Backend untouched.

Mouse: player._ready() captured the pointer before the menu existed, so the start
screen was not clickable. The cursor now always starts visible; the capture happens
only in player.begin_play(), called by start_game(). New `playing` flag gates Esc
and the Web click capture to the actual match.

Linux export preset: single-file x86_64 binary with the PCK embedded, no
libstdc++ dependency, official 4.7.1 template (glibc >= 2.28). Notes and the
export_presets.cfg `#`-is-not-a-comment trap documented in docs/KIOSK-ARCADE.md.

Tests: profile 30/30, start screen 15/15 (cursor free at menu, captured only in
game), boss 50/50, rebalance 14/14, touch 14 PASS, API 42/42, all other suites pass.
This commit is contained in:
enne2
2026-10-24 12:50:03 +02:00
parent 21a1d5bf79
commit cb14fd5400
9 changed files with 397 additions and 106 deletions
+19 -5
View File
@@ -132,11 +132,17 @@ func _on_start_pressed(player_name: String) -> void:
start_game(player_name, true)
func start_game(player_name: String, save_profile := true) -> void:
profile["name"] = player_name
# Modalita' arcade/kiosk: l'identita' nasce dal nome scritto in questo momento,
# non dal profilo precedente. Stesso nome = stesso id (quindi stessa persona anche
# dopo un riavvio); nome diverso = identita' diversa.
profile["name"] = Profile.sanitize_name(player_name)
profile["player_id"] = Profile.player_id_for_name(profile["name"])
profile["best"] = Profile.best_for_name(profile, profile["name"])
if leaderboard != null:
leaderboard.player_id = profile["player_id"]
if save_profile and not boss_simulation:
Profile.save_profile(profile)
if not player.touch_mode:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
player.begin_play()
if touch != null:
touch.enabled = true
if start_screen != null:
@@ -155,11 +161,19 @@ func _on_player_died() -> void:
if score_submitted:
return
score_submitted = true
var played_name := str(profile.get("name", ""))
if int(score) > int(profile.get("best", 0)):
profile["best"] = score
Profile.record_best(profile, played_name, int(score))
# Game over: profilo sbloccato e nome liberato. Al ritorno al menu (R o pulsante
# Ricomincia) il campo e' vuoto e viene richiesto il nome della partita successiva.
Profile.unlock_name(profile)
if not boss_simulation:
Profile.save_profile(profile)
if start_screen != null:
start_screen.reset_for_new_player()
player.set_death_note("invio del punteggio in corso...")
leaderboard.submit(profile["name"], score)
# Il nome della partita appena conclusa, non quello liberato per la partita nuova.
leaderboard.submit(played_name, score)
func _on_score_submitted(info: Dictionary) -> void:
var total := int(info.get("total", 0))