New remappable action `toggle_fullscreen` (Alt+F) handled in main.gd::_input(), so it works in the menu, during a match with the pointer captured and in the boss sandbox; the event is marked handled so the key does not reach the rest of the game. toggle_fullscreen() switches between WINDOW_MODE_FULLSCREEN and WINDOW_MODE_WINDOWED; the state lives in main.fullscreen (initialised from the real window mode) instead of querying the driver on every press, so the toggle is deterministic and testable. On Web it is a no-op because fullscreen belongs to the page. Desktop HUD hint updated. New tools/test_fullscreen_shortcut.gd: 8 checks (action and key mapping, alternation, bare F inert, works mid-match without freeing the cursor). Verified on a real display: window_get_mode 0 -> 3 -> 0.
132 lines
7.2 KiB
Markdown
132 lines
7.2 KiB
Markdown
# 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).
|