extends Node2D const PLANET_DEFINITIONS = preload("res://PlanetDefinitions.gd") const BUILDING_DEFINITIONS = preload("res://BuildingDefinitions.gd") const WORLD_TO_LOCAL: float = 0.04 # 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: var definition: Resource = BUILDING_DEFINITIONS.get_definition(String(b.type)) if definition == null: continue markers.append({ "position": b.position * WORLD_TO_LOCAL, "type": b.type, "color": definition.map_color, "sides": definition.map_sides, "radius": definition.map_radius, "phase": definition.map_phase, }) return markers # 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 var definition: Resource = PLANET_DEFINITIONS.get_definition(String(data.definition_id)) draw_circle(planet_local, 5.0, definition.map_color) 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(): _draw_marker_polygon(_regular_polygon(marker.position, marker.radius, marker.sides, marker.phase), 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)