This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/re/mission-objective-vocabulary.md
Sylpheed RE agent 7a88d24e66 re: correct 'no mission counts kills' -- attribution IS used, globals are not
I wrote that no mission counts kills. Too strong. What holds is narrower: no
mission reads the two GLOBAL counters (33/34, verified 0 sites). The game does
track kills and missions test them through attribution:

  built-in 105 killed_by_player(unit) -- reads [squadron+600], which the
  unit-death handler sets from the damage message's _BY_PLAYER flag -- 117 sites
  across 14 stages (S02: 38, S01: 32), the 17th-most-used built-in in Stage 02.

  built-in 72 -- numerator is a per-(killer, victim) squadron KILL COUNTER,
  incremented in the unit-death handler; 13 sites, S01 only.

So 'did the player get that kill' is a common predicate; the game just never asks
'how many kills in total'. Evidence for 105 is the game's own strings: the death
handler branches into KILLED_BY_PLAYER vs KILLED, beside LOST_TURRET_BY_PLAYER,
SHIELD_DOWNED_BY_PLAYER and the rest.

Also withdraws the 'current / initial members' gloss on built-in 72: it is
100 x (units of B killed by A) / (declared members of B), attributed kills rather
than survivors, and reads 0 unless the script registered the (A,B) pair.

Built-in 55 resolved and shown to be effectively dead (0 sites corpus-wide);
built-in 75 confirmed as five HUD text lines via a [phase+376] dictionary.
2026-08-25 20:23:34 +00:00

6.2 KiB
Raw Blame History

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, <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) — 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.