Update function: 0x00411476; status: movement-consumer-partially-grounded
0x004114e2 -> 0x00422d74 input/action mask update0x004114e7 -> 0x00435c04 active object callback update0x004114ec -> 0x00432ff0 object script/timer updateDraw prep: 0x0041150d -> 0x00424a2a
Input packer: 0x00422d91 -> 0x0042f6d2; continuous mask 0x0059e310; edge mask 0x0059e312.
Tile projection: drawX = ((object[+0xe8] - word[0x004576dc]) << 4) + 0x18; drawY = ((object[+0xea] - word[0x004576de]) << 4) + 0x08; both are then shifted left by 16 before storing
Tile size: 16 px; web match: True; draw projection grounded: prep True; party actors True; web source rect selector mapping grounded: True; 8-command cadence grounded: True; web position interpolation still visual approximation: True; exact actor sprite-frame phase still inferred: False.
original actor position model: tile-field-snap-plus-fixed-point-draw-projection; sub-tile interpolation evidence found: False; scope identified actor controller tile mutations +0xe8/+0xea and draw projection writes +0x1c/+0x20. browser interpolation classification: browser-visual-overlay-not-original-proof; overlay detected: True; fixed-point preserved: True; offset field interpolationOffset.
Actor motion: controller 0x0043022d, collision helper 0x004319f8, collision flags 0x0058d7d0/0x0058d7ce; input-driven tile mutation grounded True, original collision flag tests grounded True. Directional cases 4; object overlap response actor-overlap collision response after tile-flag checks; clears latch True. party trail ring 7 slots at 0x00574540/0x00574550/0x00574552/0x00574554; initial cursors [0, 4, 1]; timing leader tile mutation precedes cursor advance/history write; follower reads happen after the leader write in the same actor controller pass. party actor screen projection: party actor draw coordinates are recomputed from tile/trail entries; +0x68 stores direction/animation state; party frame scripts provide the selector phase outside the actor controller.
Sprite frame selector: sprite frame selector grounded True; +0x28 uses low 16 bits plus bits 16..23, indexed through 0x0055abd8; source-rect path grounded True; script/timer 0x00432ff0 -> 0x00402321, table 0x00440538, opcode 0x21 handler 0x004044bd, grounded True; direct +0x68 mapping eliminated True; party +0x68-to-frame-script grounded True; remaining +0x68-to-+0x28 gap the 12-row descriptor table is classified: rows 0..2 ground +0x68 -> +0x64 state-table frame scripts for Ataho/Rinshan/Smashu, while rows 3..11 are battle/effect/object resource descriptors without +0x68 state-table selectors or field-map refs; save-load active-order materialization is grounded, but current selector 2:0 active order and scripted behavior for non-player descriptors remain inferred. +0x28 is the draw frame selector consumed by the bounds/source-rect helper; its low 16 bits select the frame and bits 16..23 select the 0x0055abd8 surface table. The helper also applies per-object +0x30/+0x34 sprite offsets when the object flags request it. The object script/timer path drives +0x28 through opcode 0x21 frame-script commands, while the actor controller writes +0x68 direction/animation state but does not write +0x28 in the identified controller range. Party actor descriptors ground the +0x68-to-+0x64 frame-script table and opcode 0x21 +0x28 selector sequences for Ataho/Rinshan/Smashu; the remaining descriptor rows are classified as battle/effect/object resources without +0x68 state-table selectors.
Party actor frame scripts: descriptor table 0x00442d95; opcode 0x20 handler 0x0040448a; opcode 0x18 handler 0x0040408f; command 18 a0 64 68 <state-table-va>. party actor descriptors initialize +0x64 with opcode 0x20, then use opcode 0x18 command bytes 18 a0 64 68 to select +0x64 = stateTable[object+0x68]; states 1..8 decode to opcode 0x21 +0x28 selector sequences that loop through opcode 0x03; the full 12-row descriptor table is classified so rows 3..11 stay separate as battle/effect/object resource descriptors
Object descriptor table: classified True; rows 12; party state-table rows [0, 1, 2]; non-party +0x68 state tables absent True; non-party field-map CNS refs absent True. the 12-row descriptor table has +0x68 animation state-table selectors only in rows 0..2 for the party walking descriptors; rows 3..11 are battle/effect/object resource descriptors with no +0x68 state-table selector and no field-map CNS refs
Descriptor activation/order: grounded True; count 0x004576e8, order 0x004576e9, slot base 0x00457750; load rebuild 0x0042349c -> 0x00432323, after save reads True, before selector selection True; opcode 0x20 self-mutation eliminated True; current selector active order proven False. save load calls 0x00432323 after count/order and slot blocks are read and before selector pointer selection; later add/remove active descriptor mutations are general-table opcode 0x62/0x63 paths, while the current selector 2:0 active order remains unproven because public samples do not cover it
Map collision loader: 0x0042449c copies CNS layer0 to 0x00595af0 and CNS layer1 collision flags to 0x0058d7d0.
Web movement alignment: 48 ms step from ORIGINAL_FRAME_MS; walk frames 5 (48x64), idle 0, first moving 1; source rect selectors web character source rects use original party actor +0x28 frame selector indices for idle states and the full 8-command timer=1 walking selector cadence; cadence commands 8; frame-quantized interpolation True; wall-slide fallback True; actual movement direction drives facing True; diagonal follow-up True; camera deadzone True {'x': 6, 'y': 5}; deferred map query sync True; original layer1 flag collision mode False; party overlap pass-through default True via actorCollision=1 enables review-only party blocking; partyTrail history True with 7 slots and cursor model True seeded [0, 4, 1], accepted target write True; default collision originalLayer1Flags.
The original per-frame update at 0x00411476 calls the input/action-mask step, active-object callback step, and object script/timer step once for each capped frame. The input step stores a continuous action mask separately from the new-press edge mask, which matches the browser's held-key walking behavior. The draw preparation path maps object tile fields +0xe8/+0xea against save/camera tile globals 0x004576dc/0x004576de, multiplies by 16 pixels, applies small screen biases, and stores fixed-point draw coordinates; the draw dirty-rect helper then consumes those projected +0x1c/+0x20 coordinates. The web runtime uses that actor-controller tile mutation evidence as a 48ms tile step, keeps the original party actor +0x28 idle source rects and full 8-command walking selector cadence for each facing direction, and keeps frame-quantized tile interpolation as a browser visual overlay rather than original sub-tile interpolation proof. The input-driven actor controller at 0x0043022d reads the continuous input mask, writes object tile fields +0xe8/+0xea, and calls the collision helper at 0x004319f8, which checks map collision flag bits from 0x0058d7d0/0x0058d7ce against object footprint fields +0xe6/+0xe7. The map loader at 0x0042449c copies the CNS layer0 word grid to 0x00595af0 and the CNS layer1 word grid directly to 0x0058d7d0, so the original collision flag grid is now mapped back to CNS layer1. The actor controller also maintains a 7-slot tile/direction history ring at 0x00574540/0x00574550/0x00574552/0x00574554 for companion actor following, seeded with per-actor cursors 0,4,1; in the same actor-controller pass it writes the mutated leader tile before follower reads. The browser has a partyTrail cursor-ring model for the same surface and records the accepted movement target into the ring. The actor controller also recomputes party actor draw coordinates from the leader tile or follower trail entry and uses object +0x68 for direction/animation state values 1-8. The object script/timer update at 0x00432ff0 drives +0x28 through +0x62/+0x64 and opcode 0x21 handler 0x004044bd. Party actor descriptors at 0x00442d95 initialize +0x64 through opcode 0x20 handler 0x0040448a, then use opcode 0x18 command bytes 18 a0 64 68 to select state-table frame scripts from object +0x68 for cara_at1/cara_rs1/cara_sm1. Those selected scripts contain opcode 0x21 selector sequences that loop via opcode 0x03. The same 12-row descriptor table is now classified: rows 0..2 are party walking descriptors, while rows 3..11 are battle/effect/object resource descriptors with no +0x68 state-table selector and no field-map CNS refs. Save-load active descriptor materialization is also separated: 0x00432323 rebuilds slot descriptor pointers from count/order after save blocks are read and before selector pointer selection, and later add/remove paths are general-table opcode 0x62/0x63 mutations. The draw helper's +0x28 frame selector, 0x0055abd8 surface-table lookup, source-rect math, and optional +0x30/+0x34 sprite offset path are identified, and the identified actor-controller range does not directly write +0x28, and no original sub-tile interpolation writer has been identified in the actor tile/projection path; current selector 2:0 active order and scripted behavior for non-player descriptors remain unresolved. The browser now keeps the same 8-command timer=1 selector cadence for party actor source rects while a held move chains from one original-frame tile step into the next. The browser uses originalLayer1Flags as the default collision mode for that grid, lets party actors overlap/pass through each other by default, and keeps actorCollision=1 only as a review switch for the old overlap-blocking behavior; tileClassLayer0 remains available as a local-review fallback. Full non-player descriptor behavior still requires more reconstruction.
| check | value |
|---|---|
| updateFunctionMatchesExpected | True |
| perFrameCallsMatchExpected | True |
| drawPrepCallMatchesExpected | True |
| actionPackerCallMatchesExpected | True |
| inputMaskStoresMatchExpected | True |
| cameraGlobalsMatchExpected | True |
| tileProjectionShiftMatchesExpected | True |
| tileProjectionBiasMatchesExpected | True |
| fixedPointShiftMatchesExpected | True |
| webTileSizeMatchesRuntimeProjection | True |
| webOriginalFrameMsMatchesRuntimeTiming | True |
| webDefaultTileStepMatchesOriginalFrame | True |
| webWalkFrameConstantsMatchSpriteSheet | True |
| webFrameQuantizedInterpolationPresent | True |
| webSourceRectSelectorMappingPresent | True |
| webSourceRectSelectorCadencePresent | True |
| webHeldInputTileChainingPresent | True |
| webWallSlideFallbackPresent | True |
| webDefaultCollisionModeIsOriginalLayer1Flags | True |
| webOriginalLayer1FlagCollisionModePresent | False |
| webActorOverlapCollisionModePresent | True |
| webActorOverlapCollisionDefaultDisabled | True |
| webPartyTrailHistoryPresent | True |
| webPartyTrailRingMatchesOriginalSlotCount | True |
| webPartyTrailCursorModelPresent | True |
| webPartyTrailInitialCursorsMatchOriginal | True |
| webPartyTrailRecordsAcceptedMoveTarget | True |
| webPartyTrailCallTimingMatchesOriginalTileMutation | True |
| actorControllerMatchesExpected | True |
| drawProjectionDetailsMatchExpected | True |
| spriteFrameSelectorDetailsMatchExpected | True |
| descriptorActivationOrderPathMatchesExpected | True |
| inputDrivenTileMutationGrounded | True |
| originalPartyTrailRingGrounded | True |
| originalCollisionFlagTestsGrounded | True |
| collisionFlagGridMappedToCnsLayer1 | True |
| character | descriptor | surface | state table | idle state 1 selector | moving state 5 loop |
|---|---|---|---|---|---|
| cara_at1.cns | 0x004f867c | 0x11 | 0x004f8e68 | 0x00110000 | 0x00110004, 0x00110005, 0x00110004, 0x00110000, 0x00110006, 0x00110007, 0x00110006, 0x00110000 |
| cara_rs1.cns | 0x004e75a0 | 0x14 | 0x004e78ec | 0x00140000 | 0x00140004, 0x00140005, 0x00140004, 0x00140000, 0x00140006, 0x00140007, 0x00140006, 0x00140000 |
| cara_sm1.cns | 0x00546004 | 0x17 | 0x0054638c | 0x00170000 | 0x00170004, 0x00170005, 0x00170004, 0x00170000, 0x00170006, 0x00170007, 0x00170006, 0x00170000 |
| index | descriptor | class | linked CNS | +0x68 state table | valid frame initializers |
|---|---|---|---|---|---|
| 0 | 0x004f867c | party-walking-state-table | cara_at1.cns | 0x004f8e68 | 1 |
| 1 | 0x004e75a0 | party-walking-state-table | cara_rs1.cns | 0x004e78ec | 1 |
| 2 | 0x00546004 | party-walking-state-table | cara_sm1.cns | 0x0054638c | 1 |
| 3 | 0x004b218c | battle/effect-resource | btl_etc.cns, btl_efc.cns | - | 0 |
| 4 | 0x004d5868 | battle/effect-resource | btl_at.cns | - | 1 |
| 5 | 0x004da88c | battle/effect-resource | btl_rs.cns | - | 1 |
| 6 | 0x00524060 | battle/effect-resource | btl_sm.cns | - | 1 |
| 7 | 0x004d21c0 | object-resource | zsa_saru.cns | - | 1 |
| 8 | 0x0048c094 | object-resource | zsl_yoi.cns | - | 1 |
| 9 | 0x0055734c | object-resource | zsa_mi.cns | - | 1 |
| 10 | 0x0052dad8 | object-resource | zsa_iwa.cns | - | 1 |
| 11 | 0x004aab00 | object-resource | zsa_kika.cns | - | 1 |
| direction | input latch | blocked fallback | flag bit | flag grid | scan shape | boundary |
|---|---|---|---|---|---|---|
| down | 0x05 | 0x01 | 0x02 | 0x0058d7d0 | horizontal footprint width at bottom edge row | mapHeight - footprintHeight == tileY |
| up | 0x06 | 0x02 | 0x01 | 0x0058d7d0 | horizontal footprint width at top/block row | tileY == 0 |
| left | 0x07 | 0x03 | 0x04 | 0x0058d7d0 | left footprint edge sample at bottom row | tileX == 0 |
| right | 0x08 | 0x04 | 0x08 | 0x0058d7ce | right footprint edge sample through edge-shifted collision table | mapWidth - footprintWidth == tileX |