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.
Files changed (81) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +12 -6
  3. package/dist/1681-engine.js +582 -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/dialogue/DialogueRunner.js +173 -0
  10. package/dist/dialogue/expr.js +137 -0
  11. package/dist/dialogue/validateDialogue.js +52 -0
  12. package/dist/ecs/prefab.js +94 -0
  13. package/dist/electron/webPatcher.js +16 -10
  14. package/dist/input/Actions.js +58 -27
  15. package/dist/input/InputCapture.js +98 -0
  16. package/dist/input/Keymap.js +32 -26
  17. package/dist/input/inputTape.js +68 -0
  18. package/dist/mobile/backButton.js +40 -0
  19. package/dist/mobile/capacitor.js +48 -0
  20. package/dist/mobile/screen.js +31 -0
  21. package/dist/mobile.js +12 -0
  22. package/dist/nav/flowField.js +58 -0
  23. package/dist/nav/steering.js +77 -0
  24. package/dist/net/InterpolationBuffer.js +30 -23
  25. package/dist/net/LagCompensator.js +100 -0
  26. package/dist/net/Room.js +122 -43
  27. package/dist/net/RoomHost.js +13 -4
  28. package/dist/net/SpatialHash.js +66 -0
  29. package/dist/types/ai/behaviorTree.d.ts +133 -0
  30. package/dist/types/ai/behaviorTree.d.ts.map +1 -0
  31. package/dist/types/ai/buildTree.d.ts +55 -0
  32. package/dist/types/ai/buildTree.d.ts.map +1 -0
  33. package/dist/types/audio/spatial.d.ts +107 -0
  34. package/dist/types/audio/spatial.d.ts.map +1 -0
  35. package/dist/types/dialogue/DialogueRunner.d.ts +78 -0
  36. package/dist/types/dialogue/DialogueRunner.d.ts.map +1 -0
  37. package/dist/types/dialogue/expr.d.ts +12 -0
  38. package/dist/types/dialogue/expr.d.ts.map +1 -0
  39. package/dist/types/dialogue/script.d.ts +78 -0
  40. package/dist/types/dialogue/script.d.ts.map +1 -0
  41. package/dist/types/dialogue/validateDialogue.d.ts +21 -0
  42. package/dist/types/dialogue/validateDialogue.d.ts.map +1 -0
  43. package/dist/types/ecs/prefab.d.ts +84 -0
  44. package/dist/types/ecs/prefab.d.ts.map +1 -0
  45. package/dist/types/electron/webPatcher.d.ts +4 -1
  46. package/dist/types/electron/webPatcher.d.ts.map +1 -1
  47. package/dist/types/index.d.ts +15 -1
  48. package/dist/types/index.d.ts.map +1 -1
  49. package/dist/types/input/Actions.d.ts +14 -0
  50. package/dist/types/input/Actions.d.ts.map +1 -1
  51. package/dist/types/input/InputCapture.d.ts +53 -0
  52. package/dist/types/input/InputCapture.d.ts.map +1 -0
  53. package/dist/types/input/Keymap.d.ts +12 -6
  54. package/dist/types/input/Keymap.d.ts.map +1 -1
  55. package/dist/types/input/inputTape.d.ts +66 -0
  56. package/dist/types/input/inputTape.d.ts.map +1 -0
  57. package/dist/types/mobile/backButton.d.ts +30 -0
  58. package/dist/types/mobile/backButton.d.ts.map +1 -0
  59. package/dist/types/mobile/capacitor.d.ts +62 -0
  60. package/dist/types/mobile/capacitor.d.ts.map +1 -0
  61. package/dist/types/mobile/index.d.ts +4 -0
  62. package/dist/types/mobile/index.d.ts.map +1 -0
  63. package/dist/types/mobile/screen.d.ts +27 -0
  64. package/dist/types/mobile/screen.d.ts.map +1 -0
  65. package/dist/types/nav/flowField.d.ts +31 -0
  66. package/dist/types/nav/flowField.d.ts.map +1 -0
  67. package/dist/types/nav/steering.d.ts +47 -0
  68. package/dist/types/nav/steering.d.ts.map +1 -0
  69. package/dist/types/net/InterpolationBuffer.d.ts +5 -0
  70. package/dist/types/net/InterpolationBuffer.d.ts.map +1 -1
  71. package/dist/types/net/LagCompensator.d.ts +70 -0
  72. package/dist/types/net/LagCompensator.d.ts.map +1 -0
  73. package/dist/types/net/Room.d.ts +72 -3
  74. package/dist/types/net/Room.d.ts.map +1 -1
  75. package/dist/types/net/RoomHost.d.ts +8 -3
  76. package/dist/types/net/RoomHost.d.ts.map +1 -1
  77. package/dist/types/net/SpatialHash.d.ts +25 -0
  78. package/dist/types/net/SpatialHash.d.ts.map +1 -0
  79. package/dist/types/ui/ScrollView.d.ts.map +1 -1
  80. package/dist/ui/ScrollView.js +10 -8
  81. package/package.json +5 -1
package/dist/net/Room.js CHANGED
@@ -1,21 +1,38 @@
1
- import { createMover as l, stepMover as p } from "./movement.js";
2
- import { moverToWire as c } from "./protocol.js";
3
- class g {
1
+ import { LagCompensator as f } from "./LagCompensator.js";
2
+ import { createMover as m, stepMover as p } from "./movement.js";
3
+ import { moverToWire as y } from "./protocol.js";
4
+ import { SpatialHash as b } from "./SpatialHash.js";
5
+ const g = 1 << 24;
6
+ class M {
4
7
  constructor(e) {
5
8
  this.options = e, this.maxQueue = e.maxQueue ?? 30, this.maxSteps = e.maxStepsPerTick ?? 3, this.backlog = e.backlog ?? 4;
9
+ const i = e.interest;
10
+ if (i && !(i.radius > 0)) throw new Error(`Room: interest.radius must be above 0 (got ${i.radius})`);
11
+ this.hash = i ? new b(i.radius) : null, this.rewindMs = Math.max(0, e.rewindMs ?? 0), this.history = this.rewindMs > 0 ? new f(this.rewindMs + 200) : null;
6
12
  }
7
13
  options;
8
14
  players = /* @__PURE__ */ new Map();
15
+ /** The bodies the server moves (`addBody`). */
16
+ bodies = /* @__PURE__ */ new Map();
9
17
  tick = 0;
18
+ /** Past positions for judging shots against what a client saw, when the room was made with `rewindMs`. */
19
+ history;
20
+ /** How far back a client's claimed view time is honoured (`rewindMs`, 0 when off). */
21
+ rewindMs;
10
22
  known = /* @__PURE__ */ new Map();
23
+ hash;
24
+ nearby = [];
11
25
  maxQueue;
12
26
  maxSteps;
13
27
  backlog;
28
+ nextBodyId = g;
14
29
  join(e, i) {
15
30
  if (this.players.has(e)) throw new Error(`Room: player ${e} has already joined`);
16
- const r = this.options.spawn(e, i), t = {
31
+ if (this.bodies.has(e)) throw new Error(`Room: id ${e} is a body's`);
32
+ const t = this.options.spawn(e, i), s = {
33
+ kind: "player",
17
34
  id: e,
18
- mover: l(r.x, r.y),
35
+ mover: m(t.x, t.y),
19
36
  data: i,
20
37
  params: { ...this.options.params },
21
38
  ack: -1,
@@ -26,66 +43,128 @@ class g {
26
43
  flags: 0,
27
44
  strikes: 0
28
45
  };
29
- return this.players.set(e, t), this.known.set(e, /* @__PURE__ */ new Set()), t;
46
+ return this.players.set(e, s), this.known.set(e, /* @__PURE__ */ new Set()), this.hash?.set(e, t.x, t.y), s;
30
47
  }
31
48
  leave(e) {
32
- this.players.delete(e), this.known.delete(e);
49
+ this.players.delete(e), this.known.delete(e), this.hash?.delete(e), this.history?.forget(e);
50
+ }
51
+ /**
52
+ * Records where every body is now, at server time `time`, into `history`; `RoomHost` calls it as each round of
53
+ * snapshots goes out, so the history holds exactly the positions clients interpolate between.
54
+ */
55
+ record(e) {
56
+ if (!this.history) return;
57
+ const i = [...this.players.values()].map((t) => ({ id: t.id, x: t.mover.x, y: t.mover.y }));
58
+ for (const t of this.bodies.values()) i.push({ id: t.id, x: t.x, y: t.y });
59
+ this.history.record(e, i);
60
+ }
61
+ /**
62
+ * Adds a body the server moves, at (x, y), with the game's `data` (what players are told it looks like). Its id
63
+ * is `id`, or the next from `BODY_ID_BASE` up; an id a player or body has is refused.
64
+ */
65
+ addBody(e, i, t, s) {
66
+ let o = s;
67
+ if (o === void 0) {
68
+ for (; this.bodies.has(this.nextBodyId) || this.players.has(this.nextBodyId); ) this.nextBodyId++;
69
+ o = this.nextBodyId++;
70
+ } else if (this.bodies.has(o) || this.players.has(o)) throw new Error(`Room: id ${o} is taken`);
71
+ if (!Number.isInteger(o) || o < 0 || o > 2147483647) throw new Error(`Room: a body id must fit an i32 (got ${o})`);
72
+ const a = { kind: "body", id: o, x: i, y: t, aim: 0, anim: 0, hp: 0, flags: 0, data: e };
73
+ return this.bodies.set(o, a), this.hash?.set(o, i, t), a;
74
+ }
75
+ /** Takes a body out: players who saw it get it in `rm` with their next snapshot. */
76
+ removeBody(e) {
77
+ this.bodies.delete(e) && (this.hash?.delete(e), this.history?.forget(e));
33
78
  }
34
79
  /**
35
80
  * Input steps `seq`, `seq + 1`, … from a player. Steps already received are skipped (a resend), any step with
36
81
  * numbers no client sends refuses the whole message, and steps past a full queue are dropped. Returns false when
37
82
  * something was refused.
38
83
  */
39
- input(e, i, r) {
40
- const t = this.players.get(e);
41
- if (!t) return !1;
42
- if (!Number.isInteger(i) || i < 0 || !Array.isArray(r) || r.length > this.maxQueue)
43
- return t.strikes++, !1;
44
- for (const s of r)
45
- if (!Array.isArray(s) || s.length !== 3 || !s.every((u) => Number.isFinite(u)) || Math.abs(s[0]) > 1 || Math.abs(s[1]) > 1 || !Number.isInteger(s[2]))
46
- return t.strikes++, !1;
47
- const n = t.ack + 1 + t.queue.length, o = i - n;
48
- if (o > this.maxQueue)
49
- return t.strikes++, !1;
50
- for (let s = 0; s < o; s++) t.queue.push([0, 0, 0]);
51
- let h = !0;
52
- for (let s = 0; s < r.length; s++)
53
- if (!(i + s < n)) {
54
- if (t.queue.length >= this.maxQueue) {
55
- h = !1, t.strikes++;
84
+ input(e, i, t) {
85
+ const s = this.players.get(e);
86
+ if (!s) return !1;
87
+ if (!Number.isInteger(i) || i < 0 || !Array.isArray(t) || t.length > this.maxQueue)
88
+ return s.strikes++, !1;
89
+ for (const r of t)
90
+ if (!Array.isArray(r) || r.length !== 3 || !r.every((h) => Number.isFinite(h)) || Math.abs(r[0]) > 1 || Math.abs(r[1]) > 1 || !Number.isInteger(r[2]))
91
+ return s.strikes++, !1;
92
+ const o = s.ack + 1 + s.queue.length, a = i - o;
93
+ if (a > this.maxQueue)
94
+ return s.strikes++, !1;
95
+ for (let r = 0; r < a; r++) s.queue.push([0, 0, 0]);
96
+ let u = !0;
97
+ for (let r = 0; r < t.length; r++)
98
+ if (!(i + r < o)) {
99
+ if (s.queue.length >= this.maxQueue) {
100
+ u = !1, s.strikes++;
56
101
  break;
57
102
  }
58
- t.queue.push([r[s][0], r[s][1], r[s][2]]);
103
+ s.queue.push([t[r][0], t[r][1], t[r][2]]);
59
104
  }
60
- return h;
105
+ return u;
61
106
  }
62
107
  /** One simulation step for everyone. */
63
108
  step() {
64
109
  this.tick++;
65
110
  for (const e of this.players.values()) {
66
- const i = e.queue.length;
67
- if (i === 0) continue;
68
- const r = i > this.backlog ? Math.min(this.maxSteps, i - this.backlog + 1) : 1;
69
- for (let t = 0; t < r; t++) {
70
- const n = e.queue.shift();
71
- p(e.mover, n, e.params, this.options.grid), e.ack++, this.options.onStep?.(e, n);
111
+ const i = e.queue.length, t = i > this.backlog ? Math.min(this.maxSteps, i - this.backlog + 1) : Math.min(1, i);
112
+ for (let s = 0; s < t; s++) {
113
+ const o = e.queue.shift();
114
+ p(e.mover, o, e.params, this.options.grid), e.ack++, this.options.onStep?.(e, o);
72
115
  }
116
+ this.hash?.set(e.id, e.mover.x, e.mover.y);
73
117
  }
118
+ if (this.hash) for (const e of this.bodies.values()) this.hash.set(e.id, e.x, e.y);
119
+ }
120
+ /**
121
+ * Moves a player by hand (a teleport, a respawn), and files it in the interest grid at once. Writing `mover.x/y`
122
+ * directly also works; the room then finds it at its new place from the next step.
123
+ */
124
+ place(e, i, t) {
125
+ const s = this.players.get(e), o = this.bodies.get(e);
126
+ if (s)
127
+ s.mover.x = i, s.mover.y = t;
128
+ else if (o)
129
+ o.x = i, o.y = t;
130
+ else return;
131
+ this.hash?.set(e, i, t);
74
132
  }
75
133
  /** The snapshot player `id` should get now. `time` is the server's wall clock (ms). */
76
134
  snapshotFor(e, i) {
77
- const r = this.players.get(e), t = this.known.get(e);
78
- if (!r || !t) return null;
79
- const n = [], o = [], h = /* @__PURE__ */ new Set();
80
- for (const a of this.players.values())
81
- h.add(a.id), n.push([a.id, a.mover.x, a.mover.y, a.aim, a.anim, a.hp, a.flags]), t.has(a.id) || (t.add(a.id), o.push({ id: a.id, data: a.data }));
82
- const s = [];
83
- for (const a of t)
84
- h.has(a) || (t.delete(a), s.push(a));
85
- const u = { t: "snap", tick: this.tick, time: i, ack: r.ack, me: c(r.mover), up: n };
86
- return o.length && (u.sp = o), s.length && (u.rm = s), u;
135
+ const t = this.players.get(e), s = this.known.get(e);
136
+ if (!t || !s) return null;
137
+ const o = [], a = [], u = /* @__PURE__ */ new Set();
138
+ for (const n of this.visibleTo(t, s)) {
139
+ u.add(n.id);
140
+ const c = d(n);
141
+ o.push([n.id, c.x, c.y, n.aim, n.anim, n.hp, n.flags]), s.has(n.id) || (s.add(n.id), a.push({ id: n.id, data: n.data }));
142
+ }
143
+ const r = [];
144
+ for (const n of s)
145
+ u.has(n) || (s.delete(n), r.push(n));
146
+ const h = { t: "snap", tick: this.tick, time: i, ack: t.ack, me: y(t.mover), up: o };
147
+ return a.length && (h.sp = a), r.length && (h.rm = r), h;
148
+ }
149
+ /** What `me` is sent: itself always; with interest, the rest in range (a known one until past the margin). */
150
+ visibleTo(e, i) {
151
+ const t = this.options.interest;
152
+ if (!t || !this.hash) return [...this.players.values(), ...this.bodies.values()];
153
+ const s = t.radius, o = s + (t.leaveMargin ?? s / 4), a = this.nearby;
154
+ a.length = 0, this.hash.near(e.mover.x, e.mover.y, o, a);
155
+ const u = [e];
156
+ for (const r of a) {
157
+ if (r === e.id) continue;
158
+ const h = this.players.get(r) ?? this.bodies.get(r);
159
+ if (!h) continue;
160
+ const n = d(h);
161
+ (n.x - e.mover.x) ** 2 + (n.y - e.mover.y) ** 2 > s * s && !i.has(r) || t.canSee && !t.canSee(e, h) || u.push(h);
162
+ }
163
+ return u.sort((r, h) => r.id - h.id);
87
164
  }
88
165
  }
166
+ const d = (l) => l.kind === "player" ? l.mover : l;
89
167
  export {
90
- g as Room
168
+ g as BODY_ID_BASE,
169
+ M as Room
91
170
  };
@@ -1,5 +1,6 @@
1
- import { encodeSnapshotFrame as n } from "./snapshotCodec.js";
2
- class p {
1
+ import { LagCompensator as n } from "./LagCompensator.js";
2
+ import { encodeSnapshotFrame as r } from "./snapshotCodec.js";
3
+ class c {
3
4
  constructor(t, s = {}) {
4
5
  this.room = t, this.options = s, this.stepMs = 1e3 / (s.stepRate ?? 60), this.snapEvery = Math.max(1, Math.round((s.stepRate ?? 60) / (s.snapRate ?? 20))), this.now = s.now ?? Date.now;
5
6
  }
@@ -64,7 +65,7 @@ class p {
64
65
  /** Sends one of the game's own messages to a player. */
65
66
  sendTo(t, s) {
66
67
  const e = this.sockets.get(t);
67
- e && (s.t === "snap" && s.up?.length ? e.send(n(s)) : e.send(JSON.stringify(s)));
68
+ e && (s.t === "snap" && s.up?.length ? e.send(r(s)) : e.send(JSON.stringify(s)));
68
69
  }
69
70
  /** Starts stepping on a timer. Steps are counted by the clock, so a late timer runs the steps it owes. */
70
71
  start() {
@@ -81,11 +82,19 @@ class p {
81
82
  stepOnce() {
82
83
  if (this.room.step(), this.room.tick % this.snapEvery !== 0) return;
83
84
  const t = this.now();
85
+ this.room.record(t);
84
86
  for (const s of this.joined) {
85
87
  const e = this.room.snapshotFor(s, t);
86
88
  e && this.sendTo(s, e);
87
89
  }
88
90
  }
91
+ /**
92
+ * The server time a client's claimed view time (`InterpolationBuffer.viewTime`, sent with a shot) is judged at:
93
+ * kept within the room's `rewindMs` of now, so no claim reaches further back. `now` with no history kept.
94
+ */
95
+ rewindTime(t) {
96
+ return n.clampTime(t, this.now(), this.room.rewindMs);
97
+ }
89
98
  strike(t) {
90
99
  const s = this.room.players.get(t);
91
100
  s && (s.strikes++, this.checkStrikes(t));
@@ -96,5 +105,5 @@ class p {
96
105
  }
97
106
  }
98
107
  export {
99
- p as RoomHost
108
+ c as RoomHost
100
109
  };
@@ -0,0 +1,66 @@
1
+ const z = (a, t) => (a + 32768) * 65536 + (t + 32768);
2
+ class M {
3
+ /** `cellSize`: world px per cell. About the radius queries ask for keeps a query to a 3×3 block. */
4
+ constructor(t) {
5
+ if (this.cellSize = t, !(t > 0)) throw new Error(`SpatialHash: cellSize must be above 0 (got ${t})`);
6
+ }
7
+ cellSize;
8
+ cells = /* @__PURE__ */ new Map();
9
+ entries = /* @__PURE__ */ new Map();
10
+ get size() {
11
+ return this.entries.size;
12
+ }
13
+ has(t) {
14
+ return this.entries.has(t);
15
+ }
16
+ /** Puts `id` at (x, y), or moves it there. */
17
+ set(t, s, e) {
18
+ const l = z(Math.floor(s / this.cellSize), Math.floor(e / this.cellSize)), i = this.entries.get(t);
19
+ if (i) {
20
+ if (i.x = s, i.y = e, i.cell === l) return;
21
+ this.unlink(t, i.cell), i.cell = l;
22
+ } else this.entries.set(t, { x: s, y: e, cell: l });
23
+ let n = this.cells.get(l);
24
+ n || this.cells.set(l, n = /* @__PURE__ */ new Set()), n.add(t);
25
+ }
26
+ delete(t) {
27
+ const s = this.entries.get(t);
28
+ s && (this.unlink(t, s.cell), this.entries.delete(t));
29
+ }
30
+ clear() {
31
+ this.cells.clear(), this.entries.clear();
32
+ }
33
+ /** The ids within `radius` of (x, y), edge included, pushed onto `out` in no set order. */
34
+ near(t, s, e, l = []) {
35
+ const i = e * e;
36
+ return this.visit(t - e, s - e, t + e, s + e, (n, c) => {
37
+ const r = c.x - t, o = c.y - s;
38
+ r * r + o * o <= i && l.push(n);
39
+ }), l;
40
+ }
41
+ /** The ids inside the box from (x0, y0) to (x1, y1), edges included. */
42
+ inRect(t, s, e, l, i = []) {
43
+ return this.visit(t, s, e, l, (n, c) => {
44
+ c.x >= t && c.x <= e && c.y >= s && c.y <= l && i.push(n);
45
+ }), i;
46
+ }
47
+ visit(t, s, e, l, i) {
48
+ const n = this.cellSize, c = Math.floor(t / n), r = Math.floor(s / n), o = Math.floor(e / n), S = Math.floor(l / n);
49
+ if ((o - c + 1) * (S - r + 1) > this.entries.size) {
50
+ for (const [h, f] of this.entries) i(h, f);
51
+ return;
52
+ }
53
+ for (let h = r; h <= S; h++)
54
+ for (let f = c; f <= o; f++) {
55
+ const g = this.cells.get(z(f, h));
56
+ if (g) for (const u of g) i(u, this.entries.get(u));
57
+ }
58
+ }
59
+ unlink(t, s) {
60
+ const e = this.cells.get(s);
61
+ e && (e.delete(t), e.size === 0 && this.cells.delete(s));
62
+ }
63
+ }
64
+ export {
65
+ M as SpatialHash
66
+ };
@@ -0,0 +1,133 @@
1
+ /** What a node answers each tick: done well, done badly, or not done yet. */
2
+ export type BtStatus = 'success' | 'failure' | 'running';
3
+ /**
4
+ * One node of a tree. Nodes hold no state of their own, so one tree drives any number of agents: what a node must
5
+ * remember between ticks (the child a sequence is on, when a wait began) lives in the agent's memory, `run.state`.
6
+ */
7
+ export interface BtNode<C> {
8
+ readonly kind: string;
9
+ /** Shown in a trace; actions and conditions are named after what they do. */
10
+ readonly name?: string;
11
+ readonly children: readonly BtNode<C>[];
12
+ tick(ctx: C, run: BtRun<C>): BtStatus;
13
+ /** A running node left behind (its parent moved on, the tree was reset): undo what it started. */
14
+ halt?(ctx: C, run: BtRun<C>): void;
15
+ }
16
+ /** What a node may use while it ticks: the agent's clock and memory, and its children's ticks. */
17
+ export interface BtRun<C> {
18
+ /** The time this tick was given (ms), for waits, cooldowns and timeouts. */
19
+ readonly now: number;
20
+ /** This node's memory for this agent, made by `init` the first time and after a halt. */
21
+ state<T>(node: BtNode<C>, init: () => T): T;
22
+ /** Memory that outlives the node finishing (a cooldown's ready time); only `reset` clears it. */
23
+ lasting<T>(node: BtNode<C>, init: () => T): T;
24
+ /** Ticks a child; use this, never `child.tick`, so it is traced and halted right. */
25
+ tick(child: BtNode<C>, ctx: C): BtStatus;
26
+ /** Stops a child that was left running, and everything under it. */
27
+ halt(child: BtNode<C>, ctx: C): void;
28
+ }
29
+ /** One line of `BtAgent.trace`: a node, how deep it is, and what it last answered (null: not ticked this time). */
30
+ export interface BtTraceLine {
31
+ depth: number;
32
+ kind: string;
33
+ name: string | undefined;
34
+ status: BtStatus | null;
35
+ }
36
+ /** A tree's root, and the agents it drives. */
37
+ export declare class BehaviorTree<C> {
38
+ readonly root: BtNode<C>;
39
+ constructor(root: BtNode<C>);
40
+ /** A new agent on this tree, with memory of its own. */
41
+ agent(): BtAgent<C>;
42
+ }
43
+ /** One agent running a tree: its memory, and the tick that drives it. */
44
+ export declare class BtAgent<C> implements BtRun<C> {
45
+ readonly root: BtNode<C>;
46
+ now: number;
47
+ private memory;
48
+ private readonly kept;
49
+ /** Nodes that answered 'running' last time they ticked: the ones a halt must reach. */
50
+ private readonly open;
51
+ private last;
52
+ private seen;
53
+ constructor(root: BtNode<C>);
54
+ /** Ticks the tree once at time `now` (ms) and returns the root's answer. */
55
+ update(ctx: C, now: number): BtStatus;
56
+ /** Halts whatever is running and forgets all memory: the agent starts the tree afresh (it died, it was stunned). */
57
+ reset(ctx: C): void;
58
+ /** Whether a node is running for this agent now. */
59
+ isRunning(node: BtNode<C>): boolean;
60
+ state<T>(node: BtNode<C>, init: () => T): T;
61
+ lasting<T>(node: BtNode<C>, init: () => T): T;
62
+ tick(child: BtNode<C>, ctx: C): BtStatus;
63
+ halt(child: BtNode<C>, ctx: C): void;
64
+ /** The tree as it ran on the last tick, depth first: for a debug overlay. */
65
+ trace(): BtTraceLine[];
66
+ }
67
+ /**
68
+ * A trace as text, one node a line, indented by depth, with what each answered (`·` when it was not ticked): for a
69
+ * debug overlay's `Text`, or a log.
70
+ *
71
+ * ```
72
+ * reactive-selector running
73
+ * sequence failure
74
+ * hurt failure
75
+ * flee ·
76
+ * wander running
77
+ * ```
78
+ */
79
+ export declare function formatTrace(trace: readonly BtTraceLine[]): string;
80
+ /**
81
+ * Runs its children in order until one fails: all must succeed. A running child is resumed on the next tick
82
+ * without asking the ones before it again (`reactiveSequence` does ask them).
83
+ */
84
+ export declare function sequence<C>(...children: BtNode<C>[]): BtNode<C>;
85
+ /** Tries its children in order until one does not fail: the first that works. Resumes a running child. */
86
+ export declare function selector<C>(...children: BtNode<C>[]): BtNode<C>;
87
+ /**
88
+ * A sequence that asks every child from the first again on each tick: a guard in front ("still sees the player?")
89
+ * is checked every time, and when it fails the child that was running is halted.
90
+ */
91
+ export declare function reactiveSequence<C>(...children: BtNode<C>[]): BtNode<C>;
92
+ /**
93
+ * A selector that asks from the first child again on each tick: a higher-priority branch ("flee when hurt") takes
94
+ * over the moment it can run, halting the lower one it interrupts.
95
+ */
96
+ export declare function reactiveSelector<C>(...children: BtNode<C>[]): BtNode<C>;
97
+ export interface ParallelPolicy {
98
+ /** Children that must succeed for the whole to (default all). */
99
+ succeed?: number;
100
+ /** Children that must fail for the whole to (default 1). */
101
+ fail?: number;
102
+ }
103
+ /**
104
+ * Ticks every child each tick (walk and look about at once). Succeeds once `succeed` have succeeded, fails once
105
+ * `fail` have failed, halting the rest; a child that finished is not ticked again until the whole finishes.
106
+ */
107
+ export declare function parallel<C>(policy: ParallelPolicy, ...children: BtNode<C>[]): BtNode<C>;
108
+ /** Swaps success and failure. */
109
+ export declare function invert<C>(child: BtNode<C>): BtNode<C>;
110
+ /** Succeeds whatever its child answers, once the child is done: an optional step. */
111
+ export declare function succeed<C>(child: BtNode<C>): BtNode<C>;
112
+ /**
113
+ * Runs its child `times` times in a row (Infinity: for good), stopping at the first failure. One pass per tick at
114
+ * most, so a child that succeeds at once cannot spin a frame.
115
+ */
116
+ export declare function repeat<C>(times: number, child: BtNode<C>): BtNode<C>;
117
+ /** Tries its child up to `times` times until it succeeds, a try a tick. */
118
+ export declare function retry<C>(times: number, child: BtNode<C>): BtNode<C>;
119
+ /** Runs its child at most once every `ms`: after it finishes, it fails at once until the time is up. */
120
+ export declare function cooldown<C>(ms: number, child: BtNode<C>): BtNode<C>;
121
+ /** Fails, halting its child, when the child has run longer than `ms`. */
122
+ export declare function timeout<C>(ms: number, child: BtNode<C>): BtNode<C>;
123
+ /** Runs for `ms`, then succeeds. */
124
+ export declare function wait<C>(ms: number): BtNode<C>;
125
+ /** A test: success when `test` holds, else failure. Never runs. */
126
+ export declare function condition<C>(name: string, test: (ctx: C) => boolean): BtNode<C>;
127
+ export interface ActionOptions<C> {
128
+ /** The action was running and the tree left it (a higher branch took over): stop what it started. */
129
+ onHalt?: (ctx: C) => void;
130
+ }
131
+ /** Something the agent does: `run` answers each tick until the doing is done. */
132
+ export declare function action<C>(name: string, run: (ctx: C, now: number) => BtStatus, options?: ActionOptions<C>): BtNode<C>;
133
+ //# sourceMappingURL=behaviorTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"behaviorTree.d.ts","sourceRoot":"","sources":["../../../src/ai/behaviorTree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import { type ActionOptions, type BtNode, type BtStatus } from './behaviorTree.js';
2
+ /**
3
+ * A tree written as data (a JSON file a designer edits): composites hold `children`, decorators one `child`, and
4
+ * leaves name an action or a condition the game registered.
5
+ *
6
+ * ```json
7
+ * { "type": "reactiveSelector", "children": [
8
+ * { "type": "sequence", "children": [{ "type": "condition", "name": "hurt" }, { "type": "action", "name": "flee" }] },
9
+ * { "type": "cooldown", "ms": 2000, "child": { "type": "action", "name": "bark" } },
10
+ * { "type": "action", "name": "wander" }
11
+ * ] }
12
+ * ```
13
+ */
14
+ export type BtSpec = {
15
+ type: 'sequence' | 'selector' | 'reactiveSequence' | 'reactiveSelector';
16
+ children: BtSpec[];
17
+ } | {
18
+ type: 'parallel';
19
+ succeed?: number;
20
+ fail?: number;
21
+ children: BtSpec[];
22
+ } | {
23
+ type: 'invert' | 'succeed';
24
+ child: BtSpec;
25
+ } | {
26
+ type: 'repeat' | 'retry';
27
+ times: number;
28
+ child: BtSpec;
29
+ } | {
30
+ type: 'cooldown' | 'timeout';
31
+ ms: number;
32
+ child: BtSpec;
33
+ } | {
34
+ type: 'wait';
35
+ ms: number;
36
+ } | {
37
+ type: 'action' | 'condition';
38
+ name: string;
39
+ }
40
+ /** A named subtree of the registry, so a tree is shared by several (a "flee" every monster uses). */
41
+ | {
42
+ type: 'tree';
43
+ name: string;
44
+ };
45
+ /** What the leaves of a data tree name. */
46
+ export interface BtRegistry<C> {
47
+ actions?: Record<string, ((ctx: C, now: number) => BtStatus) | ({
48
+ run: (ctx: C, now: number) => BtStatus;
49
+ } & ActionOptions<C>)>;
50
+ conditions?: Record<string, (ctx: C) => boolean>;
51
+ trees?: Record<string, BtSpec>;
52
+ }
53
+ /** A tree from its data. Throws, naming the path, on an unknown type, a missing leaf or a subtree that names itself. */
54
+ export declare function buildTree<C>(spec: BtSpec, registry: BtRegistry<C>): BtNode<C>;
55
+ //# sourceMappingURL=buildTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildTree.d.ts","sourceRoot":"","sources":["../../../src/ai/buildTree.ts"],"names":[],"mappings":"AAAA,OAAO,EAeL,KAAK,aAAa,EAClB,KAAK,MAAM,EACX,KAAK,QAAQ,EACd,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,MAAM,GACd;IAAE,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAC/F;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GACzE;IAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAChD,qGAAqG;GACnG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC,2CAA2C;AAC3C,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,QAAQ,CAAC,IAAG;QAAE,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,QAAQ,CAAA;KAAE,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA,CAAC,CAAC;IAC9H,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,wHAAwH;AACxH,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAE7E"}
@@ -0,0 +1,107 @@
1
+ /** A point in the world, px. */
2
+ export interface Point {
3
+ readonly x: number;
4
+ readonly y: number;
5
+ }
6
+ export interface SpatialMixOptions {
7
+ /** Beyond this (px) the source is not heard (default 480). */
8
+ range?: number;
9
+ /** How the loudness falls off: 1 straight, higher drops faster near the edge of `range` (default 1.5). */
10
+ rolloff?: number;
11
+ /** Px off to one side at which the sound is panned fully that way (default `range / 2`). */
12
+ panWidth?: number;
13
+ /** The furthest it pans, 0–1: 1 is one ear only, which sounds wrong on headphones (default 0.8). */
14
+ maxPan?: number;
15
+ /** Low-pass cutoff (Hz) beside the listener and at the edge of `range`, so far sounds are dull; null for none. */
16
+ muffle?: readonly [number, number] | null;
17
+ }
18
+ /** How a source sounds from where the listener stands. */
19
+ export interface SpatialMix {
20
+ /** Within `range`. */
21
+ audible: boolean;
22
+ /** 0–1. */
23
+ gain: number;
24
+ /** −1 left … 1 right. */
25
+ pan: number;
26
+ /** The low-pass cutoff, Hz (the top of hearing when `muffle` is null). */
27
+ cutoffHz: number;
28
+ }
29
+ /** The mix of a source at `source` heard from `listener`: louder near, panned to its side, duller far. */
30
+ export declare function spatialMix(listener: Point, source: Point, options?: SpatialMixOptions): SpatialMix;
31
+ export interface EmitterOptions extends SpatialMixOptions {
32
+ /** 0–1 (default 1). */
33
+ volume?: number;
34
+ /** Plays on for good (default true); a one-shot ends by itself. */
35
+ loop?: boolean;
36
+ playbackRate?: number;
37
+ }
38
+ /** A sound placed in the world by `SpatialAudio`. */
39
+ export interface SpatialEmitter {
40
+ x: number;
41
+ y: number;
42
+ volume: number;
43
+ /** Playing now: false while out of range (a loop waits silent), and for good once stopped or ended. */
44
+ readonly playing: boolean;
45
+ readonly done: boolean;
46
+ stop(fadeMs?: number): void;
47
+ }
48
+ /**
49
+ * Sounds placed in the world, heard from a listener that moves: a waterfall, a campfire, a machine humming, or a
50
+ * one-shot at a spot. Each emitter is panned to its side, fades with distance and loses its highs far away
51
+ * (`spatialMix`), eased each `update` so a moving source does not click. A loop out of range stops its source to
52
+ * save the voice and starts again at the point it would have reached, so it seems to have played on.
53
+ *
54
+ * ```ts
55
+ * const world = new SpatialAudio(audioContext);
56
+ * const falls = world.emitter(fallsBuffer, 320, 96, { range: 400, volume: 0.6 });
57
+ * // each frame: world.update(hero);
58
+ * ```
59
+ */
60
+ export declare class SpatialAudio {
61
+ readonly context: BaseAudioContext;
62
+ /** Every emitter's gain goes through this: the game's volume setting. */
63
+ readonly output: GainNode;
64
+ private readonly emitters;
65
+ private here;
66
+ constructor(context: BaseAudioContext, destination?: AudioNode);
67
+ /** The listener's place as of the last `update`. */
68
+ get listener(): Point;
69
+ /** How many emitters are alive (playing or waiting out of range). */
70
+ get size(): number;
71
+ /** A sound at (x, y), looping by default. Starts at once when in range of the listener. */
72
+ emitter(buffer: AudioBuffer, x: number, y: number, options?: EmitterOptions): SpatialEmitter;
73
+ /** A one-shot at (x, y), dropped when it ends; skipped (and done at once) when out of range. */
74
+ play(buffer: AudioBuffer, x: number, y: number, options?: Omit<EmitterOptions, 'loop'>): SpatialEmitter;
75
+ /** Moves the listener and re-mixes every emitter. Call once a frame. */
76
+ update(listener?: Point): void;
77
+ /** Stops every emitter. */
78
+ stopAll(fadeMs?: number): void;
79
+ destroy(): void;
80
+ /** @internal */
81
+ forget(e: Emitter): void;
82
+ }
83
+ declare class Emitter implements SpatialEmitter {
84
+ private readonly owner;
85
+ private readonly buffer;
86
+ x: number;
87
+ y: number;
88
+ private readonly options;
89
+ volume: number;
90
+ done: boolean;
91
+ private source;
92
+ private readonly gain;
93
+ private readonly panner;
94
+ private readonly filter;
95
+ /** When the loop would have begun, had it never stopped: where a restart picks it up. */
96
+ private readonly origin;
97
+ private readonly rate;
98
+ constructor(owner: SpatialAudio, buffer: AudioBuffer, x: number, y: number, options: EmitterOptions);
99
+ get playing(): boolean;
100
+ refresh(listener: Point, smooth: boolean): void;
101
+ stop(fadeMs?: number): void;
102
+ private begin;
103
+ private halt;
104
+ private release;
105
+ }
106
+ export {};
107
+ //# sourceMappingURL=spatial.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spatial.d.ts","sourceRoot":"","sources":["../../../src/audio/spatial.ts"],"names":[],"mappings":"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"}