spaceship/tests/test_planet_identity_persists.gd

122 lines
3.9 KiB
GDScript3
Raw Normal View History

extends SceneTree
# Headless gameplay test: run with
# godot --headless -s res://tests/test_planet_identity_persists.gd
# Regression test for: leaving a planet's surface and coming back
# respawned Main.tscn's PlanetField from scratch, and since planet
# name/position/shape were picked with the unseeded global RNG at spawn
# time, the "same" planet (same chunk) would come back with a different
# name, a different position within the chunk, and a different outline
# shape every single time. Fixed by seeding planet identity purely from
# the chunk's own coordinates (mirrors WorldGen's own deterministic noise
# pattern), so this loads Main.tscn twice and checks the planet at the
# same chunk is identical both times.
var target_chunk: Vector2i
var first_name: String
var first_pos: Vector2
var first_shape: PackedVector2Array
var stage: int = 0
var main_scene: Node
var elapsed: float = 0.0
func _initialize() -> void:
var found: bool = false
for x in range(-15, 15):
for y in range(-15, 15):
var chunk: Vector2i = Vector2i(x, y)
if WorldGen.has_planet_at_chunk(chunk):
target_chunk = chunk
found = true
break
if found:
break
if not found:
print("FAIL: could not find any chunk with a planet to test with")
quit(1)
return
_load_main()
func _load_main() -> void:
# GameState (an autoload) isn't accessible as a bare global identifier
# from a script that extends SceneTree directly - fetch it via root.
var game_state: Node = root.get_node("GameState")
game_state.has_return_position = true
game_state.surviving_ship_count = 5
game_state.return_world_position = WorldGen.chunk_origin(target_chunk) + Vector2.ONE * WorldGen.CHUNK_SIZE * 0.5
game_state.return_camera_zoom = 1.0
var packed: PackedScene = load("res://Main.tscn")
main_scene = packed.instantiate()
root.add_child(main_scene)
main_scene.get_node("AsteroidField").set_process(false)
main_scene.get_node("EnemySpawner").set_process(false)
func _process(_delta: float) -> bool:
match stage:
0:
# Wait one tick for _ready() (and the forced synchronous
# update_chunks() call inside it) to have actually run.
stage = 1
return false
1:
var planet_field: Node = main_scene.get_node("PlanetField")
if not planet_field.populated_chunks.has(target_chunk):
print("FAIL: planet never spawned in target chunk on first load")
quit(1)
return true
var planet: Node2D = planet_field.populated_chunks[target_chunk][0]
first_name = planet.planet_name
first_pos = planet.position
first_shape = planet.shape_points.duplicate()
main_scene.queue_free()
stage = 2
return false
2:
# Give queue_free() a moment to actually clear the old tree
# before loading a fresh Main.tscn on top of it.
elapsed += 1.0
if elapsed < 3.0:
return false
_load_main()
elapsed = 0.0
stage = 3
return false
3:
stage = 4
return false
4:
var planet_field: Node = main_scene.get_node("PlanetField")
if not planet_field.populated_chunks.has(target_chunk):
print("FAIL: planet never spawned in target chunk on second load")
quit(1)
return true
var planet: Node2D = planet_field.populated_chunks[target_chunk][0]
var name_ok: bool = planet.planet_name == first_name
var pos_ok: bool = planet.position.distance_to(first_pos) < 0.01
var shape_ok: bool = planet.shape_points.size() == first_shape.size()
if shape_ok:
for i in range(first_shape.size()):
if planet.shape_points[i].distance_to(first_shape[i]) > 0.01:
shape_ok = false
break
print("name: %s vs %s (ok=%s)" % [first_name, planet.planet_name, name_ok])
print("pos: %s vs %s (ok=%s)" % [first_pos, planet.position, pos_ok])
print("shape_ok=%s" % shape_ok)
if name_ok and pos_ok and shape_ok:
print("TEST_PLANET_IDENTITY_PERSISTS: PASS")
quit(0)
else:
print("TEST_PLANET_IDENTITY_PERSISTS: FAIL")
quit(1)
return true
return false