spaceship/tests/test_map_building_markers.gd
Loïc Wisniewski 8dd5d7b78f Show buildings on the world map, and extractors on their planets
Forgejo #11: buildings the player has placed now appear on the world
map. They are placed freely in space rather than tied to a chunk, so
they get their own pass in WorldMapContent._draw() rather than riding
along with the chunk markers. Each type has its own shape and colour,
mirroring Building.gd's constants so a marker reads as the same
building you built.

Forgejo #9: a planet with an extractor built on it now shows it, in
both places you look at a planet - an amber derrick on its outline in
space, and a matching badge on its world-map marker. The extractor
lives on PlanetSurface.tscn, a scene you are not looking at from
space, so an already-mined planet was otherwise indistinguishable
from an untouched one until you landed on it.

The positioning steps are factored out (building_markers(),
chunk_has_extractor_badge(), extractor_derrick_tip()) so headless
tests can assert on them - --headless has no GPU and draws no pixels,
so there is nothing to check in _draw() itself. Both features were
also rendered for real via tests/screenshot.sh and looked at.

extractor_derrick_tip() exists because the first version of the
"derrick is not buried inside the planet" check measured the furthest
of all four derrick points, which passes a derrick pointing straight
into the ground: the base corners sit a half-width off the rim vertex
and clear the outline on their own. Mutation-testing caught it.

screenshot.sh now clears a stale /tmp/.X99-lock. A run killed
mid-screenshot leaves it behind and Xvfb then refuses :99, which
resurfaces later as Godot's misleading "X11 Display is not available".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 15:29:56 +02:00

99 lines
3.7 KiB
GDScript

extends SceneTree
# Headless gameplay test: run with
# godot --headless -s res://tests/test_map_building_markers.gd
# Forgejo issue #11: a building the player has placed must appear on the
# world map. Without it the map showed explored chunks, planets and the
# fleet, but nothing the player had actually built - so there was no way to
# find your own base again once you had flown away from it.
#
# Buildings are not tied to a chunk (they are placed freely in space), so
# they cannot ride along with the chunk markers and need their own pass.
#
# --headless has no GPU and draws no pixels, so this cannot assert on what
# was rendered. It asserts on building_markers(), the step that turns
# GameState.buildings into map-local marker data, which is the part that
# _draw() then simply stamps out.
#
# Verifies:
# 1. no buildings -> no markers (so a passing count is not just "always 3")
# 2. every placed building produces exactly one marker
# 3. markers land at the building's world position scaled into map space
# 4. the three building types are told apart by colour, rather than all
# being drawn as the same anonymous dot
var scene: Node
var game_state: Node
var map_content: Node2D
var stage: int = 0
const MOTHERSHIP_AT: Vector2 = Vector2(2000.0, -1200.0)
const CRAFTER_AT: Vector2 = Vector2(-800.0, 400.0)
const COMMAND_AT: Vector2 = Vector2(150.0, 3000.0)
func _initialize() -> void:
game_state = root.get_node("GameState")
game_state.buildings.clear()
game_state.extractors.clear()
scene = load("res://Main.tscn").instantiate()
root.add_child(scene)
map_content = scene.get_node("UI/WorldMap/MapContent")
func _process(_delta: float) -> bool:
match stage:
0:
# Nothing built yet: the map must show no building markers at all.
var empty: Array = map_content.building_markers()
if not empty.is_empty():
print("TEST_MAP_BUILDING_MARKERS: FAIL (%d markers with nothing built)" % empty.size())
quit(1)
return true
game_state.add_building(GameState.BUILDING_MOTHERSHIP, MOTHERSHIP_AT)
game_state.add_building(GameState.BUILDING_SHIP_CRAFTER, CRAFTER_AT)
game_state.add_building(GameState.BUILDING_COMMAND_CENTER, COMMAND_AT)
stage = 1
return false
1:
var markers: Array = map_content.building_markers()
print("markers=%d for %d buildings" % [markers.size(), game_state.buildings.size()])
if markers.size() != 3:
print("TEST_MAP_BUILDING_MARKERS: FAIL (expected one marker per building)")
quit(1)
return true
var scale: float = map_content.WORLD_TO_LOCAL
var expected: Dictionary = {
GameState.BUILDING_MOTHERSHIP: MOTHERSHIP_AT * scale,
GameState.BUILDING_SHIP_CRAFTER: CRAFTER_AT * scale,
GameState.BUILDING_COMMAND_CENTER: COMMAND_AT * scale,
}
var colours: Array = []
for m in markers:
if not expected.has(m.type):
print("TEST_MAP_BUILDING_MARKERS: FAIL (unexpected marker type %s)" % m.type)
quit(1)
return true
var want: Vector2 = expected[m.type]
if m.position.distance_to(want) > 0.001:
print("%s marker at %s, expected %s" % [m.type, m.position, want])
print("TEST_MAP_BUILDING_MARKERS: FAIL (marker is not where the building is)")
quit(1)
return true
colours.append(m.color)
# Three buildings drawn in one indistinguishable colour would
# satisfy every check above and still be useless to read.
if colours[0] == colours[1] or colours[1] == colours[2] or colours[0] == colours[2]:
print("colours=%s" % [colours])
print("TEST_MAP_BUILDING_MARKERS: FAIL (building types are not visually distinct)")
quit(1)
return true
print("all 3 markers positioned correctly, colours distinct: %s" % [colours])
print("TEST_MAP_BUILDING_MARKERS: PASS")
quit(0)
return true
return false