diff --git a/original/MECHANICS_PROGRESS.md b/original/MECHANICS_PROGRESS.md index c794701..141c707 100644 --- a/original/MECHANICS_PROGRESS.md +++ b/original/MECHANICS_PROGRESS.md @@ -105,7 +105,8 @@ The executable image remains unchanged; only the current disposable Wine process is modified. Run the injector while the ball is waiting in the launcher so it can identify the live game object, or supply the previously reported `--object-base` to both the injector and tracer during a sequence of active -probes. +probes. `--substeps 1` isolates one collision integration step; omitting it +retains the original configured detail level. A probe at `(289,94)` captured the original terminal-18 path. The collision suspended the ball while frames advanced from 10 through 18 at the configured @@ -113,3 +114,11 @@ suspended the ball while frames advanced from 10 through 18 at the configured then cleared suspension during the same timer callback. The three default physics substeps each added 15 millipixels of vertical velocity, confirming the observed total gravity increment of 45 per timer tick. + +A one-substep probe against the vertical shooter walls recovered the type-2 +response arithmetic. For unit tangent `t` from the registered start to end and +left normal `n`, an incoming contact with `vn = dot(v,n)` and `vt = dot(v,t)` +uses `vn' = -0.6 * vn` and `vt' = vt + 0.1 * vn` for the ordinary rail +coefficients stored in its two Real48 fields. The new position is the previous +position plus this response velocity; the original does not separate a circle +from the line with a modern capsule solver. diff --git a/original/tools/inject_original_state.py b/original/tools/inject_original_state.py index 8b0d816..412e0cb 100755 --- a/original/tools/inject_original_state.py +++ b/original/tools/inject_original_state.py @@ -18,6 +18,7 @@ from trace_original_state import ( NEXT_BALL_Y, OBJECT_BALL_X, OBJECT_BALL_Y, + PHYSICS_SUBSTEPS, PREVIOUS_BALL_X, PREVIOUS_BALL_Y, VELOCITY_X, @@ -49,6 +50,12 @@ def parse_args() -> argparse.Namespace: parser.add_argument("--y", type=float, required=True, help="ball center y in logical pixels") parser.add_argument("--vx", type=float, default=0.0, help="x velocity in pixels per timer tick") parser.add_argument("--vy", type=float, default=0.0, help="y velocity in pixels per timer tick") + parser.add_argument( + "--substeps", + type=int, + choices=range(1, 6), + help="temporarily force the original physics substep count", + ) return parser.parse_args() @@ -106,6 +113,8 @@ def main() -> None: (NEXT_BALL_Y, next_y), ): memory.write(data_base + offset, value) + if args.substeps is not None: + memory.write(data_base + PHYSICS_SUBSTEPS, struct.pack(" tuple[int, ...]: return ( data[PLAYER_COUNT], data[CURRENT_PLAYER], + unpack_u16(data, PHYSICS_SUBSTEPS), + obj[ACTIVE_BALL_COUNT], + unpack_i32(obj, MAXIMUM_SPEED), + unpack_i32(obj, GRAVITY_PER_SUBSTEP), unpack_i32(data, BALL_X), unpack_i32(data, BALL_Y), unpack_i32(data, NEXT_BALL_X), @@ -249,6 +265,8 @@ def sample_state(data: bytes, obj: bytes) -> tuple[int, ...]: unpack_u16(obj, RIGHT_FLIPPER_POSITION), obj[LEFT_FLIPPER_DOWN], obj[RIGHT_FLIPPER_DOWN], + obj[BALL_ACTIVE], + obj[COLLISION_DISABLED], obj[BALL_SUSPENDED], data[CLAW_ACTIVE], unpack_u16(data, CLAW_FRAME),