Skip to content

Enemy Randomization

github-actions[bot] edited this page Oct 9, 2026 · 21 revisions

Enemy randomization

Enemy Randomization is now a selectable guarded shared browser/CLI feature; the browser default is ON. Its planner/materializer has bounded Runtime-confirmed evidence rather than exhaustive generated-seed or release coverage. The research and proof chronology below retains its original evidence limits; current option and integration semantics are owned by Web patcher and product behavior.

Ordinary-enemy architecture

stage stream -> 0x80071500 -> 0x800719F0 -> 0x80071B20 -> 0x8002FCDC

The current stage publishes a command-stream pointer at 0x800C11E4. Process 0x70 interprets it. Spawn records supply coordinates, type, quota, and activation mode; the shared constructor resolves a resource pointer and type-relative descriptor before allocating the fighter. Exact record fields are in Data structures and encodings.

Stream registry

Stage/branch Stream
Temple 0x800B2D40
Earth 0x800B2EB8
Water 0x800B2F9C
Wind ordinary (state != 9) 0x800B30E4
Fortress ordinary (state < 8) 0x800B33A8
Prison 0x800B361C
Fire ordinary (state != 5) 0x800B3890
Fire alternate/boss-like (state == 5) 0x800B3A54
Bridge 0x800B3A7C
Fortress alternate (state >= 8) 0x800B3C88

Representative ordinary types include Water 0x1A/0x05, Fire 0x0A/0x09, and Prison 0x15/0x0E/0x11/0x16/0x14.

Constructor resource tables

0x80071B20 indexes the four-byte resource-pointer-slot table at 0x800B14C0; 0x8002FCDC separately indexes descriptor offsets at 0x800B13D0 and stores type at actor +0x78.

Type Resource-pointer slot Descriptor offset Native file
0x01 0x801AE46C 0x22AD8 0x89 Temple monk
0x09 0x801AE578 0x2C084 0x25
0x0A 0x800C2560 0x20FC8 0x20

Changing a type is safe only when the expected resource slot is resident and its supporting behavior/presentation is compatible.

Runtime proofs

The first ordinary Fire type halfword is RAM 0x800B38AA / ROM 0xB44AA. Guarded 0x000A -> 0x0009 succeeded: the replacement spawned and normal Fire continued.

The same record changed to type 0x01 originally froze because Fire left slot 0x801AE46C null. A bounded import proof loaded Temple file 0x89, populated that slot, and replaced Fire's native type-0x0A allocation/file-0x20 path rather than adding past the arena limit. The imported monk rendered, moved, fought, and was killable. The fighter-only proof lacked normal death/despawn presentation. In a later bounded proof, Temple current-stage resource selector 10 was also materialized in Fire; the user confirmed correct death animation and despawn on the tested route.

This establishes cross-stage fighter residency, construction, and one tested terminal route, not arbitrary roster compatibility.

Boss and special exclusions

  • Wind state 9 directly constructs type 0x08 with fixed coordinates and process ID 0x71.
  • Water boss directly constructs type 0x0B with fixed coordinates/process ID.
  • Earth boss 0x802EDF50 uses custom allocation and explicitly stores type 0x19.
  • Fire state 5 uses dedicated one-spawn stream 0x800B3A54, type 0x0C.
  • Fortress alternate state uses 0x800B3C88, types 0x1B/0x1D, with dedicated resources.

These are not members of an ordinary-enemy pool. Boss randomization needs per-encounter work.

Rejected candidates

0x80002DA8/0x80037CD0 are graphics/resource registration; 0x800366B4/0x80036964 and file 0x87 are moving geometry; 0x80064788 is geometry/camera work; 0x8005EF5C is an effects/render constructor; 0x8002EC78/0x8002ECF4 are player construction.

Production requirements

A production planner needs deterministic seed integration, stage/branch classification, allowed-type pools, resource-file import/allocation, arena budgets, constructor and auxiliary presentation dependencies, death/despawn validation, and explicit boss exclusion. The current proof is beta feasibility, not a ready patch module.

Ordinary-enemy compatibility matrix — static pass v01 (2026-09-27)

This pass is Static-confirmed from the supported clean N64 USA Rev. 0 ROM plus the already-established constructor/resource model. No ROM was built and no emulator was run.

The purpose of this matrix is narrower than a final randomizer allow-list. It answers two questions first:

  1. which fighter types actually occur in each main stage's ordinary branch; and
  2. for a foreign type, whether the destination stage already has every required fighter resource resident in the exact constructor slot.

A native/resident cell is not automatically a pairwise runtime-substitution approval. Encounter role, auxiliary presentation, death/despawn behavior, and mixed-roster arena pressure remain separate gates.

Complete ordinary-branch roster

The eight main ordinary streams contain 104 spawn commands and 19 distinct fighter types:

Stage Ordinary-branch spawn records
Temple 0x01 ×3, 0x02 ×5, 0x12 ×1
Earth 0x00 ×8
Water 0x05 ×4, 0x1A ×5
Wind 0x03 ×17
Fortress 0x0D ×1, 0x0E ×6, 0x0F ×7, 0x13 ×1, 0x1C ×1
Prison 0x0E ×2, 0x11 ×1, 0x14 ×3, 0x15 ×8, 0x16 ×6
Fire 0x09 ×5, 0x0A ×7
Bridge 0x14 ×7, 0x17 ×6

These are stream records, not a count of simultaneously live enemies. Quotas, activation modes, and opcode semantics remain attached to the destination encounter and are not part of the type swap.

Singleton entries (0x12 in Temple; 0x0D/0x13/0x1C in Fortress; 0x11 in Prison) must receive an encounter-role audit before they are admitted to a general ordinary pool. Their presence in an ordinary-branch stream is resource-residency evidence, not proof that they are semantically interchangeable with repeated grunts.

All five singleton records use the gated opcode-6 form, each with quota 1:

Stage Type Stream ROM record Gate mask Raw XYZ Activation
Temple 0x12 0xB3A94 0x08 0x1B7F, -0x783, 0 1
Fortress 0x0D 0xB3FBC 0x01 0x97, 0x540, 0 0
Fortress 0x13 0xB3FE8 0x02 -0x145C, 0xA80, 0 0
Fortress 0x1C 0xB4014 0x04 -0x463B, 0xFC0, 0 0
Prison 0x11 0xB436C 0x20 -0x4B00, -0xC0, 0 0

The follow-up static trace resolves the encounter ownership far enough to remove these five records from the general ordinary pool:

  • The clean Test Characters table/name table maps type 0x12 to SCORPION, type 0x11 to UNDEAD SCORP, and Fortress types 0x0D/0x1C/0x13 to ASSASSIN1/ASSASSIN2/ASSASSIN3 respectively. The same table also confirms the ordinary grunt family names used below.
  • Temple overlay file 0xA0 owns a dedicated progression path that ORs 0x08 into 0x802C1140 at VA 0x802ED01C..24 / ROM 0xCA6FC..704. That bit exactly matches the singleton SCORPION record's opcode-6 mask 0x08. This is a dedicated gated Scorpion encounter, not a generic monk slot.
  • Prison overlay file 0x9F has a selector-5 path that conditionally writes 0x20 to 0x802C1140 at VA 0x802EDEA4 / ROM 0xC5CE4 (otherwise it clears the word). That bit exactly matches the singleton UNDEAD SCORP record's mask 0x20. The condition comes from save-backed state 0x8009A8CC; its higher-level gameplay label remains unresolved, but the gate ownership is stage-specific and special.
  • Fortress overlay file 0x9E resolves the three singleton assassins completely. Its three-entry encounter table at VA 0x802F15C4 / ROM 0xC4AC4 selects, by encounter index s4=0,1,2, files/slots for type 0x0D ASSASSIN1, type 0x13 ASSASSIN3, and type 0x1C ASSASSIN2. The manager seeds s5=1 and writes 1 << s4 to 0x802C1140 at VA 0x802F03B4..BC, producing exactly masks 1,2,4.
  • The same Fortress manager computes s4 * 0x30 and directly updates the first three Fortress pickup records at 0x802F1334 + s4*0x30: it copies the defeated actor's X coordinate into record +0x00 and clears bit 0x8000 from record +0x14 at VA 0x802F043C..046C / ROM 0xC393C..C396C. Those records are the already-established Kia, Jataaka, and Sareena defeat-reward locations. Therefore the exact static association is ASSASSIN1 / type 0x0D -> Kia, ASSASSIN3 / type 0x13 -> Jataaka, and ASSASSIN2 / type 0x1C -> Sareena.

These five stream records are mechanically dispatched by the ordinary interpreter, but they are special encounter records embedded in the ordinary-branch streams, not general grunt-pool members. They are excluded from the first enemy-randomization pool.

Static fighter-type names relevant to the ordinary pool

The clean N64 Test Characters records at VA 0x8009AC28 and their adjacent name table provide a direct type/name mapping. Type 0x00 MONK1 is the separately handled Earth entry; the 24 table records then align with names MONK2 through SHINNOK.

For the 19 ordinary-branch types in this matrix:

Type Static name Pool classification
0x00 MONK1 repeated ordinary candidate (Earth)
0x01 MONK2 repeated ordinary candidate (Temple)
0x02 MONK3 repeated ordinary candidate (Temple)
0x03 MONK4 repeated ordinary candidate (Wind)
0x05 MONK6 repeated ordinary candidate (Water)
0x09 HULK MONK repeated ordinary candidate (Fire)
0x0A FAST MONK repeated ordinary candidate (Fire)
0x0D ASSASSIN1 / Kia special singleton — excluded
0x0E GRUNT1 repeated ordinary candidate (Fortress/Prison)
0x0F GRUNT2 repeated ordinary candidate (Fortress)
0x11 UNDEAD SCORP special singleton — excluded
0x12 SCORPION special singleton — excluded
0x13 ASSASSIN3 / Jataaka special singleton — excluded
0x14 PRIS GRUNT1 repeated ordinary candidate (Prison/Bridge)
0x15 PRIS GRUNT2 repeated ordinary candidate (Prison)
0x16 PRIS GRUNT3 repeated ordinary candidate (Prison)
0x17 PRIS GRUNT4 repeated ordinary candidate (Bridge)
0x1A MONK5 repeated ordinary candidate (Water)
0x1C ASSASSIN2 / Sareena special singleton — excluded

“Repeated ordinary candidate” is a static pool-candidate classification, not pairwise runtime approval. Resource residency is native in the listed stage(s), but encounter behavior, death/despawn presentation and every destination-type pairing have not been exhaustively runtime-tested.

Constructor resource bundles

The constructor's type-indexed slot and descriptor tables, combined with the normal/test-character load records, resolve the following bundles. Type 0x00, which is outside that debug record block, is independently resolved by the Earth load path: file 0x88 is loaded into slot 0x801AE470.

Type Native ordinary branch(es) Resource-pointer slot Descriptor offset Primary file Secondary dependency Full raw bundle
0x00 Earth 0x801AE470 0x2239C 0x88 / 0x225B0 — 0x225B0 (137.4 KiB)
0x01 Temple 0x801AE46C 0x22AD8 0x89 / 0x22BF0 — 0x22BF0 (139.0 KiB)
0x02 Temple 0x800C11B8 0xB3F8 0x8A / 0xB510 0x89 → 0x801AE46C 0x2E100 (184.2 KiB)
0x03 Wind 0x802E7DF0 0x14DCC 0x8B / 0x14EE0 — 0x14EE0 (83.7 KiB)
0x05 Water 0x802E7268 0xA264 0x8D / 0xA370 0x8C → 0x801114C0 0x284D0 (161.2 KiB)
0x09 Fire 0x801AE578 0x2C084 0x25 / 0x2C210 — 0x2C210 (176.5 KiB)
0x0A Fire 0x800C2560 0x20FC8 0x20 / 0x21160 — 0x21160 (132.3 KiB)
0x0D Fortress 0x800C25B8 0x33EDC 0x1F / 0x34030 — 0x34030 (208.0 KiB)
0x0E Fortress, Prison 0x802E7DC4 0x25454 0x22 / 0x255E0 — 0x255E0 (149.5 KiB)
0x0F Fortress 0x802E83E4 0x1C6AC 0x23 / 0x1C7C0 0x22 → 0x802E7DC4 0x41DA0 (263.4 KiB)
0x11 Prison 0x802C1998 0x3F92C 0x95 / 0x3FB10 — 0x3FB10 (254.8 KiB)
0x12 Temple 0x802E6E48 0x458CC 0x87 / 0x459E0 — 0x459E0 (278.5 KiB)
0x13 Fortress 0x800C255C 0x3AE00 0x1D / 0x3AFD0 — 0x3AFD0 (236.0 KiB)
0x14 Prison, Bridge 0x80111528 0x1FA7C 0x8E / 0x1FC10 — 0x1FC10 (127.0 KiB)
0x15 Prison 0x80112008 0x1B7DC 0x8F / 0x1B910 0x8E → 0x80111528 0x3B520 (237.3 KiB)
0x16 Prison 0x802C0FA0 0x1C418 0x90 / 0x1C550 0x8E → 0x80111528 0x3C160 (240.3 KiB)
0x17 Bridge 0x80111ED0 0x1306C 0x91 / 0x131A0 0x8E → 0x80111528 0x32DB0 (203.4 KiB)
0x1A Water 0x801114C0 0x1E050 0x8C / 0x1E160 — 0x1E160 (120.3 KiB)
0x1C Fortress 0x801AE714 0x372F8 0x1C / 0x37400 — 0x37400 (221.0 KiB)

The secondary dependencies explain the stock paired families already visible in the streams: 0x02 -> 0x01's file 0x89, 0x05 -> 0x1A's file 0x8C, 0x0F -> 0x0E's file 0x22, and 0x15/0x16/0x17 -> 0x14's file 0x8E.

Stage × type residency matrix

Legend:

  • N — native in the destination ordinary branch; exact constructor resource slot(s) are stock-resident there.
  • P — cross-stage import is Runtime-confirmed for construction/combat and, after separate selector-10 materialization, normal death/despawn on the tested route. Other routes remain untested.
  • I — at least one required resource/slot is absent from the destination's native ordinary bundle; explicit import/materialization is required and compatibility is still Pending.
Stage 00 01 02 03 05 09 0A 0D 0E 0F 11 12 13 14 15 16 17 1A 1C
Temple I N N I I I I I I I I N I I I I I I I
Earth N I I I I I I I I I I I I I I I I I I
Water I I I I N I I I I I I I I I I I I N I
Wind I I I N I I I I I I I I I I I I I I I
Fortress I I I I I I I N N N I I N I I I I I N
Prison I I I I I I I I N I N I I N N N I I I
Fire I P I I I N N I I I I I I I I I I I I
Bridge I I I I I I I I I I I I I N I I N I I

There are no non-native N cells in this first pass: every foreign type is missing at least one exact resource-file/slot pair in the destination's native ordinary bundle. The only cross-stage exception with runtime evidence is Fire × 0x01: file 0x89 at 0x801AE46C proved construction/combat, and a subsequent proof also materializing Temple stage-resource selector 10 proved normal death/despawn on the tested route.

Immediate planner consequence

A production enemy planner cannot treat the current arena tail as a generic additive fighter cache. The smallest foreign raw bundle in this matrix is type 0x03 at 0x14EE0 (83.7 KiB), while the current 16 KiB-reservation profiler's strongest global bounded headroom observation is only 0x75A8 (29.41 KiB). The historical Fire proof already demonstrated the same failure mode concretely: appending type-0x01 file 0x89 crossed the arena boundary by 0x3098.

Therefore the production compatibility problem splits cleanly into:

  1. same-stage/native-resource substitutions — no new fighter-file allocation;
  2. cross-stage imports — require replacement, compact/rebased storage, deduplicated composition, or an independently proven load/unload policy; simple additive full-file loading is not a production-safe general strategy;
  3. lifecycle compatibility — death/despawn and auxiliary presentation must still be proven even after construction/resource residency succeeds.

The recently reclaimed high-ROM space from compact Rainbow helps ROM packaging, but it does not change this RDRAM arena constraint.

Excluded special routes

This matrix intentionally excludes the existing special/boss paths: auxiliary hardcoded type 0x07, Wind boss type 0x08, Water boss type 0x0B, Fire state-5 type 0x0C, Earth custom boss type 0x19, and Fortress alternate types 0x1B/0x1D. Those remain per-encounter work and are not ordinary-pool candidates.

Conservative first ordinary pool

With the singleton trace resolved, the static first-pool candidates are:

  • Temple: 0x01 MONK2, 0x02 MONK3
  • Earth: 0x00 MONK1
  • Water: 0x05 MONK6, 0x1A MONK5
  • Wind: 0x03 MONK4
  • Fortress: 0x0E GRUNT1, 0x0F GRUNT2
  • Prison: 0x0E GRUNT1, 0x14 PRIS GRUNT1, 0x15 PRIS GRUNT2, 0x16 PRIS GRUNT3
  • Fire: 0x09 HULK MONK, 0x0A FAST MONK
  • Bridge: 0x14 PRIS GRUNT1, 0x17 PRIS GRUNT4

This is the smallest conservative same-stage roster because every listed type is repeated in its native ordinary branch and its complete constructor resource bundle is already resident there. It does not yet authorize arbitrary pairwise substitution; Fire 0x0A -> 0x09 remains the only direct type-swap Runtime confirmation.

Next matrix pass

The next gate is a per-type terminal-presentation and callback trace, followed by bounded composition proofs. Do not generalize the Fire × MONK2 result to other types or assume the stage-local selector number itself is portable.

Auxiliary presentation pass — bounded evidence (2026-09-27)

The user reports Runtime-confirmed, bounded Fire × MONK2 normal death animation/despawn after materializing Temple current-stage selector 10 alongside fighter file 0x89 in slot 0x801AE46C. The earlier fighter-only proof is its negative control. The exact new proof file, selector repointing, and route hash were not supplied with this report.

The clean-ROM stage files establish an asset relationship. Temple resource file (ROM 0x513710, relative 0xADF4..0xCD5F) and Water resource file (ROM 0x611780, relative 0x4508..0x6473) each have a six-record selector-10 list. Their six contiguous compressed image payloads (0x1ED8 bytes) are byte-identical; the preceding 0x94 bytes of list/record pointers differ and must be rebased. The full embedded closure spans 0x1F6C bytes before destination placement. Wind selector 10 has six external IDs 0x37A..0x37F with matching dimensions/anchors; pixel identity and destination cache availability remain unproved. Earth selector 10 aliases other selectors, Prison selector 10 is a different eight-frame bundle, and Fire/Bridge selector 10 are empty. Thus selector number 10 is not a globally portable death resource. Temple/Water payloads can be deduplicated byte for byte if both are needed in one destination and their relative pointers are rebuilt. Identical data alone does not prove a fighter's terminal callback consumes it.

The table records confirmed dependencies and distinguishes candidate assets from traced callbacks. A dash means unresolved, not no auxiliary dependency. Constructor slots, descriptor offsets, and full file sizes remain in the matrix above.

Fighter type Native ordinary stage Fighter bundle Terminal-presentation dependency Shared/family/stage-specific Evidence
00 MONK1 Earth 88 —; Earth selector 10 differs unresolved Static stage table; callback pending
01 MONK2 Temple 89 Temple selector 10 six-frame closure in tested Fire import monk-family candidate, stage-local selector Runtime-confirmed bounded Fire route; static Temple/Water payload
02 MONK3 Temple 8A + 89 —; Temple selector 10 native monk-family candidate Static residency; callback pending
03 MONK4 Wind 8B —; Wind selector 10 six external records monk-family candidate, cache-dependent Static form/geometry; callback/pixels pending
05 MONK6 Water 8D + 8C Water selector 10 on the normal terminal branch stage-local selector, same payload as Temple Static terminal dispatcher 0x80056F80..0x80057060 / 0x80053DA8; runtime pending
09 HULK MONK Fire 25 —; Fire selector 10 empty Fire native route Static stage table; callback pending
0A FAST MONK Fire 20 —; Fire selector 10 empty Fire native route Static stage table; callback pending
0E GRUNT1 Fortress, Prison 22 File 22 palette source +0x2555C, animation root 0x32 / six frames inside 22 on the normal terminal branch fighter-specific, no selector 10 on this branch Static 0x80053E8C; runtime pending
0F GRUNT2 Fortress 23 + 22 In Water: resident Water selector 10 on the normal terminal branch; native Fortress stage-ID-9 branch differs stage-specific dispatcher; Water shares monk terminal payload Static 0x80056F80..0x80057060 / 0x80053DA8; runtime pending
14 PRIS GRUNT1 Prison, Bridge 8E —; Bridge selector 10 empty family/stage-specific unresolved Static dual-stage residency; callback pending
15 PRIS GRUNT2 Prison 8F + 8E — prison-grunt family candidate Static bundle; callback pending
16 PRIS GRUNT3 Prison 90 + 8E — prison-grunt family candidate Static bundle; callback pending
17 PRIS GRUNT4 Bridge 91 + 8E —; Bridge selector 10 empty prison-grunt family candidate Static bundle; callback pending
1A MONK5 Water 8C Water selector 10 on the normal terminal branch stage-local selector, same payload as Temple Static terminal dispatcher 0x80056F80..0x80057060 / 0x80053DA8; runtime pending

Water MONK5/MONK6 → GRUNT1/GRUNT2 sizing. Native Water files 8C (0x1E160) and 8D (0xA370) total 0x284D0. Replacement files 22 (0x255E0) and 23 (0x1C7C0) total 0x41DA0: net 0x198D0 (104,656 bytes) after removing both native fighters, before auxiliary presentation and allocator overhead. The Water overlay loader at ROM 0xB58B4..0xB58F8 normally allocates/loads 8C into 0x801114C0, then 8D into 0x802E7268; the grunt constructor expects 22 at 0x802E7DC4 and 23 at 0x802E83E4. Both new files and slots must be composed together. The archived global 0x75A8 headroom observation supplied an earlier conservative 0x2FA78 pair ceiling; the subsequently supplied Water-specific peak 0x80284940 raises the observed-route pair ceiling to 0x34520 (details below). The normal terminal branches need no new stage resource in Water (details below); other death variants and different compositions remain unbounded. The complete raw-pair replacement is unsupported by either bound.

A candidate compact composition replaces the two Water fighter allocations as a pair, repacks/rebases the reachable 22/23 content and all terminal frames, and materializes any auxiliary closure found by callback tracing. Raw Type-5 image chunks differ in their file-relative pointers; a later normalized comparison identified one six-frame duplicate set, detailed below. Even sharing those frames and their model-1 pattern block is insufficient for the Water-observed ceiling. No safe minimum compact size is established yet. Resolve reachable animation/AI/death roots, base-file references, other readers of the Water native slots, composition-specific peak validity, and relocation closure before choosing a packed footprint or preparing a ROM.

Water pair: normal terminal path and conservative footprint (2026-09-28, static only)

The permanent terminal dispatcher at 0x80056F80..0x80057060 examines actor +0x78 and, outside native stage IDs 7/9, sends 0x05 MONK6, 0x1A MONK5, and 0x0F GRUNT2 through 0x80053DA8. On Water (stage ID 2), that routine replaces actor +0x98 with the current stage resource base from 0x802E82B8, sets its animation cursor to base + *(base + 0x28) (outer selector 10), runs the six-frame sequence, decrements the encounter count, releases the actor, and exits the process. Water file 0x75 already has selector 10 at relative 0x4508 and its entire 0x1F6C embedded list/image closure at 0x4508..0x6473. Therefore this normal terminal branch of imported GRUNT2 requires no additional stage-resource allocation in Water: it can consume the same resident selector-10 payload as both native Water monks. This is a static dispatch/data proof, not a Water × GRUNT2 runtime result or an all-death-variants claim.

Type 0x0E GRUNT1 takes the other dispatcher branch, 0x80053E8C. It acquires the type-indexed source at 0x800B1538 + 0x0E*4 = 0x2555C relative to file 0x22, selects fighter animation index 0x32 through 0x8002FE54, then performs the same count/release/process exit. File 0x22 root word +0xC8 points to script +0x1D0: six frame descriptors +0x24DFC, +0x24F30, +0x25060, +0x25180, +0x252A8, +0x25394, followed by zero. The palette source and all six descriptors/images remain inside file 0x22 (end +0x255E0). This normal branch does not read Water selector 10. GRUNT2 has a separate reachable type-morph route at 0x8005765C..0x800577BC: it replaces actor +0x98 with the file-0x22 slot 0x802E7DC4, stores type 0x0E, and selects root 0; thus 0x22 must coexist with 0x23 even when both ordinary types are imported together. Other hit/death variants and all fighter-script roots still need a complete liveness audit before any frame can be deleted.

The stock files contain 177 valid Type-5 images in 0x22 and 115 in 0x23, each referring to its file's own two-model table (+0x740 and +0x474). The second model's 3,000-byte pattern block is byte-identical in the two files (500 six-byte patterns, in the same order). A rebased combined representation can store that block once while retaining each file's distinct first model, table metadata, script roots, palettes, and image streams; all affected model-relative and image-relative offsets must be regenerated. This is a static deduplication candidate, not a validated native loader format. A direct scan finds one image in 0x22 and six in 0x23 without a direct 32-bit descriptor reference before the image area (332 and 1,624 bytes respectively); computed references/other consumers remain unruled out, so none of these bytes count as safely removable yet.

For a two-allocation replacement, native 8C+8D use 0x284D0 payload bytes and 22+23 use 0x41DA0; all four sizes are eight-byte aligned, and the allocator's two eight-byte bookkeeping charges cancel. Water's normal setup loads resource file 0x75 at 0x800B47C0..0x800B47E0 before the fighter loads at 0x800B4CB4..0x800B4CF8. In the historical Water capture where file 0x75 began at 0x802504A8, even ignoring every other allocation, two later fighter payloads must end at or beyond 0x802504A8 + 0x64E0 + 0x10 + payload_sum. The raw grunt pair's resulting lower cursor bound is 0x80298738, 0x7DA8 above 0x80290990. This is a useful Water-qualified lower bound, not the exact peak of the present production configuration; changes in the stage-file base/route require recalculation.

Let P_W be Water's peak cursor on the same profiler composition and route. With unchanged later allocations and no new auxiliary allocation, the payload ceiling is 0x284D0 + (0x80290990 - P_W). The archived all-stage profiler's global peak 0x802893E8 supplied an earlier conservative 0x2FA78 ceiling, but was not a Water measurement. The user subsequently supplied Water's observed peak P_W = 0x80284940; its 0xC050 headroom yields the Water-observed 0x34520 ceiling. The raw pair exceeds this by 0xD880; the conditional 0x1210 duplicate saving leaves a 0x40B90 pair, still 0xC670 (50,800 bytes) too large, before rebasing metadata. The Water peak is a user-supplied runtime observation, not a new run in this static trace or a current-production worst-case guarantee. A minimum reachable compact closure remains unproved.

GRUNT1/GRUNT2 reachable-closure follow-up (2026-09-28, Static-confirmed; compaction Pending)

Exact required source islands on the normal Water routes. File 22 root slot +0xC8 (primary index 0x32) leads to the seven-word list [+0x1D0,+0x1EC): six pointers to frame records +0x24DFC, +0x24F30, +0x25060, +0x25180, +0x252A8, +0x25394, then zero. The six aligned descriptor/image extents form [+0x24DFC,+0x25454) (0x658 bytes), and each image chooses model 1 in the table at +0x740. The terminal palette source is [+0x2555C,+0x255E0) (0x84 bytes: count word and 64 halfword colors). Constructor palette/descriptor records also begin at file 22 +0x25454 and file 23 +0x1C6AC; both must remain accessible through the type-indexed tables. GRUNT2 normal terminal presentation in Water uses existing stage-file 75 selector 10, not file 23's terminal frames. Its 0x000F type-morph branch at 0x80057740..0x800577BC replaces actor +0x98 with the contents of slot 0x802E7DC4 (file 22), changes actor type to 0x0E, and selects root 0. The ordinary GRUNT2 route therefore requires file 22 resident concurrently with file 23, even if the two pair members share stored subresources.

One exact duplicate family, conditional on pointer rebasing. In file 22, model-1 pattern bytes [+0xF14E,+0xFD06) (0xBB8) equal file 23 [+0xBFFC,+0xCBB4) byte for byte. Both model-1 records have identical remaining 100 bytes; their four-byte pattern offsets differ. File 23's six terminal-looking descriptors/images [+0x1C054,+0x1C6AC) (0x658) match file 22 [+0x24DFC,+0x25454) in order, geometry, compressed bitstreams, decoded pixels, and alignment padding. Each descriptor and image carries different file-relative pointers, explaining why raw whole-chunk equality missed the match. All six file-23 images use model 1; file 22's six listed terminal images are likewise its only model-1 images. A single rebuilt dictionary and single six-frame body could save at most 0xBB8 + 0x658 = 0x1210 compared with both raw files if every inbound pointer and any computed entry are safely rebased. Separate model-0 dictionaries, model tables, root tables and palettes are not thereby shared. The near-matching constructor palettes at 22+0x25454 and 23+0x1C6AC differ at their last byte and are not byte-identical resources.

Precise unresolved indirect dependency (blocks an exact live-range map). 0x8002FE54 computes actor+0x98 + 4*animation_index (or +0x104 + 4*animation_index for secondary roots) and uses the loaded file-relative offset as an animation cursor. At 0x800321D0 / 0x80032580, it is the sixth caller stack argument, not the fifth, that becomes $s4 and selects/skips the root. The fifth argument controls rate; the earlier attribution of 0x8004E260's 4 and 0x80053D6C's 5 to fighter roots was wrong: both sites supply root 0x1E as their sixth argument. The interpreter at 0x800304C0 follows file-relative script jumps at 0x8003058C..0x800305A4 and conditional type-selected jumps at 0x80030710..0x8003074C; its ordinary frame reader 0x8001BDA0 makes a second relative dereference from each selected frame record. 0x8002E978 similarly takes the sixth argument for its type-specific record index at 0x800AF340 + type*4 (GRUNT1 -> 0x800B0430, GRUNT2 -> 0x800B0508); $a3 controls a separate animation choice. In particular, the six duplicates in 23 lack direct earlier-word references, and 22+0x18B3C also lacks one; this is no proof that a computed pointer cannot reach them. No animation, model-0 dictionary, palette or image range is certified dead.

Consequently the conditional duplicated-data-only pair would be 0x41DA0 - 0x1210 = 0x40B90 before metadata and alignment, still 0xC670 (50,800 bytes) over the Water-observed 0x34520 ceiling. This figure is not the smallest reachable representation or a safe patch footprint. An exact compact pair remains Pending until the specific shared reaction/action roots and computed script-entry paths are closed. No ROM build or emulator run was performed for this follow-up.

Water GRUNT action and reaction correction (2026-09-28, static only)

The clean Rev. 0 ROM (SHA-256 9c18254abf6722b95aa782fcd310bd95f6bcf147da66beb77ce32ca90673ffc6) and preserved permanent-image function inventory were read without modifying the ROM. The argument correction above follows the actual MIPS stack offsets after each callee prologue, including call delay slots.

Action 0xE/0xF ownership. 0x8004D280 supplies sixth argument 0xE to 0x8002E978 at 0x8004D2C4; 0x8004D3A8 supplies 0xF at 0x8004D420. Their ordinary calls are 0x8002A510 and 0x8002A0A4 inside the class-1 player-controller dispatch 0x80028F3C, whose actor is its controller +0x6E0. The other permanent calls at 0x80006634 / 0x80006588 enter through special controller setups, not the 0x80071500 -> 0x80071B20 ordinary-enemy constructor. Thus these two sites cannot be reached by an ordinary type 0x0E/0x0F actor on the stock Water spawn route. This rejects 0xE/0xF as grunt action requirements from these callers; it does not certify every other action index or an artificial actor-type override on a player process. The ten direct permanent 0x8002E978 sites include 0xE/0xF, while 0x80033880 passes 0/1; call-site constants alone are not a grunt reachability set.

Shared reaction call-site inventory. The 77 direct permanent-image calls to 0x800321D0 divide by their sixth argument: 32 pass -1 and skip root selection at 0x80032270; 37 pass root 0x1E; and eight singleton sites pass 0 (0x800252DC), 0x16 (0x8002AF4C), 2 (0x8002B538), 0x1A (0x80041180), 0x41 (0x80041B4C), 0x31 (0x80051000), 0x4A (0x80058A18), or 0x2E (0x80058F74). 0x80069968 reuses $s0=-1 from 0x80069850 and belongs to the first group. These are syntactic call-site classifications, not ordinary-GRUNT reachability claims. The fifth argument is a rate, not a root: e.g. 0x8004E260 passes fifth 4, sixth 0x1E; 0x80053D6C passes fifth 5, sixth 0x1E. 0x80032580 has no direct permanent-image call; the previously identified raw-overlay 0xA2 call (stage-ID-1 path, ROM 0xD45A8) has fifth 2, but its sixth argument and Water reachability must not be inferred from that value. Thirteen raw-overlay calls to 0x800321D0 also remain outside this permanent-image inventory.

Root-alias normalization. Clean file-table entries 0x22 and 0x23 at ROM 0xA51A8 / 0xA51B4 identify uncompressed [0x20F900,0x234EE0) and [0x234EE0,0x2516A0). Primary root words are file-relative; identical offsets denote aliases, not independent script storage. Both files alias 0=0x27, 1=0x28, and 9=0xA=0xE. File 0x22 additionally aliases 0xD=0x24, 0x11..0x16, 0x17=0x19, and 0x1E=0x2A; file 0x23 aliases 0xC=0x10, 0x13..0x15, and 0x16..0x1C. File 0x22 root 0x32 points to the proven terminal list +0x1D0; the file 0x23 word at index 0x1E is +0x474, its model table, not a certified playable script. Therefore the 37 shared root-0x1E calls cannot simply be assigned to GRUNT2. File 0x22 root 0 begins at +0x2A8 with frame-record offsets +0x1DA38,+0x1DCA0,+0x1DF10; file 0x23 root 0 begins at +0x188 with +0x18130,+0x183AC,+0x18644. File 0x22 root 0x1E begins at +0x618 with +0x19EC8,+0x1A0EC,+0x1A2F8. These initial words establish real two-level frame/image reads via 0x800304C0 -> 0x8001BDA0; they do not close the scripts after control tokens and conditional jumps.

0x800328FC → 0x80053D28 process-state trace (2026-09-28, static only)

Construction and writers. The ordinary spawn path 0x80071500 → 0x800719F0 → 0x80071B20 creates a 0x71..0x74 process, normalizes its class to 2..5 (addiu -0x6F at 0x80071B64; the earlier 0x6F..0x72 result was an arithmetic error), and zero-fills process+0x638..+0x6D7 with 0x80070710 before actor construction and transfer to 0x800039AC. This includes +0x6B2; the more general freelist constructor 0x8002830C does not itself clear that field. A clean-ROM permanent-image halfword-store census at exact offset 0x6B2 found 35 direct sites. Current-process action/physics writers are 0x8002663C/26874/26AD0, 0x80032210/32318/3254C, 0x800325BC/32704, 0x80033BD4/33C48/33C80, 0x80034134/341C8/342FC, 0x80036670, 0x80040B4C, 0x8004285C/42970, 0x800435B8/43698, 0x80044450, 0x8004872C, 0x800488FC, 0x80048E44, and 0x80059164/591DC. The other direct sites address a specified player controller or linked process: 0x800182BC, 0x80037618 (current+0x644), 0x80038640, 0x8003D180/3D47C/3D694, 0x80041890, 0x8005BB54 (current+0x640), and 0x80069BE8. This is a direct-store inventory, not proof that every generic callback is an ordinary-GRUNT action. The Water raw overlay is file 0xA1, ROM [0xB4FC0,0xBAF60) (its fighter loader is at ROM 0xB58B4); it has zero direct calls to 0x800321D0 and zero direct halfword stores to +0x6B2. The 13 identified raw-overlay 0x800321D0 calls instead split across Earth 0x9C (4), Prison 0x9F (2), Temple 0xA0 (3), and stage-ID-1 0xA2 (4). File 0x9B, Fire 0x9D, and Fortress 0x9E also have none. These are raw-file assignments, not imports into Water.

Live flag and transfer. 0x800321D0 explicitly clears +0x6B2 at entry (0x80032210), increments it before the one-frame sleep at 0x80032318, and clears it on return (0x8003254C). 0x80033BB4 and 0x800341AC also set it to one across timed loops and clear it on ordinary exit. 0x800341AC's direct 0x800328FC edge at 0x800342B4 follows a call to 0x800321D0 (which clears the flag), so that edge alone is not a nonzero-state proof. 0x8002B690 returns 0x8000 iff the current process +0x6B2 is nonzero; 0x800328FC then calls 0x80053D28 at 0x80032920. Unblocked collision resolves the high byte of strike record +0x08 through reaction table 0x800A1190 at 0x8002DF10..28; 0x8002E078 → 0x80032CD4 replaces the victim's callback and marks reaction bit +0x6BE:0x04 without clearing +0x6B2. Reaction-table entry 0 is 0x8004E530, which calls 0x800328FC at 0x8004E68C. The separate callback 0x80059070 occupies reaction indices 0x3A/0x5A and directly calls 0x80053D28 at 0x800591E0, after clearing its own +0x6B2; its strike-selector availability to ordinary Water play is not established.

The raw-overlay direct sh +0x6B2 census adds 22 sites outside Water: Earth file 0x9C ROM 0xDCDF8,0xDDD50,0xDEC30,0xDEEA4,0xDF1B0,0xDF380,0xDF8EC,0xE0468; Temple 0xA0 ROM 0xCB668,0xCB94C,0xCB9A0; stage-ID-1 0xA2 ROM 0xD51F8,0xD5B64,0xD6A68,0xD6B88,0xD6B8C,0xD6BEC,0xD6BF4,0xD7408,0xD79EC,0xD7A38,0xD7EF0. The other stage-overlay files 0x9B/0x9D/0x9E/0x9F/0xA1 have no direct sh +0x6B2 site. Raw opcode matches in other overlays do not establish an ordinary Water process writer.

Morph and root. 0x80053D28 first calls 0x80057478; for current type 0x0F, that helper rebinds actor +0x98 to file 0x22, changes type to 0x0E, and selects root 0 before returning. The subsequent 0x800321D0 call has fifth argument 5 (rate) and sixth 0x1E (primary root). On file 0x22, root 0x1E aliases root 0x2A at script +0x618; its first three frame-record offsets are +0x19EC8,+0x1A0EC,+0x1A2F8. The earlier file-0x23 word 0x1E=+0x474 is a model table, but it is not the root used by this callback after the 0x0F morph. The focused root-0x1E script/frame closure below supersedes this remaining script-specific task; other roots and computed consumers remain unclosed.

Prior stopping dependency, narrowed below. Callback replacement can preserve victim +0x6B2, but a normal return from 0x800321D0 clears it. The specific 0x800328FC → 0x80053D28 handoff still needs an eligible second strike and callback schedule during the nonzero interval. The independent Slide reaction below already certifies file-0x22 root 0x1E as live; the handoff is no longer its sole liveness route. No further dead bytes follow from the collision trace.

Ordinary GRUNT collision and root-0x1E closure (2026-09-28, static only)

Victim ownership and gates. The 0x8002BADC collision core enters its ordinary-victim scan at 0x8002CAE4..0x8002CB84 after the player-target check. 0x80028510 finds a process whose +0x6D8 class is 2, 3, or 4 (excluding the current attacker); ordinary spawn slots 0x71..0x73 become exactly those classes. Slot 0x74 becomes class 5 and is not selected by this three-slot scan. The victim is $fp, the attacker is the current 0x802ECE20 process, and global 0x802C1AC0 is the player controller. The code requires victim action +0x6AA other than 0x502/0x619, nonzero health +0x654, actor +0x64:0x100 clear, facing and hit geometry, and a zero result from 0x80025370's obstacle/zone check. Special cases at 0x8002DBB8..0x8002DCA0 concern other actor types/actions and state 0x50B only when the player is action 0x215; 0x8002DE48..0x8002DF0C excludes types 0x1D, 8, and 0x0C, not 0x0E/0x0F. 0x80034964 sends guard states 0x700/0x701 to the blocked table 0x800A1290 when the guard global is clear. An ordinary alive, facing, unguarded GRUNT in a valid hitbox takes the unblocked 0x800A1190 table. Neither the tested victim gates nor 0x80025370 reads +0x6B2 or reaction bit +0x6BE:0x04. At 0x8002DF28, 0x8002E078 → 0x80032CD4 installs the selected callback and resets victim stack pointers to victim+0x620, leaving its +0x6B2 intact. The next scheduler invocation of that process runs the replacement callback with the victim as current process. This is the mechanical handoff, not proof that every strike can overlap a nonzero interval.

Concrete live reaction. Normal type-4 player Slide is reached through its stock action callback 0x80059FF0 (recognized at 0x8003C9F8 → 0x8003CFAC, tier at least 2). At 0x8005A0B8 it passes type-4 strike-table slot 0x10, 0x800AF614, to 0x8002BA04; that record's +0x08 high byte is 0x2D. On an unblocked hit against ordinary class-2..4 type 0x0E/0x0F, 0x800A1190[0x2D]=0x8004E2D4. This callback calls 0x80057478, so type 0x0F morphs to 0x0E and file 0x22 before 0x800321D0 receives sixth argument root 0x1E at 0x8004E3CC. It sets victim action +0x6AA=0x50B, but does not set actor +0x64:0x100; 0x800321D0 clears then increments that victim +0x6B2 immediately before its sleep. Thus root 0x1E is an ordinary Water GRUNT requirement independently of 0x80053D28. The normal player type-4 record at slot 0, 0x800AF504, separately has reaction byte 0: 0x800A1190[0]=0x8004E530; table index 0x19 is the same callback. Slide repeatedly presents slot 0x10, so its own later hits do not select entry 0.

Exact root-0x1E file-0x22 reads. Root slots 0x1E ([+0x78,+0x7C)) and alias 0x2A ([+0xA8,+0xAC)) both contain +0x618. The script [+0x618,+0x638) holds seven frame-relative offsets followed by zero; it contains no control token, relative jump, or type branch. 0x800304C0 reads each offset, 0x8001BDA0 reads its frame record's second relative image offset at frame+8, and then the Type-5 image's relative model-table offset. All seven images resolve model 0 in file 0x22 table +0x740 (table/header [+0x740,+0x814) and stock model-0 pattern region beginning +0x814; model 1 begins +0xF14E). Exact four-byte-aligned frame/descriptor/image extents, from the stock Type-5 bitstream decoder, are:

Script word Frame and image extent in file 0x22 Image start Model
+0x618 [+0x19EC8,+0x1A0EC) +0x19EDC 0
+0x61C [+0x1A0EC,+0x1A2F8) +0x1A100 0
+0x620 [+0x1A2F8,+0x1A4DC) +0x1A30C 0
+0x624 [+0x1A4DC,+0x1A670) +0x1A4F0 0
+0x628 [+0x1A670,+0x1A7E0) +0x1A684 0
+0x62C [+0x1A7E0,+0x1A968) +0x1A7F4 0
+0x630 [+0x1678C,+0x16910) +0x167A0 0

The six consecutive records total 0xAA0, the seventh 0x184, and the script 0x20. The decoder consumed 1,165 distinct six-byte model-0 patterns (0x1B4E referenced bytes) scattered across the stock model-0 dictionary, with index span 0..9925; those references cannot be dropped merely because frame bytes are retained. 0x8001BDA0 uses the actor's already acquired palette selector +0x9E → +0x80; this root has no separate palette-acquisition token. Constructor palette ownership, especially after a GRUNT2 morph, remains a separate existing resource requirement. The stock extents are certified live, not a proposed packed size or a certificate that unused dictionary entries can be deleted.

Remaining 0x800328FC edge. A Slide hit can start 0x8004E2D4 and produce a hittable nonzero victim interval (+0x6AA=0x50B, actor +0x64:0x100 clear). A subsequent eligible type-4 strike slot 0 would install 0x8004E530; that callback has sleeps but no flag clear before 0x8004E68C → 0x800328FC, which would take 0x80053D28 if the inherited victim flag remained nonzero. Static code does not yet establish an ordinary Water schedule that overlaps slot 0 with the Slide-produced interval: the same player's Slide callback continues to use slot 0x10, and normal 0x800321D0 completion clears the flag. An independent ordinary-GRUNT AI/action writer of +0x6B2 concurrent with a player slot-0 hit is the other specific candidate; its type-0x0E/0x0F dispatch and hit window remain unproved. Therefore the 0x800328FC → 0x80053D28 handoff is Pending, while root 0x1E itself is Static-confirmed live via Slide. No further range is certified dead. The raw pair 0x41DA0 is a source-payload upper bound (and exceeds the ceiling by 0xD880); 0x1210 sharing would conditionally lower that to 0x40B90, still 0xC670 (50,800 bytes) above the Water observed-route ceiling 0x34520, before rebasing/metadata. No tighter byte-saving bound is certified: the new seven-frame/model-0 set is live, and other roots, computed readers and safe repacking are unclosed. There is no exact minimum compact footprint. The smallest next static check is to trace ordinary type-0x0E/0x0F AI dispatch into one concrete nonzero +0x6B2 writer (or bound Slide recovery against its victim loop), then prove whether a player slot-0 collision can be scheduled before that victim clears the flag. A guarded runtime proof is premature. No ROM build or emulator was run.

PRIS GRUNT armed-family projectile and native paging closure (2026-09-28, static only)

This pass used the supported clean USA Rev. 0 ROM and the project's established Type-5 decoder. No ROM was built and no emulator was run.

Family identity and ordinary AI. Type 0x14 PRIS GRUNT1 is the base/disarmed family member. Types 0x15/0x16/0x17 are armed variants that all depend on file 0x8E. The four generic distance-band action-list tables at 0x800A32B4, 0x800A34C8, 0x800A370C, and 0x800A3948 make the ranged ownership explicit: type 0x16 and type 0x17 select 0x8000C6D8 as their sole action in all four bands; type 0x15 mixes 0x8000C6D8 with 0x80004E4C in one near band and 0x8000C958 in the next, then uses only 0x8000C6D8 in the two farther bands. The selector compares horizontal separation against 450, computes the vertical/horizontal slope, and installs common-code aim callbacks 0x8000C688, 0x8000C6B0, 0x8000C628, or 0x8000C600 according to the slope/gates. These wrappers reach 0x80043E58, 0x80043E84, 0x80043E00, or 0x80043DD4, all of which call the shared ranged action 0x80043B18. No Prison/Bridge overlay callback participates in this AI/aim dispatch.

Projectile construction and file ownership. 0x80043B18 indexes a 0x28-byte parameter record by (type - 0x15), advances the attack animation, creates class 0x806 with callback 0x80043EB0, copies the shot parameters into that child, and continues the parent recovery. The projectile process 0x80043EB0 then calls 0x80047160 → 0x80046F74. The latter resolves the fighter-file slot through 0x800A0324[type], acquires the projectile palette from the type-specific file-relative offset in 0x800A0FC0[type], and constructs secondary animation slot 6 from that same primary fighter file. Therefore the visible projectile is variant-file-owned, not a Prison/Bridge stage-resource selector.

Type Primary + base bundle Projectile palette/source Secondary slot-6 script Static Type-5 projectile decode First dropped-weapon frame
0x15 PRIS GRUNT2 0x8F + 0x8E file-0x8F +0x1B860; 16-color green/yellow ramp +0x248 two 32×10 green/yellow bolt frames 12×98; decoded art is the long staff
0x16 PRIS GRUNT3 0x90 + 0x8E file-0x90 +0x1C49C; 16-color red/orange/yellow ramp +0x250 32×30, 28×30, 32×30 red/orange blast frames 48×72; decoded art is the bulky cannon/launcher
0x17 PRIS GRUNT4 0x91 + 0x8E file-0x91 +0x130F0; same green/yellow ramp as 0x15 +0x230 five green/yellow frames: 44×36, 48×28, 48×22, 44×28, 40×36 20×42; exact stock weapon label remains Pending

The 0x15 staff identification is therefore Static-confirmed from the decoded native weapon/projectile art rather than walkthrough inference. The 0x16 cannon/launcher label is likewise supported by its decoded large weapon and distinct red/orange blast; the clean table itself still names the fighter only PRIS GRUNT3. Type 0x17 is confirmed as a distinct armed Bridge variant, but this pass does not assign a stronger human-readable weapon name.

Projectile effects and collision. 0x80043EB0 uses permanent-code SFX selection (0x15 -> 0x126, 0x17 -> 0x22B, otherwise including 0x16 -> 0x22C) and starts common helper processes 0x80060E68 and 0x80061220. Both helpers acquire their shared effect source at permanent address 0x800B1808; no stage resource file is introduced by those helpers. Projectile collision reaches 0x8004CF3C, which resolves 0x800AF340[type] and the aim-supplied selector before calling 0x8002BA04. For all three aim selector records inspected per fighter, the strike-record reaction byte is stable per type: 0x15 -> 0x30 -> 0x8005854C, 0x16 -> 0x31 -> 0x80058660, and 0x17 -> 0x32 -> 0x80058754. Those victim reactions are also permanent main-image functions.

Armed-to-base disarm transition. 0x80057478 handles all three PRIS armed variants. For 0x15, 0x16, or 0x17, it rebinds actor +0x98 to base file 0x8E at slot 0x80111528, changes the fighter type to 0x14, and selects the base animation. In parallel it creates class 0x207 callback 0x8005E1FC for the detached weapon. That child keeps the original primary file (0x8F/0x90/0x91), uses palette records at +0x1B7DC/+0x1C418/+0x1306C respectively, and constructs secondary animation slot 0 from that file. The base 0x8E dependency is therefore a live combat/lifecycle requirement, not merely a loader-table association.

Native enemy-family paging. The original overlays already demonstrate section-scoped fighter-resource replacement through 0x80066478. A raw-overlay direct-call census finds zero arena-rewind calls in Water 0xA1, but three in Prison 0x9F (ROM 0xC4DF0, 0xC4EE4, 0xC4F64), two in Bridge 0x9B (ROM 0xBEA7C, 0xBED90), and two in Fortress 0x9E (ROM 0xC37BC, 0xC3BD0). Prison uses these rewinds to alternate, among other states, 0x8F+0x8E, file 0x22, and 0x90+0x8E. Bridge rewinds its ordinary 0x8E tail for special file 0x2D, then later rewinds that allocation and reloads 0x8E+0x91. Fortress similarly rewinds the ordinary GRUNT tail for an Assassin encounter and later restores 0x22+0x23. These are native precedents for an encounter/section-scoped randomizer resource plan; they do not authorize arbitrary rewinds in Water without a destination-specific lifetime proof.

Portability conclusion. The shooting mechanics for 0x15/0x16/0x17 are Static-confirmed portable at the combat-resource level: common permanent AI/aim/projectile/collision/reaction code + the fighter's primary file + shared base file 0x8E; no Prison/Bridge overlay-owned projectile callback or stage-resource selector was found in the traced chain. This is not yet a full cross-stage runtime approval. A destination proof must still budget the full fighter bundle plus transient projectile/effect allocations, keep 0x8E resident for disarm, and validate death/despawn/encounter accounting in the destination. The smallest justified runtime proof is one armed PRIS GRUNT in a destination whose native fighter allocation can be replaced rather than appended, with projectile firing, hit reaction, disarm-to-0x14, kill/despawn, and stage continuation all exercised.

PRIS GRUNT 16-color dry-repack sizing (2026-09-29, static/offline only)

The clean 0x8E base fighter uses two 6-bpp Type-5 models. File 0x8F uses a 6-bpp body model plus a 4-bpp secondary model; the staff/projectile art already lives in the 4-bpp model and was kept exact. Files 0x8E and 0x8F share the same 64-color body palette, so the dry conversion quantized their body art jointly to 15 visible colors plus transparent index 0 while preserving the armed-to-disarmed palette relationship.

The dry pack preserved all animation/model assignments, kept 0x8F model-1 pixels exact, rebuilt only used per-model dictionaries, and re-encoded the Type-5 streams with the established encoder-v2 class geometry. All 141 Type-5 frames in 0x8E and all 112 in 0x8F independently decode back to the selected target buffers with zero mismatches. No ROM was built and no runtime claim follows from this codec round-trip.

Resource Stock size 16-color compact dry pack Saving
file 0x8E 0x1FC10 = 130,064 B 0x1AAD8 = 109,272 B 0x5138 = 20,792 B
file 0x8F 0x1B910 = 112,912 B 0x17724 = 96,036 B 0x41EC = 16,876 B
0x15 family pair 0x8E+0x8F 0x3B520 = 242,976 B 0x321FC = 205,308 B 0x9324 = 37,668 B (36.79 KiB, 15.50%)

For comparison, merely changing the 6-bpp dictionary storage to 4-bpp while retaining the existing pattern-index population would save 0x85E6 = 34,278 B and yield pair size 0x32F3A. The compact used-pattern rebuild therefore contributes an additional 3,390 B beyond the direct bit-depth saving.

Against the user-supplied Water observed-route replacement ceiling 0x34520, the compact pair fits by 0x2324 = 8,996 B (8.79 KiB). This is a positive static sizing result, not a runtime-safe margin: Type-5 decode backing remains dimension-based (align4(width) * align2(height)) rather than shrinking with source bpp, and projectile/effect actors add transient pressure. A Water proof should therefore still measure the live cursor while firing/disarming/killing rather than treating the 8.79-KiB residual as certified free space.

The same method was applied separately to Bridge's native ordinary family 0x8E+0x91, using a joint 16-color body quantization while preserving 0x91's already-4-bpp secondary art. The pair falls from 0x32DB0 = 208,304 B to 0x2B14C = 176,460 B, saving 0x7C64 = 31,844 B (31.10 KiB, 15.29%), with zero dry-pack decode mismatches. This would reduce resident arena payload pressure if implemented, but it is not evidence of a Bridge frame-rate improvement: decoded image buffers retain their dimensions and the render/AI/projectile workload is unchanged. Any Bridge-lag claim requires a separate runtime performance/profile investigation.

Actual runtime-proof resource footprint. The runtime proof pack intentionally kept conservative palette-record/layout structure rather than using the smallest dry-pack sizes above. Its compact file sizes are 0x8E = 0x1B0B8, 0x8F = 0x17A04, and 0x91 = 0x1080C. Thus the Water 0x8E+0x8F pair is 0x32ABC = 207,548 B, saving 0x8A64 = 35,428 B versus stock and leaving 0x1A64 = 6,756 B (6.60 KiB) below the user-supplied observed-route Water pair ceiling 0x34520. Bridge's compact 0x8E+0x91 pair is 0x2B8C4 = 178,372 B, saving 0x74EC = 29,932 B versus stock. v04 preserves these same resource sizes and compressed-stream layout.

PRIS GRUNT 16-color Water/Bridge runtime proofs (2026-09-29)

Runtime-confirmed, bounded Water composition. Disposable proof MKMSZR_pris-grunt2-ci4_water-proof_v01.z64 replaces Water's native MONK5/MONK6 fighter pair with the compact 16-color 0x8E+0x8F PRIS GRUNT2 family and changes the nine ordinary Water spawn records to type 0x15; the Water boss path remains stock. The user reports that Water works without crashes or hangs on the exercised route. Supplied captures visibly confirm the imported staff grunt renders in Water, participates in close combat/reactions, and fires its green projectile there. This promotes cross-stage ranged PRIS GRUNT construction/combat/projectile behavior from static-only portability to bounded Runtime-confirmed evidence. The report does not by itself exhaust every Water spawn, terminal/death/despawn branch, disarm transition, or worst-case simultaneous projectile allocation.

Runtime-confirmed Bridge negative control. Disposable proof MKMSZR_pris-grunt-ci4_bridge-proof_v01.z64 keeps Bridge's native ordinary 0x14/0x17 roster and gameplay while substituting compact 16-color fighter resources. The user reports Bridge's stock slowdowns are unchanged. This is a bounded negative result: reducing the resident native grunt-resource payload by roughly 30 KiB does not visibly alleviate the tested Bridge slowdown. It does not establish the ultimate performance bottleneck; decoded image dimensions, rendering workload, AI/process load, projectile work, and other stage systems remain unchanged.

Visual-quality limitation. The first 16-color palette is Runtime-confirmed mechanically usable but visually suboptimal. User captures show an overly gray/teal cast and noisy-looking clothing in some reaction poses (notably the pants during Sweep). Treat v01 as a storage/runtime proof, not an accepted visual palette. The next visual iteration should keep the 4-bpp/16-color storage model but use hue-aware palette selection that deliberately reserves saturated body/clothing ramps instead of optimizing mainly by aggregate color distance.

16-color palette refinement correction (v03 rejected; v04 pending, 2026-09-29). Runtime captures showed v03 substantially worse: teal/magenta appeared on unrelated materials and trousers remained corrupted-looking. The v03 derivation was invalid because the stock 64-color palette record was parsed two entries off; its first two colors were incorrectly treated as header fields. The actual record is a 32-bit count followed immediately by 64 16-bit colors. v03 is therefore Rejected / failed and must not be used as evidence for palette quality.

v04 returns to the mechanically successful v01 compact files and keeps their exact compressed stream/file sizes. It corrects the problem at the model-0 dictionary-pixel level instead of globally substituting palette slots: stock 6-bit source-index families are partitioned by neutral / yellow-brown / green / cyan / purple hue groups, then mapped into 15 opaque compact indices plus transparent index 0. Each v01 compact dictionary pattern is rewritten from its aligned stock source pattern; the already-4-bit secondary weapon/projectile art and all compressed frame streams remain untouched. Both stock 64-color body-palette variants are reduced coherently using the same source-index representatives. Offline validation decodes all compact frames successfully; unavoidable ambiguity from v01's earlier pattern dedup affects only about 0.23% of model-0 pixels in file 0x8E, 0.086% in 0x8F, and 0.179% in 0x91 versus the ideal source-derived remap. One combined Safe-Stage-Select ROM carries compact 0x8E+0x8F+0x91 for Water/Bridge A/B testing. The user then tested v04 in both Water and Bridge and explicitly accepted it as the compact PRIS GRUNT visual baseline. Supplied captures show substantially improved material/color placement under both stages' contrasting lighting; some residual 16-color noise remains but was accepted. This is Runtime-confirmed visual acceptance for the bounded v04 proof, not production-wide fighter conversion approval.

Accepted v04 baseline. MKMSZR_pris-grunt-ci4_water-bridge-proof_v04.z64, SHA-256 db85dcb8cdbcb8da3c2955f5f22b9ef00b056329e91278278f39222199c0a7b7, is the accepted compact visual proof baseline. It includes Safe Stage Select; Water uses compact 0x8E+0x8F with all ordinary Water spawns changed to staff PRIS GRUNT2/type 0x15; Bridge keeps its native ordinary 0x14/0x17 roster with compact 0x8E+0x91. The accepted result supersedes v01/v02 as the preferred visual palette and explicitly rejects v03. Residual 16-color texture noise is accepted as a bounded tradeoff for the proven resident-file reduction.

Deterministic resource-family planner v1 (2026-09-30)

Implementation-confirmed; pipeline-disconnected. The repository contains a pure planner in src/mkmszr/enemy_planner.py plus an exact 104-record physical stream catalog in src/mkmszr/data/enemies.py. This layer does not itself patch ROM bytes.

Planner v1 is fail-closed against the current evidence base:

  • all 104 main ordinary-stream spawn records are cataloged by exact fighter-type ROM word;
  • the five gated singleton/special records stay fixed, leaving 99 randomizable records;
  • native profiles preserve researched type-count semantics;
  • foreign profiles are admitted only with bounded Runtime-confirmed evidence;
  • Water profiles enforce the observed-route fighter-payload ceiling 0x34520 and count shared PRIS resources once;
  • Prison is modeled as native paging groups rather than one stage-wide pool;
  • Prison's Runtime-confirmed prison-fast-phase profile separates randomizable combat resources from fixed scripted auxiliary resources: FAST MONK file 0x20 replaces ordinary combat ownership while stock file 0x8F remains resident for the capture set-piece;
  • unresolved foreign MONK1/MONK3/MONK4 and GRUNT1/GRUNT2 paths remain excluded.

The RNG domain is isolated as MKMSZR:ENEMIES:RESOURCE-PLANNER:V1. Deterministic retry is explicit so materialization can skip unsupported profiles without changing seed behavior.

Tests cover exact 104/99/5 accounting, special-record immutability, deterministic plans, Water payload bounds, shared PRIS accounting, Prison phase boundaries/fixed auxiliaries, and fail-closed unresolved families.

Guarded enemy materializer v1 (2026-09-30)

Implementation-confirmed; bounded Runtime-confirmed; pipeline-disconnected. The guarded ROM emitter in src/mkmszr/patches/enemy_randomization.py consumes planner output while preserving the planner's deterministic retry domain. It guards all 104 stock fighter-type words before changing any enemy type and preserves the five fixed gated singleton encounters.

Materializer v1 supports all native profiles plus the repository-reproducible foreign profiles for Water HULK MONK, FAST MONK and raw PRIS GRUNT4, and the Runtime-confirmed Prison prison-fast-phase profile. Unsupported compact/auxiliary profiles remain fail-closed and are deterministically skipped rather than guessed.

Generated seed ENEMYPLAN05 v01 passed Temple, Water, Earth, Fire, Bridge and Fortress but failed in Prison because stage-wide Prison shuffling crossed native resource lifetimes. Corrected v02 (SHA-256 15da86ec7fef4bce2617a3162239065531aaf5f34d2b4ffedb73f8bad2c2c238) constrained Prison assignments to paging groups and the user confirmed Prison worked.

Prison phase-family closure

A first family-replacement proof removed too much: replacing the native 0x8F+0x8E family with FAST MONK caused a hard hang at the scripted capture after the second energy fence. Static tracing identified Prison overlay routine VA 0x802F0754 / ROM 0xC8594 as a direct consumer of file-0x8F through slot 0x80112008, independent of the ordinary enemy stream.

The corrected proof therefore models each paging phase as randomizable combat resources + fixed scripted auxiliaries. For prison-fast-phase, ordinary 0x15 combat becomes FAST MONK 0x0A / file 0x20, while stock file 0x8F remains resident for the capture set-piece. The stock 0x0E phase and final 0x14/0x16 phase remain unchanged.

Runtime-confirmed: MKMSZR_prison-fast-monk-phase-proof_v02.z64, SHA-256 90188adb7a66460f0662de18cbdf65d4b17d5ba839c405395613bd30012c5ebb. The user completed the full FAST MONK → GRUNT1 → FAST MONK / energy fences → scripted capture → final PRIS route successfully.

This Runtime-confirmed phase/auxiliary contract is encoded in both planner and materializer tests. Production browser/CLI integration remains Pending.

Post-merge generated-seed runtime confirmation — v03 (2026-09-30)

Runtime-confirmed, bounded: MKMSZR_enemy-planner_all-stages-proof_v03.z64, seed ENEMYPLAN05, SHA-256 630a5302c30c216994df2ad265cadf374e2639257fb44357adeabbf9b49a586f, CRC1/CRC2 0x4D275D85 / 0xF89D9361.

This proof was built from the merged planner + merged guarded materializer on current main, not from an earlier standalone proof branch. It exercised the actual deterministic 104-record planner/materializer composition:

  • Temple native MONK2/MONK3 multiset, seed-permuted;
  • Water foreign 0x17 PRIS GRUNT4 with raw stock 0x8E+0x91;
  • Prison Runtime-confirmed prison-fast-phase: ordinary 0x15 combat replaced by FAST MONK 0x0A, stock 0x8F retained for the scripted capture auxiliary, stock 0x0E and final 0x14/0x16 phases preserved;
  • Fire native 0x09/0x0A multiset, seed-permuted;
  • Bridge native 0x14/0x17 multiset, seed-permuted;
  • Fortress native repeated 0x0E/0x0F grunt multiset, seed-permuted, with Kia/Jataaka/Sareena fixed;
  • Wind/Earth remained homogeneous native families.

The user manually exercised all requested stage smoke routes and reported everything worked. This Runtime-confirms the merged planner→materializer composition for one representative all-stage seed, including both foreign resource-family paths and the fixed scripted Prison dependency.

This does not establish every seed/profile combination, unresolved foreign MONK1/MONK3/MONK4 or GRUNT1/GRUNT2 families, or arbitrary cross-family phase substitutions. It does satisfy the current bounded runtime gate for moving the enemy feature out of proof-only orchestration and into the normal product pipeline.

Production compact fallback

Implementation/CI-pending: the production materializer now promotes the already Runtime-confirmed compact PRIS profiles instead of deterministically skipping them.

  • water-pris15 builds compact 0x8E + 0x8F;
  • water-pris16 builds compact 0x8E + 0x90;
  • water-pris14-16 uses the same compact 0x8E + 0x90 resource family with the accepted mixed roster.

The compact resources are regenerated at build time from the user's clean ROM. No fighter-resource bytes are stored in the repository. The builder decodes native Type-5 art, applies the accepted 16-color body index/palette-family mapping, preserves already-4-bit weapon/projectile art, rebuilds native Type-5 dictionaries/streams, rebases file-internal frame/palette offsets, updates global palette-offset tables, zeroes stale shortened-file tails, and independently decodes every emitted frame before accepting the build.

The recovered production builder reproduces the accepted compact GRUNT3 0x8E and 0x90 layouts byte-for-byte and generalizes the same proven transform to 0x8F and 0x91. Raw resources remain preferred where the registered profile already fits; compaction is used by profiles whose accepted resource contract requires it.

This does not make storage the only compatibility criterion. Foreign MONK1/MONK3/MONK4 and GRUNT1/GRUNT2 remain fail-closed because their unresolved terminal/cache/action semantics are independent of file size.

Shared product integration

Implementation/CI-pending: the shared product configuration now has enemy_randomization: bool = False. Browser and CLI expose the same option (web Enemy Randomization switch; CLI --enemy-randomization), and build_pipeline() appends the guarded EnemyRandomizationPatch only when enabled. The option uses the normal build seed and the same fail-closed materializable-profile retry contract validated by the proof line. Default-OFF builds omit this patch entirely.

The remaining gate is repository CI for the composed product branch, followed by one representative build produced through the normal browser/CLI path with the option enabled.

Compatibility matrix v02 — resource-planning pass (2026-09-29)

This planner-facing pass keeps the existing 19-type v01 roster but adds resource-family lifetime, terminal closure, compaction status, and destination-fit consequences. Fit is not compatibility: the Water figures below compare resident fighter payload only against the user-measured replacement ceiling 0x34520; they do not certify transient projectile/effect pressure or a runtime route. Native Prison/Bridge/Fortress rewinds are evidence for section-scoped paging only in their proven lifetimes and do not authorize rewinds in another destination.

Type Primary bundle Native lifetime / auxiliary closure Compaction / Water planning status
00 MONK1 88 = 0x225B0 Earth resident; ordinary terminal uses current-stage selector 10 Raw fits Water; foreign selector-10 presentation unresolved
01 MONK2 89 = 0x22BF0 Temple resident; selector-10 terminal; Fire import Runtime-confirmed through death/despawn when Temple selector 10 is materialized Raw fits Water; Temple/Water terminal payload relation known
02 MONK3 8A+89 = 0x2E100 Temple resident; selector-10 terminal; base 89 required Raw fits Water; cross-stage two-file/terminal composition Pending
03 MONK4 8B = 0x14EE0 Wind resident; selector 10 is external IDs 0x37A..0x37F Raw easily fits Water, but external terminal/cache closure is Pending
05 MONK6 8D+8C = 0x284D0 Water stage-resident; audited Water overlay has 0 direct rewinds; selector-10 terminal Native Water family
09 HULK MONK 25 = 0x2C210 Fire resident; fighter-file terminal path Raw fits Water; later compact proof not required for size
0A FAST MONK 20 = 0x21160 Fire resident; fighter-file terminal path Raw fits Water; Fire 0A -> 09 remains Runtime-confirmed direct substitution
0D Kia 1F = 0x34030 Fortress phase/encounter singleton Excluded from ordinary pool
0E GRUNT1 22 = 0x255E0 Prison/Fortress phase-paged; file-internal terminal root 0x32 Raw fits Water, but ordinary action/reaction closure remains incomplete
0F GRUNT2 23+22 = 0x41DA0 Fortress phase-paged; Water normal terminal can use selector 10; morph requires 22 Raw exceeds Water; conditional 0x40B90 still 0xC670 too large; separate unresolved problem
11 Undead Scorp 95 = 0x3FB10 Prison singleton Excluded
12 Scorpion 87 = 0x459E0 Temple singleton Excluded
13 Jataaka 1D = 0x3AFD0 Fortress phase/encounter singleton Excluded
14 PRIS GRUNT1 8E = 0x1FC10 Prison/Bridge phase-paged; base/disarmed family; file-internal terminal Raw fits Water; compact 8E is Runtime-confirmed within accepted v04 family compositions
15 PRIS GRUNT2 8F+8E = 0x3B520 Prison phase-paged; permanent-code ranged path; disarm -> 14/8E Raw exceeds Water; accepted compact pair 0x32ABC fits by 0x1A64; bounded Water Runtime-confirmed
16 PRIS GRUNT3 90+8E = 0x3C160 Prison phase-paged; cannon/launcher projectile in 90; disarm -> 14/8E Raw exceeds Water by 0x7C40; compact 90 not yet derived/proven
17 PRIS GRUNT4 91+8E = 0x32DB0 Bridge phase-paged; projectile/weapon in 91; disarm -> 14/8E Raw Water import is Runtime-confirmed bounded; stock pair fits by 0x1770 (5.86 KiB); accepted compact pair 0x2B8C4 would fit by 0x8C5C (35.09 KiB)
1A MONK5 8C = 0x1E160 Water stage-resident; selector-10 terminal Native Water family
1C Sareena 1C = 0x37400 Fortress phase/encounter singleton Excluded

PRIS armed-family terminal closure

The normal terminal dispatcher 0x80056F80..0x80057060 sends types 0x15/0x16/0x17 to 0x80053E8C. While still armed they take its direct cleanup/despawn branch. If they are disarmed first, 0x80057478 rebinds the actor to base file 0x8E, changes the type to 0x14, and starts detached-weapon presenter 0x8005E1FC with the original armed file. Type 0x14 then uses file-0x8E palette source +0x1FB84 and root 0x32 at +0x134, a six-frame file-internal terminal list followed by zero. Static-confirmed: the armed family therefore does not require a destination selector-10 bundle for this normal terminal path.

Selected next proof: Water × type 0x17

Type 0x17 PRIS GRUNT4 is the smallest high-value next family proof because it adds a distinct ranged variant without repeating the already-proven staff-grunt case, while its full combat-resource closure is known: stock 0x8E + 0x91 = 0x32DB0, permanent-code aim/projectile/collision/reaction helpers, file-0x91 projectile secondary slot 6, file-0x91 detached weapon slot 0, and base 0x8E for disarm. Raw residency is only 0x1770 (6,000 B / 5.86 KiB) below the observed Water replacement ceiling, so this is deliberately a narrow bounded proof. If that margin is insufficient at runtime, the accepted compact 0x8E+0x91 = 0x2B8C4 composition is the next storage control before any animation deletion.

Runtime-confirmed, bounded: disposable MKMSZR_pris-grunt4_water-proof_v01.z64, SHA-256 0548808d417bbd006d54a8bd028926e0c3dd490a6c32cfa821c82be134ea1b34. It starts from the clean USA Rev. 0 ROM, includes current Safe Stage Select, replaces Water's two native fighter loads with stock files 0x8E and 0x91 in their exact constructor slots, changes all nine Water ordinary spawn type fields to 0x17, leaves the Water boss path stock, and updates CRC1/CRC2 to 0x4D27726B / 0x989FF0C9. The user manually validated the intended route: normal spawn/AI, ranged projectile behavior and hit reaction, armed-to-base disarm with detached weapon, kill, normal death/despawn, and continued Water-stage progression all worked perfectly. This confirms the tested stock-resource Water × 0x17 composition; it does not establish arbitrary mixed rosters or worst-case projectile concurrency.

Runtime result: the full intended validation chain was exercised successfully: spawn -> normal AI -> ranged/special behavior -> projectile hit reaction -> disarm/detached weapon -> kill -> death/despawn -> stage continues. This is bounded Runtime-confirmed evidence for Water × 0x17 using the raw stock pair only, not arbitrary mixed rosters or worst-case projectile concurrency.

Selected proof after matrix v02 — Water × raw FAST MONK / type 0x0A (2026-09-30)

The next family-expansion proof is Water × 0x0A FAST MONK, chosen to move beyond the already Runtime-confirmed PRIS ranged family while keeping the resource transaction deliberately simple.

Static-confirmed dependency composition:

  • type 0x0A primary fighter file: stock global file 0x20, size 0x21160;
  • constructor resource slot: 0x800C2560;
  • descriptor offset: 0x20FC8;
  • type-indexed terminal palette source: file 0x20 +0x210D0;
  • primary root 0x32 points to file-relative list +0x108, containing six frame-record offsets +0x209A8,+0x20AD4,+0x20C08,+0x20D24,+0x20E38,+0x20F18 followed by zero;
  • the normal 0x0A terminal route is therefore fighter-file-internal through 0x80053E8C; no foreign destination selector-10 bundle is required for that normal path.

The disposable proof intentionally preserves Water's second stock fighter load rather than deleting an allocation. Water's first native fighter load is changed from file 0x8C into stock file 0x20 and stores the returned allocation in the exact 0x0A constructor slot 0x800C2560; the second stock file 0x8D load remains unchanged but is unused by the patched ordinary roster. All nine ordinary Water records are changed to type 0x0A; the Water boss/special path is untouched.

This yields resident fighter payload 0x20 + 0x8D = 0x2B4D0. Against the user-measured Water replacement ceiling 0x34520, the bounded resident-payload margin is 0x9050 = 36.08 KiB. This is intentionally more conservative in lifecycle shape than deleting Water's second allocation, while still remaining well below the observed ceiling.

Runtime-confirmed, bounded: MKMSZR_fast-monk_water-proof_v01.z64, SHA-256 e01e8ece94f642411afb9150b8a1ee9f74b7871945bcf029a469dcb9349c774f, CRC1/CRC2 0x4D274D15 / 0x9E933CC0. Static diff audit confirms that, beyond current Safe Stage Select and header CRC, only the nine ordinary Water type words and the four first-fighter-loader words differ from retail. Stock file-table entries and bytes for 0x20 and retained 0x8D are unchanged. No emulator automation was run.

Runtime result (user manual validation, 2026-09-30): the intended route passed: spawn -> normal FAST MONK AI/movement -> several ordinary attacks -> player/enemy hit-reaction states -> kill -> full normal death/despawn -> continued Water progression, including encounters with additional FAST MONKs. All exercised behavior worked as expected.

This establishes bounded Water portability for the self-contained Fire FAST MONK family using raw stock file 0x20 in the tested all-0x0A ordinary roster. It does not establish arbitrary mixed rosters, HULK MONK 0x09, all fighter families, or worst-case transient pressure.

Compact PRIS GRUNT3 / Water proof v01 (2026-09-30)

The 0x16 PRIS GRUNT3 Water gate is now Runtime-confirmed, bounded. This pass applies the accepted v04 PRIS-family compaction methodology to stock files 0x8E and 0x90; it does not claim byte identity with the earlier accepted 0x8E/0x8F/0x91 v04 proof pack.

Compact 0x8E+0x90 resource closure

Stock file 0x90 contains 76 Type-5 frames: 59 use a 6-bpp body model and 17 use an already-4-bpp secondary model. The secondary model owns the cannon/launcher and projectile art. The two 64-color body palettes are the same source palettes used by the already studied 0x8E/0x8F/0x91 PRIS family, while projectile palette 0x90 +0x1C49C is independently 16-color.

The rebuild follows the v04 family rule:

  • source index 0 remains transparent;
  • the 6-bpp body source indices are kept within hue/material families and mapped into 15 opaque compact indices;
  • the same source-index representatives are used coherently across both stock 64-color body-palette variants;
  • 0x90's already-4-bpp secondary cannon/projectile pixels remain exact;
  • its 16-color projectile palette remains byte-exact and is only relocated;
  • all Type-5 model assignments are preserved and dictionaries/streams are rebuilt with the established encoder class geometry;
  • compact file-relative frame and palette references are rebased, and the base-file 0x8E terminal palette path is retained.

The selected body-palette representatives are source indices:

[2, 4, 8, 11, 13, 16, 20, 23, 26, 29, 34, 45, 49, 54, 58]

Offline validation decodes every rebuilt frame back to the selected target indexed buffer with zero mismatches: all 141 file-0x8E frames plus all 76 file-0x90 frames. This includes exact indexed pixels for all 17 native 4-bpp 0x90 secondary frames.

Resource Stock Compact proof v01 Saving
file 0x8E 0x1FC10 0x1A148 (8-byte aligned) 0x5AC8
file 0x90 0x1C550 0x16AC0 0x5A90
pair 0x3C160 0x30C08 0xB558 = 46,424 B (45.34 KiB)

Against the user-measured Water replacement ceiling 0x34520, the aligned pair leaves 0x3918 = 14,616 B = 14.27 KiB of resident-payload margin before transient projectile/effect pressure. This is materially larger than the accepted staff-grunt proof's residual margin, but remains a bounded sizing argument rather than certified worst-case free space.

Disposable Water × type 0x16 proof

Runtime-confirmed, bounded: MKMSZR_pris-grunt3_water-proof_v01.z64, SHA-256 665bf98d6c457853a30f66de00680e6de761d3773ce7108598a3050c856c7bed, CRC1/CRC2 0x4D2AFA17 / 0x724DC8BD.

The proof:

  • starts from the clean USA Rev. 0 ROM;
  • includes current Safe Stage Select;
  • replaces Water's native 0x8C+0x8D fighter residency with compact 0x8E+0x90;
  • loads 0x8E into constructor slot 0x80111528;
  • loads 0x90 into constructor slot 0x802C0FA0;
  • rebases the permanent body/projectile/terminal palette-offset tables to the compact file layouts;
  • changes all nine ordinary Water spawn records to type 0x16;
  • leaves the Water boss/special path untouched;
  • zeroes the stale tails of the shortened stock 0x8E/0x90 ROM ranges, so a missed old file-relative reference cannot silently succeed by reading leftover stock bytes;
  • updates N64 CRC1/CRC2.

A post-build audit re-decodes all 217 compact Type-5 frames from the finished ROM, confirms the 0x90 projectile palette is byte-exact at its relocated position, and classifies every changed ROM span into an intended guarded region. No emulator automation or runtime execution was performed.

Manual validation target:

spawn -> normal AI -> cannon/launcher fire -> projectile hit/reaction -> disarm/detached weapon -> kill -> death/despawn -> stage continues

Runtime result (user manual validation, 2026-09-30): the full intended route passed and worked perfectly: normal spawn/AI, cannon/launcher fire, projectile hit/reaction, armed-to-base disarm with detached weapon, kill, full death/despawn, and continued Water-stage progression. This completes bounded cross-stage runtime coverage for all three armed PRIS variants (0x15, 0x16, 0x17). It does not establish arbitrary mixed rosters or worst-case simultaneous projectile pressure.

Water × raw HULK MONK / type 0x09 proof v01 (2026-09-30)

Runtime-confirmed, bounded: MKMSZR_hulk-monk_water-proof_v01.z64, SHA-256 90e6ae1f97934d42ededeb0b861caa577bc86870f1df7b27be7dc49b5f6ed4c7, CRC1/CRC2 0x6CBFAD5E / 0x945B4B76.

Type 0x09 HULK MONK uses untouched stock file 0x25 (0x2C210) in constructor slot 0x801AE578 with no secondary/base fighter-file dependency. The Water proof redirects the first native fighter transaction from file 0x8C to file 0x25, stores the allocation at the exact 0x09 constructor slot, and NOPs only the now-unused second 0x8D fighter transaction at ROM 0xB58D8..0xB58F8. All nine ordinary Water records become type 0x09; the Water boss/special path remains stock.

Resident fighter payload is 0x2C210, leaving 0x8310 = 32.77 KiB below the user-measured Water replacement ceiling 0x34520. No compaction or destination rewind is used.

Runtime result: the user manually validated spawn, normal HULK MONK AI/movement, ordinary attacks, hit/reaction states, kill, full death/despawn, continued Water progression, and additional HULK MONK encounters; all worked as expected. This establishes bounded Water portability for raw stock type 0x09 under the tested homogeneous roster.

Mixed Water PRIS family proof — types 0x14 + 0x16 (2026-09-30)

Runtime-confirmed, bounded: MKMSZR_pris-mixed14-16_water-proof_v01.z64, SHA-256 57d92254e24e644f9f26b6a43d3410562081e3f66daa9db613bc25a3ece95d50, CRC1/CRC2 0x4D2AFA63 / 0x8A754699.

This proof is derived from the already validated compact Water × 0x16 composition. Resident resources, loader logic, constructor slots and palette rebasing remain unchanged: compact 0x8E + 0x90 = 0x30C08. Only the nine ordinary Water type words change to 14,16,14,16,14,16,14,16,14.

The user manually validated the mixed route and reported it worked perfectly. The tested composition includes native-base 0x14 PRIS GRUNT1 actors coexisting with armed 0x16 PRIS GRUNT3 actors, cannon/projectile activity, armed-to-base disarm/morph into 0x14, converted and originally spawned base grunts sharing the same resident base resource, normal kill/death/despawn, and continued Water progression.

This is the first bounded Runtime-confirmed mixed ordinary-enemy roster in the current cross-stage proof line. It materially reduces the value of further homogeneous single-type Water proofs for already-closed resource families. It does not establish arbitrary cross-family coexistence, unresolved MONK1/MONK3/MONK4 terminal/cache paths, or the separate GRUNT1/GRUNT2 resource problem.

Clone this wiki locally