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
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
class k {
|
|
2
|
+
constructor(t) {
|
|
3
|
+
this.root = t;
|
|
4
|
+
}
|
|
5
|
+
root;
|
|
6
|
+
/** A new agent on this tree, with memory of its own. */
|
|
7
|
+
agent() {
|
|
8
|
+
return new p(this.root);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
class p {
|
|
12
|
+
constructor(t) {
|
|
13
|
+
this.root = t;
|
|
14
|
+
}
|
|
15
|
+
root;
|
|
16
|
+
now = 0;
|
|
17
|
+
memory = /* @__PURE__ */ new Map();
|
|
18
|
+
kept = /* @__PURE__ */ new Map();
|
|
19
|
+
/** Nodes that answered 'running' last time they ticked: the ones a halt must reach. */
|
|
20
|
+
open = /* @__PURE__ */ new Set();
|
|
21
|
+
last = /* @__PURE__ */ new Map();
|
|
22
|
+
seen = /* @__PURE__ */ new Map();
|
|
23
|
+
/** Ticks the tree once at time `now` (ms) and returns the root's answer. */
|
|
24
|
+
update(t, e) {
|
|
25
|
+
this.now = e, this.seen = /* @__PURE__ */ new Map();
|
|
26
|
+
const n = this.tick(this.root, t);
|
|
27
|
+
return this.last = this.seen, n;
|
|
28
|
+
}
|
|
29
|
+
/** Halts whatever is running and forgets all memory: the agent starts the tree afresh (it died, it was stunned). */
|
|
30
|
+
reset(t) {
|
|
31
|
+
this.halt(this.root, t), this.memory.clear(), this.kept.clear(), this.open.clear(), this.last.clear();
|
|
32
|
+
}
|
|
33
|
+
/** Whether a node is running for this agent now. */
|
|
34
|
+
isRunning(t) {
|
|
35
|
+
return this.open.has(t);
|
|
36
|
+
}
|
|
37
|
+
state(t, e) {
|
|
38
|
+
return this.memory.has(t) || this.memory.set(t, e()), this.memory.get(t);
|
|
39
|
+
}
|
|
40
|
+
lasting(t, e) {
|
|
41
|
+
return this.kept.has(t) || this.kept.set(t, e()), this.kept.get(t);
|
|
42
|
+
}
|
|
43
|
+
tick(t, e) {
|
|
44
|
+
const n = t.tick(e, this);
|
|
45
|
+
return this.seen.set(t, n), n === "running" ? this.open.add(t) : (this.open.delete(t), this.memory.delete(t)), n;
|
|
46
|
+
}
|
|
47
|
+
halt(t, e) {
|
|
48
|
+
if (this.open.has(t)) {
|
|
49
|
+
for (const n of t.children) this.halt(n, e);
|
|
50
|
+
t.halt?.(e, this), this.open.delete(t), this.memory.delete(t);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** The tree as it ran on the last tick, depth first: for a debug overlay. */
|
|
54
|
+
trace() {
|
|
55
|
+
const t = [], e = (n, r) => {
|
|
56
|
+
t.push({ depth: r, kind: n.kind, name: n.name, status: this.last.get(n) ?? null });
|
|
57
|
+
for (const o of n.children) e(o, r + 1);
|
|
58
|
+
};
|
|
59
|
+
return e(this.root, 0), t;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function m(s) {
|
|
63
|
+
return s.map((t) => `${" ".repeat(t.depth)}${t.name ?? t.kind} ${t.status ?? "·"}`).join(`
|
|
64
|
+
`);
|
|
65
|
+
}
|
|
66
|
+
const u = (s, t, e, n = {}) => ({
|
|
67
|
+
kind: s,
|
|
68
|
+
children: t,
|
|
69
|
+
tick: e,
|
|
70
|
+
...n
|
|
71
|
+
});
|
|
72
|
+
function w(...s) {
|
|
73
|
+
const t = u("sequence", s, (e, n) => {
|
|
74
|
+
const r = n.state(t, () => ({ i: 0 }));
|
|
75
|
+
for (; r.i < s.length; r.i++) {
|
|
76
|
+
const o = n.tick(s[r.i], e);
|
|
77
|
+
if (o !== "success") return o;
|
|
78
|
+
}
|
|
79
|
+
return "success";
|
|
80
|
+
});
|
|
81
|
+
return t;
|
|
82
|
+
}
|
|
83
|
+
function y(...s) {
|
|
84
|
+
const t = u("selector", s, (e, n) => {
|
|
85
|
+
const r = n.state(t, () => ({ i: 0 }));
|
|
86
|
+
for (; r.i < s.length; r.i++) {
|
|
87
|
+
const o = n.tick(s[r.i], e);
|
|
88
|
+
if (o !== "failure") return o;
|
|
89
|
+
}
|
|
90
|
+
return "failure";
|
|
91
|
+
});
|
|
92
|
+
return t;
|
|
93
|
+
}
|
|
94
|
+
function v(...s) {
|
|
95
|
+
return g("reactive-sequence", s, "success");
|
|
96
|
+
}
|
|
97
|
+
function M(...s) {
|
|
98
|
+
return g("reactive-selector", s, "failure");
|
|
99
|
+
}
|
|
100
|
+
function g(s, t, e) {
|
|
101
|
+
return u(s, t, (n, r) => {
|
|
102
|
+
for (let o = 0; o < t.length; o++) {
|
|
103
|
+
const i = r.tick(t[o], n);
|
|
104
|
+
if (i !== e) {
|
|
105
|
+
for (let c = o + 1; c < t.length; c++) r.halt(t[c], n);
|
|
106
|
+
return i;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return e;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function b(s, ...t) {
|
|
113
|
+
const e = Math.min(t.length, s.succeed ?? t.length), n = Math.min(t.length, s.fail ?? 1), r = u("parallel", t, (o, i) => {
|
|
114
|
+
const c = i.state(r, () => /* @__PURE__ */ new Map());
|
|
115
|
+
for (let a = 0; a < t.length; a++) {
|
|
116
|
+
if (c.has(a)) continue;
|
|
117
|
+
const h = i.tick(t[a], o);
|
|
118
|
+
h !== "running" && c.set(a, h);
|
|
119
|
+
}
|
|
120
|
+
let f = 0, l = 0;
|
|
121
|
+
for (const a of c.values()) a === "success" ? f++ : l++;
|
|
122
|
+
if (f < e && l < n) return "running";
|
|
123
|
+
for (const a of t) i.halt(a, o);
|
|
124
|
+
return f >= e ? "success" : "failure";
|
|
125
|
+
});
|
|
126
|
+
return r;
|
|
127
|
+
}
|
|
128
|
+
function q(s) {
|
|
129
|
+
return u("invert", [s], (t, e) => {
|
|
130
|
+
const n = e.tick(s, t);
|
|
131
|
+
return n === "success" ? "failure" : n === "failure" ? "success" : "running";
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function A(s) {
|
|
135
|
+
return u("succeed", [s], (t, e) => e.tick(s, t) === "running" ? "running" : "success");
|
|
136
|
+
}
|
|
137
|
+
function S(s, t) {
|
|
138
|
+
const e = u("repeat", [t], (n, r) => {
|
|
139
|
+
const o = r.state(e, () => ({ n: 0 })), i = r.tick(t, n);
|
|
140
|
+
return i !== "success" ? i : (o.n++, o.n >= s ? "success" : "running");
|
|
141
|
+
});
|
|
142
|
+
return e;
|
|
143
|
+
}
|
|
144
|
+
function $(s, t) {
|
|
145
|
+
const e = u("retry", [t], (n, r) => {
|
|
146
|
+
const o = r.state(e, () => ({ n: 0 })), i = r.tick(t, n);
|
|
147
|
+
return i !== "failure" ? i : (o.n++, o.n >= s ? "failure" : "running");
|
|
148
|
+
});
|
|
149
|
+
return e;
|
|
150
|
+
}
|
|
151
|
+
function j(s, t) {
|
|
152
|
+
const e = u("cooldown", [t], (n, r) => {
|
|
153
|
+
const o = r.lasting(e, () => ({ readyAt: -1 / 0 }));
|
|
154
|
+
if (r.now < o.readyAt) return "failure";
|
|
155
|
+
const i = r.tick(t, n);
|
|
156
|
+
return i !== "running" && (o.readyAt = r.now + s), i;
|
|
157
|
+
});
|
|
158
|
+
return e;
|
|
159
|
+
}
|
|
160
|
+
function B(s, t) {
|
|
161
|
+
const e = u("timeout", [t], (n, r) => {
|
|
162
|
+
const o = r.state(e, () => ({ began: r.now }));
|
|
163
|
+
return r.now - o.began >= s ? (r.halt(t, n), "failure") : r.tick(t, n);
|
|
164
|
+
});
|
|
165
|
+
return e;
|
|
166
|
+
}
|
|
167
|
+
function H(s) {
|
|
168
|
+
const t = u("wait", [], (e, n) => {
|
|
169
|
+
const r = n.state(t, () => ({ began: n.now }));
|
|
170
|
+
return n.now - r.began >= s ? "success" : "running";
|
|
171
|
+
});
|
|
172
|
+
return t;
|
|
173
|
+
}
|
|
174
|
+
function T(s, t) {
|
|
175
|
+
return u("condition", [], (e) => t(e) ? "success" : "failure", { name: s });
|
|
176
|
+
}
|
|
177
|
+
function F(s, t, e = {}) {
|
|
178
|
+
return u("action", [], (n, r) => t(n, r.now), { name: s, halt: e.onHalt ? (n) => e.onHalt(n) : void 0 });
|
|
179
|
+
}
|
|
180
|
+
export {
|
|
181
|
+
k as BehaviorTree,
|
|
182
|
+
p as BtAgent,
|
|
183
|
+
F as action,
|
|
184
|
+
T as condition,
|
|
185
|
+
j as cooldown,
|
|
186
|
+
m as formatTrace,
|
|
187
|
+
q as invert,
|
|
188
|
+
b as parallel,
|
|
189
|
+
M as reactiveSelector,
|
|
190
|
+
v as reactiveSequence,
|
|
191
|
+
S as repeat,
|
|
192
|
+
$ as retry,
|
|
193
|
+
y as selector,
|
|
194
|
+
w as sequence,
|
|
195
|
+
A as succeed,
|
|
196
|
+
B as timeout,
|
|
197
|
+
H as wait
|
|
198
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { action as m, condition as s, wait as h, timeout as $, cooldown as y, retry as b, repeat as w, succeed as q, invert as v, parallel as S, reactiveSelector as k, reactiveSequence as A, selector as H, sequence as T } from "./behaviorTree.js";
|
|
2
|
+
function x(e, t) {
|
|
3
|
+
return l(e, t, "$", []);
|
|
4
|
+
}
|
|
5
|
+
function l(e, t, u, c) {
|
|
6
|
+
const r = (n) => {
|
|
7
|
+
throw new Error(`buildTree: ${u}: ${n}`);
|
|
8
|
+
};
|
|
9
|
+
if (!e || typeof e != "object") return r("not a node");
|
|
10
|
+
const a = (n) => !Array.isArray(n) || n.length === 0 ? r("needs children") : n.map((d, f) => l(d, t, `${u}.children[${f}]`, c)), i = (n) => l(n, t, `${u}.child`, c), o = (n, d) => typeof n == "number" && n >= 0 ? n : r(`${d} must be a number of at least 0`);
|
|
11
|
+
switch (e.type) {
|
|
12
|
+
case "sequence":
|
|
13
|
+
return T(...a(e.children));
|
|
14
|
+
case "selector":
|
|
15
|
+
return H(...a(e.children));
|
|
16
|
+
case "reactiveSequence":
|
|
17
|
+
return A(...a(e.children));
|
|
18
|
+
case "reactiveSelector":
|
|
19
|
+
return k(...a(e.children));
|
|
20
|
+
case "parallel":
|
|
21
|
+
return S({ succeed: e.succeed, fail: e.fail }, ...a(e.children));
|
|
22
|
+
case "invert":
|
|
23
|
+
return v(i(e.child));
|
|
24
|
+
case "succeed":
|
|
25
|
+
return q(i(e.child));
|
|
26
|
+
case "repeat":
|
|
27
|
+
return w(o(e.times, "times"), i(e.child));
|
|
28
|
+
case "retry":
|
|
29
|
+
return b(o(e.times, "times"), i(e.child));
|
|
30
|
+
case "cooldown":
|
|
31
|
+
return y(o(e.ms, "ms"), i(e.child));
|
|
32
|
+
case "timeout":
|
|
33
|
+
return $(o(e.ms, "ms"), i(e.child));
|
|
34
|
+
case "wait":
|
|
35
|
+
return h(o(e.ms, "ms"));
|
|
36
|
+
case "condition": {
|
|
37
|
+
const n = t.conditions?.[e.name];
|
|
38
|
+
return n ? s(e.name, n) : r(`no condition '${e.name}'`);
|
|
39
|
+
}
|
|
40
|
+
case "action": {
|
|
41
|
+
const n = t.actions?.[e.name];
|
|
42
|
+
return n ? typeof n == "function" ? m(e.name, n) : m(e.name, n.run, { onHalt: n.onHalt }) : r(`no action '${e.name}'`);
|
|
43
|
+
}
|
|
44
|
+
case "tree": {
|
|
45
|
+
const n = t.trees?.[e.name];
|
|
46
|
+
return n ? c.includes(e.name) ? r(`tree '${e.name}' contains itself`) : l(n, t, `${u}<${e.name}>`, [...c, e.name]) : r(`no tree '${e.name}'`);
|
|
47
|
+
}
|
|
48
|
+
default:
|
|
49
|
+
return r(`unknown type '${e.type}'`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
x as buildTree
|
|
54
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const l = [18e3, 1800];
|
|
2
|
+
function f(c, t, e = {}) {
|
|
3
|
+
const i = e.range ?? 480, s = t.x - c.x, n = Math.hypot(s, t.y - c.y), r = e.maxPan ?? 0.8, h = Math.max(-r, Math.min(r, s / (e.panWidth ?? i / 2))), o = e.muffle === void 0 ? l : e.muffle, a = Math.min(1, n / i), u = o ? o[0] * Math.pow(o[1] / o[0], a) : 2e4;
|
|
4
|
+
return n >= i ? { audible: !1, gain: 0, pan: h, cutoffHz: u } : { audible: !0, gain: Math.pow(1 - a, e.rolloff ?? 1.5), pan: h, cutoffHz: u };
|
|
5
|
+
}
|
|
6
|
+
const m = 0.05;
|
|
7
|
+
class d {
|
|
8
|
+
constructor(t, e = t.destination) {
|
|
9
|
+
this.context = t, this.output = t.createGain(), this.output.connect(e);
|
|
10
|
+
}
|
|
11
|
+
context;
|
|
12
|
+
/** Every emitter's gain goes through this: the game's volume setting. */
|
|
13
|
+
output;
|
|
14
|
+
emitters = /* @__PURE__ */ new Set();
|
|
15
|
+
here = { x: 0, y: 0 };
|
|
16
|
+
/** The listener's place as of the last `update`. */
|
|
17
|
+
get listener() {
|
|
18
|
+
return this.here;
|
|
19
|
+
}
|
|
20
|
+
/** How many emitters are alive (playing or waiting out of range). */
|
|
21
|
+
get size() {
|
|
22
|
+
return this.emitters.size;
|
|
23
|
+
}
|
|
24
|
+
/** A sound at (x, y), looping by default. Starts at once when in range of the listener. */
|
|
25
|
+
emitter(t, e, i, s = {}) {
|
|
26
|
+
const n = new p(this, t, e, i, s);
|
|
27
|
+
return this.emitters.add(n), n.refresh(this.here, !1), n;
|
|
28
|
+
}
|
|
29
|
+
/** A one-shot at (x, y), dropped when it ends; skipped (and done at once) when out of range. */
|
|
30
|
+
play(t, e, i, s = {}) {
|
|
31
|
+
return this.emitter(t, e, i, { ...s, loop: !1 });
|
|
32
|
+
}
|
|
33
|
+
/** Moves the listener and re-mixes every emitter. Call once a frame. */
|
|
34
|
+
update(t = this.here) {
|
|
35
|
+
this.here = { x: t.x, y: t.y };
|
|
36
|
+
for (const e of this.emitters) e.refresh(this.here, !0);
|
|
37
|
+
}
|
|
38
|
+
/** Stops every emitter. */
|
|
39
|
+
stopAll(t = 0) {
|
|
40
|
+
for (const e of [...this.emitters]) e.stop(t);
|
|
41
|
+
}
|
|
42
|
+
destroy() {
|
|
43
|
+
this.stopAll(), this.output.disconnect();
|
|
44
|
+
}
|
|
45
|
+
/** @internal */
|
|
46
|
+
forget(t) {
|
|
47
|
+
this.emitters.delete(t);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
class p {
|
|
51
|
+
constructor(t, e, i, s, n) {
|
|
52
|
+
this.owner = t, this.buffer = e, this.x = i, this.y = s, this.options = n;
|
|
53
|
+
const r = t.context;
|
|
54
|
+
this.volume = n.volume ?? 1, this.rate = n.playbackRate ?? 1, this.origin = r.currentTime, this.filter = r.createBiquadFilter(), this.filter.type = "lowpass", this.gain = r.createGain(), this.panner = r.createStereoPanner(), this.filter.connect(this.gain).connect(this.panner).connect(t.output);
|
|
55
|
+
}
|
|
56
|
+
owner;
|
|
57
|
+
buffer;
|
|
58
|
+
x;
|
|
59
|
+
y;
|
|
60
|
+
options;
|
|
61
|
+
volume;
|
|
62
|
+
done = !1;
|
|
63
|
+
source = null;
|
|
64
|
+
gain;
|
|
65
|
+
panner;
|
|
66
|
+
filter;
|
|
67
|
+
/** When the loop would have begun, had it never stopped: where a restart picks it up. */
|
|
68
|
+
origin;
|
|
69
|
+
rate;
|
|
70
|
+
get playing() {
|
|
71
|
+
return this.source !== null;
|
|
72
|
+
}
|
|
73
|
+
refresh(t, e) {
|
|
74
|
+
if (this.done) return;
|
|
75
|
+
const i = f(t, this, this.options), s = this.options.loop ?? !0;
|
|
76
|
+
if (!i.audible) {
|
|
77
|
+
if (!s) return this.stop();
|
|
78
|
+
this.halt();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const n = this.owner.context, r = n.currentTime, h = (o, a) => e && this.source ? o.setTargetAtTime(a, r, m) : o.setValueAtTime(a, r);
|
|
82
|
+
h(this.gain.gain, i.gain * Math.max(0, Math.min(1, this.volume))), h(this.panner.pan, i.pan), h(this.filter.frequency, Math.min(i.cutoffHz, n.sampleRate / 2)), this.source || this.begin(s);
|
|
83
|
+
}
|
|
84
|
+
stop(t = 0) {
|
|
85
|
+
if (this.done) return;
|
|
86
|
+
this.done = !0, this.owner.forget(this);
|
|
87
|
+
const e = this.source, i = this.owner.context;
|
|
88
|
+
if (e && t > 0) {
|
|
89
|
+
const s = i.currentTime + t / 1e3;
|
|
90
|
+
this.gain.gain.setValueAtTime(this.gain.gain.value, i.currentTime), this.gain.gain.linearRampToValueAtTime(0, s), e.stop(s), e.onended = () => this.release();
|
|
91
|
+
} else
|
|
92
|
+
this.halt(), this.release();
|
|
93
|
+
}
|
|
94
|
+
begin(t) {
|
|
95
|
+
const e = this.owner.context, i = e.createBufferSource();
|
|
96
|
+
i.buffer = this.buffer, i.loop = t, i.playbackRate.value = this.rate, i.connect(this.filter);
|
|
97
|
+
const s = t ? (e.currentTime - this.origin) * this.rate % this.buffer.duration : 0;
|
|
98
|
+
i.start(e.currentTime, s), t || (i.onended = () => this.stop()), this.source = i;
|
|
99
|
+
}
|
|
100
|
+
halt() {
|
|
101
|
+
const t = this.source;
|
|
102
|
+
if (t) {
|
|
103
|
+
this.source = null, t.onended = null;
|
|
104
|
+
try {
|
|
105
|
+
t.stop();
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
t.disconnect();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
release() {
|
|
112
|
+
this.filter.disconnect(), this.gain.disconnect(), this.panner.disconnect();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export {
|
|
116
|
+
d as SpatialAudio,
|
|
117
|
+
f as spatialMix
|
|
118
|
+
};
|
|
@@ -47,6 +47,7 @@ export declare class WebPatcher {
|
|
|
47
47
|
private loaded;
|
|
48
48
|
/** The tidy a start began: a build waits for it, or it could delete the `.part` folder being filled. */
|
|
49
49
|
private tidying;
|
|
50
|
+
private tidyChain;
|
|
50
51
|
constructor(options: WebPatcherOptions);
|
|
51
52
|
/** This shell's identity. */
|
|
52
53
|
get shell(): string;
|
|
@@ -75,6 +76,8 @@ export declare class WebPatcher {
|
|
|
75
76
|
private promote;
|
|
76
77
|
/** A pointer this shell may load: its own shell, newer than the installed build, its folder whole. */
|
|
77
78
|
private usable;
|
|
78
|
-
/** Removes every patched web but the active, pending and previous one, and any half-made folder.
|
|
79
|
+
/** Removes every patched web but the active, pending and previous one, and any half-made folder. A call made while
|
|
80
|
+
* one is running waits for it and then runs once more, so two never delete the same folder at once. */
|
|
79
81
|
tidy(): Promise<void>;
|
|
82
|
+
private tidyOnce;
|
|
80
83
|
}
|
|
@@ -27,6 +27,7 @@ class WebPatcher {
|
|
|
27
27
|
loaded = null;
|
|
28
28
|
/** The tidy a start began: a build waits for it, or it could delete the `.part` folder being filled. */
|
|
29
29
|
tidying = Promise.resolve();
|
|
30
|
+
tidyChain = Promise.resolve();
|
|
30
31
|
constructor(options) {
|
|
31
32
|
this.options = options;
|
|
32
33
|
this.o = {
|
|
@@ -197,8 +198,14 @@ class WebPatcher {
|
|
|
197
198
|
usable(p) {
|
|
198
199
|
return !!p && p.shell === this.shell && (0, manifest_1.isNewer)(p.version, this.o.appVersion) && (0, node_fs_1.existsSync)((0, node_path_1.join)(this.o.root, p.version, this.o.entry));
|
|
199
200
|
}
|
|
200
|
-
/** Removes every patched web but the active, pending and previous one, and any half-made folder.
|
|
201
|
-
|
|
201
|
+
/** Removes every patched web but the active, pending and previous one, and any half-made folder. A call made while
|
|
202
|
+
* one is running waits for it and then runs once more, so two never delete the same folder at once. */
|
|
203
|
+
tidy() {
|
|
204
|
+
const next = this.tidyChain.then(() => this.tidyOnce());
|
|
205
|
+
this.tidyChain = next;
|
|
206
|
+
return next;
|
|
207
|
+
}
|
|
208
|
+
async tidyOnce() {
|
|
202
209
|
try {
|
|
203
210
|
const names = ['active', 'pending', 'previous'];
|
|
204
211
|
const keep = new Set([...names.map((n) => this.pointers.read(n)?.version), this.loaded?.version].filter((v) => !!v));
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { expr as u } from "./expr.js";
|
|
2
|
+
const f = 1e3;
|
|
3
|
+
class d {
|
|
4
|
+
constructor(t, s = {}) {
|
|
5
|
+
this.script = t, this.options = s, this.vars = s.vars ?? {};
|
|
6
|
+
const i = s.functions ?? {};
|
|
7
|
+
this.scope = {
|
|
8
|
+
get: (e) => l(this.vars, e),
|
|
9
|
+
call: (e, n) => {
|
|
10
|
+
if (e === "visited") return this.visits.get(String(n[0])) ?? 0;
|
|
11
|
+
if (e === "chose") return this.chosen.has(`${n[0]}#${n[1]}`);
|
|
12
|
+
const o = i[e];
|
|
13
|
+
if (!o) throw new Error(`dialogue: no function '${e}'`);
|
|
14
|
+
return o(...n);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
script;
|
|
19
|
+
options;
|
|
20
|
+
vars;
|
|
21
|
+
node = null;
|
|
22
|
+
line = 0;
|
|
23
|
+
step = { type: "end" };
|
|
24
|
+
visits = /* @__PURE__ */ new Map();
|
|
25
|
+
chosen = /* @__PURE__ */ new Set();
|
|
26
|
+
scope;
|
|
27
|
+
/** The step showing now. */
|
|
28
|
+
get current() {
|
|
29
|
+
return this.step;
|
|
30
|
+
}
|
|
31
|
+
/** The node the dialogue is in, or null once it has ended. */
|
|
32
|
+
get at() {
|
|
33
|
+
return this.node;
|
|
34
|
+
}
|
|
35
|
+
/** How many times a node was entered. */
|
|
36
|
+
visited(t) {
|
|
37
|
+
return this.visits.get(t) ?? 0;
|
|
38
|
+
}
|
|
39
|
+
/** Begins at `node` (default the script's `start`, else its first node). */
|
|
40
|
+
start(t = this.script.start ?? Object.keys(this.script.nodes)[0]) {
|
|
41
|
+
return t === void 0 ? this.finish() : this.go(t);
|
|
42
|
+
}
|
|
43
|
+
/** The line showing was read: on to the next line, the choices, or wherever the node leads. */
|
|
44
|
+
next() {
|
|
45
|
+
return this.step.type !== "line" ? this.step : (this.line++, this.show(0));
|
|
46
|
+
}
|
|
47
|
+
/** Picks a choice by its `index` (as shown). A locked or unknown index changes nothing. */
|
|
48
|
+
choose(t) {
|
|
49
|
+
if (this.step.type !== "choices" || this.node === null) return this.step;
|
|
50
|
+
if (!this.step.choices.find((n) => n.index === t)?.enabled) return this.step;
|
|
51
|
+
const i = this.node, e = this.nodeOf(i).choices[t];
|
|
52
|
+
return this.chosen.add(`${i}#${t}`), this.assign(e.set), this.run(e.do), e.goto === void 0 ? this.finish() : this.go(e.goto);
|
|
53
|
+
}
|
|
54
|
+
/** Jumps to a node from outside (the game's own event broke in). */
|
|
55
|
+
jump(t) {
|
|
56
|
+
return this.go(t);
|
|
57
|
+
}
|
|
58
|
+
/** Ends the dialogue now. */
|
|
59
|
+
stop() {
|
|
60
|
+
return this.finish();
|
|
61
|
+
}
|
|
62
|
+
/** The value of an expression over this dialogue's variables, for a game that shares the language. */
|
|
63
|
+
evaluate(t) {
|
|
64
|
+
return u(t)(this.scope);
|
|
65
|
+
}
|
|
66
|
+
save() {
|
|
67
|
+
return {
|
|
68
|
+
node: this.node,
|
|
69
|
+
line: this.line,
|
|
70
|
+
vars: structuredClone(this.vars),
|
|
71
|
+
visits: Object.fromEntries(this.visits),
|
|
72
|
+
chosen: [...this.chosen]
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** Puts a saved dialogue back where it was, without running the node's `set` and `do` again. */
|
|
76
|
+
restore(t) {
|
|
77
|
+
for (const s of Object.keys(this.vars)) delete this.vars[s];
|
|
78
|
+
Object.assign(this.vars, structuredClone(t.vars)), this.visits.clear();
|
|
79
|
+
for (const [s, i] of Object.entries(t.visits)) this.visits.set(s, i);
|
|
80
|
+
this.chosen.clear();
|
|
81
|
+
for (const s of t.chosen) this.chosen.add(s);
|
|
82
|
+
return t.node === null || !this.script.nodes[t.node] ? this.finish() : (this.node = t.node, this.line = t.line, this.show(0));
|
|
83
|
+
}
|
|
84
|
+
go(t) {
|
|
85
|
+
return this.enter(t, 0);
|
|
86
|
+
}
|
|
87
|
+
enter(t, s) {
|
|
88
|
+
if (s > f) throw new Error(`dialogue: '${t}' loops without showing anything`);
|
|
89
|
+
const i = this.nodeOf(t);
|
|
90
|
+
return this.node = t, this.line = 0, this.visits.set(t, (this.visits.get(t) ?? 0) + 1), this.assign(i.set), this.run(i.do), this.node !== t ? this.step : this.show(s);
|
|
91
|
+
}
|
|
92
|
+
/** The step from the current line on: the next line that holds, else the choices, else the way on. */
|
|
93
|
+
show(t) {
|
|
94
|
+
const s = this.node, i = this.nodeOf(s), e = i.lines ?? [];
|
|
95
|
+
for (; this.line < e.length; this.line++) {
|
|
96
|
+
const r = e[this.line], h = typeof r == "string" ? { text: r } : r;
|
|
97
|
+
if (!(h.if !== void 0 && !this.test(h.if)))
|
|
98
|
+
return this.step = { type: "line", node: s, speaker: h.speaker, text: this.text(h.text, h.key), tags: h.tags ?? [] };
|
|
99
|
+
}
|
|
100
|
+
const n = this.offered(s, i);
|
|
101
|
+
if (n.length > 0) return this.step = { type: "choices", node: s, choices: n };
|
|
102
|
+
const o = this.wayOn(i);
|
|
103
|
+
return o === null ? this.finish() : this.enter(o, t + 1);
|
|
104
|
+
}
|
|
105
|
+
offered(t, s) {
|
|
106
|
+
const i = [];
|
|
107
|
+
return (s.choices ?? []).forEach((e, n) => {
|
|
108
|
+
if (e.once && this.chosen.has(`${t}#${n}`)) return;
|
|
109
|
+
const o = e.if === void 0 || this.test(e.if);
|
|
110
|
+
!o && !e.showLocked || i.push({ index: n, text: this.text(e.text, e.key), enabled: o });
|
|
111
|
+
}), i.some((e) => e.enabled) ? i : [];
|
|
112
|
+
}
|
|
113
|
+
wayOn(t) {
|
|
114
|
+
if (t.next === void 0) return null;
|
|
115
|
+
if (typeof t.next == "string") return t.next;
|
|
116
|
+
for (const s of t.next) if (s.if === void 0 || this.test(s.if)) return s.goto;
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
finish() {
|
|
120
|
+
return this.node = null, this.step = { type: "end" };
|
|
121
|
+
}
|
|
122
|
+
nodeOf(t) {
|
|
123
|
+
const s = this.script.nodes[t];
|
|
124
|
+
if (!s) throw new Error(`dialogue: no node '${t}'`);
|
|
125
|
+
return s;
|
|
126
|
+
}
|
|
127
|
+
test(t) {
|
|
128
|
+
return !!u(t)(this.scope);
|
|
129
|
+
}
|
|
130
|
+
assign(t) {
|
|
131
|
+
if (!t) return;
|
|
132
|
+
const s = Object.entries(t).map(([i, e]) => [i, u(e)(this.scope)]);
|
|
133
|
+
for (const [i, e] of s) p(this.vars, i, e);
|
|
134
|
+
}
|
|
135
|
+
run(t) {
|
|
136
|
+
for (const s of t ?? []) {
|
|
137
|
+
const i = typeof s == "string" ? s : s.name;
|
|
138
|
+
this.options.onCommand?.(i, typeof s == "string" ? [] : s.args ?? [], this);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** `text` translated, with each `{expression}` replaced by its value; `{{` and `}}` are braces. */
|
|
142
|
+
text(t, s) {
|
|
143
|
+
return (this.options.translate ? this.options.translate(t, s) : t).replace(/\{\{|\}\}|\{([^{}]+)\}/g, (e, n) => {
|
|
144
|
+
if (e === "{{") return "{";
|
|
145
|
+
if (e === "}}") return "}";
|
|
146
|
+
const o = u(n.trim())(this.scope);
|
|
147
|
+
return o == null ? "" : String(o);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function l(c, t) {
|
|
152
|
+
let s = c;
|
|
153
|
+
for (const i of t.split(".")) {
|
|
154
|
+
if (s === null || typeof s != "object" || !Object.prototype.hasOwnProperty.call(s, i)) return;
|
|
155
|
+
s = s[i];
|
|
156
|
+
}
|
|
157
|
+
return s;
|
|
158
|
+
}
|
|
159
|
+
function p(c, t, s) {
|
|
160
|
+
const i = t.split(".");
|
|
161
|
+
let e = c;
|
|
162
|
+
for (const o of i.slice(0, -1)) {
|
|
163
|
+
if (o === "__proto__" || o === "constructor" || o === "prototype") throw new Error(`dialogue: cannot set '${t}'`);
|
|
164
|
+
const r = e[o];
|
|
165
|
+
e = r !== null && typeof r == "object" ? r : e[o] = {};
|
|
166
|
+
}
|
|
167
|
+
const n = i[i.length - 1];
|
|
168
|
+
if (n === "__proto__" || n === "constructor" || n === "prototype") throw new Error(`dialogue: cannot set '${t}'`);
|
|
169
|
+
e[n] = s;
|
|
170
|
+
}
|
|
171
|
+
export {
|
|
172
|
+
d as DialogueRunner
|
|
173
|
+
};
|