Files

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.

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

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

  2. Out of Scope New trigger types (e.g., exited_area, stay_in_area).