extends Control ## #263: Time display — diegetic time readout on the player's neural insert (D-013, D-031). ## Shows station local time (HH:MM), day phase, and day number. ## Lives on InsertOverlay (CanvasLayer 10) per D-051 diegetic insert principle. ## Draw-based for implant visual aesthetic. Updated via update_from_state() from main.gd. ## ## Placeholder layout — position and style will be refined when #314 wireframe lands. const FONT_SIZE_TIME: int = 15 const FONT_SIZE_META: int = 10 const PADDING := Vector2(10, 7) const BG_COLOR := Color(0.04, 0.05, 0.08, 0.70) const BORDER_COLOR := Color(0.10, 0.20, 0.26, 0.65) # Day phase colors — station lighting cycle (D-031) const PHASE_COLORS := { "Morning": Color("#aed6dc"), # pale cyan-blue — early light "Afternoon": Color("#E0F7FA"), # bright cyan-white — full day "Evening": Color("#9EBFC4"), # dimmed — dusk transition "Night": Color("#4a7080"), # dark teal — station nightwatch } var _time_str: String = "--:--" var _phase_str: String = "" var _day_str: String = "" var _day_text: String = "" var _phase_color: Color = Constants.IMPLANT_TEXT_DIM var _last_signature: String = "" var _has_data: bool = false # Cached geometry — recomputed in update_from_state(), used in _draw() var _time_size: Vector2 = Vector2.ZERO var _phase_size: Vector2 = Vector2.ZERO var _day_size: Vector2 = Vector2.ZERO var _meta_h: float = 0.0 var _box_w: float = 0.0 var _box_h: float = 0.0 func _ready() -> void: mouse_filter = Control.MOUSE_FILTER_IGNORE func update_from_state() -> void: var gt: Dictionary = GameState.game_time if gt.is_empty(): return var tod: int = int(gt.get("time_of_day", 0)) var day: int = int(gt.get("day", 0)) var phase: String = str(gt.get("day_phase", "")) var sig: String = "%d:%d:%s" % [tod, day, phase] if sig == _last_signature: return _last_signature = sig _time_str = Constants.format_game_time(tod) _phase_str = phase _day_str = "D%d" % (day + 1) _day_text = " " + _day_str _phase_color = PHASE_COLORS.get(phase, Constants.IMPLANT_TEXT_DIM) _has_data = true _cache_geometry() queue_redraw() func _cache_geometry() -> void: var font := ThemeDB.fallback_font _time_size = font.get_string_size(_time_str, HORIZONTAL_ALIGNMENT_LEFT, -1, FONT_SIZE_TIME) _phase_size = font.get_string_size(_phase_str, HORIZONTAL_ALIGNMENT_LEFT, -1, FONT_SIZE_META) _day_size = font.get_string_size(_day_text, HORIZONTAL_ALIGNMENT_LEFT, -1, FONT_SIZE_META) _meta_h = font.get_string_size("A", HORIZONTAL_ALIGNMENT_LEFT, -1, FONT_SIZE_META).y var meta_w := _phase_size.x + _day_size.x var content_w := maxf(_time_size.x, meta_w) _box_w = content_w + PADDING.x * 2 _box_h = PADDING.y * 2 + _time_size.y + 3 + _meta_h func _draw() -> void: if not _has_data: return # Background draw_rect(Rect2(Vector2.ZERO, Vector2(_box_w, _box_h)), BG_COLOR) draw_rect(Rect2(Vector2.ZERO, Vector2(_box_w, _box_h)), BORDER_COLOR, false, 1.0) var font := ThemeDB.fallback_font # HH:MM (primary, full brightness) draw_string( font, Vector2(PADDING.x, PADDING.y + _time_size.y), _time_str, HORIZONTAL_ALIGNMENT_LEFT, -1, FONT_SIZE_TIME, Constants.IMPLANT_TEXT_COLOR ) # Phase + day number (secondary, dimmed + phase-tinted) var meta_y := PADDING.y + _time_size.y + 3 + _meta_h draw_string( font, Vector2(PADDING.x, meta_y), _phase_str, HORIZONTAL_ALIGNMENT_LEFT, -1, FONT_SIZE_META, _phase_color ) draw_string( font, Vector2(PADDING.x + _phase_size.x, meta_y), _day_text, HORIZONTAL_ALIGNMENT_LEFT, -1, FONT_SIZE_META, Constants.IMPLANT_TEXT_DIM )