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>
99 lines
3.7 KiB
GDScript
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
|