extends Node2D const WORLD_TO_LOCAL: float = 0.04 # Mirrors Building.gd's own MOTHERSHIP_COLOR/CRAFTER_COLOR/COMMAND_COLOR so a # marker on the map reads as "the same building" at a glance. Duplicated # rather than cross-referenced - the same colour is already independently # duplicated between Ship.gd, Fleet.gd and Building.gd's HULL_COLOR, so this # follows the codebase's existing convention rather than inventing a new one. const MOTHERSHIP_MARKER_COLOR: Color = Color(0.45, 0.9, 0.55) const CRAFTER_MARKER_COLOR: Color = Color(1.0, 0.72, 0.25) const COMMAND_MARKER_COLOR: Color = Color(0.55, 0.6, 1.0) const TRANSPORT_HUB_MARKER_COLOR: Color = Color(0.95, 0.55, 0.85) # A structure built on a planet's surface (see GameState.extractors) is not # itself a map marker - it is a small badge on the existing planet marker, # in a colour distinct from both the planets and the space buildings so it # reads as "something is mined here" rather than another building type. const EXTRACTOR_BADGE_COLOR: Color = Color(1.0, 0.85, 0.3) const EXTRACTOR_BADGE_OFFSET: Vector2 = Vector2(8.0, -8.0) const EXTRACTOR_BADGE_RADIUS: float = 5.0 @onready var tracker: Node2D = get_node("../../../ExplorationTracker") @onready var fleet: Node2D = get_node("../../../Fleet") func _process(_delta: float) -> void: if is_visible_in_tree(): queue_redraw() # Local-space marker data for every player-placed building (GameState. # buildings), one entry per building with its colour already resolved. Kept # as a separate, callable step (rather than inlined in _draw()) so a # headless test - which has no GPU and cannot check drawn pixels - can # verify a built building actually produces a marker. func building_markers() -> Array: var markers: Array = [] for b in GameState.buildings: markers.append({ "position": b.position * WORLD_TO_LOCAL, "type": b.type, "color": _building_marker_color(b.type), }) return markers func _building_marker_color(type: String) -> Color: match type: GameState.BUILDING_MOTHERSHIP: return MOTHERSHIP_MARKER_COLOR GameState.BUILDING_SHIP_CRAFTER: return CRAFTER_MARKER_COLOR GameState.BUILDING_TRANSPORT_HUB: return TRANSPORT_HUB_MARKER_COLOR _: return COMMAND_MARKER_COLOR # Whether the planet in this (explored) chunk has an extractor on it, per # GameState.has_extractor_at(). A separate predicate for the same # GPU-less-testability reason as building_markers(). func chunk_has_extractor_badge(chunk: Vector2i) -> bool: if not WorldGen.has_planet_at_chunk(chunk): return false return GameState.has_extractor_at(WorldGen.planet_data_at_chunk(chunk).name) func _regular_polygon(center: Vector2, radius: float, sides: int, phase: float = 0.0) -> PackedVector2Array: var pts: PackedVector2Array = PackedVector2Array() for i in range(sides): var a: float = (float(i) / float(sides)) * TAU + phase pts.append(center + Vector2(cos(a), sin(a)) * radius) return pts # Filled shape plus a thin white outline - the same construction the fleet # marker below already uses, just factored out since buildings now need it # three times over (once per type) and the extractor badge needs it too. func _draw_marker_polygon(pts: PackedVector2Array, color: Color) -> void: draw_colored_polygon(pts, color) var closed: PackedVector2Array = pts.duplicate() closed.append(pts[0]) draw_polyline(closed, Color(1, 1, 1), 1.5, true) func _draw() -> void: var chunk_size_local: float = WorldGen.CHUNK_SIZE * WORLD_TO_LOCAL for chunk in tracker.get_explored_chunks(): var origin_local: Vector2 = WorldGen.chunk_origin(chunk) * WORLD_TO_LOCAL var biome: int = WorldGen.biome_at_chunk(chunk) var color: Color = WorldGen.biome_color(biome) var rect: Rect2 = Rect2(origin_local, Vector2(chunk_size_local, chunk_size_local)) draw_rect(rect, color, true) draw_rect(rect, Color(0.0, 0.0, 0.0, 0.4), false, 1.5) if WorldGen.has_planet_at_chunk(chunk): # Same deterministic per-chunk data PlanetField spawns the # actual Planet from - the marker stays put on the map even # for a chunk whose Planet node isn't currently loaded (out of # spawn_radius_chunks of the fleet). var data: Dictionary = WorldGen.planet_data_at_chunk(chunk) var planet_local: Vector2 = origin_local + data.local_pos * WORLD_TO_LOCAL draw_circle(planet_local, 5.0, Color(0.7, 0.65, 0.55)) draw_arc(planet_local, 5.0, 0.0, TAU, 16, Color(1.0, 1.0, 1.0, 0.9), 1.0, true) if chunk_has_extractor_badge(chunk): var badge_pts: PackedVector2Array = _regular_polygon( planet_local + EXTRACTOR_BADGE_OFFSET, EXTRACTOR_BADGE_RADIUS, 4, PI / 4.0) _draw_marker_polygon(badge_pts, EXTRACTOR_BADGE_COLOR) # Buildings are placed freely in space rather than tied to a chunk, so # they get their own pass rather than living inside the chunk loop above. for marker in building_markers(): match marker.type: GameState.BUILDING_MOTHERSHIP: _draw_marker_polygon(_regular_polygon(marker.position, 8.0, 6, PI / 6.0), marker.color) GameState.BUILDING_SHIP_CRAFTER: _draw_marker_polygon(_regular_polygon(marker.position, 7.0, 4, 0.0), marker.color) GameState.BUILDING_TRANSPORT_HUB: _draw_marker_polygon(_regular_polygon(marker.position, 7.5, 8, PI / 8.0), marker.color) _: _draw_marker_polygon(_regular_polygon(marker.position, 6.0, 4, PI / 4.0), marker.color) var fleet_local: Vector2 = fleet.get_centroid() * WORLD_TO_LOCAL var marker_points: PackedVector2Array = PackedVector2Array([ fleet_local + Vector2(0, -10), fleet_local + Vector2(7, 8), fleet_local + Vector2(-7, 8), ]) draw_colored_polygon(marker_points, Color(0.3, 0.9, 1.0)) draw_polyline(PackedVector2Array([marker_points[0], marker_points[1], marker_points[2], marker_points[0]]), Color(1, 1, 1), 1.5, true)