fix(runtime): align portal and movement presentation
Port retail portal viewport projection and reveal behavior, preserve outbound combat style, drive remote and local grounded movement from authored CSequence root frames, and reuse the local prepared pose so animation hooks advance once. User-verified portal, observer movement, combat stance, and short-tap locomotion gates. Release build passed with 5,767 tests and five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
e95f55f25b
commit
124e046976
30 changed files with 1362 additions and 348 deletions
|
|
@ -0,0 +1,84 @@
|
|||
# Retail combat-style ownership in outbound movement
|
||||
|
||||
## Symptom
|
||||
|
||||
The local acdream player remains visually in combat while moving, but a retail
|
||||
observer sees the player leave the combat stance and locomote in NonCombat.
|
||||
|
||||
## Named-retail oracle
|
||||
|
||||
`CommandInterpreter::SendMovementEvent @ 0x006B4680` does not construct a new
|
||||
axis-only motion object. It obtains the player's canonical raw state and passes
|
||||
that exact object to the packet constructor:
|
||||
|
||||
```text
|
||||
player = CommandInterpreter.player
|
||||
raw = player.InqRawMotionState()
|
||||
if player != null and smartbox != null and raw != null and autonomy_level != 0:
|
||||
packet = MoveToStatePack(
|
||||
raw,
|
||||
player.position,
|
||||
player is Contact+OnWalkable,
|
||||
player.minterp.standing_longjump,
|
||||
player movement timestamps)
|
||||
SendMoveToStateEvent(packet)
|
||||
```
|
||||
|
||||
`CPhysicsObj::InqRawMotionState @ 0x0050FDE0` returns
|
||||
`MovementManager::InqRawMotionState`; stance and movement axes therefore have
|
||||
one canonical owner.
|
||||
|
||||
`RawMotionState::Pack @ 0x0051ED10` compares `current_style` with the retail
|
||||
default `MotionStance_NonCombat` (`0x8000003D`):
|
||||
|
||||
```text
|
||||
flags.bit1 = current_style != NonCombat
|
||||
|
||||
write flags
|
||||
if flags.bit0: write current_holdkey
|
||||
if flags.bit1: write current_style as uint32
|
||||
if flags.bit2: write forward_command
|
||||
... write the remaining axes and action queue ...
|
||||
```
|
||||
|
||||
`RawMotionState::UnPack @ 0x0051EFC0` restores NonCombat when bit 1 is absent.
|
||||
Omitting the bit is therefore an explicit NonCombat value, not “preserve the
|
||||
receiver's previous stance.”
|
||||
|
||||
## Reference cross-checks
|
||||
|
||||
- ACE `Network/Motion/RawMotionState.cs` reads `RawMotionFlags.CurrentStyle`
|
||||
(`0x2`) as a 32-bit stance.
|
||||
- ACE `Network/Motion/MovementData.cs` copies that field into the interpreted
|
||||
state relayed to observers only when the flag is present.
|
||||
- acclientlib `UtilityBelt.Common/DataTypes.cs` independently documents that an
|
||||
absent raw `CurrentStyle` defaults to `0x3D` NonCombat.
|
||||
|
||||
Thus an axis-only acdream packet necessarily makes ACE/retail reconstruct
|
||||
NonCombat movement.
|
||||
|
||||
## acdream root cause and port
|
||||
|
||||
`PlayerMovementController` already owns retail's canonical
|
||||
`MotionInterpreter.RawState`, and inbound server stance changes correctly
|
||||
update `RawState.CurrentStyle`. However, `MovementResult` omitted that field and
|
||||
`LocalPlayerOutboundController.BuildRawMotionState` rebuilt only the hold key
|
||||
and three axes. Its new `RawMotionState` retained the constructor default
|
||||
NonCombat, so `RawMotionStatePacker` correctly omitted the CurrentStyle bit.
|
||||
|
||||
Port the missing canonical field through the existing immutable frame result:
|
||||
|
||||
```text
|
||||
MovementResult.CurrentStyle = MotionInterpreter.RawState.CurrentStyle
|
||||
|
||||
BuildRawMotionState(result):
|
||||
raw.CurrentStyle = result.CurrentStyle
|
||||
raw.CurrentHoldKey = result current hold level
|
||||
raw.forward/side/turn = result axes
|
||||
return raw
|
||||
```
|
||||
|
||||
This preserves the existing update-thread ownership and packet scheduler. It
|
||||
does not infer a stance from `CombatMode` or equipped items; the raw motion
|
||||
state remains the oracle, exactly as retail. The still-unwired raw action queue
|
||||
is separate and remains tracked by divergence TS-24.
|
||||
Loading…
Add table
Add a link
Reference in a new issue