re: Interval and IntervalFluctuation are the cooldown; the chatter system closes
sub_82210C38 retires a line and is the only chatter function reading the rule's +20/+24 as words: node[+16] = Fluctuation * rand01 + Interval frames, state 0x20. Interval is a floor, Fluctuation a uniform additive jitter. The queue node IS the cooldown timer -- the tick counts +16 down and only then frees the slot, so the already-queued bail in sub_82210670 and the cooldown are one mechanism: a speaker cannot repeat an event until its node expires. A lingering node still holds one of the 128 pool slots. Also: +20 on the node is a ducking level, raised while a higher-priority line plays and released on retirement. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
This commit is contained in:
@@ -4344,3 +4344,49 @@ cooldown per event, per rule or per speaker, and what `IntervalFluctuation`
|
||||
paths read so far — `sub_82210320` was checked as a candidate and is a different
|
||||
structure (a deque at `this+8744`, with a divide-by-60 frames→seconds), not the
|
||||
rule object. Also still open: which of the 49 speakers are wingmen.
|
||||
|
||||
---
|
||||
|
||||
## ✅ 2026-08-27 — `Interval` + `IntervalFluctuation`: the chatter system closes
|
||||
|
||||
The last two open fields. **Docs checked:** `preset-message-rules.md` (mine);
|
||||
nothing else in the corpus mentions `sub_82210C38`.
|
||||
|
||||
✅ **`sub_82210C38` is the retire-a-line routine** (called from the tick
|
||||
`sub_8220FC50` and from `sub_8220FB90`) and is the **only** place in the chatter
|
||||
code that reads the rule's `+20`/`+24` as **words** — which is what separates
|
||||
them from the byte fields at the same offsets in the queue node. On retirement:
|
||||
|
||||
node[+16] = (int)( (float)rule[+24] * rand01() + (float)rule[+20] )
|
||||
node[+24] = 0x20
|
||||
|
||||
✅ **`Interval` is a floor, `IntervalFluctuation` is uniform additive jitter** —
|
||||
the cooldown is uniform over `[Interval, Interval + IntervalFluctuation)` frames.
|
||||
Disc: `Interval ∈ {0,3,4,5,30,120}` s, jitter 0 or up to +2 s (813 records).
|
||||
|
||||
✅ **The cooldown is per (speaker, event), and the queue node IS the timer.**
|
||||
`node[+16]` — which held `EffectiveTime` while the line played — is reused. In
|
||||
state `0x20` the tick decrements it per frame and only at zero unlinks the node,
|
||||
clears `[node+23]` to free the slot and decrements `[this+8740]`. **This closes
|
||||
the loop with the "already queued" bail in `sub_82210670`: they are the same
|
||||
mechanism.** A retired line keeps its node alive for `Interval + jitter` frames,
|
||||
and for exactly that long the same speaker cannot repeat the same event. The rule
|
||||
object holds no timestamp; nothing per-rule or global is involved.
|
||||
|
||||
⚠️ **Port note:** a lingering node still occupies one of the **128** pool slots —
|
||||
a 120 s `Interval` parks a slot for two minutes.
|
||||
|
||||
✅ Same routine: stops the presentation via `sub_822168E8` on `this+8776`
|
||||
(`Pattern` 1) or `node+28` (`Pattern ≥ 3`); and when `[node+20] == 1` sweeps the
|
||||
pending list calling `sub_82217980(…, 0)` on every node not in state `0x10`.
|
||||
With `sub_82210AF0` (max `[node+20]` over *higher*-priority pending nodes) that
|
||||
makes `+20` a **ducking level**, raised while a higher-priority line plays and
|
||||
released on retirement.
|
||||
|
||||
**The chatter system is now closed end to end** — data, loader, phase merge,
|
||||
firing, probability, pick, queue, priority, one-shot flag and cooldown.
|
||||
|
||||
❔ **Not established** (and none of it changes the data model a port needs): the
|
||||
numeric attenuation of the ducking level, the meaning of node states other than
|
||||
`0x10`/`0x20`, and whether `sub_822168E8`/`sub_82217980` do more than
|
||||
stop/resume. ❔ Still open elsewhere: which of the 49 speakers are wingmen.
|
||||
|
||||
Reference in New Issue
Block a user