OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_carver_weapon.gd

140 lines · gd ref:
extends Node
## GATE: the terrain carver is a WEAPON (key audit 2026-10-01), end to end on the
## real mountain map -- the input reaches map_core and the terrain takes the edit.
##
##   "$G" --path . --rendering-driver vulkan --resolution 1280x720 res://scenes/test/gate_carver_weapon.tscn
##
## While playing: LMB carves (also with Shift held -- you fire while sprinting --
## and Shift no longer turns it into an ADD), RMB does NOT carve (it is the grapple),
## Q toggles remove/add, `-` / `=` size the brush. In build mode the carver is dead.
## Events go through the map's own _unhandled_input, the path a click takes.
## ⛔ NOT HEADLESS (the mountain's field is GPU-only; the mouse must be capturable).

const MAP := "res://scenes/maps/map_mountain.tscn"
const MAP_ID := "map_mountain"
const PROBE := "res://scenes/test/terrain_probe.gd"
const SETTLE_FRAMES := 60
const SETTLE_CAP_S := 90.0

var _map: Node
var _cam: Camera3D
var _fails := 0


func _ready() -> void:
	_run.call_deferred()


func _ok(what: String, c: bool) -> void:
	if not c:
		_fails += 1
	print("[CARVER] %s %s" % ["PASS" if c else "FAIL", what])


func _since(t0: int) -> float:
	return (Time.get_ticks_msec() - t0) / 1000.0


func _terrain() -> Node:
	return _map.get_node_or_null(^"MountainTerrain")


func _settle() -> void:
	var tr := _terrain()
	var t0 := Time.get_ticks_msec()
	var calm := 0
	while calm < SETTLE_FRAMES and _since(t0) < SETTLE_CAP_S:
		await get_tree().process_frame
		var ok: bool = bool(tr.call("is_lod_settled")) and int(tr.call("get_pending_count")) == 0 \
				and int(tr.call("get_collision_queue_size")) == 0 and int(tr.call("get_worker_queue_depth")) == 0
		calm = calm + 1 if ok else 0


func _mouse(button: int, shift := false) -> InputEventMouseButton:
	var e := InputEventMouseButton.new()
	e.button_index = button
	e.pressed = true
	e.shift_pressed = shift
	return e


func _key(k: int) -> InputEventKey:
	var e := InputEventKey.new()
	e.physical_keycode = k
	e.keycode = k              # both, as a real (QWERTY) key event (keymap step 5)
	e.pressed = true
	return e


func _edits() -> int:
	return int(_terrain().call("get_edit_count"))


func _send(ev: InputEvent) -> void:
	_map.call("_unhandled_input", ev)
	await get_tree().process_frame


func _run() -> void:
	if DisplayServer.get_name() == "headless":
		print("RESULT: SKIP (needs a display)")
		get_tree().quit(2)
		return
	GameState.enter_map(MAP_ID)
	var mgr := TerrainEditManager.new()
	mgr.name = "TerrainEditManager"
	add_child(mgr)
	_cam = Camera3D.new()
	_cam.far = 20000.0
	add_child(_cam)
	_cam.current = true
	_map = (load(MAP) as PackedScene).instantiate()
	add_child(_map)
	var spawn := Vector3(0, 250, 0)
	var sp := get_tree().get_nodes_in_group("spawn_points")
	if not sp.is_empty():
		spawn = (sp[0] as Node3D).global_position
	_cam.global_position = spawn + Vector3(0, 2, 0)
	await _settle()
	var tr := _terrain()
	var col := Vector3(spawn.x + 20.0, 0, spawn.z)
	var s = _map.call("_find_surface_gpu", load(PROBE), tr, Vector3(col.x, 1500.0, col.z), 64.0)
	if s == null:
		print("RESULT: FAIL (no surface)"); get_tree().quit(1); return
	var site := Vector3(col.x, float(s), col.z)
	_cam.global_position = site + Vector3(0, 12, 12)
	_cam.look_at(site)
	await _settle()
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
	await get_tree().process_frame

	var n0 := _edits()
	await _send(_mouse(MOUSE_BUTTON_LEFT))
	_ok("LMB carves while playing (edits %d -> %d)" % [n0, _edits()], _edits() == n0 + 1)
	_ok("and it REMOVES by default", not bool(_map.get("_carve_add")))
	var n1 := _edits()
	await _send(_mouse(MOUSE_BUTTON_LEFT, true))
	_ok("Shift+LMB carves too -- you fire while sprinting", _edits() == n1 + 1)
	_ok("and Shift did NOT turn it into an add", not bool(_map.get("_carve_add")))
	var n2 := _edits()
	await _send(_mouse(MOUSE_BUTTON_RIGHT))
	_ok("RMB does NOT carve (it is the grapple)", _edits() == n2)
	await _send(_key(KEY_Q))
	_ok("Q switches the carver to ADD", bool(_map.get("_carve_add")))
	await _send(_key(KEY_Q))
	_ok("and back to REMOVE", not bool(_map.get("_carve_add")))
	var r0 := float(_map.get("_csg_radius"))
	await _send(_key(KEY_EQUAL))
	_ok("= makes the brush bigger (%.1f -> %.1f)" % [r0, float(_map.get("_csg_radius"))],
			is_equal_approx(float(_map.get("_csg_radius")), r0 + 0.5))
	await _send(_key(KEY_MINUS))
	_ok("- makes it smaller again", is_equal_approx(float(_map.get("_csg_radius")), r0))
	Keymap.push_context("build_mode")
	var n3 := _edits()
	await _send(_mouse(MOUSE_BUTTON_LEFT))
	_ok("in build mode LMB does not carve", _edits() == n3)
	Keymap.pop_context("build_mode")
	Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
	print("RESULT: %s" % ("PASS" if _fails == 0 else "FAIL"))
	get_tree().quit(0 if _fails == 0 else 1)