6.8 KiB
Phase 4.4 Diagnostic — Trigger Areas Not Firing markdown
Phase 4.4 Diagnostic — Trigger Areas Not Firing
1. Overview
The AGENTS.md documentation claims _update_area_entry() exists and evaluates entered_area rules, but the user reports nothing happens when a rule is applied to an area. This plan is a diagnostic + fix procedure to isolate and repair the break.
2. Suspected Root Causes (Ranked)
| # | Suspected Cause | Symptom |
|---|---|---|
| 1 | _update_area_entry() never called in PLAY |
No events fire at all |
| 2 | Rule's trigger.target reference goes stale (bad weakref) |
Event fires but action target invalid |
| 3 | Edge-trigger dictionary uses wrong key | Event fires once, then never re-fires |
| 4 | Area rect coordinates wrong (local vs. global) | Stickman visually inside, geometry says no |
| 5 | TriggerArea not a child of World |
Detection loop never sees the area |
| 6 | Rule builder doesn't actually save entered_area rules |
Rule appears created, _event_rules empty |
| 7 | Stickman's feet anchor wrong | Uses bone position instead of feet |
3. Diagnostic Procedure (Do First)
Ask the developer to add 4 temporary debug prints and run a test in PLAY mode. Report which prints appear.
Print 1 — Top of _update_area_entry()
print("[AREA] checking ", _world.get_child_count(), " world children")
If this never prints: _update_area_entry() is not called from _physics_process in PLAY. Check the call site.
Print 2 — Before rule matching
print("[AREA] area=", area.name, " rect=", area.get_area_rect(), " movable=", movable.name, " pos=", pos)
If this never prints: the detection loop isn't iterating areas, or the area isn't a TriggerArea child of World.
Print 3 — When a rule matches
print("[RULE] matched rule id=", rule.id, " for area=", area.name)
If this never prints but Print 2 does: the rule's trigger.target.get_ref() doesn't equal area. Reference is stale or wrong type.
Print 4 — Before action executes
print("[ACTION] executing ", action.type, " on target=", action.target.get_ref())
If this never prints but Print 3 does: the rule matched but actions array is empty, or execution is guarded out.
- Fix Plan (Per Root Cause) Fix 1 — Missing _physics_process Call The event engine must run only in PLAY mode:
func _physics_process(delta: float) -> void:
if current_mode != StageMode.PLAY:
return
_update_area_entry()
_update_stickman_prop_collision()
Verify the call exists and is guarded by the PLAY check.
Fix 2 — Stale trigger.target Reference Rules must store a live weakref to the TriggerArea node, not a serialized position or instance ID. At rule creation:
rule.trigger.target = weakref(area_node)
At evaluation:
var target = rule.trigger.target.get_ref()
if target == null:
continue # skip stale rule
if target != area:
continue
If the reference is stored as anything else, rewrite the rule builder to use weakref().
Fix 3 — Wrong Edge-Trigger Key The edge-trigger dictionary must key on BOTH the area ID and the movable ID:
var edge_key := str(area.get_instance_id()) + "_" + str(movable.get_instance_id())
if _area_fired_events.has(edge_key):
continue
_area_fired_events[edge_key] = true
# fire rules...
Using only the area ID means only one stickman can fire the event. Using only the movable ID means the same stickman can't trigger two different areas.
Fix 4 — Wrong Area Coordinates TriggerArea.get_area_rect() must return a world-space Rect2:
func get_area_rect() -> Rect2:
return Rect2(global_position - size * 0.5, size)
Do not use position (local) — the area may be a child of a moved parent.
For the movable's position, use the stickman's feet:
var pos := stickman.global_position + StickmanRig.FOOT_OFFSET
Fix 5 — Area Not in World The palette spawner must add the TriggerArea as a direct child of World, not _ghost_holder or any other intermediate node. The spawner's commit path (_place_at) should call:
_world.add_child(area)
Verify the placement path — the ghost is spawned first (into _ghost_holder), then the actual object is added to World on commit. If the commit path is broken for areas specifically, fix _spawn_area().
Fix 6 — Rule Builder Doesn't Save entered_area The "⚡ When..." flow must actually append a rule to _event_rules with:
{
"id": rule_id,
"trigger": {
"type": "entered_area",
"target": weakref(area_node),
},
"actions": [ ... ]
}
Add a debug print in _finalize_rule() to verify the rule is committed with the correct shape.
Fix 7 — Wrong Feet Anchor Use stickman.global_position + StickmanRig.FOOT_OFFSET for the "position" tested against the area rect. Do not use bone positions or the rig root alone.
- Acceptance Criteria
- Placing an Area via the palette creates a TriggerArea in World.
- A rule with an entered_area trigger can be created (targeting a specific area).
- Walking a stickman into the area in PLAY fires the rule.
- The rule's action executes on the correct target.
- The event fires once per entry (edge-triggered).
- Leaving and re-entering the area re-fires the event.
- Multiple areas + multiple stickmen work independently.
- No console warnings/errors when Play starts.
- Test Cases
| # | Test | Expected |
|---|---|---|
| 1 | Stickman walks into area speaks | Speech bubble appears once |
| 2 | Stickman walks out and back in speaks again | Speech bubble appears twice |
| 3 | Stickman stands still in area for 5s no repeat | Speech bubble appears once |
| 4 | Two stickmen enter same area each fires | Two events fire |
| 5 | Stickman enters area with no rule | Nothing happens, no errors |
| 6 | Rule targets deleted area | No crash warning logged |
| 7 | Area moved in Edit | Play New position used |
| 8 | 5 areas + 5 stickmen + 5 rules | All fire independently |
-
Deliverable A fix commit that resolves whichever of the 7 root causes the 4 debug prints isolate. Include a brief note in the commit message describing which root cause was found and what was changed.
-
Out of Scope New trigger types (e.g., exited_area, stay_in_area).