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.
This commit is contained in:
@@ -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/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/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/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_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) |
|
| `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
|
## 2. Verifiche eseguite
|
||||||
|
|
||||||
```bash
|
```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_spawn_windows.gd
|
||||||
"$G" --headless --path . --script res://tools/test_wave_growth.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_gameplay_improvements.gd
|
||||||
|
"$G" --headless --path . --script res://tools/test_fullscreen_shortcut.gd # 8/8 PASS
|
||||||
python3 tools/test_scores_api.py # 42/42
|
python3 tools/test_scores_api.py # 42/42
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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={
|
grenade={
|
||||||
"deadzone": 0.5,
|
"deadzone": 0.5,
|
||||||
"events": [Object(InputEventMouseButton, "button_index":2)]
|
"events": [Object(InputEventMouseButton, "button_index":2)]
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ func _ready() -> void:
|
|||||||
grenade_value.offset_right = 160
|
grenade_value.offset_right = 160
|
||||||
grenade_value.offset_top = 44
|
grenade_value.offset_top = 44
|
||||||
grenade_value.offset_bottom = 66
|
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.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
help.set_anchors_and_offsets_preset(Control.PRESET_CENTER_BOTTOM)
|
help.set_anchors_and_offsets_preset(Control.PRESET_CENTER_BOTTOM)
|
||||||
help.offset_left = -420
|
help.offset_left = -420
|
||||||
|
|||||||
@@ -36,9 +36,13 @@ var simulation_finished := false
|
|||||||
var simulation_restarting := false
|
var simulation_restarting := false
|
||||||
var simulation_label: Label
|
var simulation_label: Label
|
||||||
var simulation_retry: Button
|
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:
|
func _ready() -> void:
|
||||||
smoke = "--smoke" in OS.get_cmdline_user_args()
|
smoke = "--smoke" in OS.get_cmdline_user_args()
|
||||||
|
fullscreen = DisplayServer.window_get_mode() != DisplayServer.WINDOW_MODE_WINDOWED
|
||||||
if not _configure_simulation():
|
if not _configure_simulation():
|
||||||
return
|
return
|
||||||
# A simulation must never read/write the real player's profile or public scores.
|
# 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)
|
canvas.add_child(simulation_retry)
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
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.
|
# 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"):
|
if boss_simulation and simulation_finished and event.is_action_pressed("reload"):
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
_restart_simulation()
|
_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:
|
func _restart_simulation() -> void:
|
||||||
if simulation_restarting:
|
if simulation_restarting:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://bdaw8q5cc5owc
|
||||||
Reference in New Issue
Block a user