OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps commits / ccfe5d5

Fix: G on a one-piece shape (every new box) moved the generated piece, not the shape
MoveOperator.confirm took its single-brush path whenever one brush was selected, so a one-piece shape got brush.translate on its generated piece: the shape's frame stayed, the panel and gizmo disagreed with what was drawn, and the next regeneration (any shape edit) renamed the piece and lost the move. Probed: kinds {shape.create, brush.translate}, frame unmoved. Now a brush that is a shape's piece goes through translate_selection (shape.xform), as a placed asset already did. gate_shape_editor 9 (32/32) fails without the fix ([brush.translate]). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ccfe5d5d752cb566ff4746bece7ea99205335381
by plankatron <fleeky@gmail.com> · 2026-10-19T22:46:23.000Z

Changed files (2) +33 -4

scenes/maps/operators/move_operator.gd +7 -4
……@@ -700,11 +700,14 @@ func _confirm_main(d: Vector3) -> bool:
700700 # while the ring walked off — the terrain then kept a passage where the arch no
701701 # longer was. Every selected brush translates, one rebuild at the end.
702702 var multi: Array = _map_array("_sel_brushes")
703 # A PLACED ASSET always goes through translate_selection (asset.xform, #120 L4), even
704 # as ONE piece: the single-brush path below would journal brush.translate on a
705 # synthetic piece, which the next fold of the placement discards.
703 # A PLACED ASSET or a SHAPE always goes through translate_selection (asset.xform /
704 # shape.xform), even as ONE piece: the single-brush path below would journal
705 # brush.translate on a GENERATED piece — the shape's frame stays put, and the next
706 # regeneration renames the piece and the move is lost (every new box is a one-piece
707 # shape: probed 2026-10-19, G wrote brush.translate and the frame did not move).
706708 var placed := String(map.get("_sel_asset")) if "_sel_asset" in map else ""
707 if _object == "" and (multi.size() > 1 or placed != "") and map.has_method("translate_selection"):
709 var shaped := map.has_method("shape_of_brush") and _brush >= 0 and String(map.shape_of_brush(_brush)) != ""
710 if _object == "" and (multi.size() > 1 or placed != "" or shaped) and map.has_method("translate_selection"):
708711 # The map owns the journal write. The organizer's numeric fields move a
709712 # group through the same call, and two writers of one journal path is how
710713 # they come to disagree about what a record means.
scenes/test/gate_shape_editor.gd +26 -0
……@@ -19,6 +19,8 @@ extends Node
1919## 8. OUTLINER (user 2026-10-05 "the diamond icon next to every parametric shape
2020## group"): a shape's group row reads "◇ <Type>", and clicking it selects the
2121## SHAPE (the Shape panel follows), not merely its pieces
22## 9. G THROUGH MoveOperator on a ONE-piece shape (every new box): shape.xform, the frame
23## moves — its one-brush path once wrote brush.translate on the generated piece
2224##
2325## RUN AS A SCENE: "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_shape_editor.tscn
2426
……@@ -246,3 +248,27 @@ func _run(map: Node3D) -> void:
246248 String(map.selected_shape()) == dot, map.selected_shape())
247249 _ok("8. ... so the Shape panel edits it", panel != null and panel.call("shown_dot") == dot,
248250 panel.call("shown_dot") if panel != null else null)
251
252 # ---- 9. G THROUGH THE OPERATOR on a ONE-piece shape (every new box) ----
253 # The single-brush path of MoveOperator.confirm journalled brush.translate on the
254 # generated piece: the frame stayed, and the next regeneration lost the move.
255 persist.begin_gesture("box")
256 var box9: String = persist.record("shape.create", {"type": "box", "gv": 1, "params": {},
257 "frame": SceneObjects.xform_to(Transform3D(Basis.IDENTITY, Vector3(30, 0, 30))), "size": [2, 2, 2],
258 "material_id": 2, "level_id": 0})
259 persist.end_gesture()
260 persist.refold()
261 map.select_shape(box9)
262 var one: int = (map.get("_sel_brushes") as Array).size()
263 var bf0: Transform3D = persist.shape_state(box9)["frame"]
264 var seq9: int = persist.records().reduce(func(m: int, r: Dictionary) -> int: return maxi(m, int(r["seq"])), 0)
265 var stk: Node = map.call("_ensure_ops")
266 var mv: Object = map.get("_move")
267 var on: bool = stk.activate(mv)
268 mv.set_tool_setting("distance", 3)
269 mv.update_preview()
270 mv.set_tool_setting("move", true)
271 persist.refold()
272 _ok("9. G (MoveOperator) on a ONE-piece shape writes shape.xform, never brush.translate",
273 one == 1 and on and _kinds(persist, seq9) == ["shape.xform"], [one, _kinds(persist, seq9)])
274 _ok("9. ... and the shape's frame moved", not (persist.shape_state(box9)["frame"] as Transform3D).origin.is_equal_approx(bf0.origin))