From 2eb779e5de7dedbf1ab46f71a8a918787c22c3f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Wisniewski?= Date: Tue, 11 Aug 2026 15:43:43 +0200 Subject: [PATCH] Show the extractor derrick the moment it is built, not on respawn Follow-up to #9: the marker was resolved once in Planet._ready() on the assumption that extractors are built on PlanetSurface.tscn, so a Planet node could never outlive a change to its own answer. That assumption was wrong. Extractors are built from SPACE, via BuildController._planet_under_fleet() with the fleet parked on the planet, so the Planet node is already in the tree with _ready() long since run. The derrick therefore did not appear until the planet happened to despawn and respawn - building one read as doing nothing. GameState now emits extractor_added, and Planet keeps its marker in step with it, redrawing only when its own answer actually changes. The test missed this because it spawned a fresh planet after registering the extractor, which is not the order the game does it in. It now keeps a planet alive across the build and asserts the derrick appears on that node. Co-Authored-By: Claude Opus 5 --- GameState.gd | 8 ++++++++ Planet.gd | 19 ++++++++++++++++--- tests/test_planet_extractor_marker.gd | 19 ++++++++++++++++++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/GameState.gd b/GameState.gd index dfd45e2..28cb105 100644 --- a/GameState.gd +++ b/GameState.gd @@ -270,12 +270,20 @@ func has_extractor_at(planet_name: String) -> bool: return false +signal extractor_added(planet_name: String) + + func add_extractor(planet_name: String, at_position: Vector2) -> void: extractors.append({ "position": at_position, "planet_name": planet_name, "progress": 0.0, }) + # An extractor is built from SPACE, with the fleet parked on the planet - + # so the Planet node is already in the tree with its _ready() long since + # run. Without this it kept showing no derrick until it happened to + # despawn and respawn, which reads as the build having done nothing. + extractor_added.emit(planet_name) func get_hauler_route_time(extractor: Dictionary) -> float: diff --git a/Planet.gd b/Planet.gd index b682030..f3dbc4a 100644 --- a/Planet.gd +++ b/Planet.gd @@ -29,9 +29,12 @@ const EXTRACTOR_MARKER_COLOR: Color = Color(1.0, 0.85, 0.3) const DERRICK_HEIGHT: float = 44.0 const DERRICK_HALF_WIDTH: float = 16.0 -# Resolved once in _ready() rather than polled: building an extractor happens -# on PlanetSurface.tscn, and coming back to space rebuilds Main.tscn from -# scratch, so a Planet node never outlives a change to its own answer. +# Kept in step with GameState.extractor_added rather than polled per frame. +# An extractor is built from SPACE (BuildController._planet_under_fleet(), +# with the fleet parked on the planet), so this node is already in the tree +# with its _ready() long since run when the answer changes - resolving it +# only at _ready() left the planet looking untouched until it happened to +# despawn and respawn. var shows_extractor: bool = false @@ -47,6 +50,16 @@ func _ready() -> void: var radius: float = rng.randf_range(70.0, 100.0) shape_points.append(Vector2(cos(angle), sin(angle)) * radius) shows_extractor = GameState.has_extractor_at(planet_name) + GameState.extractor_added.connect(_on_extractor_added) + queue_redraw() + + +func _on_extractor_added(built_on: String) -> void: + # Only the planet it was actually built on, and only a redraw when the + # answer really changed - every other planet in range gets this too. + if built_on != planet_name or shows_extractor: + return + shows_extractor = true queue_redraw() diff --git a/tests/test_planet_extractor_marker.gd b/tests/test_planet_extractor_marker.gd index 2104da5..ed4b4cb 100644 --- a/tests/test_planet_extractor_marker.gd +++ b/tests/test_planet_extractor_marker.gd @@ -29,6 +29,7 @@ var map_content: Node2D var planet_chunk: Vector2i var planet_name: String = "" var stage: int = 0 +var already_spawned: Node2D func _initialize() -> void: @@ -78,13 +79,29 @@ func _process(_delta: float) -> bool: print("TEST_PLANET_EXTRACTOR_MARKER: FAIL (map badge with no extractor)") quit(1) return true - clean.queue_free() print("no extractor -> no derrick, no badge (planet '%s')" % planet_name) + # Deliberately keep this planet ALIVE across the build. An + # extractor is built from space with the fleet parked on the + # planet (BuildController._planet_under_fleet()), so the node is + # already in the tree with _ready() long since run. Resolving the + # marker only at _ready() meant the planet kept looking untouched + # until it happened to despawn and respawn - the build read as + # having done nothing at all. Spawning a fresh planet afterwards + # (as the checks below do) cannot catch that. + already_spawned = clean game_state.add_extractor(planet_name, WorldGen.chunk_origin(planet_chunk)) stage = 1 return false 1: + # --- the planet that was already on screen when it was built -- + if not already_spawned.shows_extractor: + print("TEST_PLANET_EXTRACTOR_MARKER: FAIL (a planet already in the tree never picked up its derrick - the build reads as doing nothing)") + quit(1) + return true + print("planet already in the tree picked up its derrick on build") + already_spawned.queue_free() + # --- extractor built ---------------------------------------- var mined: Node2D = _spawn_planet() if not mined.shows_extractor: