benjivers/val Public Public: anyone can read this; the crew can change it.

Files b5f6bddcbc04948cd09fc3f6b819512f8c7d129c
110 lines · 4.4 KB · ~1.3k tok Raw History
1
/**
2
 * Step and slide, on a grid written out here rather than the real map, so
3
 * the cases are the ones being argued about: a wall, an inner corner, and a
4
 * body already pressed against both.
5
 */
6
7
import { expect, test } from "bun:test";
8
import { BODY_R, circleBlocked, moveSpeed, RUN, slideCircle } from "./glide.ts";
9
import { moveCircle } from "./map.ts";
10
11
const TILE = 80;
12
// A room with solid walls, open from (1,1) to (3,3).
13
const room = ["#####", "#...#", "#...#", "#...#", "#####"];
14
const solid = (col: number, row: number): boolean =>
15
  col < 0 || row < 0 || row >= room.length || col >= room[0]!.length || room[row]![col] === "#";
16
17
const slide = (x: number, y: number, dx: number, dy: number) =>
18
  slideCircle(x, y, dx, dy, BODY_R, solid, TILE);
19
20
test("an unobstructed step is the step it was asked for", () => {
21
  const to = slide(200, 200, 12, -7);
22
  expect(to.x).toBeCloseTo(212, 9);
23
  expect(to.y).toBeCloseTo(193, 9);
24
  expect(slide(200, 200, 0, 0)).toEqual({ x: 200, y: 200 });
25
});
26
27
test("a wall keeps the motion along it: running into it still slides", () => {
28
  // Hard against the right-hand wall (x = 320 - 16), running down and right.
29
  const to = slide(300, 160, 10, 10);
30
  expect(to.y).toBeCloseTo(170, 6); // the free axis goes through whole
31
  expect(to.x).toBeLessThanOrEqual(304);
32
  expect(to.x).toBeGreaterThan(300); // and the blocked one creeps up to the wall
33
  expect(circleBlocked(to.x, to.y, BODY_R, solid, TILE)).toBe(false);
34
});
35
36
test("a corner is one place, whichever axis is resolved first", () => {
37
  // The bottom-right inner corner of the room: both walls at once.
38
  const diagonal = slide(280, 280, 40, 40);
39
  // Getting there one axis at a time, either order, lands in the same place.
40
  const alongX = slide(280, 280, 40, 0);
41
  const xThenY = slide(alongX.x, alongX.y, 0, 40);
42
  const alongY = slide(280, 280, 0, 40);
43
  const yThenX = slide(alongY.x, alongY.y, 40, 0);
44
  expect(xThenY.x).toBeCloseTo(diagonal.x, 3);
45
  expect(xThenY.y).toBeCloseTo(diagonal.y, 3);
46
  expect(yThenX.x).toBeCloseTo(diagonal.x, 3);
47
  expect(yThenX.y).toBeCloseTo(diagonal.y, 3);
48
  // And it is the corner: a body's radius from both walls, inside them.
49
  expect(diagonal.x).toBeGreaterThan(320 - BODY_R - 1.5);
50
  expect(diagonal.x).toBeLessThanOrEqual(320 - BODY_R);
51
  expect(diagonal.y).toBeGreaterThan(320 - BODY_R - 1.5);
52
  expect(diagonal.y).toBeLessThanOrEqual(320 - BODY_R);
53
});
54
55
test("a body held against a wall stops moving instead of alternating", () => {
56
  // This is the skip: the old resolution snapped to the tile edge on the
57
  // blocked tick and moved freely on the next, so a player leaning on a
58
  // wall produced a position that changed every tick with no input change.
59
  let at = { x: 300, y: 200 };
60
  const seen: number[] = [];
61
  for (let i = 0; i < 12; i++) {
62
    at = slide(at.x, at.y, 8.3, 0);
63
    seen.push(at.x);
64
  }
65
  const settled = seen.slice(4);
66
  expect(Math.max(...settled) - Math.min(...settled)).toBeLessThan(0.05);
67
  expect(Math.max(...settled)).toBeLessThanOrEqual(320 - BODY_R);
68
});
69
70
test("a corner is a fixed point: pressed into it, nothing moves at all", () => {
71
  let at = { x: 300, y: 300 };
72
  for (let i = 0; i < 6; i++) at = slide(at.x, at.y, 8.3, 8.3);
73
  const settled = slide(at.x, at.y, 8.3, 8.3);
74
  expect(settled.x).toBeCloseTo(at.x, 6);
75
  expect(settled.y).toBeCloseTo(at.y, 6);
76
});
77
78
test("a step never ends inside a wall, from anywhere in the room", () => {
79
  for (let x = 96; x < 320 - 16; x += 7)
80
    for (let y = 96; y < 320 - 16; y += 7)
81
      for (const [dx, dy] of [
82
        [30, 0],
83
        [-30, 0],
84
        [0, 30],
85
        [0, -30],
86
        [24, 24],
87
        [-24, 24],
88
        [24, -24],
89
        [-24, -24],
90
      ] as const) {
91
        const to = slide(x, y, dx, dy);
92
        expect(circleBlocked(to.x, to.y, BODY_R, solid, TILE)).toBe(false);
93
      }
94
});
95
96
test("the real map moves by the same maths", () => {
97
  // Mid, walking east into whatever is there: the map's own binding.
98
  const to = moveCircle(1200, 640, 9, 0, BODY_R);
99
  expect(to.x).toBeGreaterThanOrEqual(1200);
100
  expect(to.x).toBeLessThanOrEqual(1209);
101
  expect(to.y).toBe(640);
102
});
103
104
test("speed is the one table both sides read", () => {
105
  expect(moveSpeed({})).toBe(RUN);
106
  expect(moveSpeed({ haste: true })).toBeCloseTo(RUN * 1.35, 9);
107
  expect(moveSpeed({ slow: true })).toBeCloseTo(RUN * 0.55, 9);
108
  expect(moveSpeed({ heavy: true })).toBeCloseTo(RUN * 0.86, 9);
109
  expect(moveSpeed({ stopped: true, haste: true })).toBe(0);
110
});
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts