Player Actions and Special Moves - smeagol44/MKMSZ-Randomizer GitHub Wiki

Player actions and special moves

Scope: This page owns the MKMSZ host-side action/control ABI: special-action descriptor and installer semantics, scheduler/context transfer, player movement/animation/strike primitives, callback lifetime and cleanup rules, and durable host-side findings from the Reverse Elbow proof line.

Donor choreography, donor move phases, MKT strike/reaction meanings, no-repel semantics, combo translation, and reusable donor -> MKMSZ mappings are canonical in MKT adapter primitives. The old URL is retained only as a historical supersession stub at Foreign moves and Reptile.

Low Kick descriptor and special-action path

semantic Low Kick 0x80014C60
-> event-4 handler 0x8003C94C
-> descriptor parser 0x8004A0E0
-> matcher 0x80049E60
-> transfer dispatcher 0x80049D14
-> installer 0x80032CD4

The Low Kick table is RAM 0x800B0F68 / ROM 0xB1B68. Corrected 2026-09-26 clean-ROM reconciliation: it contains one active 0x2C-byte record, followed by the zero u16 sentinel at 0x800B0F94. RAM 0x800B0F98 begins the separate High Kick table; it is not a second Low Kick record. The older two-record interpretation is superseded. Any added descriptor still requires separately owned/relocated storage rather than extending through the sentinel into the HK table.

Offset Type MKMSZ host meaning
+0x00/+0x02 two u16 Legal semantic-button masks
+0x04 u32 Transfer selector; stock implemented values 0 and 1
+0x08 pointer Optional no-argument condition
+0x0C pointer No-argument action/process callback
+0x10 u16 Input-history window
+0x12..+0x1C six u16 Facing-right tokens, zero-terminated
+0x1E..+0x2A seven u16 Facing-left tokens, zero-terminated

Direction tokens are 8=Left and 9=Right. The Reverse Elbow proof used the semantic command Back -> Forward + Low Kick: facing right 8,9,0, facing left 9,8,0, with window 0x10. The donor reason for that command and the donor move choreography belong to MKT adapter primitives.

Horizontal locomotion and facing

Static-confirmed. The main player process at 0x80028F3C owns normal directional locomotion. Controller +0x638 points directly to the remapping-aware semantic input word 0x800BF2EE. In its normal horizontal state, Right (0x2000) and Left (0x8000) are copied to controller +0x68C as the active direction.

The same input classification derives controller +0x704 from actor facing bit +0x8C & 0x10:

  • Right stores the facing bit directly;
  • Left stores the facing bit XOR 0x10.

The result is direction-independent: +0x704 == 0 means the requested horizontal direction matches current facing, while nonzero means it opposes current facing.

At 0x800293B0, stock branches on this marker:

+0x704 == 0
    -> 0x8002EAFC run check
    -> 0x80030178 forward locomotion setup
    -> selector 1 / primary slot 0x01 walk_forward

+0x704 != 0
    -> 0x80030208 backward locomotion setup
    -> selector 2 / primary slot 0x02 walk_backward

Both setup helpers preserve fighter-specific speed tables and stock movement state. 0x80030178 writes the forward parameters and returns 1; 0x80030208 writes the backward parameters and returns 2. The selected value is shadowed in controller +0x680 before 0x8002FE54 installs the corresponding animation.

The stock facing-flip primitive is 0x8003188C(actor). It toggles only actor +0x8C bit 0x10 and then calls the normal frame/setup path 0x8001BDA0 with actor +0x74. Future control work should use this primitive rather than writing the facing bit directly.

Turn / facing-lock design seam

Semantic Turn/Combine is bit 0x0001. The player controller is wired to the remapping-aware semantic input word 0x800BF2EE through controller +0x638 during player construction. Inventory code at 0x8007764C separately reads that same semantic word and masks 0x0001 for Combine, so a modern-control implementation must not globally clear or remap Turn; inventory Combine behavior must remain context-local and stock.

The stock gameplay turn-action routine at 0x8003D86C selects primary animation slot 0x03, the canonical turn animation, and runs the native turn-action sequence. Because the routine operates through the current controller rather than being intrinsically player-only, a control-assist proof should suppress the standalone turn only when current controller +0x638 == 0x800BF2EE; this preserves non-player/native users while making held Turn available as the player-facing modifier.

The accepted control-assist direction is gameplay-local:

requested direction matches facing
    -> vanilla forward path

requested direction opposes facing
    + Turn held
        -> vanilla backward path
    + Turn not held
        -> stock facing flip
        -> stock forward/run path

This preserves the native backward-walk implementation as an intentional Facing Lock / Backpedal modifier instead of recreating it. The ordinary stock turn-action state is distinct from inventory handling and must be suppressed or bypassed for this mode so holding Turn can function as a modifier rather than immediately consuming the press as a standalone turn.

While a walk is already active, stock polls controller+0x68C & semantic_input. Releasing/changing the active direction exits through the normal stop/cleanup path and the next locomotion entry recomputes direction/facing. A first proof may therefore tolerate a one-tick re-entry on reversal, but release of Turn during an already-active backward walk needs an explicit check if the desired modifier semantics are to switch immediately back to auto-facing without requiring a direction repress.

Smallest proof contract

A bounded proof can stay almost entirely inside stock locomotion semantics:

after stock computes controller +0x704:
    if +0x704 == 0:
        leave stock forward path unchanged
    else if Turn/Combine bit 0x0001 is held:
        leave stock backward path unchanged
    else if nearest opponent has +0x6BC & 0x0200:
        leave stock forced-facing behavior unchanged
    else:
        0x8003188C(player_actor)
        controller +0x704 = 0
        resume stock forward/run selection

The same proof must player-gate the standalone 0x8003D86C turn action so a held Turn press does not itself consume control before horizontal locomotion sees it. Inventory Combine is outside that gameplay action path and remains vanilla. Earth boss behavior is an explicit manual test item because the generic 0x0200 policy is not statically universal.

Stock forced-facing exception

Shared host routines use opponent-controller +0x6BC bit 0x0200 as a gate for automatic 0x80031EF0 face-opponent behavior. The stock fighter-characteristics table sets this bit for several special/boss fighter types, including the documented Wind 0x08, Water 0x0B, Fire 0x0C, and Fortress 0x1B/0x1D encounters. This is a face-policy flag, not a proven universal "boss bit"; other special types also carry it.

Earth type 0x19 is a separate unresolved exception. Its stock characteristics-table word is zero, and the Earth custom boss constructor at overlay VA 0x802EDF50 (Earth overlay file 0x9C) bypasses the generic fighter-characteristics initialization: it manually installs type 0x19 at actor +0x78. A direct audit of the Earth overlay finds neither a +0x6BC write nor a direct call to 0x80031EF0 in that boss constructor/overlay. Therefore +0x6BC & 0x0200 is not yet proven to detect the Earth boss, and it must not be documented or implemented as a universal boss bit.

For the first modern-facing proof, the safe static rule is: defer completely to stock when the nearest opponent carries +0x6BC & 0x0200; test Earth separately. If Earth runtime behavior still auto-faces as expected, capture that route and trace its distinct mechanism before production integration instead of hard-coding a stage ID.

Direction-facing proof v01/v02

The first expansion-loaded control proof uses a proof-only file-0x1A module in the 15 KiB expansion pool and enters freshly loaded code only through uncached KSEG1 trampolines.

  • v01 β€” Rejected / failed. Pressing the first opposite horizontal direction immediately hard-hung. The proof used current-controller global 0x802FCE20, repeating the already-rejected unsigned-low-immediate interpretation. The actual address is 0x802ECE20: lui 0x802F paired with signed low immediate 0xCE20 subtracts 0x31E0.
  • v02 β€” Runtime-confirmed on the tested route. Changing only that global to 0x802ECE20 made the control behavior work as intended: opposite horizontal input flips Sub-Zero and proceeds through stock forward locomotion; holding Turn preserves vanilla backward walking / facing lock. The user reported the corrected behavior worked perfectly and matched the requested control model.

The proof line continued through v10/v06 and has now been promoted into the shared browser/CLI patch core. Runtime-confirmed production composition: TURN: TOGGLE preserves vanilla controls; TURN: LOCK uses the accepted v10 world-direction/facing-lock model, while v06 makes the fragile release/action-install hooks leaf-only and temporarily defers to stock semantics when the bounded controller-list scan finds opponent +0x6BC & 0x0200. The full production build passed both former Temple hang regressions and the first Scorpion forced-facing route. Earth type 0x19 remains Pending and is not generalized from Scorpion.

ATTACK: MODERN semantic input translation

Accepted control contract / proof status. The accepted user-facing setting is ATTACK: CLASSIC / MODERN. The current production frontend/code still names the same durable 0x0400 state bit COMBOS=ASSIST / COMBOS_ASSIST_STATE_MASK; that historical name remains current until production integration. The v04 common proof is Runtime-confirmed on the tested control routes and is the accepted ATTACK: MODERN gameplay baseline. No state-layout migration is required to integrate it.

ATTACK: MODERN is an input-translation layer over stock combat, not a replacement combat engine. The accepted priority is:

Context ATTACK: MODERN result Stock authority preserved
Blocking + Attack Low Punch; Block is cancelled directly into the stock LP state Close LP still reaches the normal grab/throw route
Straight jump or somersault + Down + Attack Air kick semantic Stock airborne attack states, timing and presentation
Straight jump or somersault + Attack Air punch semantic Stock airborne attack state
Normal grounded control, currently moving backward + Run + Attack Roundhouse semantic Stock HK/roundhouse action
Normal grounded control, currently moving backward + Attack Sweep semantic Stock LK/sweep action
Active stock combo Each Attack press satisfies the stock combo parser's current expected button while the parser retains its own timing, chaining, reaction data and conditions Final Back requirement remains a real stock condition
Otherwise High Punch Stock neutral HP path

Live backward predicate

Controller +0x6FC is not a live "currently walking backward" flag. Stock writes locomotion mode 1 or 2 during forward/backward setup and the value can remain after returning to idle. ATTACK v03 therefore leaked the last walk direction into later attacks: after a backward walk, idle Attack produced kicks; after a forward walk, it produced punches.

v04 instead requires all of the following at the event-0 seam:

  • active player input map 0x8009F9E8 (normal grounded control);
  • a horizontal semantic direction is currently held: Left|Right = 0xA000;
  • controller +0x704 != 0, the stock current direction-vs-facing mismatch;
  • Run bit 0x0004 selects Roundhouse; otherwise Sweep.

This makes the predicate about current backward locomotion intent, not historical locomotion setup. It is independent of TURN: TOGGLE / LOCK.

Airborne translation

The event-0 wrapper recognizes both stock airborne control maps:

  • straight-air map 0x8009FA00;
  • somersault map 0x8009F9A0.

With Down 0x4000 held, Attack translates to semantic event 3 and stock chooses the airborne kick path. Without Down, event 0 remains the stock airborne punch. v02 Runtime-confirmed the somersault correction and v04 preserves it.

Stock combo-parser translation

The accepted combo implementation does not synthesize a hard-coded combo or force animation states. The stock parser at 0x8004D764 remains authoritative.

At 0x8004D7A8, stock stores the current expected semantic event into controller +0x71C. The v04 proof hook at VA 0x8004D7AC / ROM 0x0004E3AC changes only the history source while ATTACK: MODERN is active:

  • CLASSIC replays stock sll v0,v0,3 and reads the expected event's history;
  • MODERN uses event-0 / Attack history for that combo record;
  • the displaced actor load is replayed;
  • the parser's record chaining, windows, misses/expiry, strike/reaction data and condition callbacks remain stock-owned.

The accepted Sub-Zero sequence is therefore Attack x5, then Back+Attack. Back is not synthesized: the final stock Back condition still has to be physically satisfied.

Proof chronology v01-v04

  • v01 β€” Rejected / unsafe composition. Artifact MKMSZR_attack-modern_common-proof_v01.z64, SHA-256 665afc2e60373ad20f009c474089ec28eae61b39d3698c8f9619fbcca6aa65ba, CRC1/CRC2 CA215655 / A8146A72. Neutral HP, Block+Attack LP/grab, and ordinary airborne punch were observed; Down+Attack worked only in straight jump; Sweep, Roundhouse and combo failed. Music later accelerated, matching an earlier class of unsafe frontend/runtime corruption symptoms. The exact audio corruption writer was not isolated. The bespoke standalone bootstrap/composition is rejected and was not carried forward.
  • v02 β€” Partial Runtime-confirmed / superseded. SHA-256 b36bc02366040cd1b44b780fa5753ece0f10046dbb8629518f824dc130fcea08, CRC1/CRC2 5A438C7E / AA682D64. Rebuilt on the production Runtime-V2/file-0x1A lifecycle; music remained normal and somersault Down+Attack was fixed. Sweep, Roundhouse and combo still failed because the helper loaded the address 0x802C1AC0 but omitted the dereference needed to obtain the actual player controller.
  • v03 β€” Partial Runtime-confirmed / superseded. Artifact MKMSZR_attack-modern_common-proof_v03.z64, SHA-256 794ded0efcc1907d6899e25c5c01f81ddfbcb585d6deeb5a092342370f352ffd, CRC1/CRC2 5A438C7E / AA682D64. Adding the missing pointer dereference made Sweep and Roundhouse work, but exposed the sticky-+0x6FC error: attacks after a completed backward walk kept producing kicks, and the combo still did not work.
  • v04 β€” Runtime-confirmed accepted gameplay proof. Artifact MKMSZR_attack-modern_common-proof_v04.z64, SHA-256 914905a6d880c0533fbfc7fce3223d1f248c1f1aaefc437f6b2997b8ed343abc, CRC1/CRC2 A8B1BCB0 / C6D26323. v04 replaces the sticky locomotion test with the live normal-map + held-horizontal + +0x704 predicate and moves combo assist into the stock parser. The user reported the complete behavior works perfectly: neutral HP, Block+Attack LP/grab, straight-air and somersault attacks, Sweep, Roundhouse, no stale last-direction behavior, and the full Attack x5 -> Back+Attack stock combo. Music remained normal. Runtime confirmation is bounded to the exercised routes; it is not exhaustive stage/enemy coverage.

v04 proof layout and production boundary

v04 extends the existing shared proof file 0x1A only through ROM 0x00F685B0 / runtime 0x801AFDD0, still below the established Toasty runtime base 0x801B0000. Its high-ROM helper layout is proof evidence, not production ownership:

  • event helper: ROM 0x00F68410 / runtime 0x801AFC30;
  • inherited Block helper: ROM 0x00F68510 / runtime 0x801AFD30;
  • combo helper: ROM 0x00F68578 / runtime 0x801AFD98;
  • combo trampoline: ROM 0x0009AEC0 / VA 0x8009A2C0.

The combo trampoline is inside the production bootstrap-composite ownership range. Its success in the disposable proof does not make that address reusable production space.

Full-composition check v01 (2026-09-26)

Runtime-confirmed, bounded disposable composition. From main 70ac6ba6bf661e6fcbbf4417dffbf2b4881e1f20, build_attack_modern_production_check_v01.py built MKMSZR_attack-modern_full-composition-check_v01.z64 using the clean USA Rev. 0 ROM (SHA-256 9c18254abf6722b95aa782fcd310bd95f6bcf147da66beb77ce32ca90673ffc6), MKT USA Rev. 2 donor (SHA-256 30efdbe266dda8b8b12652a8d0a71b3b4bfec88bdf11ed12c219f5c4e1eaf7bb), and seed ATTACKMODERN01. Output SHA-256 is 797ed2cae81bbe99f9b2a0edd49f535945ece4f4026e12a5208c578871d1105a; CRC1/CRC2 are 4DCE4172 / 5EE0A00D. The user reported β€œEverything working as expected” after the requested manual check. That is confirmation of this artifact, not separately itemized observations for every suggested control, stage, or Toasty trigger; the assistant did not run an emulator.

The builder reuses the exact v04 event, Block, and combo helper bytes (each hash-checked against the accepted v04 proof) after the 0x404-byte production TURN prefix in shared file 0x1A. The low file extends through ROM 0x00F68600 / cached runtime 0x801AFE20; optional Toasty still begins at ROM 0x00F687E0 / runtime 0x801B0000. The event helper occupies 0x801AFC30..0x801AFD2F, Block 0x801AFD30..0x801AFD97, combo 0x801AFDA0..0x801AFDD3, and the capture/event dispatcher 0x801AFDE0..0x801AFE1F. An ordinary no-donor composition was also built and statically checked against the same low file contents; its runtime is untested.

This check does not use v04's conflicting 0x9AEC0 trampoline. It reroutes the already-owned pickup-capture trampoline at ROM [0x9AE18,0x9AE28) through a KSEG1 capture/event dispatcher that preserves the original capture caller's t0 and return address; the event-0 JAL shares that entry. Two guarded, proof-specific frontend tails route Block and combo to their KSEG1 helpers: ROM [0x77688,0x77694) and [0x775D0,0x775DC) respectively. Their hooks preload the high target word in the delay slot. The disposable frontend says ATTACK: CLASSIC / MODERN while reusing durable bit 0x0400 and the existing JUMP dependency. The builder guarded all three stock hook pairs, the production capture stub, shared file/table ownership, both frontend tails, and the label/value pointer; an independent rebuild was byte-identical and the changed-byte audit was bounded to those edits plus header CRC. These guards validate this artifact; they are not yet normal browser/CLI source tests.

Production integration plan

The production integration must preserve v04 semantics exactly; it is not an opportunity to redesign ATTACK: MODERN.

  1. Frontend/state: reuse durable bit 0x0400; rename the user-facing row from COMBOS: CLASSIC / ASSIST to ATTACK: CLASSIC / MODERN. JUMP remains enabled/editable only when ATTACK=MODERN AND SPECIALS=MODERN. An internal constant rename may follow as cleanup, but changing the bit or persistence semantics is unnecessary.
  2. Shared expansion composition: append the ATTACK event, Block and combo helpers after the existing production TURN module through the normal ExpansionPoolAllocator / shared file-0x1A composition. Do not copy v04's literal proof offsets. The resulting low module must remain below Toasty 0x801B0000, and optional Toasty must still start at its established aligned base.
  3. Guarded hook seams: compose the accepted event-0 dispatcher hook, Block-to-LP seam, and combo-parser hook with exact expected-byte guards. CLASSIC paths must replay stock semantics byte-for-byte. The v04 combo-parser seam is ROM 0x0004E3AC / VA 0x8004D7AC; the Block seam remains VA 0x80029C48; the event-0 dispatcher seam remains VA 0x80014B38.
  4. Production trampoline capacity: do not promote v04's 0x9AEC0 proof trampoline. The current bootstrap composite owns that region. Either compose additional KSEG0 trampoline capacity inside an explicitly owned/refactored production region with CI bounds, or establish another production-safe entry mechanism. No zero/padding assumption is sufficient.
  5. Implementation/CI gate: add exact hook guards, helper/classic-fallback tests, state-bit/UI tests, deterministic module packing, expansion-pool bounds, shared file-0x1A end checks, and explicit no-overlap assertions against Toasty. Build both ordinary and Toasty-enabled compositions statically.
  6. Disposable production-composition proof: the Toasty-enabled check v01 has a positive user runtime report, and the ordinary no-donor composition has passed static checks. The report does not itemize each suggested manual route; retain that limit when citing it. Carry the accepted composition into the patch core with guarded tests before calling it production.

The v04 semantics and check-v01 composition are accepted runtime proof baselines. The complete ATTACK: MODERN behavior is now in the shared browser/CLI patch core; the later v02 controls/CI4 composition is the bounded full-product runtime baseline.

SPECIALS: MODERN + JUMP: BUTTON control-suite proof line (2026-09-26)

Stock-special reconciliation

A fresh clean-ROM trace reconciled the stock special parser before implementing MODERN recognition. The descriptor parser at 0x8004A0E0 advances records by exactly 0x2C; matcher 0x80049E60, transfer dispatcher 0x80049D14, and callback installer 0x80032CD4 remain the stock authority. The relevant stock commands are:

Ability Stock command / route Native condition/action facts
Ice Blast Down -> Forward + LP LP record at 0x800B0F38; condition 0x8004A304; callback 0x80049C34; normal Ice root 0x8004AB84; tier >=1; cost 0x20
Slide Back + Block + LP + LK Direct recognizer 0x8003C9F8, not a descriptor record; callback 0x8003CFAC -> action index 0x10 -> 0x80059FF0; tier >=2; no Ice-resource cost observed in the traced root
Directional Ice β€” Up Down -> Forward + HK HK table at 0x800B0F98; condition 0x8004A434; callback 0x80049C84, Ice mode 1; tier >=3; cost 0x20
Directional Ice β€” Down Down -> Back + LK sole LK record at 0x800B0F68; condition 0x8004A4A4; callback 0x80049CB0, Ice mode 2; tier >=3; cost 0x60
Air Ice Blast Down -> Forward + LP while airborne same LP callback 0x80049C34, selector 2 -> root 0x8004B1D0; tier >=4; cost 0x20
Ice Clone Down -> Back + LP LP record at 0x800B0F0C; condition 0x8004A22C; callback 0x8003CF8C -> action index 4 -> 0x80059750; tier >=5; cost 0x60
Ice Shatter contextual after two Ice Blasts Not a standalone special descriptor; qualifying stock attack/reaction paths gate it at tier >=6. MODERN must preserve this contextual behavior rather than assign a Special-button command.
Super Slide Back + Block + LP + HP Direct recognizer 0x8003CAF4; callback 0x8005A5C0; tier >=7; cost 0x60
Freeze on Contact Down -> Forward -> Forward + HP HP record at 0x800B1080; condition 0x8005A9AC; action 0x8005A7B4; tier >=8; cost 0x80
Polar Blast Forward -> Back -> Back + HP HP record at 0x800B1054; condition 0x8004A684; action/root 0x8004BD28; tier >=9; cost 0xA6
Spine Rip Forward -> Down -> Forward + HP HP record at 0x800B1028; condition 0x8004A514; callback 0x80049BE8; fatality/context checks rather than progression-tier gating

This reconciliation supersedes the earlier claim that 0x800B0F68 contained two LK records and also corrects the earlier misidentification of the first HP record as Ice Shatter: that record is Spine Rip. Native progression evaluator 0x80074FBC returns tiers 0..9; the established thresholds remain 85, 258, 834, 1410, 2323, 3315, 4503, 5911, and 7354 XP.

Accepted SPECIALS: MODERN mapping

The physical button normally mapped to LP is the Special Button. SPECIALS: CLASSIC leaves vanilla recognition intact. SPECIALS: MODERN bypasses the old directional/chord recognizers for the ordinary special family and uses the following direct mapping while preserving native condition checks, costs, callbacks, scheduler/action ownership, and contextual Ice Shatter behavior:

Context MODERN input Result
Ground Special Ice Blast
Ground or air Back + Special Ice Clone
Ground Forward + Special Slide
Ground Block + Forward + Special Directional Ice β€” Up
Ground Block + Back + Special Directional Ice β€” Down
Ground Action + Special Freeze on Contact
Ground Run + Forward + Special Super Slide
Ground Block + Special Polar Blast
Air Special Air Ice Blast
Fatality context Block + Run + Special Spine Rip, only when the native fatality condition accepts

Forward/Back are facing-relative. The proof explicitly gives airborne Back+Special priority over bare airborne Special so Clone wins instead of Air Ice Blast. The old Up+Special / Down+Special candidates are rejected as ergonomic mappings because their useful window was only the few frames before stock jump/crouch took ownership.

In SPECIALS: MODERN, the Special/LP button itself no longer performs ordinary LP. Block + Attack is the sole accepted LP macro. The temporary Run + Forward + Attack -> LP route was removed from both ATTACK and SPECIALS paths after runtime review and must not return. ATTACK: MODERN also suppresses ordinary raw LP/LK/HK gameplay attacks while leaving semantic history available where stock systems need it.

Block startup cancellation and JUMP: BUTTON

Stock Block startup at 0x80032810 plays its animation synchronously; polling only the later Block loop cannot cancel the wind-up. The earlier v05/v06 helper attempted an equivalent bounded startup loop, but post-v09 reconciliation showed its controller+0x6DC == 0x000B gate was dead on the real player path because the main dispatcher clears +0x6DC at 0x80028F94 before the normal Block call. v10 is the Runtime-confirmed correction: it identifies the exact normal-player caller 0x80029C28 -> 0x80032810 by return address 0x80029C30, retains the actual-player and MODERN-control gates, reproduces stock startup cadence with per-tick polling, and enters the same proven LP route at 0x8002A0B4. The user confirmed the Block transition can now be cancelled correctly.

JUMP: BUTTON uses both LK and HK as Jump. The accepted v05 behavior is runtime-confirmed from standing, forward movement, backward movement, and Run; ordinary D-pad Up no longer initiates those locomotion jumps while BUTTON mode is active. The moving/running route is tied to the native diagonal-jump classifier rather than only the standing Up branch.

Proof v06 extends the accepted behavior to the ledge-hanging state 0x030F: LK or HK invokes the exact native Up action used by the hanging loop. Existing contextual D-pad Up behavior in that ledge state is intentionally left stock. The same v06 state gate allows Block + Forward + Special -> Directional Ice Up while hanging, using the normal facing-relative Forward classification and the stock Directional-Ice-Up condition/cost/action route.

Control proof chronology and accepted baseline

  • SPECIALS v01 β€” partial Runtime-confirmed / superseded. MKMSZR_specials-modern_common-proof_v01.z64, SHA-256 a29f31545fca6279aee2b5a21c762fd562074eb73ce365c57233f8256f3f9109. Core callbacks worked, but Up/Down inputs raced jump/crouch, Air Clone lost priority to Air Ice, and Attack+Special was not a usable LP macro.
  • SPECIALS v02 β€” partial Runtime-confirmed / superseded. SHA-256 bc54bc84aada41a83f44f4150a2ae66615087e1d119e2b4b8548055711bd118b. Revised chords and Air Clone worked, but facing-relative Forward/Back was wrong because the proof read the 16-bit fighter-facing field with a 32-bit lw.
  • SPECIALS v03 β€” Runtime-confirmed / superseded by later control composition. MKMSZR_specials-modern_common-proof_v03.z64, SHA-256 5511a708945b1c5188176f9afd431d76a2df17241c337ab50ec68a4e118c59fd. Correct lhu facing read fixed both facings; Run+Forward+Attack -> LP was removed, leaving Block+Attack as the sole LP macro.
  • Controls v04 β€” partial Runtime-confirmed / superseded. MKMSZR_controls-modern_final-proof_v04.z64, SHA-256 e14efaf99f8ff3a3d8638cd21f9e96ea6464b9b6bc65e59eaef152ad68a895e7. Standing BUTTON jump worked, but moving/running jump and Block-startup cancellation were incomplete.
  • Controls v05 β€” Runtime-confirmed / superseded. MKMSZR_controls-modern_final-proof_v05.z64, SHA-256 e4dd8f5caaa9b5d3289fcd3cfcc906aa02827f35b4370357a361588c1aea788f, CRC1/CRC2 BF447A12 / 517A0FBC. BUTTON jump behavior was confirmed, but the later v09 retest exposed that the intended Block-startup cancellation gate had never owned the actual player startup path.
  • Controls v06 β€” Runtime-confirmed / superseded by v10. MKMSZR_controls-modern_ledge-extension-proof_v06.z64, SHA-256 7ea9cf1d606e303fe04a67bc40e76940a90a3ff6232a8f301a8a35eab809c411, CRC1/CRC2 8C209838 / 1D5CDE46. Adds ledge-button Jump and ledge Directional Ice Up; all non-Block-startup ATTACK/SPECIALS/JUMP behavior remains inherited by v10.
  • Controls v09 β€” Runtime-confirmed frontend baseline. MKMSZR_controls-run-frontend_common-proof_v09.z64, SHA-256 430c16973e247da5d8fd56b4ec9410c160642eed653a59cbd106efed80fa12dd, CRC1/CRC2 DAE24212 / 236EC8FA. The clean one-line TURN / ATTACK / SPECIALS / JUMP / RUN / EXIT frontend, six cursor positions, edits, persistence and existing gameplay were manually validated; RUN gameplay was intentionally absent.
  • Controls v10 β€” Runtime-confirmed accepted complete proof baseline. MKMSZR_controls-run-gameplay_common-proof_v10.z64, SHA-256 7081c1f9c4629fd88eab78d3c04d72342960ed404d7494421aa8b1f14ba8b325, CRC1/CRC2 A9978A81 / CF350A67. Adds corrected Block-startup cancellation and the final RUN:HOLD/AUTO runtime path on top of v09. The user reported: β€œIt works perfectly!” This supersedes v06 as the accepted complete control-suite proof baseline.

The SPECIALS/JUMP research builders temporarily forced current XP to 20,000 while SPECIALS: MODERN was active so all native power tiers were immediately testable. That is strictly a proof-only diagnostic convenience and is absent from the browser/CLI product. Production uses the player's real progression state and native tier checks.

Runtime-confirmed progression regression (2026-09-26): the first 16-KiB/CI4 full-product candidate removed the XP-force helper, exposing that MODERN Slide and Super Slide still bypassed their stock progression recognizers and called their action callbacks directly. Static reconciliation confirms these are the only two MODERN progression-sensitive routes that did not already use native condition functions. The corrected v02 production composition routes Slide through stock direct recognizer 0x8003C9F8 (native tier >=2 plus contextual gates) and Super Slide through 0x8003CAF4 (native tier >=7, resource >=0x60, plus contextual gates). The user reported the corrected replacement v02 full-product ROM β€œWorks perfectly!” It is Runtime-confirmed for the tested progression routes. The accepted seed CONTROLSV10CI4 / MKT Rev. 2 donor build, SHA-256 b12ed90201ab754aac542b7b89458735b5a756f480fffca89c129c0ed3173147, remains the Runtime-confirmed baseline. Current builds intentionally differ only in the approved Toasty four-word TLUT purple polish.

Ice Blast -> Pickup cancel β€” post-1.0 Runtime-confirmed proof

A bounded 2026-10-02 Wind proof Runtime-confirms the requested normal ground Ice Blast -> Pickup cancel. The proof does not invent a pickup animation or bypass spatial/input checks. Instead, it broadens the ordinary pickup manager's action-eligibility gate at ROM 0x00039AF8 / VA 0x80038EF8: stock pickup state 0x303 remains accepted, and normal Ice is additionally accepted only when controller +0x6F0 == 0x8004AB84 and native Ice mode 0x80111F98 == 0. This excludes Directional Ice modes 1/2; Air Ice uses a different action root.

Only after the stock Pickup input/proximity/facing gates have passed does the proof hook ROM 0x00039BA4 / VA 0x80038FA4, recheck normal Ice, and clear special-action lock 0x800BF308. Stock pickup-manager code then performs the ordinary pickup transition and award path. The user reported the proof worked perfectly on the tested Wind route. This is intentionally 4Fun / Post-1.0, not a 1.0 control requirement and not yet a production allocation/composition.

Reproducer: tools/build_iceblast_pickup_cancel_proof.py. Exact artifact identity, placement-probe context, and evidence limits are in Pickup placement and Ice-Blast cancel proofs.

RUN: HOLD / AUTO β€” Runtime-confirmed proof semantics

The accepted behavior is now Runtime-confirmed in control-suite v10:

  • HOLD: vanilla Run behavior.
  • AUTO: ordinary horizontal locomotion defaults to Run; holding the configured/remapped physical Run button acts as a temporary Walk modifier.
  • Pressing Run during an already-active AUTO run transitions to Walk without requiring a stop; releasing Run while movement continues returns to Run.
  • The real semantic Run bit remains available for Run-based attack/special chords.
  • Vanilla full-analog auto-run synthesis remains intact.
  • Durable proof state uses bit 0x2000 in 0x800A60E8; the five-setting frontend persists it across menu exit/re-entry and box switching.

v07 β€” Rejected / failed. MKMSZR_controls-run-auto_common-proof_v07.z64, SHA-256 a476315ddb64250855f65f7b500597d04f094e448a42942c10af55684d1c67a9. It over-compressed the menu, overwrote ATTACK gameplay tails including Block, globally inverted merged semantic Run, failed live Run->Walk, and broke analog auto-run.

v08 β€” Rejected / failed. MKMSZR_controls-run-auto_common-proof_v08.z64, SHA-256 1967cbe4902d2b10ddca461714edb8e1fbe229fd61f9f55bf6bfe516d7ecfd4e. Block ownership was repaired, but GAME SETTINGS entry hard-hung because the draw helper reused caller-saved t0 after 0x8001CA88 clobbered it. Its active-run hook at 0x800296E8 was also rejected because it could bypass the accepted running-jump classifier.

v09 β€” Runtime-confirmed frontend. The corrected frontend rebuilds volatile bases after renderer calls, preserves the ATTACK tails, keeps type-2 cursor indices to 0..3, and maps RUN/EXIT through existing type-0 coordinates. The user confirmed the new menu works and looks correct. RUN state toggling/persistence was confirmed while locomotion intentionally remained stock.

v10 β€” Runtime-confirmed gameplay. The final runtime design leaves stock predicate 0x8002EAFC byte-for-byte intact. At 0x80015B4C, after remapping but before the later analog-generated Run merge, it snapshots semantic Run bit 0x0004 as the configured physical/remapped Run-button state. The four stock locomotion Run-predicate call sites are wrapped locally: HOLD returns the stock predicate result; AUTO for the actual player returns Run when the physical button is released and Walk when it is held. Active AUTO Run->Walk is interposed at the post-classifier back-edge 0x80029778 / ROM 0x0002A378, after 0x800296E8 -> 0x8002B1F8, preserving running JUMP:BUTTON priority. Walk re-entry uses 0x800293A8, whose accepted TURN decision path reconstructs the needed direction classification.

The v10 standalone no-Toasty proof originally overlapped the earlier CI8 Toasty base. The accepted v02 composition repacks Toasty to CI4 at 0x801B1000, keeps the controls below 0x801B0880, and preserves the 16 KiB arena reservation. The normal shared builder now emits that confirmed composition.

Host action primitives

Address Correct MKMSZ meaning
0x8002B1EC Player velocity helper; writes player actor +0x14 and +0x58
0x8002B1C0 Stop/clear linear motion
0x80031D00 Find nearest opponent controller
0x80031EF0 Face opponent
0x8002FE54 Select animation (table,index)
0x800304C0 Advance animation
0x8004CF3C Fighter strike dispatch
0x8002BA04 Collision/damage/reaction core
0x80028794 Process sleep
0x800328FC Exit action / restore control
0x8004AA4C Scheduler context-transfer shim using table 0x800A1050
0x8004AB84 Complete ice-projectile action root
0x80030974 Animation token-0x0B secondary-resource actor handler
0x80031208 Facing-aware actor-relative XY adjustment
0x8004CBC4 Projectile child-process bridge; binds staged secondary actor into a new child controller/process
0x8004CC50 Generic projectile flight/collision loop
0x8004CE6C Projectile strike-record resolver
0x800BF308 Special-action lock; native action roots set it

0x8004B82C is the ice-projectile flight callback. 0x8004CC14 is projectile setup that writes a projectile actor's +0x14 and initializes projectile animation timing through 0x80031724. Neither is the player propulsion helper. The older recommendation to use 0x8004CC14 for player movement is Rejected / failed.

Projectile secondary-actor staging

The stock Zap/Ice path uses two controller fields that alias the same secondary actor at the projectile handoff but serve different lifecycle roles:

  • animation token 0x0B -> 0x80030974 -> 0x80034510 creates a resource-backed actor and temporarily installs it at current controller +0x6E0;
  • 0x80030974 stores that actor at +0x714, restores the owner to +0x6E0, then copies +0x714 -> +0x648;
  • stock straight-Ice placement at 0x8004AFF8 passes +0x648 to 0x80031208;
  • the same action later uses +0x714 as the temporary current actor for animation;
  • 0x8004CBC4 gives parent +0x714 to child controller +0x6E0, tags the actor 0x700, and clears parent +0x648.

Therefore +0x648 is the stable secondary-actor working/staging pointer in this setup, while +0x714 is the active/transfer pointer and may be temporarily repurposed before being restored. The v71 postmortem statically rejects the hypothesis that the stock 0x80031208 call positions a different helper actor from the one handed to the projectile child.

Projectile flight callback and velocity seam

The complete v75 host transaction has three independent actors/process lifetimes. The parent 0x8004AB84 saves owner +0x6E4/+0x714, temporarily sets +0x714 to the value loaded from 0x80291C10 (file-0x87 resource base), +0x6E4 to offset 0xF88, and +0x718 to 6, and starts class-0x100 companion and tint processes. 0x8002830C copies this helper context to its child. Helper 0x80060A3C uses script resource+0xFA0 to construct a new primary and mirror actor; it allocates texture slot 0x200..0x2FF and the stock Ice palette, inserts both actors, advances the primary through 0x800304C0, and copies its body to the mirror with mirror +0xFA=0. It removes both through 0x80024B00, sleeps two ticks, releases the texture through 0x8001C424, and exits. Its traced cleanup does not release the acquired palette handle. 0x8004AB0C calls the blue screen tint and exits without creating an actor. The parent restores its saved +0x714 during its sleep. These helper actors are not the subsequently created projectile.

The initial slot-0x24 runner stops at file-0x87 +0xDB4 before the projectile token. Following the companion sleep, parent advance 0x8004AEDC consumes token +0xDB8 and operand +0xDBC, creating the projectile through 0x80030974 -> 0x80034510 -> 0x800281A0/0x80028128. The token sets +0x714=+0x648 to the new actor, gives it owner +0x98, render record +0x7C, fixed texture slot +0x9C=0x24, stock Ice palette selector +0x9E, owner position and flags, and inserts it at 0x80030AC8. The parent places +0x648 with native 0x80031208(+5,+38). On the ordinary straight branch (0x80111F98=0), 0x8004B020 invokes 0x8004B584 and 0x8004B028 jumps over the proof wrapper at 0x8004B040: the wrapper does not bind the normal v75 rocket. 0x8004B584 temporarily makes +0x714 current at +0x6E0 and runs stock 0x800304C0 at 0x8004B5A0, consuming the next Ice entry +0xDC4; then it restores the owner, checks strike, and normally hands +0x714 to class-0x700 child through 0x8004CBC4 (which clears parent +0x648). Other mode/contact branches have separate behavior.

The straight child 0x8004B82C reads file-0x87 +0xFD0 = +0xD90, sets +0x6E4 to the rocket-loop cursor, calls 0x8004CC14 once to set facing velocity and the 0x80031724 rate (+0x6A6=3, +0x6A8=1), and v75's later wrapper sets initial velocity 0x46666 and child callback +0x70C. The generic 0x8004CC50 loop sleeps one tick; 0x8003174C first consumes the countdown and then calls 0x800304C0 on +0xD90, binding the genuine rocket after the earlier Ice frame has been published. The callback changes velocity, not image or palette. This is the observed late, well-formed v75 rocket's actual path. The parent wrapper at 0x8009A210 is reachable from the nonzero-mode branch, so results from the normal straight shot cannot validate its palette store or frame transaction.

0x8001BDA0 stores actor +0x74 (entry), constructs +0x7C's image pointer, dimensions, facing and anchors from +0x98, and with +0x8C bit 0x40 set increments +0xFA to request a texture decode. It refreshes +0x68/+0x80/+0x6A/+0x6C/+0x6E/+0x70 from texture slot +0x9C and palette selector +0x9E. Constructor 0x800281A0 calls it before the token has finalized slot/selector/owner flags, so its first call is not a complete ready-to-render transaction. 0x800185B8 later consumes +0xFA, decodes record +0x7C with 0x8000322C, and queues slot-0x24 upload through 0x8001D6EC; the frame scheduler drains the queue with 0x8001D7F0. 0x80024650 links the actor into the active movement and render list sorted by +0x5C/+0x30; it neither builds the record nor uploads pixels. Both the Ice entry +0x34A58 and rocket entry +0x4E6E4 have a zero preceding descriptor word. On the +0x8C bit 0x40 path, a same-entry bind and a changed-entry bind with that zero word both increment +0xFA and use +0x9C; the earlier claim that same-shape binding skips a necessary rocket rebuild is false for these entries.

In v83 the raw pre-insertion 0x8001BDA0 changes actor +0x74 but leaves controller +0x6E4 on the Ice script, so 0x8004B5A0 replaces that early bind with Ice. v84 redirects +0x6E4 to +0xD90 and advances the rocket before insertion, but also forces selector zero. v88 repeats the early rocket descriptor/cursor advance while preserving the correct dynamic ROCKET_P selector and still shows one frame of corrupted Sektor-like texture. Therefore descriptor/cursor state alone is not enough to guarantee first-published pixel data. The render path makes the distinction explicit: 0x8001BDA0/0x800304C0 marks actor +0xFA dirty, while the active pass at 0x800185B8 later decodes +0x7C, queues the texture-slot upload through 0x8001D6EC, and only the separate queue drain commits it. v89 Runtime-confirms pre-publication texture-slot readiness as the missing seam: native synchronous 0x8001BF70 prepares finalized slot +0x9C before stock list insertion, 0x8001BDA0 refreshes the actor fields, and the user observes immediate correct rocket presentation with no one-frame corrupted Sektor. This establishes that actor descriptor/palette correctness is insufficient until the slot's pixel data is resident before publication.

Palette selection is a distinct resource transaction: token 0x80030974 calls 0x8001C528(0x800B1A24,0x100,0) and stores the returned handle minus 0x80 at actor +0x9E. The player type-4 constructor instead acquires its source via 0x800B13D0[type]: v75 file-0x87 +0x4584C (table entry +0x45848) contains the resident Sektor colors, but its runtime selector is dynamically allocated, ordinarily in 0xC0..0x1BF. Selector zero maps to handle 0x80; it does not mean β€œthe owner's Sektor palette.” 0x8001BDA0 resolves active +0x80 through 0x80290A02 + selector*8; render upload uses 0x802E73E0 + selector*4. Matching +0x80 to selector zero alone preserves the wrong palette source. 0x8001C64C releases an explicitly owned palette handle when called; neither the traced companion cleanup nor the normal projectile 0x8004CDF8 calls it. The exact contents of slot zero in the runtime captures, and an end-to-end per-shot palette release, are unverified.

Static-confirmed cleanup split: 0x8004CC50 calls 0x8004CDF8 for offscreen/lifetime exit; this removes the projectile actor and terminates the child process. On normal loop return, the Ice child switches scheduler callback to 0x8004BB64. Separately, the parent finishes seven postlaunch animation advances and six sleeps, calls 0x8004B524 to clear 0x800BF308, then 0x800328FC. The companion helper removes its own pair of actors; tint process exits on its own. New donor actions must model each termination path explicitly. The full donor/host map and evidence limits are in MKT adapter primitives.

0x80031394 is not a proven safe drop-in match_ani_points at this seam. Besides copying owner position, its child +0x64 branch can flip facing through 0x8003188C; its other branch dereferences child render record +0x7C and uses anchor offsets. v78 called it on the staged Ice actor before the genuine rocket first descriptor and hard-hung. The exact failing precondition or register state is unresolved, so a generic origin adapter must first establish the actor/descriptor state it requires.

Static-confirmed. The generic projectile flight loop at 0x8004CC50 provides a reusable per-tick callback seam:

loop entry:
    child +0x680 = child +0x70C

each flight iteration:
    sleep 1
    target animation/update work
    offscreen/lifetime checks
    if child +0x680 != 0:
        call child +0x680
    resolve strike record
    run projectile strike/contact path

For the stock Ice/Sektor proof actor, horizontal projectile velocity is actor +0x14. 0x8004CC14(actor, magnitude, rate) is the one-time facing-aware setup helper used by the stock path and also initializes animation timing through 0x80031724. The flight loop itself does not rewrite actor +0x14 between callback invocations. A translated per-tick acceleration callback can therefore mutate only actor +0x14 while preserving the stock lifecycle and animation-rate state.

This distinction explains the v71 failure mode: repeatedly calling 0x8004CC14 from the tick callback also reinitialized animation timing every tick. Direct per-tick +0x14 mutation is the narrower target operation for the current acceleration proof.

Projectile local-position units

0x80031208 consumes signed local whole-coordinate offsets. On the normal freshly-created projectile path (actor +0xDC == 0), it mirrors local X for actor flip, rotates vector (x,y,0) through the actor-local orientation, and adds the resulting integer components to actor world position +0x2C/+0x30/+0x34 after << 8. Target actor positions are therefore 8-fractional-bit fixed-point while this helper's placement arguments are whole local units. Its matrix normalization is unity; there is no additional target scale factor.

For the current Sektor straight-missile case, the donor's already-N64-scaled retail displacement is definitively semantic X=+5, Y=+38: MKT source multi_adjust_xy applies its second argument to oxpos and third to oypos, and retail make_rocket passes a1=5,a2=38 to the same operation. The X/Y-swap hypothesis is therefore Rejected / failed.

Those values map directly to the target helper's offset arguments (+5,+38), with facing remaining the helper's responsibility. However, complete visible spawn equivalence also depends on the projectile's position before that adjustment. MKT performs setup_proj_obj -> get_proj_obj_m -> match_ani_points(owner, projectile) before applying the offset, while the current target proof inherits stock Ice secondary-actor creation/alignment. Equivalence of those pre-adjust base origins is Pending and is the next relevant placement seam; v72 proves stable mirrored offset application, not full donor-equivalent origin setup.

Projectile velocity integration

Static-confirmed. 0x80017F80 is the actor update/integration loop used by the gameplay main loop. For a normal projectile with actor +0xDC == 0, it reads local velocity fields +0x14/+0x18, arithmetic-shifts them by 8, rotates the resulting local vector through actor orientation, and adds three times the transformed integer components into fixed8 world position +0x2C/+0x30/+0x34.

Therefore, at neutral orientation:

delta_world_x_per_actor_update =
    3 * (actor_xvel >> 8) / 256

in world-coordinate units. The low eight velocity bits do not contribute to that update.

The gameplay loop invokes the actor integrator at 0x80014258 and gates the next iteration on the retrace/tick counter at 0x802FCD44; the resulting ordinary rendering/movement cadence is approximately 30 Hz. This is distinct from MKT's 16.16 oxpos += oxvel motion at nominal 60 Hz. The donor-to-target projectile adapter must therefore translate both numeric velocity units and update cadence, not merely copy MKT oxvel.pos values into target actor +0x14.

The exact Sektor resampling policy is owned by MKT adapter primitives. v73 Runtime-confirms that direct per-tick writes to +0x14 are stable, but its raw donor constants are not the final calibrated translation.

The current process/controller pointer is at effective address 0x802ECE20, not 0x802FCE20.

Function-level semantics are indexed in Function registry; this page owns how the helpers compose into the host action lifecycle.

Projectile actor/process host ABI

The v63-v70 Sektor missile work and follow-up static trace expose a target-native projectile split that should be reused by donor adapters rather than treating Ice Blast as one monolithic move.

Static-confirmed target composition:

animation token 0x0B
-> 0x80030974
-> 0x80034510
-> 0x800281A0 -> 0x80028128   ; resource-backed secondary actor
-> stage actor in controller +0x714/+0x648
-> 0x80024650                  ; insert actor

0x80031208(actor, dx, dy)      ; facing-aware relative placement

0x8004CBC4(callback)
-> 0x8002830C(class 0x700, callback)
-> copy parent/opponent context
-> child +0x6E0 = parent staged actor +0x714
-> tag/clear staging state

This establishes distinct host primitives for resource actor creation, relative placement, and child projectile process creation. The exact function meanings remain indexed in Function registry; donor-side semantics and the reusable adapter vocabulary remain in MKT adapter primitives.

The projectile collision/strike path is likewise separate from Ice freeze presentation: 0x8004CC50 drives the generic projectile loop, 0x8004CE6C resolves a fighter-local strike record from the supplied selector, and 0x8004CF3C -> 0x8002BA04 reaches the normal target strike/collision core. A donor strike index is still not portable numerically.

Resolved player animation cursor

The current controller/process animation cursor is +0x6E4. v69 is Runtime-confirmed for one narrow use: after the stock special helper completes, temporarily redirecting +0x6E4 to the imported chest script, advancing once through 0x800304C0, and restoring the cursor displays the genuine chest-open frame on the player without hanging.

That runtime result proves the host cursor/update mechanism only. It does not make a one-frame post-helper injection the final donor-animation runner.

Installer, scheduler, and context-transfer semantics

0x80032CD4 installs a top-level special-action callback. In the observed installed context, the callback begins with $ra equal to the callback entry. A normal jr ra therefore re-enters the callback instead of returning to an ordinary caller.

0x8004AA4C is not a generic action initializer. It transfers scheduler context according to selector table 0x800A1050. Stock selectors 0..2 are defined. The stable Reverse Elbow v6 proof used a deliberately added selector-3 bridge as a proof-specific controlled extension; that does not make selector 3 a stock ABI entry or a production allocation.

A native special action also needs the target action-lock/cleanup conventions. The successful host-side proof set 0x800BF308, kept its loops bounded, and exited through native scheduler/control restoration.

Callback lifetime and cleanup rules

These are durable host-side safety constraints:

  • an installed top-level action callback must not simply return normally;
  • every movement/contact wait loop must be bounded;
  • action exit must transfer through the native scheduler/cleanup path or an equivalently proven target-native path;
  • the native special-action lock must be established/preserved where the action path requires it;
  • interruption/contact branches are lifecycle paths and must reach cleanup, not just switch animation;
  • one observed state byte is not enough to prove an action is still active after interruption.

These rules come from both successful and failed bounded proofs and are applicable to later donor-adapter work regardless of the specific move choreography.

Durable Reverse Elbow host-side findings

The Reverse Elbow experiments are useful here only for what they established about MKMSZ's host action system.

Finding Evidence / implication
Early movement interpretation used projectile setup as propulsion Rejected / failed. 0x8004CC14 is projectile setup; 0x8002B1EC is the player velocity helper.
Experimental victim callback could hard-hang on contact Rejected / failed. Victim/contact work must not be added before its callback/lifetime semantics are understood.
v3 hung immediately with or without an enemy Rejected / failed. Later analysis explains this as installed-callback jr ra self-reentry.
v5 omitted native special lock 0x800BF308 Rejected / failed as a complete lifecycle model; omission is a strong host-side hang risk.
v6 used selector-3 bridge + lock + player velocity + bounded loops + native restore Runtime-confirmed stable lifecycle baseline; repeatable without whole-game hang on the tested route.
v7 added strike 0x14 and a Y-distance gate The Y gate behaved as intended against a jumping enemy; no abnormal jumping-enemy contact was observed on that bounded test.
v8 attempted velocity 0xD0000, locomotion index controller+0x6E6, per-tick reapply, forced-X crossover +0x3800, and expanded diagnostics Not confirmed as an improvement. Runtime testing still showed slow movement/no pass-through; attacks could cancel forward motion while the displayed L 1 state stayed unchanged; expanded HUD diagnostics were absent. These parameters are preserved as failed-proof reproduction data, not accepted movement values.

The durable conclusion is the v6 lifecycle model, not the v8 movement behavior. Direct player-velocity measurement remains the clean way to separate movement calibration from pass-through, victim-hold, and strike behavior.

The old forced-X crossover attempt is not a reusable host primitive and is not accepted donor behavior. Donor-side pass-through/no-repel semantics are documented in MKT adapter primitives.

Current host-side boundaries

Established:

  • descriptor parsing/matching and special-action installation path;
  • bounded scheduler sleep/context transfer primitives;
  • player velocity/stop/facing helpers;
  • animation select/advance helpers and the resolved controller animation cursor at +0x6E4;
  • resource-backed secondary-actor construction, facing-aware relative actor placement, and projectile child-process binding;
  • generic projectile collision/strike resolution path;
  • strike dispatch and collision/reaction entry points;
  • native action lock and exit/control restoration;
  • callback self-reentry hazard and bounded cleanup requirement.

Not established here:

  • a production-ready generic donor action runner;
  • generic donor velocity-unit conversion;
  • a generic no-repel shim;
  • arbitrary donor victim-reaction translation;
  • donor animation control-token translation;
  • a production-ready generic wrapper that composes the identified projectile actor/process primitives with donor callback/state/effect semantics.

Those translation-layer gaps are tracked in MKT adapter primitives.

Related pages