pike/val Public Public: anyone can read this; nobody can change it.

forked from benjivers/val

workbenchthis machine
Files 42b76dcb841680461f654961fcecac06b27bd342
1
/**
2
 * Where you are, drawn at the frame rate.
3
 *
4
 * Everybody else in a snapshot is smoothed toward the last position the
5
 * server sent, which is right for a body you only hear about fifteen times a
6
 * second. Doing it to your own body is what made the view skip: the camera
7
 * was always being dragged toward a position taken sixty-six milliseconds
8
 * ago, and against a wall the server kept correcting it besides.
9
 *
10
 * So your own body is run forward here instead, from your own keys, through
11
 * the same step-and-slide the server uses (`src/glide.ts` — one copy, or the
12
 * guess fights the answer at every wall). The server is still the only thing
13
 * that decides where you are: every snapshot leans the drawn body back
14
 * toward its answer, and a real disagreement is taken outright. This is a
15
 * drawing optimisation and nothing here is ever sent anywhere.
16
 */
17
18
import { circleBlocked, slideCircle } from "../src/glide.ts";
19
20
/** Past this much disagreement the server simply wins: a spawn, a teleport. */
21
export const SNAP = 40;
22
/** How fast the rest of a disagreement is leaned off, a second. */
23
export const PULL = 12;
24
/** How far ahead of a snapshot your own input is ever carried, in seconds. */
25
export const LEAD_MAX = 0.25;
26
27
export const makeLocal = () => ({ x: 0, y: 0, ready: false, gap: 0 });
28
29
/** Take the server's word for it outright. */
30
export function anchor(local, at) {
31
  local.x = at.x;
32
  local.y = at.y;
33
  local.ready = true;
34
  local.gap = 0;
35
  return local;
36
}
37
38
/**
39
 * One frame of your own movement: a direction, the speed the server will
40
 * move you at, and the world to slide against — `{ solid, tile, r }`.
41
 */
42
export function advance(local, mx, my, speed, dt, world) {
43
  if (!local.ready) return local;
44
  const len = Math.hypot(mx, my);
45
  if (len > 1) {
46
    mx /= len;
47
    my /= len;
48
  }
49
  if (speed <= 0 || (mx === 0 && my === 0) || dt <= 0) return local;
50
  const to = slideCircle(local.x, local.y, mx * speed * dt, my * speed * dt, world.r, world.solid, world.tile);
51
  local.x = to.x;
52
  local.y = to.y;
53
  return local;
54
}
55
56
/**
57
 * Where the server's last answer has got to by now.
58
 *
59
 * A snapshot is a position from a moment ago, and the input you have held
60
 * since it was sent has not reached it yet. Rather than a sequence number
61
 * the server echoes back, the unacknowledged part of your own movement is
62
 * measured in time: the age of the snapshot, capped, walked through the
63
 * same slide. Hold nothing and this is the server's answer exactly, which
64
 * is the point — it is what the drawn body settles onto.
65
 */
66
export function project(at, mx, my, speed, age, world) {
67
  const len = Math.hypot(mx, my);
68
  if (len > 1) {
69
    mx /= len;
70
    my /= len;
71
  }
72
  const ahead = Math.min(LEAD_MAX, Math.max(0, age));
73
  if (speed <= 0 || ahead === 0 || (mx === 0 && my === 0)) return { x: at.x, y: at.y };
74
  return slideCircle(at.x, at.y, mx * speed * ahead, my * speed * ahead, world.r, world.solid, world.tile);
75
}
76
77
/**
78
 * Lean the drawn body toward the server's answer. A small disagreement —
79
 * the width of the guess, a shot of lag — is walked off over a moment; a
80
 * large one is not a disagreement but a different place, and is taken.
81
 */
82
export function reconcile(local, server, dt) {
83
  if (!local.ready) return anchor(local, server);
84
  const dx = server.x - local.x;
85
  const dy = server.y - local.y;
86
  local.gap = Math.hypot(dx, dy);
87
  if (local.gap > SNAP) return anchor(local, server);
88
  const k = 1 - Math.exp(-PULL * Math.max(0, dt));
89
  local.x += dx * k;
90
  local.y += dy * k;
91
  return local;
92
}
93
94
/**
95
 * Never past the answer. The guess may fill the gap to where the server's
96
 * word and your own held keys put you and no further: left to run, a guess
97
 * that moves faster than its target is corrected finds its own equilibrium
98
 * somewhere beyond it, which is a claim about a position rather than a
99
 * drawing of one.
100
 */
101
export function holdBack(local, target, mx, my) {
102
  const len = Math.hypot(mx, my);
103
  if (len === 0) return local;
104
  const dx = mx / len;
105
  const dy = my / len;
106
  const over = (local.x - target.x) * dx + (local.y - target.y) * dy;
107
  if (over > 0) {
108
    local.x -= dx * over;
109
    local.y -= dy * over;
110
  }
111
  return local;
112
}
113
114
/** A body drawn inside a wall is worse than a body drawn late. */
115
export const insideWall = (local, world) => circleBlocked(local.x, local.y, world.r, world.solid, world.tile);
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts