OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_circle_select.gd

119 lines · gd ref:
extends Node
## GATE — C = Blender 2.79's CIRCLE SELECT (view3d.select_circle; #109 "remaining step 6"):
## a ring follows the pointer, LMB drag PAINTS brushes into the selection, Shift+LMB
## paints them out, the wheel resizes the ring, RMB / Esc / Enter leaves. A brush counts
## when its centre projects inside the ring — box select's (B) rule.
##
## Three 1 m brushes A, B, C in a row 6 m apart, the camera looking down at them:
##   C1  C (through Keymap.run, the real input path) enters circle mode
##   C2  LMB on A's screen point, ring smaller than the spacing: selects A only
##   C3  dragging (still pressed) onto B paints B in: A + B, not C
##   C4  Shift+LMB on A paints A out: B only
##   C5  the wheel grows the ring
##   C6  RMB leaves circle mode, the selection kept
##
## RUN AS A SCENE: "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_circle_select.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")
	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
	_cam.global_position = Vector3(206, 30, 0.01)
	_cam.look_at(Vector3(206, 0, 0), Vector3.UP)
	for i in 8:
		await get_tree().process_frame
	await _run(map)
	get_tree().root.get_node("/root/GameMode").set_mode("play")
	if checks < 7:
		print("  **FAIL** only %d checks ran -- setup died before the assertions" % checks)
		fails += 1
	print("\nGATE circle_select: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
	get_tree().quit(0 if fails == 0 else 1)


func _brush(map: Node, x: float) -> int:
	var persist: Node = map.get("_persist")
	var dot: String = persist.record("brush.create", {"planes": [1, 0, 0, x + 0.5, -1, 0, 0, -(x - 0.5),
		0, 1, 0, 1, 0, -1, 0, 0, 0, 0, 1, 0.5, 0, 0, -1, 0.5], "proj": [], "material_id": 2,
		"level_id": 0, "sky": false, "group": ""})
	persist.refold()
	return (map.get("level") as Node).brush_by_key(dot)


func _button(pos: Vector2, idx: MouseButton, pressed: bool, shift := false) -> InputEventMouseButton:
	var e := InputEventMouseButton.new()
	e.position = pos
	e.button_index = idx
	e.pressed = pressed
	e.shift_pressed = shift
	return e


func _motion(pos: Vector2, held: bool) -> InputEventMouseMotion:
	var e := InputEventMouseMotion.new()
	e.position = pos
	e.button_mask = MOUSE_BUTTON_MASK_LEFT if held else 0
	return e


func _sel(map: Node) -> Array:
	return Array(map.get("_sel_brushes"))


func _run(map: Node3D) -> void:
	var a := _brush(map, 200.0)
	var b := _brush(map, 206.0)
	var c := _brush(map, 212.0)
	await get_tree().process_frame
	var pa := _cam.unproject_position(Vector3(200, 0.5, 0))
	var pb := _cam.unproject_position(Vector3(206, 0.5, 0))
	var spacing := pa.distance_to(pb)
	map.select_brushes(PackedInt32Array())

	Keymap.run("builder.select_circle")
	await get_tree().process_frame
	_ok("C1 C enters circle select", bool(map.call("is_circle_selecting")))
	map.set("_circ_radius", spacing * 0.3)

	map.call("_circle_select_input", _motion(pa, false))
	map.call("_circle_select_input", _button(pa, MOUSE_BUTTON_LEFT, true))
	_ok("C2 LMB on A selects A only", _sel(map) == [a], _sel(map))
	map.call("_circle_select_input", _motion(pa.lerp(pb, 0.5), true))
	map.call("_circle_select_input", _motion(pb, true))
	map.call("_circle_select_input", _button(pb, MOUSE_BUTTON_LEFT, false))
	var s := _sel(map)
	_ok("C3 dragging onto B paints it in: A + B, not C", s.has(a) and s.has(b) and not s.has(c), s)

	map.call("_circle_select_input", _motion(pa, false))
	map.call("_circle_select_input", _button(pa, MOUSE_BUTTON_LEFT, true, true))
	map.call("_circle_select_input", _button(pa, MOUSE_BUTTON_LEFT, false, true))
	_ok("C4 Shift+LMB on A paints it out: B only", _sel(map) == [b], _sel(map))

	var r0: float = map.get("_circ_radius")
	map.call("_circle_select_input", _button(pb, MOUSE_BUTTON_WHEEL_UP, true))
	_ok("C5 the wheel grows the ring", float(map.get("_circ_radius")) > r0, [r0, map.get("_circ_radius")])

	map.call("_circle_select_input", _button(pb, MOUSE_BUTTON_RIGHT, true))
	_ok("C6 RMB leaves circle select", not bool(map.call("is_circle_selecting")))
	_ok("C6 and the selection is kept", _sel(map) == [b], _sel(map))