feat(physics): R5-V1 — port PositionManager/Sticky/Constraint + TargetManager (Core, unwired)
The retail movement-manager family the R4 MoveToManager port left as do-not-invent seams (decomp §9f/§9g). Faithful C# ports of retail's PositionManager facade + StickyManager + ConstraintManager + the TargetManager voyeur system, with full conformance tests. NO wiring yet — purely additive, no behavior change. Wiring (retiring TS-39 sticky + AP-79 target adapter) is R5-V2/V3. New Core classes (src/AcDream.Core/Physics/Motion/): - StickyManager (0x00555400): follow-a-target steering. adjust_offset's dense x87 mush decoded via ACE (StickyRadius 0.3, StickyTime 1.0, follow speed ×5 / fallback 15) — speed-clamped signed-distance steer + bounded turn-to-face; 1 s watchdog; Ok→initialized / non-Ok→teardown. - ConstraintManager (0x00556090): the server-position rubber-band leash. 90% IsFullyConstrained jump gate + grounded linear brake taper. Structural only — acdream never ARMS it (retail arms from SmartBox::HandleReceivedPosition, which acdream lacks, with two x87 constants BN elided). IsFullyConstrained stays false = TS-35 behavior; leash-arming + the unknown constants are a deferred issue. - PositionManager facade (0x00555160): lazy Sticky/Constraint + fan-out. - TargetManager (0x0051a370) + TargettedVoyeurInfo: the peer-to-peer voyeur subscription system (0.5 s throttle, 10 s staleness, send-on-drift-past-radius, dead-reckon GetInterpolatedPosition). A faithful superset of the AP-79 adapter — SetTarget subscribes ON the target; the target's HandleTargetting pushes updates back. - IPhysicsObjHost: the CPhysicsObj back-pointer seam (position/velocity/ radius/contact/GetObjectA + target-tracking fan-out) the App wires per entity in V2/V3. MotionDeltaFrame: mutable retail-Frame delta accumulator. Supporting: - TargetInfo extended to the full retail 10-field struct (additive defaults keep the R4 4-arg call sites compiling). - MoveToMath: signed CylinderDistanceNoZ, NormalizeCheckSmall, GlobalToLocalVec. - Rename: the misnamed AcDream.Core.Physics.PositionManager (a remote anim+interp per-frame combiner, NOT the retail facade) → RemoteMotion Combiner, freeing the name and removing the ambiguity that breaks every file importing both Physics + Physics.Motion (GameWindow will in V2/V3). Tests: 42 new conformance cases (Sticky/Constraint/Position facade + TargetManager incl. the full cross-entity voyeur round-trip). Full suite 4006 green (+2 skipped), no regressions. Decomp + ACE cross-ref + port plan: docs/research/2026-07-03-r5-managers/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
517bbfdae4
commit
3d89446d98
25 changed files with 7279 additions and 12 deletions
338
docs/research/2026-07-03-r5-managers/r5-port-plan.md
Normal file
338
docs/research/2026-07-03-r5-managers/r5-port-plan.md
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
# R5 port plan — MovementManager facade + PositionManager (Sticky/Constraint) + TargetManager
|
||||
|
||||
Synthesis of the R5 recon (2026-07-03). Verbatim retail decomp lives in the
|
||||
sibling files (`r5-*-decomp.md`); ACE cross-reference in `r5-ace-crossref.md`;
|
||||
current acdream integration seams in `r5-acdream-seams.md`. This doc is the
|
||||
**pseudocode + integration + sub-slice plan** (mandatory workflow step 3).
|
||||
|
||||
Retirement targets: **TS-39** (sticky no-op seams) and **AP-79** (the P4
|
||||
TargetTracker adapter). ConstraintManager is ported for structural completeness
|
||||
of PositionManager but its arming stays unported (see §Constraint scope) — **TS-35
|
||||
stays open**.
|
||||
|
||||
---
|
||||
|
||||
## 0. The retail structure vs acdream's reality
|
||||
|
||||
**Retail** (`CPhysicsObj` owns three managers, all lazily created):
|
||||
|
||||
```
|
||||
CPhysicsObj
|
||||
├── movement_manager : MovementManager (facade → MotionInterp + MoveToManager)
|
||||
├── position_manager : PositionManager (facade → Interpolation + Sticky + Constraint)
|
||||
└── target_manager : TargetManager (voyeur subscription system)
|
||||
```
|
||||
|
||||
Per-tick, `CPhysicsObj::UpdateObjectInternal` (0x005156b0) fans out in THIS order
|
||||
(decomp §positionmanager-sticky callers):
|
||||
|
||||
1. `DetectionManager::CheckDetection`
|
||||
2. `TargetManager::HandleTargetting` ← voyeur tick (no separate UseTime)
|
||||
3. `MovementManager::UseTime` → `MoveToManager::UseTime` only
|
||||
4. `CPartArray::HandleMovement`
|
||||
5. `PositionManager::UseTime` → Interp/Sticky/Constraint UseTime
|
||||
|
||||
And `UpdatePositionInternal` (0x00512c30) composes the frame delta:
|
||||
`Frame::cache(&d)` → `CPartArray::Update(d)` (animation) →
|
||||
`PositionManager::adjust_offset(&d, quantum)` (interp+sticky+constraint ADD into
|
||||
the SAME delta `d`) → `Frame::combine(out, m_position.frame, d)` → transition
|
||||
(collision) → commit. **Sticky/constraint steering composes with animation motion
|
||||
in the same per-tick delta frame, before collision resolves it.**
|
||||
|
||||
**acdream** has no per-entity `CPhysicsObj`. The per-entity owner today is:
|
||||
|
||||
| Retail | acdream (remote) | acdream (player) |
|
||||
|---|---|---|
|
||||
| `CPhysicsObj` | `GameWindow.RemoteMotion` (nested class, GameWindow.cs:411) | `_playerController` (`PlayerMovementController`) + GameWindow fields |
|
||||
| `movement_manager` | `rm.Motion` (MotionInterpreter) + `rm.MoveTo` (MoveToManager) | `_playerController.Motion` + `playerMoveTo` |
|
||||
| `position_manager` | **absent** | **absent** |
|
||||
| `target_manager` | **AP-79 adapter**: `rm.TrackedTarget*` fields + `TickRemoteMoveTo` | **AP-79 adapter**: `_playerMoveToTarget*` fields + `OnUpdateFrame` block |
|
||||
| `UpdateObjectInternal` tick | `TickRemoteMoveTo(rm)` (GameWindow.cs:4469) | `OnUpdateFrame` block (GameWindow.cs:7994) + `_playerController.Update` |
|
||||
|
||||
R5 adds the two missing managers per-entity. The MovementManager facade (collapsing
|
||||
`Motion`+`MoveTo` into one owner) is the optional capstone (§V4) — the retirement
|
||||
targets don't require it, so it lands last and only if the arc has budget.
|
||||
|
||||
---
|
||||
|
||||
## 1. Struct/field map (acclient.h → C#)
|
||||
|
||||
```
|
||||
MovementManager (0x10): motion_interpreter, moveto_manager, physics_obj, weenie_obj
|
||||
PositionManager (0x10): interpolation_manager, sticky_manager, constraint_manager, physics_obj
|
||||
StickyManager (0x60): target_id:uint, target_radius:float, target_position:Position,
|
||||
physics_obj, initialized:int, sticky_timeout_time:double
|
||||
ConstraintManager(0x5c): physics_obj, is_constrained:int, constraint_pos_offset:float,
|
||||
constraint_pos:Position, constraint_distance_start:float,
|
||||
constraint_distance_max:float
|
||||
TargetManager (0x18): physobj, target_info:TargetInfo*, voyeur_table:Hash<TargettedVoyeurInfo>,
|
||||
last_update_time:double
|
||||
TargetInfo (0xd0): context_id:uint, object_id:uint, radius:float, quantum:double,
|
||||
target_position:Position, interpolated_position:Position,
|
||||
interpolated_heading:Vec3, velocity:Vec3, status:TargetStatus,
|
||||
last_update_time:double
|
||||
TargettedVoyeurInfo(0x60): object_id:uint, quantum:double, radius:float,
|
||||
last_sent_position:Position
|
||||
```
|
||||
|
||||
acdream already has `TargetInfo` (4-field record) + `TargetStatus` enum in
|
||||
`MoveToManager.cs` — R5 EXTENDS `TargetInfo` to the full 10-field shape (add
|
||||
`ContextId`, `Radius`, `Quantum`, `InterpolatedHeading`, `Velocity`,
|
||||
`LastUpdateTime`). The existing `MoveToManager.HandleUpdateTarget` only reads
|
||||
`ObjectId`/`Status`/`InterpolatedPosition`, so the extension is additive-safe.
|
||||
|
||||
---
|
||||
|
||||
## 2. The math — decoded (ACE is the oracle for the x87 mush)
|
||||
|
||||
All three `adjust_offset` bodies and both timeout gates were dense x87 mush in the
|
||||
BN decomp. ACE's ports resolve them cleanly and were confirmed against the mush
|
||||
structure (fld/fmul/fsqrt/fcomp shapes match). **Port ACE's structure; cite the
|
||||
retail address.**
|
||||
|
||||
### 2a. StickyManager.adjust_offset (retail 0x00555430, ACE StickyManager.cs:91)
|
||||
|
||||
```
|
||||
// Guard: no-op unless PhysicsObj && target_id != 0 && initialized
|
||||
target = GetObjectA(target_id) // live resolve
|
||||
targetPos = target != null ? target.Position : cached target_position // fallback to last-known
|
||||
offset = self.Position.GetOffset(targetPos) // world vector self→target
|
||||
offset = self.Position.GlobalToLocalVec(offset) // rotate into own frame
|
||||
offset.Z = 0 // horizontal-only
|
||||
|
||||
radius = self.GetRadius()
|
||||
dist = Position.CylinderDistanceNoZ(radius, self.Position, target_radius, targetPos)
|
||||
- 0.3f // StickyRadius const (ACE StickyRadius=0.3f)
|
||||
|
||||
if NormalizeCheckSmall(ref offset): offset = Vector3.Zero // too close → don't chase jitter
|
||||
|
||||
speed = 0
|
||||
minterp = self.get_minterp()
|
||||
if minterp != null: speed = minterp.get_max_speed() * 5.0f // 5× own max speed (catch up)
|
||||
if speed < EPSILON: speed = 15.0f // fallback
|
||||
|
||||
delta = speed * quantum
|
||||
if delta >= abs(dist): delta = dist // don't overshoot (dist can be NEGATIVE)
|
||||
offset.Origin *= delta
|
||||
|
||||
curHeading = self.Position.Frame.get_heading()
|
||||
targetHeading = self.Position.heading(targetPos)
|
||||
h = targetHeading - curHeading
|
||||
if abs(h) < EPSILON: h = 0
|
||||
if h < -EPSILON: h += 360
|
||||
offset.set_heading(h) // per-tick RELATIVE turn toward target
|
||||
```
|
||||
|
||||
Net: a speed-clamped horizontal steer + bounded turn toward the stuck target,
|
||||
written into the shared per-tick delta frame. `StickyRadius=0.3f`, `StickyTime=1.0f`
|
||||
are the two named constants.
|
||||
|
||||
### 2b. ConstraintManager.adjust_offset (retail 0x00556180, ACE ConstraintManager.cs:62)
|
||||
|
||||
```
|
||||
// Guard: no-op unless PhysicsObj && is_constrained
|
||||
if (self.TransientState & Contact): // ONLY clamps while grounded
|
||||
if constraint_pos_offset < constraint_distance_max:
|
||||
if constraint_pos_offset > constraint_distance_start:
|
||||
offset.Origin *= (constraint_distance_max - constraint_pos_offset)
|
||||
/ (constraint_distance_max - constraint_distance_start) // linear brake taper
|
||||
else:
|
||||
offset.Origin = Vector3.Zero // fully pinned
|
||||
// UNCONDITIONAL (grounded or airborne):
|
||||
constraint_pos_offset = offset.Origin.Length() // NB: length of the per-tick step, NOT distance-to-anchor
|
||||
```
|
||||
|
||||
`ConstraintPos` (the anchor) is stored by ConstrainTo but never read by
|
||||
adjust_offset — the taper uses `constraint_pos_offset` (last tick's step length)
|
||||
vs start/max. See §ConstraintScope for why acdream never arms this.
|
||||
|
||||
### 2c. ConstraintManager.IsFullyConstrained (retail 0x005560d0, ACE:37)
|
||||
|
||||
```
|
||||
return constraint_distance_max * 0.9f < constraint_pos_offset; // ≥90% of leash → "fully constrained"
|
||||
```
|
||||
|
||||
Read by `jump_is_allowed`: if true → return 0x47 (block jump). Since acdream never
|
||||
arms the constraint, this stays false → jump never blocked here (= TS-35 current
|
||||
behavior).
|
||||
|
||||
### 2d. TargetManager quantum gates (retail 0x0051aa90 / 0x0051a650)
|
||||
|
||||
```
|
||||
HandleTargetting(): // per-tick, self-throttled
|
||||
if PhysicsTimer.CurrentTime - last_update_time < 0.5f: return // 0.5s throttle
|
||||
if target_info != null && target_info.target_position == null: return
|
||||
if target_info != null && target_info.status == Undefined
|
||||
&& target_info.last_update_time + 10.0f < Timer.cur_time: // 10s staleness
|
||||
target_info.status = TimedOut
|
||||
PhysicsObj.HandleUpdateTarget(copy(target_info))
|
||||
for voyeur in voyeur_table.Values.ToList():
|
||||
CheckAndUpdateVoyeur(voyeur)
|
||||
last_update_time = PhysicsTimer.CurrentTime
|
||||
|
||||
CheckAndUpdateVoyeur(voyeur):
|
||||
newPos = GetInterpolatedPosition(voyeur.quantum) // self.pos + velocity*quantum (dead-reckon)
|
||||
if newPos.Distance(voyeur.last_sent_position) > voyeur.radius: // send-on-significant-move
|
||||
SendVoyeurUpdate(voyeur, newPos, Ok)
|
||||
|
||||
GetInterpolatedPosition(quantum):
|
||||
pos = copy(self.Position); pos.Origin += self.get_velocity() * quantum; return pos
|
||||
```
|
||||
|
||||
`0.5f` throttle, `10.0f` staleness — retail-tuned quanta (cross-checked ACE; verify
|
||||
against named-retail before treating literally, per ACE's own `// ref?` caveats).
|
||||
|
||||
### 2e. The voyeur round-trip (peer-to-peer position notification)
|
||||
|
||||
```
|
||||
Watcher W wants to track target T:
|
||||
W.SetTarget(ctx=0, T.id, radius=0.5, quantum=q)
|
||||
→ W.target_info = new TargetInfo(...)
|
||||
→ T.add_voyeur(W.id, 0.5, q) // W subscribes ON T's voyeur_table
|
||||
→ T immediately SendVoyeurUpdate(W-voyeur, T.pos, Ok) // initial snapshot
|
||||
→ W.receive_target_update(info) → W.ReceiveUpdate(info)
|
||||
→ W.target_info updated; W.HandleUpdateTarget(info)
|
||||
→ W.movement_manager.HandleUpdateTarget (MoveToManager steers)
|
||||
→ W.position_manager.HandleUpdateTarget (StickyManager follows)
|
||||
|
||||
Each tick, T.HandleTargetting → CheckAndUpdateVoyeur(W-voyeur):
|
||||
if T drifted > radius since last sent → SendVoyeurUpdate → W.ReceiveUpdate → W.HandleUpdateTarget
|
||||
|
||||
W stops: W.ClearTarget → T.remove_voyeur(W.id)
|
||||
T despawns: T.exit_world → NotifyVoyeurOfEvent(ExitWorld) → all watchers get ExitWorld
|
||||
T teleports: T.teleport_hook → NotifyVoyeurOfEvent(Teleported)
|
||||
```
|
||||
|
||||
This is exactly what the AP-79 adapter approximates (poll target pos, feed
|
||||
HandleUpdateTarget when drift>radius). The voyeur port is a **faithful superset**:
|
||||
same moveto behavior + correct sticky + timeout/exit/teleport events.
|
||||
|
||||
---
|
||||
|
||||
## 3. Two consumers of set_target (both radius 0.5)
|
||||
|
||||
| Caller | quantum | Meaning |
|
||||
|---|---|---|
|
||||
| `MoveToManager.MoveToObject/TurnToObject` | **0.0** | resend as fast as the 0.5s tick allows |
|
||||
| `StickyManager.StickTo` | **0.5** | throttled resend |
|
||||
|
||||
Both go through the SAME `CPhysicsObj::set_target → TargetManager::SetTarget`, and
|
||||
both receive updates through `CPhysicsObj::HandleUpdateTarget →
|
||||
{MovementManager, PositionManager}::HandleUpdateTarget`. In acdream the
|
||||
`_setTarget`/`_clearTarget` seams (already on MoveToManager) get repointed at the
|
||||
real TargetManager; StickyManager gets its own seam to the same TargetManager.
|
||||
|
||||
---
|
||||
|
||||
## 4. acdream integration seams (delegate contracts)
|
||||
|
||||
The Core classes stay engine-agnostic via ctor-injected seams (same convention as
|
||||
MoveToManager). Per entity the App layer supplies:
|
||||
|
||||
- `getPosition : () → Position` (world-space, cell + frame)
|
||||
- `getVelocity : () → Vector3`
|
||||
- `getRadius : () → float`, `getHeight : () → float` (from setup cylsphere — **finally
|
||||
needed here; today MoveToManager passes 0** — see the P4 note)
|
||||
- `getMinterpMaxSpeed : () → float?` (null if no interp) — StickyManager speed
|
||||
- `getContact : () → bool` (transient Contact bit) — ConstraintManager gate
|
||||
- `getObjectA : uint guid → IPhysicsTargetHandle?` — cross-entity resolve (the guid→entity
|
||||
seam; drives voyeur delivery + sticky live-target). Backed by GameWindow's
|
||||
`_entitiesByServerGuid`.
|
||||
- `handleUpdateTarget : TargetInfo → void` — fans to MoveToManager + PositionManager(Sticky)
|
||||
- `clearTarget`/`interruptCurrentMovement` — teardown (already exist on MoveToManager/Motion)
|
||||
- clock : `() → double` (real clock, as R4-V5 fixed)
|
||||
|
||||
`getObjectA` returns a small handle exposing the OTHER entity's
|
||||
`receive_target_update(TargetInfo)` (→ its TargetManager.ReceiveUpdate) + its live
|
||||
Position — enough for `SendVoyeurUpdate` and sticky's live-target resolve.
|
||||
|
||||
---
|
||||
|
||||
## 5. Sub-slice plan
|
||||
|
||||
### R5-V1 — Core classes + conformance tests (NO wiring; low risk)
|
||||
New files under `src/AcDream.Core/Physics/Motion/` (Position/Sticky/Constraint) and
|
||||
`src/AcDream.Core/Physics/Combat/` (TargetManager — retail namespace split):
|
||||
- `PositionManager` (facade: adjust_offset chain interp→sticky→constraint, UseTime,
|
||||
StickTo/Unstick/ConstrainTo/UnConstrain/IsFullyConstrained/HandleUpdateTarget).
|
||||
- `StickyManager` (§2a math, StickTo/UnStick/UseTime timeout/HandleUpdateTarget/adjust_offset).
|
||||
- `ConstraintManager` (§2b/§2c, ConstrainTo/UnConstrain/IsFullyConstrained/adjust_offset).
|
||||
- `TargetManager` (§2d/§2e full voyeur system) + `TargettedVoyeurInfo` + extended `TargetInfo`.
|
||||
- InterpolationManager: acdream already has a remote `Interp` (PhysicsBody interp) —
|
||||
keep it; PositionManager's interp slot delegates to a thin adapter or is left null
|
||||
for the player. (Interp is NOT an R5 retirement target; don't re-port it.)
|
||||
- Seam interfaces + a test harness with a fake `getObjectA` wiring two managers to
|
||||
each other. Golden values from ACE + the decoded math above. Tests:
|
||||
sticky steer/clamp/heading, sticky 1s timeout, constraint taper + IsFullyConstrained
|
||||
90% gate, voyeur subscribe/immediate-snapshot/distance-gate/10s-timeout/exit/teleport,
|
||||
ReceiveUpdate match+heading-fallback, the full W↔T round-trip.
|
||||
- `dotnet build`+`dotnet test` green. Commit. **No behavior change** (nothing wired).
|
||||
|
||||
### R5-V2 — Wire TargetManager per-entity; retire AP-79
|
||||
- Add `TargetManager` to `RemoteMotion` + a player-side owner; construct beside the
|
||||
MoveToManager binds. Repoint MoveToManager's `_setTarget`/`_clearTarget`/quantum
|
||||
seams at `TargetManager.SetTarget`/`ClearTarget`/quantum.
|
||||
- `getObjectA` backed by `_entitiesByServerGuid` (returns a handle to the target's
|
||||
TargetManager). Per-tick: call `TargetManager.HandleTargetting()` in
|
||||
`TickRemoteMoveTo` + the player block, BEFORE `MoveToManager.UseTime()` (retail order).
|
||||
- Delete the AP-79 tracker fields (`TrackedTarget*` / `_playerMoveToTarget*`) and the
|
||||
manual poll blocks — the voyeur round-trip replaces them.
|
||||
- Register: delete AP-79 row same commit. **Visual gate**: a server-directed creature
|
||||
chasing the player still tracks + moves identically (the exact behavior AP-79 drove).
|
||||
|
||||
### R5-V3 — Wire PositionManager (Sticky); retire TS-39; apply mt-0 sticky guid
|
||||
- Add `PositionManager` to each entity. Bind `MoveToManager.StickTo →
|
||||
PositionManager.StickTo`, `MoveToManager.Unstick → PositionManager.UnStick`.
|
||||
- Integrate `PositionManager.adjust_offset` into the per-frame body integration
|
||||
(the composed delta-frame chokepoint — retail's UpdatePositionInternal). This is
|
||||
the load-bearing wiring: sticky steer must ADD to the body's per-tick motion.
|
||||
- Per-tick `PositionManager.UseTime()` AFTER MoveToManager.UseTime (retail order).
|
||||
- Apply the mt-0 wire flags (UpdateMotion.cs already parses them, unconsumed):
|
||||
`0x1 StickToObject` → `stick_to_object(guid)` → `PositionManager.StickTo`;
|
||||
`0x2 StandingLongJump` → `MotionInterpreter.StandingLongJump`.
|
||||
- Register: delete TS-39 row same commit. **Visual gate**: a sticky scenario (server
|
||||
/follow-style sticky moveto or a moving-platform stick) holds the target instead of
|
||||
stopping.
|
||||
|
||||
### R5-V4 (capstone, optional) — MovementManager facade + #164 + head stance dispatch + docs
|
||||
- Collapse per-entity `Motion`+`MoveTo` into a `MovementManager` owner (structural;
|
||||
must keep the 183-case + funnel + moveto suites green UNMODIFIED).
|
||||
- #164: action-replay Autonomous bit (params 0x1000 from per-action autonomy flag,
|
||||
raw 305982) in `MotionInterpreter.MoveToInterpretedState` action loop.
|
||||
- Head stance-change dispatch for mt 6-9 (verification item 1 / the S3 exclusion at
|
||||
`RetailObserverTraceConformanceTests.cs:33`).
|
||||
- Final register/ISSUES/roadmap/memory + successor handoff.
|
||||
|
||||
---
|
||||
|
||||
## Constraint scope (why TS-35 stays open)
|
||||
|
||||
ConstraintManager is the **server-position rubber-band leash**, armed ONLY from
|
||||
`SmartBox::HandleReceivedPosition` (arm on every inbound position packet, anchor =
|
||||
self or server-received position) with two constants from `GetStart/MaxConstraintDistance`
|
||||
(0x0050ebc0 / 0x0050ec10) that are **x87 float returns BN elided — literal values
|
||||
unknown** (need a live cdb read or Ghidra float-return fix). acdream has no SmartBox
|
||||
leash (its position reconciliation is separate), so:
|
||||
- Port the class + wire it into PositionManager (IsFullyConstrained routes through it;
|
||||
adjust_offset participates in the chain) for structural faithfulness.
|
||||
- **Do NOT arm it** (no ConstrainTo call site) — the leash + jump-blocking + the two
|
||||
unknown constants are out of R5 scope. IsFullyConstrained stays false = TS-35's
|
||||
current behavior. Update TS-35's `where` to point at the now-real-but-unarmed
|
||||
ConstraintManager; keep the row OPEN. Filing an issue for the leash port + the two
|
||||
unknown constants.
|
||||
|
||||
## Open verification items (carry into V1 tests / cdb)
|
||||
1. `GetStart/MaxConstraintDistance` x87 return constants — unknown (constraint arming,
|
||||
deferred). File as issue.
|
||||
2. `HandleTargetting`'s 0.5s/10s quanta + `StickyTime` 1.0s / `StickyRadius` 0.3f /
|
||||
sticky speed `×5` + `15.0f` fallback — ACE values; verify against named-retail.
|
||||
3. mt-0 header flags: confirm `0x1`/`0x2` (wire) == decomp `0x100`/`0x200` shift
|
||||
(recon §4 confirms: same bits, 8-bit motionFlags at bit-8 of the unpack header dword).
|
||||
4. `TargetInfo` pass-by-value-vs-ref (ACE's two `// ref?` markers) — retail copy-constructs
|
||||
(`TargetInfo::TargetInfo(©, src)` at every fan-out); port as copy.
|
||||
|
||||
## Do-NOT-re-port (already shipped)
|
||||
MoveToManager (R4, full incl. queue + HandleUpdateTarget consumer), MotionInterpreter
|
||||
(R3, #161 apply-pass), MotionTableManager+GetObjectSequence (R2), CSequence (R1),
|
||||
RemoteWeenie + PhysicsBody.InWorld (R4-V5), InterpolationManager (acdream's remote
|
||||
Interp — not an R5 target).
|
||||
Loading…
Add table
Add a link
Reference in a new issue