103 lines
3.8 KiB
TypeScript
103 lines
3.8 KiB
TypeScript
import { expect, test } from "vitest";
|
|
import { type AABB, aabbOverlap, vec } from "./given.ts";
|
|
import { penetrationVector, safeMoveAndSlide } from "./overlap.ts";
|
|
|
|
// --- penetrationVector -------------------------------------------------------
|
|
|
|
test("separated boxes -> null", () => {
|
|
const a: AABB = { x: 0, y: 0, w: 2, h: 2 };
|
|
const b: AABB = { x: 5, y: 5, w: 2, h: 2 };
|
|
expect(penetrationVector(a, b)).toBeNull();
|
|
});
|
|
|
|
test("touching edges (no strict overlap) -> null", () => {
|
|
const a: AABB = { x: 0, y: 0, w: 2, h: 2 };
|
|
const b: AABB = { x: 2, y: 0, w: 2, h: 2 };
|
|
expect(penetrationVector(a, b)).toBeNull();
|
|
});
|
|
|
|
test("shallow on x, deep on y -> pushes left (the short way out)", () => {
|
|
// a pokes 1 unit into b's left side; getting out via y would cost 6
|
|
const a: AABB = { x: 0, y: 0, w: 4, h: 4 };
|
|
const b: AABB = { x: 3, y: -2, w: 4, h: 8 };
|
|
expect(penetrationVector(a, b)).toEqual(vec(-1, 0));
|
|
});
|
|
|
|
test("mirrored -> pushes right", () => {
|
|
const a: AABB = { x: 4, y: 0, w: 4, h: 4 };
|
|
const b: AABB = { x: 1, y: -2, w: 4, h: 8 };
|
|
expect(penetrationVector(a, b)).toEqual(vec(1, 0));
|
|
});
|
|
|
|
test("shallow on y -> pushes up", () => {
|
|
const a: AABB = { x: 0, y: 0, w: 4, h: 4 };
|
|
const b: AABB = { x: -2, y: 3, w: 8, h: 4 };
|
|
expect(penetrationVector(a, b)).toEqual(vec(0, -1));
|
|
});
|
|
|
|
test("fully contained -> still gets out (all the way past the nearest edge)", () => {
|
|
// a is swallowed by b: the push must clear the whole distance to an edge,
|
|
// not just the 2-unit "overlap" of the intervals
|
|
const a: AABB = { x: 10, y: 10, w: 2, h: 2 };
|
|
const b: AABB = { x: 0, y: 0, w: 32, h: 32 };
|
|
const p = penetrationVector(a, b);
|
|
expect(p).not.toBeNull();
|
|
if (p !== null) {
|
|
const moved: AABB = { ...a, x: a.x + p.x, y: a.y + p.y };
|
|
expect(aabbOverlap(moved, b)).toBe(false);
|
|
}
|
|
});
|
|
|
|
// --- safeMoveAndSlide --------------------------------------------------------
|
|
|
|
const player: AABB = { x: 0, y: 0, w: 2, h: 2 };
|
|
|
|
test("no overlap at start -> behaves exactly like moveAndSlide", () => {
|
|
// step 10's diagonal test: x blocked at 3, y keeps going
|
|
const walls: AABB[] = [{ x: 5, y: -10, w: 2, h: 40 }];
|
|
const p = safeMoveAndSlide(player, vec(10, 10), walls);
|
|
expect(p.x).toBeCloseTo(3);
|
|
expect(p.y).toBeCloseTo(10);
|
|
});
|
|
|
|
test("starting inside a wall -> position stays a number", () => {
|
|
const inside: AABB = { x: 10, y: 10, w: 2, h: 2 };
|
|
const wall: AABB = { x: 0, y: 0, w: 32, h: 32 };
|
|
const p = safeMoveAndSlide(inside, vec(5, 0), [wall]);
|
|
expect(Number.isFinite(p.x)).toBe(true);
|
|
expect(Number.isFinite(p.y)).toBe(true);
|
|
});
|
|
|
|
test("starting inside a wall -> ends free of it", () => {
|
|
const inside: AABB = { x: 10, y: 10, w: 2, h: 2 };
|
|
const wall: AABB = { x: 0, y: 0, w: 32, h: 32 };
|
|
const p = safeMoveAndSlide(inside, vec(5, 0), [wall]);
|
|
expect(aabbOverlap({ ...inside, x: p.x, y: p.y }, wall)).toBe(false);
|
|
});
|
|
|
|
test("overlapping and walking AWAY -> actually gets away", () => {
|
|
// pokes 1 into the wall's left side; pushed out to x≈8, then walks -5
|
|
const stuck: AABB = { x: 9, y: 0, w: 2, h: 2 };
|
|
const wall: AABB = { x: 10, y: -5, w: 4, h: 10 };
|
|
const p = safeMoveAndSlide(stuck, vec(-5, 0), [wall]);
|
|
expect(p.x).toBeCloseTo(3);
|
|
expect(p.y).toBeCloseTo(0);
|
|
});
|
|
|
|
test("overlapping and pushing IN -> freed, then held at the wall", () => {
|
|
// pushed out to x≈8 first; the sweep then stops the rightward move there
|
|
const stuck: AABB = { x: 9, y: 0, w: 2, h: 2 };
|
|
const wall: AABB = { x: 10, y: -5, w: 4, h: 10 };
|
|
const p = safeMoveAndSlide(stuck, vec(5, 0), [wall]);
|
|
expect(p.x).toBeCloseTo(8);
|
|
expect(p.y).toBeCloseTo(0);
|
|
});
|
|
|
|
test("overlapping and pushing IN (rtl) -> freed, then held at the wall", () => {
|
|
const stuck: AABB = { x: 9, y: 0, w: 2, h: 2 };
|
|
const wall: AABB = { x: 10, y: -5, w: 4, h: 10 };
|
|
const p = safeMoveAndSlide(stuck, vec(-5, 0), [wall]);
|
|
expect(p.x).toBeCloseTo(3);
|
|
expect(p.y).toBeCloseTo(0);
|
|
});
|