Move the three shell scenes (SystemDock, NarrationBook, MainWindowShell) from build-everything-in-_ready() to authored .tscn node trees with the theme + type-variations set in the editor, so the layout previews and can be arranged in the Godot editor. Scripts keep only on-load work: @onready node refs, DM-loop wiring, the dock slide, and binding seed ShellState into the fixed authored turn-order tokens + consumable slots. Fixes the dock offset + over-long buttons (now fixed-width DockButtons in a right-anchored menu). Headless probe: root 1920x1080, world/book split 1180/740, dock at the world's right edge — no collapse. Suite 138/138. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
62 lines
2.2 KiB
GDScript
62 lines
2.2 KiB
GDScript
class_name SystemDock
|
||
extends Control
|
||
## The slide-in system dock (mock 2a): a gold handle toggles a column of screen
|
||
## buttons in/out. §2: state (owns the open toggle via ShellState). The buttons
|
||
## are the routing seam — they emit screen_requested; the screens they open land
|
||
## in later milestones, so the signal is currently a no-op for the shell.
|
||
##
|
||
## Layout is authored in SystemDock.tscn (editor-previewable). This script only
|
||
## wires the buttons' signals, drives the slide animation, and toggles state. Each
|
||
## dock button carries a `screen_id` StringName in its scene metadata; add/reorder
|
||
## buttons in the editor and the wiring follows.
|
||
|
||
signal screen_requested(id: StringName)
|
||
|
||
const SLIDE_SECONDS := 0.32
|
||
const HIDDEN_SHIFT := 230.0
|
||
|
||
var _screen_buttons: Dictionary = {}
|
||
var _state: ShellState
|
||
var _menu_open_pos: Vector2
|
||
|
||
@onready var _menu: VBoxContainer = $Menu
|
||
@onready var _handle: Button = $Handle
|
||
|
||
|
||
func _ready() -> void:
|
||
# The buttons live in the scene; harvest each one's authored screen_id so the
|
||
# routing seam follows editor edits (add/remove/reorder needs no code change).
|
||
for child in _menu.get_children():
|
||
if child is Button and child.has_meta("screen_id"):
|
||
var id: StringName = child.get_meta("screen_id")
|
||
child.pressed.connect(func(): screen_requested.emit(id))
|
||
_screen_buttons[id] = child
|
||
_handle.pressed.connect(_on_handle_pressed)
|
||
# Capture the authored open position so the slide is relative to wherever the
|
||
# menu is placed in the editor — moving it in the editor just works.
|
||
_menu_open_pos = _menu.position
|
||
|
||
|
||
func setup(state: ShellState) -> void:
|
||
_state = state
|
||
_apply_open(_state.dock_open)
|
||
|
||
|
||
func _on_handle_pressed() -> void:
|
||
if _state == null:
|
||
return
|
||
_apply_open(_state.toggle_dock())
|
||
|
||
|
||
func _apply_open(open: bool) -> void:
|
||
_handle.text = "›" if open else "‹"
|
||
var target := _menu_open_pos if open else _menu_open_pos + Vector2(HIDDEN_SHIFT, 0.0)
|
||
var alpha := 1.0 if open else 0.0
|
||
if not is_inside_tree():
|
||
_menu.position = target
|
||
_menu.modulate.a = alpha
|
||
return
|
||
var tween := create_tween().set_parallel(true)
|
||
tween.tween_property(_menu, "position", target, SLIDE_SECONDS)
|
||
tween.tween_property(_menu, "modulate:a", alpha, SLIDE_SECONDS)
|