Forgejo #18, part 5 of #12. The building only - the vessels that do the hauling are #19. Placeable, costed at 30, gated behind a new "Freight Handling" research node hanging off Logistics, with its own info popup, build-menu glyph and map marker. Drawn as an octagonal dock ring with berth arms and a squared-off freight core: deliberately radial-but-flat so it does not read as a small mothership, which is the only structure with swept arms. Its stationed transports are NeonPiece children of one rotating holder, per #10 - the geometry is rasterised once and only the holder's rotation changes, so the traffic costs no redraws. Building._draw()'s match has a catch-all that renders anything unrecognised as a ship crafter, so a missing case is silent and looks almost right. The test checks for the hub's own _hub_traffic holder rather than trusting that something plausible appeared - which caught exactly that: a _ready() patch had silently not applied and the hub was rendering through the crafter branch. The build menu grew a third row. Every button, label and price moved: the old row occupied 332px against a 372px pitch, and three of those do not fit above a 1280px screen. Rows are now shorter (128px buttons, 40px price digits) at a 290px pitch, ending at y=1176. Rendered and looked at - the first attempt tightened the pitch alone and left every price overlapping the row beneath it, which no headless test would have seen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
133 lines
5.6 KiB
GDScript
133 lines
5.6 KiB
GDScript
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)
|