@patterkit/runtime 0.7.1 → 0.9.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 +74 -0
- package/dist/index.cjs +135 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +134 -15
- package/dist/index.d.ts +134 -15
- package/dist/index.js +136 -92
- package/dist/index.js.map +1 -1
- package/dist/patterplay.min.js +2 -2
- package/dist/patterplay.min.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// src/engine.ts
|
|
2
|
-
import { evaluate, deserialiseAst } from "@wildwinter/expr";
|
|
2
|
+
import { evaluate, deserialiseAst, makePrng, toUint32 } from "@wildwinter/expr";
|
|
3
3
|
import { matchedSpecificity as scoreSpecificity } from "@wildwinter/expr-specificity";
|
|
4
4
|
import { ScopeRegistry } from "@wildwinter/scoperegistry";
|
|
5
|
+
import { defaultFor, PropertyBag } from "@wildwinter/scoperegistry";
|
|
5
6
|
import { patterDialect, interpolate, splitRef, stripCaptions } from "@patterkit/dialect";
|
|
6
7
|
import { walkNodes, effectiveGameId, castStringKey, DEFAULT_CAPTION_DELIMITERS, DEFAULT_CAPTION_CHARACTER } from "@patterkit/model";
|
|
7
8
|
|
|
@@ -60,6 +61,10 @@ var Engine = class _Engine {
|
|
|
60
61
|
/** The options this engine was built with - reused verbatim by `hotSwap` so the replacement
|
|
61
62
|
* engine keeps the same world resolver, custom RNG, and diagnostic hooks. */
|
|
62
63
|
creationOptions;
|
|
64
|
+
/** The run's ordered stream: every flow's events, each naming its flow. Empty and
|
|
65
|
+
* unwritten unless `options.log` asked for it. */
|
|
66
|
+
engineLog = [];
|
|
67
|
+
engineTraceHandlers = /* @__PURE__ */ new Set();
|
|
63
68
|
constructor(bundle, options = {}) {
|
|
64
69
|
this.creationOptions = options;
|
|
65
70
|
const locale = options.locale ?? bundle.locales.default;
|
|
@@ -95,7 +100,7 @@ var Engine = class _Engine {
|
|
|
95
100
|
const patterSharedDecls = props.filter((p) => p.shared ?? true).map(toDecl);
|
|
96
101
|
const patterLocalDecls = props.filter((p) => !(p.shared ?? true)).map(toDecl);
|
|
97
102
|
const patterSharedNames = new Set(patterSharedDecls.map((d) => d.name.toLowerCase()));
|
|
98
|
-
const shared = new ScopeRegistry().defineOwned("patter", patterSharedDecls);
|
|
103
|
+
const shared = new ScopeRegistry().defineOwned("patter", patterSharedDecls, "@patter.");
|
|
99
104
|
const hostBound = /* @__PURE__ */ new Set();
|
|
100
105
|
if (options.world) {
|
|
101
106
|
const worldSpec = bundle.scopeRegistry?.scopes.find((s) => s.token === "world");
|
|
@@ -114,6 +119,8 @@ var Engine = class _Engine {
|
|
|
114
119
|
sceneSharedNames.set(sceneId, names);
|
|
115
120
|
}
|
|
116
121
|
this.host = {
|
|
122
|
+
logEnabled: options.log ?? false,
|
|
123
|
+
emitEngine: (flow, event, scene) => this.emitEngine(flow, event, scene),
|
|
117
124
|
bundle,
|
|
118
125
|
emitIds,
|
|
119
126
|
strings,
|
|
@@ -488,14 +495,44 @@ var Engine = class _Engine {
|
|
|
488
495
|
}
|
|
489
496
|
/** The shared `@patter` properties, for a live state inspector: each with its ref, type, current
|
|
490
497
|
* value, declared default (for reset), and enum options. Mirrors the Unity / Godot ports. */
|
|
498
|
+
/** The run's decisions, in order, each naming the flow it happened in. Empty unless the
|
|
499
|
+
* run was opened with `log: true`. A flow's own log stays flow-local; this is the only
|
|
500
|
+
* place a story spanning several flows reads as one sequence. */
|
|
501
|
+
log() {
|
|
502
|
+
return this.engineLog;
|
|
503
|
+
}
|
|
504
|
+
/** Drop the retained entries. `seq` does NOT restart: two reads of a log either side of a
|
|
505
|
+
* clear still agree about what came first. */
|
|
506
|
+
clearLog() {
|
|
507
|
+
this.engineLog.length = 0;
|
|
508
|
+
}
|
|
509
|
+
/** Live tap on the run's decisions, for tooling that wants them as they happen rather than
|
|
510
|
+
* retained. Returns its own unsubscribe. */
|
|
511
|
+
onTrace(handler) {
|
|
512
|
+
this.engineTraceHandlers.add(handler);
|
|
513
|
+
return () => this.engineTraceHandlers.delete(handler);
|
|
514
|
+
}
|
|
515
|
+
emitEngine(flow, event, scene) {
|
|
516
|
+
for (const h of this.engineTraceHandlers) h(flow, event);
|
|
517
|
+
if (!this.host.logEnabled) return;
|
|
518
|
+
this.engineLog.push({ ...event, flow, seq: this.engineLog.length, ...scene ? { scene } : {} });
|
|
519
|
+
}
|
|
491
520
|
listProperties() {
|
|
492
521
|
return this.host.patterSharedDecls.map((d) => ({
|
|
493
|
-
|
|
522
|
+
name: d.name,
|
|
523
|
+
// The qualified address, matching what the bag composes for every other scope.
|
|
524
|
+
// `@gold` still resolves on input; it is the shorthand, not the address.
|
|
525
|
+
path: `@patter.${d.name}`,
|
|
494
526
|
type: d.type,
|
|
495
527
|
values: d.values,
|
|
496
528
|
stages: d.stages,
|
|
497
529
|
value: this.getProperty(`@${d.name}`),
|
|
498
|
-
default:
|
|
530
|
+
default: defaultFor(d),
|
|
531
|
+
// Part of the shared row. Always true here today: `toDecl` never sets it, because
|
|
532
|
+
// Patter has no read-only shared property. The Storylet Engine does declare them
|
|
533
|
+
// and its panels disable the editor accordingly, so the field is carried rather
|
|
534
|
+
// than dropped - and the day a read-only @patter property exists, the row says so.
|
|
535
|
+
writable: d.writable ?? true
|
|
499
536
|
}));
|
|
500
537
|
}
|
|
501
538
|
// @scene is scene-namespaced and needs a flow's current scene - silently
|
|
@@ -528,7 +565,7 @@ var Engine = class _Engine {
|
|
|
528
565
|
shared: this.host.shared.save(),
|
|
529
566
|
sharedVisits: Object.fromEntries(this.host.sharedVisits),
|
|
530
567
|
sharedSelectors: serialiseSelectors(this.host.sharedSelectors),
|
|
531
|
-
stageBags: Object.fromEntries([...this.host.stageBags].map(([s, bag]) => [s,
|
|
568
|
+
stageBags: Object.fromEntries([...this.host.stageBags].map(([s, bag]) => [s, bag.save()])),
|
|
532
569
|
flows
|
|
533
570
|
};
|
|
534
571
|
}
|
|
@@ -541,7 +578,13 @@ var Engine = class _Engine {
|
|
|
541
578
|
this.host.sharedSelectors.clear();
|
|
542
579
|
for (const [id, st] of deserialiseSelectors(save.sharedSelectors)) this.host.sharedSelectors.set(id, st);
|
|
543
580
|
this.host.stageBags.clear();
|
|
544
|
-
for (const [s,
|
|
581
|
+
for (const [s, values] of Object.entries(save.stageBags ?? {})) {
|
|
582
|
+
const shared = this.host.sceneSharedNames.get(s) ?? /* @__PURE__ */ new Set();
|
|
583
|
+
const decls = (this.host.bundle.scenes[s]?.sceneProps ?? []).filter((d) => shared.has(d.name.toLowerCase()));
|
|
584
|
+
const bag = new PropertyBag(decls);
|
|
585
|
+
bag.load(values);
|
|
586
|
+
this.host.stageBags.set(s, bag);
|
|
587
|
+
}
|
|
545
588
|
this.flowsById.clear();
|
|
546
589
|
for (const [id, snap] of Object.entries(save.flows)) {
|
|
547
590
|
const flow = new Flow(id, this.host, this.defaultSeed);
|
|
@@ -584,6 +627,7 @@ var Flow = class {
|
|
|
584
627
|
// The SHARED halves live on the host (`host.shared` / `host.stageBags`). Each
|
|
585
628
|
// resolver presents one merged scope, routing each property to its half by the
|
|
586
629
|
// declared `shared` flag.
|
|
630
|
+
/** This flow's per-scene LOCAL scene props; see FlowHost.stageBags. */
|
|
587
631
|
sceneBags = /* @__PURE__ */ new Map();
|
|
588
632
|
patterResolver = {
|
|
589
633
|
get: (n) => this.host.patterSharedNames.has(n) ? this.host.shared.get("patter", n) : this.local.get("patter", n),
|
|
@@ -597,13 +641,13 @@ var Flow = class {
|
|
|
597
641
|
const s = this.currentSceneId;
|
|
598
642
|
if (s === null) return void 0;
|
|
599
643
|
const bag = this.host.sceneSharedNames.get(s)?.has(n) ? this.host.stageBags.get(s) : this.sceneBags.get(s);
|
|
600
|
-
return bag?.
|
|
644
|
+
return bag?.get(n);
|
|
601
645
|
},
|
|
602
646
|
set: (n, v) => {
|
|
603
647
|
const s = this.currentSceneId;
|
|
604
648
|
if (s === null) return;
|
|
605
649
|
const bag = this.host.sceneSharedNames.get(s)?.has(n) ? this.host.stageBags.get(s) : this.sceneBags.get(s);
|
|
606
|
-
if (bag) bag
|
|
650
|
+
if (bag) bag.set(n, v);
|
|
607
651
|
}
|
|
608
652
|
};
|
|
609
653
|
// The eval context is built ONCE: every constituent resolves live state at
|
|
@@ -612,10 +656,13 @@ var Flow = class {
|
|
|
612
656
|
// `local`/`sceneBags`/`currentSceneId`; the host callbacks read current flow
|
|
613
657
|
// fields). Rebuilding it per evaluation was the engine's hottest allocation.
|
|
614
658
|
evalCtx;
|
|
659
|
+
flowLog = [];
|
|
660
|
+
/** Monotonic across the flow's life; survives clearLog so two reads agree on order. */
|
|
661
|
+
flowSeq = 0;
|
|
615
662
|
constructor(id, host, seed) {
|
|
616
663
|
this.id = id;
|
|
617
664
|
this.host = host;
|
|
618
|
-
this.rngState = seed
|
|
665
|
+
this.rngState = toUint32(seed);
|
|
619
666
|
this.local = this.freshLocal();
|
|
620
667
|
const scopes = { ...host.shared.toEvalContext().scopes };
|
|
621
668
|
scopes["patter"] = this.patterResolver;
|
|
@@ -837,7 +884,17 @@ var Flow = class {
|
|
|
837
884
|
this.stack.pop();
|
|
838
885
|
continue;
|
|
839
886
|
}
|
|
887
|
+
const from = frame.index;
|
|
840
888
|
while (frame.index < children.length && !this.eligible(children[frame.index])) frame.index++;
|
|
889
|
+
if (this.host.logEnabled && frame.index !== from) {
|
|
890
|
+
this.emit({
|
|
891
|
+
type: "select",
|
|
892
|
+
group: frame.containerId,
|
|
893
|
+
selector: "run",
|
|
894
|
+
children: children.slice(from, frame.index + 1).map((c, i) => ({ id: c.id, eligible: from + i === frame.index })),
|
|
895
|
+
picked: children[frame.index]?.id ?? null
|
|
896
|
+
});
|
|
897
|
+
}
|
|
841
898
|
if (frame.index >= children.length) {
|
|
842
899
|
this.stack.pop();
|
|
843
900
|
continue;
|
|
@@ -846,6 +903,24 @@ var Flow = class {
|
|
|
846
903
|
}
|
|
847
904
|
}
|
|
848
905
|
/** The options of a pending choice (empty when not at a choice point). */
|
|
906
|
+
/** This flow's decisions, in order. Empty unless the run was opened with `log: true`.
|
|
907
|
+
* The engine's log carries the same events tagged with the flow; this one is what a
|
|
908
|
+
* single conversation reads as. */
|
|
909
|
+
log() {
|
|
910
|
+
return this.flowLog;
|
|
911
|
+
}
|
|
912
|
+
/** Drop the retained entries. `seq` keeps counting, so order survives a clear. */
|
|
913
|
+
clearLog() {
|
|
914
|
+
this.flowLog.length = 0;
|
|
915
|
+
}
|
|
916
|
+
/** Record one decision, on this flow's log and the engine's. Cheap to call with logging
|
|
917
|
+
* off: the entry is never built. */
|
|
918
|
+
emit(event) {
|
|
919
|
+
const scene = this.currentSceneId ?? void 0;
|
|
920
|
+
this.host.emitEngine(this.id, event, scene);
|
|
921
|
+
if (!this.host.logEnabled) return;
|
|
922
|
+
this.flowLog.push({ ...event, seq: this.flowSeq++, ...scene ? { scene } : {} });
|
|
923
|
+
}
|
|
849
924
|
getChoices() {
|
|
850
925
|
return this.pendingChoice?.options ?? [];
|
|
851
926
|
}
|
|
@@ -857,6 +932,7 @@ var Flow = class {
|
|
|
857
932
|
if (!option) throw new Error(`unknown choice option: ${id}`);
|
|
858
933
|
if (!option.eligible) throw new Error(`choice option is not eligible: ${id}`);
|
|
859
934
|
const node = choice.byId.get(id);
|
|
935
|
+
this.emit({ type: "chose", group: choice.groupId, option: id });
|
|
860
936
|
this.pendingChoice = null;
|
|
861
937
|
this.pendingPromptBeat = this.host.replayPromptOnChoose ? this.promptBeatOf(node) ?? null : null;
|
|
862
938
|
this.pendingPromptOwnerId = this.pendingPromptBeat ? node.id : null;
|
|
@@ -890,7 +966,7 @@ var Flow = class {
|
|
|
890
966
|
return {
|
|
891
967
|
scopes: this.local.save(),
|
|
892
968
|
// owned scope "patter" = the NOT-shared globals (@scene saved separately)
|
|
893
|
-
sceneBags: Object.fromEntries([...this.sceneBags].map(([s, bag]) => [s,
|
|
969
|
+
sceneBags: Object.fromEntries([...this.sceneBags].map(([s, bag]) => [s, bag.save()])),
|
|
894
970
|
rngState: this.rngState,
|
|
895
971
|
visits: Object.fromEntries(this.visitCounts),
|
|
896
972
|
cursor: {
|
|
@@ -913,7 +989,7 @@ var Flow = class {
|
|
|
913
989
|
}
|
|
914
990
|
/** @internal Restore this flow from a snapshot. */
|
|
915
991
|
restore(snap) {
|
|
916
|
-
this.rngState = snap.rngState
|
|
992
|
+
this.rngState = toUint32(snap.rngState);
|
|
917
993
|
this.visitCounts = new Map(Object.entries(snap.visits ?? {}));
|
|
918
994
|
const c = snap.cursor;
|
|
919
995
|
this.started = true;
|
|
@@ -928,7 +1004,13 @@ var Flow = class {
|
|
|
928
1004
|
}
|
|
929
1005
|
return { ...frame };
|
|
930
1006
|
});
|
|
931
|
-
this.sceneBags = new Map(Object.entries(snap.sceneBags ?? {}).map(([s,
|
|
1007
|
+
this.sceneBags = new Map(Object.entries(snap.sceneBags ?? {}).map(([s, values]) => {
|
|
1008
|
+
const shared = this.host.sceneSharedNames.get(s) ?? /* @__PURE__ */ new Set();
|
|
1009
|
+
const decls = (this.host.bundle.scenes[s]?.sceneProps ?? []).filter((d) => !shared.has(d.name.toLowerCase()));
|
|
1010
|
+
const bag = new PropertyBag(decls);
|
|
1011
|
+
bag.load(values);
|
|
1012
|
+
return [s, bag];
|
|
1013
|
+
}));
|
|
932
1014
|
this.local = this.freshLocal();
|
|
933
1015
|
this.local.load(snap.scopes);
|
|
934
1016
|
this.activeSnippet = null;
|
|
@@ -1022,6 +1104,7 @@ var Flow = class {
|
|
|
1022
1104
|
byId.set(child.id, child);
|
|
1023
1105
|
}
|
|
1024
1106
|
if (options.length > 0) {
|
|
1107
|
+
this.emit({ type: "choice", group: group.id, options: options.map((o) => ({ id: o.id, eligible: o.eligible })) });
|
|
1025
1108
|
this.pendingChoice = { groupId: group.id, options, byId };
|
|
1026
1109
|
return;
|
|
1027
1110
|
}
|
|
@@ -1030,6 +1113,7 @@ var Flow = class {
|
|
|
1030
1113
|
this.enterChild(fallback);
|
|
1031
1114
|
return;
|
|
1032
1115
|
}
|
|
1116
|
+
this.emit({ type: "dry", group: group.id });
|
|
1033
1117
|
this.host.onDryChoice?.(group.id);
|
|
1034
1118
|
}
|
|
1035
1119
|
// -- Jumps (jump / call-return) ----------------------------------------
|
|
@@ -1044,6 +1128,7 @@ var Flow = class {
|
|
|
1044
1128
|
* hard-ends the flow regardless of the callstack.
|
|
1045
1129
|
*/
|
|
1046
1130
|
enterTarget(to, mode) {
|
|
1131
|
+
this.emit({ type: "jump", to, mode });
|
|
1047
1132
|
if (to === "END") {
|
|
1048
1133
|
this.flowEnded = true;
|
|
1049
1134
|
this.stack = [];
|
|
@@ -1075,17 +1160,28 @@ var Flow = class {
|
|
|
1075
1160
|
}
|
|
1076
1161
|
// -- Selectors ------------------------------------------------------------
|
|
1077
1162
|
selectChild(group) {
|
|
1163
|
+
const verdicts = group.children.map((c) => ({ id: c.id, eligible: this.eligible(c) }));
|
|
1078
1164
|
const eligible = group.children.filter((c) => this.eligible(c));
|
|
1079
|
-
|
|
1165
|
+
const order = group.options?.order ?? "sequential";
|
|
1166
|
+
const exhaust = group.options?.exhaust ?? "once";
|
|
1167
|
+
const trace = (picked) => {
|
|
1168
|
+
this.emit({
|
|
1169
|
+
type: "select",
|
|
1170
|
+
group: group.id,
|
|
1171
|
+
selector: group.selector ?? "default",
|
|
1172
|
+
...group.selector === "sequence" ? { order, exhaust } : {},
|
|
1173
|
+
children: verdicts,
|
|
1174
|
+
picked: picked?.id ?? null
|
|
1175
|
+
});
|
|
1176
|
+
return picked;
|
|
1177
|
+
};
|
|
1178
|
+
if (eligible.length === 0) return trace(null);
|
|
1080
1179
|
const st = this.selectorState(group);
|
|
1081
1180
|
switch (group.selector) {
|
|
1082
1181
|
case "branch":
|
|
1083
|
-
return eligible[0];
|
|
1084
|
-
case "sequence":
|
|
1085
|
-
|
|
1086
|
-
const exhaust = group.options?.exhaust ?? "once";
|
|
1087
|
-
return order === "shuffle" ? this.pickShuffle(eligible, exhaust, st) : order === "specificity" ? this.pickSpecificity(eligible, exhaust, st) : this.pickSequential(eligible, exhaust, st);
|
|
1088
|
-
}
|
|
1182
|
+
return trace(eligible[0]);
|
|
1183
|
+
case "sequence":
|
|
1184
|
+
return trace(order === "shuffle" ? this.pickShuffle(eligible, exhaust, st) : order === "specificity" ? this.pickSpecificity(eligible, exhaust, st) : this.pickSequential(eligible, exhaust, st));
|
|
1089
1185
|
case "run":
|
|
1090
1186
|
case "choice":
|
|
1091
1187
|
default:
|
|
@@ -1203,7 +1299,10 @@ var Flow = class {
|
|
|
1203
1299
|
// -- Effects + expressions ------------------------------------------------
|
|
1204
1300
|
runEffects(effects) {
|
|
1205
1301
|
for (const e of effects ?? []) {
|
|
1206
|
-
|
|
1302
|
+
const value = this.evalExpr(e.value);
|
|
1303
|
+
const prev = this.host.logEnabled ? this.getProperty(e.target) : void 0;
|
|
1304
|
+
this.setProperty(e.target, value);
|
|
1305
|
+
this.emit({ type: "write", target: e.target, value, ...prev !== void 0 ? { prev } : {} });
|
|
1207
1306
|
}
|
|
1208
1307
|
}
|
|
1209
1308
|
eligible(node) {
|
|
@@ -1228,14 +1327,19 @@ var Flow = class {
|
|
|
1228
1327
|
this.visitCounts.set(id, (this.visitCounts.get(id) ?? 0) + 1);
|
|
1229
1328
|
this.host.sharedVisits.set(id, (this.host.sharedVisits.get(id) ?? 0) + 1);
|
|
1230
1329
|
}
|
|
1231
|
-
/** Next float in [0, 1): the shared custom PRNG, or this flow's serialisable mulberry32.
|
|
1330
|
+
/** Next float in [0, 1): the shared custom PRNG, or this flow's serialisable mulberry32.
|
|
1331
|
+
*
|
|
1332
|
+
* The mixing is @wildwinter/expr's makePrng, not a copy inline here. It is a
|
|
1333
|
+
* fixed published algorithm that both product families need and neither
|
|
1334
|
+
* owns, and it existed thirteen times across the two of them. `rngState` is
|
|
1335
|
+
* still the serialisable position, so saves are unaffected: a generator is
|
|
1336
|
+
* made from it, drawn once, and the new state written back. */
|
|
1232
1337
|
rng = () => {
|
|
1233
1338
|
if (this.host.customRng) return this.host.customRng();
|
|
1234
|
-
const
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
1339
|
+
const prng = makePrng(this.rngState);
|
|
1340
|
+
const draw = prng.next();
|
|
1341
|
+
this.rngState = prng.state();
|
|
1342
|
+
return draw;
|
|
1239
1343
|
};
|
|
1240
1344
|
// -- Strings / beats ------------------------------------------------------
|
|
1241
1345
|
beatResult(beat) {
|
|
@@ -1342,7 +1446,7 @@ var Flow = class {
|
|
|
1342
1446
|
}
|
|
1343
1447
|
/** The per-flow registry: the NOT-shared `@patter` globals (the shared ones live on the host). */
|
|
1344
1448
|
freshLocal() {
|
|
1345
|
-
return new ScopeRegistry().defineOwned("patter", this.host.patterLocalDecls);
|
|
1449
|
+
return new ScopeRegistry().defineOwned("patter", this.host.patterLocalDecls, "@patter.");
|
|
1346
1450
|
}
|
|
1347
1451
|
/**
|
|
1348
1452
|
* Seed a scene's `@scene` props (spec §7). The not-shared props seed THIS flow's
|
|
@@ -1353,27 +1457,18 @@ var Flow = class {
|
|
|
1353
1457
|
*/
|
|
1354
1458
|
seedScene(scene) {
|
|
1355
1459
|
const shared = this.host.sceneSharedNames.get(scene.id) ?? /* @__PURE__ */ new Set();
|
|
1460
|
+
const props = scene.sceneProps ?? [];
|
|
1356
1461
|
if (!this.sceneBags.has(scene.id)) {
|
|
1357
|
-
|
|
1358
|
-
for (const decl of scene.sceneProps ?? []) {
|
|
1359
|
-
const name = decl.name.toLowerCase();
|
|
1360
|
-
if (!shared.has(name)) bag[name] = sceneDefault(decl);
|
|
1361
|
-
}
|
|
1362
|
-
this.sceneBags.set(scene.id, bag);
|
|
1462
|
+
this.sceneBags.set(scene.id, new PropertyBag(props.filter((d) => !shared.has(d.name.toLowerCase()))));
|
|
1363
1463
|
}
|
|
1364
1464
|
if (!this.host.stageBags.has(scene.id)) {
|
|
1365
|
-
|
|
1366
|
-
for (const decl of scene.sceneProps ?? []) {
|
|
1367
|
-
const name = decl.name.toLowerCase();
|
|
1368
|
-
if (shared.has(name)) bag[name] = sceneDefault(decl);
|
|
1369
|
-
}
|
|
1370
|
-
this.host.stageBags.set(scene.id, bag);
|
|
1465
|
+
this.host.stageBags.set(scene.id, new PropertyBag(props.filter((d) => shared.has(d.name.toLowerCase()))));
|
|
1371
1466
|
}
|
|
1372
1467
|
for (const decl of scene.sceneProps ?? []) {
|
|
1373
1468
|
if (!decl.temporary) continue;
|
|
1374
1469
|
const name = decl.name.toLowerCase();
|
|
1375
1470
|
const bag = shared.has(name) ? this.host.stageBags.get(scene.id) : this.sceneBags.get(scene.id);
|
|
1376
|
-
if (bag) bag
|
|
1471
|
+
if (bag) bag.set(name, defaultFor(decl));
|
|
1377
1472
|
}
|
|
1378
1473
|
}
|
|
1379
1474
|
};
|
|
@@ -1411,47 +1506,13 @@ function deserialiseSelectors(rec) {
|
|
|
1411
1506
|
function toDecl(decl) {
|
|
1412
1507
|
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default };
|
|
1413
1508
|
}
|
|
1414
|
-
function declDefault(d) {
|
|
1415
|
-
if (d.default !== void 0) return d.default;
|
|
1416
|
-
switch (d.type) {
|
|
1417
|
-
case "number":
|
|
1418
|
-
return 0;
|
|
1419
|
-
case "string":
|
|
1420
|
-
return "";
|
|
1421
|
-
case "flags":
|
|
1422
|
-
return [];
|
|
1423
|
-
case "enum":
|
|
1424
|
-
return d.values?.[0] ?? "";
|
|
1425
|
-
case "quality":
|
|
1426
|
-
return d.stages?.[0] ?? "";
|
|
1427
|
-
default:
|
|
1428
|
-
return false;
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
1509
|
function toForeignDecl(decl) {
|
|
1432
1510
|
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default, writable: decl.writable };
|
|
1433
1511
|
}
|
|
1434
|
-
function hostScopeDefault(decl) {
|
|
1435
|
-
if (decl.default !== void 0) return decl.default;
|
|
1436
|
-
switch (decl.type) {
|
|
1437
|
-
case "boolean":
|
|
1438
|
-
return false;
|
|
1439
|
-
case "number":
|
|
1440
|
-
return 0;
|
|
1441
|
-
case "string":
|
|
1442
|
-
return "";
|
|
1443
|
-
case "flags":
|
|
1444
|
-
return [];
|
|
1445
|
-
case "enum":
|
|
1446
|
-
return decl.values?.[0] ?? "";
|
|
1447
|
-
case "quality":
|
|
1448
|
-
return decl.stages?.[0] ?? "";
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
1512
|
function selfBackedResolver(decls) {
|
|
1452
1513
|
const key = (name) => name.toLowerCase();
|
|
1453
1514
|
const bag = /* @__PURE__ */ new Map();
|
|
1454
|
-
for (const d of decls) bag.set(key(d.name),
|
|
1515
|
+
for (const d of decls) bag.set(key(d.name), defaultFor(d));
|
|
1455
1516
|
return {
|
|
1456
1517
|
get: (name) => bag.get(key(name)),
|
|
1457
1518
|
set: (name, value) => {
|
|
@@ -1459,23 +1520,6 @@ function selfBackedResolver(decls) {
|
|
|
1459
1520
|
}
|
|
1460
1521
|
};
|
|
1461
1522
|
}
|
|
1462
|
-
function sceneDefault(decl) {
|
|
1463
|
-
if (decl.default !== void 0) return decl.default;
|
|
1464
|
-
switch (decl.type) {
|
|
1465
|
-
case "boolean":
|
|
1466
|
-
return false;
|
|
1467
|
-
case "number":
|
|
1468
|
-
return 0;
|
|
1469
|
-
case "string":
|
|
1470
|
-
return "";
|
|
1471
|
-
case "flags":
|
|
1472
|
-
return [];
|
|
1473
|
-
case "enum":
|
|
1474
|
-
return decl.values?.[0] ?? "";
|
|
1475
|
-
case "quality":
|
|
1476
|
-
return decl.stages?.[0] ?? "";
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
1523
|
function truthy(v) {
|
|
1480
1524
|
if (typeof v === "boolean") return v;
|
|
1481
1525
|
if (typeof v === "number") return v !== 0;
|