extends Node
## GATE — keymap step 3's NEW commands (docs/keymap-blender-279.md §3a), each through its key.
##
## A toggles select all / none · Ctrl+I inverts · B + a drag box-selects
## Shift+A opens the add menu; its Stairs row starts the create tool on stairs
## U opens the texture menu with every texture command (each with its key)
## N collapses / restores the right column · Tab element <-> brush mode
## Z toggles the wireframe · Shift+R repeats the last command (here: G -> move tool)
##
## RUN AS A SCENE: "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_keymap_step3.tscn
var fails := 0
var checks := 0
var _cam: Camera3D = null
func _ok(what: String, cond: bool, note: Variant = "") -> void:
checks += 1
if not cond:
fails += 1
print(" %-64s %s %s" % [what, "OK" if cond else "**FAIL**", str(note)])
func _ready() -> void:
var gs: Node = get_tree().root.get_node_or_null("/root/GameState")
gs.route("world:level:*", 1, "peer")
var keet: Node = get_tree().root.get_node_or_null("/root/KeetService")
if keet != null:
keet.current_member_id = "gate-author"
get_tree().root.get_node("/root/GameMode").set_mode("build")
get_tree().root.size = Vector2i(1920, 1080) # headless is 64x64 (see memory)
var map: Node3D = (load("res://scenes/maps/map_test.tscn") as PackedScene).instantiate()
add_child(map)
_cam = Camera3D.new()
add_child(_cam)
_cam.current = true
for i in 6:
await get_tree().process_frame
await _run(map)
get_tree().root.get_node("/root/GameMode").set_mode("play")
if checks < 16:
print(" **FAIL** only %d checks ran -- setup died before the assertions" % checks)
fails += 1
print("\nGATE keymap_step3: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
get_tree().quit(0 if fails == 0 else 1)
func _frames(n: int) -> void:
for i in n:
await get_tree().process_frame
func _key(code: Key, ctrl := false, shift := false, alt := false) -> InputEventKey:
var e := InputEventKey.new()
e.keycode = code
e.physical_keycode = code
e.pressed = true
e.ctrl_pressed = ctrl
e.shift_pressed = shift
e.alt_pressed = alt
return e
func _send(map: Node, e: InputEventKey) -> void:
map._unhandled_key_input(e)
await _frames(2)
func _run(map: Node3D) -> void:
var level: Node = map.get("level")
var persist: Node = map.get("_persist")
var ol: Node = map.get("_outliner")
if ol != null:
ol.set("_save_key", "gate_keymap_step3") # never the user's default.dat
# Three boxes of our own, in front of the camera (map_test has one brush).
for i in 3:
var x := 60.0 + i * 3.0
persist.record("brush.create", {"planes": [1, 0, 0, x + 1, -1, 0, 0, -x, 0, 1, 0, 1, 0, -1, 0, 0,
0, 0, 1, 61, 0, 0, -1, -60], "proj": [], "material_id": 2, "level_id": 0, "sky": false, "group": ""})
persist.refold()
_cam.global_position = Vector3(63.5, 6, 70)
_cam.look_at(Vector3(63.5, 0.5, 60.5), Vector3.UP)
await _frames(2)
var live: PackedInt32Array = level.get_live_brush_ids()
_ok("setup: at least our three brushes", live.size() >= 3, live.size())
print("-- select all / none / invert --")
map.clear_selection()
await _send(map, _key(KEY_A))
_ok("A with nothing selected selects all", (map.get("_sel_brushes") as Array).size() == live.size(),
(map.get("_sel_brushes") as Array).size())
await _send(map, _key(KEY_A))
_ok("A again selects none", (map.get("_sel_brushes") as Array).is_empty())
map.select_brushes(PackedInt32Array([live[0]]))
await _send(map, _key(KEY_I, true))
var inv: Array = map.get("_sel_brushes")
_ok("Ctrl+I inverts (all but the one)", inv.size() == live.size() - 1 and not inv.has(live[0]), inv.size())
print("-- box select --")
map.clear_selection()
await _send(map, _key(KEY_B))
_ok("B arms box select", bool(map.call("is_box_selecting")))
var whole := Rect2(Vector2.ZERO, Vector2(get_viewport().get_visible_rect().size))
var n: int = map.box_select(whole, false)
map.set("_box_sel", false)
_ok("a full-screen box selects what is in front of the camera", n > 0 and (map.get("_sel_brushes") as Array).size() == n, n)
print("-- Shift+A add menu --")
map.clear_selection()
await _send(map, _key(KEY_A, false, true))
var qm: Node = map.get("_q_menu")
_ok("Shift+A opens the add menu with every shape", qm != null and bool(qm.is_open())
and qm.has_action("builder.add_stairs") and qm.has_action("builder.add_sphere"))
qm.pick("builder.add_stairs")
await _frames(4)
var op: Object = map.call("_ensure_ops").get("active")
var bx: Object = map.get("_box")
var si: int = (bx.shape_ids() as Array).find("param:stairs")
_ok("its Stairs row sets the create tool to stairs", int(bx.get("_shape")) == si, [bx.get("_shape"), si])
# Headless the POINTER sits at (0, 0), and the create tool aims where the pointer is, so
# it cannot place here. What the row must do is dispatch the create tool's action —
# which the map's builder handler records as the last repeatable command.
_ok("... and dispatches the create tool", String(map.get("_last_repeatable")) == "builder.create_box",
[map.get("_last_repeatable"), op])
map.call("_ensure_ops").cancel()
await _frames(2)
print("-- U texture menu --")
await _send(map, _key(KEY_U))
_ok("U opens the texture menu", bool(qm.is_open()) and qm.has_action("builder.texture_apply")
and qm.has_action("builder.texture_pick") and qm.has_action("builder.uv_hotspot") and qm.has_action("builder.uv_reroll"))
_ok("... and its rows show their keys (apply = Ctrl+U)", String(qm.accel_text_of("builder.texture_apply")) == "Ctrl+U",
qm.accel_text_of("builder.texture_apply"))
(qm.get("_menu") as PopupMenu).hide()
await _frames(2)
print("-- N sidebar --")
var was: bool = BuildDock.is_region_collapsed(BuildDock.SIDE_RIGHT)
await _send(map, _key(KEY_N))
_ok("N toggles the right column", BuildDock.is_region_collapsed(BuildDock.SIDE_RIGHT) != was)
await _send(map, _key(KEY_N))
_ok("N again restores it", BuildDock.is_region_collapsed(BuildDock.SIDE_RIGHT) == was)
print("-- Tab element <-> brush --")
map.call("set_select_mode", "face")
await _send(map, _key(KEY_TAB))
_ok("Tab from face mode -> brush mode", String(map.get("_sel_mode")) == "brush", map.get("_sel_mode"))
await _send(map, _key(KEY_TAB))
_ok("Tab again -> back to face mode", String(map.get("_sel_mode")) == "face", map.get("_sel_mode"))
print("-- Z wireframe --")
var vp := get_viewport()
var dd0 := vp.debug_draw
await _send(map, _key(KEY_Z))
_ok("Z toggles the wireframe", vp.debug_draw != dd0, vp.debug_draw)
await _send(map, _key(KEY_Z))
_ok("Z again restores it", vp.debug_draw == dd0)
print("-- Shift+R repeat last --")
map.select_brushes(PackedInt32Array([live[0]]))
await _send(map, _key(KEY_G))
var stack: Node = map.call("_ensure_ops")
_ok("G starts the move tool", stack.get("active") is MoveOperator, stack.get("active"))
stack.cancel()
await _frames(2)
await _send(map, _key(KEY_R, false, true))
await _frames(3)
_ok("Shift+R repeats it: the move tool again", stack.get("active") is MoveOperator, stack.get("active"))
stack.cancel()