# 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.