OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_select_linked.gd

139 lines · gd ref:
extends Node
## GATE — keymap step 6.3, SELECT LINKED / GROUPED (Blender L / Shift+L / Ctrl+L / Shift+G):
##   Ctrl+L grows the selection to everything TOUCHING it (transitively), not what is apart;
##   Shift+G's rows select by group / material / texture / type like the active brush;
##   L's unit is the whole shape / group.
##
## RUN AS A SCENE: "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_select_linked.tscn

var fails := 0
var checks := 0


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")
	var map: Node3D = (load("res://scenes/maps/map_test.tscn") as PackedScene).instantiate()
	add_child(map)
	for i in 8:
		await get_tree().process_frame
	await _run(map)
	get_tree().root.get_node("/root/GameMode").set_mode("play")
	if checks < 10:
		print("  **FAIL** only %d checks ran -- setup died before the assertions" % checks)
		fails += 1
	print("\nGATE select_linked: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
	get_tree().quit(0 if fails == 0 else 1)


func _box(persist: Node, x0: float, mat: int, tex: String) -> String:
	var ft: Array = []
	for i in 6:
		ft.append({"tex": tex, "mode": 1} if tex != "" else {})
	return persist.record("brush.create", {"planes": [1, 0, 0, x0 + 1, -1, 0, 0, -x0, 0, 1, 0, 1, 0, -1, 0, 0,
		0, 0, 1, 1, 0, 0, -1, 0], "proj": [], "material_id": mat, "level_id": 0, "sky": false, "group": "",
		"face_tex": ft})


func _key(code: Key, ctrl := false, shift := false) -> InputEventKey:
	var e := InputEventKey.new()
	e.keycode = code
	e.physical_keycode = code
	e.pressed = true
	e.ctrl_pressed = ctrl
	e.shift_pressed = shift
	return e


func _run(map: Node3D) -> void:
	var persist: Node = map.get("_persist")
	var level: Node = map.get("level")
	# A chain of three TOUCHING boxes at x 100..103, and a lone one at x 110 (same material).
	var a: String = _box(persist, 100.0, 3, "kenney-prototype/dark_01")
	var b: String = _box(persist, 101.0, 3, "kenney-prototype/dark_01")
	var c: String = _box(persist, 102.0, 4, "kenney-prototype/light_01")
	var lone: String = _box(persist, 110.0, 3, "kenney-prototype/dark_01")
	persist.refold()
	var ia: int = level.brush_by_key(a)
	var ib: int = level.brush_by_key(b)
	var ic: int = level.brush_by_key(c)
	var il: int = level.brush_by_key(lone)

	print("-- Ctrl+L: everything touching, transitively --")
	map.select_brushes(PackedInt32Array([ia]))
	map._unhandled_key_input(_key(KEY_L, true))
	var sel: Array = map.get("_sel_brushes")
	_ok("the chain a-b-c is selected", sel.has(ia) and sel.has(ib) and sel.has(ic), sel)
	_ok("... and the lone box apart from it is not", not sel.has(il))

	print("-- Shift+G rows: like the active brush --")
	map.select_brushes(PackedInt32Array([ia]))
	map.select_grouped("material")
	sel = map.get("_sel_brushes")
	_ok("same material (3): a, b and the lone box, not c", sel.has(ia) and sel.has(ib) and sel.has(il) and not sel.has(ic), sel)
	map.select_brushes(PackedInt32Array([ic]))
	map.select_grouped("texture")
	sel = map.get("_sel_brushes")
	_ok("same texture (light_01): only c", sel.size() == 1 and sel.has(ic), sel)
	map._unhandled_key_input(_key(KEY_G, false, true))
	await get_tree().process_frame
	var qm: Node = map.get("_q_menu")
	_ok("Shift+G opens the select-grouped menu", qm != null and bool(qm.is_open()) and qm.has_action("builder.select_same_material"))
	(qm.get("_menu") as PopupMenu).hide()
	# A popup hidden and re-popped in the SAME frame closes again at once (the root's
	# focus-in from the removal reaches the new popup's _parent_focused). No key press can
	# do that; this gate can, so it lets the frame end before X re-pops the same menu.
	await get_tree().process_frame

	print("-- L's unit is the whole shape --")
	var shp: String = persist.record("shape.create", {"type": "stairs", "gv": 1, "params": {"steps": 4},
		"frame": SceneObjects.xform_to(Transform3D(Basis.IDENTITY, Vector3(120, 0, 0))),
		"size": [4.0, 2.0, 2.0], "material_id": 2, "level_id": 0})
	persist.refold()
	var pieces: PackedInt32Array = level.get_group_brush_ids(shp)
	var unit: PackedInt32Array = map._unit_of(pieces[0])
	_ok("a stairs piece's linked unit is every piece of the stairs", unit.size() == pieces.size() and unit.size() >= 4,
		[unit.size(), pieces.size()])
	var solid := 0
	for p: int in pieces:
		if not bool((level.get_brush(p) as Dictionary).get("clearance", false)):
			solid += 1
	var first_solid := -1
	for p2: int in pieces:
		if not bool((level.get_brush(p2) as Dictionary).get("clearance", false)):
			first_solid = p2
			break
	map.select_brushes(PackedInt32Array([first_solid]))
	map.select_grouped("type")
	_ok("same shape type: exactly the stairs' solid pieces (clearance is air)",
		(map.get("_sel_brushes") as Array).size() == solid, [(map.get("_sel_brushes") as Array).size(), solid])

	print("-- X: the confirming delete (step 6.4) --")
	map.select_brushes(PackedInt32Array([ia, ib]))
	var e := InputEventKey.new()
	e.keycode = KEY_X
	e.physical_keycode = KEY_X
	e.pressed = true
	map._unhandled_key_input(e)
	await get_tree().process_frame
	var qm2: Node = map.get("_q_menu")
	_ok("X opens the delete popup naming the count", bool(qm2.is_open()) and String(qm2.item_text_of("builder.delete")) == "Delete 2 selected",
		qm2.item_text_of("builder.delete"))
	_ok("... and deleted NOTHING yet (the popup is the confirm)", (level.get_live_brush_ids() as PackedInt32Array).has(ia))
	qm2.pick("builder.delete")
	for i in 4:
		await get_tree().process_frame
	_ok("picking the row deletes the two", not (level.get_live_brush_ids() as PackedInt32Array).has(ia)
		and not (level.get_live_brush_ids() as PackedInt32Array).has(ib))