Implement Phase 8: Rig Relocation and Component Modularization

- Moved the rig scene from `res://master_rig.tscn` to `res://scenes/stickman_rig.tscn`, preserving its UID.
- Split the `StickmanRig` controller into three components: `StickmanLocomotion`, `StickmanRagdoll`, and `StickmanActionRunner`.
- Updated the architecture to ensure `StickmanRig` acts as the public facade for the new components.
- Created a new test suite `test_phase8_rig_modularization.gd` to verify the integrity of the relocated rig and its components.
- Added checks for scene loading, component resolution, action queue delegation, and state authority.
This commit is contained in:
2026-09-18 14:11:53 -04:00
parent 9a22322dba
commit db4ff8d939
29 changed files with 2820 additions and 2747 deletions
+10 -9
View File
@@ -366,7 +366,7 @@ func _test_bug3_walk_termination() -> void:
print("--- Bug 3: nav-mode termination + off-mesh direct still works ---")
# On-mesh waypoint near the slab's right edge (the former oscillation zone):
# the rig must terminate exactly once through the nav branch and stay put.
await _walk_case("on-mesh (near slab edge)", Vector2(88.0, -8.0), "nav")
await _walk_case("on-mesh (near slab edge)", Vector2(88.0, 0.0), "nav")
# Off-mesh waypoint in open space: direct mode must still reach arrival.
await _walk_case("off-mesh (open space)", Vector2(250.0, 0.0), "direct")
@@ -388,24 +388,25 @@ func _walk_case(label: String, target_feet: Vector2, expected_mode: String) -> v
_arrived_count = 0
rig.arrived.connect(_on_arrived)
rig.walk_to(target_feet, 200.0)
_check(not rig._walk_done, "%s: walk not done immediately" % label)
var locomotion: StickmanLocomotion = rig.get_locomotion()
_check(locomotion != null and not locomotion._walk_done, "%s: walk not done immediately" % label)
var frames := 0
var mode_flipped := false
while not rig._walk_done and frames < MAX_WALK_FRAMES:
if rig._walk_mode_latched and rig._walk_mode != expected_mode:
while not locomotion._walk_done and frames < MAX_WALK_FRAMES:
if locomotion._walk_mode_latched and locomotion._walk_mode != expected_mode:
mode_flipped = true
await physics_frame
frames += 1
_check(rig._walk_done, "%s: walk finished within %d frames (used %d)" % [label, MAX_WALK_FRAMES, frames])
_check(locomotion._walk_done, "%s: walk finished within %d frames (used %d)" % [label, MAX_WALK_FRAMES, frames])
_check(_arrived_count == 1, "%s: exactly one arrived emission (got %d)" % [label, _arrived_count])
_check(rig._walk_mode_latched, "%s: steering mode latched" % label)
_check(rig._walk_mode == expected_mode,
"%s: latched to '%s' (got '%s')" % [label, expected_mode, rig._walk_mode])
_check(locomotion._walk_mode_latched, "%s: steering mode latched" % label)
_check(locomotion._walk_mode == expected_mode,
"%s: latched to '%s' (got '%s')" % [label, expected_mode, locomotion._walk_mode])
_check(not mode_flipped, "%s: latched mode never flipped mid-walk" % label)
# Position snapped to the exact final root target (feet + FOOT_OFFSET).
var final_root: Vector2 = target_feet + RIG.FOOT_OFFSET
var final_root: Vector2 = target_feet + StickmanLocomotion.FOOT_OFFSET
_check(rig.global_position.distance_to(final_root) <= 0.01,
"%s: position snapped to final root (dist=%.3f)" % [label, rig.global_position.distance_to(final_root)])
_check(not rig.is_walking(), "%s: is_walking false after arrival" % label)