feat: Implement Phase 4 Trigger Events System

- Added a new event-driven system for reactive storytelling, allowing rules like "When X happens, do Y."
- Introduced TriggerArea class for placeable sensors in the stage.
- Enhanced StickmanRig to emit signals for actions and arrivals.
- Updated StageDirectorVisuals to render rules visually with labels and badges.
- Modified StageSpawner to support spawning TriggerAreas.
- Improved text baseline calculations in speech bubbles and rule labels.
- Added tests for text baseline fixes to ensure proper rendering.
- Documented the implementation plan for Phase 4 in PHASE_4_TRIGGER_EVENTS.md.
- Created a polish plan for Phase 4 in PHASE_4b_POLISH.md.
This commit is contained in:
2026-09-01 08:58:07 -04:00
parent bf11a5fab5
commit f208127917
17 changed files with 1538 additions and 8 deletions
+20 -2
View File
@@ -272,7 +272,7 @@ signal state_changed(new_state: int)
# Director signals (Phase 3a)
# ---------------------------------------------------------------------------
signal arrived # walk_to reached its destination
signal arrived(target: Vector2) # walk_to reached its destination
signal action_started(action: Dictionary, index: int)
signal action_finished(action: Dictionary, index: int)
signal queue_finished # queue ran to completion (not on stop)
@@ -1134,7 +1134,7 @@ func _finish_walk(reason: String = "") -> void:
_restore_standing_markers()
_walk_done = true
_walking = false
arrived.emit()
arrived.emit(_walk_target_feet)
func _cancel_walking() -> void:
@@ -1215,6 +1215,24 @@ func queue_size() -> int:
return action_queue.size()
## Append `actions` to the queue and, when the runner is idle, resume execution
## at the first newly-appended action WITHOUT replaying the existing queue.
## Used by the Phase 4 event engine to inject reactive actions onto a stickman.
func enqueue_reactive(actions: Array[Dictionary]) -> void:
if actions.is_empty():
return
var start := action_queue.size()
action_queue.append_array(actions)
queue_changed.emit()
if _runner_state == RunnerState.IDLE:
# Resume at the first appended action (not at index 0), so any queued
# sequential actions are skipped over rather than replayed.
_current_index = start - 1
_runner_state = RunnerState.EXECUTING
_action_phase = ActionPhase.NONE
_stop_requested = false
# ---------------------------------------------------------------------------
# Runner state machine (Phase 3a)
# ---------------------------------------------------------------------------