extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_extractor_income.gd # Covers the extractor's automated hauler: # 1. income accrues with NOTHING about the route loaded (no scene showing # it), because the simulation lives in the GameState autoload # 2. ore is delivered exactly when the hauler reaches the mothership - # the payout and the drawn arrival are the same event, driven by the # same `progress` value, so they cannot drift apart # 3. no mothership means no route and therefore no income # 4. the hauler's drawn position tracks that progress along the route var game_state: Node var stage: int = 0 var elapsed: float = 0.0 # The test has to sit through a whole there-and-back hauler cycle, and the # cycle length is distance/HAULER_SPEED - so this fixture distance IS the # test's runtime. 600 units is ~4.6s of simulated round trip, against ~31s # for the 4000 it used to be. Nothing here asserts on the distance itself, # only on payout and drawn position tracking the same `progress`, so a short # route exercises exactly the same code. var mothership_pos: Vector2 = Vector2(600.0, 0.0) var planet_pos: Vector2 = Vector2.ZERO var rocks_before_arrival: int = 0 func _initialize() -> void: game_state = root.get_node("GameState") game_state.rocks = 0 game_state.buildings.clear() game_state.extractors.clear() # An extractor with NO mothership: there is nowhere to deliver, so it # must not pay out. game_state.add_extractor("Test Planet", planet_pos) func _process(delta: float) -> bool: elapsed += delta match stage: 0: if elapsed < 1.0: return false if game_state.rocks != 0: print("earned %d rocks with no mothership" % game_state.rocks) print("TEST_EXTRACTOR_INCOME: FAIL (income without a delivery point)") quit(1) return true print("no mothership -> no income (rocks=%d)" % game_state.rocks) # Give it somewhere to deliver. game_state.add_building(GameState.BUILDING_MOTHERSHIP, mothership_pos) game_state.extractors[0].progress = 0.0 elapsed = 0.0 stage = 1 return false 1: var e: Dictionary = game_state.extractors[0] var cycle: float = game_state.get_hauler_route_time(e) if cycle <= 0.0: print("TEST_EXTRACTOR_INCOME: FAIL (route never established)") quit(1) return true # Nothing in this scene tree draws the route at all - no Main # scene, no HaulerField, no hauler node. Income must still tick. if e.progress < GameState.HAULER_ARRIVAL: rocks_before_arrival = game_state.rocks # The drawn position must track progress along the route. var pos: Variant = game_state.get_hauler_position(e) var expected: Vector2 = planet_pos.lerp(mothership_pos, e.progress / GameState.HAULER_ARRIVAL) if pos == null or pos.distance_to(expected) > 1.0: print("hauler at %s, expected %s" % [pos, expected]) print("TEST_EXTRACTOR_INCOME: FAIL (drawn position does not follow the simulation)") quit(1) return true return false # Just crossed the arrival point: the payout must have landed on # the very same step, and the hauler must be AT the mothership. var paid: bool = game_state.rocks == rocks_before_arrival + GameState.HAULER_YIELD var at_mothership: Variant = game_state.get_hauler_position(e) var arrived: bool = at_mothership != null and at_mothership.distance_to(mothership_pos) < 40.0 print("arrival: rocks %d -> %d (paid=%s) hauler at %s (arrived=%s)" % [ rocks_before_arrival, game_state.rocks, paid, at_mothership, arrived ]) if not (paid and arrived): print("TEST_EXTRACTOR_INCOME: FAIL (payout and arrival did not coincide)") quit(1) return true rocks_before_arrival = game_state.rocks elapsed = 0.0 stage = 2 return false 2: # It must not pay again until it has been back and returned. var e2: Dictionary = game_state.extractors[0] if e2.progress > GameState.HAULER_ARRIVAL: if game_state.rocks != rocks_before_arrival: print("paid %d extra mid-return" % (game_state.rocks - rocks_before_arrival)) print("TEST_EXTRACTOR_INCOME: FAIL (paid while returning empty)") quit(1) return true return false # Wrapped past the planet and started a new outbound leg. print("returned empty without paying again (rocks=%d)" % game_state.rocks) print("TEST_EXTRACTOR_INCOME: PASS") quit(0) return true return false