All 28 StageNN.ssb decoded: 2,085,628 bytes, 25,705 call sites, 108 of 147 built-ins used. Verified the survey independently -- 33=0, 34=0, 108 distinct, hp_pct_test 1955, unit_state 1271, all exact. global_counter0/1 have ZERO call sites in all 28 stages. The handlers exist and are wired; no mission calls them. So 'does a wave start after N kills?' is answered for the whole game, not just Stage 02: no mission counts kills. Two idioms change how the counts read. hp_pct_test(unit, 0.0) IS a destruction test -- 1786 of 1955 calls (91%) pass 0.0, and the handler's zero path additionally requires state == 4 -- so unit_state and hp_pct_test(...,0) are interchangeable and stages just pick one. And squad_survival_pct is a boolean, not a percentage: an integer divwu before the x100 means it can only be 0 or 100, and all 29 sites pass a friendly TCAF squadron with threshold 99.9, making it 'has this escort lost anybody'. The game has no destroy-N%-of-a-squadron objective. Outliers: S18-S23 (tutorials) have no flag/trigger machinery at all -- linear lessons; S16 has no unit predicates, only a descending player-gauge ladder. Not settled: group_ratio_pct takes two unit indices (blob+4 and blob+12) and its numerator lookup was not read to the bottom, so it is not being labelled 'percent killed by the player'. isl.py's UNIT_ARG omitted 71/72.
4.0 KiB
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.
That settles the "does a wave start after N kills?" question far beyond Stage 02:
no mission in the game counts kills. 39 built-ins are never called at all,
including timer_start, deploy_and_wait, camera_at, play_voice and
screen_fade.
✅ 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, <ADAN squadron>) |
| 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, 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) takes two unit indices — blob+4 and blob+12 — and its numerator comes from a pair-keyed lookup through[phase+328]that was not read to the bottom. The natural reading (a per-squadron ratio credited to the player's flight) fits S01's usage, but is not established, so it is not being called "percent killed by the player".isl.py'sUNIT_ARGomitted 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.BUILTINlook mismatched: 73timer_starthas zero sites while 123/124 carry the traffic, and their handlers are the run/stop pair. Flagged, not renamed.