# Modalità arcade/kiosk (macchina condivisa) e build Linux ## 1. Regola di flusso Il gioco è installato su un computer condiviso. Il profilo precedente **non viene mai dato per scontato**: * il campo del nome del menu è **sempre presente e vuoto** a ogni avvio e a ogni riavvio; * alla morte (game over) il profilo viene **sbloccato** e il nome liberato, così al ritorno al menu — `R` o pulsante `RICOMINCIA` — viene richiesto il nome della nuova partita; * senza nome scritto la partita **non parte** (`GIOCA` non emette `started`), perché un punteggio senza nome sarebbe anonimo in classifica. Il `name lock` delle versioni precedenti (`zombie_fps_locked = 1` → menu con `giocatore: Mat` e nessun campo) è ignorato: `Profile.is_name_locked()` restituisce `false` con `ALWAYS_ASK_NAME`. I profili vecchi e i valori di `localStorage` arrivano ancora letti, ma solo come chiave del miglior punteggio. **Identità** (backend invariato, API pubblica così com'è): `player_id` nasce dal nome inserito, `player_id_for_name()` = FNV-1a 32 bit con due semi → 16 caratteri esadecimali, accettato dal server (`8–64 [A-Za-z0-9_-]`). Stesso nome = stessa persona anche dopo un riavvio; nome diverso = identità diversa. Il record è tenuto **per nome** (`bests` nel file `.cfg`). Permalink del flusso: `start_screen.gd::_start()` → `main.gd::start_game()` (id e best del nome, `leaderboard.player_id`, `player.begin_play()`) → morte → `main.gd::_on_player_died()` (`record_best` con il nome della partita appena conclusa, `unlock_name`, salvataggio, `reset_for_new_player()`) → reload → menu con campo vuoto. ## 1b. Cursore del mouse Bug risolto: `player.gd::_ready()` catturava il pointer (`MOUSE_MODE_CAPTURED`) alla costruzione del giocatore, cioè **prima** del menu, quindi il cursore era già bloccato sulla schermata iniziale e i pulsanti non erano cliccabili. * `player.gd::_ready()` imposta **sempre** `MOUSE_MODE_VISIBLE`. * La cattura avviene solo in `player.gd::begin_play()`, chiamata da `main.gd::start_game()` quando si preme GIOCA. * Nuovo flag `player.playing`: `Esc` (pausa) e il click sul Web catturano il cursore solo durante la partita, mai nel menu. * In modalità touch il cursore non viene catturato (serve ai controlli on-screen). * Alla morte `_die()` lo riporta visibile, quindi la schermata di morte è cliccabile. ### File toccati | File | Modifica | | --- | --- | | `scripts/player_profile.gd` | `ALWAYS_ASK_NAME`, `player_id_for_name()`, `fnv1a32()`, `best_for_name()`, `record_best()`, `unlock_name()`; nome non presupposto, record per nome | | `scripts/start_screen.gd` | campo del nome sempre mostrato e vuoto, `reset_for_new_player()`, `GIOCA` bloccato a campo vuoto | | `scripts/main.gd` | `start_game()` deriva id e record dal nome e chiama `player.begin_play()`; `_on_player_died()` sblocca il profilo e invia il nome della partita conclusa | | `scripts/player.gd` | cursore visibile al menu, `begin_play()` con la cattura, flag `playing` | | `scripts/game_hud.gd` | promemoria dei comandi desktop aggiornato con `Alt+F` | | `tools/test_player_profile.gd` | 30 controlli (campo libero, id derivato, record per nome, game over) | | `tools/test_start_screen.gd` | 15 controlli (campo sempre richiesto, partenza bloccata a campo vuoto, campo liberato dopo il game over, cursore libero al menu e catturato solo in partita) | ## 1c. Scorciatoia Alt+F (finestra / schermo intero) * Azione in `project.godot`: `toggle_fullscreen` = `Alt+F` (rimappabile dal pannello Input Map come le altre). * Gestita in `main.gd::_input()`, quindi vale **sempre**: menu, partita (anche con il cursore catturato) e sandbox del boss. L'evento viene marcato come gestito, così il tasto non arriva al resto del gioco. * `main.gd::toggle_fullscreen()` alterna `WINDOW_MODE_FULLSCREEN` e `WINDOW_MODE_WINDOWED`; lo stato vive in `main.fullscreen`, inizializzato dalla modalità reale della finestra. Nel browser non fa nulla (il fullscreen è della pagina). * Verificato su display reale: `window_get_mode` 0 (finestra) → 3 (schermo intero) → 0. * `tools/test_fullscreen_shortcut.gd`: 8 controlli (azione e tasto mappati, alternanza, F da solo inerte, funzionamento in partita senza liberare il cursore). ## 2. Verifiche eseguite ```bash G="$HOME/.local/opt/godot-4.7.1/Godot_v4.7.1-stable_linux.x86_64" "$G" --headless --path . --script res://tools/test_player_profile.gd # 30/30 PASS "$G" --path . --rendering-method gl_compatibility --script res://tools/test_start_screen.gd # 15/15 PASS # suite precedenti, riuscite dopo la modifica: "$G" --headless --path . --script res://tools/test_boss.gd # 50/50 "$G" --headless --path . --script res://tools/test_rebalance_branding.gd # 14/14 "$G" --headless --path . --script res://tools/test_spawn_windows.gd "$G" --headless --path . --script res://tools/test_wave_growth.gd "$G" --headless --path . --script res://tools/test_gameplay_improvements.gd "$G" --headless --path . --script res://tools/test_fullscreen_shortcut.gd # 8/8 PASS python3 tools/test_scores_api.py # 42/42 ``` Nessuna modifica al backend: nessun cambiamento a nginx, Docker, API o database. La release Web pubblicata (`https://enne2.net/pub/zombie-fps/`) resta quella precedente finché non viene riesportata e rimpiantata con questa regola. ## 3. Build Linux portabile `export_presets.cfg` → `preset.1` (name `Linux`, platform `Linux`): ```ini export_filter="all_resources" exclude_filter="addons/*,tools/*,docs/*,build/*,assets/**/*.mdl,assets/half_life_manifest.json" export_path="build/linux/work_death_balance.x86_64" binary_format/embed_pck=true binary_format/architecture="x86_64" ``` * **PCK incorporato**: sul kiosk serve un solo file (`work_death_balance.x86_64`, 80.517.080 byte = template 73.470.264 + PCK 7.046.808), verificato eseguendolo da una cartella vuota (`--headless -- --smoke` → `SMOKE: wave=0 alive=0 player_hp=100.0`). * **Dipendenze dinamica minime**: `libc`, `libm`, `librt`, `libpthread`, `libdl` + `ld-linux-x86-64.so.2`. Nessuna `libstdc++` (già linkata staticamente nel template). * **Baseline glibc del template ufficiale 4.7.1**: simboli richiesti fino a `GLIBC_2.28` (`readelf -V`) → il binario gira su glibc ≥ 2.28 (distro dal 2018 in poi). È il miglior risultato ottenibile senza ricompilare l'engine. * **musl statico scartato**: un binario completamente statico non carica le librerie grafiche, perché i driver OpenGL/Vulkan linkano glibc (funziona solo headless). Non è adatto a un gioco con finestra. * Runtime richiesto sul kiosk (non linkato, caricato a runtime): driver OpenGL (Compatibility) o Vulkan (Forward+, renderer nativo attuale), X11 **o** Wayland, ALSA o PulseAudio. Sono `dlopen`, quindi non rompono la portabilità del binario. * Attenzione: `#` dentro `export_presets.cfg` non è un commento per il parser → una riga commentata dopo `binary_format/embed_pck=true` ha fatto esportare il PCK separato. ### Avvio sul kiosk La build nativa non ha un'origine di pagina, quindi la classifica va indicata esplicitamente (è la stessa API pubblica già in uso): ```bash ./work_death_balance.x86_64 --api=https://enne2.net/api/zombie-fps/ ``` Senza `--api=` la classifica è disattiva di proposito (una build di prova non deve poter scrivere alla classifica pubblica).