extends Node
## GATE — PROPORTIONAL EDITING, ELEMENT LEVEL (#109; user 2026-10-13: "also element
## vertices"). G on a vertex with O on: the corners near it follow by the falloff, each
## one re-hulled (brush.reshape), on its own brush and — unless Alt+O (connected only) —
## on the brushes beside it, so shared seams move together.
## 2 m cube A at the origin, G on its corner (1,1,1), straight up one grid step d;
## linear, radius 3. Cube B beside it: its corner (2.5,1,1) is 1.5 m from the vertex.
## V1 the vertex moves by d
## V2 A's corner (-1,1,1), 2 m away, follows by d/3
## V3 A's far corner (-1,-1,-1), beyond the radius, stays
## V4 B's corner (2.5,1,1) follows by d/2
## V5 one undo puts every corner back
## V6 connected only (Alt+O): B stays, A's corner still follows
## V7 off: only the vertex moves
##
## RUN AS A SCENE: "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_proportional_vertex.tscn
var fails := 0
var checks := 0
const CUBE := [1, 0, 0, 1, -1, 0, 0, 1, 0, 1, 0, 1, 0, -1, 0, 1, 0, 0, 1, 1, 0, 0, -1, 1]
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 < 16:
print(" **FAIL** only %d checks ran -- setup died before the assertions" % checks)
fails += 1
print("\nGATE proportional_vertex: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
get_tree().quit(0 if fails == 0 else 1)
func _key(code: Key) -> InputEventKey:
var e := InputEventKey.new()
e.keycode = code
e.physical_keycode = code
e.pressed = true
return e
func _cube(map: Node, at: Vector3) -> String:
var persist: Node = map.get("_persist")
var pl: Array = CUBE.duplicate()
for f in 6:
pl[f * 4 + 3] = float(pl[f * 4 + 3]) + Vector3(pl[f * 4], pl[f * 4 + 1], pl[f * 4 + 2]).dot(at)
var dot: String = persist.record("brush.create", {"planes": pl, "proj": [], "material_id": 2,
"level_id": 0, "sky": false, "group": ""})
persist.refold()
return dot
func _verts(level: Node, key: String) -> PackedVector3Array:
var out := PackedVector3Array()
for p: Vector3 in level.get_brush_edges_of(level.brush_by_key(key)):
var seen := false
for q: Vector3 in out:
if q.distance_to(p) < 1e-3:
seen = true
break
if not seen:
out.append(p)
return out
func _has(vs: PackedVector3Array, p: Vector3) -> bool:
for q: Vector3 in vs:
if q.distance_to(p) < 0.01:
return true
return false
func _select_vertex(map: Node, key: String, point: Vector3) -> void:
var level: Node = map.get("level")
var b: int = level.brush_by_key(key)
map.select_brushes(PackedInt32Array([b]))
var idx: PackedInt32Array = map.call("_planes_through", b, point, 1e-3)
var keys: PackedStringArray = level.get_brush(b).get("plane_keys", PackedStringArray())
var sel := PackedStringArray()
for i in idx:
sel.append(keys[i])
map.set("_sel_mode", "vertex")
map.set("_sel_kind", "vertex")
map.set("_sel_keys", sel)
map.set("_sel_point", point)
## G on the selected vertex, straight up `steps` grid steps, Enter. Returns d.
func _grab_up(map: Node, steps: int) -> Vector3:
var stack: Node = map.call("_ensure_ops")
map._unhandled_key_input(_key(KEY_G))
var g: Object = stack.get("active")
if not (g is ElementOperator):
return Vector3.ZERO
g.set_tool_setting("axis", ElementOperator.AXIS_ROWS.find("y"))
g.set_tool_setting("distance", steps)
stack.call("confirm")
return Vector3(0, float(map.grid_step()) * float(steps), 0)
func _run(map: Node3D) -> void:
var persist: Node = map.get("_persist")
var level: Node = map.get("level")
var a := _cube(map, Vector3.ZERO)
var b := _cube(map, Vector3(3.5, 0, 0))
map.set("proportional", true)
map.set("prop_falloff", "linear")
map.set("prop_radius", 3.0)
map.set("prop_connected", false)
print("-- V1-V5: G on a vertex, proportional on --")
_select_vertex(map, a, Vector3(1, 1, 1))
var d := _grab_up(map, 1)
_ok("(setup) G moved the vertex up one grid step", d.y > 0.0, d)
var va := _verts(level, a)
var vb := _verts(level, b)
_ok("V1 the vertex moved by d", _has(va, Vector3(1, 1, 1) + d), va)
_ok("V2 A's corner 2 m away followed by d/3", _has(va, Vector3(-1, 1, 1) + d / 3.0), va)
_ok("V3 A's far corner stayed", _has(va, Vector3(-1, -1, -1)))
_ok("V4 B's corner 1.5 m away followed by d/2", _has(vb, Vector3(2.5, 1, 1) + d * 0.5), vb)
_ok("V4 B's far corner stayed", _has(vb, Vector3(4.5, -1, -1)))
persist.undo_gesture(persist.newest_live_gesture())
persist.refold()
va = _verts(level, a)
vb = _verts(level, b)
_ok("V5 one undo: the vertex is back", _has(va, Vector3(1, 1, 1)), va)
_ok("V5 ... and A's corner", _has(va, Vector3(-1, 1, 1)))
_ok("V5 ... and B's corner", _has(vb, Vector3(2.5, 1, 1)), vb)
print("-- V6: connected only --")
map.set("prop_connected", true)
_select_vertex(map, a, Vector3(1, 1, 1))
d = _grab_up(map, 1)
va = _verts(level, a)
vb = _verts(level, b)
_ok("V6 A's corner still follows", _has(va, Vector3(-1, 1, 1) + d / 3.0), va)
_ok("V6 B stays (another brush)", _has(vb, Vector3(2.5, 1, 1)), vb)
persist.undo_gesture(persist.newest_live_gesture())
persist.refold()
print("-- V8: the [ ] nudge too --")
map.set("prop_connected", false)
_select_vertex(map, a, Vector3(1, 1, 1))
map._unhandled_key_input(_key(KEY_BRACKETRIGHT))
vb = _verts(level, b)
_ok("V8 ] on the vertex: B's corner 1.5 m away followed", not _has(vb, Vector3(2.5, 1, 1)), vb)
persist.undo_gesture(persist.newest_live_gesture())
persist.refold()
_ok("V8 and one undo puts it back", _has(_verts(level, b), Vector3(2.5, 1, 1)))
_ok("V8 ... the vertex too (one gesture)", _has(_verts(level, a), Vector3(1, 1, 1)), _verts(level, a))
print("-- V7: off --")
map.set("proportional", false)
map.set("prop_connected", false)
_select_vertex(map, a, Vector3(1, 1, 1))
d = _grab_up(map, 1)
va = _verts(level, a)
_ok("V7 off: the vertex moved", _has(va, Vector3(1, 1, 1) + d), va)
_ok("V7 and A's other corner stayed", _has(va, Vector3(-1, 1, 1)), va)