diff --git a/client/ui/implant/apps/atlas/atlas_marker_overlay.gd b/client/ui/implant/apps/atlas/atlas_marker_overlay.gd index 31c171f55..39e5c7e50 100644 --- a/client/ui/implant/apps/atlas/atlas_marker_overlay.gd +++ b/client/ui/implant/apps/atlas/atlas_marker_overlay.gd @@ -32,8 +32,11 @@ const COLOR_PRODUCTION: Color = Color(0.40, 0.80, 0.55, 0.18) const COLOR_SHADOW: Color = Color(0.35, 0.20, 0.50, 0.22) const COLOR_CORP: Color = Color(0.85, 0.65, 0.20, 0.75) # Generation overlays (#960, D-225, Araminta's encoding). -const COLOR_GEN_RIVER: Color = Color(0.35, 0.60, 0.90, 0.75) -const COLOR_GEN_MOUTH: Color = Color(0.50, 0.80, 1.0, 0.90) +# Rivers/mouths use the reliefmap's own water colour (sampled median ocean blue) +# so river lines blend seamlessly into the surface water bodies instead of +# reading as a distinct-coloured line flowing onto the sea. +const COLOR_GEN_RIVER: Color = Color(0.353, 0.647, 0.776, 1.0) +const COLOR_GEN_MOUTH: Color = Color(0.353, 0.647, 0.776, 1.0) const COLOR_GEN_BASIN_FILL: Color = Color(0.20, 0.35, 0.55, 0.06) const COLOR_GEN_BASIN_LINE: Color = Color(0.45, 0.65, 0.85, 0.45) const GEN_ATTRACTOR_MIN_STRENGTH: float = 0.15 @@ -55,6 +58,14 @@ const PRODUCTION_FUNCTIONS: Array = [ var viewer = null # AtlasViewer (untyped to avoid cyclic ref) +# Generation-overlay coordinate mapping (#960): set in _draw before the gen +# layers are drawn. Positions are [row, col] in the Layer-1 working grid +# (_gen_grid_*), mapped onto the displayed heightmap texture (_gen_tex_*). +var _gen_grid_w: float = 0.0 +var _gen_grid_h: float = 0.0 +var _gen_tex_w: float = 0.0 +var _gen_tex_h: float = 0.0 + func _draw() -> void: if viewer == null: @@ -102,12 +113,19 @@ func _draw() -> void: # Draw order: basins (area) under rivers under attractors (point anchors). var layer1: Variant = viewer.get_generation_layer1() if layer1 is Dictionary: - if viewer.is_overlay_visible("gen_l1_basins"): - _draw_gen_basins(layer1) - if viewer.is_overlay_visible("gen_l1_rivers"): - _draw_gen_rivers(layer1) - if viewer.is_overlay_visible("gen_l1_attractors"): - _draw_gen_attractors(layer1) + # The Layer1Output carries the working-grid dims its positions live in + # (#960). Map from those, NOT the markers.json grid (_grid_w/_grid_h). + _gen_grid_w = float(layer1.get("grid_w", 0)) + _gen_grid_h = float(layer1.get("grid_h", 0)) + _gen_tex_w = tex_w + _gen_tex_h = tex_h + if _gen_grid_w > 0.0 and _gen_grid_h > 0.0: + if viewer.is_overlay_visible("gen_l1_basins"): + _draw_gen_basins(layer1) + if viewer.is_overlay_visible("gen_l1_rivers"): + _draw_gen_rivers(layer1) + if viewer.is_overlay_visible("gen_l1_attractors"): + _draw_gen_attractors(layer1) # POIs (non-gate first, then gates on top if enabled) _draw_pois(markers) @@ -359,9 +377,12 @@ func _draw_corp_presence(markers: Dictionary) -> void: # ============================================================================= -## Map a Layer-1 [row, col] grid position to canvas space. +## Map a Layer-1 [row, col] position (in the Layer1Output working grid) onto the +## displayed heightmap texture. Uses the grid dims FROM THE DATA — NOT +## viewer.grid_to_canvas, whose _grid_w/_grid_h is the markers.json coordinate +## space. Conflating the two scaled the overlays off by the downsample ratio (#960). func _gen_pos(rc: Variant) -> Vector2: - return viewer.grid_to_canvas(Vector2(float(rc[1]), float(rc[0]))) + return Vector2(float(rc[1]) / _gen_grid_w * _gen_tex_w, float(rc[0]) / _gen_grid_h * _gen_tex_h) func _draw_gen_rivers(layer1: Dictionary) -> void: diff --git a/client/ui/implant/apps/atlas/atlas_viewer.gd b/client/ui/implant/apps/atlas/atlas_viewer.gd index 115b334d8..b800fbdde 100644 --- a/client/ui/implant/apps/atlas/atlas_viewer.gd +++ b/client/ui/implant/apps/atlas/atlas_viewer.gd @@ -182,7 +182,7 @@ var _city_panel = null # ImplantPanel sidebar (city data) var _empty_notice = null # ImplantPanel shown when heightmap missing var _overlay_bar = null # #836 overlay toggle bar (no class_name, review #8) var _screen_header: ImplantHeader = null # top-left title/hint (D-169 composition) -var _gen_pending_indicator: ImplantPending = null # #960 Layer-1 "generating" overlay +var _gen_pending_indicator = null # ImplantPending — loaded by path, not a class_name dep func _ready() -> void: @@ -220,7 +220,10 @@ func _ready() -> void: SimBridge.atlas_layers_received.connect(_on_atlas_layers_received) # #960: diegetic "generating" indicator, shown only while the proxy is Pending. - _gen_pending_indicator = ImplantPending.new() + # Loaded by path (not `ImplantPending.new()`) so a stale global-class cache — + # e.g. a running session that hasn't re-imported after this class was added — + # can't fail to parse AtlasViewer and break the atlas from opening (#960). + _gen_pending_indicator = load("res://ui/implant/implant_pending.gd").new() _gen_pending_indicator.name = "GenPending" add_child(_gen_pending_indicator) _gen_pending_indicator.apply_implant_theme(_implant_theme) @@ -359,7 +362,7 @@ func _position_pending_indicator() -> void: if _gen_pending_indicator == null: return _gen_pending_indicator.position = Vector2( - (size.x - ImplantPending.DEFAULT_WIDTH) * 0.5, size.y * 0.45 + (size.x - _gen_pending_indicator.DEFAULT_WIDTH) * 0.5, size.y * 0.45 )