Skip to content

decomp: clarify function, variable and state names (batch 36) - #274

Merged
pezzadev merged 1502 commits into
lombyte-project:mainfrom
pezzadev:decompilerizer-batch-36
Nov 1, 2026
Merged

pezzadev merged 1502 commits into
lombyte-project:mainfrom
pezzadev:decompilerizer-batch-36

Conversation

@pezzadev

@pezzadev pezzadev commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Before

Many already matching resident and level functions still used address-based names, short temporary names, and numeric moby state comparisons and assignments.

After

Clarify function, parameter, local and global names across the boot executable and level code while preserving retail symbols and existing shared types.

  • Name helpers such as build_axis_rotation_quaternion, move_moby_xy_with_collision_constraints, spawn_timed_hero_relative_particle, and the hero item-moby flag helpers; update their declarations and callers.
  • Replace proven state and selector numbers with named constants, reusing existing enums where available. Share Drek boss states in a header and consolidate repeated declarations used by multiple functions in the same file.
  • Clarify motion, targeting, effects, camera, audio and menu variables. Preserve upstream struct layouts, assembler labels and small-data placement aliases.

This is readability work: it preserves existing byte matching and does not claim additional matched coverage.

Verification

  • make elf ends with PASS: reconstructed boot ELF matches retail
  • make overlays ends with PASS
  • make check passes
  • python3 scripts/check-cleanup.py --base 56671712c514aaf5713c0521a88d7f9443b162e9 reports no new problems

@pezzadev pezzadev changed the title decompilerizer batch 36 decomp: clarify function, variable and state names (batch 36) Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown

Decompilation progress

C_EXACT Change Matched bytes Functions C_FUZZY
All code 100.00 % — 3,547,932 / 3,547,932 B (—) 4,244 / 4,244 (—) 100.00 %
Boot executable 100.00 % — 354,504 / 354,504 B (—) 1,230 / 1,230 (—) 100.00 %
Game 100.00 % — 264,872 / 264,872 B (—) 850 / 850 (—) 100.00 %
SDK 100.00 % — 89,632 / 89,632 B (—) 380 / 380 (—) 100.00 %
Level overlays 100.00 % — 3,193,428 / 3,193,428 B (—) 3,014 / 3,014 (—) 100.00 %
Shared level code 100.00 % — 1,130,492 / 1,130,492 B (—) 1,583 / 1,583 (—) 100.00 %
Per-level code 100.00 % — 2,062,936 / 2,062,936 B (—) 1,431 / 1,431 (—) 100.00 %
Data in C Change Matched bytes
All data 62.81 % — 7,809,078 / 12,433,372 B (—)
Boot executable 26.98 % — 188,645 / 699,136 B (—)
Level overlays 64.94 % — 7,620,433 / 11,734,236 B (—)

No function changed its match state compared with main (9071100).

Compared with main (9071100). C_FUZZY uses main's last measurement.

domain_0_value_0 -> PAL_MODE_DISABLED: Authoritative source src/overlays/shared/game/run_level_main_loop.c SHA-256 04addf2fa2a4192904a17251dac3245e2658768348ef76f846ecbdd4f10efcad uses pal_mode != 0 to select the frame timer divisor. Original retail Ghidra tests iRam0015ed80 != 0; instructions 0x0024623c..0x00246264 load that selector and take the zero branch with divisor 9600.0, while nonzero selects 11520.0. Thus zero is the disabled PAL flag, without identifying the alternative television standard.
domain_1_value_0 -> PROFILER_MODE_DISABLED: The authoritative source binds g_debug_menu.profiler_mode to the selector tested after the mode-zero gate. Original retail Ghidra tests DAT_0016c070 != 0; instructions 0x00246278..0x00246288 load debug-menu offset 0x18 and branch over the callback at 0x00246288 when it is zero. Zero therefore disables the profiler-mode callback in this path. It is a debug profiler selector, not the separately loaded level frame mode or any hero-state ID.
domain_2_value_0 -> PAL_MODE_DISABLED: This second authoritative pal_mode != 0 comparison loads the same retail 0x0015ed80 selector as the earlier divisor test. Original retail Ghidra initializes the threshold to 0x2580 and changes it to 0x2d00 only for nonzero iRam0015ed80. Instructions 0x002462bc..0x002462d8 load that flag, initialize 9600, and movn selects 11520 only when the flag is nonzero. Zero is the same disabled PAL flag and must reuse PAL_MODE_DISABLED.
domain_0_value_255 -> DRAWBRIDGE_MISSION_ALREADY_COMPLETE: Authoritative drawbridge.c SHA-256 77fd89fd548fb57bd92e951d3879f35ed1ded089c6f234352270654fd27d03d4 loads status from visited52[moby->mission + (current_level_index << 4)]. Retail 0x002f5604-0x002f562c computes that mission/level entry, loads its byte and compares against 0xFF. Equality at 0x002f5634-0x002f5658 sets moby+0x20 to established DRAWBRIDGE_COMPLETE (6) and restores extension=20, pitch=1000, yaw=1000 before child placement. Those are the final targets of normal animation at 0x002f5974-0x002f5aa8; normal completion also enters 6 at 0x002f5cf8-0x002f5d24. Original Ghidra agrees. Thus this mission-table sentinel means this drawbridge is already complete, independently of the separate moby state enum.
domain_0_value_2 -> SEEKER_DISPATCH_MODE_SKIP_STATE_UPDATE: Retail 0x002c5a20-0x002c5a24 loads the separate global at 0x0015f5c4 (alternate gp load at 0x002c5a0c); 0x002c5a28 loads 2 and 0x002c5a2c branches directly to the epilogue at 0x002c6210 on equality. The moby state byte is first read at 0x002c5a34 and dispatched at 0x002c5a54, so this value skips the state update switch. Original Ghidra iVar7==2 return and authoritative mode=seeker_state_dispatch_mode; if(mode==2)return agree. This selector is separate from moby->state and all supplied hero enum domains.
domain_2_value_273 -> ROUTE_ENTRY_ADVANCE_LIMIT: Retail 0x002d035c..0x002d0384 indexes D_L00_001B04B0 by vars+0xc0, checks row count, then loads first entry state at row+0x1c. At 0x002d0398 state 0x111 branches to 0x002d03b8/0x002d03bc to invalidate route_index. The following unsigned (state-0x11)<0x100 test at 0x002d039c..0x002d03a4 bounds the advance sequence to 0x11..0x110; 0x002d03ac..0x002d03c4 checks count and increments that sequence by one. Thus 0x111 is its explicit excluded advance limit, which invalidates instead of taking the out-of-range reset-and-clear path at 0x002d03c8..0x002d0404. This names a route-entry boundary, not a hero state.
domain_2_value_1 -> SHRUB_PART_RELEASED: In SHRUB_BREAK_APART_AND_REMOVE, retail 002e946c selects the parent's child-part table at pvars+0x70. 002e9488 loads each child Moby into s2; 002e94a8 loads that child's pvars into s1. 002e94b8-002e94dc stores child-position minus parent-position into child pvars and normalizes it to frame_time*5.0. 002e947c provides 1, 002e94e0 writes it to the CHILD state byte at s2+0x20, and 002e94e4 immediately clears the parent's table slot. Current C expresses exactly this as part->state=1 followed by shrub_control_vars->parts[i].moby=NULL. Thus this is the released child-part state, a separate domain from the parent's state 1; SHRUB_PART_RELEASED describes the proven transition without claiming the unobserved child update lifecycle.
domain_0_value_0 -> GAME_MODE_CONTINUE: 0x001fced8-0x001fcef0 accepts Cross after the delay and stores zero to game_mode; the established FREEZE_MODE_DELAYED_CONTINUE branch then exits. 0x001fd698-0x001fd6c4 resumes sounds/music for that continuation mode.
domain_12_value_0 -> GAME_MODE_CONTINUE: 0x001fd1d0-0x001fd278 selects the confirmed choice, performs resource cleanup and stores zero to game_mode at 0x001fd264 before the callback; this leaves the frozen choice and continues with audio resumption.
domain_13_value_0 -> GAME_MODE_CONTINUE: 0x001fd284-0x001fd2b4 handles the alternate cancelled choice and stores zero to game_mode at 0x001fd288 before optional lap correction; final 0x001fd698-0x001fd6c4 resumes audio in this continuation mode.
domain_1_value_0 -> GAME_MODE_CONTINUE: 0x001fcf20-0x001fcf54 hides hero.moby and disables collision on Triangle, then stores zero to game_mode to continue; the established outer case is FREEZE_MODE_TRIANGLE_HIDE_HERO_CONTINUE.
domain_2_value_0 -> GAME_MODE_CONTINUE: 0x001fcf58-0x001fcf5c branches to 0x001fd0d8-0x001fd0dc on the alternate Cross path, storing zero to game_mode to continue out of FREEZE_MODE_TRIANGLE_HIDE_HERO_CONTINUE.
domain_3_value_0 -> VIDEO_MODE_REQUEST_PAL_DISABLED: Retail 0x001fcfe0-0x001fd004 starts timed preview after fade: sb zero to 0x0016034c. Original retail Ghidra names that exact byte g_bRequestedPalVideoMode and authoritative C names it requested_video_mode. This proves PAL request enabled/disabled, without assuming a particular alternative standard.
domain_4_value_1 -> VIDEO_MODE_REQUEST_PAL_ENABLED: Retail 0x001fd01c-0x001fd02c restores the flag on timeout: sb s2 (known 1) to 0x0016034c. Original retail Ghidra names that exact byte g_bRequestedPalVideoMode and authoritative C names it requested_video_mode. This proves PAL request enabled/disabled, without assuming a particular alternative standard.
domain_5_value_1 -> VIDEO_MODE_REQUEST_PAL_ENABLED: Retail 0x001fd044-0x001fd05c restores the flag on Triangle: sb s2 (known 1) to 0x0016034c. Original retail Ghidra names that exact byte g_bRequestedPalVideoMode and authoritative C names it requested_video_mode. This proves PAL request enabled/disabled, without assuming a particular alternative standard.
domain_6_value_0 -> VIDEO_MODE_REQUEST_PAL_DISABLED: Retail 0x001fd060-0x001fd078 retains the preview choice on Cross: sb zero to 0x0016034c. Original retail Ghidra names that exact byte g_bRequestedPalVideoMode and authoritative C names it requested_video_mode. This proves PAL request enabled/disabled, without assuming a particular alternative standard.
domain_7_value_0 -> GAME_MODE_CONTINUE: 0x001fd0a4-0x001fd0d0 tests Triangle, stores zero to game_mode and sets hero.ship_flags bit 0. This is the established FREEZE_MODE_TRIANGLE_SET_SHIP_FLAG_CONTINUE continuation branch.
domain_8_value_0 -> GAME_MODE_CONTINUE: 0x001fd0d4-0x001fd0dc tests Cross and stores zero to game_mode for the alternate continuation path of FREEZE_MODE_TRIANGLE_SET_SHIP_FLAG_CONTINUE.
domain_9_value_0 -> GAME_MODE_CONTINUE: 0x001fd0e8-0x001fd104 tests pressed Cross and stores zero to game_mode in the established FREEZE_MODE_AWAIT_CROSS_CONTINUE case; final audio resumption follows outside frozen modes.
domain_0_value_8 -> SWING_CAMERA_TYPE_REQUIRE_NONZERO_FOLLOW_MODE: Retail 0x002ec9c4-0x002ec9d8 indexes the camera descriptor table through record+0x84, loads params from descriptor+0x1c, then reads the signed type at params+0x2c. Original Ghidra pfVar1+0xb and the bound SwingCameraParams.type agree; this is not HeroStateId. At 0x002ec9d8-0x002ec9f4 type 8 returns when current_follow_cam->state (+0x70)->tail.mode (+0x230) is zero, so entry requires nonzero follow mode. The same type comparison is reused in the distance, height and offset branches at 0x002ecf58-0x002ecf70, 0x002ecfdc-0x002ecff4 and 0x002ed040-0x002ed058.
domain_10_value_6 -> SWING_CAMERA_TYPE_REQUIRE_ZERO_FOLLOW_MODE: Retail 0x002ec9c4-0x002ec9d8 indexes the camera descriptor table through record+0x84, loads params from descriptor+0x1c, then reads the signed type at params+0x2c. Original Ghidra pfVar1+0xb and the bound SwingCameraParams.type agree; this is not HeroStateId. At 0x002ec9fc-0x002eca10 type 6 returns when current_follow_cam->state (+0x70)->tail.mode (+0x230) is nonzero, so entry requires zero follow mode. The same type comparison is reused in the distance, height and offset branches at 0x002ecf4c-0x002ecf70, 0x002ecfd0-0x002ecff4 and 0x002ed034-0x002ed058.
domain_11_value_8 -> SWING_CAMERA_TYPE_REQUIRE_NONZERO_FOLLOW_MODE: Retail 0x002ec9c4-0x002ec9d8 indexes the camera descriptor table through record+0x84, loads params from descriptor+0x1c, then reads the signed type at params+0x2c. Original Ghidra pfVar1+0xb and the bound SwingCameraParams.type agree; this is not HeroStateId. At 0x002ec9d8-0x002ec9f4 type 8 returns when current_follow_cam->state (+0x70)->tail.mode (+0x230) is zero, so entry requires nonzero follow mode. The same type comparison is reused in the distance, height and offset branches at 0x002ecf58-0x002ecf70, 0x002ecfdc-0x002ecff4 and 0x002ed040-0x002ed058.
domain_12_value_6 -> SWING_CAMERA_TYPE_REQUIRE_ZERO_FOLLOW_MODE: Retail 0x002ec9c4-0x002ec9d8 indexes the camera descriptor table through record+0x84, loads params from descriptor+0x1c, then reads the signed type at params+0x2c. Original Ghidra pfVar1+0xb and the bound SwingCameraParams.type agree; this is not HeroStateId. At 0x002ec9fc-0x002eca10 type 6 returns when current_follow_cam->state (+0x70)->tail.mode (+0x230) is nonzero, so entry requires zero follow mode. The same type comparison is reused in the distance, height and offset branches at 0x002ecf4c-0x002ecf70, 0x002ecfd0-0x002ecff4 and 0x002ed034-0x002ed058.
domain_13_value_8 -> SWING_CAMERA_TYPE_REQUIRE_NONZERO_FOLLOW_MODE: Retail 0x002ec9c4-0x002ec9d8 indexes the camera descriptor table through record+0x84, loads params from descriptor+0x1c, then reads the signed type at params+0x2c. Original Ghidra pfVar1+0xb and the bound SwingCameraParams.type agree; this is not HeroStateId. At 0x002ec9d8-0x002ec9f4 type 8 returns when current_follow_cam->state (+0x70)->tail.mode (+0x230) is zero, so entry requires nonzero follow mode. The same type comparison is reused in the distance, height and offset branches at 0x002ecf58-0x002ecf70, 0x002ecfdc-0x002ecff4 and 0x002ed040-0x002ed058.
domain_1_value_6 -> SWING_CAMERA_TYPE_REQUIRE_ZERO_FOLLOW_MODE: Retail 0x002ec9c4-0x002ec9d8 indexes the camera descriptor table through record+0x84, loads params from descriptor+0x1c, then reads the signed type at params+0x2c. Original Ghidra pfVar1+0xb and the bound SwingCameraParams.type agree; this is not HeroStateId. At 0x002ec9fc-0x002eca10 type 6 returns when current_follow_cam->state (+0x70)->tail.mode (+0x230) is nonzero, so entry requires zero follow mode. The same type comparison is reused in the distance, height and offset branches at 0x002ecf4c-0x002ecf70, 0x002ecfd0-0x002ecff4 and 0x002ed034-0x002ed058.
domain_5_value_9 -> SWING_CAMERA_TYPE_RELEASE_ANGLE_AT_END: Retail 0x002ec9c4-0x002ec9d8 indexes the camera descriptor table through record+0x84, loads params from descriptor+0x1c, then reads the signed type at params+0x2c. Original Ghidra pfVar1+0xb and the bound SwingCameraParams.type agree; this is not HeroStateId. At 0x002ecd68-0x002ece14 type 9 shares the 200-frame end/400-frame restart timing. At 0x002ecef8-0x002ecf2c it joins types 1 and 7 in suppressing set_follow_camera_angle_blend_from_direction when at_end is set, releasing angle control at the end. The slide calls at 0x002ed1b0-0x002ed1f8 select only 1,7,10, so type 9 has no slide qualifier.
domain_7_value_9 -> SWING_CAMERA_TYPE_RELEASE_ANGLE_AT_END: Retail 0x002ec9c4-0x002ec9d8 indexes the camera descriptor table through record+0x84, loads params from descriptor+0x1c, then reads the signed type at params+0x2c. Original Ghidra pfVar1+0xb and the bound SwingCameraParams.type agree; this is not HeroStateId. At 0x002ecd68-0x002ece14 type 9 shares the 200-frame end/400-frame restart timing. At 0x002ecef8-0x002ecf2c it joins types 1 and 7 in suppressing set_follow_camera_angle_blend_from_direction when at_end is set, releasing angle control at the end. The slide calls at 0x002ed1b0-0x002ed1f8 select only 1,7,10, so type 9 has no slide qualifier.
domain_8_value_6 -> SWING_CAMERA_TYPE_REQUIRE_ZERO_FOLLOW_MODE: Retail 0x002ec9c4-0x002ec9d8 indexes the camera descriptor table through record+0x84, loads params from descriptor+0x1c, then reads the signed type at params+0x2c. Original Ghidra pfVar1+0xb and the bound SwingCameraParams.type agree; this is not HeroStateId. At 0x002ec9fc-0x002eca10 type 6 returns when current_follow_cam->state (+0x70)->tail.mode (+0x230) is nonzero, so entry requires zero follow mode. The same type comparison is reused in the distance, height and offset branches at 0x002ecf4c-0x002ecf70, 0x002ecfd0-0x002ecff4 and 0x002ed034-0x002ed058.
domain_9_value_8 -> SWING_CAMERA_TYPE_REQUIRE_NONZERO_FOLLOW_MODE: Retail 0x002ec9c4-0x002ec9d8 indexes the camera descriptor table through record+0x84, loads params from descriptor+0x1c, then reads the signed type at params+0x2c. Original Ghidra pfVar1+0xb and the bound SwingCameraParams.type agree; this is not HeroStateId. At 0x002ec9d8-0x002ec9f4 type 8 returns when current_follow_cam->state (+0x70)->tail.mode (+0x230) is zero, so entry requires nonzero follow mode. The same type comparison is reused in the distance, height and offset branches at 0x002ecf58-0x002ecf70, 0x002ecfdc-0x002ecff4 and 0x002ed040-0x002ed058.
domain_1_value_1 -> GUNSHIP_PART_RELEASED: In the authoritative update_gunship snapshot, part comes from gunship_control_vars->links[i].part in GUNSHIP_BREAK_APART_AND_REMOVE. Retail 0x002e2cb8-0x002e2cd0 loads each linked child from the 16-byte link table. At 0x002e2cf0 its pvars is loaded; 0x002e2d00-0x002e2d24 writes part position minus parent position there and normalizes this outward drift to frame_time*5. At 0x002e2d28 sb s7,0x20(s3) assigns state 1 to that child, immediately followed at 0x002e2d2c by clearing its parent link. Thus this is the linked-part released state, distinct from the parent's primary-path state 1 and all Hero modes.
domain_0_value_3 -> GADGETRON_CEO_DIALOGUE_START_CHALLENGE: Retail 0x002e6840/0x002e684c establishes s3=data+0x20; 0x002e6a8c-0x002e6a98 compares signed short s3+0x36 with 3. Its taken branch saves the dialogue entry, selects entry 2, calls set_race_camera with gadgetron_ceo_challenge_start_pose at 0x002e6acc, then enters CEO challenge monitoring and activates the challenge environment at 0x002e6af8-0x002e6b00. Bounded helper 0x00261888 confirms this same activation short indexes 0x1c-byte dialogue records at 0x00261a50-0x00261a5c. Thus entry 3 triggers starting the challenge, not HERO_STATE_STOP or CEO state 3.
domain_1_value_2 -> GADGETRON_CEO_DIALOGUE_AFTER_CHALLENGE_START: In the authoritative source, activation->mode=2 is executed only in the mode==3 challenge-start branch. Retail 0x002e6aa4-0x002e6ab8 saves the prior entry at s3+4, writes short 2 at s3+0x36 and records the pending time; 0x002e6acc-0x002e6b00 then installs the challenge-start pose and activates challenge monitoring/environment. Helper evidence request 39d7322e0a364d7bacfcce2c376e33be at 0x00261a50-0x00261a5c proves +0x36 is a dialogue-table index. The name describes the entry selected after starting, without borrowing HERO_STATE_RUN, gravity or vehicle constants.
domain_2_value_4 -> GADGETRON_CEO_DIALOGUE_AFTER_CHALLENGE_RETURN: Retail 0x002e6c28-0x002e6c54 handles moby event 1 by returning CEO state to dialogue polling, calling set_race_camera with gadgetron_ceo_challenge_return_pose and clearing the event. If the flag at 0x0013d507 is clear, 0x002e6c68-0x002e6c7c saves the current activation dialogue entry, writes entry 4 to s3+0x36 and sets its activation flag/time before restoring the non-challenge environment. The checked dialogue helper indexes records with that exact +0x36 short at 0x00261a50-0x00261a5c. This proves the event-1 return follow-up dialogue entry, not HERO_STATE_CROUCH, VOICE_SLOT_STOP_REQUESTED or CEO state 4.
domain_0_value_1 -> RYNO_STATE_WAIT_ANIMATION: At 0x002cc620 the weapon Moby is retained in s3; 0x002cc8a8-0x002cc8ac stores 1 at its state byte +0x20 after initialization. The same byte selects the switch at 0x002cc874, whose existing RYNO_STATE_WAIT_ANIMATION case waits for anim_flags bit 2 at 0x002cc8b8-0x002cc8cc.
domain_1_value_2 -> RYNO_STATE_READY_TO_FIRE: Retail 0x002cc8b8-0x002cc8cc tests this weapon's animation flags and stores 2 to s3+0x20 when bit 2 is set. That identical state byte selects the existing RYNO_STATE_READY_TO_FIRE case, which gates firing on pressed input, item availability and ammo at 0x002cc8f8-0x002cc924.
domain_2_value_4 -> RYNO_STATE_FIRE_SALVO: After pressed-fire, item-lock and use_ammo gates at 0x002cc8f8-0x002cc924, retail 0x002cc9bc-0x002cc9c8 writes 4 into weapon Moby s3+0x20 and clears shot timer, slot and shot count. This is the same selector as existing RYNO_STATE_FIRE_SALVO, which emits timer-spaced rockets at 0x002ccb7c-0x002ccce8.
domain_3_value_2 -> RYNO_STATE_READY_TO_FIRE: In the existing RYNO_STATE_READY_TO_FIRE_WITH_RETICLE case, 0x002cca48-0x002cca5c tests held input mask 5 and loads 2 on release, branching to the store at 0x002cceac into weapon Moby s3+0x20. The same state byte dispatches to existing RYNO_STATE_READY_TO_FIRE and its pressed-fire/ammo gates.
domain_4_value_4 -> RYNO_STATE_FIRE_SALVO: The reticle-ready branch passes pressed-input, item-lock and ammo gates at 0x002cca60-0x002cca90; 0x002ccb30-0x002ccb48 stores 4 into weapon Moby s3+0x20 and resets shot timer, slot and shots before starting animation. This selector dispatches to existing RYNO_STATE_FIRE_SALVO at 0x002ccb7c, which repeatedly creates rockets.
domain_5_value_5 -> RYNO_STATE_COOLDOWN: Retail 0x002cccec-0x002ccd24 compares incremented shots against 7, releases the item switch lock, cancels its animation, and stores 5 into weapon Moby s3+0x20 at 0x002ccd14 while initializing the 60-frame timers. The same state selector's existing RYNO_STATE_COOLDOWN case waits for the shot timer at 0x002cce94-0x002cceac.
domain_6_value_2 -> RYNO_STATE_READY_TO_FIRE: The existing RYNO_STATE_COOLDOWN case decrements the shot timer at 0x002cce94-0x002cce9c, then clears target and shots and writes 2 into weapon Moby s3+0x20 at 0x002cceac. This is the same selector dispatched at 0x002cc874, with 2 already canonically named RYNO_STATE_READY_TO_FIRE in the bound C.
domain_0_value_3 -> GROUND_MATERIAL_SINK_SURFACE_PROBE_THROUGH: Retail 0x002403e0-0x002403f0 stores collision_hit_material into hero+0x12E0, proving the material selector. At 0x002404dc-0x00240520, material 3 stores collision hit Z into hero+0x2F4 (source hero.sink_surface_z), lowers ground_probe_endpoint.z by 0.01, and retries collision_line with mask 4. The supplied same-source material-effects consumer tests selector_3 against sink_surface_z while descending and requests state 0x68 or 0x7B. This proves a sink-surface material probed through for underlying ground, without identifying a particular substance.
domain_1_value_11 -> GROUND_MATERIAL_SINK_SURFACE_CONTACT_BYPASS: The collision_hit_material return/store at retail 0x002403e0-0x002403f0 proves hero.ground_type is the collision material domain. At 0x0024052c-0x00240548 material 11 copies collision hit Z into hero+0x2F4 (source hero.sink_surface_z) and branches through 0x00240770 to 0x00240774, bypassing moby/contact point, distance, normal and grounded-counter updates. The supplied same-source material-effects consumer uses selector_11 and sink_surface_z to request state 0x7B for descent below this surface. The name states the proven sink-surface/contact-bypass behavior only.
domain_0_value_1 -> METAL_DETECTOR_ACTIVE: The assigned source already declares METAL_DETECTOR_ACTIVE = 1 for this exact moby->state switch. Retail 0x002d2fd8 loads 1 and 0x002d2fe0 stores it to byte +0x20 of s5, the same detector moby whose selector is loaded at 0x002d2f48. Original Ghidra case 0 performs this assignment after initialization; cases 1/2 enter tracking at 0x002d2fe4. Reuse the existing canonical local member for the assignment.
domain_1_value_254 -> MOBY_STATE_REMOVAL_AT_OR_ABOVE_INSTANCE_END: Retail 0x002d3414 loads hero.metal_detector_target into a0, checks class at +0xa6, then 0x002d342c loads that Moby's state byte at +0x20; 0x002d3430-0x002d3434 rejects 0xFE before collection. Original Ghidra expresses this as target +0x20 == -2. Supplied mark_moby_for_removal source assigns MOBY_STATE_REMOVAL_AT_OR_ABOVE_INSTANCE_END = 0xFE to this same struct Moby state field when moby >= moby_instance_end. The assigned function calls that removal operation on hero.metal_detector_target at 0x002d357c-0x002d3580 after draining its count, proving the generic Moby removal-state domain rather than a hero/control domain.
domain_2_value_253 -> MOBY_STATE_REMOVAL_BELOW_INSTANCE_END: Retail 0x002d3414 loads hero.metal_detector_target into a0 and 0x002d342c reads its +0x20 Moby state byte; 0x002d3438-0x002d343c rejects 0xFD before collection. Original Ghidra expresses this same target-state test as == -3. Supplied mark_moby_for_removal source assigns MOBY_STATE_REMOVAL_BELOW_INSTANCE_END = 0xFD to struct Moby.state for moby < moby_instance_end. The target is passed to mark_moby_for_removal at 0x002d357c-0x002d3580 after count drainage. These are the same generic Moby removal sentinels, with the allocation-side distinction proved by the supplied writer source.
domain_0_value_1 -> SPAWNER_BOMB_STATE_SPAWN_CHILD: Retail 002c4934..002c4958 tests the damped motion vector length against 0.025 and stores 1 to the same moby state byte +0x20 dispatched at 002c4770..002c4778. That dispatch reaches ground placement and revive_spawner_pooled_child at 002c4d94..002c4dd8, proving reuse of the existing local SPAWNER_BOMB_STATE_SPAWN_CHILD.
domain_1_value_1 -> SPAWNER_BOMB_STATE_SPAWN_CHILD: Retail sphere-collision path 002c4b14..002c4b40 tests damped velocity length below 0.025 and stores 1 to moby+0x20. The identical state selector at 002c4770..002c4778 enters ground placement and pooled-child revival at 002c4d94..002c4dd8, the existing SPAWNER_BOMB_STATE_SPAWN_CHILD branch.
domain_2_value_254 -> MOBY_STATE_REMOVAL_AT_OR_ABOVE_INSTANCE_END: Retail 002c4c18..002c4c58 traverses the Moby list through +0x28 and reads each neighbor's actual state byte at +0x20, skipping 0xfe before position repulsion. Supplied mark_moby_for_removal assigns MOBY_STATE_REMOVAL_AT_OR_ABOVE_INSTANCE_END=0xFE to this same struct Moby state field for mobys at or above moby_instance_end; this is the removal sentinel domain.
domain_3_value_253 -> MOBY_STATE_REMOVAL_BELOW_INSTANCE_END: Retail 002c4c50..002c4c5c reads the linked neighbor Moby's state byte +0x20 and skips 0xfd before applying position repulsion. Supplied mark_moby_for_removal writes MOBY_STATE_REMOVAL_BELOW_INSTANCE_END=0xFD to struct Moby.state for mobys below moby_instance_end, proving the removal-state meaning of this comparison.
domain_4_value_2 -> SPAWNER_BOMB_STATE_SHRINKING: Retail 002c4cd8..002c4ce8 decrements the timer at pvars+0x14 and on expiry writes 2 to moby+0x20. State dispatch 002c479c sends that same byte value to 002c4de8, which reduces bomb scale and optionally grows the held child, exactly the existing local SPAWNER_BOMB_STATE_SHRINKING branch; expiry need not create a child.
domain_5_value_2 -> SPAWNER_BOMB_STATE_SHRINKING: Retail 002c4dd0..002c4de4 calls revive_spawner_pooled_child, saves its returned child at pvars+0x18, and writes 2 to the bomb's state byte +0x20. The same byte is dispatched at 002c479c to bomb shrinkage and optional held-child growth at 002c4de8..002c4e3c, proving reuse of SPAWNER_BOMB_STATE_SHRINKING.
domain_6_value_254 -> MOBY_STATE_REMOVAL_AT_OR_ABOVE_INSTANCE_END: Retail 002c4dd8 stores the revived struct Moby child at pvars+0x18; 002c4e8c..002c4e9c reloads that child, reads its state byte +0x20 and removes the shrinking bomb when it is 0xfe. Supplied mark_moby_for_removal writes MOBY_STATE_REMOVAL_AT_OR_ABOVE_INSTANCE_END=0xFE to that same Moby state field, proving the removal sentinel rather than a bomb-local state.
domain_7_value_253 -> MOBY_STATE_REMOVAL_BELOW_INSTANCE_END: Retail 002c4e8c..002c4ea4 reloads the child stored from revive_spawner_pooled_child at pvars+0x18, reads its actual Moby state byte +0x20, and branches to bomb removal when it equals 0xfd. Supplied mark_moby_for_removal assigns MOBY_STATE_REMOVAL_BELOW_INSTANCE_END=0xFD to that same field for below-instance-end mobys, establishing the removal-state domain.
domain_0_value_2 -> MINE_GLOVE_IDLE: At 002c2280 initialization clears held; 002c2284..002c2298 tests glove anim_flags bit 2 and stores 2 into 0x20(s5) when animation waiting is unnecessary. s5 is the assigned moby (002c1adc), and 002c2258 loads that same byte as the local glove switch selector. Source state 2 has no specialized switch action, the passive idle successor also reached after post-release waiting.
domain_1_value_2 -> MINE_GLOVE_IDLE: The MINE_GLOVE_WAIT_FOR_ANIMATION branch at 002c229c..002c22b0 tests 0x70(s5) bit 2 and stores 2 to the exact glove switch selector 0x20(s5) on completion of the wait. In the authoritative switch, 2 has no specialized action; it is the same passive idle phase selected by initialization and by completion of post-release waiting, rather than a hero state or controller domain.
domain_2_value_2 -> MINE_GLOVE_IDLE: At 002c23a0..002c23c4 the MINE_GLOVE_POST_RELEASE_WAIT branch retains state 4 while hero current state is 0x23 or item animation is active, then stores 2 to 0x20(s5), the glove's own selector loaded at 002c2258. This returns to the same passive idle phase reached after initial animation waiting; source switch state 2 has no specialized action, while common aiming and reacquisition continue.
domain_0_value_1 -> BEAM_BARRIER_ACTIVE: Retail 002f6480 retains the input moby in s3; 002f64b4 reads its state byte at +0x20 and 002f64b8 dispatches value 1 to 002f6554 (voice, strand, collision and linked-completion updates). Initialization stores 1 to that identical +0x20 field at 002f6540. The bound C already names this selector's value 1 BEAM_BARRIER_ACTIVE, proving canonical reuse for the assignment.
domain_2_value_2 -> BEAM_BARRIER_EMIT_SPARK_BURST: After the four linked-moby checks, retail 002f6aac loads 2, 002f6ab0 disables this moby's collision and 002f6ab4 stores 2 at +0x20(s3), the same field dispatched at 002f64b4/002f64dc. That branch starts at 002f6bb0 and emits nine groups of twenty particles through 002f6d74. Bound C already names this exact switch domain's value 2 BEAM_BARRIER_EMIT_SPARK_BURST.
domain_3_value_3 -> BEAM_BARRIER_REMOVE: After the nine-by-twenty particle loop, retail 002f6d78 loads 3 and 002f6d80 stores it to +0x20(s3). This is the original moby's state read at 002f64b4; its value 3 dispatches at 002f64e4 to 002f6d84, which calls mark_moby_for_removal with that same moby. Bound C already names this switch value BEAM_BARRIER_REMOVE, proving reuse for the transition assignment.
domain_0_value_0 -> HERO_VEHICLE_MODE_ON_FOOT: Retail 0x00240f34 establishes a0=&hero; 0x00240f38 loads the byte at hero+0x20a4, mapped by the authoritative source to hero.vehicle_mode. 0x00240f3c branches on zero to continue equipped-item processing, while nonzero returns 0 at 0x00240f44-0x00240f48. This is the HeroVehicleMode selector, not state.current or control_mode. The supplied shared include/rnc/hero/hero_state.h HeroVehicleMode member HERO_VEHICLE_MODE_ON_FOOT=0 canonically names this no-vehicle mode.
domain_2_value_0 -> HERO_VEHICLE_MODE_ON_FOOT: In ITEM_VEHICLE_MODE_TOGGLE, retail 0x002413cc establishes s0=&hero and 0x002413d0 loads hero+0x20a4, the same hero.vehicle_mode byte used by the entry guard at 0x00240f38. 0x002413d4 rejects nonzero mode before can_select_hero_item_31 at 0x002413dc; the successful path requests the vehicle toggle via +0x22dc/+0x22de at 0x00241450-0x00241458. The supplied canonical HeroVehicleMode enum names zero HERO_VEHICLE_MODE_ON_FOOT, matching this on-foot prerequisite.
domain_0_value_254 -> MOBY_STATE_MARKED_FOR_REMOVAL: Target is loaded from MineVars+0x68 at 0x002cc080; lbu 0x20(a1) at 0x002cc08c supplies target_moby->state and comparison against 0xFE at 0x002cc090 excludes homing. Checked level-03 direct helper mark_moby_for_removal (0x0023d6d8, evidence request 824a581d92d64eb48b76cce460ea3c49) sets v0=0xFE at 0x0023d6fc and stores it into the same moby state offset +0x20 at 0x0023d700. The helper is called on this mine after explosion at 0x002cc764 and after shrink at 0x002cc7d8. Thus 0xFE denotes a moby marked for removal; this does not claim it is the only removal state or establish its allocation category.
domain_0_value_1 -> SCREAMER_STATE_WAIT_FOR_ACTIVATION: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002df38c..002df398 writes 1 to +0x20 after home/goal initialization when pvars+0x174 equals 1; case 1 waits on the activation gate before entering state 2.
domain_10_value_9 -> SCREAMER_STATE_WAIT_TO_WANDER: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002dfb68..002dfb88 detects a blocked candidate wander step, clears step and writes 9 to +0x20. Destination 002dfa28 waits on wander_timer before choosing the next random wander goal.
domain_11_value_9 -> SCREAMER_STATE_WAIT_TO_WANDER: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002dfb94..002dfbb8 tests step vector length against zero and writes 9 to +0x20 on zero length. Destination case 9 waits on wander_timer before generating the next home-relative goal.
domain_1_value_2 -> SCREAMER_STATE_WAIT_TO_RISE: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002df3ac..002df3d4 releases the activation gate and stores 2 to +0x20; destination case 2 at 002df3f0 waits for animation flag bit 2 before rising.
domain_2_value_4 -> SCREAMER_STATE_RISE_FROM_HOME: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002df3f0..002df408 gates on animation bit 2 and stores 4 to +0x20 while clearing velocity. Destination 002df424..002df494 advances pos.z toward home.z+2.
domain_3_value_5 -> SCREAMER_STATE_FOLLOW_PATH: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002df470..002df494 stores 5 to +0x20 when the rise scalar completes. Destination 002df498 onward indexes path/node fields +0x150/+0x158 and moves toward that waypoint.
domain_4_value_6 -> SCREAMER_STATE_FACE_LEADER_AT_WAYPOINT: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002df64c..002df694 advances the arrived node index and writes 6 to +0x20 when its marker is positive. Destination 002df724..002df784 turns toward leader loaded from pvars+0x100.
domain_5_value_10 -> SCREAMER_STATE_WANDER: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002df6b0..002df6cc compares the advanced node index with path count and writes 10 to +0x20 at path completion, then initializes home and wander_goal. Destination 002dfabc onward steps toward wander_goal.
domain_6_value_10 -> SCREAMER_STATE_WANDER: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002df7f8..002df824 writes 10 to +0x20 after the leader-facing waypoint action when node_index equals path count; 002df844..002df878 initializes home/wander_goal for destination wander movement.
domain_7_value_5 -> SCREAMER_STATE_FOLLOW_PATH: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002df80c branches to 002df884 when more path nodes remain; v0=5 from 002df810 is stored to +0x20 at 002df890, resuming the existing path-following switch branch.
domain_8_value_7 -> SCREAMER_STATE_IDLE_AT_HOME: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002df9f8..002dfa24 measures XY distance to home and writes 7 to +0x20 when below 2.0. The same selector's existing IDLE_AT_HOME case performs no state-specific work.
domain_9_value_10 -> SCREAMER_STATE_WANDER: Original retail 002df280 retains the moby in s1; 002df310 reads its state byte at +0x20 for this function's switch. The authoritative C declares the destination member in this same screamer-state enum. 002dfa28..002dfab8 waits for wander_timer, creates a random offset added to home into wander_goal (+0x140), then stores 10 to +0x20 to enter the existing goal-stepping wander branch.
domain_1_value_2 -> BOMB_GLOVE_READY: The original a0 moby is retained in s3 at 0x002c26d4; its +0x20 byte is the switch selector at 0x002c2fa8. Initialization clears held at 0x002c2fd0, then stores 2 at 0x002c2fe8 when anim_flags bit 2 is set, otherwise entering BOMB_GLOVE_WAIT_FOR_ANIMATION=1. State 2 has no switch action and is the stable ready state before input/timing consumes ammo and enters RELEASE_HELD_MOBY at 0x002c28cc-0x002c28dc. This is the glove state domain, not a hero or control enum.
domain_2_value_2 -> BOMB_GLOVE_READY: In BOMB_GLOVE_WAIT_FOR_ANIMATION=1, retail 0x002c2fec-0x002c3000 reads original moby s3+0x70, tests animation flag bit 2 and stores 2 into s3+0x20 when it becomes set. This is exactly the switch selector loaded at 0x002c2fa8 and the same stable destination selected by initialization. Therefore reuse BOMB_GLOVE_READY=2 in the function-local glove state domain.
domain_3_value_2 -> BOMB_GLOVE_READY: After release clears held and enters BOMB_GLOVE_POST_RELEASE_WAIT=4 at 0x002c30cc-0x002c30dc, retail 0x002c30e0-0x002c3108 waits until hero.state.current differs from 0x23 and hero.item_anim_active is zero, then stores 2 into original glove s3+0x20. This returns to the same stable ready destination reached after initialization/animation wait, with no state-2 switch action. Reuse BOMB_GLOVE_READY=2; the hero fields are conditions, not the assigned selector.
domain_0_value_3 -> BOLT_PICKUP_CONTROLLED_BY_THIEF: The source list builder selects class 0x279 pickups, not hero or thief objects. Retail 0x002e2eb0-0x002e2ed8 traverses pvars+0x158 and compares each pickup byte +0x20 with 3 before resetting it to 1 on thief knockback. The same pickup byte is set to 3 at 0x002e3244 immediately before the thief's joint-pulling phase, which directly updates pickup positions at 0x002e32c8-0x002e3304. Thus 3 denotes pickups under thief control; its comparison and assignment share this pickup domain.
domain_3_value_3 -> BOLT_PICKUP_CONTROLLED_BY_THIEF: Retail 0x002e321c refreshes the class-0x279 target list; 0x002e3230-0x002e3244 loads each listed pickup and writes 3 to its +0x20 state after the signed-state eligibility test. The thief then enters its own separate state 4 at 0x002e3264. In that phase 0x002e32c8 obtains thief joint 0, 0x002e32dc subtracts pickup position and 0x002e3300 adds the pull displacement directly to pickup position. This proves pickup state 3 is controlled by the thief, distinct from thief state 3.
domain_5_value_4 -> BOLT_PICKUP_COLLECTION_TRIGGERED: Retail 0x002e331c compares the joint-pull displacement length against twice the frame scalar; on reaching the threshold 0x002e332c writes 4 to the listed class-0x279 pickup state, 0x002e3330 clears its target-list slot, and 0x002e3334-0x002e333c increments the thief collection count. Bounded checked direct helper 0x002afcf0, called with the same pickup objects at 0x002e349c, rejects already-state-4 pickups at 0x002afd2c-0x002afd3c, stores the attraction target at pvars+0x60 (0x002afe68), initializes motion and sets pickup state 4 at 0x002afee8. Together these prove collection is triggered, without claiming immediate removal or completed award.
domain_0_value_2 -> TAXI_STATE_HIDDEN: Retail 0x002f06a0-0x002f06c8 finds the group moby by target tag at taxi pvars+0xB6, stores 2 to returned moby+0x20, sets flags bit 0, clears collision at +0x94 and visibility at +0x31. This is a hidden taxi-state assignment, not stop index 2: bounded helper 0x002f1278-0x002f12d0 finds a moby through the same helper/tag, restores its stop pose and calls taxi-stop initializer 0x002f1040 on that returned pointer. Supplied source for that initializer enters TAXI_STATE_WAIT_AT_STOP. Evidence request a87e51b84f594ba39314bcc5961bd24f, report SHA-256 0cc6e3719e9f59ce3c1e7f53375da31a41af2b8c09c1951f77423dfc3b1591ea establishes the selected moby's taxi-state domain.
domain_1_value_2 -> TAXI_STATE_HIDDEN: Retail 0x002f0e70-0x002f0ea0 finds the group moby by taxi target at pvars+0xB6 after the saved-pose route transition, writes 2 to returned moby+0x20, sets flags bit 0 and clears collision +0x94 and visibility +0x31. It performs the same hidden-taxi transition as 0x002f06b8. Bounded helper 0x002f1278-0x002f12d0 uses the identical lookup and target field, restores the returned moby's stop pose and passes that pointer to taxi initializer 0x002f1040, proving this state selector belongs to the taxi domain. Thus both listed stores reuse TAXI_STATE_HIDDEN; none of the supplied Hero constants or TAXI_STOP_RETURN_FEEDER_ORIGIN names this state.
domain_0_value_254 -> MOBY_STATE_REMOVAL_AT_OR_ABOVE_INSTANCE_END: Authoritative source src/overlays/shared/enemies/bomb_spawner_bomb.c SHA-256 687e5798723fc1009b13bc950bd092e9cd7a6b3f2aae46458d5035c80a87631d already declares this canonical name in update_spawner_bomb for the same Moby->state domain. Checked caller 0x002c4dd0 revives the child and stores it at pvars+0x18; 0x002c4e98-0x002c4ea4 reads that child's +0x20 state. Removal sibling 0x0023d6dc loads boundary 0x0015ffdc, 0x0023d6e8 compares the Moby pointer unsigned, and 0x0023d700 writes the selected value to the same +0x20 byte. 0x0023d6ec/0x0023d6fc selects 0xFE when the Moby pointer is at or above the boundary. Assigned retail 0x002becf0-0x002bed10 tests this state on the indexed pooled Moby and permits revival rather than counting it live; original Ghidra reads +0x20 as -2.
domain_1_value_253 -> MOBY_STATE_REMOVAL_BELOW_INSTANCE_END: Authoritative source src/overlays/shared/enemies/bomb_spawner_bomb.c SHA-256 687e5798723fc1009b13bc950bd092e9cd7a6b3f2aae46458d5035c80a87631d already declares this canonical name in update_spawner_bomb for the same Moby->state domain. Checked caller 0x002c4dd0 revives the child and stores it at pvars+0x18; 0x002c4e98-0x002c4ea4 reads that child's +0x20 state. Removal sibling 0x0023d6dc loads boundary 0x0015ffdc, 0x0023d6e8 compares the Moby pointer unsigned, and 0x0023d700 writes the selected value to the same +0x20 byte. 0x0023d6e8-0x0023d6f8 selects 0xFD when the Moby pointer is below the boundary. Assigned retail 0x002becfc branches on this value at the pooled Moby's +0x20 state, permitting revival rather than counting it live; original Ghidra reads it as -3.
domain_2_value_254 -> MOBY_STATE_REMOVAL_AT_OR_ABOVE_INSTANCE_END: Authoritative source src/overlays/shared/enemies/bomb_spawner_bomb.c SHA-256 687e5798723fc1009b13bc950bd092e9cd7a6b3f2aae46458d5035c80a87631d already declares this canonical name in update_spawner_bomb for the same Moby->state domain. Checked caller 0x002c4dd0 revives the child and stores it at pvars+0x18; 0x002c4e98-0x002c4ea4 reads that child's +0x20 state. Removal sibling 0x0023d6dc loads boundary 0x0015ffdc, 0x0023d6e8 compares the Moby pointer unsigned, and 0x0023d700 writes the selected value to the same +0x20 byte. 0x0023d6ec/0x0023d6fc selects 0xFE at or above the boundary. Assigned retail 0x002beecc loads 0xFE and 0x002beee0-0x002beee8 compares linked-list Moby +0x20, skipping this removed class-0x221 Moby in the live count. The linked-list cursor advances via +0x28, not another state domain.
domain_3_value_253 -> MOBY_STATE_REMOVAL_BELOW_INSTANCE_END: Authoritative source src/overlays/shared/enemies/bomb_spawner_bomb.c SHA-256 687e5798723fc1009b13bc950bd092e9cd7a6b3f2aae46458d5035c80a87631d already declares this canonical name in update_spawner_bomb for the same Moby->state domain. Checked caller 0x002c4dd0 revives the child and stores it at pvars+0x18; 0x002c4e98-0x002c4ea4 reads that child's +0x20 state. Removal sibling 0x0023d6dc loads boundary 0x0015ffdc, 0x0023d6e8 compares the Moby pointer unsigned, and 0x0023d700 writes the selected value to the same +0x20 byte. 0x0023d6e8-0x0023d6f8 selects 0xFD below the boundary. Assigned retail 0x002beed0 loads 0xFD and 0x002beeec-0x002beef0 compares linked-list Moby +0x20, skipping this removed class-0x221 Moby in the live count. Original Ghidra confirms the same state byte as -3.
domain_0_value_0 -> WRENCH_MOBY_STATE_READY: Retail 0x002280C0 loads equipped moby from hero+0x1090; 0x002280CC-D4 validates it with is_wrench_moby (class 0x47 in supplied context), then 0x002280DC-E0 loads its state byte +0x20 and rejects nonzero before melee requests or launch_hero_wrench at 0x0022815C. Original Ghidra tests *(char *)(iVar3+0x20) != 0 with the same flow. Checked launch helper 0x00217658 loads that same hero+0x1090 at 0x00217778, validates it at 0x0021777C, and writes 10 to moby+0x20 at 0x002177A0-B0 before initializing throw direction, speed and trail. Thus zero is the wrench moby state ready to accept this attack/throw input, in a distinct object-state domain.
domain_0_value_4 -> TESLA_CLAW_STATE_FINISHED: Retail 002b8814 retains the Tesla Claw Moby in s3; 002b8908 loads its +0x20 state and 002b8904-002b8910 excludes value 4 from firing. The same field is the switch selector at 002b8eac and already has canonical TESLA_CLAW_STATE_FINISHED = 4; completion writes it at 002b92d4.
domain_1_value_0 -> TESLA_CLAW_STATE_INITIALIZE: Retail 002b8914 tests the same s3+0x20 Moby state loaded at 002b8908 against zero to prevent firing during initialization. That exact field selects the switch at 002b8eac; its existing TESLA_CLAW_STATE_INITIALIZE = 0 branch initializes timer, light, targets and locks at 002b8ed4-002b8f2c.
domain_4_value_1 -> TESLA_CLAW_STATE_WAIT_FOR_FIRE: In the existing TESLA_CLAW_STATE_INITIALIZE branch, retail 002b8efc loads 1 and 002b8f08 stores it to s3+0x20, the same Moby state dispatched at 002b8eac. It enters canonical TESLA_CLAW_STATE_WAIT_FOR_FIRE = 1 after timer/light setup and before waiting for active fire.
domain_5_value_3 -> TESLA_CLAW_STATE_FIRING: Within TESLA_CLAW_STATE_WAIT_FOR_FIRE, active firing in s6 passes 002b8f38, starts equipped-item animation at 002b8f40, and writes 3 to s3+0x20 at 002b8f48-002b8f4c. This is the same switch field whose canonical TESLA_CLAW_STATE_FIRING member is 3.
domain_6_value_3 -> TESLA_CLAW_STATE_FIRING: The pressed-fire, unlocked and active path updates firing-start statistics and calls start_equipped_item_animation at 002b9010; 002b901c sets v0 to 3 and 002b917c stores it to the Tesla Claw Moby state at s3+0x20. This enters existing TESLA_CLAW_STATE_FIRING = 3 on the same switch domain.
domain_7_value_1 -> TESLA_CLAW_STATE_WAIT_FOR_FIRE: In the wait-for-fire branch's pressed-button but inactive path, 002b9020-002b9038 clears targets and locks; 002b9024 sets 1 and 002b9044 writes s3+0x20 before canceling item animation at 002b9048. The same Moby state selector already names 1 TESLA_CLAW_STATE_WAIT_FOR_FIRE.
domain_8_value_1 -> TESLA_CLAW_STATE_WAIT_FOR_FIRE: The TESLA_CLAW_STATE_FIRING release/inactive/locked path clears targets, locks and reach at 002b9160-002b9174 and cancels item animation at 002b9170. Retail 002b9178-002b917c writes 1 to s3+0x20, returning the same switch selector to canonical TESLA_CLAW_STATE_WAIT_FOR_FIRE = 1.
domain_9_value_4 -> TESLA_CLAW_STATE_FINISHED: Retail s8 captures hero.items[0].state == 3 at 002b882c-002b8848. After completion releases the point light and resets its handle at 002b92a0-002b92b8, 002b92bc-002b92d4 writes 4 to the same s3+0x20 Moby state dispatched by the switch. Reuse its canonical TESLA_CLAW_STATE_FINISHED = 4.
domain_2_value_7 -> SWING_CAMERA_TYPE_SLIDE_RELEASE_ANGLE_ALLOW_AIRBORNE_ENTRY: Retail 0x002ec9c4-0x002ec9d8 indexes camera_descriptors by record+0x84, loads descriptor+0x1c into s8 and reads params+0x2c. This proves SwingCameraParams.type, a camera behavior selector separate from HeroStateId, HeroGravityMode, HeroVehicleMode and FollowCamTail.mode. Retail 0x002ecd58-0x002ece14 routes type 7 through the stick-skippable 200-frame swing-end/400-frame restart timing. At 0x002ecef8-0x002ecf2c type 7 suppresses set_follow_camera_angle_blend_from_direction when at_end is set. At 0x002ed1b8-0x002ed1f8 it calls follow_camera_start_slide and the up-spring setter. Checked caller 0x002ed294-0x002ed2ec limits the timer-zero frames_off_ground check to types 1 and 10; type 7 bypasses it and reaches the call at 0x002ed318, permitting airborne entry subject to the ordinary volume eligibility checks. Original retail Ghidra decompilation and bound C agree. This occurrence selects the timing branch.
domain_3_value_7 -> SWING_CAMERA_TYPE_SLIDE_RELEASE_ANGLE_ALLOW_AIRBORNE_ENTRY: Retail 0x002ec9c4-0x002ec9d8 indexes camera_descriptors by record+0x84, loads descriptor+0x1c into s8 and reads params+0x2c. This proves SwingCameraParams.type, a camera behavior selector separate from HeroStateId, HeroGravityMode, HeroVehicleMode and FollowCamTail.mode. Retail 0x002ecd58-0x002ece14 routes type 7 through the stick-skippable 200-frame swing-end/400-frame restart timing. At 0x002ecef8-0x002ecf2c type 7 suppresses set_follow_camera_angle_blend_from_direction when at_end is set. At 0x002ed1b8-0x002ed1f8 it calls follow_camera_start_slide and the up-spring setter. Checked caller 0x002ed294-0x002ed2ec limits the timer-zero frames_off_ground check to types 1 and 10; type 7 bypasses it and reaches the call at 0x002ed318, permitting airborne entry subject to the ordinary volume eligibility checks. Original retail Ghidra decompilation and bound C agree. This != occurrence controls angle-output suppression at the swing end.
domain_4_value_7 -> SWING_CAMERA_TYPE_SLIDE_RELEASE_ANGLE_ALLOW_AIRBORNE_ENTRY: Retail 0x002ec9c4-0x002ec9d8 indexes camera_descriptors by record+0x84, loads descriptor+0x1c into s8 and reads params+0x2c. This proves SwingCameraParams.type, a camera behavior selector separate from HeroStateId, HeroGravityMode, HeroVehicleMode and FollowCamTail.mode. Retail 0x002ecd58-0x002ece14 routes type 7 through the stick-skippable 200-frame swing-end/400-frame restart timing. At 0x002ecef8-0x002ecf2c type 7 suppresses set_follow_camera_angle_blend_from_direction when at_end is set. At 0x002ed1b8-0x002ed1f8 it calls follow_camera_start_slide and the up-spring setter. Checked caller 0x002ed294-0x002ed2ec limits the timer-zero frames_off_ground check to types 1 and 10; type 7 bypasses it and reaches the call at 0x002ed318, permitting airborne entry subject to the ordinary volume eligibility checks. Original retail Ghidra decompilation and bound C agree. This occurrence selects slide initialization and up-spring output.
domain_0_value_2 -> HIT_STATUS_DAMAGE_PROCESSING_THRESHOLD: Retail 0x002bf1a4-0x002bf1c4 passes sp+0x80 as fifth-argument status output to resolve_moby_hit_response; 0x002bf1c8-0x002bf1d0 tests that output against 2, and 0x002bf1d8-0x002bf1f4 subtracts hit+0x2C from spawner hp only on status >=2. Checked helper request 12ae18c00fe448ddaa21784772560d5f (level-03 0x00248f00) saves t0 in s3 at 0x00248f10, writes status 0 at 0x00248f58/0x00248fdc, writes status 1 on the no-hit path at 0x00249030-0x00249034, and writes hit-response statuses 2/3/4 through 0x002492c8. The helper can downgrade status to 1 and zero damage at 0x002493d4-0x002493e0. Thus 2 is the lower threshold for entering this caller's damage processing, in the helper output domain, separate from all equal-valued spawner enums.
domain_0_value_0 -> WRENCH_MOBY_STATE_READY_FOR_ATTACK: Retail 0x00240ff8 loads equipped slot-0 moby; 0x00241004-0x00241010 verifies is_wrench_moby. 0x00241014-0x00241018 reads its byte state at +0x20 and rejects nonzero before selecting wrench melee (0x13/0x70), crouch swing (0x15), or launch_hero_wrench at 0x00241094. Checked launch helper 0x00236ec0-0x00236ecc reloads and verifies that same slot moby; 0x00236ee8 loads 10 and 0x00236ef8 writes it to +0x20 before throw transform/velocity setup. Thus zero is the wrench moby's attack-ready state, in a distinct domain from hero state IDs.
domain_0_value_5 -> SAND_MOUSE_FOLLOW_HERO: Retail 0x002e3f60-0x002e3f68 loads this moby's state byte from s3+0x20 and compares it against 5 to select parent-facing input. The same byte is dispatched at 0x002e4004-0x002e4024; authoritative C names its case 5 SAND_MOUSE_FOLLOW_HERO, whose 0x002e47e4-0x002e47f4 calls compute a hero-relative goal and steer toward it. Reuse that established same-domain member as the >= boundary; this does not assert all later states follow the hero.
domain_10_value_20 -> SAND_MOUSE_RETURN_TO_PARENT: Retail 0x002e4a44-0x002e4a70 gates on fall-helper bit 2, zeros velocity and copies indexed parent's position to private target; 0x002e4a74-0x002e4a7c stores 0x14 into self state +0x20. The same self-state switch already names case 20 SAND_MOUSE_RETURN_TO_PARENT; 0x002e4a80-0x002e4b54 faces and moves toward that captured target, then restores waiting state.
domain_11_value_1 -> SAND_MOUSE_WAIT_FOR_HERO_IN_VOLUME: Retail return handler checks mover bit 4 at 0x002e4b04, restores idle animation, then 0x002e4b3c-0x002e4b58 commands parent 1, writes 1 to self state +0x20 and hides self. The self byte is the switch selector, whose authoritative case 1 is SAND_MOUSE_WAIT_FOR_HERO_IN_VOLUME and waits on hero-volume/line acceptance. Only the self-state assignment is proposed; parent command 1 is a separate domain.
domain_1_value_1 -> SAND_MOUSE_WAIT_FOR_HERO_IN_VOLUME: Retail initialization loads 1 at 0x002e427c and stores it to this moby's state byte s3+0x20 at 0x002e4288. The same field selects authoritative C case SAND_MOUSE_WAIT_FOR_HERO_IN_VOLUME; retail 0x002e4350-0x002e4384 and 0x002e4468-0x002e449c gate activation on hero position in the configured volume. This assignment enters that established local-enum state.
domain_2_value_2 -> SAND_MOUSE_WAIT_FOR_PARENT_READY: Retail 0x002e4528-0x002e4540 commands indexed parent +0xbc to 5 and writes 2 to self s3+0x20. Dispatch reads self +0x20; case 2 at 0x002e4544-0x002e457c waits for indexed parent's state +0x20 to equal 5 before proceeding. Authoritative C already names this self-state SAND_MOUSE_WAIT_FOR_PARENT_READY, distinct from the parent's command domain.
domain_3_value_3 -> SAND_MOUSE_MOVE_TOWARD_HERO: In the activation path without a parent, retail 0x002e4520 branches to 0x002e4584; 0x002e4588-0x002e4594 writes 3 into self state +0x20 and restores draw distance. This is the same self-state dispatched into authoritative case SAND_MOUSE_MOVE_TOWARD_HERO at 0x002e4598, which computes the hero-relative approach target and steps movement at 0x002e4614-0x002e4624.
domain_4_value_3 -> SAND_MOUSE_MOVE_TOWARD_HERO: Retail 0x002e4554-0x002e4564 checks the indexed parent's state for readiness; 0x002e4568-0x002e4570 writes 3 to self state +0x20 on success. This self-state is the existing local-enum SAND_MOUSE_MOVE_TOWARD_HERO case, retail 0x002e4598-0x002e4624, which refreshes the hero-relative target, faces it and calls the movement step.
domain_5_value_3 -> SAND_MOUSE_MOVE_TOWARD_HERO: Retail parent-ready case branches on absent parent at 0x002e454c to 0x002e4580, clears timer and stores 3 to self state +0x20 at 0x002e4594. This is the same field loaded for switch dispatch at 0x002e4004. Existing SAND_MOUSE_MOVE_TOWARD_HERO case 3 computes a hero-relative target and moves toward it at 0x002e4598-0x002e4624; reuse that local member.
domain_6_value_4 -> SAND_MOUSE_BEGIN_FOLLOW_HERO: Retail 0x002e4628-0x002e4654 tests mover result bits or distance to hero below 1.0, then 0x002e465c-0x002e4670 zeros velocity and stores 4 at self +0x20. Existing authoritative case SAND_MOUSE_BEGIN_FOLLOW_HERO handles that same switch field and transitions to follow state 5 at 0x002e46f0-0x002e4734. Reuse its established member for the state assignment.
domain_7_value_5 -> SAND_MOUSE_FOLLOW_HERO: Retail begin-follow handler 0x002e46f0-0x002e4728 releases hero control and clears self/parent flag bits, then 0x002e472c-0x002e4734 stores 5 to self state +0x20. Same-byte switch case 5 is already SAND_MOUSE_FOLLOW_HERO in authoritative C; 0x002e47e4-0x002e47f4 computes a hero-relative goal and steers toward it. This is a self-state assignment, not the helper's subsequent state-2 write.
domain_8_value_6 -> SAND_MOUSE_FADE_OUT_AND_RESET_HOME: Retail failed-follow path 0x002e4830-0x002e4844 zeros velocity, initializes fade timer +0x4c and stores 6 to self +0x20. The same dispatch domain's established SAND_MOUSE_FADE_OUT_AND_RESET_HOME case at 0x002e4878-0x002e49f8 emits particles/reduces alpha until resetting position and target to home. Reuse the existing local-enum member.
domain_9_value_19 -> SAND_MOUSE_DAMP_VELOCITY_BEFORE_RETURN: Retail fade completion 0x002e4888-0x002e48b4 copies home into self position and private target, zeros velocity and writes 0x13 to self state +0x20. That exact switch field selects established SAND_MOUSE_DAMP_VELOCITY_BEFORE_RETURN case 19 at 0x002e49fc-0x002e4a40, damping XY velocity before the return transition. Reuse that existing local member.
domain_0_value_0 -> SANDMOUSE_HOUSE_INITIALIZE: Retail 0x002f2b8c-0x002f2b90 tests the house state byte at s0+0x20 against zero before yaw springing. The same byte feeds the jump table at 0x002f2bf4-0x002f2c14; its existing case zero is SANDMOUSE_HOUSE_INITIALIZE, which captures yaw at 0x002f2c44-0x002f2c54.
domain_10_value_1 -> SANDMOUSE_HOUSE_WAIT_FOR_INITIAL_COMMAND: Retail 0x002f2e64-0x002f2ea0 completes restoration, selects animation 0 if necessary and writes 1 to house state +0x20. Initialization writes the same state at 0x002f2c4c; existing case 1 waits for the initial command at 0x002f2c58-0x002f2c64.
domain_1_value_1 -> SANDMOUSE_HOUSE_WAIT_FOR_INITIAL_COMMAND: Initialization stores 1 into the same house state byte +0x20 at 0x002f2c48-0x002f2c4c. Existing case SANDMOUSE_HOUSE_WAIT_FOR_INITIAL_COMMAND selects that byte value and waits for command +0xBC in 3..5 at 0x002f2c58-0x002f2c64.
domain_2_value_2 -> SANDMOUSE_HOUSE_WAIT_FOR_INITIAL_COMMAND_ANIMATION_END: Retail 0x002f2c58-0x002f2c98 accepts initial command 3..5, selects animation 1, then supplies state 2 to the common +0x20 store at 0x002f2ea0. Existing case 2 waits for animation flags +0x70 bit 2 at 0x002f2c9c-0x002f2ca4.
domain_3_value_3 -> SANDMOUSE_HOUSE_WAIT_FOR_FOLLOWUP_COMMAND: Retail 0x002f2c9c-0x002f2cd8 completes the initial-command animation, selects animation 2 and writes state 3 through 0x002f2ea0. Existing case 3, on this same +0x20 selector, waits for followup commands at 0x002f2cdc-0x002f2d3c.
domain_4_value_4 -> SANDMOUSE_HOUSE_WAIT_FOR_FOLLOWUP_COMMAND_ANIMATION_END: Retail 0x002f2cdc-0x002f2d1c accepts command 4/5, selects animation 3 and supplies state 4 to the common +0x20 store. Existing case 4 is the followup-animation wait, testing +0x70 bit 2 at 0x002f2d40-0x002f2d48.
domain_5_value_7 -> SANDMOUSE_HOUSE_RESTORE_INITIAL_ANIMATION: In source case WAIT_FOR_FOLLOWUP_COMMAND, commands 7/1 route via retail 0x002f2d20-0x002f2d3c to 0x002f2e00-0x002f2e30, selecting animation 6 and storing 7 at +0x20. Existing house case 7 restores animation 0 after completion at 0x002f2e64-0x002f2ea0.
domain_6_value_5 -> SANDMOUSE_HOUSE_WAIT_FOR_POST_FOLLOWUP_COMMAND: Retail 0x002f2d40-0x002f2d7c waits for followup animation completion, selects animation 4 and supplies state 5 to the +0x20 store. The existing case 5 on this same selector waits for post-followup commands 3/6/7/1 at 0x002f2d80-0x002f2da0.
domain_7_value_6 -> SANDMOUSE_HOUSE_WAIT_FOR_POST_FOLLOWUP_COMMAND_ANIMATION_END: Retail 0x002f2d80-0x002f2dd8 accepts post-followup commands 3/6/7/1, selects animation 5 and supplies state 6 to +0x20. Existing case 6 tests animation completion at 0x002f2ddc-0x002f2de4 before choosing its next transition.
domain_8_value_7 -> SANDMOUSE_HOUSE_RESTORE_INITIAL_ANIMATION: In source case WAIT_FOR_POST_FOLLOWUP_COMMAND_ANIMATION_END, completion plus command 7/1 routes through retail 0x002f2dec-0x002f2e30 to animation 6 and a store of 7 at +0x20. Existing house case 7 restores initialization animation 0 at 0x002f2e64-0x002f2ea0.
domain_9_value_3 -> SANDMOUSE_HOUSE_WAIT_FOR_FOLLOWUP_COMMAND: The other branch of completed state 6 selects animation 2 and supplies state 3 at retail 0x002f2e34-0x002f2e60 for the common +0x20 store. This is the same selector and animation/state pair reached at 0x002f2cd8; existing case 3 waits for followup commands.
domain_0_value_42 -> PROXIMITY_MOBY_STATE_FOLLOWUP_SCENES: Retail 0x0028e190 retains the moby argument in s1; 0x0028e1a0 loads its state byte at +0x20 and 0x0028e1a4 branches on 42 to 0x0028e424. That branch tests one-time bytes at 0x0013D388+0x48..+0x4B and runs start_scene(4), start_scene(5), queue_scene_movie_with_game_language(11), or start_scene(6), setting each byte afterward (0x0028e43c-0x0028e4fc). This proves the follow-up-scenes state of this moby, not shared hero state 42.
domain_1_value_42 -> PROXIMITY_MOBY_STATE_FOLLOWUP_SCENES: Retail 0x0028e3e0 loads 42 and 0x0028e3e8 stores it to +0x20 of the same moby retained in s1. This occurs after proximity/help/input acceptance and level 10 plus Clank vehicle-mode checks, then sets moby flags 0x41 and calls start_scene(9) at 0x0028e3ec-0x0028e3f8. The next update's exact same state-byte comparison at 0x0028e1a0-0x0028e1a4 selects the follow-up scene sequence at 0x0028e424-0x0028e4fc, proving reuse of PROXIMITY_MOBY_STATE_FOLLOWUP_SCENES.
domain_2_value_0 -> PROXIMITY_MOBY_STATE_INTERACTIVE: After the state-42 branch has no eligible follow-up scene, retail 0x0028e504 stores zero to moby+0x20 and 0x0028e50c-0x0028e510 clears flags 0x41, restoring the previously suppressed interaction. On subsequent calls, 0x0028e1a0-0x0028e1a4 sends this zero state through the proximity tests (0x0028e284-0x0028e390) and help/input interaction gate (0x0028e394-0x0028e3c0). The authoritative source identifies this field as MOBY(moby)->state, proving the interactive state of this moby rather than HERO_STATE_IDLE or another equal-valued domain.
domain_1_value_1 -> BOLT_FALLING: Retail 0x002a6e00 retains input bolt a0 in s4; 0x002a711c loads 1 and 0x002a7128 stores it to that same bolt's current-state byte at +0x20. Original Ghidra likewise assigns *(undefined1 *)(iVar7 + 0x20)=1. The gated branch resets spin at pvars+0x30, sets quaternion w=1, initializes velocity at +0x20, adds it to bolt position and clears ground_moby at 0x002a71cc. Authoritative bolt_update declares BOLT_FALLING=1, handles that state with gravity, velocity integration and collision/settling, and calls this function with the same Bolt in BOLT_IDLE and BOLT_SETTLING. spawn_bolt_by_value also uses BOLT_FALLING=1 for newly airborne bolts. Thus this is the established bolt current-state member, unrelated to HeroStateId, HeroGravityMode, HeroVehicleMode or TRUE.
domain_0_value_1 -> MOBY_STATE_FOLLOW_PATH: Retail loads the selector from moby+0x20 at 0x002de894; comparison with 1 at 0x002de8a4 enters path sampling at 0x002de950. Initialization computes progress/increment and height offset at 0x002de924-0x002de940, then stores s5=1 into that same state byte at 0x002de948 and falls through to sampling. Thus this assignment selects the existing local MOBY_STATE_FOLLOW_PATH member, not the independent loop argument 1.
domain_1_value_2 -> MOBY_STATE_FALLING: Retail 0x002dec40-0x002dec44 stores 2 into moby+0x20 after the hit path seeds velocity from frame displacement at 0x002deba4-0x002debbc and adds upward velocity at 0x002dec18. The same byte is the switch selector at 0x002de894; dispatch of 2 at 0x002de8c8 enters gravity subtraction and position integration at 0x002de9c4-0x002dea38. This proves reuse of existing local MOBY_STATE_FALLING, also already used by the hit guard.
domain_0_value_1 -> DOOR_WAIT_TO_MOVE_TO_OFFSET_POSITION: Retail 00310e54 loads the door selector from s2+0x20; 00310e7c-00310eb4 saves the initial position, clears progress and writes 1 to that same field at 00310eac. This enters the existing local enum's waiting branch at 00310eb8, which gates offset movement on the linked moby's configured state.
domain_10_value_4 -> DOOR_RETURN_TO_INITIAL_POSITION: Retail 003110e4-00311118 requires a valid linked moby whose state matches door data+0x18. It clears progress at 00311128, optionally plays sound, and loads 4 at 0031113c for the s2+0x20 store at 0031129c. This enters the existing DOOR_RETURN_TO_INITIAL_POSITION selector branch, which applies negative offset motion and restores the saved position.
domain_13_value_2 -> DOOR_MOVE_TO_OFFSET_POSITION: Within the return branch, retail 00311160-00311194 checks missing/invalid linked moby or linked state equal to data+0x14. The selected reversal path loads 2 at 003111b8 and stores to the door selector s2+0x20 at 0031129c. The same field is switched at 00310e54, and its existing DOOR_MOVE_TO_OFFSET_POSITION branch advances toward the configured offset.
domain_14_value_1 -> DOOR_WAIT_TO_MOVE_TO_OFFSET_POSITION: Retail 0031124c-00311260 detects completed return and copies saved initial position from s1 to door position s2+0x10. After sound/status handling it loads 1 at 00311298 and stores to s2+0x20 at 0031129c. This re-enters the existing DOOR_WAIT_TO_MOVE_TO_OFFSET_POSITION branch on the identical door-state selector.
domain_3_value_2 -> DOOR_MOVE_TO_OFFSET_POSITION: Retail case 1 initializes offset length from door data+0x1c/+0x20/+0x24 at 00310f14-00310f50, then loads 2 at 00310f64 and stores it to s2+0x20 at 0031129c. That same selector dispatches to the existing DOOR_MOVE_TO_OFFSET_POSITION case, whose 00310ffc-00311078 path advances position and decrements remaining distance.
domain_6_value_4 -> DOOR_RETURN_TO_INITIAL_POSITION: In the offset movement branch, retail 00310f8c-00310fc0 tests linked moby+0x20 against door data+0x18. On equality it plays the movement sound and loads 4 at 00310fe0, storing to door s2+0x20 at 0031129c. This is the same selector as the existing DOOR_RETURN_TO_INITIAL_POSITION case, whose reverse step and saved-position restore occur at 003111bc-00311260.
domain_7_value_3 -> DOOR_WAIT_TO_RETURN_TO_INITIAL_POSITION: Retail 00311068-00311088 detects completed offset travel and sets position to saved initial position plus transformed offset. It clears progress and writes 3 to door s2+0x20 at 0031108c-00311094, entering the existing DOOR_WAIT_TO_RETURN_TO_INITIAL_POSITION branch at 003110c0, which waits for linked state data+0x18.
domain_0_value_1 -> BRIDGE_WAIT_TO_MOVE_TO_OFFSET_POSITION: Retail 0x0031afd8-0x0031afdc stores 1 to 0x20(s4), the same moby state byte loaded at 0x0031af78 for the switch. Initialization saves the starting position and clears progress; established case 1 waits for the linked-state condition before outbound travel.
domain_10_value_4 -> BRIDGE_RETURN_TO_INITIAL_POSITION: Retail 0x0031b1e0-0x0031b244 checks the linked state against bridge_data+0xb8 while waiting at the offset position, clears progress and loads 4 for the shared moby+0x20 store at 0x0031b398. The same byte selects established return-to-initial-position case 4.
domain_13_value_2 -> BRIDGE_MOVE_TO_OFFSET_POSITION: Retail 0x0031b248-0x0031b2a4 checks the outbound linked-state condition during return travel and loads 2 for the shared store to moby+0x20 at 0x0031b398. This is the same selector loaded at 0x0031af78 and already names case 2 BRIDGE_MOVE_TO_OFFSET_POSITION, resuming outbound motion.
domain_14_value_1 -> BRIDGE_WAIT_TO_MOVE_TO_OFFSET_POSITION: Retail 0x0031b354-0x0031b35c restores bridge_data+0xa0 into the moby position on return completion; 0x0031b390 clears the optional flag and 0x0031b394-0x0031b398 stores 1 to moby+0x20. This is the same bridge selector whose established case 1 waits before moving to the offset position.
domain_3_value_2 -> BRIDGE_MOVE_TO_OFFSET_POSITION: Retail 0x0031b028-0x0031b064 initializes travel distance from configured offset +0xbc/+0xc0/+0xc4, then 0x0031b074 loads 2 for the shared store to moby+0x20 at 0x0031b398. This is the exact selector of established BRIDGE_MOVE_TO_OFFSET_POSITION case 2.
domain_6_value_4 -> BRIDGE_RETURN_TO_INITIAL_POSITION: Retail 0x0031b080-0x0031b0b8 checks linked moby state against bridge_data+0xb8 while outbound, then 0x0031b0d4 loads 4 for the moby+0x20 store at 0x0031b398. The same selector dispatches established return case 4, which applies negative travel and restores the saved initial position.
domain_7_value_3 -> BRIDGE_WAIT_TO_RETURN_TO_INITIAL_POSITION: Retail 0x0031b180-0x0031b1bc snaps position to saved initial position plus transformed offset, clears progress, sets the optional flag and stores 3 directly to moby+0x20. That byte is the switch selector; established case 3 waits for the linked return condition before entering return state 4.
domain_0_value_1 -> EMIT_SPARKS: The selector is this emitter's own MOBY(moby)->state, loaded from +0x20 at retail 0x002a2c34. Initialization stores 1 to the same byte at 0x002a2ce4 after the save/kill checks pass. The dispatch at 0x002a2c38 branches on 1 to 0x002a2cfc, whose body emits sixteen sparks through 0x002647f0 at 0x002a3034. Thus this assignment enters the existing local EMIT_SPARKS=1 case, not an unrelated boolean or shared state domain.
domain_1_value_3 -> EFFECT_DISABLED: Retail 0x002a2c98 and 0x002a2cd8 send initialization to 0x002a2ce8 when the cleared/save or kill-bit checks prevent emission. 0x002a2ce8-0x002a2cf8 stores 3 to this emitter's state byte +0x20, sets command to 0xFF and clears collision. The dispatcher at 0x002a2c34-0x002a2c68 handles only 0, 1 and 2; state 3 returns without spark emission or draw-callback registration. EFFECT_DISABLED names that observed terminal inactive effect state.
domain_2_value_3 -> EFFECT_DISABLED: In the existing WAIT_FOR_EFFECT_TIMEOUT case, retail 0x002a30c0-0x002a30d4 increments pvars+0x14 and tests its prior value against 0x3D. At timeout 0x002a30d8-0x002a30e0 stores 3 to the same moby+0x20 state byte dispatched at 0x002a2c34; it skips the draw callback otherwise scheduled at 0x002a30f0. State 3 is unhandled and returns on later updates, matching the disabled state selected by initialization at 0x002a2cec. Reuse EFFECT_DISABLED for both assignments in this same emitter-state domain.
domain_0_value_6 -> PAD_INITIALIZE_FLASHING: Source SHA-256 21fe678af80e7a179327988ed7228bca304df07006564290bc2c69f9a0d9c31a compares MOBY(moby)->state, the same byte at moby+0x20 used by the outer PAD_* switch. Retail 0030bfc8-0030bfcc loads that byte and tests it below 6. Existing PAD_INITIALIZE_FLASHING=6 is that domain's boundary: 0030c3cc-0030c3f4 initializes the flash timer and phase and enters state 7. This reuses the established state member, not a nested flash-phase constant.
domain_1_value_3 -> PAD_WAIT_TO_ENTER_FLASH_ACTIVATION_WAIT: In source case PAD_ADVANCE_TO_FLASH_ACTIVATION_GATE, assignment to MOBY(moby)->state writes the outer pad-state selector. Retail 0030c334 branches to the common sb v0,0x20(s2) at 0030c4d8, with 0030c338 loading 3. Dispatch of that same byte at 0030c248-0030c268 reaches state 3, whose 0030c33c activation gate and 0030c36c-0030c374 state-4 request match existing PAD_WAIT_TO_ENTER_FLASH_ACTIVATION_WAIT=3.
domain_2_value_5 -> PAD_WAIT_FOR_FLASH_ACTIVATION: Source case PAD_ENTER_FLASH_ACTIVATION_WAIT assigns the same MOBY(moby)->state byte consumed by the outer switch. Retail 0030c380 branches to byte store 0030c4d8 and delay slot 0030c384 loads 5. State 5 at 0030c388 waits for activation flag s5, then sets pad data+8, applies green and requests state 6 at 0030c3ac-0030c3b0. Thus the assignment reuses existing outer-state member PAD_WAIT_FOR_FLASH_ACTIVATION=5.
domain_3_value_7 -> PAD_UPDATE_FLASHING: Source case PAD_INITIALIZE_FLASHING initializes pad_data->unkC and unkE and assigns MOBY(moby)->state=7. Retail 0030c3cc-0030c3ec initializes the short timer, clears phase and applies pale green; 0030c3f0-0030c3f4 loads 7 and branches to sb at moby+0x20 in 0030c4d8. This exact outer selector dispatches existing PAD_UPDATE_FLASHING=7, whose 0030c434-0030c4b8 reloads the flash timer and alternates colors.
domain_4_value_1 -> PAD_WAIT_FOR_ACTIVATION: Source case PAD_RESET_TO_ACTIVATION_WAIT assigns MOBY(moby)->state=1 after gray color and clearing command. Retail 0030c4bc-0030c4d8 applies gray, loads 1, clears moby+0xBC and stores 1 to moby+0x20. That same outer switch byte dispatches existing PAD_WAIT_FOR_ACTIVATION=1 at 0030c2a4, clearing pad data+8 and waiting at 0030c2bc for activation before starting the duration timer. PAD_FLASH_APPLY_WHITE=1 belongs to separate pad_data->unkE and is not applicable.
domain_0_value_0 -> UPDATE_INITIALIZE: Assigned retail 0x002c2d54-0x002c2d58 tests moby/s2+0x20 and returns for zero. Checked caller request df81b031ab914b94b0be2dff887265ba calls common at 0x002c25dc, then loads the same moby+0x20 at 0x002c25e4 for dispatch. Authoritative caller C names zero UPDATE_INITIALIZE; its initialization branch 0x002c2610-0x002c26f4 copies hover center, initializes health, creates rockets and selects the next state. This proves the same state domain, independently of HIT_REACTION_NONE.
domain_1_value_7 -> WAIT_TO_RESUME_ATTACK_SEQUENCE: Retail 0x002c30d4 loads s0=7; after damage subtraction, positive health takes 0x002c3114-0x002c3118 and stores s0 to moby/s2+0x20. Original Ghidra and authoritative C agree. Checked caller df81b031ab914b94b0be2dff887265ba loads that same byte after the common call at 0x002c25dc. Authoritative caller names state 7 WAIT_TO_RESUME_ATTACK_SEQUENCE: 0x002c2cd0-0x002c2d08 waits for animation flag 2, restores command animation and stores state 4 to resume attack dispatch. This is not hit-reaction code 7.
domain_2_value_8 -> EMIT_EFFECT_AND_REMOVE: Original Ghidra and authoritative C map lethal/remove reactions to retail 0x002c315c-0x002c3178, calling removal preparation and storing 8 to moby/s2+0x20. Checked caller df81b031ab914b94b0be2dff887265ba dispatches the same byte after common at 0x002c25dc. Its established state 8 EMIT_EFFECT_AND_REMOVE branch at 0x002c2c28-0x002c2cc4 emits an effect, removes both attached rockets if present, then removes the moby. Thus this assignment uses the caller state domain, not equal-valued hit reaction 8.
domain_3_value_4 -> UPDATE_ATTACK_SEQUENCE: Retail 0x002c31a0-0x002c31c4 loads moby/s2+0x20 and compares it with 4, selecting target range 50 instead of 40. Original Ghidra and authoritative C show this state comparison. Checked caller df81b031ab914b94b0be2dff887265ba loads the identical byte at 0x002c25e4 after invoking common. Authoritative caller names state 4 UPDATE_ATTACK_SEQUENCE; its 0x002c28cc-0x002c2c24 body aims toward the target, dispatches the separate command byte at +0xBC and alternates rocket launches. This proves domain membership rather than numeric equality with hit-reaction 4.
domain_4_value_5 -> WAIT_TO_MOVE_TO_TABLE_POINT: Retail 0x002c325c-0x002c3264 compares moby/s2+0x20 with 5 and skips the visibility line probe when equal. Original Ghidra and authoritative C agree. Checked caller df81b031ab914b94b0be2dff887265ba dispatches this same state byte after common; authoritative caller names 5 WAIT_TO_MOVE_TO_TABLE_POINT. Initialization chooses 5 when data+0xD4 is not -1 at 0x002c26b8-0x002c26d4; its wait branch 0x002c26f8-0x002c270c waits while data+0xB4==2 and otherwise selects state 6, whose following branch moves toward the table/cuboid point. This is separate from command 5 and hit-reaction 5.
domain_0_value_1 -> WAIT_FOR_LINKED_MOBY: Retail 0x00304acc stores 1 at this moby's +0x20 when pvars+0xF10 is not -1, disabling collision. Dispatch 0x00304a34-0x00304a38 reads that same state byte and enters 0x00304ae0, which indexes the linked moby and waits on its class/state. This is the existing local WAIT_FOR_LINKED_MOBY domain member.
domain_1_value_2 -> UPDATE_VECTOR_STREAMS: Initialization without a linked index branches at 0x00304ad8 with v0=2 (0x00304adc) to the state-byte store 0x00304fc4. That same +0x20 state is dispatched at 0x00304a5c to 0x00304b2c, the four-stream audio/collision/vector update branch already named UPDATE_VECTOR_STREAMS.
domain_2_value_2 -> UPDATE_VECTOR_STREAMS: After the linked moby wait clears, 0x00304b14 loads 2 and 0x00304b1c stores it at this moby's +0x20; 0x00304b28 restores class collision. Dispatch of the identical byte at 0x00304a5c selects the four timed vector-stream update at 0x00304b2c, reusing existing UPDATE_VECTOR_STREAMS.
domain_3_value_3 -> EMIT_PARTICLE_BURST: When the linked +0xBC flag is nonzero, 0x00304d80 loads 3 and 0x00304d88 stores it at this moby's +0x20, with collision disabled and stream voice released. The same state byte dispatches at 0x00304a64 to 0x00304dec, whose four transforms each emit 50 particles. Reuse existing EMIT_PARTICLE_BURST.
domain_4_value_4 -> PARTICLE_BURST_COMPLETE: Retail 0x00304fa8 finishes the 50-particle inner loop and 0x00304fb4-0x00304fb8 finishes all four transforms, then 0x00304fc0-0x00304fc4 stores 4 into the same moby+0x20 state byte. Dispatch 0x00304a34-0x00304a6c handles only 0..3, so this marks completed particle emission and prevents another burst in this updater; no broader destruction lifecycle is asserted.
domain_0_value_1 -> WAIT_FOR_HERO_IN_CLIP_VOLUME: Retail 0x002fc0b0-0x002fc0c0 writes 1 to parent s4+0x20 after initialization when the configured clip-volume index is nonnegative. This is the identical byte loaded for dispatch at 0x002fc000; case 1 at 0x002fc12c-0x002fc140 tests hero.motion.pos against that clip volume and returns until inside. Reuse the existing same-selector WAIT_FOR_HERO_IN_CLIP_VOLUME member.
domain_2_value_5 -> WAIT_FOR_RESET: Retail 0x002fc0d8-0x002fc120 places parent and child at opposing fixed offsets, then 0x002fc124-0x002fc128 routes value 5 to the shared state store at 0x002fc568, writing s4+0x20, the dispatch byte at 0x002fc000. Its case 5 at 0x002fc56c-0x002fc590 waits for reset enable before restoring transforms; reuse existing WAIT_FOR_RESET.
domain_3_value_4 -> ANIMATE_PAIR_WITH_TIMED_PHASES: Retail 0x002fc150-0x002fc164 stores 4 to parent s4+0x20 and initializes the scaled phase countdown after the case-1 hero clip-volume test. The dispatch reads this same state byte at 0x002fc000; case 4 at 0x002fc2d8-0x002fc524 interpolates paired distances/rotations and changes command phase on timer expiry. Reuse existing ANIMATE_PAIR_WITH_TIMED_PHASES.
domain_4_value_3 -> MOVE_PAIR_TO_FIXED_OFFSETS: Retail 0x002fc184-0x002fc188 directly stores 3 to s4+0x20 when the activated door has no group. That same byte selects case 3 through 0x002fc000-0x002fc024; 0x002fc1a4-0x002fc294 advances parent/child distances toward fixed targets and writes opposing offsets around the saved origin. Reuse existing MOVE_PAIR_TO_FIXED_OFFSETS.
domain_5_value_5 -> WAIT_FOR_RESET: Retail case 3 checks both interpolated distances against fixed targets with strict <0.01 tests at 0x002fc2a0-0x002fc2cc, then branches through 0x002fc558-0x002fc568 to store 5 in s4+0x20. This is the dispatch byte read at 0x002fc000; case 5 waits for reset enable at 0x002fc56c-0x002fc580. Reuse existing WAIT_FOR_RESET.
domain_6_value_5 -> WAIT_FOR_RESET: Retail case 4 checks parent/child phase distances against configured targets at 0x002fc528-0x002fc554; successful strict <0.01 tests load 5 at 0x002fc560 and store it at 0x002fc568 to s4+0x20. That is the dispatch selector at 0x002fc000, whose case 5 waits for reset enable before restoring transforms. Reuse existing WAIT_FOR_RESET.
domain_7_value_1 -> WAIT_FOR_HERO_IN_CLIP_VOLUME: Retail reset branch 0x002fc588-0x002fc590 clears the separate command byte s4+0xbc and stores 1 in state byte s4+0x20, then restores paired transforms at 0x002fc594-0x002fc5cc. The state byte dispatch at 0x002fc000 selects case 1, whose 0x002fc12c-0x002fc140 clip-volume test waits for hero entry. Reuse existing WAIT_FOR_HERO_IN_CLIP_VOLUME.
domain_0_value_4 -> SHIP_PICKUP_ACTIVE: At 0x002e1d38-0x002e1d54 the immediate-activation flag selects full class scale and stores 4 to pickup s2+0x20, the same byte dispatched at 0x002e1cfc. Existing case SHIP_PICKUP_ACTIVE handles that value.
domain_1_value_1 -> SHIP_PICKUP_SELECT_ACTIVATION_GATE: At 0x002e1d58-0x002e1d88 initialization hides the pickup, disables collision, sets one-tenth class scale and stores 1 to s2+0x20. That same switch selector enters existing SHIP_PICKUP_SELECT_ACTIVATION_GATE.
domain_2_value_2 -> SHIP_PICKUP_WAIT_FOR_TARGET_STATE: At 0x002e1d8c-0x002e1da0 private target index != -1 stores 2 to pickup s2+0x20. Existing case 2 computes moby_base + index*0x100 and waits on target state, proving SHIP_PICKUP_WAIT_FOR_TARGET_STATE.
domain_3_value_3 -> SHIP_PICKUP_WAIT_FOR_ACTIVATION_TIMER: At 0x002e1d94 the absent target index branches to 0x002e1dec-0x002e1df4, storing 3 into the pickup switch byte s2+0x20. Existing case SHIP_PICKUP_WAIT_FOR_ACTIVATION_TIMER checks timer configuration and decrements private timer +0x10.
domain_4_value_3 -> SHIP_PICKUP_WAIT_FOR_ACTIVATION_TIMER: At 0x002e1da4-0x002e1df4 satisfying or bypassing the target-state gate stores 3 to pickup s2+0x20. This is the switch byte whose existing SHIP_PICKUP_WAIT_FOR_ACTIVATION_TIMER branch checks private +8 and timer +0x10.
domain_5_value_4 -> SHIP_PICKUP_ACTIVE: At 0x002e1df8-0x002e1e18 an absent or expired activation timer stores 4 to pickup s2+0x20; subsequent code enables visibility/collision. The same selector's existing SHIP_PICKUP_ACTIVE case rotates and collects the pickup.
domain_6_value_4 -> SHIP_PICKUP_ACTIVE: At 0x002e1e1c-0x002e1e48 the pickup s2+0x20 is compared against 4 before enabling collision and visibility. It reads the identical state byte used by the switch and by the preceding activation store, proving existing SHIP_PICKUP_ACTIVE.
domain_7_value_5 -> SHIP_PICKUP_COLLECTED_SHRINKING: Retail 0x002e2028-0x002e2104 clears pickup collision, awards capped ship ammo/HP by pickup class and stores 5 to s2+0x20. Existing SHIP_PICKUP_COLLECTED_SHRINKING for that selector approaches one-percent class scale and rotates faster.
domain_8_value_6 -> SHIP_PICKUP_REMOVE_OR_RESET: At 0x002e21e8-0x002e2204 scale below five percent of class scale stores 6 to pickup s2+0x20. Existing SHIP_PICKUP_REMOVE_OR_RESET reads private byte +0xd and either removes the pickup or resets visibility, scale and timer.
domain_9_value_1 -> SHIP_PICKUP_SELECT_ACTIVATION_GATE: At 0x002e2224-0x002e225c reset hides the pickup, disables collision, restores one-tenth scale, reloads timer +0x10 from +8 and stores 1 to pickup s2+0x20. Existing SHIP_PICKUP_SELECT_ACTIVATION_GATE then chooses target-state or timer gating.
domain_0_value_1 -> PLATFORM_STATE_WAIT_FOR_LINK_REMOVAL: Retail 0x003091c4 preserves input moby in s4; 0x00309234 reads its +0x20 state and 0x0030923c dispatches value 1 to linked-moby waiting at 0x0030930c. Initialization with a valid +0x8C link lowers the platform five units and stores 1 to the identical s4+0x20 field at 0x003092e0-0x003092ec. This is the exact selector domain already named PLATFORM_STATE_WAIT_FOR_LINK_REMOVAL in the local enum.
domain_1_value_2 -> PLATFORM_STATE_ACTIVE: Retail initialization checks vars+0x8C against -1 at 0x00309290-0x00309294; its missing-link diagnostic path at 0x003092f0-0x00309308 loads 2 and reaches the state-byte store at 0x00309344 to s4+0x20. That same byte is read at 0x00309234 and value 2 dispatches at 0x00309260 to active sinking, tilt, restoration and wobble at 0x00309348 onward. Reuse the local enum's PLATFORM_STATE_ACTIVE for this same state domain.
domain_4_value_2 -> PLATFORM_STATE_ACTIVE: Retail wait handler at 0x0030930c-0x00309334 tests the indexed linked moby and reaches 0x0030933c-0x00309344 when it is absent or has state 0xFE/0xFD; it loads 2 and stores it to this platform's s4+0x20 state byte. The dispatcher reads that exact field at 0x00309234 and branches for 2 at 0x00309260 to 0x00309348, the existing PLATFORM_STATE_ACTIVE branch. This assignment reuses the established local platform state member.
…ibility

domain_0_value_0 -> SENTRYBOT_INITIALIZE: Retail 0029a584 retains the sentrybot moby in s2; 0029a598..0029a59c loads its state byte at +0x20 and returns when zero. The checked sentrybot_update caller passes this same moby, and the authoritative source switches on that same MOBY(moby)->state with SENTRYBOT_INITIALIZE=0, initializing health, home position and patrol/link state. Thus this is the sentrybot initialization state, not hero idle or a hit-response selector.
domain_1_value_23 -> SENTRYBOT_DEATH_KNOCKBACK: Retail 0029a608..0029a610 loads the sentrybot moby's +0x20 state byte and skips new hit reactions when it equals 23. The same function enters that state after death knockback at 0029a854..0029a85c. Authoritative sentrybot_update switches on the same moby's state and names 23 SENTRYBOT_DEATH_KNOCKBACK; its branch steps knockback motion then bursts, releases linked objects and removes the moby. Reuse that established state member for this comparison.
domain_2_value_21 -> SENTRYBOT_RECOVERABLE_KNOCKBACK: Retail 0029a6cc..0029a77c initializes recoverable knockback velocities, computes direction away from the hit source, and starts knockback animation 7. At 0029a780..0029a788 it stores 21 to +0x20 of the same sentrybot moby passed by sentrybot_update. The authoritative caller's switch on MOBY(moby)->state names 21 SENTRYBOT_RECOVERABLE_KNOCKBACK and permits recovery to active states after motion/ground checks. This assignment belongs to that sentrybot state domain.
domain_3_value_23 -> SENTRYBOT_DEATH_KNOCKBACK: Retail 0029a794..0029a850 initializes death knockback velocities, clears the instance flag and starts knockback animation 8; 0029a854..0029a85c stores 23 into sentrybot moby+0x20. This is the same state field switched on by authoritative sentrybot_update, whose existing SENTRYBOT_DEATH_KNOCKBACK=23 branch steps motion, bursts and removes the moby. Reuse the same member as the earlier reaction-suppression comparison.
domain_4_value_16 -> SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM: Retail 0029aa40..0029aa58 compares the sentrybot's +0x20 state byte with 16 and accepts stored line-of-sight without the normal heading/height restriction. The checked caller passes this same sentrybot; its authoritative state switch names 16 SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM. That branch waits for animation completion, then enters 17 and animation 4; state 17 emits using the negated selected-joint direction. Reuse the established member of this same sentrybot state selector.
domain_5_value_17 -> SENTRYBOT_EMIT_WITH_NEGATIVE_JOINT_AIM: Retail 0029aa40..0029aa58 loads moby+0x20 and compares with 17 as the second state bypassing normal heading/height visibility restrictions. Authoritative sentrybot_update receives this same moby and names state 17 SENTRYBOT_EMIT_WITH_NEGATIVE_JOINT_AIM. Its branch computes a projectile vector from selected joint 3 using negative sentrybot_emission_vector_speed_gp*frame_time and emits during the animation window. This proves reuse of that sentrybot state member, independently of equal-valued hero states.
domain_0_value_3 -> EXTERMITANK_REMOVE: Retail 0030518c preserves the recipient moby in s8; 003052a4 loads 3 and 003052a8..003052ac loads its state byte at +0x20 and branches to 00305694 when equal, skipping damage/destruction processing. In the authoritative source, extermitank_update passes this same moby to this handler before switching on MOBY(moby)->state; its established EXTERMITANK_REMOVE=3 branch marks children at pvars+0x184/+0x188 and the parent for removal. Thus this is the Extermitank removal state, not HeroStateId.
domain_1_value_3 -> EXTERMITANK_REMOVE: Retail 003052b4..003052dc subtracts damage and enters the destruction branch at 003052fc when positive-health/positive-damage continuation fails; after removal bookkeeping and three explosions, 0030550c loads 3 and 00305514 stores it to +0x20(s8), the same recipient moby's state byte tested at 003052a8. Authoritative caller extermitank_update calls this handler before its switch on this same MOBY(moby)->state; established EXTERMITANK_REMOVE=3 removes both child mobies and parent. The assignment therefore enters that existing removal state.
domain_0_value_1 -> BOMBOMATIC_WAIT_TO_LAUNCH: Retail 0x0030d750 sets s2=1; 0x0030d7f8 stores s2 to moby state +0x20 after initialization sets deadline pvars+0xB4 at 0x0030d7e0. The same byte is loaded at 0x0030d76c and value 1 dispatches at 0x0030d770 to 0x0030d804, which waits for that deadline. Authoritative C names that exact switch case BOMBOMATIC_WAIT_TO_LAUNCH=1, proving reuse within the same state domain.
domain_1_value_2 -> BOMBOMATIC_LAUNCH_BURST: Retail 0x0030d804..0x0030d84c checks the wait deadline, sets a scaled 180-frame burst deadline, clears launch timer +0xB8 and counter +0xBA, initializes heading +0xBC, then stores 2 to moby+0x20. The same byte dispatches value 2 at 0x0030d790 to 0x0030d850, whose seven-frame timer gates projectile spawn 0x0030daec. Authoritative C already names this exact state switch case BOMBOMATIC_LAUNCH_BURST=2.
domain_2_value_1 -> BOMBOMATIC_WAIT_TO_LAUNCH: Retail 0x0030db00..0x0030db34 checks the burst deadline, computes a scaled 120-frame delay, writes the next deadline to pvars+0xB4, and stores 1 to moby+0x20. On the next update 0x0030d76c..0x0030d770 reads that same state byte and dispatches 1 to the deadline-wait branch 0x0030d804. Authoritative C already names that switch case BOMBOMATIC_WAIT_TO_LAUNCH=1, so this is the return to the same waiting state.
domain_0_value_255 -> ROCKET_STRIKE_FIGHTER_MISSION_COMPLETE: Authoritative src/overlays/l14/mobys/rocket_strike_fighter.c SHA-256 47a68f45eb64ef8e47877658fbe02bff8558df70b9c677d811a87d6262082579 explicitly describes activation once three missions are done and declares mission[3] as the missions that must be complete. Retail 0x002fced8-0x002fcef0 loads visited52[mission[0]+current_level_index*16] as an unsigned byte; 0x002fcee4/0x002fcef4 requires 255. Loads from pvars+0x9c and +0xa0 must equal that byte at 0x002fcf0c and 0x002fcf24. Original retail decompilation agrees on all three -1 byte tests. Success continues through hero/clip/camera checks and restores fighter/rocket flags, sets FOLLOW_PATH and activates the flythrough at 0x002fd0c8-0x002fd0f4. Thus 0xFF is the required completed-mission status in this gate, not a Moby state ID.
domain_0_value_2 -> TURRET_HIT_SPIN: Retail 0x0031a2b4-0x0031a2bc loads byte 0x20(s2) and compares it below 2 before admitting hit contact. The identical byte selects the turret switch at 0x0031a360-0x0031a380; its existing local TURRET_HIT_SPIN=2 is therefore the canonical boundary, not an animation or control value.
domain_1_value_2 -> TURRET_HIT_SPIN: Retail 0x0031a2e0 sets s0=2 and 0x0031a348 stores s0 to byte 0x20(s2) after hit contact and a scaled 60-frame timer. That exact byte is dispatched at 0x0031a360; case TURRET_HIT_SPIN rotates heading at 0x0031a7dc-0x0031a7f4. Reuse the established state member.
domain_2_value_1 -> TURRET_SWEEP_AND_FIRE: Initialization sets s0=1 at 0x0031a4a4 and branches to 0x0031a9c0, which stores it to byte 0x20(s2), the switch selector. Initialization has set sweep bounds, saved height and firing bookkeeping; the existing TURRET_SWEEP_AND_FIRE=1 branch at 0x0031a4f8-0x0031a794 sweeps and emits projectiles.
domain_3_value_3 -> TURRET_LOWER_AND_REALIGN: After hit-spin timer expiry, retail 0x0031a808-0x0031a810 sets a scaled 180-frame timer, then 0x0031a814-0x0031a81c stores 3 to byte 0x20(s2). This switch field selects existing TURRET_LOWER_AND_REALIGN=3, whose 0x0031a85c-0x0031a908 branch lowers Z and realigns sweep heading.
domain_4_value_4 -> TURRET_RESTORE_HEIGHT: The lower/realign branch expires its timer at 0x0031a90c-0x0031a914 and stores 4 to byte 0x20(s2) at 0x0031a948-0x0031a950. The same switch selector's existing TURRET_RESTORE_HEIGHT=4 branch at 0x0031a954-0x0031a99c raises Z to saved height and restores flag 0x1000.
domain_5_value_1 -> TURRET_SWEEP_AND_FIRE: At restoration completion retail 0x0031a990 loads s0=1, clamps Z to saved height and restores flag 0x1000; 0x0031a9c0 stores s0 to byte 0x20(s2). This is the same turret switch selector, returning to existing TURRET_SWEEP_AND_FIRE=1 at 0x0031a4f8, which resumes sweep and timed projectile emission.
domain_0_value_6 -> ARMED_TRANSPORT_STATE_TRACK_TARGET: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e23c0 a missing first path branches to 0x002e2770-0x002e2778, storing 6. Existing case 6 computes target XY bearing and eases yaw at 0x002e27c8-0x002e2818.
domain_10_value_7 -> ARMED_TRANSPORT_STATE_SPAWN_DIRECTED_MOBY: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e2854-0x002e2874 the elapsed private+0x164 timer and private+0xB4 != 2 cause a store of 7. Existing case 7 obtains point 1, forms scaled target-minus-point direction and invokes the directed moby creation helper at 0x002e28e4.
domain_11_value_6 -> ARMED_TRANSPORT_STATE_TRACK_TARGET: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e28ec-0x002e28f8 the directed-spawn branch stores 6 after the creation call, then resets private+0x164 to scaled 180 frames. Existing case 6 resumes target-bearing yaw tracking and checks that timer.
domain_1_value_1 -> ARMED_TRANSPORT_STATE_WAIT_FOR_ACTIVATION: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e2400-0x002e2404 initialization stores 1 after copying the first path point; it hides the object and disables collision. Existing case 1 at 0x002e2420-0x002e2468 waits on the global gate and optional hero-in-region test.
domain_2_value_2 -> ARMED_TRANSPORT_STATE_FOLLOW_FIRST_PATH: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e2470-0x002e2474 successful activation stores 2. Existing case 2 loads private+0xD8 at 0x002e24f8 and calls advance_armed_transport_along_path at 0x002e25c4 using that first path.
domain_3_value_3 -> ARMED_TRANSPORT_STATE_RELEASE_PASSENGERS: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e25d4-0x002e25e0 completion of the first path with an occupied passenger slot stores 3. Existing case 3 scans private+0xC0 slots and releases a passenger through 0x002d3de0 at 0x002e2acc, then clears the slot.
domain_4_value_6 -> ARMED_TRANSPORT_STATE_TRACK_TARGET: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e25e4-0x002e25ec first-path completion without passengers and without private+0xDC second path branches to 0x002e2770-0x002e2778, storing 6. Existing case 6 eases yaw toward tracked XY bearing.
domain_5_value_4 -> ARMED_TRANSPORT_STATE_ALIGN_SECOND_PATH: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e25e4-0x002e25f8 first-path completion without passengers but with private+0xDC second path stores 4. Existing case 4 reads that path's first heading at 0x002e26a8 and eases transport yaw toward it before proceeding.
domain_6_value_4 -> ARMED_TRANSPORT_STATE_ALIGN_SECOND_PATH: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e263c-0x002e2648 the exhausted passenger-slot scan stores 4 when private+0xDC is present. Existing case 4 at 0x002e2668-0x002e26c4 resolves that second path and aligns transport yaw to its first heading.
domain_7_value_6 -> ARMED_TRANSPORT_STATE_TRACK_TARGET: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e263c-0x002e2644 the exhausted passenger-slot scan stores 6 when private+0xDC is -1. Existing case 6 at 0x002e27c8-0x002e2818 computes bearing from tracked XY minus object XY and eases yaw toward it.
domain_8_value_5 -> ARMED_TRANSPORT_STATE_FOLLOW_SECOND_PATH: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e26d8-0x002e26e8 yaw difference below 0.01 stores 5 and resets progress. Existing case 5 resolves private+0xDC at 0x002e273c-0x002e2758 and calls advance_armed_transport_along_path on that second path.
domain_9_value_6 -> ARMED_TRANSPORT_STATE_TRACK_TARGET: The retail object is a0 saved in s3 at 0x002e22e4; lbu at 0x002e2308 selects the switch from byte s3+0x20, which authoritative C maps to MOBY(transport_moby)->state. This assignment writes that same byte and therefore uses the existing function-local armed transport state enum, not HeroStateId or vehicle/control modes. At 0x002e275c-0x002e2778 second-path completion with private+0xD0 present falls through to store 6. Existing case 6 computes tracked-position bearing and eases yaw at 0x002e27c8-0x002e2818.
domain_0_value_1 -> PLANETBUSTER_WAIT_FOR_PATH_TRIGGER: Retail 00306c08 loads 1 and 00306c14 stores it to moby+0x20 after linked-part/path initialization. The identical byte is loaded at 00306a44 for the outer state dispatch. Authoritative source already names that dispatch case PLANETBUSTER_WAIT_FOR_PATH_TRIGGER; original Ghidra case 1 gates height/visibility and waits for the clip-volume/global trigger at 00306e64..00306e80.
domain_1_value_2 -> PLANETBUSTER_FOLLOW_PATH: Retail 00306e64..00306e80 tests pvars+0x5EC clip volume or D_L12_00161F00, then 00306ed0/00306ed8 stores 2 to moby+0x20. This is the same outer switch byte read at 00306a44, whose existing source case is PLANETBUSTER_FOLLOW_PATH. Original Ghidra case 2 and 00306fe8..00307044 compute waypoint modulo, normalize displacement and add it to moby position.
domain_2_value_3 -> PLANETBUSTER_POST_PATH_GUN_UPDATE: Retail 00306f6c..00306fa8 approaches path speed pvars+0x5F0 to zero near path count minus 11; 00306fb4/00306fbc stores 3 to moby+0x20 when zero. The same outer switch has existing source case PLANETBUSTER_POST_PATH_GUN_UPDATE, matching original Ghidra case 3 and retail 0030718c..0030727c yaw/oscillation, linked-transform and gun updates.
domain_3_value_1 -> PLANETBUSTER_WAIT_FOR_PATH_TRIGGER: The source's lower bound reads MOBY(moby)->state, the same +0x20 byte as its named outer cases. Retail 00307620..0030762c loads that byte, subtracts 1 and tests unsigned less than 3, implementing states 1 through 3 before voice allocation. Thus the lower endpoint is the existing PLANETBUSTER_WAIT_FOR_PATH_TRIGGER member, not the separate +0xBC transition interpolation command.
domain_4_value_3 -> PLANETBUSTER_POST_PATH_GUN_UPDATE: The source's upper bound reads MOBY(moby)->state at +0x20. Retail 00307620..0030762c implements the inclusive 1..3 range by unsigned (state-1)<3 and branches to voice allocation at 00307640..00307654. Its upper endpoint is the same state 3 dispatched as the existing PLANETBUSTER_POST_PATH_GUN_UPDATE case; it is unrelated to HeroStateId despite equal numeric values.
domain_0_value_10 -> COMMANDO_STATE_FOLLOW_PARENT_TRANSFORM: Retail 0x002d2f3c loads 10 and 0x002d2f4c stores it to s2+0x20, the same commando state byte dispatched at 0x002d2d68-0x002d2d88. Its existing case composes parent basis, position and heading at 0x002d2f7c-0x002d2fd8; reuse the local canonical member.
domain_10_value_4 -> COMMANDO_STATE_PREPARE_ROCKET_FIRE: Retail 0x002d3570-0x002d3598 stores 4 to Moby+0x20 after attack-node distance is below 0.01 or pvars+0x2dc expires, then blends animation 2. This exact selector already names case 4 COMMANDO_STATE_PREPARE_ROCKET_FIRE; that case turns to target before entering FIRE_ROCKETS.
domain_11_value_2 -> COMMANDO_STATE_IDLE: Retail 0x002d3610-0x002d362c tests recovery completion bit at Moby+0x70 and stores 2 to Moby+0x20 at the source idle label. Initialization without a parent also branches here at 0x002d2f38. Both enter existing COMMANDO_STATE_IDLE, with animation-0 blend when needed.
domain_1_value_2 -> COMMANDO_STATE_IDLE: Retail 0x002d30dc tests saved-position distance below 1.0; 0x002d30e8-0x002d30f0 stores 2 to Moby+0x20. This is the selector of existing COMMANDO_STATE_IDLE, whose body decrements timers and dispatches patrol or attack at 0x002d30f4 onward.
domain_2_value_2 -> COMMANDO_PURSUIT_MODE_DISABLED: Retail 0x002d3100-0x002d3104 compares pvars+0x114 with 2, distinct from Moby state +0x20. Checked helper request a4c4fe35fced428f88c84fd213c7c0ca loads the same pvars from Moby+0x78 at 0x002d38e0 and stores 2 to +0x114 at 0x002d3d00 for mode 22 or 0x002d3d84 when target distance exceeds range. This suppresses attack transitions and selects timed patrol. Supplied update_blarg_elite_commando_joint_aim already calls this exact field/value COMMANDO_PURSUIT_MODE_DISABLED; reuse that separate pursuit-domain member.
domain_3_value_7 -> COMMANDO_STATE_MOVE_TO_ATTACK_NODE: Retail 0x002d3120-0x002d3138 stores 7 to Moby+0x20 after idle's attack countdown, then advances pvars+0x300 node and selects movement animation. Existing case COMMANDO_STATE_MOVE_TO_ATTACK_NODE at 0x002d3520 moves to that node and enters rocket preparation on arrival or timeout.
domain_4_value_4 -> COMMANDO_STATE_PREPARE_ROCKET_FIRE: Retail 0x002d3190-0x002d319c stores 4 to Moby+0x20 after idle's random gate, then blends animation 2. It selects existing COMMANDO_STATE_PREPARE_ROCKET_FIRE, whose 0x002d3320-0x002d3394 target-facing branch enters established firing state 5.
domain_5_value_3 -> COMMANDO_STATE_MOVE_TO_PATROL_NODE: Retail 0x002d31d4-0x002d31fc advances the path node and stores 3 to Moby+0x20 on the pursuit-disabled idle path; 0x002d3200 seeds patrol timer. Existing COMMANDO_STATE_MOVE_TO_PATROL_NODE moves toward that table node at 0x002d3214-0x002d3244 and returns idle on arrival or timeout.
domain_6_value_2 -> COMMANDO_STATE_IDLE: Retail 0x002d325c-0x002d3284 stores 2 to Moby+0x20 when patrol-node distance is below 0.01; the source's first assignment in MOVE_TO_PATROL_NODE is this arrival path. It selects existing COMMANDO_STATE_IDLE and blends animation 0.
domain_7_value_2 -> COMMANDO_STATE_IDLE: The source's second assignment in MOVE_TO_PATROL_NODE is the timer-expiry path. Retail 0x002d326c-0x002d3284 tests pvars+0x302 expiry and shares the store of 2 to Moby+0x20 and animation-0 blend with the arrival path. Both select existing COMMANDO_STATE_IDLE.
domain_8_value_5 -> COMMANDO_STATE_FIRE_ROCKETS: Retail 0x002d3378-0x002d3394 tests Moby+0x70 bit 2 then stores 5 to the dispatched Moby+0x20 state. Existing COMMANDO_STATE_FIRE_ROCKETS at 0x002d33b0 onward aims at target and creates rockets at animation windows through 0x002d349c; reuse the canonical local member.
domain_9_value_2 -> COMMANDO_STATE_IDLE: Retail 0x002d34c4-0x002d34ec increments pvars+0x2de and, after three firing cycles, stores 2 to Moby+0x20. It blends animation 0, seeds the 60-frame countdown and clears the cycle count at 0x002d34f0-0x002d351c, selecting existing COMMANDO_STATE_IDLE.
domain_0_value_1 -> DECOY_HELD: Retail 0x002c4498..0x002c44a0 loads moby byte +0x20 and compares with 1 before removal when the equipped item is absent or not class 0x232. This is the identical selector used by existing case DECOY_HELD; its 0x002c4614..0x002c464c branch retains the decoy while its holder and equipped item remain valid.
domain_1_value_1 -> DECOY_HELD: Retail 0x002c44cc..0x002c44d4 compares 1 against the same moby byte +0x20 dispatched at 0x002c45cc..0x002c45ec. Nonzero hero vehicle mode removes the held decoy. Existing local DECOY_HELD=1 names this exact selector, independently of the separate hero vehicle domain.
domain_2_value_2 -> DECOY_THROWN_MOVEMENT: Retail 0x002c4514..0x002c451c loads moby +0x20 and tests unsigned state <2 to gate flags 0x41. The same byte selects existing DECOY_THROWN_MOVEMENT=2 at the switch; that branch applies gravity at 0x002c46cc..0x002c46dc and collision movement at 0x002c4800. The constant is the threshold excluding thrown and subsequent states.
domain_3_value_1 -> DECOY_HELD: Original Ghidra case 0 stores 1 to param_1+0x20, exactly retail li v0,1 / sb v0,0x20(s1) at 0x002c45f4..0x002c45fc. This enters the existing DECOY_HELD branch of the same selector, which clears timer +0x44 and checks holder +0x40 and equipped item at 0x002c4614..0x002c464c.
domain_4_value_3 -> DECOY_SETTLED_MOVEMENT: Retail 0x002c4a70 clears the thrown velocity, then 0x002c4a74..0x002c4a84 stores 3 to the same moby state byte +0x20 and initializes timer +0x44; 0x002c4a98 snaps to collision height. Existing DECOY_SETTLED_MOVEMENT=3 is the matching switch member, whose branch handles ground motion and supporting mobies.
domain_5_value_4 -> DECOY_PRE_REMOVAL_DELAY: Retail 0x002c4ee4..0x002c4eec loads moby state +0x20 and branches on 4 to 0x002c5098. Authoritative C expresses its inverse as state !=4. This is the same selector as existing DECOY_PRE_REMOVAL_DELAY=4: its branch restores scale, decrements +0x44 and moves to wait-to-shrink at 0x002c50d0..0x002c5120.
domain_6_value_5 -> DECOY_WAIT_TO_SHRINK: Retail 0x002c50d0..0x002c50d8 tests timer +0x44; after list removal, 0x002c5118..0x002c5120 stores 5 to moby +0x20. This enters existing DECOY_WAIT_TO_SHRINK=5 on the same switch selector, whose retail 0x002c5178..0x002c51ac waits for timer expiry or animation flag bit 2 before selecting shrink state 6.
domain_7_value_6 -> DECOY_SHRINK_FOR_REMOVAL: Retail 0x002c5188..0x002c51ac selects 6 and stores it to moby state +0x20 when the timer expires or animation bit 2 is set. Existing DECOY_SHRINK_FOR_REMOVAL=6 names the same switch branch: retail 0x002c520c..0x002c5258 reduces scale and removes the moby below 10 percent of class scale.
domain_8_value_0 -> DECOY_INITIALIZE: Original Ghidra default stores zero to param_1+0x20, matching sb zero,0x20(s1) at retail 0x002c5264. This is the exact byte dispatched by the function's switch and initialized by supplied spawn_decoy. Existing DECOY_INITIALIZE=0 resets timers and flags before entering DECOY_HELD at 0x002c45f4..0x002c4610.
domain_0_value_21 -> SENTRYBOT_RECOVERABLE_KNOCKBACK: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297abc-00297ac0 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_RECOVERABLE_KNOCKBACK member, not an equal-valued hero, animation or target-mode member. The established recoverable-knockback case steps motion with data+0x70 and permits material/height-gated recovery.
domain_10_value_3 -> SENTRYBOT_PATROL_PAUSE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297ed0-00297ee4 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_PATROL_PAUSE member, not an equal-valued hero, animation or target-mode member. The established patrol-pause case decrements data+0x14c before turning toward the next patrol point.
domain_11_value_5 -> SENTRYBOT_DEACTIVATE_LINKED_MOBYS_AFTER_ANIMATION: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297f34-00297f40 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_DEACTIVATE_LINKED_MOBYS_AFTER_ANIMATION member, not an equal-valued hero, animation or target-mode member. The established case waits for animation completion, deactivates indexed linked mobys, then restores saved patrol state.
domain_12_value_3 -> SENTRYBOT_PATROL_PAUSE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297ea0/00297f80 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_PATROL_PAUSE member, not an equal-valued hero, animation or target-mode member. The established patrol-pause case decrements data+0x14c before turning toward the next patrol point.
domain_13_value_16 -> SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298040-00298050 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM member, not an equal-valued hero, animation or target-mode member. The established negative-joint-aim wait case waits for animation completion before selecting state 17 and animation 4.
domain_14_value_11 -> SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 0029806c-00298070 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION member, not an equal-valued hero, animation or target-mode member. The established transition-animation case waits for moby+0x70 bit 2, then restores continuation state and animation from data+0x15c/+0x15d.
domain_15_value_2 -> SENTRYBOT_PATROL_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298080-00298094 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_PATROL_IDLE member, not an equal-valued hero, animation or target-mode member. The established patrol-idle case arms a pause timer; patrol arrival and animation completion restore this same state with animation 0.
domain_16_value_16 -> SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002981f8-00298208 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM member, not an equal-valued hero, animation or target-mode member. The established negative-joint-aim wait case waits for animation completion before selecting state 17 and animation 4.
domain_17_value_11 -> SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298224-00298228 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION member, not an equal-valued hero, animation or target-mode member. The established transition-animation case waits for moby+0x70 bit 2, then restores continuation state and animation from data+0x15c/+0x15d.
domain_18_value_2 -> SENTRYBOT_PATROL_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002983b4-002983c4 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_PATROL_IDLE member, not an equal-valued hero, animation or target-mode member. The established patrol-idle case arms a pause timer; patrol arrival and animation completion restore this same state with animation 0.
domain_19_value_16 -> SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 0029840c-0029841c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM member, not an equal-valued hero, animation or target-mode member. The established negative-joint-aim wait case waits for animation completion before selecting state 17 and animation 4.
domain_1_value_23 -> SENTRYBOT_DEATH_KNOCKBACK: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297ac4-00297ac8 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_DEATH_KNOCKBACK member, not an equal-valued hero, animation or target-mode member. The established death-knockback case steps motion with data+0x70 and bursts/removes the moby on flags 0x160.
domain_20_value_11 -> SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298438-0029843c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION member, not an equal-valued hero, animation or target-mode member. The established transition-animation case waits for moby+0x70 bit 2, then restores continuation state and animation from data+0x15c/+0x15d.
domain_21_value_13 -> SENTRYBOT_PATROL_MOVE_FAST: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 0029848c-0029849c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_PATROL_MOVE_FAST member, not an equal-valued hero, animation or target-mode member. The established fast-patrol case selects the indexed patrol point and moves at frame_time*3.5 before marking arrival.
domain_22_value_11 -> SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002993d8-002993e0 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION member, not an equal-valued hero, animation or target-mode member. The established transition-animation case waits for moby+0x70 bit 2, then restores continuation state and animation from data+0x15c/+0x15d.
domain_23_value_5 -> SENTRYBOT_DEACTIVATE_LINKED_MOBYS_AFTER_ANIMATION: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298510-0029851c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_DEACTIVATE_LINKED_MOBYS_AFTER_ANIMATION member, not an equal-valued hero, animation or target-mode member. The established case waits for animation completion, deactivates indexed linked mobys, then restores saved patrol state.
domain_24_value_12 -> SENTRYBOT_ALERT_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002985f0-00298600 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_ALERT_IDLE member, not an equal-valued hero, animation or target-mode member. The established alert-idle case chooses return-home, target response or fast patrol; arrivals use idle animation 0.
domain_25_value_16 -> SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298658-00298668 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM member, not an equal-valued hero, animation or target-mode member. The established negative-joint-aim wait case waits for animation completion before selecting state 17 and animation 4.
domain_26_value_11 -> SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298684-00298688 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION member, not an equal-valued hero, animation or target-mode member. The established transition-animation case waits for moby+0x70 bit 2, then restores continuation state and animation from data+0x15c/+0x15d.
domain_27_value_9 -> SENTRYBOT_RETURN_HOME: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298698/00299274-00299284 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_RETURN_HOME member, not an equal-valued hero, animation or target-mode member. The established return-home case moves to the spawn position saved at data+0x160 at frame_time*2.
domain_28_value_12 -> SENTRYBOT_ALERT_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298874-0029887c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_ALERT_IDLE member, not an equal-valued hero, animation or target-mode member. The established alert-idle case chooses return-home, target response or fast patrol; arrivals use idle animation 0.
domain_29_value_12 -> SENTRYBOT_ALERT_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002988d0-002988dc compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_ALERT_IDLE member, not an equal-valued hero, animation or target-mode member. The established alert-idle case chooses return-home, target response or fast patrol; arrivals use idle animation 0.
domain_2_value_2 -> SENTRYBOT_PATROL_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297c2c-00297c74 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_PATROL_IDLE member, not an equal-valued hero, animation or target-mode member. The established patrol-idle case arms a pause timer; patrol arrival and animation completion restore this same state with animation 0.
domain_30_value_14 -> SENTRYBOT_APPROACH_LINKED_MOBY: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298930-00298940 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_APPROACH_LINKED_MOBY member, not an equal-valued hero, animation or target-mode member. The established approach case resolves data+0x128, computes a destination 1.5 short of that linked moby and moves toward it.
domain_31_value_15 -> SENTRYBOT_ACTIVATE_LINKED_MOBY: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298a38-00298a4c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_ACTIVATE_LINKED_MOBY member, not an equal-valued hero, animation or target-mode member. The established activation case calls the alarm-duration helper on data+0x128 at animation frame >=12 and completion.
domain_32_value_12 -> SENTRYBOT_ALERT_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298b2c-00298b38 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_ALERT_IDLE member, not an equal-valued hero, animation or target-mode member. The established alert-idle case chooses return-home, target response or fast patrol; arrivals use idle animation 0.
domain_33_value_19 -> SENTRYBOT_EMIT_WITH_POSITIVE_JOINT_AIM: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298b28/00298b54-00298b58 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_EMIT_WITH_POSITIVE_JOINT_AIM member, not an equal-valued hero, animation or target-mode member. The established emission case selects joint 2, positively scales the nominal matrix axis and emits during frames [0.85,3).
domain_34_value_12 -> SENTRYBOT_ALERT_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298bb4-00298bc0 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_ALERT_IDLE member, not an equal-valued hero, animation or target-mode member. The established alert-idle case chooses return-home, target response or fast patrol; arrivals use idle animation 0.
domain_35_value_17 -> SENTRYBOT_EMIT_WITH_NEGATIVE_JOINT_AIM: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298bb0/00298bdc-00298be0 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_EMIT_WITH_NEGATIVE_JOINT_AIM member, not an equal-valued hero, animation or target-mode member. The established emission case selects joint 3, negates the nominal matrix-axis scale and emits during frames [0.85,3).
domain_36_value_12 -> SENTRYBOT_ALERT_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298dc8/00299160-0029916c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_ALERT_IDLE member, not an equal-valued hero, animation or target-mode member. The established alert-idle case chooses return-home, target response or fast patrol; arrivals use idle animation 0.
domain_37_value_14 -> SENTRYBOT_APPROACH_LINKED_MOBY: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298e00-00298e10 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_APPROACH_LINKED_MOBY member, not an equal-valued hero, animation or target-mode member. The established approach case resolves data+0x128, computes a destination 1.5 short of that linked moby and moves toward it.
domain_38_value_12 -> SENTRYBOT_ALERT_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298e90-00298e98/00299160-0029916c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_ALERT_IDLE member, not an equal-valued hero, animation or target-mode member. The established alert-idle case chooses return-home, target response or fast patrol; arrivals use idle animation 0.
domain_39_value_16 -> SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00298e9c-00298eac compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM member, not an equal-valued hero, animation or target-mode member. The established negative-joint-aim wait case waits for animation completion before selecting state 17 and animation 4.
domain_3_value_1 -> SENTRYBOT_WAIT_FOR_LINKED_MOBY: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297c84-00297cc0 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_LINKED_MOBY member, not an equal-valued hero, animation or target-mode member. Initialization selects this state when a linked_moby slot exists; its established case waits for a linked object's state >= 2.
domain_40_value_12 -> SENTRYBOT_ALERT_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00299088/00299160-0029916c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_ALERT_IDLE member, not an equal-valued hero, animation or target-mode member. The established alert-idle case chooses return-home, target response or fast patrol; arrivals use idle animation 0.
domain_41_value_14 -> SENTRYBOT_APPROACH_LINKED_MOBY: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002990c0-002990d0 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_APPROACH_LINKED_MOBY member, not an equal-valued hero, animation or target-mode member. The established approach case resolves data+0x128, computes a destination 1.5 short of that linked moby and moves toward it.
domain_42_value_12 -> SENTRYBOT_ALERT_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00299150-0029916c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_ALERT_IDLE member, not an equal-valued hero, animation or target-mode member. The established alert-idle case chooses return-home, target response or fast patrol; arrivals use idle animation 0.
domain_43_value_18 -> SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_POSITIVE_JOINT_AIM: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 0029915c/00299194-002991a0 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_POSITIVE_JOINT_AIM member, not an equal-valued hero, animation or target-mode member. The established positive-joint-aim wait case waits for animation completion before selecting state 19 and animation 6.
domain_44_value_9 -> SENTRYBOT_RETURN_HOME: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002991dc-002991ec compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_RETURN_HOME member, not an equal-valued hero, animation or target-mode member. The established return-home case moves to the spawn position saved at data+0x160 at frame_time*2.
domain_45_value_9 -> SENTRYBOT_RETURN_HOME: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00299274-00299284 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_RETURN_HOME member, not an equal-valued hero, animation or target-mode member. The established return-home case moves to the spawn position saved at data+0x160 at frame_time*2.
domain_46_value_13 -> SENTRYBOT_PATROL_MOVE_FAST: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002993ac-002993bc compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_PATROL_MOVE_FAST member, not an equal-valued hero, animation or target-mode member. The established fast-patrol case selects the indexed patrol point and moves at frame_time*3.5 before marking arrival.
domain_47_value_11 -> SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002993d8-002993e0 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION member, not an equal-valued hero, animation or target-mode member. The established transition-animation case waits for moby+0x70 bit 2, then restores continuation state and animation from data+0x15c/+0x15d.
domain_48_value_12 -> SENTRYBOT_ALERT_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002994a0-002994ac compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_ALERT_IDLE member, not an equal-valued hero, animation or target-mode member. The established alert-idle case chooses return-home, target response or fast patrol; arrivals use idle animation 0.
domain_49_value_16 -> SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002994d4-002994e4 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM member, not an equal-valued hero, animation or target-mode member. The established negative-joint-aim wait case waits for animation completion before selecting state 17 and animation 4.
domain_4_value_2 -> SENTRYBOT_PATROL_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297d24-00297d2c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_PATROL_IDLE member, not an equal-valued hero, animation or target-mode member. The established patrol-idle case arms a pause timer; patrol arrival and animation completion restore this same state with animation 0.
domain_50_value_11 -> SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00299504-00299508 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION member, not an equal-valued hero, animation or target-mode member. The established transition-animation case waits for moby+0x70 bit 2, then restores continuation state and animation from data+0x15c/+0x15d.
domain_5_value_16 -> SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297d50-00297d60 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_ANIMATION_THEN_EMIT_WITH_NEGATIVE_JOINT_AIM member, not an equal-valued hero, animation or target-mode member. The established negative-joint-aim wait case waits for animation completion before selecting state 17 and animation 4.
domain_6_value_11 -> SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297d7c-00297d80 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION member, not an equal-valued hero, animation or target-mode member. The established transition-animation case waits for moby+0x70 bit 2, then restores continuation state and animation from data+0x15c/+0x15d.
domain_7_value_13 -> SENTRYBOT_PATROL_MOVE_FAST: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297e14-00297e24 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_PATROL_MOVE_FAST member, not an equal-valued hero, animation or target-mode member. The established fast-patrol case selects the indexed patrol point and moves at frame_time*3.5 before marking arrival.
domain_8_value_11 -> SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 002993d8-002993e0 compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_WAIT_FOR_TRANSITION_ANIMATION member, not an equal-valued hero, animation or target-mode member. The established transition-animation case waits for moby+0x70 bit 2, then restores continuation state and animation from data+0x15c/+0x15d.
domain_9_value_2 -> SENTRYBOT_PATROL_IDLE: Authoritative sentrybot.c SHA-256 578802efa53934667081df431113ee0e627c3ac4d9ee9e6fd4ee4a1eef1e4795 identifies this token as MOBY(moby)->state. Original retail Ghidra and instructions 00297e94-00297e9c compare or store the state byte at 0x20(s2); s2 is the input moby (002979e8), also dispatched at 00297bd0-00297bf0. Reuse the existing function-local SENTRYBOT_PATROL_IDLE member, not an equal-valued hero, animation or target-mode member. The established patrol-idle case arms a pause timer; patrol arrival and animation completion restore this same state with animation 0.
@pezzadev
pezzadev force-pushed the decompilerizer-batch-36 branch from b0a79a7 to d663255 Compare November 1, 2026 18:55
@pezzadev
pezzadev marked this pull request as ready for review November 1, 2026 19:00
@pezzadev
pezzadev merged commit a23dc98 into lombyte-project:main Nov 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant