OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_no_default_creatures.gd

93 lines · gd ref:
extends SceneTree
## GATE — NO DEFAULT CREATURES (user, 2026-10-13): "remove the default creatures from our
## maps for the time being and if people want to add them into their specific rooms
## that is up to them". A room adds creatures as placed bots (the `bot` object).
##   N1  no shipped map scene carries a spawner (the old CreatureSpawner or the new
##       spawner object)
##   N2  an older session's world:creatures:registry: rows draw NOTHING: the creature
##       system that read them is retired (#114 phase 5 — creatures are bots); drawn
##       with no simulator they were ghost creatures, frozen forever
##   N3  those rows are kept (nothing is deleted)
##
## Run: CI=true HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_no_default_creatures.gd

const MAP := "res://scenes/maps/map_flat.tscn"
const ROW := "world:creatures:registry:crawler_leftover"

var ok := true
var checks := 0
var _frame := 0
var _game: Node
var _gs: Node


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


func _creature_nodes() -> Array:
	var out: Array = []
	for n in root.find_children("*", "", true, false):
		var s: Script = n.get_script()
		if s != null and ["GroundCrawler", "FlyingSerpent", "Hopper"].has(String(s.get_global_name())) \
				and not n.is_queued_for_deletion():
			out.append(n)
	return out


func _listener(map: Node) -> Node:
	return map.get_node_or_null(^"CreatureSyncListener") if map != null else null


## Every .tscn under res://scenes/maps that instances the spawner script.
func _maps_with_spawner(dir: String) -> Array:
	var out: Array = []
	var d := DirAccess.open(dir)
	if d == null:
		return out
	for f: String in d.get_files():
		var text := FileAccess.get_file_as_string(dir.path_join(f)) if f.ends_with(".tscn") else ""
		if text.contains("creature_spawner.gd") or text.contains("spawner_object.gd") or text.contains("prefabs/spawner.tscn"):
			out.append(dir.path_join(f))
	for sub: String in d.get_directories():
		out.append_array(_maps_with_spawner(dir.path_join(sub)))
	return out


func _process(_d: float) -> bool:
	_frame += 1
	match _frame:
		1:
			var found := _maps_with_spawner("res://scenes/maps")
			_p(found.is_empty(), "N1 no shipped map carries a spawner", found)
			_game = (load("res://scenes/game/game.tscn") as PackedScene).instantiate()
			_game.name = "Game"
			root.add_child(_game)
		30:
			_game.call("change_map", MAP, Vector3.ZERO)
		45:
			_gs = root.get_node("/root/GameState")
			var keet: Node = root.get_node_or_null("/root/KeetService")
			if keet != null:
				keet.current_member_id = "gate-author"
			_gs.route("world:creatures:registry:*", 1, "peer")
			# An older session's row, arriving with the room's state for this map.
			_gs.set_state(ROW, {"type": "crawler", "seed": 3, "pos": [6.0, 0.0, 6.0]})
			_p(_gs.get_state(ROW) != null, "precondition: the left-over row is in state")
		50:
			var map: Node = _game.get("current_map")
			_p(map != null and String(map.scene_file_path) == MAP, "on " + MAP.get_file())
			_p(_listener(map) == null, "N2 no creature listener exists")
			_p(_creature_nodes().is_empty(), "N2 and nothing draws the left-over row", _creature_nodes().size())
			_p(_gs.get_state(ROW) != null, "N3 the left-over row is kept")
			if checks < 5:          # a gate that died early must not read as a pass
				ok = false
				print("FAIL: only %d checks ran -- the gate died before its assertions" % checks)
			print("\n%d checks — %s" % [checks, "PASS" if ok else "FAIL"])
			quit(0 if ok else 1)
			return true
	return false