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.
This commit is contained in:
2026-09-23 21:26:13 -04:00
parent 0a7f97cec7
commit 043f114083
22 changed files with 1444 additions and 37 deletions
+18 -1
View File
@@ -225,6 +225,7 @@ var source_stk_path: String = ""
var _locomotion_component: StickmanLocomotion = null
var _ragdoll_component: StickmanRagdoll = null
var _action_runner_component: StickmanActionRunner = null
var _prop_interaction_component: StickmanPropInteraction = null
# ---------------------------------------------------------------------------
# Lifecycle
@@ -234,12 +235,15 @@ func _ready() -> void:
_locomotion_component = get_node_or_null(NodePath("RigComponents/Locomotion")) as StickmanLocomotion
_ragdoll_component = get_node_or_null(NodePath("RigComponents/Ragdoll")) as StickmanRagdoll
_action_runner_component = get_node_or_null(NodePath("RigComponents/ActionRunner")) as StickmanActionRunner
_prop_interaction_component = get_node_or_null(NodePath("RigComponents/PropInteraction")) as StickmanPropInteraction
if _locomotion_component == null:
push_warning("StickmanRig: missing RigComponents/Locomotion; locomotion boundary unavailable.")
if _ragdoll_component == null:
push_warning("StickmanRig: missing RigComponents/Ragdoll; ragdoll boundary unavailable.")
if _action_runner_component == null:
push_warning("StickmanRig: missing RigComponents/ActionRunner; action boundary unavailable.")
if _prop_interaction_component == null:
push_warning("StickmanRig: missing RigComponents/PropInteraction; prop interaction boundary unavailable.")
if _locomotion_component != null:
_locomotion_component.configure(self)
_locomotion_component.arrived.connect(func(target: Vector2) -> void: arrived.emit(target))
@@ -248,7 +252,7 @@ func _ready() -> void:
_ragdoll_component.recovery_requested.connect(func() -> void: request_recovery())
_ragdoll_component.state_transition_requested.connect(_set_state)
if _action_runner_component != null:
_action_runner_component.configure(self, _locomotion_component)
_action_runner_component.configure(self, _locomotion_component, _prop_interaction_component)
_action_runner_component.action_queue = _pending_action_queue
_action_runner_component.action_started.connect(func(action: Dictionary, index: int) -> void: action_started.emit(action, index))
_action_runner_component.action_finished.connect(func(action: Dictionary, index: int) -> void: action_finished.emit(action, index))
@@ -257,6 +261,8 @@ func _ready() -> void:
_action_runner_component.speech_finished.connect(func() -> void: speech_finished.emit())
_action_runner_component.ragdoll_requested.connect(func(enabled: bool) -> void: set_ragdoll(enabled))
_action_runner_component.recovery_requested.connect(func() -> void: request_recovery())
if _prop_interaction_component != null:
_prop_interaction_component.configure(self, _locomotion_component)
# Resolve all runtime node references.
_skeleton = get_node_or_null(NodePath(SKELETON_PATH)) as Skeleton2D
if _skeleton == null:
@@ -313,6 +319,8 @@ func _physics_process(delta: float) -> void:
_pin_mirrored_head_rotation()
if _locomotion_component != null:
_locomotion_component.tick_post_skeleton(delta, int(state))
if _prop_interaction_component != null:
_prop_interaction_component.update_held_attachment(delta)
func get_locomotion() -> StickmanLocomotion:
@@ -450,6 +458,8 @@ func _set_state(next_state: int) -> void:
func set_ragdoll(enabled: bool) -> void:
if enabled:
if _prop_interaction_component != null:
_prop_interaction_component.release_for_ragdoll()
if _locomotion_component != null:
_locomotion_component.reset_for_ragdoll_handoff()
match state:
@@ -805,6 +815,13 @@ func stop_queue() -> void:
if _action_runner_component != null:
_action_runner_component.stop_queue()
func get_prop_interaction() -> StickmanPropInteraction:
return _prop_interaction_component
func release_held_prop_for_lifecycle(reason: StringName = &"lifecycle") -> void:
if _prop_interaction_component != null:
_prop_interaction_component.release_for_lifecycle(reason)
func is_queue_running() -> bool:
return _action_runner_component != null and _action_runner_component.is_queue_running()