Files
client/spacewar/menu/pause_menu.gd
T
anekdotin a94bccc57b Add capital ships, and settings screen with smooth-motion fixes
Capital ships & support art:
- New Flagship capital ships (world/flagship.gd) spawn a small
  point-defense formation at each team's spawn marker, independently
  targeting the nearest enemy in range with a slow unmissable-looking
  missile and a faster bullet stream -- keeps a team from parking on
  the enemy spawn and farming respawns.
- Per-race/role bullet sprites for turret fire (assets/images/effects/
  bullets/), flagship art for both factions, minimap now draws
  flagship blips, bot_ai awareness tuned to notice them.

Settings screen (items 20-21 in CLAUDE.md) -- was a stub, now five
real sections shared between the main menu and the in-game pause menu:
- Diagnosed and fixed a "movement looks blurry/laggy" report down to
  three independent causes: physics-tick vs. display-refresh judder
  (fixed via Godot's built-in physics interpolation, plus a
  frame-rate-cap + VSync dropdown so each player can match their own
  monitor), missing mipmaps on every minified ship texture (real GPU
  sampling shimmer, unrelated to frame timing), and a periodic hitch
  from the matchmaking heartbeat spinning up a new HTTPRequest thread
  every 8 seconds instead of reusing one.
- Nameplates get their own manual per-frame interpolation, since
  Godot's physics interpolation only covers Node2D/Node3D, not the
  Control-based Label they're built from.
- Audio: Master/Music/SFX volume sliders backed by a real bus layout
  (default_bus_layout.tres) -- the project had no volume control at
  all before this.
- Window mode (Fullscreen/Exclusive Fullscreen/Windowed), a
  colorblind-friendly enemy-color toggle (also fixes the minimap's own
  separate, inconsistent yellow enemy color), and keyboard rebinding
  for every action with a live capture UI.
- New GameConfig.settings_focused flag (same pattern as chat_focused/
  team_select_focused) so a key-rebind capture can't also move the
  ship or toggle the pause menu underneath the panel.

Spawn intro: a ship's first-ever appearance now eases in from a
random direction (fast off the start, decelerating into its landing
spot) instead of popping into place -- purely a cosmetic sprite
offset, so collision/camera/networking are untouched and every peer
(including bots) sees the same warp-in.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 20:02:31 -04:00

144 lines
3.9 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends CanvasLayer
var _root: Control
func _ready() -> void:
layer = 10
_build_ui()
_root.visible = false
func _input(event: InputEvent) -> void:
if event.is_action_pressed("toggle_pause") and not GameConfig.chat_focused \
and not GameConfig.team_select_focused and not GameConfig.settings_focused:
_toggle()
get_viewport().set_input_as_handled()
func is_paused() -> bool:
return _root.visible
func _toggle() -> void:
_root.visible = not _root.visible
func _build_ui() -> void:
_root = Control.new()
_root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
add_child(_root)
# Dim overlay — blocks mouse clicks reaching the game
var overlay := ColorRect.new()
overlay.color = Color(0.0, 0.0, 0.0, 0.58)
overlay.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
overlay.mouse_filter = Control.MOUSE_FILTER_STOP
_root.add_child(overlay)
# Center panel (360 × 490)
var panel := Panel.new()
panel.set_anchors_preset(Control.PRESET_CENTER)
panel.offset_left = -180
panel.offset_top = -245
panel.offset_right = 180
panel.offset_bottom = 245
var ps := StyleBoxFlat.new()
ps.bg_color = Color(0.04, 0.07, 0.16, 0.97)
ps.set_corner_radius_all(10)
ps.set_border_width_all(1)
ps.border_color = Color(0.18, 0.38, 0.65, 0.9)
panel.add_theme_stylebox_override("panel", ps)
_root.add_child(panel)
var vb := VBoxContainer.new()
vb.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
vb.offset_left = 28
vb.offset_top = 28
vb.offset_right = -28
vb.offset_bottom = -28
vb.add_theme_constant_override("separation", 10)
panel.add_child(vb)
var title := Label.new()
title.text = "MENU"
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
title.add_theme_font_size_override("font_size", 26)
title.add_theme_color_override("font_color", Color(0.4, 0.85, 1.0))
vb.add_child(title)
vb.add_child(_hsep())
var gap := Control.new()
gap.custom_minimum_size = Vector2(0, 4)
vb.add_child(gap)
_add_btn(vb, "RESUME", true).pressed.connect(_toggle)
_add_btn(vb, "SETTINGS", true).pressed.connect(_on_settings)
_add_btn(vb, "SELECT TEAM", true).pressed.connect(_on_select_team)
vb.add_child(_hsep())
_add_btn(vb, "QUIT TO MENU", true).pressed.connect(_on_quit_to_menu)
_add_btn(vb, "QUIT TO DESKTOP", true).pressed.connect(_on_quit_to_desktop)
func _add_btn(parent: VBoxContainer, label_text: String, active: bool) -> Button:
var btn := Button.new()
btn.text = label_text
btn.custom_minimum_size = Vector2(0, 54)
btn.add_theme_font_size_override("font_size", 16)
if active:
btn.add_theme_color_override("font_color", Color(0.92, 0.95, 1.0))
var sn := _flat(Color(0.07, 0.13, 0.25, 1.0), 6)
var sh := _flat(Color(0.13, 0.24, 0.42, 1.0), 6)
sh.set_border_width_all(1)
sh.border_color = Color(0.3, 0.65, 1.0)
btn.add_theme_stylebox_override("normal", sn)
btn.add_theme_stylebox_override("hover", sh)
else:
btn.add_theme_color_override("font_color", Color(0.35, 0.40, 0.50))
var sn := _flat(Color(0.04, 0.06, 0.10, 1.0), 6)
btn.add_theme_stylebox_override("normal", sn)
btn.add_theme_stylebox_override("hover", sn)
parent.add_child(btn)
return btn
func _hsep() -> HSeparator:
var sep := HSeparator.new()
sep.add_theme_color_override("color", Color(0.18, 0.28, 0.45, 0.7))
return sep
func _flat(col: Color, radius: int = 0) -> StyleBoxFlat:
var s := StyleBoxFlat.new()
s.bg_color = col
s.set_corner_radius_all(radius)
return s
func _on_settings() -> void:
var settings := get_node_or_null("/root/World/SettingsPanel") as SettingsPanel
if settings:
settings.open()
func _on_select_team() -> void:
_root.visible = false
var team_select := get_node_or_null("/root/World/TeamSelect") as TeamSelect
if team_select:
team_select.reopen()
func _on_quit_to_menu() -> void:
NetworkManager.disconnect_from_game()
get_tree().change_scene_to_file("res://menu/main_menu.tscn")
func _on_quit_to_desktop() -> void:
get_tree().quit()