re: the bark queue, Priority ordering, and Yes = a one-shot line

sub_822109B0 allocates 68-byte nodes from a pool capped at 128; the Pattern
byte at +23 doubles as the occupancy flag.  Priority orders the pending list
descending; the >=10 front-push arm is dead because the disc's range is 1..9.

The tick sub_8220FC50 does rule[+36] |= (1 << node[+25]) & rule[+32], so a
line enters the used mask only if its Yes bit is set: Yes = one-shot, No =
repeatable.  388 one-shot lines on the disc.  Closes what reads +32.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
This commit is contained in:
Claude (auto)
2026-08-27 17:17:01 +00:00
parent 8dc270091b
commit f78565fe9e
3 changed files with 121 additions and 6 deletions

View File

@@ -265,11 +265,11 @@ can never reach, because it skips the record first. Thirteen dead records out of
### 🟡 What this does *not* settle
`Probability` and the line pick are settled above. **Still open: everything
after the hand-off to `sub_822109B0`** — how `Priority` orders the queue, whether
`Interval` is a cooldown per event or per speaker, what `IntervalFluctuation`
randomises, and where `+36`'s bits are set. `+32` (the disc `Yes` mask) is still
read by nothing that has been identified.
`Probability`, the pick, the queue, `Priority` and the `Yes`/`No` flag are all
settled above. **What remains is only the two interval fields:** whether
`Interval` (`+20`) is a cooldown per event, per rule or per speaker, and what
`IntervalFluctuation` (`+24`) randomises against. Neither is read by the entry,
the pick, the enqueue or the tick paths read so far.
🔴 One handle was tried and refuted: searching the whole image for functions
that touch `+16`/`+17`/`+18`/`+20`/`+28`/`+32`/`+36` returns dozens of unrelated
@@ -404,6 +404,75 @@ hand-off, not on the way in. Within the whole chatter compilation unit
`sub_82213840` (the comparator) and `sub_82210670` — which is what makes this
attribution safe where a binary-wide offset search is not.
## ✅ The queue, `Priority`, and what `Yes`/`No` actually means
**Settled 2026-08-27** from `sub_822109B0` (enqueue) and `sub_8220FC50` (the
per-frame tick, called from `sub_821AB650` and `sub_821B5FB8`).
### The pending queue
`sub_822109B0` allocates from a fixed pool: **68-byte nodes starting at
`this+32`**, with `[this+8740]` as the live count and `[this+8736]` as the list
head. **If the count is already 128 it returns 0 and the bark is dropped.**
A slot is free iff its byte at `+23` is zero — and `+23` holds **`Pattern`**,
which is never 0 for a loaded rule (the enum is 1…4). The presentation field
doubles as the occupancy flag.
| node | content |
|---|---|
| `+4` | the chosen message id |
| `+8` | the speaker entry |
| `+12` | the rule object |
| `+16` | `EffectiveTime` (frames) |
| `+20`, `+21` | zero at enqueue |
| `+22` | `Priority` |
| `+23` | `Pattern` — also the in-use flag |
| `+24` | state byte, zero at enqueue |
| `+25` | the chosen message **index** |
### `Priority` orders the list, and its fast path is dead
Insertion compares `Priority` against **10**. At or above 10 the node is pushed
straight onto the front with no ordering; below 10 it is spliced ahead of the
first node with a strictly smaller `Priority`, leaving the list **descending by
`Priority`**. **No shipped record reaches 10** — the disc's range is 1…9 — so the
front-push arm never runs on the retail data, and every bark takes the ordered
path.
`sub_82210AF0` (called only from the tick) walks the list and returns the largest
`[node+20]` among pending nodes whose `Priority` is **higher** than the one being
considered and whose `[node+24]` is not `0x10` — a preemption / ducking query
against higher-priority speech already queued.
### ✅ `Yes`/`No` is a one-shot flag — the `+32`/`+36` pair closes
The tick ends a line with exactly this, at `0x82210160`:
```
r10 = node[+25] ; the index of the line just spoken
r9 = rule[+32] ; the Yes mask, straight off the disc
r8 = rule[+36] ; the used mask, zeroed by the loader
rule[+36] = ((1 << r10) & r9) | r8
```
The spoken line's bit enters the **used** mask **only if its `Yes` bit is set**.
Since `sub_82210670` treats a message as eligible only when its bit in `+36` is
clear, the meaning is exact:
* **`Yes` → the line is one-shot.** Once spoken it is permanently removed from
that rule's pool.
* **`No` → the line repeats.** Its bit never enters `+36`.
That is what the census counts: **8 940 `No` and 388 `Yes`** — 388 lines across
the disc are authored to be heard once. It also closes the earlier open question
"what reads `+32`": nothing until a line finishes, and then only to gate this
one AND.
The tick also branches on `Pattern < 3` (using `this+8776` on the taken side), so
`Window` (3) is on a different presentation path from `Sound` (1) — consistent
with the two values the disc ships.
## 🟡 Not settled
* The *policy* the seven fields drive — see "what this does not settle" above.