extends Control ## #592: News ticker — scrolling horizontal headline bar, active in The Last Shift zone. ## Lives on UILayer (z-layer 7 per D-049). Not suppressed by insert_active (D-013): ## the ticker is a real-world screen the player can see regardless of insert state. ## Text scrolls left at SCROLL_SPEED px/sec. When current_ticker is null, hides. const BG_COLOR := Color(0.05, 0.05, 0.07, 0.75) const TEXT_COLOR := Color(0.784, 0.816, 0.878, 1.0) # INSERT_COLOR_TEXT const FONT_SIZE := 13 const SCROLL_SPEED := 60.0 # pixels per second const BAR_HEIGHT := 28 var _text: String = "" var _scroll_x: float = 0.0 var _content_width: float = 0.0 @onready var _label: Label = $TickerLabel func _ready() -> void: mouse_filter = Control.MOUSE_FILTER_IGNORE _label.add_theme_font_size_override("font_size", FONT_SIZE) _label.add_theme_color_override("font_color", TEXT_COLOR) visible = false func update_from_state() -> void: var ticker: Variant = GameState.current_snapshot.get("current_ticker") if ticker == null or not ticker is Dictionary: visible = false return var new_text: String = ticker.get("text", "") if new_text.is_empty(): visible = false return if new_text != _text: _text = new_text _label.text = _text # Reset scroll to start from right edge on new headline. # Defer width read by one frame: get_minimum_size() returns stale # data if called before the layout pass that follows text assignment. _scroll_x = size.x _content_width = 0.0 # will be updated after layout in _process call_deferred("_update_content_width") visible = true func _update_content_width() -> void: _content_width = _label.get_minimum_size().x func _process(delta: float) -> void: if not visible: return _scroll_x -= SCROLL_SPEED * delta # Restart from right edge when text has fully exited left. if _scroll_x + _content_width < 0.0: _scroll_x = size.x _label.position.x = _scroll_x