spaceship/WorldMapContent.gd
2026-08-14 16:13:24 +02:00

141 lines
6 KiB
GDScript

extends Node2D
const PLANET_DEFINITIONS = preload("res://PlanetDefinitions.gd")
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)
const SHIP_LAB_MARKER_COLOR: Color = Color(0.55, 0.95, 0.9)
# 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
GameState.BUILDING_SHIP_LAB:
return SHIP_LAB_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
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():
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)
GameState.BUILDING_SHIP_LAB:
_draw_marker_polygon(_regular_polygon(marker.position, 7.5, 3, -PI / 2.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)