OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_modal_drag.gd

98 lines · gd ref:
extends Node
## GATE — DRAG THE FOCUSED VALUE WITH THE MOUSE (#120 §17.7 Q2, §17.8 step 2; Blender's).
##   D1  ModalControls.drag: horizontal pixels x step / DRAG_PX
##   D2  Shift (finer): a tenth
##   D3  Ctrl: snapped to whole steps
##   D4  an integer value (its setter rounds) still accumulates small motions
##   D5  a change from elsewhere ([ ]) restarts the drag from the new value
##   D6  in the map: Rotate on a selected brush, mouse motion turns it (drags_value)
##   D7  ... and Move, whose value follows the aim, is not dragged by it
##
## RUN AS A SCENE: "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_modal_drag.tscn

var fails := 0
var checks := 0
var map: Node3D


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


func _motion(x: float) -> void:
	var ev := InputEventMouseMotion.new()
	ev.position = Vector2(x, 300.0)
	map._input(ev)


func _run() -> void:
	# ---- D1..D5: ModalControls alone ----
	var box := {"v": 0.0}
	var m := ModalControls.new()
	m.bind("w", "width", func() -> float: return box["v"], func(x: float) -> void: box["v"] = x, 1.0)
	_ok("D1 ModalControls can drag", m.has_method("drag"))
	if not m.has_method("drag"):
		return
	var px: float = ModalControls.DRAG_PX
	m.drag(px * 2.0, false, false)
	_ok("D1 two steps' worth of pixels = +2", is_equal_approx(box["v"], 2.0), box["v"])
	box["v"] = 0.0
	m.drag(px * 2.0, true, false)
	_ok("D2 Shift: a tenth", is_equal_approx(box["v"], 0.2), box["v"])
	box["v"] = 0.0
	m.drag(px * 1.48, false, true)
	_ok("D3 Ctrl: snapped to a whole step", is_equal_approx(box["v"], 1.0), box["v"])
	var ibox := {"n": 2}
	var mi := ModalControls.new()
	mi.bind("n", "count", func() -> float: return float(ibox["n"]), func(x: float) -> void: ibox["n"] = int(roundf(x)), 1.0)
	for i in 10:
		mi.drag(px * 0.2, false, false)        # each alone rounds to nothing
	_ok("D4 small motions on an integer accumulate (2 + 2)", ibox["n"] == 4, ibox["n"])
	box["v"] = 5.0                              # changed from elsewhere
	m.drag(px, false, false)
	_ok("D5 a change from elsewhere restarts the drag there", is_equal_approx(box["v"], 6.0), box["v"])

	# ---- D6 / D7: through the map ----
	var level: Node = map.get("level")
	var b: int = (level.get_live_brush_ids() as PackedInt32Array)[0]
	map.select_brushes(PackedInt32Array([b]))
	var ops: Node = map.call("_ensure_ops")
	var rt: Node = map.get("_rotate")
	ops.activate(rt)
	_motion(400.0)
	_motion(400.0 + px * 2.0)
	_ok("D6 Rotate: two steps of mouse turn it 2 x 15 deg", is_equal_approx(float(rt.get("_deg")), 30.0), rt.get("_deg"))
	ops.cancel()
	await get_tree().process_frame
	var mv: Node = map.get("_move")
	map.select_brushes(PackedInt32Array([b]))
	ops.activate(mv)
	var s0 := int(mv.get("_steps"))
	_motion(400.0)
	_motion(400.0 + px * 3.0)
	_ok("D7 Move (follows the aim) is not dragged", int(mv.get("_steps")) == s0, [s0, mv.get("_steps")])
	ops.cancel()