class_name MetaScreen extends Control ## Base class for all meta-UI screens (#618, #680). ## Scene-root screens (main_menu, character_creation) extend this for the ## lifecycle contract. Overlay screens (settings, debug_console, bug_report, ## loading_screen) extend this AND push onto MetaStack. signal closed signal escape_pressed enum Phase { HIDDEN, OPENING, OPEN, CLOSING } @export var pauses_sim: bool = false @export var closable_by_escape: bool = true @export var captures_input: bool = true var _phase: Phase = Phase.HIDDEN func open() -> void: if _phase != Phase.HIDDEN: return _phase = Phase.OPENING visible = true mouse_filter = Control.MOUSE_FILTER_STOP if captures_input else Control.MOUSE_FILTER_IGNORE on_open() _phase = Phase.OPEN func close() -> void: if _phase != Phase.OPEN: return _phase = Phase.CLOSING on_close() visible = false mouse_filter = Control.MOUSE_FILTER_IGNORE _phase = Phase.HIDDEN closed.emit() func is_open() -> bool: return _phase == Phase.OPEN ## Called by MetaStack when ESC is pressed with this screen on top. ## ## Return true: consumed-and-held — keep this screen open (e.g. "are you sure" ## prompt was shown, do not close the underlying screen). ## Return false: did nothing internally — let MetaStack close this screen. ## ## To express "consume-and-hold" — the screen must remain open and ESC must NOT ## fall through to gameplay/implant — set `closable_by_escape = false` instead. ## MetaStack treats that as: call on_escape (to let the screen react), do not ## pop, return true so the event stops here. Returning true from `on_escape` is ## the per-event variant; setting the flag is the screen-wide variant. func on_escape() -> bool: escape_pressed.emit() return false # --- Lifecycle hooks — subclasses override --- func on_open() -> void: pass func on_close() -> void: pass