# ✅ The MISSION timer is a second, separate clock — and `timer_set`'s second argument is a second COUNTDOWN `mission-phase-timers.md` left this open: "`timer_set`'s second argument (`180`) — a limit and a warning threshold is the obvious reading, but it is **not** established." It is established now, and **the obvious reading is wrong**. This clock has nothing to do with the 32 stopwatches in [isl-timers](isl-timers.md); it is five separate scalar fields on the same ScriptPhase, driven by its own updater. ## The five fields Zeroed together by `sub_8225FEF8` (`0x82260084`–`0x82260098`): | field | type | meaning | |---|---|---| | `[phase+304]` | f32 | **elapsed**, counts **up** | | `[phase+308]` | f32 | **countdown A**, counts **down** | | `[phase+312]` | f32 | **countdown B**, counts down *only after A reaches 0* | | `[phase+316]` | int | **running** — gates the elapsed counter | | `[phase+320]` | int | **armed** — gates the two countdowns | ## The five built-ins All are stubs into the ScriptPhase vtable, slots 90–94 (`0x8226C690`–`0x8226C6F0`): | built-in | slot | what it does | calls disc-wide | |---|---|---|---| | 127 `timer_set(a, b)` | 94 | `armed = 1; A = (f32)local[0]; B = (f32)local[1]` | 29 | | 123 `timer_resume` | 90 | `running = 1` | 40 | | 124 `timer_stop` | 91 | `running = 0; armed = 0` | 170 | | 125 `timer_reset` | 92 | `elapsed = A = B = 0.0; armed = 0` | **0** | | 126 `timer_elapsed` | 93 | returns `elapsed` as a double in `[phase+176]` | **0** | `timer_set` reads its two arguments as **doubles** at `0(r4)` and `8(r4)`, `r4` being the converted locals array — so `local[0]` and `local[1]`. ## ✅ The update — `sub_822639B8(phase, dt)` Called from **`ScriptPhase::Update` at `0x82263528`**, with **`fmr f1, f30`** — the *same* `dt` the stopwatch bank gets at `0x82263480`. So this clock counts the same **seconds** ([isl-timers](isl-timers.md)), and `600 / 900 / 1200 / 1800` and `180` are 10 / 15 / 20 / 30 minutes and 3 minutes. ``` 822639C8 if [+316] != 0: [+304] += dt ; elapsed counts UP 822639E4 if [+320] != 0: 822639F4 if [+308] > 0.0: [+308] -= dt ; A counts down ... 82263A00 else: [+312] -= dt ; ... then B does 82263A10 if [+312] < 0.0: [+312] = 0.0 ``` ### 🔴 That refutes "a limit and a warning threshold" A threshold would be **compared** against the countdown. `[+312]` is never compared with `[+308]` anywhere. It is *decremented*, and the `bc 12, gt` at `0x822639FC` jumps **away** to the `A -= dt` arm — so `B -= dt` runs **only** in the `A <= 0` branch. The two are **sequential countdowns**: a per-mission period, then a fixed second period. ## ✅ Every mission agrees, and the second value never varies 29 `timer_set` sites across the disc: | first argument | sites | |---|---| | 600 | 19 | | 1200 | 8 | | 900 | 1 (Stage 26) | | 1800 | 1 (Stage 24) | | **second argument** | **180 in all 29** | A per-mission limit that varies, followed by a fixed 3-minute stage. Note this constancy is *consistent* with the sequential reading and does not by itself establish it — the disassembly above is what does. ## ✅ What it feeds — and what it does not Each frame the updater allocates a 32-byte message (type tag `0xAB03E5BA`, vtable `0x820A8D68`) and posts it to the bus at `[0x828F35DC]`, carrying: ``` +24 = [phase+304] elapsed +28 = [phase+312] countdown B +16 = running ([+316] != 0) +20 = expired ([+320] == 1 && [+308] <= 0.0) ``` `expired` is exactly "countdown A has run out" — the point at which B starts. 🔑 **The script never reads this clock.** `timer_elapsed` and `timer_reset` have **zero call sites in all 28 scripts**; the script only *arms* (127), *starts* (123) and *stops* (124) it. Its only consumer read here is the posted message. ## 🔴 A conflation in `mission-phase-timers.md`, corrected That page says timer 0 "is started by `timer_set(1200, 180)` then `timer_resume`, with a 1200-second limit". Those are **two different clocks armed back to back**: ``` timer_set(1200, 180) -> this object: A = 1200, B = 180, armed timer_resume -> this object: elapsed starts counting set_flag(0) -> STOPWATCH 0 starts (isl-timers.md) ``` The timeline's `kind = 0` reads stopwatch 0, not `[phase+304]`. The two start in the same coroutine one instruction apart, which is why they read almost the same value and the conflation was invisible. **The 1200-second limit belongs to the mission timer; it is not stopwatch 0's limit.** ## 🟡 Not settled * **Who subscribes to `0xAB03E5BA`.** The literal appears at exactly one site — the producer — but `0xAB03` is the high half of *many* message tags in this image and dispatch is not by literal compare, so a single occurrence is **not** evidence that nothing consumes it. Whether running out of time ends the mission is therefore unread. * What B counting to zero does. Nothing observed acts on it. * Whether `timer_stop`'s 170 sites are all phase teardown, or some are a real mid-mission stop.