# What the missions actually test โ€” all 28 scripts surveyed Status: โœ… corpus-wide counts, verified independently; โœ… two idioms identified that change how the numbers read; ๐ŸŸก one built-in's semantics only partly read. All 28 `Stage\StageNN.ssb` scripts decoded with `tools/re-capture/isl.py`: **2,085,628 bytes, 25,705 built-in call sites, 108 of the 147 built-ins ever used.** Nothing failed to parse. Re-checked here against the survey: `33=0`, `34=0`, `108` distinct, `hp_pct_test` 1955, `unit_state` 1271 โ€” all exact. ## โœ… The kill-counter primitives are DEAD, game-wide `global_counter0` / `global_counter1` (built-ins 33/34) have **zero call sites in all 28 stages**. Their handlers exist and are wired โ€” they read `[0x828F35C4]+8` and `+12` into the int result register โ€” and **no mission ever calls them**. 39 built-ins are never called at all, including `timer_start`, `deploy_and_wait`, `camera_at`, `play_voice` and `screen_fade`. ### ๐Ÿ”ด CORRECTION: "no mission counts kills" was too strong I wrote that here, and it is wrong as stated. What is true is narrower: **no mission reads the two GLOBAL counters.** The game does track kills, and missions do test them โ€” through *attribution*, per unit and per squadron pair: | built-in | what it is | sites | |---|---|---| | **105 `killed_by_player(unit)`** | reads `[squadron+600]`, which the unit-death handler sets from the damage message's `_BY_PLAYER` flag โ€” "was the most recent death in this squadron the player's doing" | **117 across 14 stages** (S02: 38, S01: 32) | | **72** | numerator is a per-`(killer, victim)` **squadron kill counter** incremented in the unit-death handler | 13, **S01 only** | `killed_by_player` is the 17th-most-used built-in in Stage 02. So "did *the player* get that kill" is a common mission predicate โ€” the game simply never asks "how many kills in total". The evidence for 105 is the game's own strings: the death handler branches on that flag into `"KILLED_BY_PLAYER"` versus `"KILLED"`, alongside `LOST_TURRET_BY_PLAYER`, `SHIELD_DOWNED_BY_PLAYER`, `DAMAGED_BY_PLAYER_GREATER_50` and the rest of the family. ## โœ… `hp_pct_test(unit, 0.0)` *is* a destruction test **1786 of 1955 `hp_pct_test` calls (91 %) pass threshold `0.0`** โ€” 107 of Stage 02's 109 resolvable sites. Built-in 20's handler (`0x8226AB78`) explains it: above zero it returns `pct โ‰ค threshold`, but at exactly `0.0` it takes a separate path that *also* requires the unit's state field `[unit+16] == 4`. So **`unit_state` and `hp_pct_test(โ€ฆ, 0)` are interchangeable "is it destroyed" tests**, and stages simply pick an idiom. Real damage thresholds are the minority: 50 (57ร—), 30 (19ร—), 70 (18ร—), 40 (14ร—), then a thin tail. This matters for reading the counts: a stage with no `unit_state` is not necessarily testing something different โ€” S14 has none, and 25 of its 34 `hp_pct_test` calls pass `0.0`. ## โœ… `squad_survival_pct` is a boolean, not a percentage 29 sites, in S06/S07/S08/S09/S15, and **every one passes a friendly TCAF squadron and the threshold 99.9**. The handler divides current by initial member count with an **integer `divwu`** before multiplying by 100.0, so the value can only be **100 or 0**. At a 99.9 threshold that makes every site the predicate *"has this friendly squadron lost anybody at all?"* โ€” an escort-casualty test. **The game has no "destroy N% of a squadron" objective.** ## โœ… The condition vocabulary, and the outliers | pattern | stages | |---|---| | `unit_state` + `hp_pct_test` + `dist_lt` + `unit_relation` | the story stages | | `squad_survival_pct` (escort intact?) | S06, S07, S08, S09, S15 | | `group_ratio_pct` | **S01 only**, 13 sites, always `(TCN001, )` | | player gauges 132/133/134 | **S16 only** โ€” 11 + 2 + 2 sites | | `is_engaged` | **S05 only**, 3 sites, all on `TCN004` | **S18โ€“S23, the tutorials, are the outliers by a distance.** No flag/trigger machinery at all โ€” zero `read_freg`, `clear_flag`, `reset_phase_threads` (built-in 100, formerly mis-named `push_trigger`), `named_event`, `MARK_LAST_PHASE`, `FORCE_END_PHASE`, and none of the timer family. One `set_flag` and one `END_PHASE` each. They are linear scripted lessons, not missions. **S16 has no unit predicates whatsoever** โ€” its symbol table 2 holds three entries, and its conditions are a descending player-gauge ladder (75/50/25/10/0). A player-state set piece. ## ๐ŸŸก Not settled * โœ… **`group_ratio_pct` (72) โ€” read to the bottom, and the old label was wrong in the numerator.** It is `100 ร— (units of squadron B killed by squadron A) รท (declared member count of B)`, with a real `fdiv`. The numerator is `e[+12]` of a 16-byte entry in the table at `0x828F363C`, created by an interpreter command (max 32 entries) and incremented inside the unit-death handler as `(killer squadron id, victim squadron id)`. So it is **attributed kills, not survivors** โ€” and it reads 0 unless the script first registered the `(A, B)` pair in `[phase+328]`. The name `group_ratio_pct` is retained but the "current รท initial members" gloss is **withdrawn**. * โœ… **Built-in 55 is effectively dead**: **0 call sites corpus-wide**. It computes a component-count ratio by category (Bridge / Hatch / Turret / ShieldGenerator / Thruster / Versatile โ€” names taken from the unit-`.tbl` loader's own `TurretCount`/`ShieldGeneratorCount` strings), but its numerator is a spawn-time snapshot nothing decrements, its category pairing is offset by one (slot 0's denominator absorbed `Hatch`), its k=5 denominator is never written, and it truncates with an integer `divw` before the ร—100. Whether the misalignment is a shipped bug is **unknown**. * โœ… **Built-in 75 is text** โ€” five symbol-table-1 names resolved through a dictionary at `[phase+376]` into five 1024-wchar slots, which `sub_821AA1B0` copies into five 256-wchar HUD fields when `[phase+336] == 2`. Mechanism confirmed; **which** text (objectives, briefing, subtitles) is not. 39 sites, 3 in Stage 02. * `isl.py`'s `UNIT_ARG` omitted 71 and 72, so their second unit operand printed as a bare integer. Noted in the tool; 56 may be the same shape, unverified. * The timer names in `isl.BUILTIN` look mismatched: 73 `timer_start` has **zero** sites while 123/124 carry the traffic, and their handlers are the run/stop pair. Flagged, not renamed.