OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / dc_world_scene_gate.gd

101 lines · gd ref:
extends SceneTree
## map_dc_world structure + teleporter round-trip gate — §5a.1.
##
##     "$HYPER_TPS_GODOT" --headless --script scenes/test/dc_world_scene_gate.gd
##
## Checks the two things that make the world REACHABLE and LEAVABLE, because both
## fail silently:
##
##   - a .tscn node of an unregistered type degrades to a plain Node rather than
##     erroring (that is how 0.768.0 shipped an inert DCLevel), so "has a
##     DCTerrain" has to be asserted by CLASS, not by name;
##   - a teleporter whose target_map does not resolve is a prop you walk through.
##
## The scene is INSTANTIATED BUT NEVER ADDED TO THE TREE. _ready is what triggers
## the .MAP import, the rebuild and the terrain meshing; none of that belongs in a
## structural check, and dragging it into headless is what hyper-games #16 is
## about. So this asserts wiring only — that the pieces are present and point at
## each other. Whether the level RENDERS correctly inside the terrain is a thing
## you look at, not a thing this gate can claim.

const WORLD := "res://scenes/maps/map_dc_world.tscn"
const ENTRY := "res://scenes/maps/map_test.tscn"

var _failures: Array[String] = []


func _check(label: String, got: Variant, want: Variant) -> void:
	var ok: bool = got == want
	if not ok:
		_failures.append("%s: got %s, want %s" % [label, str(got), str(want)])
	print("  %-34s %-30s %s" % [label, str(got), "OK" if ok else "FAIL (want %s)" % str(want)])


func _find_child_of_class(n: Node, cls: String) -> Node:
	for c in n.get_children():
		if c.is_class(cls):
			return c
	return null


## Every teleporter in a scene, by target_map.
func _teleport_targets(n: Node) -> Dictionary:
	var out := {}
	for c in n.get_children():
		if "target_map" in c and String(c.get("target_map")) != "":
			out[String(c.get("target_map"))] = c.name
	return out


func _initialize() -> void:
	print("=== map_dc_world structure ===")
	var ps: PackedScene = load(WORLD)
	if ps == null:
		print("FAIL — %s did not load" % WORLD)
		quit(1)
		return
	var w: Node = ps.instantiate()

	_check("root script is map_dc_world", w.get_script().resource_path,
		   "res://scenes/maps/map_dc_world.gd")

	# BY CLASS, not by name — see the header.
	var lvl := _find_child_of_class(w, "DCLevel")
	var ter := _find_child_of_class(w, "DCTerrain")
	_check("inherits a DCLevel", lvl != null, true)
	_check("adds a DCTerrain", ter != null, true)
	if lvl != null:
		print("    DCLevel   node '%s' class %s" % [lvl.name, lvl.get_class()])
	if ter != null:
		print("    DCTerrain node '%s' class %s" % [ter.name, ter.get_class()])

	# The inherited pipeline: if these are gone, the subclass is not reusing
	# map_dc_level at all and the duplicate this design exists to avoid is back.
	_check("inherits TeleporterToHome", w.get_node_or_null("TeleporterToHome") != null, true)

	print("\n=== round trip ===")
	var from_world := _teleport_targets(w)
	print("    world teleports to: %s" % str(from_world.keys()))
	_check("world -> entry world", from_world.has(ENTRY), true)

	var entry: Node = (load(ENTRY) as PackedScene).instantiate()
	var from_entry := _teleport_targets(entry)
	print("    entry teleports to: %s" % str(from_entry.keys()))
	_check("entry world -> world", from_entry.has(WORLD), true)

	# A target_map that does not resolve is a teleporter you walk through.
	for target in from_entry.keys():
		_check("entry target exists: %s" % String(target).get_file(),
			   ResourceLoader.exists(String(target)), true)
	for target in from_world.keys():
		_check("world target exists: %s" % String(target).get_file(),
			   ResourceLoader.exists(String(target)), true)

	w.free()
	entry.free()

	print("\nRESULT: %s" % ("PASS" if _failures.is_empty() else "FAIL"))
	for f in _failures:
		print("  - %s" % f)
	quit(0 if _failures.is_empty() else 1)