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. signal screen_requested(id: StringName) const SCREENS := [ [&"inventory", "Inventory", "I"], [&"character", "Character", "C"], [&"quest_log", "Quest Log", "J"], [&"spellbook", "Spellbook", "K"], [&"world_map", "World Map", "M"], [&"party", "Party", "P"], ] const SLIDE_SECONDS := 0.32 const HIDDEN_SHIFT := 230.0 var _screen_buttons: Dictionary = {} var _state: ShellState var _menu: VBoxContainer var _handle: Button func _ready() -> void: _menu = VBoxContainer.new() _menu.add_theme_constant_override("separation", 10) add_child(_menu) for entry in SCREENS: var id: StringName = entry[0] var button := Button.new() button.text = "%s %s" % [entry[2], entry[1]] button.theme_type_variation = ThemeKeys.DARK_PANEL button.pressed.connect(func(): screen_requested.emit(id)) _menu.add_child(button) _screen_buttons[id] = button _handle = Button.new() _handle.text = "›" _handle.theme_type_variation = ThemeKeys.TAB_ACTIVE _handle.pressed.connect(_on_handle_pressed) add_child(_handle) 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 := Vector2.ZERO if open else 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)