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)