class_name HudHeadingTape extends HudInstrument # Aircraft-style heading tape: a strip of compass ticks slides under a fixed # centre marker, with cardinal letters so the player always knows which way # they're facing. A dumb display fed by HUDController via set_heading. const BG_COLOR := Color(0.05, 0.07, 0.11, 0.55) const TICK_COLOR := Color(0.85, 0.92, 1.0, 0.9) const MARKER_COLOR := Color(1.0, 0.8, 0.2) # Degrees of heading visible across the full tape width. const VISIBLE_RANGE := 90.0 const CARDINALS := {0: "N", 45: "NE", 90: "E", 135: "SE", 180: "S", 225: "SW", 270: "W", 315: "NW"} var _target_heading := 0.0 var _heading := 0.0 func _ready() -> void: clip_contents = true func set_heading(heading_deg: float) -> void: _target_heading = heading_deg # Below this, the tape has visually settled — skip the redraw. const REDRAW_EPSILON_DEG := 0.05 func _process(delta: float) -> void: var t := _smoothing_weight(delta) var new_heading := fmod(lerp_angle_deg(_heading, _target_heading, t) + 360.0, 360.0) var changed := absf(angle_delta_deg(_heading, new_heading)) > REDRAW_EPSILON_DEG _heading = new_heading if changed: queue_redraw() func _draw() -> void: var font := ThemeDB.fallback_font var center_x := size.x / 2.0 var px_per_deg := size.x / VISIBLE_RANGE draw_rect(Rect2(Vector2.ZERO, size), BG_COLOR) # Ticks every 5 degrees across the visible window, majors every 15. var first := ceili((_heading - VISIBLE_RANGE / 2.0) / 5.0) * 5 for d in range(first, first + int(VISIBLE_RANGE) + 5, 5): var x := center_x + (float(d) - _heading) * px_per_deg var shown := wrapi(d, 0, 360) var major := shown % 15 == 0 draw_line(Vector2(x, 0), Vector2(x, 10.0 if major else 5.0), TICK_COLOR, 1.0) if major: var text: String = CARDINALS.get(shown, str(shown)) var text_size := font.get_string_size(text, HORIZONTAL_ALIGNMENT_CENTER, -1, 11) draw_string(font, Vector2(x - text_size.x / 2.0, 22.0), text, HORIZONTAL_ALIGNMENT_CENTER, -1, 11, MARKER_COLOR if CARDINALS.has(shown) else TICK_COLOR) # Fixed centre marker and numeric readout. draw_colored_polygon(PackedVector2Array([ Vector2(center_x, 12.0), Vector2(center_x - 5, 2.0), Vector2(center_x + 5, 2.0), ]), MARKER_COLOR) var reading := "%03d" % (roundi(_heading) % 360) var reading_size := font.get_string_size(reading, HORIZONTAL_ALIGNMENT_CENTER, -1, 13) draw_string(font, Vector2(center_x - reading_size.x / 2.0, size.y - 4.0), reading, HORIZONTAL_ALIGNMENT_CENTER, -1, 13, MARKER_COLOR)