Item 1: while zero tiles have arrived, the viewer draws a centered screen-space 'DERIVING TERRAIN…' label (text_dim role, no new hue), dropping the instant the first tile lands — a cold wait now reads as loading, not broken. New has_any_tile_arrived() predicate (distinct from has_pending_tiles(): both true mid-arrival, tested exactly there). Item 2: legend re-fit root-caused empirically, two plausible fixes disproven by trace before the real one: a manually-positioned Control's size NEVER tracks a shrinking minimum in this parenting shape, and RichTextLabel.fit_content reports degenerate minimums until laid out at real width once — so reset must be DEFERRED and run after refill, not inside clear(). ImplantPanel.reset_to_content_size() (call_deferred), wired into both legend refresh()es. The load-bearing test compares size.y to get_minimum_size().y — a size-to-size comparison passed trivially with both numbers equally stuck (caught on first draft). Item 3: make atlas now builds the RELEASE server and passes SR_SERVER_BIN (a cold DEBUG server delivers zero tiles for >10s on a new body — live-measured — vs 210ms warm; release serves cold in well under a second). atlas_standalone._server_binary_path() honors the env override per the SR_PORT two-tier precedent, debug path unchanged when unset. All fixes revert-verified; nine suites green collateral-checked; gdlint clean.
193 lines
8.1 KiB
GDScript
193 lines
8.1 KiB
GDScript
extends ImplantPanel
|
|
|
|
## Left-side generation-overlay legend (D-226 item 3, T-960) — the shape/color
|
|
## key for the generation overlay group (attractor types, sub-biome colors,
|
|
## district morphology, road/rail authority + style, settlement size/capital).
|
|
##
|
|
## This script has no `class_name` on purpose, mirroring atlas_overlay_bar.gd
|
|
## (review #8 there): the owner (AtlasViewer) passes the viewer reference to
|
|
## _init(), and a `class_name` + required-arg _init() combo is a Godot editor
|
|
## footgun. Instance it via
|
|
## load("res://ui/implant/apps/atlas/atlas_legend_panel.gd").new(self).
|
|
##
|
|
## Data-driven (GENERATION_LEGEND below): one spec entry per generation
|
|
## overlay id — multiple entries may share an id (e.g. gen_l1_attractors has
|
|
## both a shape key and a color key, D-226's shape-vs-color separation taught
|
|
## the same way here as it is drawn on the map). Adding a future layer's
|
|
## legend is a new table row, never a layout change. refresh() shows only the
|
|
## sections whose overlay is currently toggled on, and hides the whole panel
|
|
## when none are active (invisible when not needed).
|
|
|
|
const PANEL_MARGIN: float = 16.0
|
|
const LEGEND_PANEL_WIDTH: float = 260.0
|
|
|
|
# Generation overlay colors (T-960) — kept in sync with the actual render
|
|
# values in atlas_marker_overlay.gd; duplicated rather than cross-referenced,
|
|
# matching the existing COLOR_ROAD/COLOR_RAIL/COLOR_CITY precedent shared
|
|
# between atlas_viewer.gd and atlas_marker_overlay.gd (each file owns its own
|
|
# reading of the palette: this one for the legend, the marker overlay for the
|
|
# draw calls).
|
|
const COLOR_ROAD_ADMINISTRATIVE: Color = Color(0.45, 0.65, 0.90, 0.85)
|
|
const COLOR_ROAD_CORPORATE: Color = Color(0.85, 0.65, 0.20, 0.85)
|
|
const COLOR_ROAD_COMMUNAL: Color = Color(0.45, 0.75, 0.50, 0.85)
|
|
const COLOR_ROAD_TRADE: Color = Color(0.85, 0.60, 0.30, 0.85)
|
|
const COLOR_ROAD_ABANDONED: Color = Color(0.45, 0.42, 0.38, 0.65)
|
|
const COLOR_SETTLEMENT_GEN: Color = Color(0.94, 0.82, 0.38, 1.0)
|
|
const COLOR_SETTLEMENT_CAPITAL_GEN: Color = Color(1.0, 0.92, 0.55, 1.0)
|
|
# T-1118 — region climate grid cold/hot ramp endpoints (matches
|
|
# atlas_overlay_colors.gd's COLOR_REGION_TEMP_COLD/COLOR_REGION_TEMP_HOT).
|
|
const COLOR_REGION_TEMP_COLD_GEN: Color = Color(0.25, 0.45, 0.85, 0.60)
|
|
const COLOR_REGION_TEMP_HOT_GEN: Color = Color(0.90, 0.25, 0.20, 0.60)
|
|
# T-1119 — quarter-footprint density ramp endpoints, D-226 T-1112 amendment
|
|
# SS3's literal legend spec (matches atlas_overlay_colors.gd's
|
|
# COLOR_QUARTER_LOW_DENSITY/COLOR_QUARTER_HIGH_DENSITY).
|
|
const COLOR_QUARTER_LOW_DENSITY_GEN: Color = Color(0.55, 0.48, 0.30, 0.6)
|
|
const COLOR_QUARTER_HIGH_DENSITY_GEN: Color = Color(0.94, 0.82, 0.38, 1.0)
|
|
|
|
## One entry per generation-overlay id. "color": Color.TRANSPARENT means
|
|
## "shape/style carries the meaning here, let the theme's dim text color
|
|
## apply" — used for every row where color is NOT the encoded axis.
|
|
const GENERATION_LEGEND: Array = [
|
|
{
|
|
"overlay_id": "gen_l1_rivers",
|
|
"title": "RIVERS — L1",
|
|
"rows": [
|
|
{"glyph": "━", "color": Color(0.353, 0.647, 0.776, 1.0), "label": "channel / confluence"},
|
|
{"glyph": "◎", "color": Color(0.353, 0.647, 0.776, 1.0), "label": "sea mouth"},
|
|
],
|
|
},
|
|
{
|
|
"overlay_id": "gen_l1_basins",
|
|
"title": "DRAINAGE BASINS — L1",
|
|
"rows": [
|
|
{"glyph": "▭", "color": Color(0.45, 0.65, 0.85, 0.70), "label": "basin fill + boundary"},
|
|
],
|
|
},
|
|
{
|
|
"overlay_id": "gen_l1_attractors",
|
|
"title": "ATTRACTORS — L1 (shape = type)",
|
|
"rows": [
|
|
{"glyph": "●", "color": Color.TRANSPARENT, "label": "river mouth"},
|
|
{"glyph": "◑", "color": Color.TRANSPARENT, "label": "coastal access"},
|
|
{"glyph": "◆", "color": Color.TRANSPARENT, "label": "river crossing"},
|
|
{"glyph": "▼", "color": Color.TRANSPARENT, "label": "valley floor"},
|
|
{"glyph": "▲", "color": Color.TRANSPARENT, "label": "pass entrance"},
|
|
{"glyph": "○", "color": Color.TRANSPARENT, "label": "lake shore"},
|
|
{"glyph": "■", "color": Color.TRANSPARENT, "label": "plain center"},
|
|
],
|
|
},
|
|
{
|
|
"overlay_id": "gen_l1_attractors",
|
|
"title": "SUB-BIOME — L1 (color)",
|
|
"rows": [
|
|
{"glyph": "●", "color": Color(0.25, 0.72, 0.65, 0.90), "label": "tropical / coastal"},
|
|
{"glyph": "●", "color": Color(0.45, 0.68, 0.45, 0.90), "label": "temperate"},
|
|
{"glyph": "●", "color": Color(0.78, 0.62, 0.35, 0.90), "label": "arid"},
|
|
{"glyph": "●", "color": Color(0.52, 0.58, 0.72, 0.90), "label": "alpine"},
|
|
{"glyph": "●", "color": Color(0.40, 0.60, 0.52, 0.90), "label": "wetland"},
|
|
{"glyph": "●", "color": Color(0.55, 0.68, 0.82, 0.90), "label": "cold"},
|
|
],
|
|
},
|
|
{
|
|
"overlay_id": "gen_region_grid",
|
|
"title": "REGION CLIMATE — mean temperature (~205 km cells)",
|
|
"rows": [
|
|
{"glyph": "▦", "color": COLOR_REGION_TEMP_COLD_GEN, "label": "cold"},
|
|
{"glyph": "▦", "color": COLOR_REGION_TEMP_HOT_GEN, "label": "hot"},
|
|
{"glyph": "▦", "color": Color.TRANSPARENT, "label": "airless — no reading (skipped)"},
|
|
],
|
|
},
|
|
{
|
|
"overlay_id": "gen_district",
|
|
"title": "DISTRICT MORPHOLOGY — coarse",
|
|
"rows": [
|
|
{"glyph": "▦", "color": Color.TRANSPARENT, "label": "terrain-colored fill (17 zones)"},
|
|
],
|
|
},
|
|
{
|
|
"overlay_id": "gen_l2_roads",
|
|
"title": "ROADS/RAIL — L2 (color = authority)",
|
|
"rows": [
|
|
{"glyph": "━", "color": COLOR_ROAD_ADMINISTRATIVE, "label": "administrative"},
|
|
{"glyph": "━", "color": COLOR_ROAD_CORPORATE, "label": "corporate"},
|
|
{"glyph": "━", "color": COLOR_ROAD_COMMUNAL, "label": "communal"},
|
|
{"glyph": "━", "color": COLOR_ROAD_TRADE, "label": "trade"},
|
|
{"glyph": "━", "color": COLOR_ROAD_ABANDONED, "label": "abandoned"},
|
|
],
|
|
},
|
|
{
|
|
"overlay_id": "gen_l2_roads",
|
|
"title": "LINE STYLE (road vs rail)",
|
|
"rows": [
|
|
{"glyph": "━", "color": Color.TRANSPARENT, "label": "road (solid)"},
|
|
{"glyph": "┄", "color": Color.TRANSPARENT, "label": "rail (dashed)"},
|
|
{"glyph": "◇", "color": Color.TRANSPARENT, "label": "junction (3+ ways)"},
|
|
],
|
|
},
|
|
{
|
|
"overlay_id": "gen_l3_settlements",
|
|
"title": "SETTLEMENTS — L3 (size = population)",
|
|
"rows": [
|
|
{"glyph": "●", "color": COLOR_SETTLEMENT_GEN, "label": "settlement"},
|
|
{"glyph": "★", "color": COLOR_SETTLEMENT_CAPITAL_GEN, "label": "capital / major hub"},
|
|
],
|
|
},
|
|
{
|
|
"overlay_id": "gen_l4_quarters",
|
|
"title": "QUARTER FOOTPRINT — L4 (color = density, shape = dominant type)",
|
|
"rows": [
|
|
{"glyph": "▪", "color": COLOR_QUARTER_LOW_DENSITY_GEN, "label": "low density"},
|
|
{"glyph": "▪", "color": COLOR_QUARTER_HIGH_DENSITY_GEN, "label": "high density"},
|
|
{"glyph": "◪", "color": Color.TRANSPARENT, "label": "commercial (corner tab, top-right)"},
|
|
{"glyph": "◩", "color": Color.TRANSPARENT, "label": "industrial (corner tab, bottom-right)"},
|
|
{"glyph": "◈", "color": Color.TRANSPARENT, "label": "administrative (diamond cutout)"},
|
|
],
|
|
},
|
|
]
|
|
|
|
var _viewer = null # AtlasViewer (untyped to avoid cyclic ref)
|
|
|
|
|
|
func _init(viewer_ref = null) -> void:
|
|
_viewer = viewer_ref
|
|
custom_minimum_size.x = LEGEND_PANEL_WIDTH
|
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
visible = false
|
|
|
|
|
|
func reposition() -> void:
|
|
position = Vector2(PANEL_MARGIN, 60.0)
|
|
|
|
|
|
## Rebuilds from GENERATION_LEGEND, showing only the sections whose overlay is
|
|
## currently toggled on — invisible when no generation overlay is active,
|
|
## updates every time AtlasViewer.set_overlay_visible() runs.
|
|
func refresh() -> void:
|
|
if _viewer == null:
|
|
return
|
|
var active_specs: Array = []
|
|
for spec: Dictionary in GENERATION_LEGEND:
|
|
if _viewer.is_overlay_visible(str(spec.get("overlay_id", ""))):
|
|
active_specs.append(spec)
|
|
|
|
clear()
|
|
visible = not active_specs.is_empty()
|
|
if active_specs.is_empty():
|
|
reset_to_content_size()
|
|
return
|
|
|
|
add_component(
|
|
ImplantHeader.new("GENERATION LEGEND", "%d layer(s) active" % active_specs.size())
|
|
)
|
|
add_component(ImplantSeparator.new())
|
|
for i: int in range(active_specs.size()):
|
|
var spec: Dictionary = active_specs[i]
|
|
add_component(ImplantTextBlock.new(str(spec.get("title", ""))))
|
|
for row: Dictionary in spec.get("rows", []):
|
|
var text: String = "%s %s" % [str(row.get("glyph", "-")), str(row.get("label", ""))]
|
|
add_component(ImplantDataRow.new(text, row.get("color", Color.TRANSPARENT)))
|
|
if i < active_specs.size() - 1:
|
|
add_component(ImplantSeparator.new())
|
|
reposition()
|
|
reset_to_content_size()
|