# Mission scoring and the S/A/B/C/D rank thresholds **✅ Settled 2026-08-27** (static; regenerator `tools/re-capture/mission_scoring.py`, artefact [`data/mission-scoring.txt`](../data/mission-scoring.txt)). Found by following `sub_8230D1F8` — the loader that the `AA_`/`AV_` investigation turned up as a *contaminant*, because it stores at the same object offsets the unit definition uses. Reading its field names instead of its offsets identified it as the **stage-settings loader**, and the scoring block is the part of it the corpus did not have. **Docs checked:** `isl-condition-builtins.md` owns `AUTO_SETTINGS` — the 28 `stageNN_settings.tbl` / `tutorialNN` / `challengeNN` **filenames**; `unit-datasheet-static.md` and `unit-struct-runtime.md` own the *per-unit* `ScorePoint` / `DamageScore` / `MassScore` fields. **What none of them says is how a mission score is assembled or what earns a rank** — `RankScore`, `TimeBonus`, `KillBonus`, `FFPenalty`, `Difficulty_*` appear nowhere in `docs/re/`. ## The tables **24 IDXD objects per language pack × 6 packs = 144.** Each holds exactly three records — `Score_Easy`, `Score_Normal`, `Score_Hard` — so **72 scoring records per pack**, and they share **one** 22-field schema with no variants. | group | fields | |---|---| | rank thresholds | `RankScore_S` `_A` `_B` `_C` `_D` | | score multipliers | `CraftScore_Adjustment`, `VesselScore_Adjustment`, `OtherScore_Adjustment` | | objective bonus | `MainMissionCount`, `MainMissionBonus` | | time bonus | `TimeBonus_Maximun` (sic), `TimeBonus_Basis_Minimum`, `TimeBonus_Basis_Maximum` | | kill / escort bonus | `KillBonus_Maximum`, `FriendlyVesselDamageBonus`, `GettingScoreBasis_Minimum`, `GettingScoreBasis_Maximum` | | penalties | `LostWingmanPenalty`, `FFPenalty_Craft_Basis`, `FFPenalty_Craft_Maximum`, `FFPenalty_Vessel_Basis`, `FFPenalty_Zessel_Maximum` (sic — "Vessel") | Two of the game's own misspellings are load-bearing as join keys: **`TimeBonus_Maximun`** and **`FFPenalty_Zessel_Maximum`**. ## ✅ Difficulty changes the earning rate, not the bar Comparing the three difficulty records inside each object, **10 of the 22 fields move and 12 never do** — and the ones that never move include **all five `RankScore_*` thresholds**: ``` differ between Easy/Normal/Hard CraftScore_Adjustment 21 VesselScore_Adjustment 21 (count = objects out of 24) OtherScore_Adjustment 21 LostWingmanPenalty 21 FFPenalty_Craft_Maximum 21 FFPenalty_Zessel_Maximum 21 KillBonus_Maximum 18 MainMissionBonus 18 TimeBonus_Maximun 18 FriendlyVesselDamageBonus 13 never differ RankScore_S/A/B/C/D, MainMissionCount, GettingScoreBasis_*, TimeBonus_Basis_*, FFPenalty_*_Basis ``` So a rank bar is **per stage**, identical on every difficulty; what difficulty changes is how fast you earn against it (`×0.5` / `×1.0` / `×2.0` on the three `*Score_Adjustment` multipliers) and how hard the penalties bite. That is a design statement a port has to reproduce exactly, and it is measured, not read off the names. ## Per-stage variation **23 of the 24 objects differ from the commonest `Score_Normal` record**, almost always in the `RankScore_*` thresholds and the objective count/bonus — as expected for per-mission tuning. Nine of the 72 records (three objects × three difficulties) zero the multipliers, the penalties and `TimeBonus_Maximun` outright: missions that are not scored. Full per-object diffs are in the artefact. ## ✅ Which object is which stage — settled All 24 are named `stage\StageParameter_S.tbl`, plus `_Tutorial` (shared by all six tutorials) and `_Test`. See [`stage-settings-table.md`](stage-settings-table.md); that also explains 24 against the 29 stage records, and confirms the `IsBoss16Enable` object is S16. ⚠️ The names are **not** `AUTO_SETTINGS`'s `stageNN_settings.tbl`; none of those 28 hashes to any of these objects.