1681-engine 0.5.10 → 0.6.1

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.
Files changed (98) hide show
  1. package/CHANGELOG.md +61 -1
  2. package/README.md +12 -6
  3. package/dist/1681-engine.js +588 -522
  4. package/dist/ai/behaviorTree.js +198 -0
  5. package/dist/ai/buildTree.js +54 -0
  6. package/dist/audio/spatial.js +118 -0
  7. package/dist/cjs/electron/webPatcher.d.ts +4 -1
  8. package/dist/cjs/electron/webPatcher.js +9 -2
  9. package/dist/core/Camera.js +10 -0
  10. package/dist/core/ObjectFactory.js +23 -21
  11. package/dist/core/PixelGrid.js +47 -0
  12. package/dist/core/SharpText.js +72 -0
  13. package/dist/core/Stage.js +48 -31
  14. package/dist/core/gridPlacement.js +14 -0
  15. package/dist/dialogue/DialogueRunner.js +173 -0
  16. package/dist/dialogue/expr.js +137 -0
  17. package/dist/dialogue/validateDialogue.js +52 -0
  18. package/dist/ecs/prefab.js +94 -0
  19. package/dist/electron/webPatcher.js +16 -10
  20. package/dist/input/Actions.js +58 -27
  21. package/dist/input/InputCapture.js +98 -0
  22. package/dist/input/Keymap.js +32 -26
  23. package/dist/input/inputTape.js +68 -0
  24. package/dist/mobile/backButton.js +40 -0
  25. package/dist/mobile/capacitor.js +48 -0
  26. package/dist/mobile/screen.js +31 -0
  27. package/dist/mobile.js +12 -0
  28. package/dist/nav/flowField.js +58 -0
  29. package/dist/nav/steering.js +77 -0
  30. package/dist/net/InterpolationBuffer.js +30 -23
  31. package/dist/net/LagCompensator.js +100 -0
  32. package/dist/net/Room.js +122 -43
  33. package/dist/net/RoomHost.js +13 -4
  34. package/dist/net/SpatialHash.js +66 -0
  35. package/dist/types/ai/behaviorTree.d.ts +133 -0
  36. package/dist/types/ai/behaviorTree.d.ts.map +1 -0
  37. package/dist/types/ai/buildTree.d.ts +55 -0
  38. package/dist/types/ai/buildTree.d.ts.map +1 -0
  39. package/dist/types/audio/spatial.d.ts +107 -0
  40. package/dist/types/audio/spatial.d.ts.map +1 -0
  41. package/dist/types/core/Camera.d.ts +8 -0
  42. package/dist/types/core/Camera.d.ts.map +1 -1
  43. package/dist/types/core/ObjectFactory.d.ts.map +1 -1
  44. package/dist/types/core/PixelGrid.d.ts +30 -0
  45. package/dist/types/core/PixelGrid.d.ts.map +1 -0
  46. package/dist/types/core/SharpText.d.ts +32 -0
  47. package/dist/types/core/SharpText.d.ts.map +1 -0
  48. package/dist/types/core/Stage.d.ts +14 -0
  49. package/dist/types/core/Stage.d.ts.map +1 -1
  50. package/dist/types/core/gridPlacement.d.ts +20 -0
  51. package/dist/types/core/gridPlacement.d.ts.map +1 -0
  52. package/dist/types/dialogue/DialogueRunner.d.ts +78 -0
  53. package/dist/types/dialogue/DialogueRunner.d.ts.map +1 -0
  54. package/dist/types/dialogue/expr.d.ts +12 -0
  55. package/dist/types/dialogue/expr.d.ts.map +1 -0
  56. package/dist/types/dialogue/script.d.ts +78 -0
  57. package/dist/types/dialogue/script.d.ts.map +1 -0
  58. package/dist/types/dialogue/validateDialogue.d.ts +21 -0
  59. package/dist/types/dialogue/validateDialogue.d.ts.map +1 -0
  60. package/dist/types/ecs/prefab.d.ts +84 -0
  61. package/dist/types/ecs/prefab.d.ts.map +1 -0
  62. package/dist/types/electron/webPatcher.d.ts +4 -1
  63. package/dist/types/electron/webPatcher.d.ts.map +1 -1
  64. package/dist/types/index.d.ts +18 -1
  65. package/dist/types/index.d.ts.map +1 -1
  66. package/dist/types/input/Actions.d.ts +14 -0
  67. package/dist/types/input/Actions.d.ts.map +1 -1
  68. package/dist/types/input/InputCapture.d.ts +53 -0
  69. package/dist/types/input/InputCapture.d.ts.map +1 -0
  70. package/dist/types/input/Keymap.d.ts +12 -6
  71. package/dist/types/input/Keymap.d.ts.map +1 -1
  72. package/dist/types/input/inputTape.d.ts +66 -0
  73. package/dist/types/input/inputTape.d.ts.map +1 -0
  74. package/dist/types/mobile/backButton.d.ts +30 -0
  75. package/dist/types/mobile/backButton.d.ts.map +1 -0
  76. package/dist/types/mobile/capacitor.d.ts +62 -0
  77. package/dist/types/mobile/capacitor.d.ts.map +1 -0
  78. package/dist/types/mobile/index.d.ts +4 -0
  79. package/dist/types/mobile/index.d.ts.map +1 -0
  80. package/dist/types/mobile/screen.d.ts +27 -0
  81. package/dist/types/mobile/screen.d.ts.map +1 -0
  82. package/dist/types/nav/flowField.d.ts +31 -0
  83. package/dist/types/nav/flowField.d.ts.map +1 -0
  84. package/dist/types/nav/steering.d.ts +47 -0
  85. package/dist/types/nav/steering.d.ts.map +1 -0
  86. package/dist/types/net/InterpolationBuffer.d.ts +5 -0
  87. package/dist/types/net/InterpolationBuffer.d.ts.map +1 -1
  88. package/dist/types/net/LagCompensator.d.ts +70 -0
  89. package/dist/types/net/LagCompensator.d.ts.map +1 -0
  90. package/dist/types/net/Room.d.ts +72 -3
  91. package/dist/types/net/Room.d.ts.map +1 -1
  92. package/dist/types/net/RoomHost.d.ts +8 -3
  93. package/dist/types/net/RoomHost.d.ts.map +1 -1
  94. package/dist/types/net/SpatialHash.d.ts +25 -0
  95. package/dist/types/net/SpatialHash.d.ts.map +1 -0
  96. package/dist/types/ui/ScrollView.d.ts.map +1 -1
  97. package/dist/ui/ScrollView.js +10 -8
  98. package/package.json +5 -1
@@ -1,9 +1,9 @@
1
1
  import { parseChord as c, chordHeld as l } from "./keyChord.js";
2
2
  import { rumblePad as f } from "./rumble.js";
3
3
  const u = ["a", "b", "x", "y", "lb", "rb", "lt", "rt", "select", "start", "ls", "rs", "up", "down", "left", "right", "home"], w = ["lx", "ly", "rx", "ry"];
4
- class g {
5
- constructor(t, e, s = {}) {
6
- this.keyboard = t, this.bindings = new Map(Object.entries(e)), this.deadZone = s.deadZone ?? 0.25, this.gamepads = s.gamepads ?? (() => typeof navigator < "u" && typeof navigator.getGamepads == "function" ? navigator.getGamepads() : []);
4
+ class v {
5
+ constructor(t, s, e = {}) {
6
+ this.keyboard = t, this.bindings = new Map(Object.entries(s)), this.deadZone = e.deadZone ?? 0.25, this.gamepads = e.gamepads ?? (() => typeof navigator < "u" && typeof navigator.getGamepads == "function" ? navigator.getGamepads() : []);
7
7
  }
8
8
  keyboard;
9
9
  bindings;
@@ -18,10 +18,14 @@ class g {
18
18
  chords = /* @__PURE__ */ new Map();
19
19
  /** Keys a chord holds this step: their bare bindings read 0 until the key comes up. */
20
20
  claimed = /* @__PURE__ */ new Set();
21
+ /** Where `poll` takes its values from instead of the devices (a replay), until it returns null. */
22
+ source = null;
23
+ /** Handed every step's values after `poll` (a recorder). */
24
+ taps = /* @__PURE__ */ new Set();
21
25
  keyDown = (t) => this.keyboard !== null && (this.keyboard.isDown(t) || this.keyboard.pressed(t));
22
26
  /** Replaces one action's bindings (a player's rebinding). */
23
- bind(t, e) {
24
- this.bindings.set(t, [...e]);
27
+ bind(t, s) {
28
+ this.bindings.set(t, [...s]);
25
29
  }
26
30
  bindingsOf(t) {
27
31
  return this.bindings.get(t) ?? [];
@@ -37,19 +41,46 @@ class g {
37
41
  * Rumbles the gamepads the actions read: every connected one, or only `padIndex` among them. Resolves true when any
38
42
  * played; a pad without rumble is skipped silently.
39
43
  */
40
- async rumble(t, e) {
41
- const s = this.gamepads(), i = e === void 0 ? s : [s[e] ?? null];
44
+ async rumble(t, s) {
45
+ const e = this.gamepads(), i = s === void 0 ? e : [e[s] ?? null];
42
46
  return (await Promise.all(i.map((r) => r ? f(r, t) : Promise.resolve(!1)))).includes(!0);
43
47
  }
44
48
  /** Reads every device once; call at the start of each simulation step. */
45
49
  poll() {
46
- [this.before, this.now] = [this.now, this.before], this.now.clear();
47
- const t = this.gamepads().filter((e) => e !== null);
50
+ if ([this.before, this.now] = [this.now, this.before], this.now.clear(), this.source) {
51
+ const t = this.source();
52
+ if (t) {
53
+ for (const [s, e] of t) e > 0 && this.now.set(s, e);
54
+ for (const s of this.taps) s(this.now);
55
+ return;
56
+ }
57
+ this.source = null;
58
+ }
59
+ this.readDevices();
60
+ for (const t of this.taps) t(this.now);
61
+ }
62
+ /**
63
+ * Takes each step's values from `source` instead of the devices (an `InputPlayer`), until it returns null; then
64
+ * the devices answer again. Pass null to stop at once.
65
+ */
66
+ replay(t) {
67
+ this.source = t;
68
+ }
69
+ /** Whether a replay is driving the actions now. */
70
+ get replaying() {
71
+ return this.source !== null;
72
+ }
73
+ /** Calls `tap` with every step's values right after `poll` (an `InputRecorder`); returns the stop. */
74
+ tap(t) {
75
+ return this.taps.add(t), () => this.taps.delete(t);
76
+ }
77
+ readDevices() {
78
+ const t = this.gamepads().filter((s) => s !== null);
48
79
  this.claimChords();
49
- for (const [e, s] of this.bindings) {
80
+ for (const [s, e] of this.bindings) {
50
81
  let i = 0;
51
- for (const o of s) i = Math.max(i, this.read(o, t));
52
- i > 0 && this.now.set(e, i);
82
+ for (const o of e) i = Math.max(i, this.read(o, t));
83
+ i > 0 && this.now.set(s, i);
53
84
  }
54
85
  }
55
86
  /** Held this step. */
@@ -69,12 +100,12 @@ class g {
69
100
  return !this.down(t) && (this.before.get(t) ?? 0) > 0;
70
101
  }
71
102
  /** `value(positive) - value(negative)`, in −1…1. */
72
- axis(t, e) {
73
- return this.value(e) - this.value(t);
103
+ axis(t, s) {
104
+ return this.value(s) - this.value(t);
74
105
  }
75
106
  /** A 2D direction from four actions, never longer than 1 (a diagonal is not faster). */
76
- vector(t, e, s, i) {
77
- const o = this.axis(t, e), r = this.axis(s, i), n = Math.hypot(o, r);
107
+ vector(t, s, e, i) {
108
+ const o = this.axis(t, s), r = this.axis(e, i), n = Math.hypot(o, r);
78
109
  return n > 1 ? [o / n, r / n] : [o, r];
79
110
  }
80
111
  /** Ends the step: the keyboard forgets its edges; virtual taps shorter than a step have now been seen. */
@@ -90,20 +121,20 @@ class g {
90
121
  this.keyboard?.endStep(), this.virtualDown.clear(), this.poll(), this.before = new Map(this.now);
91
122
  }
92
123
  chordOf(t) {
93
- let e = this.chords.get(t);
94
- return e === void 0 && (e = t.startsWith("pad:") || t.startsWith("touch:") ? null : c(t), this.chords.set(t, e)), e;
124
+ let s = this.chords.get(t);
125
+ return s === void 0 && (s = t.startsWith("pad:") || t.startsWith("touch:") ? null : c(t), this.chords.set(t, s)), s;
95
126
  }
96
127
  /** Marks the keys held chords use, and frees those whose key has come up. */
97
128
  claimChords() {
98
129
  for (const t of this.claimed) this.keyDown(t) || this.claimed.delete(t);
99
130
  if (this.keyboard)
100
131
  for (const t of this.bindings.values())
101
- for (const e of t) {
102
- const s = this.chordOf(e);
103
- s && l(s, this.keyDown) && this.claimed.add(s.code);
132
+ for (const s of t) {
133
+ const e = this.chordOf(s);
134
+ e && l(e, this.keyDown) && this.claimed.add(e.code);
104
135
  }
105
136
  }
106
- read(t, e) {
137
+ read(t, s) {
107
138
  if (t.startsWith("touch:")) {
108
139
  const i = t.slice(6);
109
140
  return this.virtual.has(i) || this.virtualDown.has(i) ? 1 : 0;
@@ -115,7 +146,7 @@ class g {
115
146
  if (r !== 0) {
116
147
  const h = w.indexOf(i.slice(0, -1));
117
148
  if (h < 0) return 0;
118
- for (const a of e) {
149
+ for (const a of s) {
119
150
  const d = (a.axes[h] ?? 0) * r;
120
151
  d > this.deadZone && (o = Math.max(o, (d - this.deadZone) / (1 - this.deadZone)));
121
152
  }
@@ -123,17 +154,17 @@ class g {
123
154
  }
124
155
  const n = u.indexOf(i);
125
156
  if (n < 0) return 0;
126
- for (const h of e) {
157
+ for (const h of s) {
127
158
  const a = h.buttons[n];
128
159
  a?.pressed && (o = Math.max(o, a.value > 0 ? a.value : 1));
129
160
  }
130
161
  return Math.min(1, o);
131
162
  }
132
- const s = this.chordOf(t);
133
- return s ? l(s, this.keyDown) ? 1 : 0 : this.claimed.has(t) ? 0 : this.keyDown(t) ? 1 : 0;
163
+ const e = this.chordOf(t);
164
+ return e ? l(e, this.keyDown) ? 1 : 0 : this.claimed.has(t) ? 0 : this.keyDown(t) ? 1 : 0;
134
165
  }
135
166
  }
136
167
  export {
137
- g as Actions,
168
+ v as Actions,
138
169
  u as PAD_BUTTONS
139
170
  };
@@ -0,0 +1,98 @@
1
+ import { PAD_BUTTONS as r } from "./Actions.js";
2
+ function c(s) {
3
+ return s.startsWith("pad:") ? "pad" : s.startsWith("touch:") ? "touch" : "keys";
4
+ }
5
+ const h = ["lx", "ly", "rx", "ry"], a = {
6
+ a: "A",
7
+ b: "B",
8
+ x: "X",
9
+ y: "Y",
10
+ lb: "LB",
11
+ rb: "RB",
12
+ lt: "LT",
13
+ rt: "RT",
14
+ select: "Select",
15
+ start: "Start",
16
+ ls: "Left stick press",
17
+ rs: "Right stick press",
18
+ up: "D-pad up",
19
+ down: "D-pad down",
20
+ left: "D-pad left",
21
+ right: "D-pad right",
22
+ home: "Home",
23
+ "lx-": "Left stick left",
24
+ "lx+": "Left stick right",
25
+ "ly-": "Left stick up",
26
+ "ly+": "Left stick down",
27
+ "rx-": "Right stick left",
28
+ "rx+": "Right stick right",
29
+ "ry-": "Right stick up",
30
+ "ry+": "Right stick down"
31
+ };
32
+ function p(s, t = {}) {
33
+ if (!s.startsWith("pad:")) return s;
34
+ const e = s.slice(4);
35
+ return t[e] ?? a[e] ?? e;
36
+ }
37
+ class l {
38
+ result;
39
+ settle;
40
+ done = !1;
41
+ held = /* @__PURE__ */ new Set();
42
+ options;
43
+ gamepads;
44
+ target;
45
+ onKey = (t) => this.key(t);
46
+ constructor(t = {}) {
47
+ this.options = {
48
+ devices: t.devices ?? ["keys", "pad"],
49
+ ignoreKeys: t.ignoreKeys ?? ["Escape"],
50
+ cancelKeys: t.cancelKeys ?? ["Escape"],
51
+ axisThreshold: t.axisThreshold ?? 0.6
52
+ }, this.gamepads = t.gamepads ?? (() => typeof navigator < "u" && typeof navigator.getGamepads == "function" ? navigator.getGamepads() : []), this.target = t.target ?? (typeof window < "u" ? window : null), this.result = new Promise((e) => this.settle = e);
53
+ for (const e of this.padInputs()) this.held.add(e);
54
+ this.options.devices.includes("keys") && this.target?.addEventListener("keydown", this.onKey, { capture: !0 });
55
+ }
56
+ /** Reads the gamepads once; call every frame while the capture is open. */
57
+ poll() {
58
+ if (this.done || !this.options.devices.includes("pad")) return;
59
+ const t = new Set(this.padInputs());
60
+ for (const e of t)
61
+ if (!this.held.has(e))
62
+ return this.finish(e);
63
+ for (const e of [...this.held]) t.has(e) || this.held.delete(e);
64
+ }
65
+ /** Stops waiting; the result is null. */
66
+ cancel() {
67
+ this.finish(null);
68
+ }
69
+ get open() {
70
+ return !this.done;
71
+ }
72
+ key(t) {
73
+ if (!(this.done || t.repeat)) {
74
+ if (t.preventDefault(), t.stopImmediatePropagation(), this.options.cancelKeys.includes(t.code)) return this.finish(null);
75
+ this.options.ignoreKeys.includes(t.code) || !t.code || this.finish(t.code);
76
+ }
77
+ }
78
+ finish(t) {
79
+ this.done || (this.done = !0, this.target?.removeEventListener("keydown", this.onKey, { capture: !0 }), this.settle(t));
80
+ }
81
+ /** Every pad input down on any pad now, as bindings. */
82
+ padInputs() {
83
+ const t = [];
84
+ for (const e of this.gamepads())
85
+ e && (e.buttons.forEach((i, n) => {
86
+ (i.pressed || i.value > this.options.axisThreshold) && r[n] && t.push(`pad:${r[n]}`);
87
+ }), h.forEach((i, n) => {
88
+ const o = e.axes[n] ?? 0;
89
+ o > this.options.axisThreshold ? t.push(`pad:${i}+`) : o < -this.options.axisThreshold && t.push(`pad:${i}-`);
90
+ }));
91
+ return t;
92
+ }
93
+ }
94
+ export {
95
+ l as InputCapture,
96
+ c as deviceOf,
97
+ p as padLabel
98
+ };
@@ -1,4 +1,5 @@
1
- class g {
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
- /** The keys of one action; the first is the one a settings screen shows and edits. */
27
- keysFor(t) {
28
- return this.binds[t] ?? [];
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`, taking it off whatever else held it; returns the actions that lost it (to tell the
40
- * player). A reserved key is refused (returns null). An action stripped of its last key keeps none: it is unbound
41
- * until something is put back on it.
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((a) => a !== s), e.push(n));
48
- const h = this.binds[t].slice(1).filter((n) => n !== s);
49
- return this.binds[t] = [s, ...h], this.changed(), e;
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 = u(this.defaults, t, this.reserved), this.changed();
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 u(i, t, s = /* @__PURE__ */ new Set()) {
94
- const e = Object.keys(i), h = {}, n = /* @__PURE__ */ new Set(), a = t && typeof t == "object" ? t : {};
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((f) => typeof f == "string") ? (h[r] = [...new Set(o)].filter((f) => !s.has(f)), n.add(r)) : h[r] = i[r].filter((f) => !s.has(f));
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 l = /* @__PURE__ */ new Map();
105
+ const n = /* @__PURE__ */ new Map();
100
106
  for (const r of e)
101
- n.has(r) && (h[r] = h[r].filter((o) => l.has(o) ? !1 : (l.set(o, r), !0)));
102
- for (const r of e) n.has(r) || (h[r] = h[r].filter((o) => !l.has(o)));
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 b(i) {
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 c = {
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 d(i, t = {}) {
161
- if (i.length > 1 && i.includes("+")) return i.split("+").map((e) => d(e, t)).join("+");
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)}` : c[i] ?? i;
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
- g as Keymap,
167
- d as keyLabel,
168
- b as keymapStorage,
169
- u as mergeKeymap
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
+ };