OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_brush_maps.gd

111 lines · gd ref:
extends SceneTree
## Gate: every demo map can build brushes (#93 §3, #94 — user decision 2026-10-02:
## "all four maps, including the initial teleport map").
##
## Brushes need a DCLevel and the brush half of map_dc_level (journal wired to the
## level, BuildDock/builder context, brush view, outliner). Only the Mountain had
## it — by extending map_dc_level with level_import OFF (an empty brush level).
## Flat, Erosion and the hub (map_test, the teleport map) ran map_core with no
## DCLevel, so F4 brushes could not exist there at all.
##
## Structural for all four (instantiate, no tree). Behavioural for the hub, in the
## tree: the journal's level IS the map's DCLevel.
##
## Run: HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_brush_maps.gd

const MAPS := [
	"res://scenes/maps/map_mountain.tscn",
	"res://scenes/maps/map_test.tscn",
	"res://scenes/maps/map_dc_erosion.tscn",
	"res://scenes/maps/map_flat.tscn",
]
const DC_LEVEL_SCRIPT := "res://scenes/maps/map_dc_level.gd"

var ok := true
var _frame := 0


func _p(c: bool, m: String) -> void:
	print(("ok   " if c else "FAIL: ") + m)
	if not c:
		ok = false


func _inherits(script: Script, path: String) -> bool:
	var s := script
	while s != null:
		if s.resource_path == path:
			return true
		s = s.get_base_script()
	return false


## Maps put in the tree one at a time, ~30 frames each: their journal must fold into
## their own DCLevel. (The Mountain's in-tree wiring is covered by its own gates.)
const IN_TREE := [
	"res://scenes/maps/map_test.tscn",
	"res://scenes/maps/map_dc_erosion.tscn",
	"res://scenes/maps/map_flat.tscn",
]
var _idx := -1
var _cur: Node = null
var _cur_path := ""
var _since := 0


func _process(_delta: float) -> bool:
	_frame += 1
	if _frame == 1:
		_structural()
		return false
	if _cur == null:
		_idx += 1
		if _idx >= IN_TREE.size():
			print("GATE brush_maps: %s" % ("PASS" if ok else "FAIL"))
			quit(0 if ok else 1)
			return true
		_cur_path = IN_TREE[_idx]
		var scene := load(_cur_path) as PackedScene
		_cur = scene.instantiate() if scene else null
		if _cur == null:
			_p(false, "%s instantiates" % _cur_path.get_file())
			return false
		root.add_child(_cur)
		_since = 0
		return false
	_since += 1
	if _since == 30:
		_behavioural(_cur, _cur_path.get_file())
		_cur.queue_free()
		_cur = null
	return false


func _structural() -> void:
	for path: String in MAPS:
		var scene := load(path) as PackedScene
		_p(scene != null, "%s loads" % path.get_file())
		if scene == null:
			continue
		var m: Node = scene.instantiate()
		var level: Node = m.get_node_or_null("DCLevel")
		_p(level != null and level.get_class() == "DCLevel", "%s has a DCLevel node" % path.get_file())
		_p(m.get_script() != null and _inherits(m.get_script(), DC_LEVEL_SCRIPT),
			"%s runs the map_dc_level brush editor" % path.get_file())
		# map_mountain sets level_import = false in CODE, before super._ready (its own
		# comment says why); the others set it in their scene. Not-yet-ready nodes only
		# show the scene value, so the Mountain is read from its source instead.
		var off: bool = m.get("level_import") == false
		if path.ends_with("map_mountain.tscn"):
			off = FileAccess.get_file_as_string("res://scenes/maps/map_mountain.gd").contains("level_import = false")
		_p(off, "%s: level_import off (an empty brush level, no START.MAP)" % path.get_file())
		m.free()


func _behavioural(m: Node, name: String) -> void:
	print("-- %s in the tree" % name)
	var level: Node = m.get_node_or_null("DCLevel")
	var persist: Node = m.get("_persist")
	_p(persist != null, "%s has its brush journal (LevelPersistence)" % name)
	_p(persist != null and level != null and persist.get("level") == level, "%s: the journal folds into its DCLevel" % name)