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