extends CanvasLayer ## Меню паузы. Кладётся внутрь игровой сцены и работает само по себе: ## ловит действие «pause», останавливает дерево и переиспользует те же ## экраны настроек и сохранений, что и главное меню. ## ## Слой обрабатывается всегда (process_mode = ALWAYS), поэтому анимации и ## ввод продолжают работать, пока остальная игра стоит на паузе. ## Куда уходим по «В главное меню». @export_file("*.tscn") var main_menu_scene: String = "res://scenes/ui/main_menu.tscn" signal opened signal closed @onready var _saves: SaveManagerService = get_node("/root/SaveManager") @onready var _root: Control = $Root @onready var _card: Control = $Root/Center/Card @onready var _menu: VBoxContainer = $Root/Center/Card/Body/Menu @onready var _settings_screen: Control = $Root/SettingsScreen @onready var _save_screen: SaveMenuScreen = $Root/SaveScreen ## Режим курсора до паузы — игра могла держать его захваченным. var _previous_mouse_mode := Input.MOUSE_MODE_VISIBLE func _ready() -> void: _root.visible = false for button: Button in _menu.get_children(): # Мышь и клавиатура делят одну подсветку: наведение = фокус. button.mouse_entered.connect(button.grab_focus) button.pressed.connect(_on_menu_pressed.bind(button.name)) _settings_screen.closed.connect(_focus_first_button) _save_screen.closed.connect(_focus_first_button) func _unhandled_input(event: InputEvent) -> void: # Вложенные экраны сами обрабатывают Esc и гасят событие — сюда оно не # дойдёт, но проверка оставлена на случай другой раскладки для «pause». if _settings_screen.visible or _save_screen.visible: return if not event.is_action_pressed("pause"): return toggle() get_viewport().set_input_as_handled() func toggle() -> void: if _root.visible: close() else: open() func open() -> void: # Миниатюру снимаем до показа меню, иначе в слот попадёт само меню. _saves.capture_thumbnail() get_tree().paused = true _previous_mouse_mode = Input.mouse_mode Input.mouse_mode = Input.MOUSE_MODE_VISIBLE _root.modulate.a = 0.0 _root.visible = true # Контейнеры раскладываются только после того, как узел стал видимым. await get_tree().process_frame _card.pivot_offset = _card.size * 0.5 _card.scale = Vector2(0.96, 0.96) var tween := create_tween().set_parallel(true).set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_CUBIC) tween.tween_property(_root, "modulate:a", 1.0, 0.15) tween.tween_property(_card, "scale", Vector2.ONE, 0.2) _focus_first_button() opened.emit() func close() -> void: var tween := create_tween().set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_CUBIC) tween.tween_property(_root, "modulate:a", 0.0, 0.12) await tween.finished _root.visible = false get_tree().paused = false Input.mouse_mode = _previous_mouse_mode closed.emit() func _on_menu_pressed(button_name: StringName) -> void: match String(button_name): "ResumeButton": close() "SaveButton": _save_screen.open(SaveMenuScreen.Mode.SAVE) "LoadButton": _save_screen.open(SaveMenuScreen.Mode.LOAD) "SettingsButton": _settings_screen.open() "MainMenuButton": _return_to_main_menu() "QuitButton": _quit() func _focus_first_button() -> void: for button: Button in _menu.get_children(): if button.visible: button.grab_focus() return func _return_to_main_menu() -> void: get_tree().paused = false Input.mouse_mode = Input.MOUSE_MODE_VISIBLE _saves.end_session() get_tree().change_scene_to_file(main_menu_scene) func _quit() -> void: var tween := create_tween() tween.tween_property(_root, "modulate:a", 0.0, 0.2) await tween.finished get_tree().paused = false get_tree().quit()