diff --git a/client/ui/implant/atlas_marker_overlay.gd b/client/ui/implant/atlas_marker_overlay.gd index 8ef9134b4..d5d46b195 100644 --- a/client/ui/implant/atlas_marker_overlay.gd +++ b/client/ui/implant/atlas_marker_overlay.gd @@ -35,6 +35,11 @@ const COLOR_CORP: Color = Color(0.85, 0.65, 0.20, 0.75) const RAIL_DASH_ON: float = 6.0 const RAIL_DASH_OFF: float = 4.0 +const PRODUCTION_FUNCTIONS: Array = [ + "industrial", "industry", "manufacturing", "extraction", "mining", + "refinery", "foundry", "shipyard", "production", +] + var viewer = null # AtlasViewer (untyped to avoid cyclic ref) @@ -200,24 +205,44 @@ func _draw_population_density(markers: Dictionary) -> void: draw_circle(pos, r, COLOR_POP_HEAT) +# Review #3: derive overlays from existing cities[] fields. The original +# code read production_zones / shadow_zones / corp_presence from keys that +# are not in the D-191 §8 markers schema, so toggling was a silent no-op. +# When the server schema lands with explicit zone polygons we can promote +# these back to dedicated arrays; until then the MVP reads what's there. func _draw_production_zones(markers: Dictionary) -> void: - for zone: Dictionary in markers.get("production_zones", []): - var p: Vector2 = _center_to_canvas(zone.get("center")) - var radius: float = float(zone.get("radius", 12.0)) - draw_circle(p, radius, COLOR_PRODUCTION) + for city: Dictionary in markers.get("cities", []): + var func_id: String = str(city.get("primary_function", "")).to_lower() + if not PRODUCTION_FUNCTIONS.has(func_id): + continue + var pos: Vector2 = viewer._city_canvas_pos(city) + var tier: int = int(city.get("population_tier", 1)) + var radius: float = 10.0 + float(tier) * 4.0 + draw_circle(pos, radius, COLOR_PRODUCTION) func _draw_shadow_economy(markers: Dictionary) -> void: - for zone: Dictionary in markers.get("shadow_zones", []): - var p: Vector2 = _center_to_canvas(zone.get("center")) - var radius: float = float(zone.get("radius", 16.0)) - draw_circle(p, radius, COLOR_SHADOW) + # Broad bands around cities outside the Commission's reach. Intentionally + # soft + overlapping — D-181 treats shadow zones as "broad bands", not + # precise polygons. + for city: Dictionary in markers.get("cities", []): + var commission: bool = bool(city.get("commission_presence", false)) + if commission: + continue + var pos: Vector2 = viewer._city_canvas_pos(city) + var tier: int = int(city.get("population_tier", 1)) + var radius: float = 18.0 + float(tier) * 6.0 + draw_circle(pos, radius, COLOR_SHADOW) func _draw_corp_presence(markers: Dictionary) -> void: - for corp: Dictionary in markers.get("corp_presence", []): - var p: Vector2 = _center_to_canvas(corp.get("pos")) - draw_rect(Rect2(p - Vector2(3, 3), Vector2(6, 6)), COLOR_CORP) + # Tier 1 corp presence — keyed off the Commission-presence flag on cities + # until D-191 §8 schema explicitly lists per-corp dots. + for city: Dictionary in markers.get("cities", []): + if not bool(city.get("commission_presence", false)): + continue + var pos: Vector2 = viewer._city_canvas_pos(city) + draw_rect(Rect2(pos - Vector2(3, 3), Vector2(6, 6)), COLOR_CORP) # ============================================================================= diff --git a/client/ui/implant/atlas_panel.gd b/client/ui/implant/atlas_panel.gd index c77b29e01..eaff87e13 100644 --- a/client/ui/implant/atlas_panel.gd +++ b/client/ui/implant/atlas_panel.gd @@ -230,18 +230,34 @@ func _compute_body_positions() -> void: angle = -PI / 2.0 + TAU * float(i) / float(count) _body_positions[b["body_id"]] = center + Vector2(cos(angle), sin(angle)) * ring_r - # Place moons near their parent body + # Place moons near their parent body. Moons per parent count drives the + # angular spacing — a hard-coded divisor made the 5th+ moon overlap moon 1 + # and become unclickable on gas giants with many satellites (review #1). + var moons_by_parent: Dictionary = {} for b: Dictionary in _orbital_bodies: var parent_id: Variant = b.get("parent_body_id") if parent_id == null: continue - if not _body_positions.has(str(parent_id)): + var key: String = str(parent_id) + if not moons_by_parent.has(key): + moons_by_parent[key] = [] + moons_by_parent[key].append(b) + for parent_key: String in moons_by_parent: + if not _body_positions.has(parent_key): continue - var parent_pos: Vector2 = _body_positions[str(parent_id)] - var moon_idx: int = int(b.get("orbit_index", 1)) - # Distribute moons around parent at evenly spaced angles - var angle: float = -PI / 2.0 + TAU * float(moon_idx - 1) / 4.0 - _body_positions[b["body_id"]] = parent_pos + Vector2(cos(angle), sin(angle)) * MOON_ORBIT_RADIUS + var siblings: Array = moons_by_parent[parent_key] + siblings.sort_custom( + func(a: Dictionary, b: Dictionary) -> bool: + return int(a.get("orbit_index", 0)) < int(b.get("orbit_index", 0)) + ) + var parent_pos: Vector2 = _body_positions[parent_key] + var count: int = siblings.size() + for i: int in range(count): + var moon: Dictionary = siblings[i] + var angle: float = -PI / 2.0 + TAU * float(i) / float(count) + _body_positions[moon["body_id"]] = ( + parent_pos + Vector2(cos(angle), sin(angle)) * MOON_ORBIT_RADIUS + ) # Place stations near their parent body (offset right + slightly up) for s: Dictionary in _orbital_stations: diff --git a/client/ui/implant/atlas_viewer.gd b/client/ui/implant/atlas_viewer.gd index 25e958e5b..f19d3f0a6 100644 --- a/client/ui/implant/atlas_viewer.gd +++ b/client/ui/implant/atlas_viewer.gd @@ -177,7 +177,13 @@ func get_markers() -> Dictionary: func _load_heightmap() -> void: + # Reset to sentinel defaults so a body with no heightmap (or that fails to + # load) doesn't inherit the previous body's dimensions — otherwise + # grid_to_canvas would project markers using the prior texture size + # (review #2). _heightmap_texture = null + _tex_w = 1024.0 + _tex_h = 512.0 var ref: Variant = _body.get("terrain_reference") if ref == null or str(ref).is_empty(): return @@ -196,6 +202,8 @@ func _load_heightmap() -> void: func _load_markers() -> void: _markers = {} + # Reset grid dims alongside _tex_* in _load_heightmap so we start from a + # known baseline regardless of which body ran previously. _grid_w = _tex_w _grid_h = _tex_h