feat(#182): frames_stationary_fall round-trip in the kept transition internals (Slice 1b/1c)
Completes the TS-3 stub (the deferred 'full physics port'): - ValidateTransition: the fsf increment/reset ladder + the fsf>=3 upward-contact-plane manufacture (retail validate_transition 0x0050aa70 pc:272625-656; ACE Transition.cs: 1029-1061). Runs after acdream's fused contact block (structural adaptation preserving the L.2.3c/L.2.4/A6.P3 contact-retention divergences), deriving retail's _redo as cleanAdvance || OnWalkable — a grounded wall-slide is not a stuck-fall. - The sweep-loop fsf early-out (retail find_valid_position pc:273745 / ACE :587): stop as soon as fsf != 0, so a later advancing sub-step can't reset it in the same frame (load-bearing — without it the counter never escalates across frames). - ObjectInfo.MoverHasGravity gate (pc:272625). - PhysicsEngine: seed ci.fsf from the body's Stationary* bits AFTER InitPath (retail transition() pc:280940-947); writeback publishes body.FramesStationaryFall + encodes the Stationary* bits (co-located with the fsf compute so the round-trip is self-contained in Core; retail encodes in handle_all_collisions — register note). Tests: FramesStationaryFallTests — an airborne jump wedged under an overhead creature escalates fsf 0->1->2->3 and at fsf 3 manufactures the UP plane (grounded 'glide onto the crowd top'); a grounded wall-slide never accumulates fsf. Core 2609/0. Behaviour dormant in ordinary locomotion (fsf stays 0 unless a gravity mover is blocked). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6e8117741b
commit
6c3cd96b7a
3 changed files with 264 additions and 2 deletions
|
|
@ -971,6 +971,11 @@ public sealed class PhysicsEngine
|
|||
// (matches non-player movement, all targets collide).
|
||||
transition.ObjectInfo.State |= moverFlags;
|
||||
|
||||
// frames_stationary_fall gate input: retail reads the mover's GRAVITY state bit
|
||||
// (object_info.object->state & 0x400, pc:272625). Seed it from the body so the ladder
|
||||
// in ValidateTransition runs for gravity movers (the player) and not floating props.
|
||||
transition.ObjectInfo.MoverHasGravity = body?.HasGravity ?? false;
|
||||
|
||||
if (isOnGround)
|
||||
transition.ObjectInfo.State |= ObjectInfoState.Contact | ObjectInfoState.OnWalkable;
|
||||
|
||||
|
|
@ -1043,6 +1048,19 @@ public sealed class PhysicsEngine
|
|||
body.WalkableUp);
|
||||
}
|
||||
|
||||
// Seed collision_info.frames_stationary_fall from the body's carried Stationary*
|
||||
// transient bits — retail transition() 0x00512dc0 seeds fsf from transient_state
|
||||
// 0x40/0x20/0x10 AFTER init_path and immediately BEFORE find_valid_position
|
||||
// (pc:280939-949). Placed here (post-InitPath) so InitPath's CollisionInfo reset
|
||||
// doesn't wipe the seed.
|
||||
if (body is not null)
|
||||
{
|
||||
transition.CollisionInfo.FramesStationaryFall =
|
||||
body.TransientState.HasFlag(TransientStateFlags.StationaryStuck) ? 3 :
|
||||
body.TransientState.HasFlag(TransientStateFlags.StationaryStop) ? 2 :
|
||||
body.TransientState.HasFlag(TransientStateFlags.StationaryFall) ? 1 : 0;
|
||||
}
|
||||
|
||||
bool ok = transition.FindTransitionalPosition(this);
|
||||
|
||||
var sp = transition.SpherePath;
|
||||
|
|
@ -1072,6 +1090,23 @@ public sealed class PhysicsEngine
|
|||
body.ContactPlaneValid = false;
|
||||
}
|
||||
|
||||
// Publish frames_stationary_fall + carry it to the next frame via the Stationary*
|
||||
// transient bits. Retail encodes these bits in handle_all_collisions (pc:282737-758);
|
||||
// acdream co-locates the encode with the fsf writeback here (STRUCTURAL ADAPTATION,
|
||||
// register) so the round-trip (seed→ladder→writeback→seed) is self-contained in Core.
|
||||
// handle_all_collisions (PhysicsObjUpdate) then only READS body.FramesStationaryFall.
|
||||
body.FramesStationaryFall = ci.FramesStationaryFall;
|
||||
body.TransientState &= ~(TransientStateFlags.StationaryFall
|
||||
| TransientStateFlags.StationaryStop
|
||||
| TransientStateFlags.StationaryStuck);
|
||||
body.TransientState |= ci.FramesStationaryFall switch
|
||||
{
|
||||
1 => TransientStateFlags.StationaryFall,
|
||||
2 => TransientStateFlags.StationaryStop,
|
||||
3 => TransientStateFlags.StationaryStuck,
|
||||
_ => TransientStateFlags.None,
|
||||
};
|
||||
|
||||
if (sp.HasLastWalkablePolygon && sp.LastWalkableVertices is not null)
|
||||
{
|
||||
body.WalkablePolygonValid = true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue