# 13 — Retail's cycle decision tree

**Question**: When `InterpretedMotionState` has simultaneous
`forward_command=RunForward` + `sidestep_command=SidestepRight` +
`turn_command=TurnLeft`, **what cycle plays in retail?**

**Answer (TL;DR)**: All three. Retail does **not** pick "the winning
substate" out of a 3-axis state. Instead, `apply_interpreted_movement`
issues **three separate `DoInterpretedMotion` calls** —
forward-cmd, then sidestep-cmd, then turn-cmd — each landing in
`CMotionTable::GetObjectSequence`, which dispatches by command **class
bits** (0x40000000 substate / 0x10000000 action / 0x20000000 modifier)
to **either replace the substate or attach a modifier**. Forward goes
into the substate slot; sidestep+turn go into the modifier list. The
`CSequence` is rebuilt with all three layers via `add_motion`.

This means **acdream's "priority winner" picker is wrong** — and so is
the `RunForward → WalkForward → Ready` fallback chain. Retail has no
fallback chain; it just calls `GetObjectSequence` per axis and ignores
NULL results.

---

## A. Top of the call tree — `CMotionInterp::apply_interpreted_movement`

Line **305713–305788** (`acclient_2013_pseudo_c.txt`), address `00528600`.

```c
void CMotionInterp::apply_interpreted_movement(this, arg2, arg3) {
    if (!physics_obj) return;
    MovementParameters var_2c;                              // 305719
    MovementParameters::MovementParameters(&var_2c);

    // Sync run-rate from forward_speed if running
    if (interpreted_state.forward_command == 0x44000007 /*RunForward*/)
        my_run_rate = (float)interpreted_state.forward_speed;  // 305722

    // 1) Always re-issue current_style (e.g. CombatMode_NonCombat)
    DoInterpretedMotion(this, interpreted_state.current_style, &var_2c);  // 305724

    // 2) Forward axis
    if (!contact_allows_move(this, interpreted_state.forward_command)) {
        var_18_2 = 0x3f800000;                              // 1.0f speed
        DoInterpretedMotion(this, 0x40000015 /*Stand*/, &var_2c);  // 305729
    } else if (standing_longjump) {
        DoInterpretedMotion(this, 0x41000003 /*Ready*/, &var_2c);  // 305738
        StopInterpretedMotion(this, 0x6500000f /*Sidestep*/, &var_2c);
    } else {
        DoInterpretedMotion(this, interpreted_state.forward_command, &var_2c);  // 305744

        // 3) Sidestep axis
        if (interpreted_state.sidestep_command == 0)
            StopInterpretedMotion(this, 0x6500000f /*Sidestep*/, &var_2c);  // 305748
        else
            DoInterpretedMotion(this, interpreted_state.sidestep_command, &var_2c);  // 305752
    }

    // 4) Turn axis
    if (interpreted_state.turn_command != 0)
        DoInterpretedMotion(this, interpreted_state.turn_command, &var_2c);  // 305762
    else
        // No turn — explicitly stop any prior TurnLeft modifier
        StopInterpretedMotion(this, 0x6500000d /*TurnLeft*/, &var_2c);  // implied 305770
}
```

**Critical observation**: this is **four `DoInterpretedMotion` calls
per UM** (current_style, forward, sidestep, turn). Each one is a
distinct `MotionTableManager::PerformMovement` → `CMotionTable::DoObjectMotion`
→ `CMotionTable::GetObjectSequence` round-trip. The composite cycle is
the sum of four state-machine transitions, not the result of a
priority pick.

`apply_interpreted_movement` is invoked by `apply_current_movement`
(305838–305857). `apply_current_movement` is called by every UM
arrival on the player or remote object after `RawMotionState::ApplyMotion`
(or `InterpretedMotionState::ApplyMotion`) populates the state struct.

---

## B. The state-routing dispatcher — `RawMotionState::ApplyMotion`

Line **293630–293703**, address `0051eb60`. This is the **retail
analog of acdream's `AnimationCommandRouter.Classify`**.

```c
void RawMotionState::ApplyMotion(this, arg2, arg3) {
    // arg2 = motion command (e.g. 0x44000007 RunForward)
    // arg3 = MovementParameters (speed, hold_key_to_apply, etc.)

    if ((arg2 - 0x6500000d) > 3) {                  // not Turn/Sidestep range
        if ((arg2 & 0x40000000) == 0) {             // not substate
            if (arg2 >= 0) {
                if ((arg2 & 0x10000000) != 0)       // ACTION class
                    AddAction(this, arg2, ...);     // 293640 → action queue
            } else if (current_style != arg2) {     // STYLE change
                forward_command = 0x41000003;       // Ready
                current_style = arg2;               // 293645
            }
        } else if (arg2 != 0x44000007 /*RunForward*/) {
            // 0x40000000-class but NOT RunForward (i.e. WalkForward,
            // BackForward etc) goes into FORWARD slot
            forward_command = arg2;                 // 293650
            forward_holdkey = arg3->hold_key_to_apply;
            forward_speed = arg3->speed;
        }
        return;
    }

    switch (arg2) {                                  // 293666
    case 0x6500000d /*TurnLeft*/:
    case 0x6500000e /*TurnRight*/:
        turn_command = arg2;                         // 293671
        turn_holdkey = arg3->hold_key_to_apply;
        turn_speed = arg3->speed;
        return;
    case 0x6500000f /*SidestepRight*/:
    case 0x65000010 /*SidestepLeft*/:
        sidestep_command = arg2;                     // 293688
        sidestep_holdkey = arg3->hold_key_to_apply;
        sidestep_speed = arg3->speed;
        return;
    }
}
```

**Routing classes** (matches `0x40000000`/`0x10000000`/`0x20000000` mask
checks, also visible in `CMotionTable::GetObjectSequence`):

| Class bit | Range | Slot | Effect |
|---|---|---|---|
| `0x40000000` | substate (e.g. `0x44000007` RunForward, `0x40000015` Stand) | `forward_command` (or replaces substate) | replaces previous substate; modifiers may be cleared |
| `0x10000000` | action (e.g. emote) | `action_head` queue | overlay; substate cycle keeps running |
| `0x20000000` | modifier | `modifier_head` list | overlay; substate cycle keeps running |
| `0x6500000d-10` | turn/sidestep (special-cased) | `turn_command` / `sidestep_command` | dedicated slots (effectively modifiers) |
| `< 0` (`0x80...`) | style change | `current_style` | full reset, forward_command → Ready |
| `0x44000007` | **RunForward** is special-cased OUT of the forward slot here — see below | — | not stored in `forward_command` directly by `RawMotionState`; it's the result of `adjust_motion` running on `WalkForward + HoldKey.Run` |

(The InterpretedMotionState equivalent at line 293531 is functionally
the same with one extra branch — `current_style` initialization.)

---

## C. `adjust_motion` — the `WalkForward + Run` → `RunForward` transform

Line **305343–305400**, address `00528010`. This is what `DoMotion`
calls before `DoInterpretedMotion` to translate a raw key event into
a substate.

```c
void CMotionInterp::adjust_motion(this, arg2 /*&cmd*/, arg3 /*&speed*/, arg4 /*hold_key*/) {
    if (weenie_obj == 0 || weenie_obj->IsCreature()) {
        switch (*arg2) {
        case 0x65000010 /*SidestepLeft*/:
            *arg2 = 0x6500000f;                  // collapse Left → Right
            *arg3 *= -1;                          // with negative speed
            // fallthrough
        case 0x6500000f /*SidestepRight*/:
            // Sidestep speed-mod: (3.12/1.25) * 0.5 = 1.248
            *arg3 = (3.12f / 1.25f) * 0.5f * (*arg3);
            break;
        case 0x6500000e /*TurnRight*/:
            *arg2 = 0x6500000d;                  // collapse Right → Left
            *arg3 *= -1;                          // with negative speed
            break;
        case 0x45000006 /*WalkBackward*/:
            *arg2 = 0x45000005;                  // collapse to BackForward
            *arg3 = -0.65f * (*arg3);
            break;
        case 0x44000007 /*RunForward*/:
            // already a run cmd — fall through to apply_run_to_command
            break;
        }

        // Then: if hold_key == HoldKey_Run, escalate to RunForward
        HoldKey current = arg4 == HoldKey_Invalid ? raw_state.current_holdkey : arg4;
        if (current == HoldKey_Run)
            apply_run_to_command(this, arg2, arg3);
    }
}
```

`apply_run_to_command` (line 305062, addr `00527be0`):

```c
void CMotionInterp::apply_run_to_command(this, arg2, arg3) {
    float run_rate = weenie_obj ? weenie_obj->InqRunRate() : my_run_rate;

    if (*arg2 == 0x45000005 /*WalkForward*/) {
        if (*arg3 != 0)
            *arg2 = 0x44000007;          // → RunForward
        *arg3 *= run_rate;               // speed *= runRate (e.g. 2.94)
    } else if (*arg2 == 0x6500000d /*TurnLeft*/) {
        *arg3 *= 1.5f;                    // turn 1.5x while running
    } else if (*arg2 == 0x6500000f /*SidestepRight*/) {
        *arg3 *= run_rate;
        // clamp to ±3 m/s
        if (fabs(*arg3) > 3.0f)
            *arg3 = (sign(*arg3)) * 3.0f;
    }
}
```

So **the way `RunForward` gets into `forward_command` in retail is**:

1. Wire UM has `cmd=WalkForward (0x45000005)` + `hold_key=HoldKey_Run`
2. `DoMotion(0x45000005, params)` is called.
3. `adjust_motion` swaps `cmd → 0x44000007 RunForward`, `speed *= runRate`.
4. `RawMotionState::ApplyMotion(0x44000007, ...)` runs. The
   special-case `arg2 != 0x44000007` branch at line 293648 means
   RunForward is **NOT** stored in `forward_command` here. (This
   appears intentional — `RunForward` is the post-`adjust_motion`
   form; the persistent `RawMotionState` keeps the original
   WalkForward.)
5. **InterpretedMotionState** stores the post-adjust value because
   `apply_raw_movement` (305817) copies `raw_state.*` then runs
   `adjust_motion` over each of the three axes (305829-305831) before
   `apply_interpreted_movement` consumes it.

ACE matches this: it auto-upgrades `WalkForward + HoldKey.Run` →
`RunForward` on the **outbound** wire to remote observers, which is
why our inbound parser sees `fwd=0x07` for "remote is running."

---

## D. The cycle-decision core — `CMotionTable::GetObjectSequence`

Line **298636–298950**, address `00522860`. This is where a single
motion command lands and the `CSequence` is rebuilt. It is invoked once
per `DoInterpretedMotion` call.

Signature:
```c
int CMotionTable::GetObjectSequence(
    this,
    uint32_t   motion,           // arg2 — the command
    MotionState* state,          // arg3 — table-internal state
    CSequence* sequence,         // arg4 — the part-array sequence to mutate
    float      speed_mod,        // arg5
    uint32_t*  num_anims_out,    // arg6
    int32_t    force_flag);      // arg7 — re-modify recursion guard
```

**Three dispatch branches based on the high-bit class of `motion`**:

### D.1 — `motion < 0` (style change, e.g. `0x80000003D`)

Lines 298661–298735. Substate's effect: reset to default substate of
the new style, optionally clear modifiers, replace cycles.

### D.2 — `motion & 0x40000000` (substate)

Lines 298737–298848. The **forward-axis path**.

```c
if ((motion & 0x40000000) != 0) {                   // 298737
    uint32_t key = (motion & 0xffffff);
    MotionData* incoming = LongHash::lookup(&this->cycles, (state->style << 0x10) | key);
    if (incoming == 0)
        incoming = LongHash::lookup(&this->cycles, (this->default_style << 0x10) | key);  // fallback to default style
    if (incoming != 0 && is_allowed(this, motion, incoming, state)) {
        // Same-cycle re-speed shortcut: we're already on this cycle
        // and just changing speed (e.g. forward_speed delta)
        if (motion == state->substate &&
            same_sign(speed_mod, state->substate_mod) &&
            sequence->has_anims()) {
            change_cycle_speed(sequence, incoming, state->substate_mod, speed_mod);
            subtract_motion(sequence, incoming, state->substate_mod);
            combine_motion(sequence, incoming, speed_mod);
            state->substate_mod = speed_mod;
            return 1;
        }

        // Full transition: clear-anims + (link from current substate) + (incoming)
        if (incoming->bitfield & 1)
            state->clear_modifiers();           // some cycles clear modifiers on entry

        MotionData* link = get_link(this, state->style, state->substate, state->substate_mod, motion, speed_mod);
        // (with two-stage fallback through default_substate if direct link missing)

        sequence->clear_physics();
        sequence->remove_cyclic_anims();
        // If no direct link, route through default substate
        add_motion(sequence, link, ...);                       // transition anim
        add_motion(sequence, incoming, speed_mod);             // new cycle

        // Re-add prior substate as a modifier if it had the 0x20000000 flag
        if (state->substate != motion && (state->substate & 0x20000000))
            state->add_modifier_no_check(state->substate, state->substate_mod);

        state->substate_mod = speed_mod;
        state->substate = motion;
        re_modify(this, sequence, state);  // re-attach all modifiers
        return 1;
    }
}
```

**Key takeaway**: if the cycle-bound lookup `LongHash::lookup(&cycles,
(style<<16)|key)` returns NULL **and** the default-style fallback also
returns NULL, retail returns 0 (failure) and the call has **no effect**.
There is **no `RunForward → WalkForward → Ready` fallback chain** — that
is purely an acdream artifact.

### D.3 — `motion & 0x10000000` (action, e.g. emote)

Lines 298850–298907. **Overlay path**:

```c
if ((motion & 0x10000000) != 0) {
    uint32_t key = (state->style << 0x10) | (state->substate & 0xffffff);
    MotionData* current_substate_md = LongHash::lookup(&this->cycles, key);
    if (current_substate_md != 0) {
        MotionData* link = get_link(this, state->style, state->substate, state->substate_mod, motion, speed_mod);
        if (link != 0) {
            state->add_action(motion, speed_mod);   // append to action queue
            sequence->clear_physics();
            sequence->remove_cyclic_anims();        // remove looping anims
            add_motion(sequence, link, speed_mod);  // transition anim (one-shot)
            add_motion(sequence, current_substate_md, state->substate_mod);  // re-add substate cycle!
            re_modify(this, sequence, state);
            return 1;
        }
    }
}
```

**Crucial: actions DO NOT replace the substate cycle.** They prepend a
one-shot link animation, then re-add the current substate cycle so it
keeps looping after the action. Acdream's "Action route" is correct in
spirit but should preserve the running cycle exactly like this.

### D.4 — `motion & 0x20000000` (modifier — turn, sidestep, all overlay cycles)

Lines 298909–298945. **Modifier list overlay**:

```c
if ((motion & 0x20000000) != 0) {
    // current substate must be a non-OneShot cycle
    MotionData* current_substate_md = LongHash::lookup(&this->cycles, (state->style << 0x10) | (state->substate & 0xffffff));
    if (current_substate_md != 0 && (current_substate_md->bitfield & 1) == 0) {
        // Look up the modifier cycle
        MotionData* mod_md = LongHash::lookup(&this->modifiers, (state->style << 0x10) | (motion & 0xffffff));
        if (mod_md == 0)
            mod_md = LongHash::lookup(&this->modifiers, (motion & 0xffffff));  // default-style fallback

        if (mod_md != 0) {
            int rc = state->add_modifier(motion, speed_mod);  // adds to modifier_head list
            if (rc == 0) {
                // already has a modifier with this motion — stop it and re-add
                StopSequenceMotion(this, motion, 1.0f, state, sequence, &num_out);
                rc = state->add_modifier(motion, speed_mod);
            }
            if (rc != 0) {
                combine_motion(sequence, mod_md, speed_mod);  // BLEND velocity/omega into sequence
                return 1;
            }
        }
    }
}
```

**`combine_motion`** (line 298472, addr `00522580`) — adds the modifier's
velocity AND omega into the existing sequence via
`CSequence::combine_physics`. So **turn modifiers contribute their omega
on top of the substate's velocity**. This is how retail composes
"running while turning while strafing": three layers of physics
contributions in the same `CSequence`, animated by whichever layers
brought animations in.

---

## E. `is_allowed` — the gating predicate

Line **298526–298548**, address `005226c0`. Determines whether an
incoming substate is legal in the current state.

```c
int CMotionTable::is_allowed(this, motion, motion_data, state) {
    if (motion_data == 0) return 0;
    if ((motion_data->bitfield & 2) != 0) {       // requires "default substate"
        if (motion != state->substate) {
            // Look up the default substate for this style; legal only if state is in it
            uint32_t default_substate;
            LongNIValHash::lookup(&style_defaults, state->style, &default_substate);
            return (default_substate == state->substate) ? 1 : 0;
        }
    }
    return 1;
}
```

So a substate transition that requires the "ready" state (bitfield bit
1) will **fail** if the player is currently in a non-default substate.
This is the retail-correct way to block (for example) a Sit cycle
mid-Run — not a custom acdream "skip if airborne" hack.

---

## F. `re_modify` — the "re-attach modifiers after substate change"

Line **298300–298328**, address `005222e0`. After a substate transition
that may have cleared modifiers, this walks the modifier list and
re-applies each via `GetObjectSequence`:

```c
void CMotionTable::re_modify(this, sequence, state) {
    if (state->modifier_head == 0) return;
    MotionState backup;                                    // 298308
    MotionState::MotionState(&backup, state);
    while (i != 0) {
        MotionList* mod = state->modifier_head;
        uint32_t motion = mod->motion;
        float speed = mod->speed_mod;
        state->remove_modifier(mod, NULL);
        backup.remove_modifier(i, NULL);
        GetObjectSequence(this, motion, state, sequence, speed, &num_out, 0);  // recurse
    }
    backup.~MotionState();
}
```

This is why turn + sidestep persist across forward-cycle transitions
(WalkForward → RunForward) — they are stored in `modifier_head` and
get re-blended every time the substate changes.

---

## G. Final critical answers

### G.1 — When `forward=RunForward` + `sidestep=SidestepRight` + `turn=TurnLeft` arrive in one UM, what cycle plays?

**All three layered.** Specifically, after `apply_interpreted_movement`
processes the UM:

1. `DoInterpretedMotion(current_style)` — re-asserts style; usually
   no-op if unchanged.
2. `DoInterpretedMotion(0x44000007 RunForward, speed=runRate*1.0)` —
   `GetObjectSequence` takes the **substate** path (D.2). Replaces
   prior substate. `state->substate = RunForward`.
3. `DoInterpretedMotion(0x6500000f SidestepRight, speed=1.248)` —
   `GetObjectSequence` takes the **modifier** path (D.4). Adds to
   `modifier_head`, calls `combine_motion` to blend sidestep velocity
   into the running `CSequence`. **Substate cycle is unchanged** (still
   RunForward).
4. `DoInterpretedMotion(0x6500000d TurnLeft, speed=1.5)` — same as 3
   but for turn. Blends turn omega into the sequence.

**Visual result**: the RunForward animation cycle plays. Sidestep and
turn contribute velocity/omega only (their cycles are typically motion-
data with `velocity != 0` and `omega != 0` but `num_anims == 0` —
they're physics-only modifiers that don't override the running anim).
Some MotionTables may have animation content on sidestep/turn modifiers
for emphasis, in which case the bones get an additive blend.

### G.2 — Substate winner pick, sequential SetCycle, or Frame-level composition?

**Sequential `GetObjectSequence` calls per axis** (current_style →
forward → sidestep → turn), each mutating the same `CSequence` via:
- substate: `clear_physics + remove_cyclic_anims + add_motion(link) + add_motion(new)` (replace)
- modifier: `combine_motion` (additive blend) + `state->add_modifier` (track for re_modify)
- action: `clear_physics + remove_cyclic_anims + add_motion(link) + add_motion(current_substate)` (overlay-with-restore)

The final result is a single `CSequence` carrying:
- One looping substate cycle (animation + velocity/omega contribution)
- Zero or more queued action cycles (one-shot anims; auto-pop via
  `MotionState::remove_action_head` on completion)
- Zero or more modifier cycles (additive velocity/omega; usually no
  animation content)

There is **no priority pick**. There is **no Frame-level layering** —
all three are blended into the single `CSequence`'s velocity/omega
fields by `add_motion`/`combine_motion` and the result is integrated
once per physics tick.

### G.3 — Does the `RunForward → WalkForward → Ready` fallback chain exist?

**No.** `GetObjectSequence` has only one fallback: when the cycle for
the current style isn't found, fall back to `default_style`'s version
(line 298842 in style-change branch, line 298872-298886 in action
branch via `style_defaults` lookup). If neither exists, return 0 and
the call has no effect.

The acdream fallback (`RunForward → WalkForward → Ready`) is a
**port artifact** that papers over the fact that we're not using
`MotionTableManager` — we're synthesizing cycle-anim association
directly from a hardcoded enum. **In a faithful port this fallback
goes away.**

### G.4 — For Action overlay packets, does retail leave the substate cycle running?

**Yes, exactly.** D.3 above:
```c
add_motion(sequence, link, speed_mod);                      // one-shot transition
add_motion(sequence, current_substate_md, state->substate_mod);  // re-add running cycle
```

The `MotionState::action_head` queue tracks the active actions; the
sequence has both the action's transition anim AND the substate cycle
re-applied. When the action's one-shot anim completes,
`CSequence::CheckForCompletedMotions` (in `CPhysicsObj`) pops the
action and re-runs `apply_interpreted_movement` to restore pure
substate state.

---

## H. Acdream port implications

1. **Delete the priority cycle-picker** in `OnLiveMotionUpdated`. Replace
   with a faithful port of `apply_interpreted_movement`: 4 sequential
   `MotionTableManager.PerformMovement` calls (current_style, forward,
   sidestep, turn) per UM.

2. **Delete the `RunForward → WalkForward → Ready` fallback chain**
   entirely. If a MotionTable doesn't have a cycle, retail just
   silently fails to transition — there is no fallback. Our fallback
   is masking missing animation data.

3. **Port `MotionTableManager`** so we have an actual `MotionState`
   (style + substate + substate_mod + modifier_head + action_head)
   per remote object, and a `CMotionTable` lookup chain
   (`cycles`/`modifiers`/`links`/`style_defaults`). The current
   approach of "pick one cycle per UM and play it" cannot represent
   modifier overlay correctly.

4. **Run-detection: WalkForward+HoldKey.Run → RunForward** must happen
   in `adjust_motion` BEFORE the routing. Acdream's
   `AnimationCommandRouter.Classify` runs after this transform —
   correct in concept, but only if our outbound and inbound both
   apply the transform consistently. (ACE does this on the outbound,
   so inbound `0x07 RunForward` is post-adjusted.)

5. **Modifier physics**: `combine_motion` blends velocity AND omega
   into a single `CSequence`. Acdream's `ObservedOmega` workaround
   (audit doc 06 line 83) is a symptom of not blending omega into
   the per-tick velocity properly. Once `MotionTableManager` is
   ported, omega comes from `combine_motion` of TurnLeft's modifier
   cycle and the `update_object` MinQuantum hack disappears.

6. **Sidestep direction collapse**: retail collapses
   `SidestepLeft → SidestepRight (negative speed)` and
   `TurnRight → TurnLeft (negative speed)` in `adjust_motion`. The
   modifier list keys on the collapsed form. Acdream must do the
   same to match the modifier-table lookups.

---

## I. Citation index

| Function | Address | File line |
|---|---|---|
| `CMotionInterp::DoMotion` | `00528d20` | 306159 |
| `CMotionInterp::DoInterpretedMotion` | `00528360` | 305575 |
| `CMotionInterp::adjust_motion` | `00528010` | 305343 |
| `CMotionInterp::apply_run_to_command` | `00527be0` | 305062 |
| `CMotionInterp::apply_interpreted_movement` | `00528600` | 305713 |
| `CMotionInterp::apply_raw_movement` | `005287e0` | 305817 |
| `CMotionInterp::apply_current_movement` | `00528870` | 305838 |
| `CPhysicsObj::DoInterpretedMotion` | `0050ea70` | 276348 |
| `CPartArray::DoInterpretedMotion` | `00518750` | 286772 |
| `MotionTableManager::PerformMovement` | `0051c0b0` | 290906 |
| `MotionTableManager::initialize_state` | `0051c030` | 290875 |
| `CMotionTable::GetObjectSequence` | `00522860` | 298636 |
| `CMotionTable::DoObjectMotion` | `00523e90` | 300045 |
| `CMotionTable::StopObjectMotion` | `00523ec0` | 300053 |
| `CMotionTable::StopSequenceMotion` | `00522fc0` | 298954 |
| `CMotionTable::SetDefaultState` | `005230a0` | 299004 |
| `CMotionTable::is_allowed` | `005226c0` | 298526 |
| `CMotionTable::get_link` | `00522710` | 298552 |
| `CMotionTable::re_modify` | `005222e0` | 298300 |
| `RawMotionState::ApplyMotion` | `0051eb60` | 293630 |
| `InterpretedMotionState::ApplyMotion` | `0051ea40` | 293531 |
| `MotionState::add_modifier` | `00526340` | 303081 |
| `MotionState::add_modifier_no_check` | `00525ff0` | 302772 |
| `MotionState::add_action` | `005260a0` | 302828 |
| `MotionState::clear_modifiers` | `00526070` | 302810 |
| `MotionState::remove_modifier` | `00526040` | 302794 |
| `add_motion` (free fn) | `005224b0` | 298437 |
| `combine_motion` (free fn) | `00522580` | 298472 |
| `subtract_motion` (free fn) | `00522600` | 298492 |

| Constant | Value | Meaning |
|---|---|---|
| `0x40000000` | flag | substate class bit (forward axis) |
| `0x10000000` | flag | action class bit |
| `0x20000000` | flag | modifier class bit |
| `0x44000007` | id | RunForward substate |
| `0x45000005` | id | WalkForward substate |
| `0x45000006` | id | WalkBackward substate (collapses to BackForward) |
| `0x40000011` | id | (referenced in jump path) |
| `0x40000015` | id | Stand substate |
| `0x41000003` | id | Ready substate |
| `0x6500000d` | id | TurnLeft modifier |
| `0x6500000e` | id | TurnRight modifier (collapses to TurnLeft) |
| `0x6500000f` | id | SidestepRight modifier |
| `0x65000010` | id | SidestepLeft modifier (collapses to SidestepRight) |
| `0x6500000f` (jump-charge) | id | charge_jump cycle |
| `0x8000003d` | id | "no style" sentinel (CombatMode_NonCombat default) |
