1681-engine 0.5.10 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -1
- package/README.md +12 -6
- package/dist/1681-engine.js +582 -522
- package/dist/ai/behaviorTree.js +198 -0
- package/dist/ai/buildTree.js +54 -0
- package/dist/audio/spatial.js +118 -0
- package/dist/cjs/electron/webPatcher.d.ts +4 -1
- package/dist/cjs/electron/webPatcher.js +9 -2
- package/dist/dialogue/DialogueRunner.js +173 -0
- package/dist/dialogue/expr.js +137 -0
- package/dist/dialogue/validateDialogue.js +52 -0
- package/dist/ecs/prefab.js +94 -0
- package/dist/electron/webPatcher.js +16 -10
- package/dist/input/Actions.js +58 -27
- package/dist/input/InputCapture.js +98 -0
- package/dist/input/Keymap.js +32 -26
- package/dist/input/inputTape.js +68 -0
- package/dist/mobile/backButton.js +40 -0
- package/dist/mobile/capacitor.js +48 -0
- package/dist/mobile/screen.js +31 -0
- package/dist/mobile.js +12 -0
- package/dist/nav/flowField.js +58 -0
- package/dist/nav/steering.js +77 -0
- package/dist/net/InterpolationBuffer.js +30 -23
- package/dist/net/LagCompensator.js +100 -0
- package/dist/net/Room.js +122 -43
- package/dist/net/RoomHost.js +13 -4
- package/dist/net/SpatialHash.js +66 -0
- package/dist/types/ai/behaviorTree.d.ts +133 -0
- package/dist/types/ai/behaviorTree.d.ts.map +1 -0
- package/dist/types/ai/buildTree.d.ts +55 -0
- package/dist/types/ai/buildTree.d.ts.map +1 -0
- package/dist/types/audio/spatial.d.ts +107 -0
- package/dist/types/audio/spatial.d.ts.map +1 -0
- package/dist/types/dialogue/DialogueRunner.d.ts +78 -0
- package/dist/types/dialogue/DialogueRunner.d.ts.map +1 -0
- package/dist/types/dialogue/expr.d.ts +12 -0
- package/dist/types/dialogue/expr.d.ts.map +1 -0
- package/dist/types/dialogue/script.d.ts +78 -0
- package/dist/types/dialogue/script.d.ts.map +1 -0
- package/dist/types/dialogue/validateDialogue.d.ts +21 -0
- package/dist/types/dialogue/validateDialogue.d.ts.map +1 -0
- package/dist/types/ecs/prefab.d.ts +84 -0
- package/dist/types/ecs/prefab.d.ts.map +1 -0
- package/dist/types/electron/webPatcher.d.ts +4 -1
- package/dist/types/electron/webPatcher.d.ts.map +1 -1
- package/dist/types/index.d.ts +15 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/input/Actions.d.ts +14 -0
- package/dist/types/input/Actions.d.ts.map +1 -1
- package/dist/types/input/InputCapture.d.ts +53 -0
- package/dist/types/input/InputCapture.d.ts.map +1 -0
- package/dist/types/input/Keymap.d.ts +12 -6
- package/dist/types/input/Keymap.d.ts.map +1 -1
- package/dist/types/input/inputTape.d.ts +66 -0
- package/dist/types/input/inputTape.d.ts.map +1 -0
- package/dist/types/mobile/backButton.d.ts +30 -0
- package/dist/types/mobile/backButton.d.ts.map +1 -0
- package/dist/types/mobile/capacitor.d.ts +62 -0
- package/dist/types/mobile/capacitor.d.ts.map +1 -0
- package/dist/types/mobile/index.d.ts +4 -0
- package/dist/types/mobile/index.d.ts.map +1 -0
- package/dist/types/mobile/screen.d.ts +27 -0
- package/dist/types/mobile/screen.d.ts.map +1 -0
- package/dist/types/nav/flowField.d.ts +31 -0
- package/dist/types/nav/flowField.d.ts.map +1 -0
- package/dist/types/nav/steering.d.ts +47 -0
- package/dist/types/nav/steering.d.ts.map +1 -0
- package/dist/types/net/InterpolationBuffer.d.ts +5 -0
- package/dist/types/net/InterpolationBuffer.d.ts.map +1 -1
- package/dist/types/net/LagCompensator.d.ts +70 -0
- package/dist/types/net/LagCompensator.d.ts.map +1 -0
- package/dist/types/net/Room.d.ts +72 -3
- package/dist/types/net/Room.d.ts.map +1 -1
- package/dist/types/net/RoomHost.d.ts +8 -3
- package/dist/types/net/RoomHost.d.ts.map +1 -1
- package/dist/types/net/SpatialHash.d.ts +25 -0
- package/dist/types/net/SpatialHash.d.ts.map +1 -0
- package/dist/types/ui/ScrollView.d.ts.map +1 -1
- package/dist/ui/ScrollView.js +10 -8
- package/package.json +5 -1
package/dist/input/Keymap.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { deviceOf as u } from "./InputCapture.js";
|
|
2
|
+
class p {
|
|
2
3
|
binds;
|
|
3
4
|
defaults;
|
|
4
5
|
reserved;
|
|
@@ -23,9 +24,13 @@ class g {
|
|
|
23
24
|
isReserved(t) {
|
|
24
25
|
return this.reserved.has(t);
|
|
25
26
|
}
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The bindings of one action, or only those of one device; the first of a device is the one a settings screen
|
|
29
|
+
* shows and edits.
|
|
30
|
+
*/
|
|
31
|
+
keysFor(t, s) {
|
|
32
|
+
const e = this.binds[t] ?? [];
|
|
33
|
+
return s ? e.filter((h) => u(h) === s) : e;
|
|
29
34
|
}
|
|
30
35
|
/** The inputs an action always has besides the player's keys (the constructor's `fixed`). */
|
|
31
36
|
fixedFor(t) {
|
|
@@ -36,17 +41,18 @@ class g {
|
|
|
36
41
|
for (const s of this.actions()) if (this.binds[s].includes(t)) return s;
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
39
|
-
* Puts `code` first on `action
|
|
40
|
-
*
|
|
41
|
-
*
|
|
44
|
+
* Puts `code` first on `action` in place of the action's first binding of the same device (a key replaces its first
|
|
45
|
+
* key, a pad input its first pad input), taking it off whatever else held it; returns the actions that lost it (to
|
|
46
|
+
* tell the player). A reserved key is refused (returns null). An action stripped of its last key keeps none: it is
|
|
47
|
+
* unbound until something is put back on it.
|
|
42
48
|
*/
|
|
43
49
|
bind(t, s) {
|
|
44
50
|
if (this.reserved.has(s) || !(t in this.binds)) return null;
|
|
45
51
|
const e = [];
|
|
46
52
|
for (const n of this.actions())
|
|
47
|
-
n === t || !this.binds[n].includes(s) || (this.binds[n] = this.binds[n].filter((
|
|
48
|
-
const h = this.binds[t].
|
|
49
|
-
return this.binds[t] = [s, ...
|
|
53
|
+
n === t || !this.binds[n].includes(s) || (this.binds[n] = this.binds[n].filter((r) => r !== s), e.push(n));
|
|
54
|
+
const h = u(s), f = this.binds[t].find((n) => u(n) === h), a = this.binds[t].filter((n) => n !== f && n !== s);
|
|
55
|
+
return this.binds[t] = [s, ...a], this.changed(), e;
|
|
50
56
|
}
|
|
51
57
|
/** Takes every key off `action`. */
|
|
52
58
|
unbind(t) {
|
|
@@ -68,7 +74,7 @@ class g {
|
|
|
68
74
|
* (the saved one is what the player chose). Anything malformed is ignored.
|
|
69
75
|
*/
|
|
70
76
|
load(t) {
|
|
71
|
-
this.binds =
|
|
77
|
+
this.binds = c(this.defaults, t, this.reserved), this.changed();
|
|
72
78
|
}
|
|
73
79
|
/**
|
|
74
80
|
* Hands the bindings to an `Actions`: each action's keys, then its fixed inputs, then its `extra` ones. Only the
|
|
@@ -90,19 +96,19 @@ class g {
|
|
|
90
96
|
for (const t of [...this.listeners]) t();
|
|
91
97
|
}
|
|
92
98
|
}
|
|
93
|
-
function
|
|
94
|
-
const e = Object.keys(i), h = {},
|
|
99
|
+
function c(i, t, s = /* @__PURE__ */ new Set()) {
|
|
100
|
+
const e = Object.keys(i), h = {}, f = /* @__PURE__ */ new Set(), a = t && typeof t == "object" ? t : {};
|
|
95
101
|
for (const r of e) {
|
|
96
102
|
const o = a[r];
|
|
97
|
-
Array.isArray(o) && o.every((
|
|
103
|
+
Array.isArray(o) && o.every((l) => typeof l == "string") ? (h[r] = [...new Set(o)].filter((l) => !s.has(l)), f.add(r)) : h[r] = i[r].filter((l) => !s.has(l));
|
|
98
104
|
}
|
|
99
|
-
const
|
|
105
|
+
const n = /* @__PURE__ */ new Map();
|
|
100
106
|
for (const r of e)
|
|
101
|
-
|
|
102
|
-
for (const r of e)
|
|
107
|
+
f.has(r) && (h[r] = h[r].filter((o) => n.has(o) ? !1 : (n.set(o, r), !0)));
|
|
108
|
+
for (const r of e) f.has(r) || (h[r] = h[r].filter((o) => !n.has(o)));
|
|
103
109
|
return h;
|
|
104
110
|
}
|
|
105
|
-
function
|
|
111
|
+
function S(i) {
|
|
106
112
|
const t = () => {
|
|
107
113
|
try {
|
|
108
114
|
return typeof localStorage > "u" ? null : localStorage;
|
|
@@ -133,7 +139,7 @@ function b(i) {
|
|
|
133
139
|
}
|
|
134
140
|
};
|
|
135
141
|
}
|
|
136
|
-
const
|
|
142
|
+
const d = {
|
|
137
143
|
Space: "Space",
|
|
138
144
|
ShiftLeft: "Shift",
|
|
139
145
|
ShiftRight: "Right Shift",
|
|
@@ -157,14 +163,14 @@ const c = {
|
|
|
157
163
|
BracketLeft: "[",
|
|
158
164
|
BracketRight: "]"
|
|
159
165
|
};
|
|
160
|
-
function
|
|
161
|
-
if (i.length > 1 && i.includes("+")) return i.split("+").map((e) =>
|
|
166
|
+
function b(i, t = {}) {
|
|
167
|
+
if (i.length > 1 && i.includes("+")) return i.split("+").map((e) => b(e, t)).join("+");
|
|
162
168
|
const s = t[i];
|
|
163
|
-
return s !== void 0 ? s : i.startsWith("Key") ? i.slice(3) : i.startsWith("Digit") ? i.slice(5) : i.startsWith("Numpad") ? `Num ${i.slice(6)}` :
|
|
169
|
+
return s !== void 0 ? s : i.startsWith("Key") ? i.slice(3) : i.startsWith("Digit") ? i.slice(5) : i.startsWith("Numpad") ? `Num ${i.slice(6)}` : d[i] ?? i;
|
|
164
170
|
}
|
|
165
171
|
export {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
172
|
+
p as Keymap,
|
|
173
|
+
b as keyLabel,
|
|
174
|
+
S as keymapStorage,
|
|
175
|
+
c as mergeKeymap
|
|
170
176
|
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
class r {
|
|
2
|
+
constructor(t, e) {
|
|
3
|
+
this.meta = e, this.untap = t.tap((s) => this.capture(s));
|
|
4
|
+
}
|
|
5
|
+
meta;
|
|
6
|
+
order = [];
|
|
7
|
+
index = /* @__PURE__ */ new Map();
|
|
8
|
+
last = /* @__PURE__ */ new Map();
|
|
9
|
+
changes = [];
|
|
10
|
+
steps = 0;
|
|
11
|
+
untap;
|
|
12
|
+
stopped = null;
|
|
13
|
+
/** Steps recorded so far. */
|
|
14
|
+
get length() {
|
|
15
|
+
return this.steps;
|
|
16
|
+
}
|
|
17
|
+
/** Stops recording and returns the tape (the same one on every later call). */
|
|
18
|
+
stop() {
|
|
19
|
+
return this.stopped ? this.stopped : (this.untap(), this.stopped = { version: 1, actions: [...this.order], steps: this.steps, changes: this.changes.map((t) => [...t]) }, this.meta && (this.stopped.meta = structuredClone(this.meta)), this.stopped);
|
|
20
|
+
}
|
|
21
|
+
capture(t) {
|
|
22
|
+
const e = this.steps++;
|
|
23
|
+
for (const [s, i] of t) this.note(e, s, i);
|
|
24
|
+
for (const [s, i] of this.last) i !== 0 && !t.has(s) && this.note(e, s, 0);
|
|
25
|
+
}
|
|
26
|
+
note(t, e, s) {
|
|
27
|
+
if ((this.last.get(e) ?? 0) === s) return;
|
|
28
|
+
let i = this.index.get(e);
|
|
29
|
+
i === void 0 && (i = this.order.length, this.order.push(e), this.index.set(e, i)), this.last.set(e, s), this.changes.push([t, i, s]);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
class h {
|
|
33
|
+
constructor(t) {
|
|
34
|
+
if (this.tape = t, !(t?.version === 1 && Array.isArray(t.actions) && Number.isInteger(t.steps) && Array.isArray(t.changes) && t.changes.every((s, i) => Array.isArray(s) && s.length === 3 && s.every(Number.isFinite) && s[1] >= 0 && s[1] < t.actions.length && (i === 0 || s[0] >= t.changes[i - 1][0])))) throw new Error("InputPlayer: not an input tape (version 1)");
|
|
35
|
+
}
|
|
36
|
+
tape;
|
|
37
|
+
step = 0;
|
|
38
|
+
next = 0;
|
|
39
|
+
values = /* @__PURE__ */ new Map();
|
|
40
|
+
/** Steps played so far. */
|
|
41
|
+
get position() {
|
|
42
|
+
return this.step;
|
|
43
|
+
}
|
|
44
|
+
get done() {
|
|
45
|
+
return this.step >= this.tape.steps;
|
|
46
|
+
}
|
|
47
|
+
/** The next step's values, or null once the tape is over. */
|
|
48
|
+
read() {
|
|
49
|
+
if (this.done) return null;
|
|
50
|
+
const t = this.tape.changes;
|
|
51
|
+
for (; this.next < t.length && t[this.next][0] === this.step; ) {
|
|
52
|
+
const [, e, s] = t[this.next++], i = this.tape.actions[e];
|
|
53
|
+
s === 0 ? this.values.delete(i) : this.values.set(i, s);
|
|
54
|
+
}
|
|
55
|
+
return this.step++, this.values;
|
|
56
|
+
}
|
|
57
|
+
/** Drives `actions` from the tape until it ends; `onEnd` is called on the poll that finds it over. */
|
|
58
|
+
playInto(t, e) {
|
|
59
|
+
t.replay(() => {
|
|
60
|
+
const s = this.read();
|
|
61
|
+
return s === null && e?.(), s;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
h as InputPlayer,
|
|
67
|
+
r as InputRecorder
|
|
68
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
class r {
|
|
2
|
+
constructor(t = () => {
|
|
3
|
+
}) {
|
|
4
|
+
this.onRoot = t;
|
|
5
|
+
}
|
|
6
|
+
onRoot;
|
|
7
|
+
handlers = [];
|
|
8
|
+
/** Puts `handler` on top; the returned function takes it off (from anywhere in the stack). */
|
|
9
|
+
push(t) {
|
|
10
|
+
return this.handlers.push(t), () => {
|
|
11
|
+
const s = this.handlers.lastIndexOf(t);
|
|
12
|
+
s >= 0 && this.handlers.splice(s, 1);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
get size() {
|
|
16
|
+
return this.handlers.length;
|
|
17
|
+
}
|
|
18
|
+
/** A back press: offered from the top down; `onRoot` when none uses it. True when a handler used it. */
|
|
19
|
+
press() {
|
|
20
|
+
for (let t = this.handlers.length - 1; t >= 0; t--)
|
|
21
|
+
if (this.handlers[t]()) return !0;
|
|
22
|
+
return this.onRoot(), !1;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function h(n) {
|
|
26
|
+
const t = n.withinMs ?? 2e3, s = n.now ?? (() => Date.now());
|
|
27
|
+
let e = -1 / 0;
|
|
28
|
+
return () => {
|
|
29
|
+
const i = s();
|
|
30
|
+
if (i - e <= t) {
|
|
31
|
+
e = -1 / 0, n.exit();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
e = i, n.notify();
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
r as BackStack,
|
|
39
|
+
h as doubleBackToExit
|
|
40
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
async function i(e, n) {
|
|
2
|
+
const t = await e.addListener("backButton", () => {
|
|
3
|
+
n.press();
|
|
4
|
+
});
|
|
5
|
+
return () => {
|
|
6
|
+
t.remove();
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
async function s(e) {
|
|
10
|
+
if (!e?.getInfo) return 1 / 0;
|
|
11
|
+
try {
|
|
12
|
+
const n = parseInt((await e.getInfo()).build, 10);
|
|
13
|
+
return Number.isFinite(n) ? n : 0;
|
|
14
|
+
} catch {
|
|
15
|
+
return 1 / 0;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function u(e) {
|
|
19
|
+
if (!/^[a-zA-Z][\w]*(\.[a-zA-Z][\w]*)+$/.test(e.appId))
|
|
20
|
+
throw new Error(`capacitorConfig: appId must be reverse-domain, like com.example.game (got '${e.appId}')`);
|
|
21
|
+
const n = e.backgroundColor ?? "#000000", t = e.debug ?? !1, a = {
|
|
22
|
+
appId: e.appId,
|
|
23
|
+
appName: e.appName,
|
|
24
|
+
webDir: e.webDir ?? "dist",
|
|
25
|
+
backgroundColor: n,
|
|
26
|
+
server: { androidScheme: "https", cleartext: !1 },
|
|
27
|
+
android: { backgroundColor: n, allowMixedContent: !1, captureInput: !0, webContentsDebuggingEnabled: t },
|
|
28
|
+
ios: { backgroundColor: n, contentInset: "never", scrollEnabled: !1, webContentsDebuggingEnabled: t },
|
|
29
|
+
plugins: {
|
|
30
|
+
SplashScreen: { launchAutoHide: !1, backgroundColor: n, showSpinner: !1 },
|
|
31
|
+
CapacitorUpdater: { autoUpdate: !1 }
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
return c(a, e.extra ?? {});
|
|
35
|
+
}
|
|
36
|
+
function c(e, n) {
|
|
37
|
+
const t = { ...e };
|
|
38
|
+
for (const [a, r] of Object.entries(n)) {
|
|
39
|
+
const o = t[a];
|
|
40
|
+
t[a] = r && typeof r == "object" && !Array.isArray(r) && o && typeof o == "object" && !Array.isArray(o) ? c(o, r) : r;
|
|
41
|
+
}
|
|
42
|
+
return t;
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
u as capacitorConfig,
|
|
46
|
+
i as installBackButton,
|
|
47
|
+
s as nativeBuildOf
|
|
48
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
function r(n = globalThis.document) {
|
|
2
|
+
if (!n?.body) return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
3
|
+
const t = n.createElement("div");
|
|
4
|
+
t.style.cssText = "position:fixed;visibility:hidden;pointer-events:none;padding-top:env(safe-area-inset-top,0px);padding-right:env(safe-area-inset-right,0px);padding-bottom:env(safe-area-inset-bottom,0px);padding-left:env(safe-area-inset-left,0px)", n.body.appendChild(t);
|
|
5
|
+
const e = getComputedStyle(t), i = (o) => parseFloat(o) || 0, a = { top: i(e.paddingTop), right: i(e.paddingRight), bottom: i(e.paddingBottom), left: i(e.paddingLeft) };
|
|
6
|
+
return t.remove(), a;
|
|
7
|
+
}
|
|
8
|
+
async function l(n = globalThis.navigator?.wakeLock, t = globalThis.document) {
|
|
9
|
+
if (!n) return () => {
|
|
10
|
+
};
|
|
11
|
+
let e = null, i = !1;
|
|
12
|
+
const a = async () => {
|
|
13
|
+
try {
|
|
14
|
+
const s = await n.request("screen");
|
|
15
|
+
if (i) return void s.release();
|
|
16
|
+
e = s, s.addEventListener?.("release", () => {
|
|
17
|
+
e === s && (e = null);
|
|
18
|
+
});
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
}, o = () => {
|
|
22
|
+
t?.visibilityState === "visible" && !e && a();
|
|
23
|
+
};
|
|
24
|
+
return await a(), t?.addEventListener("visibilitychange", o), () => {
|
|
25
|
+
i = !0, t?.removeEventListener("visibilitychange", o), e?.release(), e = null;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
l as keepScreenOn,
|
|
30
|
+
r as safeAreaInsets
|
|
31
|
+
};
|
package/dist/mobile.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BackStack as t, doubleBackToExit as a } from "./mobile/backButton.js";
|
|
2
|
+
import { capacitorConfig as c, installBackButton as n, nativeBuildOf as f } from "./mobile/capacitor.js";
|
|
3
|
+
import { keepScreenOn as k, safeAreaInsets as p } from "./mobile/screen.js";
|
|
4
|
+
export {
|
|
5
|
+
t as BackStack,
|
|
6
|
+
c as capacitorConfig,
|
|
7
|
+
a as doubleBackToExit,
|
|
8
|
+
n as installBackButton,
|
|
9
|
+
k as keepScreenOn,
|
|
10
|
+
f as nativeBuildOf,
|
|
11
|
+
p as safeAreaInsets
|
|
12
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { MinHeap as C } from "./MinHeap.js";
|
|
2
|
+
import { isOpen as g, NAV_STEPS as S, canStep as E } from "./navGrid.js";
|
|
3
|
+
function M(o, s, w = {}) {
|
|
4
|
+
const { cost: r, endOnly: l, maxCost: h = 1 / 0 } = w, i = o.width, e = i * o.height, u = new Float32Array(e).fill(1 / 0), x = new Int8Array(e).fill(-1), A = new Uint8Array(e), y = new C(), d = new Uint8Array(e);
|
|
5
|
+
for (const n of s) {
|
|
6
|
+
if (!g(o, n.x, n.y)) continue;
|
|
7
|
+
const t = n.y * i + n.x;
|
|
8
|
+
d[t] = 1, u[t] = 0, y.push(t, 0);
|
|
9
|
+
}
|
|
10
|
+
const k = (n, t) => d[t * i + n] === 1;
|
|
11
|
+
for (; y.size > 0; ) {
|
|
12
|
+
const n = y.pop();
|
|
13
|
+
if (A[n]) continue;
|
|
14
|
+
A[n] = 1;
|
|
15
|
+
const t = n % i, f = n / i | 0;
|
|
16
|
+
if (!(!d[n] && l?.(t, f)))
|
|
17
|
+
for (let c = 0; c < S.length; c++) {
|
|
18
|
+
const [T, U, z] = S[c], I = t - T, F = f - U;
|
|
19
|
+
if (!g(o, I, F) || !E(o, I, F, T, U, l, k)) continue;
|
|
20
|
+
const p = F * i + I;
|
|
21
|
+
if (A[p]) continue;
|
|
22
|
+
const a = u[n] + z + (r ? r(t, f) : 0);
|
|
23
|
+
a >= u[p] || a > h || (u[p] = a, x[p] = c, y.push(p, a));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const O = (n, t) => n >= 0 && t >= 0 && n < i && t < o.height;
|
|
27
|
+
return {
|
|
28
|
+
width: i,
|
|
29
|
+
height: o.height,
|
|
30
|
+
costs: u,
|
|
31
|
+
steps: x,
|
|
32
|
+
stepAt(n, t) {
|
|
33
|
+
if (!O(n, t)) return null;
|
|
34
|
+
const f = x[t * i + n];
|
|
35
|
+
if (f < 0) return null;
|
|
36
|
+
const c = S[f];
|
|
37
|
+
return [c[0], c[1]];
|
|
38
|
+
},
|
|
39
|
+
costAt(n, t) {
|
|
40
|
+
return O(n, t) ? u[t * i + n] : 1 / 0;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function N(o, s, w = o.width * o.height) {
|
|
45
|
+
if (o.costAt(s.x, s.y) === 1 / 0) return null;
|
|
46
|
+
const r = [{ x: s.x, y: s.y }];
|
|
47
|
+
let l = s.x, h = s.y;
|
|
48
|
+
for (let i = 0; i < w; i++) {
|
|
49
|
+
const e = o.stepAt(l, h);
|
|
50
|
+
if (!e) return r;
|
|
51
|
+
l += e[0], h += e[1], r.push({ x: l, y: h });
|
|
52
|
+
}
|
|
53
|
+
return r;
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
M as flowField,
|
|
57
|
+
N as followField
|
|
58
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const M = () => ({ x: 0, y: 0 });
|
|
2
|
+
function h(o, x, t, y) {
|
|
3
|
+
const n = Math.hypot(o, x);
|
|
4
|
+
return n === 0 ? { x: -y.vx, y: -y.vy } : { x: o / n * t - y.vx, y: x / n * t - y.vy };
|
|
5
|
+
}
|
|
6
|
+
function b(o, x, t, y) {
|
|
7
|
+
return h(x - o.x, t - o.y, y, o);
|
|
8
|
+
}
|
|
9
|
+
function I(o, x, t, y, n = 1 / 0) {
|
|
10
|
+
const r = o.x - x, f = o.y - t;
|
|
11
|
+
return r * r + f * f > n * n ? M() : h(r, f, y, o);
|
|
12
|
+
}
|
|
13
|
+
function w(o, x, t, y, n) {
|
|
14
|
+
const r = x - o.x, f = t - o.y, c = Math.hypot(r, f), v = c < n ? y * (c / n) : y;
|
|
15
|
+
return h(r, f, v, o);
|
|
16
|
+
}
|
|
17
|
+
function A(o, x, t, y) {
|
|
18
|
+
let n = 0, r = 0;
|
|
19
|
+
for (const f of x) {
|
|
20
|
+
if (f === o) continue;
|
|
21
|
+
let c = o.x - f.x, v = o.y - f.y, i = Math.hypot(c, v);
|
|
22
|
+
if (i >= t) continue;
|
|
23
|
+
i === 0 ? (c = o.vx - f.vx || 1, v = o.vy - f.vy, i = Math.hypot(c, v), c /= i, v /= i, i = 0) : (c /= i, v /= i);
|
|
24
|
+
const s = (1 - i / t) * y;
|
|
25
|
+
n += c * s, r += v * s;
|
|
26
|
+
}
|
|
27
|
+
return { x: n, y: r };
|
|
28
|
+
}
|
|
29
|
+
function k(o, x, t) {
|
|
30
|
+
let y = 0, n = 0, r = 0;
|
|
31
|
+
for (const f of x)
|
|
32
|
+
f === o || (f.x - o.x) ** 2 + (f.y - o.y) ** 2 > t * t || (y += f.vx, n += f.vy, r++);
|
|
33
|
+
return r === 0 ? M() : { x: y / r - o.vx, y: n / r - o.vy };
|
|
34
|
+
}
|
|
35
|
+
function E(o, x, t, y) {
|
|
36
|
+
let n = 0, r = 0, f = 0;
|
|
37
|
+
for (const c of x)
|
|
38
|
+
c === o || (c.x - o.x) ** 2 + (c.y - o.y) ** 2 > t * t || (n += c.x, r += c.y, f++);
|
|
39
|
+
return f === 0 ? M() : b(o, n / f, r / f, y);
|
|
40
|
+
}
|
|
41
|
+
function O(o, x, t, y) {
|
|
42
|
+
const n = Math.floor(o.x / t), r = Math.floor(o.y / t), f = x.stepAt(n, r);
|
|
43
|
+
if (f) return h(f[0], f[1], y, o);
|
|
44
|
+
if (x.costAt(n, r) === 0) return { x: -o.vx, y: -o.vy };
|
|
45
|
+
let c = 1 / 0, v = 0, i = 0;
|
|
46
|
+
for (let s = 1; s <= 2 && c === 1 / 0; s++)
|
|
47
|
+
for (let u = -s; u <= s; u++)
|
|
48
|
+
for (let l = -s; l <= s; l++) {
|
|
49
|
+
if (Math.max(Math.abs(l), Math.abs(u)) !== s) continue;
|
|
50
|
+
const p = x.costAt(n + l, r + u);
|
|
51
|
+
p < c && (c = p, v = l, i = u);
|
|
52
|
+
}
|
|
53
|
+
return c === 1 / 0 ? { x: -o.vx, y: -o.vy } : b(o, (n + v + 0.5) * t, (r + i + 0.5) * t, y);
|
|
54
|
+
}
|
|
55
|
+
function Z(o, ...x) {
|
|
56
|
+
let t = 0, y = 0;
|
|
57
|
+
for (const [r, f] of x)
|
|
58
|
+
t += r.x * f, y += r.y * f;
|
|
59
|
+
const n = Math.hypot(t, y);
|
|
60
|
+
return n > o && (t = t / n * o, y = y / n * o), { x: t, y };
|
|
61
|
+
}
|
|
62
|
+
function j(o, x, t, y) {
|
|
63
|
+
o.vx += x.x * y, o.vy += x.y * y;
|
|
64
|
+
const n = Math.hypot(o.vx, o.vy);
|
|
65
|
+
n > t && (o.vx = o.vx / n * t, o.vy = o.vy / n * t), o.x += o.vx * y, o.y += o.vy * y;
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
k as alignment,
|
|
69
|
+
j as applySteering,
|
|
70
|
+
w as arrive,
|
|
71
|
+
E as cohesion,
|
|
72
|
+
Z as combineSteering,
|
|
73
|
+
I as flee,
|
|
74
|
+
O as followFlow,
|
|
75
|
+
b as seek,
|
|
76
|
+
A as separation
|
|
77
|
+
};
|
|
@@ -1,55 +1,62 @@
|
|
|
1
1
|
class x {
|
|
2
|
-
constructor(
|
|
3
|
-
this.delayMs =
|
|
2
|
+
constructor(e = 100, a = 120, t = 30) {
|
|
3
|
+
this.delayMs = e, this.maxExtrapolateMs = a, this.keep = t;
|
|
4
4
|
}
|
|
5
5
|
delayMs;
|
|
6
6
|
maxExtrapolateMs;
|
|
7
7
|
keep;
|
|
8
8
|
samples = /* @__PURE__ */ new Map();
|
|
9
9
|
/** Records every body of a snapshot taken at `time`. */
|
|
10
|
-
push(
|
|
11
|
-
for (const [t, n,
|
|
10
|
+
push(e, a) {
|
|
11
|
+
for (const [t, n, r, h, m, p, c] of a) {
|
|
12
12
|
let i = this.samples.get(t);
|
|
13
13
|
i || (i = [], this.samples.set(t, i));
|
|
14
14
|
const l = i[i.length - 1];
|
|
15
|
-
l &&
|
|
15
|
+
l && e <= l.time || (i.push({ time: e, x: n, y: r, aim: h, anim: m, hp: p, flags: c }), i.length > this.keep && i.splice(0, i.length - this.keep));
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* The server time bodies are drawn at when it is `now` (`serverNow()`): what a shot should carry, so a server with
|
|
20
|
+
* lag compensation judges it against the picture the player aimed at.
|
|
21
|
+
*/
|
|
22
|
+
viewTime(e) {
|
|
23
|
+
return e - this.delayMs;
|
|
24
|
+
}
|
|
18
25
|
/** Forgets a body (it left view). */
|
|
19
|
-
remove(
|
|
20
|
-
this.samples.delete(
|
|
26
|
+
remove(e) {
|
|
27
|
+
this.samples.delete(e);
|
|
21
28
|
}
|
|
22
|
-
has(
|
|
23
|
-
return this.samples.has(
|
|
29
|
+
has(e) {
|
|
30
|
+
return this.samples.has(e);
|
|
24
31
|
}
|
|
25
32
|
ids() {
|
|
26
33
|
return this.samples.keys();
|
|
27
34
|
}
|
|
28
35
|
/** Where body `id` should be drawn at server time `now`, or null if nothing is known of it. */
|
|
29
|
-
sample(
|
|
30
|
-
const t = this.samples.get(
|
|
36
|
+
sample(e, a) {
|
|
37
|
+
const t = this.samples.get(e);
|
|
31
38
|
if (!t || t.length === 0) return null;
|
|
32
|
-
const n = a - this.delayMs,
|
|
33
|
-
if (n <=
|
|
39
|
+
const n = a - this.delayMs, r = t[0];
|
|
40
|
+
if (n <= r.time || t.length === 1) return g(r);
|
|
34
41
|
for (let i = t.length - 1; i > 0; i--) {
|
|
35
|
-
const l = t[i - 1],
|
|
36
|
-
if (n >= l.time && n <=
|
|
37
|
-
const u =
|
|
38
|
-
return f(l,
|
|
42
|
+
const l = t[i - 1], o = t[i];
|
|
43
|
+
if (n >= l.time && n <= o.time) {
|
|
44
|
+
const u = o.time === l.time ? 1 : (n - l.time) / (o.time - l.time);
|
|
45
|
+
return f(l, o, u);
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
const h = t[t.length - 2], m = t[t.length - 1], p = Math.min(n - m.time, this.maxExtrapolateMs), c = m.time === h.time ? 1 : 1 + p / (m.time - h.time);
|
|
42
49
|
return f(h, m, c);
|
|
43
50
|
}
|
|
44
51
|
}
|
|
45
|
-
function g(
|
|
46
|
-
return { x:
|
|
52
|
+
function g(s) {
|
|
53
|
+
return { x: s.x, y: s.y, aim: s.aim, anim: s.anim, hp: s.hp, flags: s.flags };
|
|
47
54
|
}
|
|
48
|
-
function f(
|
|
49
|
-
let t =
|
|
55
|
+
function f(s, e, a) {
|
|
56
|
+
let t = e.aim - s.aim;
|
|
50
57
|
t > Math.PI && (t -= Math.PI * 2), t < -Math.PI && (t += Math.PI * 2);
|
|
51
|
-
const n = a < 1 ?
|
|
52
|
-
return { x:
|
|
58
|
+
const n = a < 1 ? s : e;
|
|
59
|
+
return { x: s.x + (e.x - s.x) * a, y: s.y + (e.y - s.y) * a, aim: s.aim + t * a, anim: n.anim, hp: n.hp, flags: n.flags };
|
|
53
60
|
}
|
|
54
61
|
export {
|
|
55
62
|
x as InterpolationBuffer
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
class k {
|
|
2
|
+
/** `keepMs`: how much history is kept; at least the longest rewind allowed plus a snapshot interval. */
|
|
3
|
+
constructor(s = 1e3) {
|
|
4
|
+
this.keepMs = s;
|
|
5
|
+
}
|
|
6
|
+
keepMs;
|
|
7
|
+
frames = [];
|
|
8
|
+
/** The latest recorded time, or -Infinity before any. */
|
|
9
|
+
get newest() {
|
|
10
|
+
return this.frames.length ? this.frames[this.frames.length - 1].time : -1 / 0;
|
|
11
|
+
}
|
|
12
|
+
get oldest() {
|
|
13
|
+
return this.frames.length ? this.frames[0].time : -1 / 0;
|
|
14
|
+
}
|
|
15
|
+
/** Records every body's position at `time`. A time not after the last one recorded replaces that frame. */
|
|
16
|
+
record(s, t) {
|
|
17
|
+
const e = /* @__PURE__ */ new Map();
|
|
18
|
+
for (const r of t) e.set(r.id, [r.x, r.y]);
|
|
19
|
+
const n = this.frames[this.frames.length - 1];
|
|
20
|
+
n && s <= n.time ? n.pos = e : this.frames.push({ time: s, pos: e });
|
|
21
|
+
const o = s - this.keepMs;
|
|
22
|
+
for (; this.frames.length > 2 && this.frames[1].time <= o; ) this.frames.shift();
|
|
23
|
+
}
|
|
24
|
+
/** Forgets a body everywhere in the history (it left the game, not just the view). */
|
|
25
|
+
forget(s) {
|
|
26
|
+
for (const t of this.frames) t.pos.delete(s);
|
|
27
|
+
}
|
|
28
|
+
clear() {
|
|
29
|
+
this.frames.length = 0;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A time a client claims it saw, kept within `[now - maxRewindMs, now]`: the window a rewind is honoured in. A
|
|
33
|
+
* claim that is not a finite number is taken as `now` (no rewind).
|
|
34
|
+
*/
|
|
35
|
+
static clampTime(s, t, e) {
|
|
36
|
+
return typeof s != "number" || !Number.isFinite(s) ? t : Math.min(t, Math.max(t - e, s));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Where body `id` was at `time`, blended between the frames around it as `InterpolationBuffer` blends; null when
|
|
40
|
+
* the history has no frame of it. Before the oldest frame it is where it was then; after the newest, where it is.
|
|
41
|
+
*/
|
|
42
|
+
at(s, t) {
|
|
43
|
+
const e = this.around(t);
|
|
44
|
+
return e ? b(e[0], e[1], e[2], s) : null;
|
|
45
|
+
}
|
|
46
|
+
/** Every body in the history at `time`. */
|
|
47
|
+
all(s) {
|
|
48
|
+
const t = this.around(s);
|
|
49
|
+
if (!t) return [];
|
|
50
|
+
const [e, n, o] = t, r = /* @__PURE__ */ new Set([...e.pos.keys(), ...n.pos.keys()]), i = [];
|
|
51
|
+
for (const a of r) {
|
|
52
|
+
const h = b(e, n, o, a);
|
|
53
|
+
i.push({ id: a, x: h.x, y: h.y });
|
|
54
|
+
}
|
|
55
|
+
return i;
|
|
56
|
+
}
|
|
57
|
+
/** The bodies whose centre was within `radius` of (x, y) at `time`, nearest first; `except` (the shooter) left out. */
|
|
58
|
+
near(s, t, e, n, o) {
|
|
59
|
+
return this.all(s).filter((i) => i.id !== o && (i.x - t) ** 2 + (i.y - e) ** 2 <= n * n).sort((i, a) => (i.x - t) ** 2 + (i.y - e) ** 2 - ((a.x - t) ** 2 + (a.y - e) ** 2));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A hitscan at `time`: the bodies a ray from (x, y) along (dx, dy) passed within `radius` of, before `range` px, in
|
|
63
|
+
* the order it met them. `except` (the shooter) is left out. Walls are the game's to check against the first hit.
|
|
64
|
+
*/
|
|
65
|
+
alongRay(s, t, e, n, o, r, i, a) {
|
|
66
|
+
const h = Math.hypot(n, o);
|
|
67
|
+
if (h === 0) return [];
|
|
68
|
+
const u = n / h, m = o / h, p = [];
|
|
69
|
+
for (const f of this.all(s)) {
|
|
70
|
+
if (f.id === a) continue;
|
|
71
|
+
const c = (f.x - t) * u + (f.y - e) * m, g = t + u * c - f.x, y = e + m * c - f.y, x = g * g + y * y;
|
|
72
|
+
if (x > i * i) continue;
|
|
73
|
+
const M = c - Math.sqrt(i * i - x);
|
|
74
|
+
c < -i || M > r || p.push({ id: f.id, x: f.x, y: f.y, along: Math.max(0, M) });
|
|
75
|
+
}
|
|
76
|
+
return p.sort((f, c) => f.along - c.along);
|
|
77
|
+
}
|
|
78
|
+
/** The two frames around `time` and how far between them it is (0 at the first, 1 at the second). */
|
|
79
|
+
around(s) {
|
|
80
|
+
const t = this.frames;
|
|
81
|
+
if (t.length === 0) return null;
|
|
82
|
+
if (s <= t[0].time) return [t[0], t[0], 0];
|
|
83
|
+
const e = t[t.length - 1];
|
|
84
|
+
if (s >= e.time) return [e, e, 0];
|
|
85
|
+
for (let n = t.length - 1; n > 0; n--) {
|
|
86
|
+
const o = t[n - 1], r = t[n];
|
|
87
|
+
if (s >= o.time && s <= r.time) return [o, r, r.time === o.time ? 1 : (s - o.time) / (r.time - o.time)];
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function b(l, s, t, e) {
|
|
93
|
+
const n = l.pos.get(e), o = s.pos.get(e);
|
|
94
|
+
if (n && o) return { x: n[0] + (o[0] - n[0]) * t, y: n[1] + (o[1] - n[1]) * t };
|
|
95
|
+
const r = n ?? o;
|
|
96
|
+
return r ? { x: r[0], y: r[1] } : null;
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
k as LagCompensator
|
|
100
|
+
};
|