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.
- package/CHANGELOG.md +61 -1
- package/README.md +12 -6
- package/dist/1681-engine.js +588 -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/core/Camera.js +10 -0
- package/dist/core/ObjectFactory.js +23 -21
- package/dist/core/PixelGrid.js +47 -0
- package/dist/core/SharpText.js +72 -0
- package/dist/core/Stage.js +48 -31
- package/dist/core/gridPlacement.js +14 -0
- 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/core/Camera.d.ts +8 -0
- package/dist/types/core/Camera.d.ts.map +1 -1
- package/dist/types/core/ObjectFactory.d.ts.map +1 -1
- package/dist/types/core/PixelGrid.d.ts +30 -0
- package/dist/types/core/PixelGrid.d.ts.map +1 -0
- package/dist/types/core/SharpText.d.ts +32 -0
- package/dist/types/core/SharpText.d.ts.map +1 -0
- package/dist/types/core/Stage.d.ts +14 -0
- package/dist/types/core/Stage.d.ts.map +1 -1
- package/dist/types/core/gridPlacement.d.ts +20 -0
- package/dist/types/core/gridPlacement.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 +18 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/input/Actions.d.ts +14 -0
- package/dist/types/input/Actions.d.ts.map +1 -1
- package/dist/types/input/InputCapture.d.ts +53 -0
- package/dist/types/input/InputCapture.d.ts.map +1 -0
- package/dist/types/input/Keymap.d.ts +12 -6
- package/dist/types/input/Keymap.d.ts.map +1 -1
- package/dist/types/input/inputTape.d.ts +66 -0
- package/dist/types/input/inputTape.d.ts.map +1 -0
- package/dist/types/mobile/backButton.d.ts +30 -0
- package/dist/types/mobile/backButton.d.ts.map +1 -0
- package/dist/types/mobile/capacitor.d.ts +62 -0
- package/dist/types/mobile/capacitor.d.ts.map +1 -0
- package/dist/types/mobile/index.d.ts +4 -0
- package/dist/types/mobile/index.d.ts.map +1 -0
- package/dist/types/mobile/screen.d.ts +27 -0
- package/dist/types/mobile/screen.d.ts.map +1 -0
- package/dist/types/nav/flowField.d.ts +31 -0
- package/dist/types/nav/flowField.d.ts.map +1 -0
- package/dist/types/nav/steering.d.ts +47 -0
- package/dist/types/nav/steering.d.ts.map +1 -0
- package/dist/types/net/InterpolationBuffer.d.ts +5 -0
- package/dist/types/net/InterpolationBuffer.d.ts.map +1 -1
- package/dist/types/net/LagCompensator.d.ts +70 -0
- package/dist/types/net/LagCompensator.d.ts.map +1 -0
- package/dist/types/net/Room.d.ts +72 -3
- package/dist/types/net/Room.d.ts.map +1 -1
- package/dist/types/net/RoomHost.d.ts +8 -3
- package/dist/types/net/RoomHost.d.ts.map +1 -1
- package/dist/types/net/SpatialHash.d.ts +25 -0
- package/dist/types/net/SpatialHash.d.ts.map +1 -0
- package/dist/types/ui/ScrollView.d.ts.map +1 -1
- package/dist/ui/ScrollView.js +10 -8
- package/package.json +5 -1
package/dist/core/Stage.js
CHANGED
|
@@ -1,24 +1,34 @@
|
|
|
1
|
-
import { Container as
|
|
2
|
-
import { Camera as
|
|
3
|
-
import { Clock as
|
|
1
|
+
import { Container as s } from "pixi.js";
|
|
2
|
+
import { Camera as a } from "./Camera.js";
|
|
3
|
+
import { Clock as n } from "./Clock.js";
|
|
4
4
|
import { ObjectFactory as o } from "./ObjectFactory.js";
|
|
5
|
+
import { PixelGrid as d } from "./PixelGrid.js";
|
|
5
6
|
import { Pointer as c } from "./Pointer.js";
|
|
6
|
-
import { Tweener as
|
|
7
|
+
import { Tweener as m } from "./Tweener.js";
|
|
7
8
|
class h {
|
|
8
|
-
constructor(
|
|
9
|
-
this.app =
|
|
9
|
+
constructor(t, e) {
|
|
10
|
+
this.app = t, this.textures = e, t.stage.addChild(this.world, this.screen), this.cameras = { main: new a(this.world, this.screen) }, this.input = { activePointer: new c(t.canvas) }, this.grid = new d(t, this.world), this.time.onError = (i) => this.caught("timers", i), this.syncSize();
|
|
10
11
|
}
|
|
11
12
|
app;
|
|
12
13
|
textures;
|
|
13
14
|
/** Told of every fault a guarded step caught (error telemetry, if the game sets one). */
|
|
14
15
|
static onCaught = null;
|
|
15
|
-
world = new
|
|
16
|
-
screen = new
|
|
16
|
+
world = new s({ sortableChildren: !0 });
|
|
17
|
+
screen = new s();
|
|
17
18
|
add = new o(this);
|
|
18
|
-
tweens = new
|
|
19
|
-
time = new
|
|
19
|
+
tweens = new m();
|
|
20
|
+
time = new n();
|
|
20
21
|
cameras;
|
|
21
22
|
input;
|
|
23
|
+
/**
|
|
24
|
+
* Pixel-perfect: the world drawn one texel to a world pixel and scaled up by the camera's zoom (`PixelGrid`), so turned,
|
|
25
|
+
* scaled and between-pixels art lands on one grid; text made by `add.text`/`add.label` stays at screen resolution.
|
|
26
|
+
* Off (the default), the world is drawn straight at the zoom. Takes effect on the next frame; may be switched any time.
|
|
27
|
+
*/
|
|
28
|
+
pixelPerfect = !1;
|
|
29
|
+
grid;
|
|
30
|
+
/** Whether the grid drew last frame, so the frame it is switched off still takes it down. */
|
|
31
|
+
gridLive = !1;
|
|
22
32
|
resizeListeners = [];
|
|
23
33
|
lastWidth = 0;
|
|
24
34
|
lastHeight = 0;
|
|
@@ -32,10 +42,10 @@ class h {
|
|
|
32
42
|
return this.app.ticker.FPS;
|
|
33
43
|
}
|
|
34
44
|
/** Calls `fn` whenever the window changes size; the returned function stops it (a per-map listener must). */
|
|
35
|
-
onResize(
|
|
36
|
-
return this.resizeListeners.push(
|
|
37
|
-
const
|
|
38
|
-
|
|
45
|
+
onResize(t) {
|
|
46
|
+
return this.resizeListeners.push(t), () => {
|
|
47
|
+
const e = this.resizeListeners.indexOf(t);
|
|
48
|
+
e >= 0 && this.resizeListeners.splice(e, 1);
|
|
39
49
|
};
|
|
40
50
|
}
|
|
41
51
|
/**
|
|
@@ -43,9 +53,16 @@ class h {
|
|
|
43
53
|
* ticker listener that throws never asks for the next animation frame, so one bad frame used to stop the world for
|
|
44
54
|
* good — the canvas frozen and no input reaching the server (seen on Android).
|
|
45
55
|
*/
|
|
46
|
-
frame(
|
|
47
|
-
const
|
|
48
|
-
this.guard("resize", () => this.syncSize()), this.guard("update", () => this.update(
|
|
56
|
+
frame(t) {
|
|
57
|
+
const e = performance.now();
|
|
58
|
+
this.guard("resize", () => this.syncSize()), this.guard("update", () => this.update(e, t)), this.guard("tweens", () => this.tweens.update(t)), this.guard("timers", () => this.time.update(e)), this.guard("camera", () => this.cameras.main.apply(e)), (this.pixelPerfect || this.gridLive) && (this.gridLive = this.pixelPerfect, this.guard("pixel grid", () => this.grid.render(this.cameras.main, this.pixelPerfect)));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Keeps a piece of text made outside `add.text`/`add.label` (put on the world any way) at screen resolution while the
|
|
62
|
+
* world is pixel-perfect. Those two do it themselves.
|
|
63
|
+
*/
|
|
64
|
+
keepSharp(t) {
|
|
65
|
+
this.grid.text.track(t);
|
|
49
66
|
}
|
|
50
67
|
reported = /* @__PURE__ */ new Set();
|
|
51
68
|
/**
|
|
@@ -53,30 +70,30 @@ class h {
|
|
|
53
70
|
* of their own `update` too: a fault before the camera follows the player used to leave the view standing on one
|
|
54
71
|
* spot while the world went on.
|
|
55
72
|
*/
|
|
56
|
-
guard(
|
|
57
|
-
this.guarded(
|
|
73
|
+
guard(t, e) {
|
|
74
|
+
this.guarded(t, e, void 0);
|
|
58
75
|
}
|
|
59
76
|
/** `guard` for a step that answers something: `fallback` when it threw. */
|
|
60
|
-
guarded(
|
|
77
|
+
guarded(t, e, i) {
|
|
61
78
|
try {
|
|
62
|
-
return
|
|
63
|
-
} catch (
|
|
64
|
-
return this.caught(
|
|
79
|
+
return e();
|
|
80
|
+
} catch (r) {
|
|
81
|
+
return this.caught(t, r), i;
|
|
65
82
|
}
|
|
66
83
|
}
|
|
67
84
|
/** What a guarded step threw: logged once per distinct error, or a fault repeating every frame floods the log at 60 a second. */
|
|
68
|
-
caught(
|
|
69
|
-
const i = `${
|
|
70
|
-
this.reported.has(i) || (this.reported.add(i), console.error(`[frame] ${
|
|
85
|
+
caught(t, e) {
|
|
86
|
+
const i = `${t}|${e instanceof Error ? e.message : String(e)}`;
|
|
87
|
+
this.reported.has(i) || (this.reported.add(i), console.error(`[frame] ${t} failed`, e), h.onCaught?.(`[frame] ${t}`, e));
|
|
71
88
|
}
|
|
72
89
|
destroy() {
|
|
73
|
-
this.tweens.clear(), this.time.clear(), this.input.activePointer.destroy(), this.world.destroy({ children: !0, context: !0 }), this.screen.destroy({ children: !0, context: !0 });
|
|
90
|
+
this.tweens.clear(), this.time.clear(), this.input.activePointer.destroy(), this.grid.destroy(this.cameras.main), this.world.destroy({ children: !0, context: !0 }), this.screen.destroy({ children: !0, context: !0 });
|
|
74
91
|
}
|
|
75
92
|
syncSize() {
|
|
76
|
-
const { width:
|
|
77
|
-
if (i.width =
|
|
78
|
-
this.lastWidth =
|
|
79
|
-
for (const
|
|
93
|
+
const { width: t, height: e } = this.app.screen, i = this.cameras.main;
|
|
94
|
+
if (i.width = t, i.height = e, !(t === this.lastWidth && e === this.lastHeight)) {
|
|
95
|
+
this.lastWidth = t, this.lastHeight = e;
|
|
96
|
+
for (const r of [...this.resizeListeners]) r();
|
|
80
97
|
}
|
|
81
98
|
}
|
|
82
99
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function o(a, c, e, h, n, f) {
|
|
2
|
+
const t = e || 1, M = Math.floor(-a / t), d = Math.floor(-c / t);
|
|
3
|
+
return {
|
|
4
|
+
tx: M,
|
|
5
|
+
ty: d,
|
|
6
|
+
width: Math.ceil(n / t) + 2,
|
|
7
|
+
height: Math.ceil(f / t) + 2,
|
|
8
|
+
x: Math.round((a + M * t) * h) / h,
|
|
9
|
+
y: Math.round((c + d * t) * h) / h
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
o as gridPlacement
|
|
14
|
+
};
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const A = ["&&", "||", "==", "!=", "<=", ">=", "<", ">", "!", "+", "-", "*", "/", "%", "(", ")", ","];
|
|
2
|
+
function N(r) {
|
|
3
|
+
const i = [];
|
|
4
|
+
let o = 0;
|
|
5
|
+
for (; o < r.length; ) {
|
|
6
|
+
const l = r[o];
|
|
7
|
+
if (/\s/.test(l)) {
|
|
8
|
+
o++;
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (/[0-9.]/.test(l)) {
|
|
12
|
+
const c = /^(?:\d+\.?\d*|\.\d+)/.exec(r.slice(o));
|
|
13
|
+
if (!c) throw new Error(`bad number at ${o}`);
|
|
14
|
+
i.push({ k: "num", v: Number(c[0]) }), o += c[0].length;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (l === '"' || l === "'") {
|
|
18
|
+
let c = o + 1, p = "";
|
|
19
|
+
for (; c < r.length && r[c] !== l; )
|
|
20
|
+
r[c] === "\\" && c + 1 < r.length && c++, p += r[c], c++;
|
|
21
|
+
if (c >= r.length) throw new Error(`unclosed string at ${o}`);
|
|
22
|
+
i.push({ k: "str", v: p }), o = c + 1;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (/[A-Za-z_$]/.test(l)) {
|
|
26
|
+
const c = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*/.exec(r.slice(o));
|
|
27
|
+
i.push({ k: "id", v: c[0] }), o += c[0].length;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const f = A.find((c) => r.startsWith(c, o));
|
|
31
|
+
if (!f) throw new Error(`unexpected '${l}' at ${o}`);
|
|
32
|
+
i.push({ k: "op", v: f }), o += f.length;
|
|
33
|
+
}
|
|
34
|
+
return i;
|
|
35
|
+
}
|
|
36
|
+
const m = (r) => !!r, k = (r, i) => r === i || r == null && i == null, s = (r) => typeof r == "number" ? r : typeof r == "boolean" ? Number(r) : r == null ? 0 : Number(r);
|
|
37
|
+
function Z(r) {
|
|
38
|
+
let i;
|
|
39
|
+
try {
|
|
40
|
+
i = N(r);
|
|
41
|
+
} catch (n) {
|
|
42
|
+
throw new Error(`expression '${r}': ${n.message}`);
|
|
43
|
+
}
|
|
44
|
+
let o = 0;
|
|
45
|
+
const l = () => i[o], f = (n) => l()?.k === "op" && l().v === n, c = (n) => {
|
|
46
|
+
const e = l();
|
|
47
|
+
return e?.k === "op" && n.includes(e.v);
|
|
48
|
+
}, p = (n) => {
|
|
49
|
+
throw new Error(`expression '${r}': ${n}`);
|
|
50
|
+
}, y = (n) => f(n) ? o++ : p(`expected '${n}'`), h = (n, e, u) => () => {
|
|
51
|
+
let t = n();
|
|
52
|
+
for (; c(e); ) {
|
|
53
|
+
const a = i[o++].v;
|
|
54
|
+
t = u(a, t, n());
|
|
55
|
+
}
|
|
56
|
+
return t;
|
|
57
|
+
}, x = () => {
|
|
58
|
+
const n = i[o++];
|
|
59
|
+
if (!n) return p("ended early");
|
|
60
|
+
if (n.k === "num" || n.k === "str") {
|
|
61
|
+
const e = n.v;
|
|
62
|
+
return () => e;
|
|
63
|
+
}
|
|
64
|
+
if (n.k === "id") {
|
|
65
|
+
const e = n.v;
|
|
66
|
+
if (e === "true") return () => !0;
|
|
67
|
+
if (e === "false") return () => !1;
|
|
68
|
+
if (e === "null") return () => null;
|
|
69
|
+
if (f("(")) {
|
|
70
|
+
o++;
|
|
71
|
+
const u = [];
|
|
72
|
+
for (; !f(")") && (u.push($()), !!f(",")); )
|
|
73
|
+
o++;
|
|
74
|
+
return y(")"), (t) => t.call(e, u.map((a) => a(t)));
|
|
75
|
+
}
|
|
76
|
+
return (u) => {
|
|
77
|
+
const t = u.get(e);
|
|
78
|
+
return typeof t == "number" || typeof t == "string" || typeof t == "boolean" || t == null ? t : String(t);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (n.v === "(") {
|
|
82
|
+
const e = $();
|
|
83
|
+
return y(")"), e;
|
|
84
|
+
}
|
|
85
|
+
return p(`unexpected '${n.v}'`);
|
|
86
|
+
}, w = () => {
|
|
87
|
+
if (f("!")) {
|
|
88
|
+
o++;
|
|
89
|
+
const n = w();
|
|
90
|
+
return (e) => !m(n(e));
|
|
91
|
+
}
|
|
92
|
+
if (f("-")) {
|
|
93
|
+
o++;
|
|
94
|
+
const n = w();
|
|
95
|
+
return (e) => -s(n(e));
|
|
96
|
+
}
|
|
97
|
+
return x();
|
|
98
|
+
}, v = h(
|
|
99
|
+
w,
|
|
100
|
+
["*", "/", "%"],
|
|
101
|
+
(n, e, u) => n === "*" ? (t) => s(e(t)) * s(u(t)) : n === "/" ? (t) => s(e(t)) / s(u(t)) : (t) => s(e(t)) % s(u(t))
|
|
102
|
+
), E = h(
|
|
103
|
+
v,
|
|
104
|
+
["+", "-"],
|
|
105
|
+
(n, e, u) => n === "+" ? (t) => {
|
|
106
|
+
const a = e(t), d = u(t);
|
|
107
|
+
return typeof a == "string" || typeof d == "string" ? `${a ?? ""}${d ?? ""}` : s(a) + s(d);
|
|
108
|
+
} : (t) => s(e(t)) - s(u(t))
|
|
109
|
+
), _ = h(E, ["<", ">", "<=", ">=", "==", "!="], (n, e, u) => {
|
|
110
|
+
switch (n) {
|
|
111
|
+
case "==":
|
|
112
|
+
return (t) => k(e(t), u(t));
|
|
113
|
+
case "!=":
|
|
114
|
+
return (t) => !k(e(t), u(t));
|
|
115
|
+
case "<":
|
|
116
|
+
return (t) => s(e(t)) < s(u(t));
|
|
117
|
+
case ">":
|
|
118
|
+
return (t) => s(e(t)) > s(u(t));
|
|
119
|
+
case "<=":
|
|
120
|
+
return (t) => s(e(t)) <= s(u(t));
|
|
121
|
+
default:
|
|
122
|
+
return (t) => s(e(t)) >= s(u(t));
|
|
123
|
+
}
|
|
124
|
+
}), z = h(_, ["&&"], (n, e, u) => (t) => m(e(t)) && m(u(t))), $ = h(z, ["||"], (n, e, u) => (t) => m(e(t)) || m(u(t)));
|
|
125
|
+
i.length === 0 && p("empty");
|
|
126
|
+
const b = $();
|
|
127
|
+
return o < i.length && p(`unexpected '${i[o].v}'`), b;
|
|
128
|
+
}
|
|
129
|
+
const g = /* @__PURE__ */ new Map();
|
|
130
|
+
function O(r) {
|
|
131
|
+
let i = g.get(r);
|
|
132
|
+
return i || (i = Z(r), g.size > 2e3 && g.clear(), g.set(r, i)), i;
|
|
133
|
+
}
|
|
134
|
+
export {
|
|
135
|
+
Z as compileExpr,
|
|
136
|
+
O as expr
|
|
137
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { compileExpr as A } from "./expr.js";
|
|
2
|
+
function S(r, d = {}) {
|
|
3
|
+
const $ = [], s = (e, n) => $.push({ path: e, message: n, level: "error" }), g = Object.keys(r.nodes ?? {});
|
|
4
|
+
if (g.length === 0)
|
|
5
|
+
return s("nodes", "the script has no nodes"), $;
|
|
6
|
+
const m = r.start ?? g[0];
|
|
7
|
+
r.nodes[m] || s("start", `no node '${m}'`);
|
|
8
|
+
const p = d.functions ? /* @__PURE__ */ new Set(["visited", "chose", ...d.functions]) : null, a = (e, n) => {
|
|
9
|
+
if (typeof n != "string") return s(e, "must be an expression in a string");
|
|
10
|
+
try {
|
|
11
|
+
A(n);
|
|
12
|
+
} catch (o) {
|
|
13
|
+
return s(e, o.message);
|
|
14
|
+
}
|
|
15
|
+
if (p)
|
|
16
|
+
for (const o of n.matchAll(/([A-Za-z_$][\w$]*)\s*\(/g)) p.has(o[1]) || s(e, `no function '${o[1]}'`);
|
|
17
|
+
}, h = (e, n) => {
|
|
18
|
+
if (typeof n != "string") return s(e, "must be text");
|
|
19
|
+
for (const o of n.replace(/\{\{|\}\}/g, "").matchAll(/\{([^{}]*)\}/g)) a(e, o[1].trim());
|
|
20
|
+
}, u = (e, n) => {
|
|
21
|
+
(typeof n != "string" || !r.nodes[n]) && s(e, `no node '${String(n)}'`);
|
|
22
|
+
}, y = (e, n) => {
|
|
23
|
+
for (const [o, i] of Object.entries(n ?? {})) a(`${e}.${o}`, i);
|
|
24
|
+
}, v = (e, n) => {
|
|
25
|
+
(n ?? []).forEach((o, i) => {
|
|
26
|
+
const t = typeof o == "string" ? o : o?.name;
|
|
27
|
+
typeof t != "string" ? s(`${e}[${i}]`, "a command needs a name") : d.commands && !d.commands.includes(t) && s(`${e}[${i}]`, `no command '${t}'`);
|
|
28
|
+
});
|
|
29
|
+
}, w = /* @__PURE__ */ new Map();
|
|
30
|
+
for (const e of g) {
|
|
31
|
+
const n = r.nodes[e], o = `nodes.${e}`, i = [];
|
|
32
|
+
y(`${o}.set`, n.set), v(`${o}.do`, n.do), (n.lines ?? []).forEach((t, f) => {
|
|
33
|
+
if (typeof t == "string") return h(`${o}.lines[${f}]`, t);
|
|
34
|
+
h(`${o}.lines[${f}].text`, t.text), t.if !== void 0 && a(`${o}.lines[${f}].if`, t.if);
|
|
35
|
+
}), (n.choices ?? []).forEach((t, f) => {
|
|
36
|
+
const c = `${o}.choices[${f}]`;
|
|
37
|
+
h(`${c}.text`, t.text), t.if !== void 0 && a(`${c}.if`, t.if), y(`${c}.set`, t.set), v(`${c}.do`, t.do), t.goto !== void 0 && (u(`${c}.goto`, t.goto), i.push(t.goto));
|
|
38
|
+
}), typeof n.next == "string" ? (u(`${o}.next`, n.next), i.push(n.next)) : Array.isArray(n.next) && n.next.forEach((t, f) => {
|
|
39
|
+
t.if !== void 0 && a(`${o}.next[${f}].if`, t.if), u(`${o}.next[${f}].goto`, t.goto), i.push(t.goto);
|
|
40
|
+
}), w.set(e, i);
|
|
41
|
+
}
|
|
42
|
+
const l = /* @__PURE__ */ new Set(), x = r.nodes[m] ? [m] : [];
|
|
43
|
+
for (; x.length; ) {
|
|
44
|
+
const e = x.pop();
|
|
45
|
+
l.has(e) || !r.nodes[e] || (l.add(e), x.push(...w.get(e) ?? []));
|
|
46
|
+
}
|
|
47
|
+
for (const e of g) l.has(e) || $.push({ path: `nodes.${e}`, message: "nothing leads here from the start", level: "warning" });
|
|
48
|
+
return $;
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
S as validateDialogue
|
|
52
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
function P(r, t) {
|
|
2
|
+
return [r, t];
|
|
3
|
+
}
|
|
4
|
+
function y(r, t, e = {}) {
|
|
5
|
+
const o = e.extends ?? null, n = new Map(o?.parts ?? []);
|
|
6
|
+
for (const [s, i] of t)
|
|
7
|
+
i === null ? n.delete(s) : n.set(s, l(n.get(s), i));
|
|
8
|
+
return { name: r, parts: n, base: o };
|
|
9
|
+
}
|
|
10
|
+
function w(r, t, e = {}) {
|
|
11
|
+
const o = e.with?.length ? y(t.name, e.with, { extends: t }).parts : t.parts, n = r.create(), s = { entity: n, args: e.args };
|
|
12
|
+
for (const [i, f] of o)
|
|
13
|
+
e.without?.includes(i) || r.add(n, i, g(f, s));
|
|
14
|
+
return n;
|
|
15
|
+
}
|
|
16
|
+
function x(r, t) {
|
|
17
|
+
for (let e = r; e; e = e.base) if (e === t) return !0;
|
|
18
|
+
return !1;
|
|
19
|
+
}
|
|
20
|
+
function g(r, t) {
|
|
21
|
+
return typeof r == "function" ? r(t) : r !== null && typeof r == "object" ? structuredClone(r) : r;
|
|
22
|
+
}
|
|
23
|
+
const h = (r) => r !== null && typeof r == "object" && !Array.isArray(r) && Object.getPrototypeOf(r) === Object.prototype;
|
|
24
|
+
function l(r, t) {
|
|
25
|
+
if (!h(r) || !h(t)) return t;
|
|
26
|
+
const e = { ...r };
|
|
27
|
+
for (const [o, n] of Object.entries(t)) e[o] = l(r[o], n);
|
|
28
|
+
return e;
|
|
29
|
+
}
|
|
30
|
+
class O {
|
|
31
|
+
types = /* @__PURE__ */ new Map();
|
|
32
|
+
factories = /* @__PURE__ */ new Map();
|
|
33
|
+
prefabs = /* @__PURE__ */ new Map();
|
|
34
|
+
/** Lets data name a component type. */
|
|
35
|
+
component(t, e) {
|
|
36
|
+
return this.types.set(t, e), this;
|
|
37
|
+
}
|
|
38
|
+
/** A value made in code for a prefab loaded from data (and its variants): the data cannot hold a function. */
|
|
39
|
+
factory(t, e, o) {
|
|
40
|
+
let n = this.factories.get(t);
|
|
41
|
+
return n || this.factories.set(t, n = /* @__PURE__ */ new Map()), n.set(e, o), this;
|
|
42
|
+
}
|
|
43
|
+
/** Adds a prefab made in code. */
|
|
44
|
+
add(t) {
|
|
45
|
+
return this.prefabs.set(t.name, t), this;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Builds the prefabs of `data`, bases before their variants (in any order in the data), and adds them. Throws,
|
|
49
|
+
* naming the prefab, on an unknown component or base, or a prefab that extends itself.
|
|
50
|
+
*/
|
|
51
|
+
load(t) {
|
|
52
|
+
const e = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Set(), n = (s) => {
|
|
53
|
+
const i = e.get(s);
|
|
54
|
+
if (i) return i;
|
|
55
|
+
const f = t[s];
|
|
56
|
+
if (!f) return this.get(s);
|
|
57
|
+
if (o.has(s)) throw new Error(`PrefabLibrary: '${s}' extends itself`);
|
|
58
|
+
o.add(s);
|
|
59
|
+
const d = f.extends === void 0 ? void 0 : n(f.extends), u = Object.entries(f.components ?? {}).map(([c, a]) => {
|
|
60
|
+
const b = this.types.get(c);
|
|
61
|
+
if (!b) throw new Error(`PrefabLibrary: '${s}' names an unknown component '${c}'`);
|
|
62
|
+
return [b, a];
|
|
63
|
+
});
|
|
64
|
+
for (const [c, a] of this.factories.get(s) ?? []) u.push([c, a]);
|
|
65
|
+
const p = y(s, u, { extends: d });
|
|
66
|
+
return o.delete(s), e.set(s, p), p;
|
|
67
|
+
};
|
|
68
|
+
for (const s of Object.keys(t)) n(s);
|
|
69
|
+
for (const [s, i] of e) this.prefabs.set(s, i);
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
get(t) {
|
|
73
|
+
const e = this.prefabs.get(t);
|
|
74
|
+
if (!e) throw new Error(`PrefabLibrary: no prefab '${t}'`);
|
|
75
|
+
return e;
|
|
76
|
+
}
|
|
77
|
+
has(t) {
|
|
78
|
+
return this.prefabs.has(t);
|
|
79
|
+
}
|
|
80
|
+
names() {
|
|
81
|
+
return [...this.prefabs.keys()];
|
|
82
|
+
}
|
|
83
|
+
/** `spawnPrefab` by name. */
|
|
84
|
+
spawn(t, e, o = {}) {
|
|
85
|
+
return w(t, this.get(e), o);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
O as PrefabLibrary,
|
|
90
|
+
y as definePrefab,
|
|
91
|
+
x as isPrefabOf,
|
|
92
|
+
P as prefabPart,
|
|
93
|
+
w as spawnPrefab
|
|
94
|
+
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { existsSync as b } from "node:fs";
|
|
2
2
|
import { rm as d, mkdir as M, readFile as V, rename as T, readdir as x } from "node:fs/promises";
|
|
3
3
|
import { join as s } from "node:path";
|
|
4
|
-
import { isNewer as y, validManifest as $, fileUrl as
|
|
5
|
-
import { planWeb as
|
|
6
|
-
import { fetchWithTimeout as u, hashWeb as
|
|
4
|
+
import { isNewer as y, validManifest as $, fileUrl as P } from "../updates/manifest.js";
|
|
5
|
+
import { planWeb as U } from "../updates/webPlan.js";
|
|
6
|
+
import { fetchWithTimeout as u, hashWeb as D, putFile as g, sha256 as E } from "./webFiles.js";
|
|
7
7
|
import { WebPointers as W } from "./webPointers.js";
|
|
8
|
-
class
|
|
8
|
+
class z {
|
|
9
9
|
constructor(e) {
|
|
10
10
|
this.options = e, this.o = {
|
|
11
11
|
root: e.root,
|
|
@@ -30,6 +30,7 @@ class C {
|
|
|
30
30
|
loaded = null;
|
|
31
31
|
/** The tidy a start began: a build waits for it, or it could delete the `.part` folder being filled. */
|
|
32
32
|
tidying = Promise.resolve();
|
|
33
|
+
tidyChain = Promise.resolve();
|
|
33
34
|
/** This shell's identity. */
|
|
34
35
|
get shell() {
|
|
35
36
|
const e = this.options.shell;
|
|
@@ -95,7 +96,7 @@ class C {
|
|
|
95
96
|
if (b(s(l, this.o.entry)) && this.pointers.read("active")?.version !== o.version)
|
|
96
97
|
return this.pointers.write("pending", p), t(100), o.version;
|
|
97
98
|
await this.tidying;
|
|
98
|
-
const f = this.loaded?.dir ?? this.o.builtinDir, h =
|
|
99
|
+
const f = this.loaded?.dir ?? this.o.builtinDir, h = U(o, await D(f)), n = s(this.o.root, `${o.version}.part`);
|
|
99
100
|
await d(n, { recursive: !0, force: !0 }), await M(n, { recursive: !0 });
|
|
100
101
|
for (const r of h.copy) await g(n, r.path, await V(s(f, ...r.from.split("/"))));
|
|
101
102
|
let v = 0;
|
|
@@ -105,9 +106,9 @@ class C {
|
|
|
105
106
|
for (let r = m.shift(); r; r = m.shift()) {
|
|
106
107
|
let a = w.get(r.sha256);
|
|
107
108
|
if (!a) {
|
|
108
|
-
const c = await u(this.o.fetch,
|
|
109
|
+
const c = await u(this.o.fetch, P(this.o.channelUrl, r.sha256), this.o.fetchTimeoutMs);
|
|
109
110
|
if (!c.ok) throw new Error(`${r.path}: ${c.status}`);
|
|
110
|
-
if (a = new Uint8Array(await c.arrayBuffer()),
|
|
111
|
+
if (a = new Uint8Array(await c.arrayBuffer()), E(a) !== r.sha256) throw new Error(`${r.path}: checksum`);
|
|
111
112
|
w.set(r.sha256, a), v += r.size, t(Math.min(99, Math.round(v / Math.max(1, h.bytes) * 100)));
|
|
112
113
|
}
|
|
113
114
|
await g(n, r.path, a);
|
|
@@ -132,8 +133,13 @@ class C {
|
|
|
132
133
|
usable(e) {
|
|
133
134
|
return !!e && e.shell === this.shell && y(e.version, this.o.appVersion) && b(s(this.o.root, e.version, this.o.entry));
|
|
134
135
|
}
|
|
135
|
-
/** Removes every patched web but the active, pending and previous one, and any half-made folder.
|
|
136
|
-
|
|
136
|
+
/** Removes every patched web but the active, pending and previous one, and any half-made folder. A call made while
|
|
137
|
+
* one is running waits for it and then runs once more, so two never delete the same folder at once. */
|
|
138
|
+
tidy() {
|
|
139
|
+
const e = this.tidyChain.then(() => this.tidyOnce());
|
|
140
|
+
return this.tidyChain = e, e;
|
|
141
|
+
}
|
|
142
|
+
async tidyOnce() {
|
|
137
143
|
try {
|
|
138
144
|
const e = ["active", "pending", "previous"], t = new Set([...e.map((i) => this.pointers.read(i)?.version), this.loaded?.version].filter((i) => !!i));
|
|
139
145
|
for (const i of await x(this.o.root))
|
|
@@ -143,5 +149,5 @@ class C {
|
|
|
143
149
|
}
|
|
144
150
|
}
|
|
145
151
|
export {
|
|
146
|
-
|
|
152
|
+
z as WebPatcher
|
|
147
153
|
};
|