Lua verbs fire their events before returning, in the reference's order (#1015)

In 1.12.1 an event a Lua verb raises runs its handlers before the verb returns: `SignalEvent` (`0x703e50`) walks the listeners in place. About 35 benilla binding sites queued theirs for a later drain instead.

- Verbs now raise inside `tick::verb` steps, and the events fire as each step's model borrow drops. A step follows the reference's split wherever it signals before changing state a getter reads: the money pickup, bag pickup and place, merchant grab, repair cursor, item use, action and pet-bar pickup and drop.
- The grid events fire before `CURSOR_UPDATE`, and a clear's unlock before both, as at `0x4952b3`/`0x495455`. `ITEM_LOCK_CHANGED` carries no arguments.
- `pending_events` is host-only now.

Tests pin handler order per family, nesting from a handler, and the order changes.

Fixes #999
Fixes #582

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Sam Sung
2026-10-12 16:14:10 +07:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 24c7cab983
commit aba120cdcb
26 changed files with 1042 additions and 593 deletions
@@ -148,6 +148,7 @@ fn fire_sites() -> Vec<(String, Vec<char>, String)> {
for call in [
concat!("fire_event", "("),
concat!("fire_event_into", "("),
concat!("model.raise", "("),
concat!("pending_events.push", "(("),
] {
let mut from = 0;
@@ -2103,10 +2103,12 @@ mod tests {
// rustfmt splits a long chain at its dots, so the three shapes are matched
// with the whitespace before each `.` removed.
let text = glue_chains(&text);
// The engine's deferred lane, `pending_events.push((name, args))`, fires too.
const CALLS: [&str; 3] = [
// A verb's `model.raise(name, args)` and the host queue,
// `pending_events.push((name, args))`, fire too.
const CALLS: [&str; 4] = [
concat!("fire_event", "("),
concat!("fire_event_into", "("),
concat!("model.raise", "("),
concat!("pending_events.push", "(("),
];
let mut fires_indirectly = false;