import { type AABB, EPSILON, moveAndSlide, penetrationVector, type Vec, } from "./given.ts"; export type Resolution = { x: number; y: number; settled: boolean }; /** * Push `box` out of EVERY wall — even when one push shoves it into another. * * One pass over the walls is not enough: a push is a teleport, and a teleport * can land you inside a wall the loop already cleared. So: run whole passes * over the wall list, applying pushes (with the EPSILON slack — on the pushed * axis only!), until a full pass finds nothing to fix. That pass proves you're * settled. * * But some arrangements have NO free spot (a gap narrower than the box), and * the passes would ping-pong forever. Cap them — 8 is plenty — and if the cap * fires, report `settled: false`. You already believe in caps: your step-10 * loop has one for exactly the same reason. * * Returns where the box ended up and whether it truly got free. */ export function resolveOverlaps(box: AABB, walls: AABB[]): Resolution { const res: Resolution = { x: box.x, y: box.y, settled: true, }; const count = walls.length; let index = 0; let tries = 0; while (index < count) { const pv = penetrationVector(box, walls[index++]); if (pv) { if (pv.x !== 0) { box.x += pv.x > 0 ? pv.x + EPSILON : pv.x - EPSILON; } if (pv.y !== 0) { box.y += pv.y > 0 ? pv.y + EPSILON : pv.y - EPSILON; } res.x = box.x; res.y = box.y; res.settled = false; index = 0; tries += 1; } else { res.settled = true; } if (tries > 7) { break; } } return res; } /** * Step 12's safeMoveAndSlide, rebuilt on resolveOverlaps. * * Settled -> sweep from the safe spot, business as usual. * Crushed (not settled) -> the sweep would be handed an overlapping box and * you know exactly what it does with one of those. Don't feed it. Our crush * policy: the walls win — the box stays where the resolver left it, finite, * and waits for the gap to open. */ export function safeMoveAndSlide(box: AABB, v: Vec, walls: AABB[]): Vec { const res = resolveOverlaps(box, walls); if (res.settled) { return moveAndSlide(box, v, walls); } return { x: box.x, y: box.y, }; }