From 4af402f8bfe4a567da86f185e1e78afc3669c594 Mon Sep 17 00:00:00 2001 From: enne2 Date: Sat, 24 Oct 2026 12:55:15 +0200 Subject: [PATCH] Add Alt+F to toggle fullscreen and window mode 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. --- docs/KIOSK-ARCADE.md | 16 +++++ project.godot | 6 ++ scripts/game_hud.gd | 2 +- scripts/main.gd | 20 ++++++ tools/test_fullscreen_shortcut.gd | 94 +++++++++++++++++++++++++++ tools/test_fullscreen_shortcut.gd.uid | 1 + 6 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 tools/test_fullscreen_shortcut.gd create mode 100644 tools/test_fullscreen_shortcut.gd.uid diff --git a/docs/KIOSK-ARCADE.md b/docs/KIOSK-ARCADE.md index a4274f6..15bdb04 100644 --- a/docs/KIOSK-ARCADE.md +++ b/docs/KIOSK-ARCADE.md @@ -51,9 +51,24 @@ sulla schermata iniziale e i pulsanti non erano cliccabili. | `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 @@ -66,6 +81,7 @@ G="$HOME/.local/opt/godot-4.7.1/Godot_v4.7.1-stable_linux.x86_64" "$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 ``` diff --git a/project.godot b/project.godot index 41c4bf2..0d32bcb 100644 --- a/project.godot +++ b/project.godot @@ -83,6 +83,12 @@ pause={ ] } +toggle_fullscreen={ +"deadzone": 0.5, +"events": [Object(InputEventKey, "resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} + grenade={ "deadzone": 0.5, "events": [Object(InputEventMouseButton, "button_index":2)] diff --git a/scripts/game_hud.gd b/scripts/game_hud.gd index 6238f45..ddc6e10 100644 --- a/scripts/game_hud.gd +++ b/scripts/game_hud.gd @@ -40,7 +40,7 @@ func _ready() -> void: grenade_value.offset_right = 160 grenade_value.offset_top = 44 grenade_value.offset_bottom = 66 - help = _label(root, "WASD: muovi | mouse: mira | R: ricarica | destro: granata", 15, Vector2.ZERO) + help = _label(root, "WASD: muovi | mouse: mira | R: ricarica | destro: granata | Alt+F: schermo intero", 15, Vector2.ZERO) help.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER help.set_anchors_and_offsets_preset(Control.PRESET_CENTER_BOTTOM) help.offset_left = -420 diff --git a/scripts/main.gd b/scripts/main.gd index 9c8f8ba..0850919 100644 --- a/scripts/main.gd +++ b/scripts/main.gd @@ -36,9 +36,13 @@ var simulation_finished := false var simulation_restarting := false var simulation_label: Label var simulation_retry: Button +## Stato dello schermo intero, tenuto qui e non letto dal driver a ogni pressione: +## cosi' Alt+F alterna davvero e il comportamento e' verificabile anche in headless. +var fullscreen := false func _ready() -> void: smoke = "--smoke" in OS.get_cmdline_user_args() + fullscreen = DisplayServer.window_get_mode() != DisplayServer.WINDOW_MODE_WINDOWED if not _configure_simulation(): return # A simulation must never read/write the real player's profile or public scores. @@ -412,11 +416,27 @@ func _setup_simulation_ui() -> void: canvas.add_child(simulation_retry) func _input(event: InputEvent) -> void: + # Alt+F: finestra <-> schermo intero. Vale sempre (menu, partita, simulazione), + # anche a cursore catturato, perche' _input arriva prima della UI. + if event.is_action_pressed("toggle_fullscreen"): + get_viewport().set_input_as_handled() + toggle_fullscreen() + return # Only the completed native simulation has this shortcut. No menu click-to-start. if boss_simulation and simulation_finished and event.is_action_pressed("reload"): get_viewport().set_input_as_handled() _restart_simulation() +## Alterna finestra e schermo intero. Restituisce il nuovo stato (true = schermo intero). +## Nel browser il fullscreen e' della pagina, non della finestra Godot: li' non si tocca. +func toggle_fullscreen() -> bool: + if OS.has_feature("web"): + return fullscreen + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED if fullscreen + else DisplayServer.WINDOW_MODE_FULLSCREEN) + fullscreen = not fullscreen + return fullscreen + func _restart_simulation() -> void: if simulation_restarting: return diff --git a/tools/test_fullscreen_shortcut.gd b/tools/test_fullscreen_shortcut.gd new file mode 100644 index 0000000..5c3d82a --- /dev/null +++ b/tools/test_fullscreen_shortcut.gd @@ -0,0 +1,94 @@ +extends SceneTree +## Verifica la scorciatoia Alt+F (finestra <-> schermo intero): +## - l'azione "toggle_fullscreen" esiste nella mappa dei comandi ed e' Alt+F; +## - ogni pressione alterna lo stato, anche a partita avviata e a cursore catturato; +## - l'evento viene marcato come gestito (il tasto non arriva al gioco); +## - Alt+F senza Alt (o F da solo) NON cambia nulla. +## Avviare con: --headless --path . --script res://tools/test_fullscreen_shortcut.gd +## Con un display reale si usa: --rendering-method gl_compatibility +const ACTIONS := ["toggle_fullscreen"] + +var world: Node3D +var failures := 0 +var checks := 0 + +func _initialize() -> void: + call_deferred("run_test") + +func check(label: String, condition: bool, detail: String = "") -> void: + checks += 1 + if condition: + print("PASS: " + label) + else: + failures += 1 + print("FAIL: %s %s" % [label, detail]) + +func run_test() -> void: + # --- mappa dei comandi --- + check("l'azione toggle_fullscreen esiste nella mappa", InputMap.has_action("toggle_fullscreen")) + var alt_f := false + for event in InputMap.action_get_events("toggle_fullscreen"): + if event is InputEventKey and event.alt_pressed \ + and (event.physical_keycode == KEY_F or event.keycode == KEY_F): + alt_f = true + check("l'azione corrisponde ad Alt+F", alt_f, str(InputMap.action_get_events("toggle_fullscreen"))) + + world = load("res://scenes/main.tscn").instantiate() + root.add_child(world) + await process_frame + await process_frame + + var start_state: bool = world.fullscreen + _press_f(true) + await process_frame + check("Alt+F capovolge lo stato dello schermo intero", + world.fullscreen != start_state, "prima=%s dopo=%s" % [str(start_state), str(world.fullscreen)]) + _press_f(true) + await process_frame + check("una seconda pressione riporta allo stato iniziale", + world.fullscreen == start_state, "dopo=%s atteso=%s" % [str(world.fullscreen), str(start_state)]) + + # F senza Alt non deve cambiare nulla + var before: bool = world.fullscreen + _press_f(false) + await process_frame + check("F da solo (senza Alt) non cambia lo schermo", world.fullscreen == before, + "prima=%s dopo=%s" % [str(before), str(world.fullscreen)]) + + # --- in partita, a cursore catturato --- + var screen = world.start_screen + var gioca: Button = null + if screen != null: + for button in screen.find_children("*", "Button", true, false): + if button.text == "GIOCA": + gioca = button + if screen != null and screen.get("name_field") != null: + screen.name_field.text = "AltF" + if gioca != null: + gioca.pressed.emit() + await process_frame + await process_frame + check("la partita e' iniziata (il cursore e' catturato o headless)", + world.player.playing == true, "playing=%s" % str(world.player.playing)) + var during_play: bool = world.fullscreen + _press_f(true) + await process_frame + check("Alt+F funziona anche in partita, a cursore catturato", + world.fullscreen != during_play, "prima=%s dopo=%s" % [str(during_play), str(world.fullscreen)]) + check("Alt+F non interrompe la partita ne' libera il cursore", + world.player.playing == true and world.player.death_started == false, + "playing=%s morte=%s" % [str(world.player.playing), str(world.player.death_started)]) + + print("%d controlli superati, %d falliti" % [checks - failures, failures]) + quit(1 if failures > 0 else 0) + +## Pressione + rilascio di F, con o senza Alt: stesso percorso di un tasto reale. +func _press_f(alt: bool) -> void: + for pressed in [true, false]: + var event := InputEventKey.new() + event.physical_keycode = KEY_F + event.keycode = KEY_F + event.alt_pressed = alt + event.pressed = pressed + Input.parse_input_event(event) + Input.flush_buffered_events() diff --git a/tools/test_fullscreen_shortcut.gd.uid b/tools/test_fullscreen_shortcut.gd.uid new file mode 100644 index 0000000..6e3651b --- /dev/null +++ b/tools/test_fullscreen_shortcut.gd.uid @@ -0,0 +1 @@ +uid://bdaw8q5cc5owc