re: the Hangar loadout system -- loadout, allow-list, arsenal item
15 loadout records, one per flight position x pilot (Bird1-Sandra .. Rhino4-Yoji), each with Arm1/Arm2/Arm3/Nose + UnitID. The same trap as PlayerWeapon, one level up: Arm1/Arm2/Arm3/Nose do NOT name items. They name a per-slot ALLOW-LIST record -- one of 24 whose only named field is Type (the slot kind) -- and the candidate items are that record's positional, unnamed fields, in order. Four hops: Rhino4-Yoji.Arm1 -> STANDARD_ARM1 -> [Falcon_9AM, Condor_105AM, ...] -> item.PlayerWeapon = Turret_NNN -> slot.WeaponID -> Weapon.ID Controls: Arm1/2/3/Nose -> allow-list record 60/60; allow-list positional entries -> arsenal item 70/88, and every one of the 18 misses is the single sentinel No_Equipment -- one of the four WEAPONS-roster values with no item record, i.e. the empty-slot marker. UnitID is two ID spaces at once: 5 rows name a unit Generic.ID (the three -Katana rows are the player -- a _Player craft plus an extra, empty PlayerUnit field), 8 name a character, resolving as Character + the value into the 64-record character table. Two values resolve to nothing, both single rows against 13 that do: Rhino2-Ellen.UnitID = UN_f001_TCAF_DeltaSaber_W exists nowhere (checked as a Generic.ID across every pak and as a record name, 0 hits) while UN_f002_TCAF_DeltaSaber_W does -- consistent with a shipped typo, reported not diagnosed -- and Rhino4-Brandon.UnitID = BRANDON has no CharacterBRANDON among the 64. New structure doc, artefact and regenerator; the other four artefacts regenerate byte-identical.
This commit is contained in:
@@ -827,6 +827,42 @@ unknown, what evidence exists, and what the first step would be. Move an item in
|
||||
field; the stat keys in records not opened here (`MegaTons`, `BulletLimit`,
|
||||
`CrewCount`, `HP_CLASS`); why the roster lists 59 of 131.
|
||||
|
||||
* ✅ **(2026-08-27) THE HANGAR LOADOUT SYSTEM — the layer above the item chain.**
|
||||
[structures/hangar-loadout-system](structures/hangar-loadout-system.md),
|
||||
artefact `data/hangar-loadouts.txt`, regenerator
|
||||
`tools/re-capture/hangar_loadouts.py`.
|
||||
**15 loadout records**, one per flight position x pilot (`Bird1-Sandra` …
|
||||
`Rhino4-Yoji`), each with `Arm1`/`Arm2`/`Arm3`/`Nose` + `UnitID`.
|
||||
🔑 **The same trap as `PlayerWeapon`, one level up**: `Arm1`/`Arm2`/`Arm3`/
|
||||
`Nose` do NOT name items — they name a **per-slot ALLOW-LIST record**, one of
|
||||
24 whose only *named* field is `Type` (the slot kind). The candidate items are
|
||||
the allow-list's **positional, unnamed fields, in order**. Four hops:
|
||||
`Rhino4-Yoji.Arm1` → `STANDARD_ARM1` → `[Falcon_9AM, Condor_105AM, …]` →
|
||||
`item.PlayerWeapon = Turret_NNN` → `slot.WeaponID` → `Weapon.ID`.
|
||||
🧪 Controls: `Arm1/2/3/Nose` → allow-list record **60/60**; allow-list
|
||||
positional entries → arsenal item **70/88**, and **every one of the 18 misses
|
||||
is the single sentinel `No_Equipment`** — one of the four `WEAPONS`-roster
|
||||
values with no item record, i.e. the empty-slot marker, not a decode failure.
|
||||
Six sets: `STANDARD_*` (11/9/9/8), `ExSET_*` (10/8/4/7), `S01SET_*` (2/1/1/1),
|
||||
`S02SET_*` (3/2/2/1), `PlayerSET_*` (2/1/1/1), `NULL_*` (1 each) — so the
|
||||
`SNNSET_` sets are stage-scoped narrowings of `STANDARD_`.
|
||||
🔑 **`UnitID` is TWO ID spaces at once**: 5 rows name a unit `Generic.ID`
|
||||
(the three `-Katana` rows are the PLAYER — they point at a `_Player` craft and
|
||||
carry an extra, empty `PlayerUnit` field), 8 name a character, resolving as
|
||||
**`Character` + the value** into the 64-record character table.
|
||||
🟡 Two values resolve to nothing, both single rows against 13 that do:
|
||||
`Rhino2-Ellen.UnitID = UN_f001_TCAF_DeltaSaber_W` — **no such unit anywhere**
|
||||
(checked as a `Generic.ID` across every pak AND as a record name, 0 hits)
|
||||
while `UN_f002_TCAF_DeltaSaber_W` exists → consistent with a shipped
|
||||
`f001`/`f002` typo, **reported, not diagnosed**; and `Rhino4-Brandon.UnitID =
|
||||
BRANDON` — no `CharacterBRANDON` among the 64.
|
||||
🟡 Not settled: who SELECTS a loadout record (the names encode flight position
|
||||
and pilot, and `stage-definition-table.md` has `AI_TCAF_RhinoFlight` /
|
||||
`AI_TCAF_BirdFlight`, but the join is not shown); `ExSET_*`/`S02SET_*`/`NULL_*`
|
||||
are referenced by no loadout in this pak; `PlayerUnit` is empty on all three
|
||||
rows that have it; `IGNORE` (13 item names) is a blocklist for something
|
||||
unread.
|
||||
|
||||
* ✅✅ **(2026-08-27) THE 59-OF-131 ARSENAL QUESTION IS CLOSED — an Arsenal item
|
||||
names a HARDPOINT SLOT, not a weapon.**
|
||||
[structures/arsenal-item-weapon-chain](structures/arsenal-item-weapon-chain.md),
|
||||
|
||||
@@ -99,6 +99,7 @@ files, which is how the same ground got covered twice.
|
||||
| [`structures/sound-cue-table.md`](structures/sound-cue-table.md) | The cue index in `tables.pak` — message id -> cue -> sound id -> `.slb` bank | ✅ CONFIRMED, 1 326/1 338 script message ids bind to a bank; SOUNDS and FILES agree on the same 12 absentees, 0 orphan files |
|
||||
| [`structures/cutscene-message-table.md`](structures/cutscene-message-table.md) | Cutscene dialogue — speaker, portrait, on-screen seconds, audio cue per page | ✅ CONFIRMED, field count = 9·PageCount+2 for all 7 PageCounts, 1 252/1 252 caption keys match, 138 ids close both ways |
|
||||
| [`structures/isl-mission-timer.md`](structures/isl-mission-timer.md) | The mission timer — two sequential countdowns, not a limit and a warning | ✅ CONFIRMED — read from `sub_822639B8`; the threshold reading is |
|
||||
| [`structures/hangar-loadout-system.md`](structures/hangar-loadout-system.md) | The Hangar loadout system — loadout → per-slot allow-list → arsenal item | ✅ CONFIRMED — 60/60 slot refs, 70/88 item refs (18 = the `No_Equipment` sentinel) |
|
||||
| [`structures/arsenal-item-weapon-chain.md`](structures/arsenal-item-weapon-chain.md) | The 59-of-131 arsenal question — an item names a hardpoint slot, not a weapon | ✅✅ CONFIRMED — 0/59 `PlayerWeapon` are a `Weapon.ID`, 59/59 are a `Turret_NNN` slot |
|
||||
| [`structures/unit-substructure-records.md`](structures/unit-substructure-records.md) | The destructible-subsystem model — a unit's `Turret`/`Bridge`/`Hatch`/`ShieldGenerator`/`Thruster` sub-records | ✅ CONFIRMED — 835 turrets, 835/835 `WeaponID` resolve, `StructureCount` control 667/684 |
|
||||
| [`structures/unit-datasheet-static.md`](structures/unit-datasheet-static.md) | The static unit datasheet and AI flight model — `Generic`, `Maneuver`, `Effect` | ✅ CONFIRMED — 394 Generic + 114 Maneuver + 114 Effect records read |
|
||||
|
||||
61
docs/re/data/hangar-loadouts.txt
Normal file
61
docs/re/data/hangar-loadouts.txt
Normal file
@@ -0,0 +1,61 @@
|
||||
# The Hangar loadout system
|
||||
# Regenerate: python3 tools/re-capture/hangar_loadouts.py
|
||||
# See docs/re/structures/hangar-loadout-system.md
|
||||
|
||||
## counts
|
||||
arsenal items 59 loadout records 15 per-slot allow-lists 24
|
||||
|
||||
## loadout records
|
||||
Bird1-Sandra S01SET_ARM1 S01SET_ARM2 S01SET_ARM3 S01SET_NOSE UnitID=SANDRA (character)
|
||||
Bird2-Billy S01SET_ARM1 S01SET_ARM2 S01SET_ARM3 S01SET_NOSE UnitID=BILLY (character)
|
||||
Bird3-Antonius S01SET_ARM1 S01SET_ARM2 S01SET_ARM3 S01SET_NOSE UnitID=ANTONIUS (character)
|
||||
Bird4-Carl S01SET_ARM1 S01SET_ARM2 S01SET_ARM3 S01SET_NOSE UnitID=CARL (character)
|
||||
Rhino1-Katana PlayerSET_ARM1 PlayerSET_ARM2 PlayerSET_ARM3 PlayerSET_NOSE UnitID=UN_f002_TCAF_DeltaSaber_W_Player (unit) PlayerUnit
|
||||
Rhino1-Raymond S01SET_ARM1 S01SET_ARM2 S01SET_ARM3 S01SET_NOSE UnitID=RAYMOND (character)
|
||||
Rhino2-Ellen STANDARD_ARM1 STANDARD_ARM2 STANDARD_ARM3 STANDARD_NOSE UnitID=UN_f001_TCAF_DeltaSaber_W (???)
|
||||
Rhino2-Gene STANDARD_ARM1 STANDARD_ARM2 STANDARD_ARM3 STANDARD_NOSE UnitID=GENE (character)
|
||||
Rhino2-Katana PlayerSET_ARM1 PlayerSET_ARM2 PlayerSET_ARM3 PlayerSET_NOSE UnitID=UN_f001_TCAF_DeltaSaber_T_Player (unit) PlayerUnit
|
||||
Rhino3-Ellen S01SET_ARM1 S01SET_ARM2 S01SET_ARM3 S01SET_NOSE UnitID=UN_f001_TCAF_DeltaSaber_T (unit)
|
||||
Rhino3-Gene STANDARD_ARM1 STANDARD_ARM2 STANDARD_ARM3 STANDARD_NOSE UnitID=GENE (character)
|
||||
Rhino3-Katana PlayerSET_ARM1 PlayerSET_ARM2 PlayerSET_ARM3 PlayerSET_NOSE UnitID=UN_f001_TCAF_DeltaSaber_T_Player (unit) PlayerUnit
|
||||
Rhino4-Brandon STANDARD_ARM1 STANDARD_ARM2 STANDARD_ARM3 STANDARD_NOSE UnitID=BRANDON (???)
|
||||
Rhino4-Ellen STANDARD_ARM1 STANDARD_ARM2 STANDARD_ARM3 STANDARD_NOSE UnitID=UN_f001_TCAF_DeltaSaber_T (unit)
|
||||
Rhino4-Yoji STANDARD_ARM1 STANDARD_ARM2 STANDARD_ARM3 STANDARD_NOSE UnitID=YOJI (character)
|
||||
|
||||
## CONTROL loadout.Arm1/2/3/Nose names an ALLOW-LIST record
|
||||
60/60
|
||||
|
||||
## CONTROL allow-list positional entries name an ARSENAL ITEM
|
||||
70/88
|
||||
misses: [('No_Equipment', 18)]
|
||||
every miss is a WEAPONS-roster sentinel with no item record: True
|
||||
|
||||
## CONTROL loadout.UnitID
|
||||
15 loadouts: 5 name a unit Generic.ID, 8 name a character (as 'Character'+value), 2 neither
|
||||
neither: ['BRANDON', 'UN_f001_TCAF_DeltaSaber_W']
|
||||
|
||||
## the allow-lists, in order
|
||||
ExSET_ARM1 Type=Arm1 10: Condor_105AM, Frail_GP37, Pilum_BP, Multi_Pike_BP, Spear_HBP, Needle_L1GP, Buzzard_10AM, Dirt_23_Rocket, Falcon_9AM, No_Equipment
|
||||
ExSET_ARM2 Type=Arm2 8: White_Shark_T53R, Piranha_T53, Hound_SMH, Terrier_SMH, Fire_Arrow_L2GP, Sling_75KG, Hawk_75AM, No_Equipment
|
||||
ExSET_ARM3 Type=Arm3 4: Glaive_120mm_Cannon, Monoceros_Long_Cannon, Tomahawk_a_Rail_Gun, No_Equipment
|
||||
ExSET_NOSE Type=Nose 7: Machiene_Cannon_MG3, Machiene_Cannon_MG1, Broad_Sword_SG1, Saber_LG1, Dagger_BG2, Rapier_BG4H, Stiletto_BG1
|
||||
NULL_ARM1 Type=Arm1 1: No_Equipment
|
||||
NULL_ARM2 Type=Arm2 1: No_Equipment
|
||||
NULL_ARM3 Type=Arm3 1: No_Equipment
|
||||
NULL_NOSE Type=Nose 1: Stiletto_BG1
|
||||
PlayerSET_ARM1 Type=Arm1 2: Falcon_9AM, No_Equipment
|
||||
PlayerSET_ARM2 Type=Arm2 1: No_Equipment
|
||||
PlayerSET_ARM3 Type=Arm3 1: No_Equipment
|
||||
PlayerSET_NOSE Type=Nose 1: Stiletto_BG1
|
||||
S01SET_ARM1 Type=Arm1 2: Falcon_9AM, No_Equipment
|
||||
S01SET_ARM2 Type=Arm2 1: No_Equipment
|
||||
S01SET_ARM3 Type=Arm3 1: No_Equipment
|
||||
S01SET_NOSE Type=Nose 1: Stiletto_BG1
|
||||
S02SET_ARM1 Type=Arm1 3: Buzzard_10AM, Falcon_9AM, No_Equipment
|
||||
S02SET_ARM2 Type=Arm2 2: Terrier_SMH, No_Equipment
|
||||
S02SET_ARM3 Type=Arm3 2: Tomahawk_a_Rail_Gun, No_Equipment
|
||||
S02SET_NOSE Type=Nose 1: Stiletto_BG1
|
||||
STANDARD_ARM1 Type=Arm1 11: Falcon_9AM, Dirt_23_Rocket, Eagle_120AM, Condor_105AM, Buzzard_10AM, Needle_L1GP, Spear_HBP, Multi_Pike_BP, Pilum_BP, Frail_GP37, No_Equipment
|
||||
STANDARD_ARM2 Type=Arm2 9: White_Shark_T53R, Cauldron_50_Rocket, Piranha_T53, Hound_SMH, Terrier_SMH, Fire_Arrow_L2GP, Sling_75KG, Hawk_75AM, No_Equipment
|
||||
STANDARD_ARM3 Type=Arm3 9: Glaive_120mm_Cannon, Monoceros_Long_Cannon, Arbalest_155KG, Tomahawk_a_Rail_Gun, Maelstrom_Bomb, Cluster_Mine_B10, Pointer_SM, Long_Spear_HBP, No_Equipment
|
||||
STANDARD_NOSE Type=Nose 8: Stiletto_BG1, Rapier_BG4H, Dagger_BG2, Saber_LG1, Twin_Sword_SG3II, Broad_Sword_SG1, Machiene_Cannon_MG3, Machiene_Cannon_MG1
|
||||
89
docs/re/structures/hangar-loadout-system.md
Normal file
89
docs/re/structures/hangar-loadout-system.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# ✅ The Hangar loadout system — loadout → per-slot allow-list → arsenal item
|
||||
|
||||
Finishes the Hangar. [The item chain](arsenal-item-weapon-chain.md) resolved an
|
||||
Arsenal item down to a `Weapon` record; this is the layer above it — **who flies
|
||||
with what**, and **what the Hangar is allowed to offer for each slot**.
|
||||
|
||||
Artefact `../data/hangar-loadouts.txt`, regenerator
|
||||
`tools/re-capture/hangar_loadouts.py`.
|
||||
|
||||
`GP_HANGAR_ARSENAL.pak` carries **15 loadout records** — `Bird1-Sandra`,
|
||||
`Bird2-Billy`, `Bird3-Antonius`, `Bird4-Carl`, `Rhino1-Katana`,
|
||||
`Rhino1-Raymond`, `Rhino2-Ellen`, `Rhino2-Gene`, `Rhino2-Katana`,
|
||||
`Rhino3-Ellen`, `Rhino3-Gene`, `Rhino3-Katana`, `Rhino4-Brandon`,
|
||||
`Rhino4-Ellen`, `Rhino4-Yoji` — one per **flight position × pilot**, each with
|
||||
`Arm1`, `Arm2`, `Arm3`, `Nose` and `UnitID`.
|
||||
|
||||
🔑 **Same trap as `PlayerWeapon`, one level up: `Arm1`/`Arm2`/`Arm3`/`Nose` do
|
||||
not name items.** They name a **per-slot ALLOW-LIST record** — one of the 24
|
||||
whose only *named* field is `Type` (the slot kind: `Arm1`/`Arm2`/`Arm3`/`Nose`).
|
||||
The candidate items live in that record's **positional, unnamed fields**, in
|
||||
order. So the full path is four hops:
|
||||
|
||||
```
|
||||
Rhino4-Yoji.Arm1 ──▶ STANDARD_ARM1 (an allow-list, Type=Arm1)
|
||||
──▶ [Falcon_9AM, Condor_105AM, …] (arsenal item names, in order)
|
||||
──▶ item.PlayerWeapon = Turret_NNN (a hardpoint slot)
|
||||
──▶ slot.WeaponID = Weapon.ID
|
||||
```
|
||||
|
||||
## 🧪 The controls
|
||||
|
||||
| | |
|
||||
|---|---:|
|
||||
| `loadout.Arm1/2/3/Nose` names an allow-list record | **60 / 60** |
|
||||
| allow-list positional entries name an arsenal item | **70 / 88** |
|
||||
| — every one of the 18 misses | the single sentinel `No_Equipment` |
|
||||
| `loadout.UnitID` resolves | 5 a unit `Generic.ID`, 8 a character (as `Character`+value), **2 neither** |
|
||||
|
||||
`No_Equipment` is one of the four `WEAPONS`-roster values that have no item
|
||||
record ([the item chain](arsenal-item-weapon-chain.md)), so the residual is the
|
||||
empty-slot sentinel appearing once per list — not a decode failure.
|
||||
|
||||
## The six allow-list sets
|
||||
|
||||
| set | Arm1 | Arm2 | Arm3 | Nose | used by |
|
||||
|---|---:|---:|---:|---:|---|
|
||||
| `STANDARD_*` | 11 | 9 | 9 | 8 | Rhino 2/3/4 wingmen |
|
||||
| `ExSET_*` | 10 | 8 | 4 | 7 | — (not referenced by any loadout here) |
|
||||
| `S01SET_*` | 2 | 1 | 1 | 1 | Bird flight, `Rhino1-Raymond`, `Rhino3-Ellen` |
|
||||
| `S02SET_*` | 3 | 2 | 2 | 1 | — |
|
||||
| `PlayerSET_*` | 2 | 1 | 1 | 1 | the three `…-Katana` rows |
|
||||
| `NULL_*` | 1 | 1 | 1 | 1 | — (`No_Equipment`, `No_Equipment`, `No_Equipment`, `Stiletto_BG1`) |
|
||||
|
||||
So a wingman's weapon choice is drawn from a small, ordered, *per-slot* list,
|
||||
and the `SNNSET_` sets are stage-scoped variants of it — `S01SET_ARM1` offers
|
||||
two Arm1 items, `STANDARD_ARM1` offers eleven.
|
||||
|
||||
## `UnitID` is two ID spaces at once
|
||||
|
||||
* **5 rows name a unit** — `UN_f001_TCAF_DeltaSaber_T`,
|
||||
`…_T_Player`, `…_f002_W_Player`. The three `-Katana` rows are the **player**:
|
||||
they carry an extra (empty) `PlayerUnit` field and point at a `_Player` craft.
|
||||
* **8 rows name a character** — `SANDRA`, `BILLY`, `ANTONIUS`, `CARL`,
|
||||
`RAYMOND`, `GENE` ×2, `YOJI`. None is a `Generic.ID` on its own; all resolve
|
||||
as **`Character` + the value** into the 64-record character table
|
||||
([the `Generic` partition](unit-datasheet-static.md)).
|
||||
|
||||
## 🟡 Two values that resolve to nothing
|
||||
|
||||
* **`Rhino2-Ellen.UnitID = UN_f001_TCAF_DeltaSaber_W`.** No such unit exists —
|
||||
checked as a `Generic.ID` across *every* pak, and as a record name: 0 hits.
|
||||
`UN_f002_TCAF_DeltaSaber_W` does exist, and `Rhino1-Katana` uses
|
||||
`UN_f002_TCAF_DeltaSaber_W_Player`. **Consistent with a shipped `f001`/`f002`
|
||||
typo** — but what the game does when the lookup fails is not shown here, so
|
||||
this is reported, not diagnosed.
|
||||
* **`Rhino4-Brandon.UnitID = BRANDON`** — no `CharacterBRANDON` among the 64.
|
||||
`CharacterKATANA` does exist, and Katana is the player.
|
||||
|
||||
Both are single rows against 13 that resolve, so they are exceptions in the
|
||||
data, not a wrong reading of the field.
|
||||
|
||||
## 🟡 Not settled
|
||||
|
||||
* Who *selects* a loadout record. The names encode flight position and pilot,
|
||||
and [the stage definition table](stage-definition-table.md) has
|
||||
`AI_TCAF_RhinoFlight` / `AI_TCAF_BirdFlight` — the join is not shown here.
|
||||
* `ExSET_*`, `S02SET_*` and `NULL_*` are referenced by no loadout in this pak.
|
||||
* The `PlayerUnit` field is present but empty on all three `-Katana` rows.
|
||||
* `IGNORE` (13 item names) — a blocklist for something unread.
|
||||
100
tools/re-capture/hangar_loadouts.py
Normal file
100
tools/re-capture/hangar_loadouts.py
Normal file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
"""The Hangar loadout system: loadout -> per-slot allow-list -> arsenal item.
|
||||
|
||||
A loadout record's `Arm1`/`Arm2`/`Arm3`/`Nose` do NOT name items. They name a
|
||||
per-slot ALLOW-LIST record whose positional (unnamed) fields are the ordered
|
||||
candidate item names. Regenerates docs/re/data/hangar-loadouts.txt.
|
||||
"""
|
||||
import sys, os, glob, collections
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, HERE)
|
||||
import unitgroup as U
|
||||
from unit_substructures import pak_entries
|
||||
|
||||
ITEMF = {'Dependency', 'MissionObjective', 'Model', 'Package',
|
||||
'PlayerWeapon', 'Points', 'Power', 'Range'}
|
||||
SLOTS = ('Arm1', 'Arm2', 'Arm3', 'Nose')
|
||||
|
||||
def main():
|
||||
ars = glob.glob('/work/sylph_extract/**/GP_HANGAR_ARSENAL.pak', recursive=True)[0]
|
||||
|
||||
unit_ids, char_ids = set(), set()
|
||||
for pk in sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)):
|
||||
for h, b in pak_entries(pk):
|
||||
if b[:4] != b'IDXD': continue
|
||||
try: recs = U.parse(b)
|
||||
except Exception: continue
|
||||
for r in recs:
|
||||
if r['squadron'] != 'Generic': continue
|
||||
n = U.named(r)
|
||||
if 'HP' in n and n.get('ID'): unit_ids.add(n['ID'])
|
||||
elif 'SideID' in n and n.get('ID'): char_ids.add(n['ID'])
|
||||
|
||||
items, loadouts, allow, roster = {}, {}, {}, None
|
||||
for h, b in pak_entries(ars):
|
||||
if b[:4] != b'IDXD': continue
|
||||
try: recs = U.parse(b)
|
||||
except Exception: continue
|
||||
for r in recs:
|
||||
n = U.named(r); nm = r['squadron']
|
||||
if nm == 'WEAPONS' and roster is None: roster = r
|
||||
if ITEMF <= set(n): items.setdefault(nm, r)
|
||||
elif set(SLOTS) <= set(n): loadouts.setdefault(nm, r)
|
||||
elif set(n) == {'Type'}: allow.setdefault(nm, r)
|
||||
|
||||
print("# The Hangar loadout system")
|
||||
print("# Regenerate: python3 tools/re-capture/hangar_loadouts.py")
|
||||
print("# See docs/re/structures/hangar-loadout-system.md")
|
||||
print("\n## counts")
|
||||
print(" arsenal items %3d loadout records %3d per-slot allow-lists %3d"
|
||||
% (len(items), len(loadouts), len(allow)))
|
||||
|
||||
print("\n## loadout records")
|
||||
for nm in sorted(loadouts):
|
||||
n = U.named(loadouts[nm])
|
||||
u = n.get('UnitID', '')
|
||||
kind = 'unit' if u in unit_ids else ('character' if 'Character' + u in char_ids else '???')
|
||||
print(" %-16s %-16s %-16s %-16s %-16s UnitID=%-34s (%s)%s"
|
||||
% (nm, n.get('Arm1'), n.get('Arm2'), n.get('Arm3'), n.get('Nose'), u, kind,
|
||||
' PlayerUnit' if 'PlayerUnit' in n else ''))
|
||||
|
||||
tot = hit = 0
|
||||
for r in loadouts.values():
|
||||
n = U.named(r)
|
||||
for f in SLOTS:
|
||||
tot += 1; hit += n.get(f, '').strip() in allow
|
||||
print("\n## CONTROL loadout.Arm1/2/3/Nose names an ALLOW-LIST record")
|
||||
print(" %d/%d" % (hit, tot))
|
||||
|
||||
ptot = phit = 0
|
||||
miss = collections.Counter()
|
||||
for r in allow.values():
|
||||
for t, n, v in r['fields']:
|
||||
if n is not None: continue
|
||||
ptot += 1
|
||||
if v in items: phit += 1
|
||||
else: miss[v] += 1
|
||||
rset = {v for _, _, v in roster['fields']}
|
||||
print("\n## CONTROL allow-list positional entries name an ARSENAL ITEM")
|
||||
print(" %d/%d" % (phit, ptot))
|
||||
print(" misses: %s" % miss.most_common())
|
||||
print(" every miss is a WEAPONS-roster sentinel with no item record: %s"
|
||||
% (set(miss) <= (rset - set(items))))
|
||||
|
||||
print("\n## CONTROL loadout.UnitID")
|
||||
u = [U.named(r).get('UnitID', '').strip() for r in loadouts.values()]
|
||||
print(" %d loadouts: %d name a unit Generic.ID, %d name a character (as 'Character'+value), %d neither"
|
||||
% (len(u),
|
||||
sum(1 for x in u if x in unit_ids),
|
||||
sum(1 for x in u if 'Character' + x in char_ids),
|
||||
sum(1 for x in u if x not in unit_ids and 'Character' + x not in char_ids)))
|
||||
print(" neither: %s" % sorted({x for x in u if x not in unit_ids and 'Character' + x not in char_ids}))
|
||||
|
||||
print("\n## the allow-lists, in order")
|
||||
for nm in sorted(allow):
|
||||
vs = [v for t, n, v in allow[nm]['fields'] if n is None]
|
||||
print(" %-16s Type=%-5s %2d: %s" % (nm, U.named(allow[nm]).get('Type'), len(vs), ", ".join(vs)))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user