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
|
|
@ -230,6 +230,58 @@ public static class MoveToMath
|
|||
return edgeDist > 0f ? edgeDist : 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail <c>Position::cylinder_distance_no_z</c> — the <b>signed</b>
|
||||
/// horizontal (X/Y) edge-to-edge distance between two cylinders:
|
||||
/// <c>centerDist − ownRadius − targetRadius</c>. Unlike
|
||||
/// <see cref="CylinderDistance"/> (the arrival-gate variant, which CLAMPS at
|
||||
/// 0), this variant is NOT clamped — overlapping cylinders report a NEGATIVE
|
||||
/// value. <c>StickyManager::adjust_offset</c> (0x00555430) relies on the
|
||||
/// sign: when the follower is inside the desired 0.3 m stick gap the
|
||||
/// distance goes negative, the per-tick delta inverts, and the mover backs
|
||||
/// off to restore the gap (ACE StickyManager.cs:156).
|
||||
/// </summary>
|
||||
public static float CylinderDistanceNoZ(
|
||||
float ownRadius, Vector3 ownPos, float targetRadius, Vector3 targetPos)
|
||||
{
|
||||
float dx = targetPos.X - ownPos.X;
|
||||
float dy = targetPos.Y - ownPos.Y;
|
||||
float centerDist = MathF.Sqrt(dx * dx + dy * dy);
|
||||
return centerDist - ownRadius - targetRadius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail <c>AC1Legacy::Vector3::normalize_check_small</c> — normalize
|
||||
/// <paramref name="v"/> in place, returning <c>true</c> if the vector was
|
||||
/// too small to normalize (near-zero) and leaving it unchanged in that
|
||||
/// case. Consumed by <c>StickyManager::adjust_offset</c> (don't chase
|
||||
/// jitter when already at the target) and
|
||||
/// <see cref="TargetManager.ReceiveUpdate"/> (interpolated-heading
|
||||
/// fallback). A public shared twin of the private helper in
|
||||
/// <c>ParticleSystem</c>; same 1e-8 near-zero length guard.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> = too small (left unchanged); <c>false</c> =
|
||||
/// normalized.</returns>
|
||||
public static bool NormalizeCheckSmall(ref Vector3 v)
|
||||
{
|
||||
float length = v.Length();
|
||||
if (length < 1e-8f)
|
||||
return true;
|
||||
v /= length;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail <c>Position::globaltolocalvec</c> — rotate a WORLD-space vector
|
||||
/// into a frame's LOCAL coordinates by the inverse of the frame's
|
||||
/// orientation. Consumed by <c>StickyManager::adjust_offset</c>
|
||||
/// (0x00555430) to express the self→target offset in the mover's own frame
|
||||
/// before flattening Z and steering. Pure rotation (no translation) —
|
||||
/// <paramref name="worldVec"/> is a direction/offset, not a point.
|
||||
/// </summary>
|
||||
public static Vector3 GlobalToLocalVec(Quaternion frameOrientation, Vector3 worldVec)
|
||||
=> Vector3.Transform(worldVec, Quaternion.Conjugate(frameOrientation));
|
||||
|
||||
/// <summary>
|
||||
/// Landblock-local wire origin → world space (verbatim relocation from
|
||||
/// the deleted <c>RemoteMoveToDriver.OriginToWorld</c>, R4-V4): MoveTo /
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue