extends SceneTree
## Gate: the walker keeps looking for its body, and finds the REAL one.
##
## THE BUG THIS EXISTS FOR. walker.gd looked the player up once in _bot_init,
## warned "no local player to drive", and returned. Nothing ever tried again, so
## a brain registered a moment before the player existed was dead for its whole
## life. hyper-games #77 item 4 reports it failing "against the real tree every
## time" -- which a one-shot lookup produces whatever the underlying timing is:
## an admin command can land before on_game_started spawns anything, and on a
## --dedicated peer nothing spawns until /start.
##
## ⛔ AND ITS GATE PROVED NOTHING. gate_walker_bot.gd builds its own Game and
## Players and attaches scenes/test/stub_local_player.gd -- a script whose
## is_local_player() returns true unconditionally. It asserted against a tree it
## had constructed to satisfy the code under test. #77 says so directly. This
## gate instances the REAL player_procedural.tscn and uses the REAL
## PlayerPosition autoload, so the thing being found is the thing that ships.
var ok := true
var _ran := false
const PLAYER_SCENE := "res://scenes/player/player_procedural.tscn"
func _p(c: bool, m: String) -> void:
print(("ok " if c else "FAIL: ") + m)
if not c:
ok = false
func _process(_delta: float) -> bool:
if _ran:
return true
_ran = true
_run()
print("GATE walker_acquire: %s" % ("PASS" if ok else "FAIL"))
if not ok:
quit(1)
return true
func _run() -> void:
var pp: Node = root.get_node_or_null("PlayerPosition")
_p(pp != null, "PlayerPosition autoload present")
if pp == null:
return
_p(pp.has_method("get_local_player"),
"PlayerPosition exposes get_local_player() -- the maintained handle")
if not pp.has_method("get_local_player"):
return
_p(pp.get_local_player() == null, "with nothing attached it returns null")
var brain_script: GDScript = load("res://bots/walker.gd")
_p(brain_script != null, "walker.gd loads")
if brain_script == null:
return
# ---- 1. no body yet: init must NOT be terminal ----
var w: Object = brain_script.new()
w.call("_bot_init")
_p(w.get("_input") == null, "with no player, the walker has not acquired one")
# A tick before anything exists must not throw and must not acquire.
w.call("_bot_tick", 2.0)
_p(w.get("_input") == null, "and a tick with no player still acquires nothing")
# ---- 2. the REAL player scene appears afterwards ----
var ps: PackedScene = load(PLAYER_SCENE)
_p(ps != null, "the real player scene loads")
if ps == null:
return
var player: Node = ps.instantiate()
player.name = "RealPlayer"
root.add_child(player)
_p(player.get_node_or_null(^"InputSync") != null,
"the real player carries an InputSync (this is what the stub faked)")
pp.call("attach_local_player", player)
_p(pp.get_local_player() == player, "PlayerPosition now holds the real player")
# The walker must pick it up on a later tick -- the whole point of the fix.
w.call("_bot_tick", 2.0)
var acquired: Variant = w.get("_input")
_p(acquired != null, "the walker acquires the player that appeared AFTER _bot_init")
if acquired != null:
_p(bool((acquired as Node).get("driven")),
"and sets driven, so player_input.gd stands down")
# ---- 3. it drives through the input fields, not a position ----
var before: Variant = (acquired as Node).get("camera_rotation_y") if acquired != null else null
w.call("_bot_tick", 1.0)
if acquired != null:
_p((acquired as Node).get("camera_rotation_y") != before,
"ticking steers via camera_rotation_y")
_p((acquired as Node).get("input_direction") == Vector2(0.0, -1.0),
"and holds forward on input_direction")
# ---- 4. releasing hands the player back ----
w.call("_bot_exit")
if acquired != null:
_p(not bool((acquired as Node).get("driven")),
"_bot_exit clears driven -- a peer is not left unresponsive")
# SELF-CHECK: the acquisition above must depend on the player actually being
# there. Detach it and a fresh brain must NOT acquire, or step 2 proved only
# that _bot_tick runs.
pp.call("detach_local_player")
player.queue_free()
var w2: Object = brain_script.new()
w2.call("_bot_init")
w2.call("_bot_tick", 2.0)
_p(w2.get("_input") == null,
"self-check: with the player detached, a fresh walker acquires nothing")