class_name ScannerGuidance extends Control const PLANET_DEFINITIONS = preload("res://planets/PlanetDefinitions.gd") const EDGE_MARGIN: float = 34.0 @onready var indicator: Control = $Indicator @onready var target_label: Label = $TargetLabel var fleet: Node2D func _ready() -> void: fleet = get_tree().get_first_node_in_group("player_fleet") as Node2D visible = false func _process(_delta: float) -> void: var target: Dictionary = GameState.active_scanner_target() if target.is_empty() or GameState.intro_active or _modal_is_open(): visible = false return if fleet == null or not is_instance_valid(fleet): fleet = get_tree().get_first_node_in_group("player_fleet") as Node2D if fleet == null: visible = false return visible = true var target_world := Vector2(target.world_position) var viewport_size: Vector2 = get_viewport_rect().size var screen_position: Vector2 = get_viewport().get_canvas_transform() * target_world var safe_margin: Vector2 = Vector2.ONE * EDGE_MARGIN + indicator.size * 0.5 var safe_rect := Rect2(safe_margin, viewport_size - safe_margin * 2.0) var at: Vector2 = screen_position if safe_rect.has_point(screen_position) \ else edge_position(viewport_size, screen_position - viewport_size * 0.5) indicator.position = at - indicator.size * 0.5 var toward_target: Vector2 = target_world - fleet.get_centroid() if toward_target.length_squared() > 0.01: indicator.rotation = toward_target.angle() + PI / 2.0 var definition: Resource = PLANET_DEFINITIONS.get_definition( String(target.definition_id)) if definition != null: indicator.glow_color = definition.map_color indicator.queue_redraw() var sectors: float = toward_target.length() / WorldGen.CHUNK_SIZE target_label.text = "%s ยท %.1f SECTORS" % [ String(target.get("name", "SCANNER TARGET")).to_upper(), sectors] _place_label(at, viewport_size) func edge_position(viewport_size: Vector2, direction: Vector2) -> Vector2: var center: Vector2 = viewport_size * 0.5 if direction.length_squared() <= 0.01: return center var half: Vector2 = center - Vector2.ONE * EDGE_MARGIN - indicator.size * 0.5 var scale_to_edge: float = minf(half.x / maxf(absf(direction.x), 0.001), half.y / maxf(absf(direction.y), 0.001)) return center + direction * scale_to_edge func _place_label(indicator_center: Vector2, viewport_size: Vector2) -> void: var toward_center: Vector2 = viewport_size * 0.5 - indicator_center if toward_center.length_squared() < 0.01: toward_center = Vector2.DOWN var wanted: Vector2 = indicator_center + toward_center.normalized() * 105.0 \ - target_label.size * 0.5 target_label.position = Vector2( clampf(wanted.x, EDGE_MARGIN, viewport_size.x - target_label.size.x - EDGE_MARGIN), clampf(wanted.y, EDGE_MARGIN, viewport_size.y - target_label.size.y - EDGE_MARGIN)) func _modal_is_open() -> bool: for menu in get_tree().get_nodes_in_group("modal_menus"): if menu.is_visible_in_tree(): return true return false