Files
stickman/plans/PHASE_11_PROP_INTERACTION.md
ryan 043f114083 Implement interactive prop handling for StickmanRig
- Added PropTrajectory class for deterministic ballistic calculations.
- Introduced StickmanPropInteraction component to manage prop interactions (pickup, drop, throw).
- Implemented grip resolution and attachment logic for props.
- Added signals for interaction events (prop_picked_up, prop_dropped, prop_thrown).
- Defined specifications for prop interaction lifecycle and event rules.
- Ensured compatibility with existing prop systems and added new capabilities for prop pickup.
2026-09-23 21:26:13 -04:00

252 lines
13 KiB
Markdown

# Architecture Specification: Interactive Prop System & Event Engine
This specification details the technical architecture for bringing interactive prop capabilities—including auto-approach, IK socket binding, parabolic throwing, ragdoll disarming, and "When..." rule triggers—into the Sandbox Stage.
---
## 1. System Overview & Layer Interactions
Prop interactions bridge five core subsystems across the runtime stack:
```
[ Director UI / Target Selector ]
│
▼
[ StickmanActionRunner ] ──(Auto-Approach)──► [ StickmanLocomotion ]
│
▼
[ StickmanRig IK ] ──(Socket Binding)──► [ PropBlock Physics ]
│
▼
[ StageEventRegistry ] ──("When..." Rules)──► [ Reactive Actions ]
```
1. **Director UI & Target Selection:** Captures spatial throw targets and enqueues prop interaction commands.
2. **Action Runner & Locomotion:** Evaluates spatial reach; automatically inserts `walk_to` sub-goals when props are out of arm's reach.
3. **IK & Bone Socketing:** Extends arm IK targets toward prop `grip` anchors and manages `RemoteTransform2D` binding.
4. **Physics Lifecycle:** Unfreezes/freezes `PropBlock` bodies and inherits velocity during release, throw, or ragdoll disarm transitions.
5. **Event Registry:** Emits and evaluates edge-triggered prop rules within the stage's "When → Then" engine.
---
## 2. Dynamic Action Queue & Auto-Approach (`walk_to`)
To ensure smooth director interaction, the action queue automatically resolves spatial gaps between the stickman and target props.
### Action Payload Schemas
- **`pick_up_prop`**: `{ "type": "pick_up_prop", "target_prop_id": String, "hand": "auto" | "left" | "right" }`
- **`drop_prop`**: `{ "type": "drop_prop" }`
- **`throw_prop`**: `{ "type": "throw_prop", "target_position": Vector2, "force": float }`
### Smart Approach Execution
When `StickmanActionRunner` receives a `pick_up_prop` payload:
```
┌──────────────────────────────────────────────┐
│ pick_up_prop Action Start │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ Distance = |rig.position - prop.position| │
└──────────────────────┬───────────────────────┘
│
Is Distance > Reach Radius (e.g. 40px)?
┌───────────────────────┴───────────────────────┐
│ │
[ YES ] [ NO ]
│ │
┌─────────────────▼─────────────────┐ ┌─────────────────▼─────────────────┐
│ 1. Pause pick_up_prop sequence │ │ 1. Skip locomotion step │
│ 2. Prepend walk_to(prop_offset) │ │ 2. Trigger arm IK reach animation │
│ 3. Resume pickup on arrival │ └───────────────────────────────────┘
└───────────────────────────────────┘
```
1. **Distance Check:** Evaluates global horizontal/spatial distance between the stickman root and prop `origin` pivot.
2. **Sub-Goal Injection:** If outside reach radius, `StickmanActionRunner` pauses the pickup sequence and internally issues a `walk_to` command to a stand point adjacent to the prop.
3. **Sequence Resume:** Upon receiving `StickmanLocomotion.arrived_at_waypoint`, the runner resumes the pickup sequence and triggers arm IK extension.
---
## 3. Pickup, Drop, and Throw Pipelines
### A. Prop Pickup Sequence & IK Socketing
1. **IK Reach Phase (150 ms):** `TwoBoneIK` targets for the selected arm interpolate toward the world position of the prop's `grip` anchor.
2. **Socket Attachment:**
- Set `PropBlock.freeze = true` (kinematic mode) to suspend environmental physics forces.
- Instantiate or activate a `RemoteTransform2D` node attached to the lower arm / wrist joint, targeting the `PropBlock`.
- Apply anchor local offsets (`x, y, rotation_deg, z_index_offset`) to align the prop grip directly within the hand geometry.
3. **Holding Stance Latch:** Lock upper limb IK targets into a carried stance during subsequent walk cycles.
### B. Prop Drop Sequence
1. **Unbinding:** Clear `RemoteTransform2D` target and reparent `PropBlock` back to the stage world root.
2. **Physics Activation:** Set `PropBlock.freeze = false` to re-enable rigid body simulation, gravity, and collision shapes.
3. **Inertia Inheritance:** Set `PropBlock.linear_velocity` equal to the stickman's current movement velocity (`_last_grounded_walk_velocity_x`) so dropped items retain forward momentum.
4. **Arm Recovery:** Blend wrist IK targets smoothly back to standard locomotion/idle rest positions.
### C. Prop Throw Sequence & Trajectory Selection
#### Director Target UI Flow
- **Targeting Mode:** Selecting "Throw Prop" in Director/Play mode activates an aim reticle cursor and dynamic arc preview.
- **Trajectory Math:** Given hand release point $P_0$, target landing point $P_t$, base speed $v_0$, and gravity $g$:
$$\theta = \arctan\left(\frac{v_0^2 \pm \sqrt{v_0^4 - g(g \cdot x^2 + 2 y \cdot v_0^2)}}{g \cdot x}\right)$$
Calculates exact initial launch velocity components $V_{\text{launch}} = (v_0 \cos\theta, v_0 \sin\theta)$.
#### Keyframe Throw Animation Arc
```
[ Windup: 100ms ] ──► [ Release Frame: 150ms ] ──► [ Follow-Through: 50ms ]
- Torso tilts -15° - Arm sweeps to angle - Arm extends along arc
- Arm pulls backward - Prop unfreezes + impulse - Blends back to idle
```
1. **Windup (100 ms):** Torso rotates backward opposite the launch vector while the arm pulls back.
2. **Release Frame (150 ms):**
- Arm sweeps rapidly along the calculated launch angle $\theta$.
- Unbind `RemoteTransform2D` and reparent prop to world root.
- Set `PropBlock.freeze = false` and execute `apply_central_impulse(V_launch)`.
- Apply rotational torque proportional to the distance between hand grip and prop center-of-mass.
3. **Follow-Through (50 ms):** Arm extends toward launch vector, then smoothly dampens back to default idle/walking pose.
---
## 4. Physics & State Machine Lifecycle (Ragdoll Disarm Handoff)
When `StickmanRig.set_ragdoll(true)` is executed (via impact, command, or keypress), any held prop must be immediately disarmed before kinematic nodes are hidden and ragdoll bodies are built.
```
┌──────────────────────────────────────────────┐
│ StickmanRig.set_ragdoll(true) │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ 1. Pre-Ragdoll Intercept │
│ Query if held_prop != null │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ 2. Execute Disarm Handoff │
│ - Clear RemoteTransform2D binding │
│ - Reparent PropBlock to world root │
│ - Set prop.freeze = false │
│ - Apply cached momentum (_track_momentum) │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ 3. Build Physics Ragdoll │
│ Hide animated body, build RigidBody2D │
│ network at current solved bone positions │
└──────────────────────────────────────────────┘
```
1. **Pre-Ragdoll Intercept:** Prior to clearing collision proxies or hiding body meshes, check for an active `held_prop` binding.
2. **Physics Unbind:**
- Detach the `RemoteTransform2D` socket.
- Reparent prop to the world root and set `PropBlock.freeze = false`.
- Transfer cached rig momentum (`_track_momentum()`) directly to `PropBlock.linear_velocity` and `angular_velocity` so the prop tumbles naturally alongside the falling figure.
3. **Ragdoll Construction:** Proceed with standard `_build_ragdoll()` processing at current solved bone transforms.
---
## 5. Event System Integration ("When..." Triggers)
Three new edge-triggered prop event types are added to `StageEventRegistry` (`sandbox_stage.gd`) to support the director's "When → Then" mental model.
### Prop Trigger Specifications
| Trigger Type | Source Object | Target Object | Firing Condition |
| ---------------- | --------------- | --------------- | ------------------------------------------------------------------------------- |
| `prop_picked_up` | Stickman / Prop | Prop / Stickman | Emitted as soon as IK arm reach completes and `RemoteTransform2D` socket locks. |
|
| `prop_dropped` | Stickman / Prop | Prop / Stickman | Emitted when a stickman drops a prop manually or via ragdoll disarm.
|
| `prop_thrown` | Stickman / Prop | Prop / Stickman | Emitted at the exact release frame of a throw animation arc.
|
### Signal Routing & Evaluation
1. `StickmanActionRunner` emits `prop_action_executed(action_type, stickman, prop)` upon completing or releasing an anchor attachment.
2. `StageEventRegistry` matches active stage rules where `rule.trigger.type` equals `prop_picked_up`, `prop_dropped`, or `prop_thrown`.
3. Matching rules trigger reactive actions on target stickmen or props (e.g., _When Stickman A picks up Sword X $\rightarrow$ Stickman B ragdolls_).
---
## 6. Implementation Roadmap for Architect Agent
```
[ Phase 1: Action Queue & Locomotion ]
├─ Add pick_up_prop, drop_prop, and throw_prop to StickmanActionRunner
└─ Implement distance evaluation & auto-enqueueing of walk_to sub-goals
[ Phase 2: Socket Attachment & IK Handshake ]
├─ Implement RemoteTransform2D socketing to wrist joint with grip offsets
└─ Implement holding stance arm pose blending
[ Phase 3: Throw Mechanics & Trajectory UI ]
├─ Add trajectory solver & reticle target selector to Director UI
└─ Build throw animation timeline (windup -> release impulse -> follow-through)
[ Phase 4: State Machine Integration & Disarm ]
├─ Intercept held props during set_ragdoll(true) transitions
└─ Transfer cached linear/angular momentum to disarmed prop physics body
[ Phase 5: Event System Expansion ]
├─ Register prop_picked_up, prop_dropped, prop_thrown triggers in StageEventRegistry
└─ Wire action signals to trigger "When -> Then" rule evaluations
```
### Architectural Acceptance Criteria Checklist
- [ ] **Auto-Approach:** Requesting pickup of a distant prop automatically causes the stickman to walk to the prop before reaching out.
- [ ] **Grip Alignment:** Hand attaches precisely to the prop's authored `grip` anchor, respecting local position, rotation, and Z-index offsets.
- [ ] **Trajectory Accuracy:** Thrown props follow the calculated parabolic arc toward the director's targeted ground point.
- [ ] **Ragdoll Disarm:** Entering ragdoll mode immediately detaches held props and applies rig momentum so items tumble realistically.
- [ ] **Event Rule Engine:** Rules configured with `When prop_picked_up`, `When prop_dropped`, or `When prop_thrown` execute downstream actions reliably.