feat(mvp): complete M13 clean-up milestone (real usernames, avatar studio redesign, pause menu, audio manager)
checkpoint / checkpoint (push) Successful in 6m22s
checkpoint / checkpoint (push) Successful in 6m22s
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
class_name PauseMenu
|
||||
extends Control
|
||||
## In-game Pause & System Menu overlay.
|
||||
##
|
||||
## Provides resume, settings, controls guide, and safe exit confirmation.
|
||||
|
||||
signal resume_requested
|
||||
signal edit_world_requested
|
||||
signal friends_requested
|
||||
signal chat_requested
|
||||
signal controls_requested
|
||||
signal leave_world_confirmed
|
||||
|
||||
var can_edit: bool = false
|
||||
var world_title: String = "World"
|
||||
|
||||
var _confirm_box: VBoxContainer
|
||||
var _main_menu_vbox: VBoxContainer
|
||||
var _fs_button: Button
|
||||
|
||||
|
||||
func _init(p_can_edit: bool = false, p_world_title: String = "World") -> void:
|
||||
name = "PauseMenu"
|
||||
can_edit = p_can_edit
|
||||
world_title = p_world_title
|
||||
set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
|
||||
# Dark semi-transparent background
|
||||
var bg := ColorRect.new()
|
||||
bg.color = Color(0.05, 0.07, 0.10, 0.85)
|
||||
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
add_child(bg)
|
||||
|
||||
var center := CenterContainer.new()
|
||||
center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
add_child(center)
|
||||
|
||||
var panel := PanelContainer.new()
|
||||
panel.custom_minimum_size = Vector2(400, 0)
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.12, 0.14, 0.18, 0.98)
|
||||
style.border_color = Color(0.35, 0.45, 0.65, 1.0)
|
||||
style.set_border_width_all(2)
|
||||
style.set_corner_radius_all(10)
|
||||
style.content_margin_left = 28
|
||||
style.content_margin_right = 28
|
||||
style.content_margin_top = 24
|
||||
style.content_margin_bottom = 24
|
||||
panel.add_theme_stylebox_override("panel", style)
|
||||
center.add_child(panel)
|
||||
|
||||
var root_vbox := VBoxContainer.new()
|
||||
root_vbox.add_theme_constant_override("separation", 16)
|
||||
panel.add_child(root_vbox)
|
||||
|
||||
# Title & World info
|
||||
var header_vbox := VBoxContainer.new()
|
||||
header_vbox.add_theme_constant_override("separation", 4)
|
||||
root_vbox.add_child(header_vbox)
|
||||
|
||||
var title := Label.new()
|
||||
title.text = "PAUSED"
|
||||
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
title.add_theme_font_size_override("font_size", 28)
|
||||
title.add_theme_color_override("font_color", Color(1.0, 1.0, 1.0))
|
||||
header_vbox.add_child(title)
|
||||
|
||||
var subtitle := Label.new()
|
||||
subtitle.text = world_title
|
||||
subtitle.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
subtitle.add_theme_font_size_override("font_size", 14)
|
||||
subtitle.add_theme_color_override("font_color", Color(0.65, 0.75, 0.9))
|
||||
header_vbox.add_child(subtitle)
|
||||
|
||||
# Main action buttons
|
||||
_main_menu_vbox = VBoxContainer.new()
|
||||
_main_menu_vbox.add_theme_constant_override("separation", 10)
|
||||
root_vbox.add_child(_main_menu_vbox)
|
||||
|
||||
var resume_btn := _make_menu_btn("▶ Resume Game (ESC)", _on_resume_pressed, Color(0.4, 0.9, 0.5))
|
||||
_main_menu_vbox.add_child(resume_btn)
|
||||
|
||||
if can_edit:
|
||||
var edit_btn := _make_menu_btn("🛠️ Creator Studio / Edit World (F2)", func(): edit_world_requested.emit())
|
||||
_main_menu_vbox.add_child(edit_btn)
|
||||
|
||||
var friends_btn := _make_menu_btn("👥 Friends & Social (F1)", func(): friends_requested.emit())
|
||||
_main_menu_vbox.add_child(friends_btn)
|
||||
|
||||
var chat_btn := _make_menu_btn("💬 Chat (T / Enter)", func(): chat_requested.emit())
|
||||
_main_menu_vbox.add_child(chat_btn)
|
||||
|
||||
var controls_btn := _make_menu_btn("⌨️ Controls & Keybindings", func(): controls_requested.emit())
|
||||
_main_menu_vbox.add_child(controls_btn)
|
||||
|
||||
_fs_button = _make_menu_btn("⛶ Toggle Fullscreen (F11)", _on_toggle_fs)
|
||||
_update_fs_label()
|
||||
_main_menu_vbox.add_child(_fs_button)
|
||||
|
||||
var leave_btn := _make_menu_btn("🚪 Leave World", _on_prompt_leave, Color(1.0, 0.45, 0.45))
|
||||
_main_menu_vbox.add_child(leave_btn)
|
||||
|
||||
# Confirmation Box (hidden initially)
|
||||
_confirm_box = VBoxContainer.new()
|
||||
_confirm_box.visible = false
|
||||
_confirm_box.add_theme_constant_override("separation", 14)
|
||||
root_vbox.add_child(_confirm_box)
|
||||
|
||||
var confirm_msg := Label.new()
|
||||
confirm_msg.text = "Are you sure you want to leave this world?\nAny unsaved changes will be lost."
|
||||
confirm_msg.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
confirm_msg.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||
confirm_msg.add_theme_font_size_override("font_size", 14)
|
||||
confirm_msg.add_theme_color_override("font_color", Color(1.0, 0.85, 0.85))
|
||||
_confirm_box.add_child(confirm_msg)
|
||||
|
||||
var confirm_btns := HBoxContainer.new()
|
||||
confirm_btns.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
confirm_btns.add_theme_constant_override("separation", 16)
|
||||
_confirm_box.add_child(confirm_btns)
|
||||
|
||||
var cancel_leave_btn := Button.new()
|
||||
cancel_leave_btn.text = "Stay in World"
|
||||
cancel_leave_btn.custom_minimum_size = Vector2(130, 36)
|
||||
cancel_leave_btn.pressed.connect(_on_cancel_leave)
|
||||
confirm_btns.add_child(cancel_leave_btn)
|
||||
|
||||
var confirm_leave_btn := Button.new()
|
||||
confirm_leave_btn.text = "Confirm Leave"
|
||||
confirm_leave_btn.custom_minimum_size = Vector2(130, 36)
|
||||
confirm_leave_btn.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4))
|
||||
confirm_leave_btn.pressed.connect(func(): leave_world_confirmed.emit())
|
||||
confirm_btns.add_child(confirm_leave_btn)
|
||||
|
||||
|
||||
func _make_menu_btn(label: String, callback: Callable, color: Color = Color.WHITE) -> Button:
|
||||
var btn := Button.new()
|
||||
btn.text = label
|
||||
btn.custom_minimum_size = Vector2(0, 38)
|
||||
if color != Color.WHITE:
|
||||
btn.add_theme_color_override("font_color", color)
|
||||
btn.pressed.connect(callback)
|
||||
return btn
|
||||
|
||||
|
||||
func _on_resume_pressed() -> void:
|
||||
resume_requested.emit()
|
||||
|
||||
|
||||
func _on_toggle_fs() -> void:
|
||||
Session.toggle_fullscreen()
|
||||
_update_fs_label()
|
||||
|
||||
|
||||
func _update_fs_label() -> void:
|
||||
if _fs_button == null:
|
||||
return
|
||||
if DisplayServer.get_name() == "headless":
|
||||
_fs_button.text = "⛶ Toggle Fullscreen (F11)"
|
||||
return
|
||||
var m := DisplayServer.window_get_mode()
|
||||
if m == DisplayServer.WINDOW_MODE_FULLSCREEN or m == DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
|
||||
_fs_button.text = "🗗 Windowed Mode (F11)"
|
||||
else:
|
||||
_fs_button.text = "⛶ Fullscreen Mode (F11)"
|
||||
|
||||
|
||||
func _on_prompt_leave() -> void:
|
||||
_main_menu_vbox.visible = false
|
||||
_confirm_box.visible = true
|
||||
|
||||
|
||||
func _on_cancel_leave() -> void:
|
||||
_confirm_box.visible = false
|
||||
_main_menu_vbox.visible = true
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("ui_cancel"):
|
||||
get_viewport().set_input_as_handled()
|
||||
if _confirm_box.visible:
|
||||
_on_cancel_leave()
|
||||
else:
|
||||
_on_resume_pressed()
|
||||
Reference in New Issue
Block a user