Add Phase 4 Final Bugs Specification and Tests
- Introduced a new architecture specification for Phase 4 final bugs, detailing scope, decisions, runtime data contracts, action roles, execution lanes, speech ownership, and signal flow. - Implemented changes in `scripts/action_registry.gd`, `scripts/action_editor.gd`, `scripts/sandbox_stage.gd`, `scripts/stage_gizmos.gd`, `scripts/stickman_factory.gd`, and `scripts/stickman_rig.gd` to address identified bugs. - Created a new test suite `tests/test_phase4_final_bugs.gd` to validate the implementation, covering action roles, grid snapping, prop manipulation, reactive runtime lane, spawn pose preservation, and more. - Added a unique identifier for the test file to ensure proper tracking and management.
This commit is contained in:
+26
-6
@@ -22,6 +22,8 @@ enum Handle { NONE, TRANSLATE, ROTATE }
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
signal transform_committed(nodes: Array[Node2D])
|
||||
signal transform_started(nodes: Array[Node2D])
|
||||
signal transform_updated(nodes: Array[Node2D])
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Style constants (screen-space px; divided by zoom for world-space drawing)
|
||||
@@ -53,6 +55,8 @@ var _hovered: Node2D = null
|
||||
var _dragging: Handle = Handle.NONE
|
||||
var _drag_last: Vector2 = Vector2.ZERO
|
||||
var _drag_anchor: Node2D = null
|
||||
var _translate_drag_origin: Vector2 = Vector2.ZERO
|
||||
var _translate_start_positions: Dictionary = {}
|
||||
var _rotate_start_rotation: float = 0.0
|
||||
var _rotate_start_angle: float = 0.0
|
||||
var _rotate_center: Vector2 = Vector2.ZERO
|
||||
@@ -78,6 +82,10 @@ func set_enabled(enabled: bool) -> void:
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func set_snap_size(size: float) -> void:
|
||||
snap_size = maxf(size, 0.0)
|
||||
|
||||
|
||||
func set_box_rect(rect: Rect2) -> void:
|
||||
_box_rect = rect
|
||||
queue_redraw()
|
||||
@@ -105,31 +113,39 @@ func begin_drag(world_pos: Vector2) -> void:
|
||||
_rotate_center = STAGE_SELECTION.get_world_aabb(_targets[0]).get_center()
|
||||
_rotate_start_rotation = _targets[0].global_rotation
|
||||
_rotate_start_angle = (world_pos - _rotate_center).angle()
|
||||
transform_started.emit(_targets.duplicate())
|
||||
|
||||
|
||||
## Begins a translate drag anchored on `node` (used when an object body is
|
||||
## clicked). The drag moves the whole current selection (`_targets`); if `node`
|
||||
## is not currently selected, the drag targets just `node`.
|
||||
func begin_translate_drag(node: Node2D, world_pos: Vector2) -> void:
|
||||
if node == null or not is_instance_valid(node):
|
||||
return
|
||||
_drag_anchor = node
|
||||
if not _targets.has(node):
|
||||
_targets = [node]
|
||||
_dragging = Handle.TRANSLATE
|
||||
_drag_last = world_pos
|
||||
_translate_drag_origin = world_pos
|
||||
_translate_start_positions.clear()
|
||||
for target: Node2D in _targets:
|
||||
if target != null and is_instance_valid(target):
|
||||
_translate_start_positions[target] = target.global_position
|
||||
transform_started.emit(_targets.duplicate())
|
||||
|
||||
|
||||
func drag_to(world_pos: Vector2) -> void:
|
||||
if _dragging == Handle.NONE or _targets.is_empty():
|
||||
return
|
||||
var delta := world_pos - _drag_last
|
||||
if _dragging == Handle.TRANSLATE:
|
||||
var actual := delta
|
||||
var actual := world_pos - _translate_drag_origin
|
||||
if snap_size > 0.0 and _drag_anchor != null and is_instance_valid(_drag_anchor):
|
||||
var old := _drag_anchor.global_position
|
||||
actual = _snap(old + delta) - old
|
||||
var anchor_start: Vector2 = _translate_start_positions.get(_drag_anchor, _drag_anchor.global_position)
|
||||
actual = _snap(anchor_start + actual) - anchor_start
|
||||
for node: Node2D in _targets:
|
||||
if is_instance_valid(node):
|
||||
node.global_position += actual
|
||||
if is_instance_valid(node) and _translate_start_positions.has(node):
|
||||
node.global_position = (_translate_start_positions[node] as Vector2) + actual
|
||||
elif _dragging == Handle.ROTATE:
|
||||
if _targets.size() == 1 and is_instance_valid(_targets[0]):
|
||||
var target := _targets[0]
|
||||
@@ -140,6 +156,7 @@ func drag_to(world_pos: Vector2) -> void:
|
||||
new_rotation = snappedf(new_rotation, deg_to_rad(15.0))
|
||||
target.global_rotation = new_rotation
|
||||
_drag_last = world_pos
|
||||
transform_updated.emit(_targets.duplicate())
|
||||
queue_redraw()
|
||||
|
||||
|
||||
@@ -148,6 +165,7 @@ func end_drag() -> void:
|
||||
transform_committed.emit(_targets.duplicate())
|
||||
_dragging = Handle.NONE
|
||||
_drag_anchor = null
|
||||
_translate_start_positions.clear()
|
||||
|
||||
|
||||
func is_dragging() -> bool:
|
||||
@@ -194,4 +212,6 @@ func _rotate_ring_radius(rect: Rect2) -> float:
|
||||
|
||||
|
||||
func _snap(v: Vector2) -> Vector2:
|
||||
if snap_size <= 0.0:
|
||||
return v
|
||||
return Vector2(roundf(v.x / snap_size) * snap_size, roundf(v.y / snap_size) * snap_size)
|
||||
|
||||
Reference in New Issue
Block a user