diff --git a/docs/re/data/isl-stage02.txt b/docs/re/data/isl-stage02.txt index e05498dc..f31566c2 100644 --- a/docs/re/data/isl-stage02.txt +++ b/docs/re/data/isl-stage02.txt @@ -91,6 +91,7 @@ Generated by `tools/re-capture/isl_report.py calls`. 30 objective_marker 179 20 hp_pct_test 167 47 squadron_attack 137 + 15 set_group_speed 127 18 dist_lt 92 5 kill_coroutine 83 70 unit_alive 71 diff --git a/docs/re/structures/isl-builtins.md b/docs/re/structures/isl-builtins.md index fb4c0639..a3073e49 100644 --- a/docs/re/structures/isl-builtins.md +++ b/docs/re/structures/isl-builtins.md @@ -140,7 +140,7 @@ and spread checks but its maximum *exceeds* the table — flag indices run 0..31 against symbol tables as small as 40 — so it is excluded, and the disassembler now declines to resolve it rather than printing an invented name. -### 🟡 Built-in 15 takes a craft-class scalar — but "speed" does not survive intact +### ✅ Built-in 15 is `set_group_speed` — the group's commanded speed `15` is the biggest unnamed built-in: **1360 sites across 27 of 28 stages**. Its operand shape is invariant — `(unit, double)` — and joining every call site's @@ -173,8 +173,51 @@ The asteroid is the control that makes this sharp: a genuinely immobile object gets **0 every time**, so "immobile things get a meaningless value" does not explain the turrets either. -So: ✅ a craft-class-dependent scalar in speed's range and with speed's ordering; -❌ not `set_speed` as a plain reading. **Left unnamed.** +#### ✅ What the members contribute — and why the turrets stop being a problem + +`sub_82348830` is a `std::map::find`, and the object it returns is the per-member +**unit definition**. That identification is not a guess: the same spawn loop +builds *two* aggregates, and each lands on a semantically apt field with the apt +reducer — + +| group field | reducer | member field (`unit_definition_layout.txt`) | +|---|---|---| +| `+192` | **min**, seeded `FLT_MAX` | `+164` = **`CruisingVelocity`** | +| `+472` | **sum** | `+84` = **`HP`** | + +A wrong struct would have to make both offsets land on apt fields *and* pair each +with the apt reducer. Minimum of a speed, sum of hit points: that is a formation's +cruise limit and its total health. + +#### ✅ The quantitative test, over all 1360 sites + +If the script is commanding a speed, its value should respect the craft's own +limits. Joining every call site to its craft's definition: + +| bound | holds | fails | +|---|---|---| +| value ≤ the craft's **`MaximumVelocity`** | **1355 / 1360 = 99.6 %** | 5 | +| value ≤ the craft's `CruisingVelocity` | 1042 / 1360 = 76.6 % | **318** | + +The test discriminates: the cruise bound is broken 318 times, the hull maximum +just 5. So the script sets a **commanded speed**, free to exceed the cruise +default and bounded by what the hull can do. + +🟡 The five exceptions are small overshoots on three craft — +`UN_e106_ADAN_Destroyer` 200 against a 150 maximum (×2) and +`UN_e011_ADAN_Attacker_B_HF`/`_Wayne` 500 against 450 (×3). Designer overrides, +or the engine clamps; not established. + +#### ✅ The turret anomaly dissolves + +`UN_e007_ADAN_Turret`'s definition carries `MaximumVelocity` **500** and +`CruisingVelocity` 280 — the data models turrets as if mobile. So a script value +of 400 is perfectly legal in the data model; it simply never manifests, because a +turret does not translate. The 13 % of sites that looked like a refutation were +an artefact of assuming turrets have no velocity fields. + +**Named `set_group_speed`.** Default = the slowest member's `CruisingVelocity`; +mode 1 restores it, mode 3 sets it, mode 2 hands it a global constant. #### ✅ The write side, read directly diff --git a/tools/re-capture/isl.py b/tools/re-capture/isl.py index ea1b4ece..4a177f0d 100755 --- a/tools/re-capture/isl.py +++ b/tools/re-capture/isl.py @@ -80,6 +80,7 @@ BUILTIN = { 1: 'start_coroutine', 2: 'deploy_squadron', 3: 'move_order', 4: 'wait_s', 5: 'kill_coroutine', 6: 'END_PHASE', 8: 'set_flag', 9: 'read_freg', 10: 'random', 11: 'end_coroutine', 13: 'play_se', 14: 'play_bgm', + 15: 'set_group_speed', 17: 'wait_frames', 18: 'dist_lt', 20: 'hp_pct_test', 24: 'squad_survival_pct', 26: 'damage_unit', 30: 'objective_marker', 31: 'objective_marker_at_route', 33: 'global_counter0', 34: 'global_counter1', 36: 'screen_fade',