@patterkit/cli 0.10.0 → 0.10.2
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/dist/cli.js +418 -105
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -18844,17 +18844,17 @@ var require_saxes = __commonJS({
|
|
|
18844
18844
|
this.fail(this.isName(entity) ? "undefined entity." : "disallowed character in entity name.");
|
|
18845
18845
|
return `&${entity};`;
|
|
18846
18846
|
}
|
|
18847
|
-
let
|
|
18847
|
+
let num2 = NaN;
|
|
18848
18848
|
if (entity[1] === "x" && /^#x[0-9a-f]+$/i.test(entity)) {
|
|
18849
|
-
|
|
18849
|
+
num2 = parseInt(entity.slice(2), 16);
|
|
18850
18850
|
} else if (/^#[0-9]+$/.test(entity)) {
|
|
18851
|
-
|
|
18851
|
+
num2 = parseInt(entity.slice(1), 10);
|
|
18852
18852
|
}
|
|
18853
|
-
if (!this.isChar(
|
|
18853
|
+
if (!this.isChar(num2)) {
|
|
18854
18854
|
this.fail("malformed character entity.");
|
|
18855
18855
|
return `&${entity};`;
|
|
18856
18856
|
}
|
|
18857
|
-
return String.fromCodePoint(
|
|
18857
|
+
return String.fromCodePoint(num2);
|
|
18858
18858
|
}
|
|
18859
18859
|
};
|
|
18860
18860
|
exports2.SaxesParser = SaxesParser;
|
|
@@ -45761,9 +45761,9 @@ var require_buffer_crc32 = __commonJS({
|
|
|
45761
45761
|
throw new Error("input must be buffer, number, or string, received " + typeof input);
|
|
45762
45762
|
}
|
|
45763
45763
|
}
|
|
45764
|
-
function bufferizeInt(
|
|
45764
|
+
function bufferizeInt(num2) {
|
|
45765
45765
|
var tmp = ensureBuffer(4);
|
|
45766
|
-
tmp.writeInt32BE(
|
|
45766
|
+
tmp.writeInt32BE(num2, 0);
|
|
45767
45767
|
return tmp;
|
|
45768
45768
|
}
|
|
45769
45769
|
function _crc32(buf, previous) {
|
|
@@ -47150,9 +47150,9 @@ var require_headers = __commonJS({
|
|
|
47150
47150
|
}
|
|
47151
47151
|
return 0;
|
|
47152
47152
|
};
|
|
47153
|
-
var indexOf = function(block,
|
|
47153
|
+
var indexOf = function(block, num2, offset, end) {
|
|
47154
47154
|
for (; offset < end; offset++) {
|
|
47155
|
-
if (block[offset] ===
|
|
47155
|
+
if (block[offset] === num2) return offset;
|
|
47156
47156
|
}
|
|
47157
47157
|
return end;
|
|
47158
47158
|
};
|
|
@@ -64132,8 +64132,8 @@ var require_base64_js2 = __commonJS({
|
|
|
64132
64132
|
}
|
|
64133
64133
|
return arr;
|
|
64134
64134
|
}
|
|
64135
|
-
function tripletToBase64(
|
|
64136
|
-
return lookup[
|
|
64135
|
+
function tripletToBase64(num2) {
|
|
64136
|
+
return lookup[num2 >> 18 & 63] + lookup[num2 >> 12 & 63] + lookup[num2 >> 6 & 63] + lookup[num2 & 63];
|
|
64137
64137
|
}
|
|
64138
64138
|
function encodeChunk(uint82, start, end) {
|
|
64139
64139
|
var tmp;
|
|
@@ -64242,16 +64242,16 @@ var require_tiny_inflate = __commonJS({
|
|
|
64242
64242
|
for (i = 0; i < 32; ++i) dt.trans[i] = i;
|
|
64243
64243
|
}
|
|
64244
64244
|
var offs = new Uint16Array(16);
|
|
64245
|
-
function tinf_build_tree(t, lengths2, off,
|
|
64245
|
+
function tinf_build_tree(t, lengths2, off, num2) {
|
|
64246
64246
|
var i, sum;
|
|
64247
64247
|
for (i = 0; i < 16; ++i) t.table[i] = 0;
|
|
64248
|
-
for (i = 0; i <
|
|
64248
|
+
for (i = 0; i < num2; ++i) t.table[lengths2[off + i]]++;
|
|
64249
64249
|
t.table[0] = 0;
|
|
64250
64250
|
for (sum = 0, i = 0; i < 16; ++i) {
|
|
64251
64251
|
offs[i] = sum;
|
|
64252
64252
|
sum += t.table[i];
|
|
64253
64253
|
}
|
|
64254
|
-
for (i = 0; i <
|
|
64254
|
+
for (i = 0; i < num2; ++i) {
|
|
64255
64255
|
if (lengths2[off + i]) t.trans[offs[lengths2[off + i]]++] = i;
|
|
64256
64256
|
}
|
|
64257
64257
|
}
|
|
@@ -64264,16 +64264,16 @@ var require_tiny_inflate = __commonJS({
|
|
|
64264
64264
|
d.tag >>>= 1;
|
|
64265
64265
|
return bit;
|
|
64266
64266
|
}
|
|
64267
|
-
function tinf_read_bits(d,
|
|
64268
|
-
if (!
|
|
64267
|
+
function tinf_read_bits(d, num2, base) {
|
|
64268
|
+
if (!num2)
|
|
64269
64269
|
return base;
|
|
64270
64270
|
while (d.bitcount < 24) {
|
|
64271
64271
|
d.tag |= d.source[d.sourceIndex++] << d.bitcount;
|
|
64272
64272
|
d.bitcount += 8;
|
|
64273
64273
|
}
|
|
64274
|
-
var val = d.tag & 65535 >>> 16 -
|
|
64275
|
-
d.tag >>>=
|
|
64276
|
-
d.bitcount -=
|
|
64274
|
+
var val = d.tag & 65535 >>> 16 - num2;
|
|
64275
|
+
d.tag >>>= num2;
|
|
64276
|
+
d.bitcount -= num2;
|
|
64277
64277
|
return val + base;
|
|
64278
64278
|
}
|
|
64279
64279
|
function tinf_decode_symbol(d, t) {
|
|
@@ -64296,7 +64296,7 @@ var require_tiny_inflate = __commonJS({
|
|
|
64296
64296
|
}
|
|
64297
64297
|
function tinf_decode_trees(d, lt, dt) {
|
|
64298
64298
|
var hlit, hdist, hclen;
|
|
64299
|
-
var i,
|
|
64299
|
+
var i, num2, length;
|
|
64300
64300
|
hlit = tinf_read_bits(d, 5, 257);
|
|
64301
64301
|
hdist = tinf_read_bits(d, 5, 1);
|
|
64302
64302
|
hclen = tinf_read_bits(d, 4, 4);
|
|
@@ -64306,27 +64306,27 @@ var require_tiny_inflate = __commonJS({
|
|
|
64306
64306
|
lengths[clcidx[i]] = clen;
|
|
64307
64307
|
}
|
|
64308
64308
|
tinf_build_tree(code_tree, lengths, 0, 19);
|
|
64309
|
-
for (
|
|
64309
|
+
for (num2 = 0; num2 < hlit + hdist; ) {
|
|
64310
64310
|
var sym = tinf_decode_symbol(d, code_tree);
|
|
64311
64311
|
switch (sym) {
|
|
64312
64312
|
case 16:
|
|
64313
|
-
var prev = lengths[
|
|
64313
|
+
var prev = lengths[num2 - 1];
|
|
64314
64314
|
for (length = tinf_read_bits(d, 2, 3); length; --length) {
|
|
64315
|
-
lengths[
|
|
64315
|
+
lengths[num2++] = prev;
|
|
64316
64316
|
}
|
|
64317
64317
|
break;
|
|
64318
64318
|
case 17:
|
|
64319
64319
|
for (length = tinf_read_bits(d, 3, 3); length; --length) {
|
|
64320
|
-
lengths[
|
|
64320
|
+
lengths[num2++] = 0;
|
|
64321
64321
|
}
|
|
64322
64322
|
break;
|
|
64323
64323
|
case 18:
|
|
64324
64324
|
for (length = tinf_read_bits(d, 7, 11); length; --length) {
|
|
64325
|
-
lengths[
|
|
64325
|
+
lengths[num2++] = 0;
|
|
64326
64326
|
}
|
|
64327
64327
|
break;
|
|
64328
64328
|
default:
|
|
64329
|
-
lengths[
|
|
64329
|
+
lengths[num2++] = sym;
|
|
64330
64330
|
break;
|
|
64331
64331
|
}
|
|
64332
64332
|
}
|
|
@@ -190959,11 +190959,11 @@ var require_b64 = __commonJS({
|
|
|
190959
190959
|
}
|
|
190960
190960
|
function uint8ToBase64(uint82) {
|
|
190961
190961
|
var i, extraBytes = uint82.length % 3, output = "", temp, length;
|
|
190962
|
-
function encode(
|
|
190963
|
-
return lookup.charAt(
|
|
190962
|
+
function encode(num2) {
|
|
190963
|
+
return lookup.charAt(num2);
|
|
190964
190964
|
}
|
|
190965
|
-
function tripletToBase64(
|
|
190966
|
-
return encode(
|
|
190965
|
+
function tripletToBase64(num2) {
|
|
190966
|
+
return encode(num2 >> 18 & 63) + encode(num2 >> 12 & 63) + encode(num2 >> 6 & 63) + encode(num2 & 63);
|
|
190967
190967
|
}
|
|
190968
190968
|
for (i = 0, length = uint82.length - extraBytes; i < length; i += 3) {
|
|
190969
190969
|
temp = (uint82[i] << 16) + (uint82[i + 1] << 8) + uint82[i + 2];
|
|
@@ -197906,6 +197906,9 @@ function usedQualifiers(project, scenes) {
|
|
|
197906
197906
|
}
|
|
197907
197907
|
return projectQualifiers(project).filter((q) => used.has(q.gameId));
|
|
197908
197908
|
}
|
|
197909
|
+
var DEFAULT_PAD_AFTER = 0.6;
|
|
197910
|
+
var PAD_AFTER_MIN = -10;
|
|
197911
|
+
var PAD_AFTER_MAX = 60;
|
|
197909
197912
|
var DEFAULT_WRITING_STATUSES = [
|
|
197910
197913
|
{ name: "stub", colour: 0 },
|
|
197911
197914
|
// red
|
|
@@ -198073,11 +198076,44 @@ function validateProject(input) {
|
|
|
198073
198076
|
const sceneGameIds = /* @__PURE__ */ new Map();
|
|
198074
198077
|
const jumps = [];
|
|
198075
198078
|
const castNames = new Set((project.cast ?? []).map((c2) => c2.name));
|
|
198076
|
-
const qualifierIds = new Set(projectQualifiers(project).
|
|
198079
|
+
const qualifierIds = project.qualifiers === void 0 || Array.isArray(project.qualifiers) ? new Set(projectQualifiers(project).flatMap((q) => isQualifierEntry(q) && typeof q.gameId === "string" ? [q.gameId] : [])) : null;
|
|
198080
|
+
const checkPad = (value, what, id) => {
|
|
198081
|
+
if (value === void 0) return;
|
|
198082
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < PAD_AFTER_MIN || value > PAD_AFTER_MAX) {
|
|
198083
|
+
issues.push({ code: "invalid-pad", message: `${what} pause '${String(value)}' must be a number of seconds from ${PAD_AFTER_MIN} to ${PAD_AFTER_MAX}`, id });
|
|
198084
|
+
}
|
|
198085
|
+
};
|
|
198086
|
+
const checkSeamPad = (beats) => {
|
|
198087
|
+
const last2 = (beats ?? []).at(-1);
|
|
198088
|
+
if (!last2 || last2.kind !== "line" && last2.kind !== "text" || typeof last2.padAfter !== "number" || last2.padAfter >= 0) return;
|
|
198089
|
+
issues.push({
|
|
198090
|
+
code: "pad-overlaps-seam",
|
|
198091
|
+
severity: "warning",
|
|
198092
|
+
id: last2.id,
|
|
198093
|
+
message: `beat '${last2.id}' is its snippet's last line, so its negative pause can't cut in on what follows; it plays as no pause`
|
|
198094
|
+
});
|
|
198095
|
+
};
|
|
198096
|
+
const checkPromptPad = (option) => {
|
|
198097
|
+
const p = option.prompt;
|
|
198098
|
+
if (!p || p.kind !== "line" && p.kind !== "text" || typeof p.padAfter !== "number" || p.padAfter >= 0) return;
|
|
198099
|
+
if ((option.selector ?? "run") !== "run" || !Array.isArray(option.children)) return;
|
|
198100
|
+
const first2 = option.children[0];
|
|
198101
|
+
if (first2 && (first2.type !== "snippet" || first2.condition || (first2.beats?.length ?? 0) > 0)) return;
|
|
198102
|
+
issues.push({
|
|
198103
|
+
code: "prompt-pad-without-beat",
|
|
198104
|
+
severity: "warning",
|
|
198105
|
+
id: p.id,
|
|
198106
|
+
message: `prompt '${p.id}' has a negative pause, but its option plays nothing before it moves on, so nothing cuts in on it`
|
|
198107
|
+
});
|
|
198108
|
+
};
|
|
198077
198109
|
const checkQualifier = (beat, what) => {
|
|
198078
|
-
if (beat.kind
|
|
198110
|
+
if (beat.kind !== "line" || beat.qualifier === void 0) return;
|
|
198111
|
+
if (qualifierIds && !qualifierIds.has(beat.qualifier)) {
|
|
198079
198112
|
issues.push({ code: "unknown-qualifier", message: `${what} '${beat.id}' qualifier '${beat.qualifier}' is not in the project's qualifiers`, id: beat.id });
|
|
198080
198113
|
}
|
|
198114
|
+
if (!beat.character) {
|
|
198115
|
+
issues.push({ code: "qualifier-without-speaker", severity: "warning", message: `${what} '${beat.id}' has the qualifier '${beat.qualifier}' but no speaker`, id: beat.id });
|
|
198116
|
+
}
|
|
198081
198117
|
};
|
|
198082
198118
|
const checkTags = (tags, where, id) => {
|
|
198083
198119
|
for (const t of tags ?? []) {
|
|
@@ -198103,6 +198139,7 @@ function validateProject(input) {
|
|
|
198103
198139
|
validateProjectFile(project, issues);
|
|
198104
198140
|
for (const scene of scenes) {
|
|
198105
198141
|
seeId(scene.id, "scene", "project");
|
|
198142
|
+
checkPad(scene.padAfterDefault, `scene '${scene.id}' default`, scene.id);
|
|
198106
198143
|
const where = `scene '${scene.id}'`;
|
|
198107
198144
|
if (typeof scene.id === "string") addressable.add(scene.id);
|
|
198108
198145
|
if (!scene.name?.trim()) {
|
|
@@ -198138,6 +198175,7 @@ function validateProject(input) {
|
|
|
198138
198175
|
}
|
|
198139
198176
|
for (const block of scene.blocks) {
|
|
198140
198177
|
seeId(block.id, "block", where);
|
|
198178
|
+
checkPad(block.padAfterDefault, `block '${block.id}' default`, block.id);
|
|
198141
198179
|
checkTags(block.tags, `block '${block.id}'`, block.id);
|
|
198142
198180
|
if (typeof block.id === "string") addressable.add(block.id);
|
|
198143
198181
|
if (!block.name?.trim()) {
|
|
@@ -198178,6 +198216,12 @@ function validateProject(input) {
|
|
|
198178
198216
|
walkNodes(block.children, (node) => {
|
|
198179
198217
|
const inBlock = `block '${block.id}'`;
|
|
198180
198218
|
checkTags(node.tags, `${node.type} '${node.id}'`, node.id);
|
|
198219
|
+
checkPad(node.padAfterDefault, `${node.type} '${node.id}' default`, node.id);
|
|
198220
|
+
if (node.type === "group" && node.prompt) checkPad(node.prompt.padAfter, `prompt '${node.prompt.id}'`, node.prompt.id);
|
|
198221
|
+
if (node.type === "snippet") {
|
|
198222
|
+
for (const beat of node.beats ?? []) if (beat.kind !== "gameEvent") checkPad(beat.padAfter, `beat '${beat.id}'`, beat.id);
|
|
198223
|
+
checkSeamPad(node.beats);
|
|
198224
|
+
}
|
|
198181
198225
|
if (node.type === "group") {
|
|
198182
198226
|
seeId(node.id, "group", inBlock);
|
|
198183
198227
|
if (!Array.isArray(node.children)) {
|
|
@@ -198203,6 +198247,7 @@ function validateProject(input) {
|
|
|
198203
198247
|
issues.push({ code: "unknown-character", message: `prompt '${p.id}' speaker '${p.character}' is not in the project cast`, id: p.id });
|
|
198204
198248
|
}
|
|
198205
198249
|
checkQualifier(p, "prompt");
|
|
198250
|
+
checkPromptPad(opt);
|
|
198206
198251
|
}
|
|
198207
198252
|
}
|
|
198208
198253
|
if (fallbacks > 1) {
|
|
@@ -198254,6 +198299,9 @@ function validateProject(input) {
|
|
|
198254
198299
|
}
|
|
198255
198300
|
return issues;
|
|
198256
198301
|
}
|
|
198302
|
+
function isQualifierEntry(q) {
|
|
198303
|
+
return typeof q === "object" && q !== null && !Array.isArray(q);
|
|
198304
|
+
}
|
|
198257
198305
|
function validateProjectFile(project, issues) {
|
|
198258
198306
|
checkDecls(project.properties, "project properties", issues);
|
|
198259
198307
|
for (const scope of project.scopeRegistry?.scopes ?? []) {
|
|
@@ -198305,8 +198353,20 @@ function validateProjectFile(project, issues) {
|
|
|
198305
198353
|
if (project.recordingStatuses) {
|
|
198306
198354
|
checkLadderNames(project.recordingStatuses.map((s) => s.name), "recordingStatuses", issues);
|
|
198307
198355
|
}
|
|
198356
|
+
const pad2 = project.padAfterDefault;
|
|
198357
|
+
if (pad2 !== void 0 && (typeof pad2 !== "number" || !Number.isFinite(pad2) || pad2 < PAD_AFTER_MIN || pad2 > PAD_AFTER_MAX)) {
|
|
198358
|
+
issues.push({ code: "invalid-pad", message: `the project default pause '${String(pad2)}' must be a number of seconds from ${PAD_AFTER_MIN} to ${PAD_AFTER_MAX}` });
|
|
198359
|
+
}
|
|
198308
198360
|
const seenQualifiers = /* @__PURE__ */ new Set();
|
|
198309
|
-
|
|
198361
|
+
const qualifiers = project.qualifiers;
|
|
198362
|
+
if (qualifiers !== void 0 && !Array.isArray(qualifiers)) {
|
|
198363
|
+
issues.push({ code: "invalid-qualifier", message: "qualifiers must be a list" });
|
|
198364
|
+
}
|
|
198365
|
+
for (const q of Array.isArray(qualifiers) ? qualifiers : []) {
|
|
198366
|
+
if (!isQualifierEntry(q)) {
|
|
198367
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q)}' is not a qualifier (a name and an address)` });
|
|
198368
|
+
continue;
|
|
198369
|
+
}
|
|
198310
198370
|
if (typeof q.name !== "string" || !q.name.trim()) {
|
|
198311
198371
|
issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q.gameId)}' has no name` });
|
|
198312
198372
|
}
|
|
@@ -199747,6 +199807,8 @@ function compileNode(node, foreign) {
|
|
|
199747
199807
|
shared: node.shared,
|
|
199748
199808
|
options: node.options,
|
|
199749
199809
|
children: node.children.map((c2) => compileNode(c2, foreign)),
|
|
199810
|
+
padAfterDefault: node.padAfterDefault,
|
|
199811
|
+
// line padding: the default for the beats inside
|
|
199750
199812
|
gameData: node.gameData,
|
|
199751
199813
|
tags: node.tags,
|
|
199752
199814
|
// author tags (#215), accumulated at runtime
|
|
@@ -199764,6 +199826,8 @@ function compileNode(node, foreign) {
|
|
|
199764
199826
|
beats: node.beats,
|
|
199765
199827
|
onEnter: node.onEnter?.map((e) => compileEffect(e, foreign)),
|
|
199766
199828
|
onExit: node.onExit?.map((e) => compileEffect(e, foreign)),
|
|
199829
|
+
padAfterDefault: node.padAfterDefault,
|
|
199830
|
+
// line padding: the default for the beats inside
|
|
199767
199831
|
gameData: node.gameData,
|
|
199768
199832
|
tags: node.tags,
|
|
199769
199833
|
// author tags (#215)
|
|
@@ -199781,6 +199845,8 @@ function compileBlock(block, foreign) {
|
|
|
199781
199845
|
...block.gameId ? { gameId: block.gameId } : {},
|
|
199782
199846
|
// host-facing address; runtime falls back to the name slug
|
|
199783
199847
|
children: block.children.map((c2) => compileNode(c2, foreign)),
|
|
199848
|
+
padAfterDefault: block.padAfterDefault,
|
|
199849
|
+
// line padding: the default for the beats inside
|
|
199784
199850
|
gameData: block.gameData,
|
|
199785
199851
|
tags: block.tags
|
|
199786
199852
|
// author tags (#215)
|
|
@@ -199793,6 +199859,8 @@ function compileScene(scene, foreign) {
|
|
|
199793
199859
|
name: scene.name,
|
|
199794
199860
|
...scene.gameId ? { gameId: scene.gameId } : {},
|
|
199795
199861
|
// host-facing address; runtime falls back to the name slug
|
|
199862
|
+
padAfterDefault: scene.padAfterDefault,
|
|
199863
|
+
// line padding: the default for the beats inside
|
|
199796
199864
|
gameData: scene.gameData,
|
|
199797
199865
|
tags: scene.tags,
|
|
199798
199866
|
// author tags (#215)
|
|
@@ -199858,7 +199926,8 @@ function exportBundle(input) {
|
|
|
199858
199926
|
strings,
|
|
199859
199927
|
properties: project.properties ?? [],
|
|
199860
199928
|
...foreignScopes ? { scopeRegistry: foreignScopes } : {},
|
|
199861
|
-
...qualifiers.length > 0 ? { qualifiers } : {}
|
|
199929
|
+
...qualifiers.length > 0 ? { qualifiers } : {},
|
|
199930
|
+
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {}
|
|
199862
199931
|
}, { trailingComma: false })),
|
|
199863
199932
|
// Structure-only fingerprint (live bundle refresh): the same hash with the string tables left
|
|
199864
199933
|
// out. Same structureHash + a different hash = a text-only edit, safe to swap in place with
|
|
@@ -199868,7 +199937,8 @@ function exportBundle(input) {
|
|
|
199868
199937
|
scenes: scenesOut,
|
|
199869
199938
|
properties: project.properties ?? [],
|
|
199870
199939
|
...foreignScopes ? { scopeRegistry: foreignScopes } : {},
|
|
199871
|
-
...qualifiers.length > 0 ? { qualifiers } : {}
|
|
199940
|
+
...qualifiers.length > 0 ? { qualifiers } : {},
|
|
199941
|
+
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {}
|
|
199872
199942
|
}, { trailingComma: false }))
|
|
199873
199943
|
},
|
|
199874
199944
|
voiced: project.voiced ?? false,
|
|
@@ -199885,6 +199955,8 @@ function exportBundle(input) {
|
|
|
199885
199955
|
return m;
|
|
199886
199956
|
}),
|
|
199887
199957
|
...qualifiers.length > 0 ? { qualifiers } : {},
|
|
199958
|
+
// The project's own line-padding default, only when it sets one: absent, every runtime uses the built-in one.
|
|
199959
|
+
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {},
|
|
199888
199960
|
properties: project.properties,
|
|
199889
199961
|
scopeRegistry: hostScopes,
|
|
199890
199962
|
gameDataFields: project.gameDataFields,
|
|
@@ -200498,6 +200570,9 @@ function mergeProject(base, ours, theirs) {
|
|
|
200498
200570
|
} else if (k === "gameDataFields") {
|
|
200499
200571
|
const v = mergeGameDataFields(asMap(base[k]), asMap(ours[k]), asMap(theirs[k]), conflicts);
|
|
200500
200572
|
if (Object.keys(v).length > 0) merged[k] = v;
|
|
200573
|
+
} else if (k === "qualifiers") {
|
|
200574
|
+
const v = mergeQualifiers(base[k], ours[k], theirs[k], conflicts);
|
|
200575
|
+
if (v !== void 0) merged[k] = v;
|
|
200501
200576
|
} else if (k === "locales") {
|
|
200502
200577
|
merged.locales = mergeLocales(asMap(base.locales), asMap(ours.locales), asMap(theirs.locales), conflicts);
|
|
200503
200578
|
} else {
|
|
@@ -200515,6 +200590,23 @@ function mergeGameDataFields(b, o, t, conflicts) {
|
|
|
200515
200590
|
}
|
|
200516
200591
|
return out;
|
|
200517
200592
|
}
|
|
200593
|
+
function mergeQualifiers(base, ours, theirs, conflicts) {
|
|
200594
|
+
if (base === void 0 && ours === void 0 && theirs === void 0) return void 0;
|
|
200595
|
+
const keyed = (arr) => new Set(arr.map((q) => isObj(q) && typeof q.gameId === "string" ? q.gameId : null)).size === arr.length && arr.every((q) => isObj(q) && typeof q.gameId === "string");
|
|
200596
|
+
const list = (v) => v === void 0 ? DEFAULT_QUALIFIERS : Array.isArray(v) && keyed(v) ? v : null;
|
|
200597
|
+
const b = list(base), o = list(ours), t = list(theirs);
|
|
200598
|
+
if (!b || !o || !t) return merge3(base, ours, theirs, "", "qualifiers", conflicts);
|
|
200599
|
+
const byId = (arr) => {
|
|
200600
|
+
const m = {};
|
|
200601
|
+
for (const q of arr) if (isObj(q) && typeof q.gameId === "string") m[q.gameId] = q;
|
|
200602
|
+
return m;
|
|
200603
|
+
};
|
|
200604
|
+
const merged = mergeMap(byId(b), byId(o), byId(t), "qualifiers", conflicts);
|
|
200605
|
+
const order = [];
|
|
200606
|
+
for (const q of [...o, ...t]) if (isObj(q) && typeof q.gameId === "string" && q.gameId in merged && !order.includes(q.gameId)) order.push(q.gameId);
|
|
200607
|
+
const out = order.map((id) => merged[id]);
|
|
200608
|
+
return eq(out, DEFAULT_QUALIFIERS) ? void 0 : out;
|
|
200609
|
+
}
|
|
200518
200610
|
function mergeLocales(b, o, t, conflicts) {
|
|
200519
200611
|
const out = {};
|
|
200520
200612
|
const def = merge3(b.default, o.default, t.default, "", "locales.default", conflicts);
|
|
@@ -201276,7 +201368,55 @@ function planBuild(loaded, opts = {}) {
|
|
|
201276
201368
|
}
|
|
201277
201369
|
|
|
201278
201370
|
// ../ops/src/playable-runtime.ts
|
|
201279
|
-
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var W=Object.defineProperty;var $e=Object.getOwnPropertyDescriptor;var He=Object.getOwnPropertyNames;var _e=Object.prototype.hasOwnProperty;var Ue=(n,e)=>{for(var t in e)W(n,t,{get:e[t],enumerable:!0})},We=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of He(e))!_e.call(n,s)&&s!==t&&W(n,s,{get:()=>e[s],enumerable:!(r=$e(e,s))||r.enumerable});return n};var Je=n=>We(W({},"__esModule",{value:!0}),n);var gt={};Ue(gt,{Checkpoint:()=>T,Engine:()=>_,Flow:()=>D,buildTagIndex:()=>H,describeBundle:()=>Ge,effectiveGameData:()=>Ve,gameDataFields:()=>je,gameDataValue:()=>ie});function R(n,e){return n!==void 0&&Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0}function k(n){switch(n[0]){case"b":return{kind:"bool",value:n[1]};case"n":return{kind:"number",value:n[1]};case"s":return{kind:"string",value:n[1]};case"sv":return{kind:"scopedvar",scope:n[1],name:n[2]};case"u":return{kind:"unary",op:n[1],operand:k(n[2])};case"bin":return{kind:"binary",op:n[1],left:k(n[2]),right:k(n[3])};case"call":{let e=n.slice(2).map(k);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var g=class extends Error{constructor(e){super(e),this.name="EvalError"}};function V(n,e,t){let r=new Map(t.scopes.map(i=>[i.token,i.missing??"false"])),s=i=>{switch(i.kind){case"bool":return i.value;case"number":return i.value;case"string":return i.value;case"scopedvar":{let o=R(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):R(o,i.name);if(a===void 0){if(r.get(i.scope)==="throw")throw new g(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{if(i.name==="advance"&&!R(t.functions,i.name)){let a=i.args[0];if(i.args.length!==1||a===void 0)throw new g(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=J(a,e);if(c===void 0)throw new g("advance() needs a quality reference (@scope.name of a quality property)");let l=w(s(a),c,"advance");return c[Math.min(l+1,c.length-1)]}let o=R(t.functions,i.name);if(!o)throw new g(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new g("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=s(i.operand);if(typeof a!="boolean")throw new g(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new g(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let f=s(i.left);if(typeof f!="boolean")throw new g(`\'and\' requires boolean operands, left is ${typeof f}`);if(!f)return!1;let u=s(i.right);if(typeof u!="boolean")throw new g(`\'and\' requires boolean operands, right is ${typeof u}`);return u}if(i.op==="or"){let f=s(i.left);if(typeof f!="boolean")throw new g(`\'or\' requires boolean operands, left is ${typeof f}`);if(f)return!0;let u=s(i.right);if(typeof u!="boolean")throw new g(`\'or\' requires boolean operands, right is ${typeof u}`);return u}let o=s(i.left),a=s(i.right),c=J(i.left,e),l=J(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!Qe(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new g(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return w(o,p,">")>w(a,p,">");case">=":return w(o,p,">=")>=w(a,p,">=");case"<":return w(o,p,"<")<w(a,p,"<");case"<=":return w(o,p,"<=")<=w(a,p,"<=");case"+":case"-":case"*":case"/":throw new g(`\'${i.op}\' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(i.op){case"==":return ge(o,a);case"!=":return!ge(o,a);case">":return E(o,a,">"),o>a;case">=":return E(o,a,">="),o>=a;case"<":return E(o,a,"<"),o<a;case"<=":return E(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new g(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return E(o,a,"-"),o-a;case"*":return E(o,a,"*"),o*a;case"/":if(E(o,a,"/"),a===0)throw new g("division by zero");return o/a}}}};return s(n)}function ge(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;let t=[...n].sort(),r=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==r[s])return!1;return!0}return n===e}function E(n,e,t){if(typeof n!="number"||typeof e!="number")throw new g(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function J(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function w(n,e,t){if(typeof n!="string")throw new g(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new g(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var Qe=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function O(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function Q(n){let e=O(n);return{next(){e=e+1831565813>>>0;let t=Math.imul(e^e>>>15,1|e);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296},state(){return e}}}var Ke={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},ze=[Ke];function ue(n,e,t){let r=t?.countingCalls??ze;return L(n,t?.want??!0,e,r)}function L(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=L(n.left,e,t,r),i=L(n.right,e,t,r);return n.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(n.kind==="unary"&&n.op==="not")return L(n.operand,!e,t,r);if(n.kind==="call"){let s=r.find(i=>i.name===n.name);if(s){let i=s.count(n),o=t(n);return e?o?i:0:o?0:1}}return t(n)===e?1:0}var b=class n{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let r=this.norm(t.name);this.decls.set(r,t),this.values[r]=structuredClone(t.default??P(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,r){let s=this.norm(e);if(!r?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:r?.silent??!1,reason:r?.reason};this.values[s]=t;for(let o of[...this.auditors])o(i);if(!i.silent)for(let o of[...this.subscribers])o(i);return i}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}onAudit(e){return this.auditors.add(e),()=>this.auditors.delete(e)}rows(){return[...this.decls.entries()].map(([e,t])=>fe(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm,pathPrefix:this.pathPrefix});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e){for(let[t,r]of Object.entries(e))this.values[this.norm(t)]=r}};function fe(n,e,t,r,s=""){let i=r??n.name.toLowerCase();return{name:i,path:s+i,type:n.type,value:e,default:n.default??P(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var Ye=n=>n.toLowerCase(),K=1,M=class{scopes=new Map;parked=new Map;rev=0;get revision(){return this.rev}defineOwned(e,t,r){let s=typeof r=="string"?{pathPrefix:r}:r??{},i=new b(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,i,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,r){this.assertFree(e,r?.owner),this.scopes.set(e,{kind:"owned",bag:t,...r?.owner!==void 0?{owner:r.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(t.load(s),this.parked.delete(e)),this}remove(e,t){let r=this.scopes.get(e);if(!r)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&r.kind==="owned"&&this.parked.set(e,r.bag.save()),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&this.parked.delete(t);return this}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??Ye,a=new Map;for(let c of r)a.set(o(c.name),c);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:i.writable??!0,norm:o,...i.owner!==void 0?{owner:i.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let r=this.scopes.get(e);if(r)return r.kind==="owned"?r.bag.get(t):r.resolver.get(r.norm(t))}set(e,t,r,s){let i=this.scopes.get(e);if(!i)throw new Error(`unknown scope \'@${e}\'`);if(i.kind==="owned"){if(!s?.host&&!i.bag.writable(t))throw new Error(`\'@${e}.${t}\' is read-only`);i.bag.set(t,r,s?.host?{host:!0}:void 0);return}let o=i.norm(t);if(!i.resolver.set)throw new Error(`\'@${e}.${t}\' is read-only`);if(!s?.host&&!this.foreignWritable(i,o))throw new Error(`\'@${e}.${t}\' is read-only`);i.resolver.set(o,r)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,r]of this.scopes){let s=r.owner!==void 0?{owner:r.owner}:{};if(r.kind==="owned")for(let i of r.bag.rows())e.push({scope:t,...s,...i});else for(let[i,o]of r.decls)e.push({scope:t,...s,...fe(o,r.resolver.get(i),this.foreignWritable(r,i),i,`${t}.`)})}return e}toEvalContext(e,t){let r=this.view(t?.aliases),s={};for(let[o,a]of r)s[o]=a.kind==="owned"?a.bag.values:a.resolver;let i=this.qualityLadders(r);return i.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(o,a)=>{let c=r.get(o);return c?i.get(o)?.get(Xe(c)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[r,s]of Object.entries(e??{})){let i=this.scopes.get(s);if(!i)throw new Error(`alias \'@${r}\' names \'${s}\', which is not registered`);t.set(r,i)}return t}qualityLadders(e){let t=new Map;for(let[r,s]of e)for(let[i,o]of he(s)){if(o.type!=="quality"||o.stages===void 0)continue;let a=t.get(r);a||(a=new Map,t.set(r,a)),a.set(i,o.stages)}return t}toSchema(e){let t=new Map;for(let[r,s]of this.view(e?.aliases)){let i=he(s);if(i.length===0)continue;let o=new Map;for(let[a,c]of i)o.set(a,{type:c.type,enumValues:c.values,...c.stages!==void 0?{stages:c.stages}:{}});t.set(r,o)}return{properties:t}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());for(let[t,r]of this.parked)e[t]=structuredClone(r);return e}load(e,t){t?.keepParked||this.parked.clear();for(let[r,s]of Object.entries(e)){let i=this.scopes.get(r);i?.kind==="owned"?i.bag.load(s):i||this.parked.set(r,structuredClone(s))}}saveFragment(){return{version:K,scopes:this.save()}}loadFragment(e){if(e.version!==K)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${K})`);this.load(e.scopes)}assertFree(e,t){let r=this.scopes.get(e);if(!r)return;let s=r.owner!==void 0?` by ${r.owner}`:"",i=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope \'@${e}\' is already registered${s}${i}`)}};function he(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function Xe(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function P(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return n.values?.[0]??"";case"flags":return[];case"quality":return n.stages?.[0]??"";default:return!1}}var me=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function B(n){return n.ctx.host??{}}var z={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new g("random(a, b) requires exactly 2 arguments");let t=B(e).nextRandom;if(!t)throw new g("random() called without a PRNG in context");let r=e.evaluate(n[0]),s=e.evaluate(n[1]);if(typeof r!="number"||typeof s!="number")throw new g("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new g("random(a, b) arguments must be integers");let i=Math.min(r,s),o=Math.max(r,s);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:ye("check_flags"),eval(n,e){let t=Se(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new g("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:ye("set_flags"),eval(n,e){let t=[...Se(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new g("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let i=t.indexOf(s.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:$("visits"),eval:(n,e)=>B(e).visits?.(q(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:$("seen"),eval:(n,e)=>(B(e).visits?.(q(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:$("patter_visits"),eval:(n,e)=>B(e).patterVisits?.(q(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:$("patter_seen"),eval:(n,e)=>(B(e).patterVisits?.(q(n,e,"patter_seen"))??0)>0}}},Pt=me.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function ve(n,e){let t=n.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function q(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new g(`${t}(id) requires a string node id`);return r}function $(n){return(e,t)=>{let r=e[0];r&&r.kind!=="string"&&t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(id): the argument must be a string id literal (a scene / block / node id)`})}}function Se(n,e,t){if(!n)throw new g(`${t}() requires at least one argument (the flags variable)`);let r=e.evaluate(n);if(Array.isArray(r))return r;if(r===!1||r===null||r===void 0)return[];throw new g(`${t}() first argument must be a flags property`)}function ye(n){return(e,t)=>{if(e.length===0)return;let r=e[0];if(r.kind!=="scopedvar"){t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): first argument must be a flags property reference (@name or @scope.name)`});return}let s=t.schema.properties.get(r.scope)?.get(r.name);if(s&&s.type!=="flags"){let i=r.scope===t.defaultScope?r.name:`${r.scope}.${r.name}`;t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): \'@${i}\' is not a flags property (got ${s.type})`});return}for(let i=1;i<e.length;i++){let o=e[i];o.kind!=="flagdelta"?t.report({path:[...t.path,"args",i],kind:"wrong-arg-type",severity:"error",message:`${n}(): argument ${i+1} must be +flagName or -flagName`}):s?.type==="flags"&&s.enumValues&&!s.enumValues.includes(o.name)&&t.report({path:[...t.path,"args",i],kind:"unknown-flag-name",severity:"error",message:`${n}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var Ze=/^@[A-Za-z0-9_.]+$/;function*et(n){let e="",t=0;for(;t<n.length;){let r=n[t];if(r==="{"&&n[t+1]==="{"){e+="{",t+=2;continue}if(r==="}"&&n[t+1]==="}"){e+="}",t+=2;continue}if(r==="{"){let s=n.indexOf("}",t+1);if(s!==-1){let i=n.slice(t,s+1),o=n.slice(t+1,s).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:Ze.test(o)?o:void 0},t=s+1;continue}e+=i,t=s+1;continue}}e+=r,t+=1}e&&(yield{kind:"text",value:e})}function tt(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function rt(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function nt(n){let e="",t=!1;for(let r of n){if(rt(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function we(n,e,t){if(e.length===0||n.indexOf(e)<0)return n;let r="",s=0,i=!1;for(;s<n.length;){if(n.startsWith(e,s)){let o=n.indexOf(t,s+e.length);if(o>=0){s=o+t.length,i=!0;continue}r+=n.slice(s);break}r+=n[s],s+=1}return i?nt(r):n}function be(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of et(n)){if(r.kind==="text"){t+=r.value;continue}if(!r.ref){t+=r.raw;continue}let s=e(r.ref);t+=s===void 0?"":tt(s)}return t}function I(n,e){for(let t of n){e(t);let r=t.children;r&&I(r,e)}}function st(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function S(n){let e=n.gameId?.trim();return e||st(n.name)}function Y(n){return`cast:${n}`}function X(n){return`qualifier:${n}`}var Z={open:"[",close:"]"},xe="SFX";var ee=3;function N(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function H(n){let e=new Map,t=(r,s)=>{let i=N([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group"&&r.prompt&&e.set(r.prompt.id,N([...i,...r.prompt.tags??[]])),r.type==="group")for(let o of r.children)t(o,i);else for(let o of r.beats??[])e.set(o.id,N([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=N(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=N([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var ke=new WeakMap,Ee=new WeakMap;function it(n){let e=Ee.get(n);return e||(e=H(n),Ee.set(n,e)),e}function C(n,e){return n.tagIndex.get(e)?.slice()}var A="Patter",F=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),Ce=Symbol("ownsRegistry"),y={stage:n=>`patter/scene/${F(n)}`,flow:n=>`patter/flow/${F(n)}/`,flowGlobals:n=>`patter/flow/${F(n)}/patter`,flowScene:(n,e)=>`patter/flow/${F(n)}/scene/${F(e)}`},T=class{constructor(e){this.journal=e}journal},_=class n{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;engineLog=[];engineSeq=0;engineTraceHandlers=new Set;constructor(e,t={}){this.creationOptions=t;let r=t.locale??e.locales.default,s=e.strings;this.allStrings=s,this.currentLocale=r;let i=s[r]??{},o=s[e.locales.default]??{},a=e.localisation,c=a?.mode==="ids"&&!a.sourceDebug;this.sourceDebug=a?.mode==="ids"&&!!a.sourceDebug,this.sourceDebug&&typeof console<"u"&&console.warn("[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.");let l=new Map;for(let d of e.cast??[])d.displayName&&l.set(d.name,d.displayName);let p=new Map;for(let d of e.qualifiers??[])d?.gameId&&p.set(d.gameId,d.name);this.defaultSeed=(t.seed??2654435769)>>>0;let f=new Map,u=new Map,oe=new Map;for(let[d,h]of Object.entries(e.scenes)){this.sceneGameIdToId.set(S(h),d);let v=new Map;for(let m of h.blocks)u.set(m.id,{sceneId:d}),oe.set(m.id,m),v.set(S(m),m.id),I(m.children,j=>f.set(j.id,j));this.blockGameIdToId.set(d,v)}let ae=e.properties??[],U=ae.filter(d=>d.shared??!0).map(Oe),Le=ae.filter(d=>!(d.shared??!0)).map(Oe),Me=new Set(U.map(d=>d.name.toLowerCase())),x=t.registry??new M,ce=!t.registry||t[Ce]===!0,le=new b(U,{pathPrefix:"@patter."}),pe=[],G=[];try{x.mountOwned("patter",le,{owner:A}),G.push("patter");let d=new Set;for(let[h,v]of Object.entries(Pe(t))){let m=e.scopeRegistry?.scopes.find(qe=>qe.token===h),j=(m?.declarations??[]).map(Ae);x.defineForeign(h,v,j,{writable:m?.writable??!0,owner:A}),G.push(h),d.add(h)}for(let h of ce?e.scopeRegistry?.scopes??[]:[]){if(d.has(h.token)||x.has(h.token))continue;let v=(h.declarations??[]).map(m=>at(m,h.writable));x.defineOwned(h.token,v,{owner:A}),G.push(h.token),pe.push(h.token)}}catch(d){for(let h of G)x.remove(h,{keep:!0});throw d}let de=new Map;for(let[d,h]of Object.entries(e.scenes)){let v=new Set((h.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));de.set(d,v)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(d,h,v)=>this.emitEngine(d,h,v),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:p,nodeIndex:f,blockIndex:u,blockById:oe,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:it(e),registry:x,ownsRegistry:ce,patterBag:le,hostScopes:pe,patterSharedDecls:U,patterLocalDecls:Le,patterSharedNames:Me,sceneSharedNames:de,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(d=>console.warn(`Patterplay: ${d.kind} on ${d.node} in flow \'${d.flow}\' failed, played through: ${d.message}`)),replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??Z).open,captionClose:(e.closedCaptions??Z).close,captionCharacter:e.closedCaptions?.character||xe,refSplitCache:new Map,refSplitRevision:x.revision}}get locale(){return this.currentLocale}get isSourceDebug(){return this.sourceDebug}get buildId(){return this.host.bundle.content?.hash??""}setLocale(e){this.currentLocale=e,this.host.strings=this.allStrings[e]??{}}replaceStrings(e){this.allStrings=e.strings,this.host.strings=this.allStrings[this.currentLocale]??{},this.host.defaultStrings=this.allStrings[this.host.bundle.locales.default]??{}}hotSwap(e){this.refuseInCheckpoint("hotSwap");let t=this.saveGame(),r=o=>(o.setLocale(this.currentLocale),o.setClosedCaptions(this.host.captionsOn),o),s=()=>new n(e,{...this.creationOptions,registry:this.host.registry,[Ce]:this.host.ownsRegistry});this.release(!0);let i=s();try{return i.loadGame(t),r(i)}catch{i.release(!0);let o=s();for(let[a,c]of Object.entries(t.flows)){let l=c.cursor.currentSceneId;try{o.openFlow(a,l!==null?{scene:l}:{})}catch{}}return r(o)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:r,blockId:s}=this.resolveOpenAddress(t.scene,t.block),i=this.flowsById.get(e),o=this.host.journal;if(o&&i&&!i.isClosed)throw new Error(`openFlow would replace the open flow \'${e}\', which can\'t be undone while a checkpoint is open`);i?.close();let a=new D(e,this.host,t.seed??this.defaultSeed);return o&&(o.flows.add(a),o.opened.add(a),o.undo.push(()=>{a.close(),i?this.flowsById.set(e,i):this.flowsById.delete(e)})),this.flowsById.set(e,a),a.begin(r,s),a}runFlow(e,t,r){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:r}).advanceToStop().played;if(!s.goto(t,r))throw new Error(`runFlow: address not found: ${t}${r===void 0?"":` / ${r}`}`);return s.advanceToStop().played}assertExternalScopes(){for(let e of this.host.bundle.externalScopes??[])if(!this.host.registry.has(e))throw new Error(`this content names @${e}, which no engine on this registry registered: give every engine the game\'s one registry`)}resolveOpenAddress(e,t){if(e!==void 0){let r=re(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=ne(this.host,r,t);if(s===void 0)throw new Error(`unknown block: ${t}`);return{sceneId:r,blockId:s}}if(t!==void 0){if(!this.host.blockIndex.has(t))throw new Error(`unknown block: ${t}`);return{blockId:t}}if(Object.keys(this.host.bundle.scenes).length===0)throw new Error("no scenes in bundle");return{}}resolveSceneRef(e){if(e!=null)return re(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:ne(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?S(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?S(t):void 0}tagsForBeat(e){return C(this.host,e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?C(this.host,t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?C(this.host,s):void 0)??[]}gameDataForScene(e){let t=this.resolveSceneRef(e);return{...(t!=null?this.host.bundle.scenes[t]:void 0)?.gameData}}gameDataForBlock(e,t){let r=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(r!=null?this.host.blockById.get(r):void 0)?.gameData}}getCast(){let e=[];for(let t of this.host.bundle.cast??[])t?.name&&e.push(t.name);return e}castForScene(e){let t=this.resolveSceneRef(e),r=t!=null?this.host.bundle.scenes[t]:void 0;if(!r)return[];let s=new Set;for(let i of r.blocks)Ie(i.children,s);return[...s]}castForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t),i=s!=null?this.host.blockById.get(s):void 0;if(!i)return[];let o=new Set;return Ie(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...S(e)?{gameId:S(e)}:{},name:e.name,...De(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...S(t)?{gameId:S(t)}:{},name:t.name,...De(t.gameData),...this.tagsField(t.id),children:t.children.map(r=>this.outlineNode(r))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let r of t.blocks)I(r.children,s=>{if(s.type==="snippet")for(let i of s.beats??[])e.push({sceneId:t.id,blockId:r.id,snippetId:s.id,beat:this.beatInfo(i)})});return e}outlineNode(e){return e.type==="group"?{type:"group",id:e.id,...this.tagsField(e.id),...e.selector?{selector:e.selector}:{},...e.prompt?{prompt:this.beatInfo(e.prompt)}:{},children:e.children.map(t=>this.outlineNode(t))}:{type:"snippet",id:e.id,...this.tagsField(e.id),beats:(e.beats??[]).map(t=>this.beatInfo(t)),...e.jump?{jumpTo:e.jump.to,...e.jump.mode?{jumpMode:e.jump.mode}:{}}:{}}}beatInfo(e){let t=C(this.host,e.id),r={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){r.character=e.character;let s=this.host.defaultStrings[Y(e.character)]??this.host.castDisplay.get(e.character);s!==void 0&&(r.characterName=s)}if(e.direction!==void 0&&(r.direction=e.direction),e.qualifier!==void 0){r.qualifier=e.qualifier;let s=this.host.defaultStrings[X(e.qualifier)]??this.host.qualifierDisplay.get(e.qualifier);s!==void 0&&(r.qualifierName=s)}}if(e.kind==="line"||e.kind==="text"){let s=this.host.defaultStrings[e.id];s!==void 0&&(r.text=s)}return e.gameData&&Object.keys(e.gameData).length&&(r.gameData=e.gameData),t&&t.length&&(r.tags=t),r}tagsField(e){let t=C(this.host,e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}listBags(){let e=[{bag:this.host.patterBag}];for(let[t,r]of this.host.stageBags)e.push({bag:r,pathPrefix:`@scene:${t}.`});return e}checkpoint(){if(this.host.journal)throw new Error("a checkpoint is already open: roll it back or commit it first");let e={undo:[],flows:new Set,opened:new Set,selectors:new Set,flowSelectors:new Map};return this.host.journal=e,new T(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let r=t.length-1;r>=0;r--)t[r]();n.forget(e.journal)}commit(e){this.endCheckpoint(e),n.forget(e.journal)}get inCheckpoint(){return this.host.journal!==null}endCheckpoint(e){if(this.host.journal!==e.journal)throw new Error("that checkpoint is not the open one");this.host.journal=null}static forget(e){e.undo=[],e.flows.clear(),e.opened.clear(),e.selectors.clear(),e.flowSelectors.clear()}refuseInCheckpoint(e){if(this.host.journal)throw new Error(`${e} can\'t be undone, so it isn\'t allowed while a checkpoint is open`)}closeFlow(e){this.refuseInCheckpoint("closeFlow"),this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){this.refuseInCheckpoint("reset");for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.patterBag.reseed(this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear();for(let e of this.host.stageBags.keys())this.host.registry.remove(y.stage(e));this.host.stageBags.clear(),this.host.registry.discardParked("patter/")}release(e){for(let r of this.flowsById.values())r.releaseBags(e),r.close();this.flowsById.clear();let t=this.host.registry;for(let r of this.host.stageBags.keys())t.remove(y.stage(r),{keep:e});this.host.stageBags.clear();for(let r of["patter",...this.host.hostScopes])t.has(r)&&t.remove(r,{keep:e});for(let r of Object.keys(Pe(this.creationOptions)))t.has(r)&&t.remove(r)}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.registry.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e),i=this.host.journal?this.host.registry.get(r,s):void 0;this.host.registry.set(r,s,t,{host:!0}),this.host.journal&&i!==void 0&&this.host.journal.undo.push(()=>this.host.registry.set(r,s,i,{host:!0}))}log(){return this.engineLog}clearLog(){this.engineLog.length=0}onTrace(e){return this.engineTraceHandlers.add(e),this.host.tracing=!0,()=>{this.engineTraceHandlers.delete(e),this.host.tracing=this.host.logEnabled||this.engineTraceHandlers.size>0}}emitEngine(e,t,r){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineSeq++,...r?{scene:r}:{}})}getVisitCounts(){return Object.fromEntries(this.host.sharedVisits)}listProperties(){return this.host.patterSharedDecls.map(e=>({name:e.name,path:`@patter.${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:P(e),writable:e.writable??!0}))}splitShared(e){let t=Te(this.host,e);if(t.scope==="scene")throw new Error(`\'${e}\': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);return t}save(){return{patter:this.host.patterBag.save()}}load(e){e.patter&&this.host.patterBag.load(e.patter)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:ee,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:Ne(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==ee)throw new Error(`unsupported save version: ${String(t)}`);let r=e.flows;if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("malformed save: no flows");this.assertExternalScopes();let s=this.host.registry;for(let[o,a]of this.flowsById)a.releaseBags(o in e.flows),a.close();this.flowsById.clear();for(let o of this.host.stageBags.keys())s.remove(y.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.version===2?ct(e):e.registry;i&&(this.host.ownsRegistry?s.load(i):s.load(i,{keepParked:!0})),this.host.sharedVisits.clear();for(let[o,a]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(o,a);this.host.sharedSelectors.clear();for(let[o,a]of Fe(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new D(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}}},D=class{id;host;local;registered=new Set;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPrompt=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.patterBag.get(e):this.local.get(e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.registry.set("patter",e,t):this.local.set(e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;if(t!==null)return this.sceneBagFor(t,e)?.get(e)},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.sceneBagFor(r,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,r){this.id=e,this.host=t,this.rngState=O(r),this.local=this.newLocal(),this.evalCtx={scopes:{},host:{nextRandom:this.rng,visits:s=>this.visitCounts.get(s)??0,patterVisits:s=>this.host.sharedVisits.get(s)??0},qualities:(s,i)=>this.stagesFor(s,i)}}context(){let e=this.host.registry;if(e.revision!==this.ctxRevision){let t=e.toEvalContext(),r=this.evalCtx.scopes;for(let s of Object.keys(r))delete r[s];Object.assign(r,t.scopes),r.patter=this.patterResolver,r.scene=this.sceneResolver,this.registryQualities=t.qualities,this.ctxRevision=e.revision}return this.evalCtx}stagesFor(e,t){let r=t.toLowerCase();if(e==="patter")return te(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(r);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:te(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(r)}return this.registryQualities?.(e,t)??te(this.host,`host/${e}`,()=>this.host.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(r)}begin(e,t){if(this.host.journal&&!this.host.journal.opened.has(this))throw new Error("a flow can\'t be started or reset while a checkpoint is open");if(this.releaseBags(!1),this.host.registry.discardParked(y.flow(this.id)),this.mountLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.activeSnippet=null,this.beatIndex=0,this.clearPending(),this.started=!0,t){let r=this.host.blockIndex.get(t);if(!r)throw new Error(`unknown block: ${t}`);this.enterSceneSetup(r.sceneId),this.stack=[{sceneId:r.sceneId,containerId:t,index:0}],this.enter(t)}else{let r=e??Object.keys(this.host.bundle.scenes)[0],s=r?this.host.bundle.scenes[r]:void 0;if(!s)throw new Error(r?`unknown scene: ${r}`:"no scenes in bundle");this.enterSceneSetup(r);let i=s.blocks[0];i&&(this.stack=[{sceneId:r,containerId:i.id,index:0}],this.enter(i.id))}this.settle()}reset(e,t){this.begin(e,t)}start(e,t){this.reset(e,t)}goto(e,t){if(this.closed)return!1;if(this.touch(),e==="END")return this.started=!0,this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let r=re(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=ne(this.host,r,t),s===void 0)?!1:this.started?(this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??r,"jump"),this.settle(),!0):(this.begin(r,s),!0)}close(){this.releaseBags(!1),this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.clearPending()}clearPending(){this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null}touch(){let e=this.host.journal;if(!e||e.flows.has(this))return;e.flows.add(this);let{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p}=this,f=this.stack.map(u=>({...u}));e.undo.push(()=>{Object.assign(this,{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p,stack:f})})}get isClosed(){return this.closed}get currentScene(){return this.currentSceneId}advance(){if(this.closed)return{type:"end"};if(!this.started)throw new Error("flow has not been started");if(this.touch(),this.pendingPrompt){let e=this.pendingPrompt;return this.pendingPrompt=null,this.pendingPromptOwnerId=null,e.shown?this.promptResult(e.beat,e.shown):this.beatResult(e.beat)}return this.settle(),this.flowEnded?{type:"end"}:this.pendingChoice?{type:"choice",groupId:this.pendingChoice.groupId,options:this.pendingChoice.options}:this.activeSnippet?this.beatResult(this.activeSnippet.beats[this.beatIndex++]):(this.flowEnded=!0,{type:"end"})}advanceToStop(){let e=[];for(;;){let t=this.advance();if(t.type==="choice"||t.type==="end")return{played:e,stop:t};e.push(t)}}settle(){let e=0;for(;;){if(++e>1e4)throw new Error("flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content");if(this.flowEnded||this.pendingChoice)return;if(this.activeSnippet){if(this.beatIndex<(this.activeSnippet.beats?.length??0))return;this.runEffects(this.activeSnippet.onExit,this.activeSnippet.id);let i=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(i);continue}let t=this.stack[this.stack.length-1];if(!t){this.flowEnded=!0;return}t.sceneId!==this.currentSceneId&&(this.currentSceneId=t.sceneId);let r=this.childrenOf(t.containerId);if(!r){this.stack.pop();continue}let s=t.index;for(;t.index<r.length&&!this.eligible(r[t.index]);)t.index++;if(this.host.tracing&&t.index!==s&&this.emit({type:"select",group:t.containerId,selector:"run",children:r.slice(s,t.index+1).map((i,o)=>({id:i.id,eligible:s+o===t.index})),picked:r[t.index]?.id??null}),t.index>=r.length){this.stack.pop();continue}this.enterChild(r[t.index++])}}listBags(){let e=[{bag:this.local,pathPrefix:`${this.id}/@patter.`}];for(let[t,r]of this.sceneBags)e.push({bag:r,pathPrefix:`${this.id}/@scene:${t}.`});return e}log(){return this.flowLog}clearLog(){this.flowLog.length=0}emit(e){let t=this.currentSceneId??void 0;this.host.emitEngine(this.id,e,t),this.host.logEnabled&&this.flowLog.push({...e,seq:this.flowSeq++,...t?{scene:t}:{}})}getVisitCounts(){return Object.fromEntries(this.visitCounts)}getChoices(){return this.pendingChoice?.options??[]}choose(e){let t=this.pendingChoice;if(!t)throw new Error("no choice is pending");let r=t.options.find(o=>o.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);this.touch();let s=t.byId.get(e);this.host.tracing&&this.emit({type:"chose",group:t.groupId,option:e}),this.pendingChoice=null;let i=this.host.replayPromptOnChoose?this.authoredPromptOf(s):void 0;this.pendingPrompt=i&&r.prompt?{beat:i,shown:{...r.prompt}}:null,this.pendingPromptOwnerId=this.pendingPrompt?s.id:null,this.enterChild(s)}isEnded(){return this.flowEnded}getProperty(e){let{scope:t,name:r}=this.splitRef(e);return t==="patter"?this.patterResolver.get(r):t==="scene"?this.sceneResolver.get(r):this.host.registry.get(t,r)}setProperty(e,t){this.writeProperty(e,t,!0)}writeProperty(e,t,r){let{scope:s,name:i}=this.splitRef(e),o=this.host.journal;if(s==="patter"){if(o){let a=this.host.patterSharedNames.has(i),c=a?this.host.patterBag.get(i):this.local.get(i),l=this.local;c!==void 0&&o.undo.push(a?()=>this.host.registry.set("patter",i,c):()=>l.set(i,c))}this.patterResolver.set(i,t)}else if(s==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);if(o){let a=this.sceneBagFor(this.currentSceneId,i),c=a?.get(i);a&&c!==void 0&&o.undo.push(()=>a.set(i,c))}this.sceneResolver.set(i,t)}else{let a=o?this.host.registry.get(s,i):void 0;this.host.registry.set(s,i,t,r?{host:!0}:void 0),o&&a!==void 0&&o.undo.push(()=>this.host.registry.set(s,i,a,{host:!0}))}}snapshot(){return{rngState:this.rngState,visits:Object.fromEntries(this.visitCounts),cursor:{flowEnded:this.flowEnded,currentSceneId:this.currentSceneId,stack:this.stack.map(e=>{let t=this.childrenOf(e.containerId)?.[e.index];return t?{...e,nextId:t.id}:{...e}}),activeSnippetId:this.activeSnippet?.id??null,beatIndex:this.beatIndex,pendingChoice:this.pendingChoice?{groupId:this.pendingChoice.groupId,options:this.pendingChoice.options.map(e=>({...e}))}:null,pendingPromptOwnerId:this.pendingPromptOwnerId,...this.pendingPrompt?.shown?{pendingPrompt:ot(this.pendingPrompt.shown)}:{},selectors:Ne(this.selectors)}}}restore(e){if(this.host.journal)throw new Error("a flow can\'t be restored while a checkpoint is open");this.restoreCursor(e),this.releaseBags(!0),this.mountLocal();for(let t of new Set([this.currentSceneId,...this.stack.map(r=>r.sceneId)]))t!==null&&this.host.bundle.scenes[t]&&this.ensureSceneBags(t)}restoreCursor(e){this.rngState=O(e.rngState),this.visitCounts=new Map(Object.entries(e.visits??{}));let t=e.cursor;if(this.started=!0,this.flowEnded=t.flowEnded,this.beatIndex=t.beatIndex,this.currentSceneId=t.currentSceneId,this.stack=t.stack.map(r=>{let{nextId:s,...i}=r;if(s!==void 0){let o=this.childrenOf(i.containerId)?.findIndex(a=>a.id===s)??-1;if(o>=0)return{...i,index:o}}return{...i}}),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=Fe(t.selectors),this.pendingChoice=null,t.pendingChoice!==null){let r=new Map,s=[];for(let i of t.pendingChoice.options){let o=this.host.nodeIndex.get(i.id);o&&(r.set(i.id,o),s.push({...i}))}s.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:s,byId:r})}if(this.pendingPrompt=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId),s=r?this.authoredPromptOf(r):void 0;s?this.pendingPrompt={beat:s,...t.pendingPrompt?{shown:{...t.pendingPrompt}}:{}}:this.pendingPromptOwnerId=null}}enterSceneSetup(e){let t=this.host.bundle.scenes[e];if(!t)throw new Error(`unknown scene: ${e}`);this.currentSceneId=e,this.enter(e),this.seedScene(t),this.runEffects(t.onEntry,t.id)}enterChild(e){if(this.enter(e.id),e.type==="snippet"){this.beginSnippet(e);return}let t=e.selector??"run";if(t==="run"){this.stack.push({sceneId:this.currentSceneId,containerId:e.id,index:0});return}if(t==="choice"){this.setupChoice(e);return}let r=this.selectChild(e);r&&this.enterChild(r)}childrenOf(e){let t=this.host.blockById.get(e);if(t)return t.children;let r=this.host.nodeIndex.get(e);if(r&&r.type==="group")return r.children}beginSnippet(e){this.runEffects(e.onEnter,e.id),this.activeSnippet=e,this.beatIndex=0}setupChoice(e){let t=[],r=new Map,s=[];for(let o of e.children){if(o.fallback===!0){s.push(o);continue}if(o.sticky!==!0&&(this.visitCounts.get(o.id)??0)>=1)continue;let a=this.eligible(o),c=o.secretUntilEligible===!0;!a&&c||(t.push({id:o.id,prompt:this.promptFor(o),eligible:a,gameData:o.gameData}),r.set(o.id,o))}if(t.some(o=>o.eligible)){this.host.tracing&&this.emit({type:"choice",group:e.id,options:t.map(o=>({id:o.id,eligible:o.eligible}))}),this.pendingChoice={groupId:e.id,options:t,byId:r};return}let i=s.find(o=>this.eligible(o));if(i){this.enterChild(i);return}this.host.tracing&&this.emit({type:"dry",group:e.id}),this.host.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(this.host.tracing&&this.emit({type:"jump",to:e,mode:t}),e==="END"){this.flowEnded=!0,this.stack=[];return}let r,s,i=this.host.bundle.scenes[e];if(i){this.enterSceneSetup(e);let a=i.blocks[0];if(!a){t==="jump"&&(this.stack=[]);return}r=e,s=a.id}else{let a=this.host.blockIndex.get(e);if(!a)throw new Error(`jump target not found: ${e}`);a.sceneId!==this.currentSceneId&&this.enterSceneSetup(a.sceneId),r=a.sceneId,s=e}this.enter(s);let o={sceneId:r,containerId:s,index:0};t==="call"?this.stack.push(o):this.stack=[o]}selectChild(e){let t=e.children.map(c=>({id:c.id,eligible:this.eligible(c)})),r=e.children.filter((c,l)=>t[l].eligible),s=e.options?.order??"sequential",i=e.options?.exhaust??"once",o=c=>(this.host.tracing&&this.emit({type:"select",group:e.id,selector:e.selector??"default",...e.selector==="sequence"?{order:s,exhaust:i}:{},children:t,picked:c?.id??null}),c);if(r.length===0)return o(null);let a=this.selectorState(e);switch(e.selector){case"branch":return o(r[0]);case"sequence":return o(s==="shuffle"?this.pickShuffle(r,i,a):s==="specificity"?this.pickSpecificity(r,i,a):this.pickSequential(r,i,a));default:return null}}pickSequential(e,t,r){let s=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,r){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(u=>u.id),a=new Set(e.map(u=>u.id));if(r.bag===void 0&&(r.bag=o()),!r.bag.some(u=>a.has(u))){if(t==="once")return null;if(i){let u=e[s-1];return r.last=u.id,u}if(r.bag=o(),r.bag.length===0)return null}let c=r.bag.filter(u=>a.has(u)),l=r.last!==void 0&&c.length>1?c.indexOf(r.last):-1,p=Math.floor(this.rng()*(l>=0?c.length-1:c.length));l>=0&&p>=l&&p++;let f=c[p];return r.bag.splice(r.bag.indexOf(f),1),r.last=f,e.find(u=>u.id===f)}pickSpecificity(e,t,r){let s=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(p=>p.id));let l=new Set(r.bag);if(s=e.filter(p=>l.has(p.id)),s.length===0)return t==="stick"&&r.last!==void 0?e.find(p=>p.id===r.last)??null:null}let i=-1,a=s.map(l=>{let p=this.specScore(l);return p>i&&(i=p),{c:l,s:p}}).filter(l=>l.s===i).map(l=>l.c),c;if(a.length===1)c=a[0];else{let l=r.last!==void 0?a.findIndex(f=>f.id===r.last):-1,p=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&p>=l&&p++,c=a[p]}return t!=="repeat"&&(r.bag=r.bag.filter(l=>l!==c.id)),r.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0,e):0}matchedSpec(e,t,r){return ue(e,i=>{try{return Be(V(i,this.context(),z))}catch(o){return this.reportError("best-match",r.id,r.condition,o),!1}},{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=this.host.journal,s;if(r&&(s=e.shared?r.selectors:r.flowSelectors.get(this),s||(s=new Set,r.flowSelectors.set(this,s))),r&&s&&!s.has(e.id)){s.add(e.id);let o=t.get(e.id),a=o?{...o,...o.bag?{bag:[...o.bag]}:{}}:void 0;r.undo.push(()=>{a?t.set(e.id,a):t.delete(e.id)})}let i=t.get(e.id);return i||(i={},t.set(e.id,i)),i}runEffects(e,t){for(let r of e??[]){let s;try{s=this.evalExpr(r.value);let i=this.host.tracing?this.getProperty(r.target):void 0;this.writeProperty(r.target,s,!1),this.host.tracing&&this.emit({type:"write",target:r.target,value:s,...i!==void 0?{prev:i}:{}})}catch(i){this.reportError("effect",t,r.value,i)}}}eligible(e){if(!e.condition)return!0;try{return Be(this.evalExpr(e.condition))}catch(t){return this.reportError("condition",e.id,e.condition,t),!1}}reportError(e,t,r,s){let i=s instanceof Error?s.message:String(s),o=r?.src;this.host.onError({flow:this.id,kind:e,node:t,...o?{source:o}:{},message:i}),this.host.tracing&&this.emit({type:"diagnostic",kind:e,node:t,...o?{source:o}:{},message:i})}evalExpr(e){return V(this.conditionAst(e),this.context(),z)}conditionAst(e){let t=ke.get(e);return t||(t=k(e.ast),ke.set(e,t)),t}enter(e){let t=this.visitCounts,r=t.get(e);t.set(e,(r??0)+1);let s=this.host.sharedVisits,i=s.get(e);s.set(e,(i??0)+1),this.host.journal&&this.host.journal.undo.push(()=>{r===void 0?t.delete(e):t.set(e,r),i===void 0?s.delete(e):s.set(e,i)})}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=Q(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=C(this.host,e.id),r=t&&t.length?{tags:t}:{};switch(e.kind){case"gameEvent":return{type:"gameEvent",id:e.id,gameData:e.gameData,...r};case"text":return{type:"text",id:e.id,text:this.interpolate(this.resolveString(e.id)),gameData:e.gameData,...r};case"line":{let s=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?s:this.interpolate(s)),c=i&&a.length===0;return{type:"line",id:e.id,text:a,character:c?void 0:e.character,characterName:c?void 0:this.resolveCharacterName(e.character),direction:c?void 0:e.direction,qualifier:c?void 0:e.qualifier,qualifierName:c?void 0:this.resolveQualifierName(e.qualifier),gameData:e.gameData,...r}}}}interpolate(e){return be(e,t=>this.getProperty(t))}stripCaptions(e){return we(e,this.host.captionOpen,this.host.captionClose)}captionLine(e){return this.host.captionsOn?e:this.stripCaptions(e)}promptFor(e){let t=this.promptBeatOf(e);if(!t)return;let r=this.interpolate(this.resolveString(t.id));return t.kind==="line"?{kind:"line",text:this.captionLine(r),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction,qualifier:t.qualifier,qualifierName:this.resolveQualifierName(t.qualifier)}:{kind:"text",text:r}}authoredPromptOf(e){return e.type==="group"?e.prompt:void 0}promptResult(e,t){let r=C(this.host,e.id),s=r&&r.length?{tags:r}:{};return t.kind==="text"?{type:"text",id:e.id,text:t.text,gameData:e.gameData,...s}:{type:"line",id:e.id,text:t.text,character:t.character,characterName:t.characterName,direction:t.direction,qualifier:t.qualifier,qualifierName:t.qualifierName,gameData:e.gameData,...s}}promptBeatOf(e){return e.type==="group"&&e.prompt?e.prompt:((e.type==="snippet"?e:this.firstTextSnippetIn(e.children))?.beats??[]).find(r=>r.kind==="line"||r.kind==="text")}firstTextSnippetIn(e){let t;return I(e,r=>{!t&&r.type==="snippet"&&(r.beats??[]).some(s=>s.kind==="line"||s.kind==="text")&&(t=r)}),t}resolveString(e){if(this.host.emitIds)return e;let t=this.host.strings[e];if(t!==void 0)return t;let r=this.host.defaultStrings[e];return r!==void 0?`<Untranslated: ${e}> ${r}`:e}resolveCharacterName(e){if(e===void 0||this.host.emitIds)return;let t=Y(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}resolveQualifierName(e){if(e===void 0||this.host.emitIds)return;let t=X(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.qualifierDisplay.get(e)}splitRef(e){return Te(this.host,e)}newLocal(){return new b(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=y.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:A}),this.registered.add(e)}releaseBags(e){for(let t of this.registered)this.host.registry.remove(t,{keep:e});this.registered.clear(),this.sceneBags.clear()}sceneBagFor(e,t){if(this.host.bundle.scenes[e])return this.ensureSceneBags(e),this.host.sceneSharedNames.get(e)?.has(t.toLowerCase())?this.host.stageBags.get(e):this.sceneBags.get(e)}ensureSceneBags(e){let t=this.host.sceneSharedNames.get(e)??new Set,r=this.host.bundle.scenes[e]?.sceneProps??[],s=this.host.journal;if(!this.sceneBags.has(e)){let i=new b(r.filter(c=>!t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),o=y.flowScene(this.id,e),a=Re(this.host.registry,o,i,s!==null);this.registered.add(o),this.sceneBags.set(e,i),s&&s.undo.push(()=>{this.sceneBags.get(e)===i&&(this.host.registry.remove(o,a?{keep:!0}:void 0),this.registered.delete(o),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let i=new b(r.filter(a=>t.has(a.name.toLowerCase())),{pathPrefix:"@scene."}),o=Re(this.host.registry,y.stage(e),i,s!==null);this.host.stageBags.set(e,i),s&&s.undo.push(()=>{this.host.stageBags.get(e)===i&&(this.host.registry.remove(y.stage(e),o?{keep:!0}:void 0),this.host.stageBags.delete(e))})}}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;this.ensureSceneBags(e.id);for(let r of e.sceneProps??[]){if(!r.temporary)continue;let s=r.name.toLowerCase(),i=t.has(s)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);if(!i)continue;let o=this.host.journal?i.get(s):void 0;o!==void 0&&this.host.journal.undo.push(()=>i.set(s,o)),i.set(s,P(r))}}};function Ie(n,e){I(n,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let r of t.beats??[])r.kind==="line"&&r.character&&e.add(r.character)})}function De(n){return n&&Object.keys(n).length?{gameData:n}:{}}function ot(n){let e={kind:n.kind,text:n.text};return n.character!==void 0&&(e.character=n.character),n.characterName!==void 0&&(e.characterName=n.characterName),n.direction!==void 0&&(e.direction=n.direction),n.qualifier!==void 0&&(e.qualifier=n.qualifier),n.qualifierName!==void 0&&(e.qualifierName=n.qualifierName),e}function Ne(n){let e={};for(let[t,r]of n){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e[t]=s}return e}function Fe(n){let e=new Map;for(let[t,r]of Object.entries(n??{})){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e.set(t,s)}return e}function Re(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:A}),r&&JSON.stringify(t.save())!==s}function Oe(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Pe(n){return{...n.world?{world:n.world}:{},...n.hostScopes}}function te(n,e,t){let r=n.qualityLadders.get(e);if(!r){r=new Map;for(let s of t()??[]){let i=s.name.toLowerCase();s.type==="quality"&&s.stages&&!r.has(i)&&r.set(i,s.stages)}n.qualityLadders.set(e,r)}return r}function Ae(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function at(n,e){let t=Ae(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function re(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function ne(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function Te(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=ve(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function ct(n){let e={};n.shared?.patter&&(e.patter=n.shared.patter);for(let[t,r]of Object.entries(n.stageBags??{}))e[y.stage(t)]=r;for(let[t,r]of Object.entries(n.flows??{})){r.scopes?.patter&&(e[y.flowGlobals(t)]=r.scopes.patter);for(let[s,i]of Object.entries(r.sceneBags??{}))e[y.flowScene(t,s)]=i}return e}function Be(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var lt=(n,e)=>n.shared??e;function se(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:lt(n,e)}}function pt(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function dt(n,e){e.blocks++;let t=[...n.children];for(;t.length;){let r=t.pop();if(r.type==="group"){e.groups++,r.prompt&&e.prompts++,t.push(...r.children);continue}e.snippets++;for(let s of r.beats??[])e.beats++,s.kind==="gameEvent"&&e.gameEvents++}}function Ge(n){let e={scenes:0,blocks:0,groups:0,snippets:0,beats:0,prompts:0,gameEvents:0,cast:n.cast?.length??0},t=[],r=[];for(let o of Object.values(n.scenes)){e.scenes++;let a=S(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:S(c),name:c.name}))});for(let c of o.blocks)dt(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>se(c,!1))})}let s=(n.scopeRegistry?.scopes??[]).map(o=>({token:o.token,writable:o.writable??!0,opaque:o.declarations===void 0,properties:(o.declarations??[]).map(a=>se(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(pt)}));return{identity:{schema:n.schema,project:n.content.project,...n.content.version!==void 0?{version:n.content.version}:{},...n.content.hash!==void 0?{hash:n.content.hash}:{},...n.content.structureHash!==void 0?{structureHash:n.content.structureHash}:{},voiced:n.voiced,defaultLocale:n.locales.default,locales:[...n.locales.included],localisation:n.localisation?.mode??"embedded",sourceDebug:n.localisation?.sourceDebug??!1},addresses:t,hostScopes:s,properties:{patter:(n.properties??[]).map(o=>se(o,!0)),scene:r},gameData:i,counts:e}}function je(n,e){return n.gameDataFields?.[e]??[]}function ie(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function Ve(n,e){let t={};for(let r of n){let s=ie(n,e,r.name);s!==void 0&&(t[r.name]=s)}for(let[r,s]of Object.entries(e??{}))r in t||(t[r]=s);return t}return Je(gt);})();\n';
|
|
201371
|
+
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var K=Object.defineProperty;var Qe=Object.getOwnPropertyDescriptor;var Ke=Object.getOwnPropertyNames;var ze=Object.prototype.hasOwnProperty;var Ye=(n,e)=>{for(var t in e)K(n,t,{get:e[t],enumerable:!0})},Xe=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Ke(e))!ze.call(n,s)&&s!==t&&K(n,s,{get:()=>e[s],enumerable:!(r=Qe(e,s))||r.enumerable});return n};var Ze=n=>Xe(K({},"__esModule",{value:!0}),n);var bt={};Ye(bt,{Checkpoint:()=>j,DEFAULT_PAD_AFTER:()=>x,Engine:()=>J,Flow:()=>D,buildTagIndex:()=>W,describeBundle:()=>$e,effectiveGameData:()=>_e,gameDataFields:()=>He,gameDataValue:()=>ae});function P(n,e){return n!==void 0&&Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0}function E(n){switch(n[0]){case"b":return{kind:"bool",value:n[1]};case"n":return{kind:"number",value:n[1]};case"s":return{kind:"string",value:n[1]};case"sv":return{kind:"scopedvar",scope:n[1],name:n[2]};case"u":return{kind:"unary",op:n[1],operand:E(n[2])};case"bin":return{kind:"binary",op:n[1],left:E(n[2]),right:E(n[3])};case"call":{let e=n.slice(2).map(E);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var u=class extends Error{constructor(e){super(e),this.name="EvalError"}};function q(n,e,t){let r=new Map(t.scopes.map(i=>[i.token,i.missing??"false"])),s=i=>{switch(i.kind){case"bool":return i.value;case"number":return i.value;case"string":return i.value;case"scopedvar":{let o=P(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):P(o,i.name);if(a===void 0){if(r.get(i.scope)==="throw")throw new u(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{if(i.name==="advance"&&!P(t.functions,i.name)){let a=i.args[0];if(i.args.length!==1||a===void 0)throw new u(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=z(a,e);if(c===void 0)throw new u("advance() needs a quality reference (@scope.name of a quality property)");let l=v(s(a),c,"advance");return c[Math.min(l+1,c.length-1)]}let o=P(t.functions,i.name);if(!o)throw new u(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new u("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=s(i.operand);if(typeof a!="boolean")throw new u(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new u(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let f=s(i.left);if(typeof f!="boolean")throw new u(`\'and\' requires boolean operands, left is ${typeof f}`);if(!f)return!1;let g=s(i.right);if(typeof g!="boolean")throw new u(`\'and\' requires boolean operands, right is ${typeof g}`);return g}if(i.op==="or"){let f=s(i.left);if(typeof f!="boolean")throw new u(`\'or\' requires boolean operands, left is ${typeof f}`);if(f)return!0;let g=s(i.right);if(typeof g!="boolean")throw new u(`\'or\' requires boolean operands, right is ${typeof g}`);return g}let o=s(i.left),a=s(i.right),c=z(i.left,e),l=z(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!et(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new u(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return v(o,p,">")>v(a,p,">");case">=":return v(o,p,">=")>=v(a,p,">=");case"<":return v(o,p,"<")<v(a,p,"<");case"<=":return v(o,p,"<=")<=v(a,p,"<=");case"+":case"-":case"*":case"/":throw new u(`\'${i.op}\' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(i.op){case"==":return fe(o,a);case"!=":return!fe(o,a);case">":return C(o,a,">"),o>a;case">=":return C(o,a,">="),o>=a;case"<":return C(o,a,"<"),o<a;case"<=":return C(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new u(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return C(o,a,"-"),o-a;case"*":return C(o,a,"*"),o*a;case"/":if(C(o,a,"/"),a===0)throw new u("division by zero");return o/a}}}};return s(n)}function fe(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;let t=[...n].sort(),r=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==r[s])return!1;return!0}return n===e}function C(n,e,t){if(typeof n!="number"||typeof e!="number")throw new u(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function z(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function v(n,e,t){if(typeof n!="string")throw new u(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new u(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var et=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function O(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function Y(n){let e=O(n);return{next(){e=e+1831565813>>>0;let t=Math.imul(e^e>>>15,1|e);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296},state(){return e}}}var tt={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},rt=[tt];function he(n,e,t){let r=t?.countingCalls??rt;return $(n,t?.want??!0,e,r)}function $(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=$(n.left,e,t,r),i=$(n.right,e,t,r);return n.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(n.kind==="unary"&&n.op==="not")return $(n.operand,!e,t,r);if(n.kind==="call"){let s=r.find(i=>i.name===n.name);if(s){let i=s.count(n),o=t(n);return e?o?i:0:o?0:1}}return t(n)===e?1:0}var b=class n{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let r=this.norm(t.name);this.decls.set(r,t),this.values[r]=structuredClone(t.default??B(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,r){let s=this.norm(e);if(!r?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:r?.silent??!1,reason:r?.reason};this.values[s]=t;for(let o of[...this.auditors])o(i);if(!i.silent)for(let o of[...this.subscribers])o(i);return i}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}onAudit(e){return this.auditors.add(e),()=>this.auditors.delete(e)}rows(){return[...this.decls.entries()].map(([e,t])=>Se(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm,pathPrefix:this.pathPrefix});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e){for(let[t,r]of Object.entries(e))this.values[this.norm(t)]=r}};function Se(n,e,t,r,s=""){let i=r??n.name.toLowerCase();return{name:i,path:s+i,type:n.type,value:e,default:n.default??B(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var nt=n=>n.toLowerCase(),X=1,H=class{scopes=new Map;parked=new Map;rev=0;get revision(){return this.rev}defineOwned(e,t,r){let s=typeof r=="string"?{pathPrefix:r}:r??{},i=new b(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,i,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,r){this.assertFree(e,r?.owner),this.scopes.set(e,{kind:"owned",bag:t,...r?.owner!==void 0?{owner:r.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(t.load(s),this.parked.delete(e)),this}remove(e,t){let r=this.scopes.get(e);if(!r)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&r.kind==="owned"&&this.parked.set(e,r.bag.save()),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&this.parked.delete(t);return this}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??nt,a=new Map;for(let c of r)a.set(o(c.name),c);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:i.writable??!0,norm:o,...i.owner!==void 0?{owner:i.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let r=this.scopes.get(e);if(r)return r.kind==="owned"?r.bag.get(t):r.resolver.get(r.norm(t))}set(e,t,r,s){let i=this.scopes.get(e);if(!i)throw new Error(`unknown scope \'@${e}\'`);if(i.kind==="owned"){if(!s?.host&&!i.bag.writable(t))throw new Error(`\'@${e}.${t}\' is read-only`);i.bag.set(t,r,s?.host?{host:!0}:void 0);return}let o=i.norm(t);if(!i.resolver.set)throw new Error(`\'@${e}.${t}\' is read-only`);if(!s?.host&&!this.foreignWritable(i,o))throw new Error(`\'@${e}.${t}\' is read-only`);i.resolver.set(o,r)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,r]of this.scopes){let s=r.owner!==void 0?{owner:r.owner}:{};if(r.kind==="owned")for(let i of r.bag.rows())e.push({scope:t,...s,...i});else for(let[i,o]of r.decls)e.push({scope:t,...s,...Se(o,r.resolver.get(i),this.foreignWritable(r,i),i,`${t}.`)})}return e}toEvalContext(e,t){let r=this.view(t?.aliases),s={};for(let[o,a]of r)s[o]=a.kind==="owned"?a.bag.values:a.resolver;let i=this.qualityLadders(r);return i.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(o,a)=>{let c=r.get(o);return c?i.get(o)?.get(st(c)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[r,s]of Object.entries(e??{})){let i=this.scopes.get(s);if(!i)throw new Error(`alias \'@${r}\' names \'${s}\', which is not registered`);t.set(r,i)}return t}qualityLadders(e){let t=new Map;for(let[r,s]of e)for(let[i,o]of me(s)){if(o.type!=="quality"||o.stages===void 0)continue;let a=t.get(r);a||(a=new Map,t.set(r,a)),a.set(i,o.stages)}return t}toSchema(e){let t=new Map;for(let[r,s]of this.view(e?.aliases)){let i=me(s);if(i.length===0)continue;let o=new Map;for(let[a,c]of i)o.set(a,{type:c.type,enumValues:c.values,...c.stages!==void 0?{stages:c.stages}:{}});t.set(r,o)}return{properties:t}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());for(let[t,r]of this.parked)e[t]=structuredClone(r);return e}load(e,t){t?.keepParked||this.parked.clear();for(let[r,s]of Object.entries(e)){let i=this.scopes.get(r);i?.kind==="owned"?i.bag.load(s):i||this.parked.set(r,structuredClone(s))}}saveFragment(){return{version:X,scopes:this.save()}}loadFragment(e){if(e.version!==X)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${X})`);this.load(e.scopes)}assertFree(e,t){let r=this.scopes.get(e);if(!r)return;let s=r.owner!==void 0?` by ${r.owner}`:"",i=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope \'@${e}\' is already registered${s}${i}`)}};function me(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function st(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function B(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return n.values?.[0]??"";case"flags":return[];case"quality":return n.stages?.[0]??"";default:return!1}}var ye=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function A(n){return n.ctx.host??{}}var Z={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new u("random(a, b) requires exactly 2 arguments");let t=A(e).nextRandom;if(!t)throw new u("random() called without a PRNG in context");let r=e.evaluate(n[0]),s=e.evaluate(n[1]);if(typeof r!="number"||typeof s!="number")throw new u("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new u("random(a, b) arguments must be integers");let i=Math.min(r,s),o=Math.max(r,s);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:ve("check_flags"),eval(n,e){let t=we(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new u("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:ve("set_flags"),eval(n,e){let t=[...we(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new u("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let i=t.indexOf(s.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:U("visits"),eval:(n,e)=>A(e).visits?.(_(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:U("seen"),eval:(n,e)=>(A(e).visits?.(_(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:U("patter_visits"),eval:(n,e)=>A(e).patterVisits?.(_(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:U("patter_seen"),eval:(n,e)=>(A(e).patterVisits?.(_(n,e,"patter_seen"))??0)>0}}},Mt=ye.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function be(n,e){let t=n.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function _(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new u(`${t}(id) requires a string node id`);return r}function U(n){return(e,t)=>{let r=e[0];r&&r.kind!=="string"&&t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(id): the argument must be a string id literal (a scene / block / node id)`})}}function we(n,e,t){if(!n)throw new u(`${t}() requires at least one argument (the flags variable)`);let r=e.evaluate(n);if(Array.isArray(r))return r;if(r===!1||r===null||r===void 0)return[];throw new u(`${t}() first argument must be a flags property`)}function ve(n){return(e,t)=>{if(e.length===0)return;let r=e[0];if(r.kind!=="scopedvar"){t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): first argument must be a flags property reference (@name or @scope.name)`});return}let s=t.schema.properties.get(r.scope)?.get(r.name);if(s&&s.type!=="flags"){let i=r.scope===t.defaultScope?r.name:`${r.scope}.${r.name}`;t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): \'@${i}\' is not a flags property (got ${s.type})`});return}for(let i=1;i<e.length;i++){let o=e[i];o.kind!=="flagdelta"?t.report({path:[...t.path,"args",i],kind:"wrong-arg-type",severity:"error",message:`${n}(): argument ${i+1} must be +flagName or -flagName`}):s?.type==="flags"&&s.enumValues&&!s.enumValues.includes(o.name)&&t.report({path:[...t.path,"args",i],kind:"unknown-flag-name",severity:"error",message:`${n}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var it=/^@[A-Za-z0-9_.]+$/;function*ot(n){let e="",t=0;for(;t<n.length;){let r=n[t];if(r==="{"&&n[t+1]==="{"){e+="{",t+=2;continue}if(r==="}"&&n[t+1]==="}"){e+="}",t+=2;continue}if(r==="{"){let s=n.indexOf("}",t+1);if(s!==-1){let i=n.slice(t,s+1),o=n.slice(t+1,s).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:it.test(o)?o:void 0},t=s+1;continue}e+=i,t=s+1;continue}}e+=r,t+=1}e&&(yield{kind:"text",value:e})}function at(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function ct(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function lt(n){let e="",t=!1;for(let r of n){if(ct(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function xe(n,e,t){if(e.length===0||n.indexOf(e)<0)return n;let r="",s=0,i=!1;for(;s<n.length;){if(n.startsWith(e,s)){let o=n.indexOf(t,s+e.length);if(o>=0){s=o+t.length,i=!0;continue}r+=n.slice(s);break}r+=n[s],s+=1}return i?lt(r):n}function ke(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of ot(n)){if(r.kind==="text"){t+=r.value;continue}if(!r.ref){t+=r.raw;continue}let s=e(r.ref);t+=s===void 0?"":at(s)}return t}function R(n,e){for(let t of n){e(t);let r=t.children;r&&R(r,e)}}function pt(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function y(n){let e=n.gameId?.trim();return e||pt(n.name)}var x=.6;function ee(n){return`cast:${n}`}function te(n){return`qualifier:${n}`}var re={open:"[",close:"]"},Ee="SFX";var F=3;function N(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function W(n){let e=new Map,t=(r,s)=>{let i=N([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group"&&r.prompt&&e.set(r.prompt.id,N([...i,...r.prompt.tags??[]])),r.type==="group")for(let o of r.children)t(o,i);else for(let o of r.beats??[])e.set(o.id,N([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=N(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=N([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var Ce=n=>n.kind==="line"||n.kind==="text",T=n=>typeof n=="number"&&Number.isFinite(n)?n:void 0;function Ie(n){let e=new Map,t=(i,o,a)=>{let c=i.kind==="gameEvent"?void 0:T(i.padAfter),l=c??o;a&&l<0&&(l=0),e.set(i.id,c!==void 0?{own:c,resolved:l}:{resolved:l})},r=(i,o)=>{let a=T(i.padAfterDefault)??o;if(i.type==="group"){i.prompt&&t(i.prompt,a,!1);for(let p of i.children)r(p,a);return}let c=i.beats??[],l=-1;c.forEach((p,f)=>{Ce(p)&&(l=f)}),c.forEach((p,f)=>{Ce(p)&&t(p,a,f===l)})},s=T(n.padAfterDefault)??x;for(let i of Object.values(n.scenes)){let o=T(i.padAfterDefault)??s;for(let a of i.blocks){let c=T(a.padAfterDefault)??o;for(let l of a.children)r(l,c)}}return e}var De=new WeakMap,Re=new WeakMap;function dt(n){let e=Re.get(n);return e||(e=W(n),Re.set(n,e)),e}var Pe=new WeakMap;function ut(n){let e=Pe.get(n);return e||(e=Ie(n),Pe.set(n,e)),e}function I(n,e){return n.tagIndex.get(e)?.slice()}var V="Patter",G=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),gt=n=>n.replace(/%2F/g,"/").replace(/%25/g,"%"),Oe=Symbol("ownsRegistry"),S={stage:n=>`patter/scene/${G(n)}`,flow:n=>`patter/flow/${G(n)}/`,flowGlobals:n=>`patter/flow/${G(n)}/patter`,flowScene:(n,e)=>`patter/flow/${G(n)}/scene/${G(e)}`},j=class{constructor(e){this.journal=e}journal},J=class n{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;engineLog=[];engineSeq=0;engineTraceHandlers=new Set;constructor(e,t={}){this.creationOptions=t;let r=t.locale??e.locales.default,s=e.strings;this.allStrings=s,this.currentLocale=r;let i=s[r]??{},o=s[e.locales.default]??{},a=e.localisation,c=a?.mode==="ids"&&!a.sourceDebug;this.sourceDebug=a?.mode==="ids"&&!!a.sourceDebug,this.sourceDebug&&typeof console<"u"&&console.warn("[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.");let l=new Map;for(let d of e.cast??[])d.displayName&&l.set(d.name,d.displayName);let p=new Map;for(let d of e.qualifiers??[])d?.gameId&&p.set(d.gameId,d.name);this.defaultSeed=(t.seed??2654435769)>>>0;let f=new Map,g=new Map,ce=new Map;for(let[d,h]of Object.entries(e.scenes)){this.sceneGameIdToId.set(y(h),d);let w=new Map;for(let m of h.blocks)g.set(m.id,{sceneId:d}),ce.set(m.id,m),w.set(y(m),m.id),R(m.children,L=>f.set(L.id,L));this.blockGameIdToId.set(d,w)}let le=e.properties??[],Q=le.filter(d=>d.shared??!0).map(Ne),Ue=le.filter(d=>!(d.shared??!0)).map(Ne),We=new Set(Q.map(d=>d.name.toLowerCase())),k=t.registry??new H,pe=!t.registry||t[Oe]===!0,de=new b(Q,{pathPrefix:"@patter."}),ue=[],M=[];try{k.mountOwned("patter",de,{owner:V}),M.push("patter");let d=new Set;for(let[h,w]of Object.entries(Te(t))){let m=e.scopeRegistry?.scopes.find(Je=>Je.token===h),L=(m?.declarations??[]).map(Me);k.defineForeign(h,w,L,{writable:m?.writable??!0,owner:V}),M.push(h),d.add(h)}for(let h of pe?e.scopeRegistry?.scopes??[]:[]){if(d.has(h.token)||k.has(h.token))continue;let w=(h.declarations??[]).map(m=>ht(m,h.writable));k.defineOwned(h.token,w,{owner:V}),M.push(h.token),ue.push(h.token)}}catch(d){for(let h of M)k.remove(h,{keep:!0});throw d}let ge=new Map;for(let[d,h]of Object.entries(e.scenes)){let w=new Set((h.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));ge.set(d,w)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(d,h,w)=>this.emitEngine(d,h,w),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:p,nodeIndex:f,blockIndex:g,blockById:ce,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:dt(e),padIndex:ut(e),registry:k,ownsRegistry:pe,patterBag:de,hostScopes:ue,patterSharedDecls:Q,patterLocalDecls:Ue,patterSharedNames:We,sceneSharedNames:ge,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(d=>console.warn(`Patterplay: ${d.kind} on ${d.node} in flow \'${d.flow}\' failed, played through: ${d.message}`)),replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??re).open,captionClose:(e.closedCaptions??re).close,captionCharacter:e.closedCaptions?.character||Ee,refSplitCache:new Map,refSplitRevision:k.revision}}get locale(){return this.currentLocale}get isSourceDebug(){return this.sourceDebug}get buildId(){return this.host.bundle.content?.hash??""}setLocale(e){this.currentLocale=e,this.host.strings=this.allStrings[e]??{}}replaceStrings(e){this.allStrings=e.strings,this.host.strings=this.allStrings[this.currentLocale]??{},this.host.defaultStrings=this.allStrings[this.host.bundle.locales.default]??{}}hotSwap(e){this.refuseInCheckpoint("hotSwap");let t=this.saveGame(),r=o=>(o.setLocale(this.currentLocale),o.setClosedCaptions(this.host.captionsOn),o),s=()=>new n(e,{...this.creationOptions,registry:this.host.registry,[Oe]:this.host.ownsRegistry});this.release(!0);let i=s();try{return i.loadGame(t),r(i)}catch{i.release(!0);let o=s();for(let[a,c]of Object.entries(t.flows)){let l=c.cursor.currentSceneId;try{o.openFlow(a,l!==null?{scene:l}:{})}catch{}}return r(o)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:r,blockId:s}=this.resolveOpenAddress(t.scene,t.block),i=this.flowsById.get(e),o=this.host.journal;if(o&&i&&!i.isClosed)throw new Error(`openFlow would replace the open flow \'${e}\', which can\'t be undone while a checkpoint is open`);i?.close();let a=new D(e,this.host,t.seed??this.defaultSeed);return o&&(o.flows.add(a),o.opened.add(a),o.undo.push(()=>{a.close(),i?this.flowsById.set(e,i):this.flowsById.delete(e)})),this.flowsById.set(e,a),a.begin(r,s),a}runFlow(e,t,r){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:r}).advanceToStop().played;if(!s.goto(t,r))throw new Error(`runFlow: address not found: ${t}${r===void 0?"":` / ${r}`}`);return s.advanceToStop().played}assertExternalScopes(){for(let e of this.host.bundle.externalScopes??[])if(!this.host.registry.has(e))throw new Error(`this content names @${e}, which no engine on this registry registered: give every engine the game\'s one registry`)}resolveOpenAddress(e,t){if(e!==void 0){let r=se(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=ie(this.host,r,t);if(s===void 0)throw new Error(`unknown block: ${t}`);return{sceneId:r,blockId:s}}if(t!==void 0){if(!this.host.blockIndex.has(t))throw new Error(`unknown block: ${t}`);return{blockId:t}}if(Object.keys(this.host.bundle.scenes).length===0)throw new Error("no scenes in bundle");return{}}resolveSceneRef(e){if(e!=null)return se(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:ie(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?y(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?y(t):void 0}tagsForBeat(e){return I(this.host,e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?I(this.host,t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?I(this.host,s):void 0)??[]}gameDataForScene(e){let t=this.resolveSceneRef(e);return{...(t!=null?this.host.bundle.scenes[t]:void 0)?.gameData}}gameDataForBlock(e,t){let r=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(r!=null?this.host.blockById.get(r):void 0)?.gameData}}getCast(){let e=[];for(let t of this.host.bundle.cast??[])t?.name&&e.push(t.name);return e}castForScene(e){let t=this.resolveSceneRef(e),r=t!=null?this.host.bundle.scenes[t]:void 0;if(!r)return[];let s=new Set;for(let i of r.blocks)Be(i.children,s);return[...s]}castForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t),i=s!=null?this.host.blockById.get(s):void 0;if(!i)return[];let o=new Set;return Be(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...y(e)?{gameId:y(e)}:{},name:e.name,...Ae(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...y(t)?{gameId:y(t)}:{},name:t.name,...Ae(t.gameData),...this.tagsField(t.id),children:t.children.map(r=>this.outlineNode(r))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let r of t.blocks)R(r.children,s=>{if(s.type==="snippet")for(let i of s.beats??[])e.push({sceneId:t.id,blockId:r.id,snippetId:s.id,beat:this.beatInfo(i)})});return e}outlineNode(e){return e.type==="group"?{type:"group",id:e.id,...this.tagsField(e.id),...e.selector?{selector:e.selector}:{},...e.prompt?{prompt:this.beatInfo(e.prompt)}:{},children:e.children.map(t=>this.outlineNode(t))}:{type:"snippet",id:e.id,...this.tagsField(e.id),beats:(e.beats??[]).map(t=>this.beatInfo(t)),...e.jump?{jumpTo:e.jump.to,...e.jump.mode?{jumpMode:e.jump.mode}:{}}:{}}}beatInfo(e){let t=I(this.host,e.id),r={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){r.character=e.character;let s=this.host.defaultStrings[ee(e.character)]??this.host.castDisplay.get(e.character);s!==void 0&&(r.characterName=s)}if(e.direction!==void 0&&(r.direction=e.direction),e.qualifier!==void 0){r.qualifier=e.qualifier;let s=this.host.defaultStrings[te(e.qualifier)]??this.host.qualifierDisplay.get(e.qualifier);s!==void 0&&(r.qualifierName=s)}}if(e.kind==="line"||e.kind==="text"){let s=this.host.padIndex.get(e.id);r.padAfter=s?.resolved??x,s?.own!==void 0&&(r.ownPadAfter=s.own);let i=this.host.defaultStrings[e.id];i!==void 0&&(r.text=i)}return e.gameData&&Object.keys(e.gameData).length&&(r.gameData=e.gameData),t&&t.length&&(r.tags=t),r}tagsField(e){let t=I(this.host,e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}listBags(){let e=[{bag:this.host.patterBag}];for(let[t,r]of this.host.stageBags)e.push({bag:r,pathPrefix:`@scene:${t}.`});return e}checkpoint(){if(this.host.journal)throw new Error("a checkpoint is already open: roll it back or commit it first");let e={undo:[],flows:new Set,opened:new Set,selectors:new Set,flowSelectors:new Map};return this.host.journal=e,new j(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let r=t.length-1;r>=0;r--)t[r]();n.forget(e.journal)}commit(e){this.endCheckpoint(e),n.forget(e.journal)}get inCheckpoint(){return this.host.journal!==null}endCheckpoint(e){if(this.host.journal!==e.journal)throw new Error("that checkpoint is not the open one");this.host.journal=null}static forget(e){e.undo=[],e.flows.clear(),e.opened.clear(),e.selectors.clear(),e.flowSelectors.clear()}refuseInCheckpoint(e){if(this.host.journal)throw new Error(`${e} can\'t be undone, so it isn\'t allowed while a checkpoint is open`)}captureFlow(e){let t=this.flowsById.get(e);if(!t)throw new Error(`no open flow \'${e}\' to capture`);return{version:F,...t.snapshot(),...St(e,this.host.registry.save())}}restoreFlow(e,t){this.refuseInCheckpoint("restoreFlow");let r=t.version;if(r!==2&&r!==F)throw new Error(`unsupported flow state version: ${String(r)}`);let s=t.cursor;if(typeof s!="object"||s===null)throw new Error("malformed flow state: no cursor");if(this.flowsById.has(e))throw new Error(`a flow \'${e}\' is already open; close it before restoring another under that id`);this.assertExternalScopes();let i=S.flow(e);this.host.registry.discardParked(i),this.host.registry.load(qe(e,t),{keepParked:!0});let o=new D(e,this.host,this.defaultSeed);try{o.restore(t)}catch(a){throw o.close(),this.host.registry.discardParked(i),a}return this.flowsById.set(e,o),o}closeFlow(e){this.refuseInCheckpoint("closeFlow"),this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){this.refuseInCheckpoint("reset");for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.patterBag.reseed(this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear();for(let e of this.host.stageBags.keys())this.host.registry.remove(S.stage(e));this.host.stageBags.clear(),this.host.registry.discardParked("patter/")}release(e){for(let r of this.flowsById.values())r.releaseBags(e),r.close();this.flowsById.clear();let t=this.host.registry;for(let r of this.host.stageBags.keys())t.remove(S.stage(r),{keep:e});this.host.stageBags.clear();for(let r of["patter",...this.host.hostScopes])t.has(r)&&t.remove(r,{keep:e});for(let r of Object.keys(Te(this.creationOptions)))t.has(r)&&t.remove(r)}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.registry.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e),i=this.host.journal?this.host.registry.get(r,s):void 0;this.host.registry.set(r,s,t,{host:!0}),this.host.journal&&i!==void 0&&this.host.journal.undo.push(()=>this.host.registry.set(r,s,i,{host:!0}))}log(){return this.engineLog}clearLog(){this.engineLog.length=0}onTrace(e){return this.engineTraceHandlers.add(e),this.host.tracing=!0,()=>{this.engineTraceHandlers.delete(e),this.host.tracing=this.host.logEnabled||this.engineTraceHandlers.size>0}}emitEngine(e,t,r){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineSeq++,...r?{scene:r}:{}})}getVisitCounts(){return Object.fromEntries(this.host.sharedVisits)}listProperties(){return this.host.patterSharedDecls.map(e=>({name:e.name,path:`@patter.${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:B(e),writable:e.writable??!0}))}splitShared(e){let t=Le(this.host,e);if(t.scope==="scene")throw new Error(`\'${e}\': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);return t}save(){return{patter:this.host.patterBag.save()}}load(e){e.patter&&this.host.patterBag.load(e.patter)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:F,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:Ve(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==F)throw new Error(`unsupported save version: ${String(t)}`);let r=e.flows;if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("malformed save: no flows");this.assertExternalScopes();let s=this.host.registry;for(let[o,a]of this.flowsById)a.releaseBags(o in e.flows),a.close();this.flowsById.clear();for(let o of this.host.stageBags.keys())s.remove(S.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.version===2?mt(e):e.registry;i&&(this.host.ownsRegistry?s.load(i):s.load(i,{keepParked:!0})),this.host.sharedVisits.clear();for(let[o,a]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(o,a);this.host.sharedSelectors.clear();for(let[o,a]of je(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new D(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}}},D=class{id;host;local;registered=new Set;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPrompt=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.patterBag.get(e):this.local.get(e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.registry.set("patter",e,t):this.local.set(e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;if(t!==null)return this.sceneBagFor(t,e)?.get(e)},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.sceneBagFor(r,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,r){this.id=e,this.host=t,this.rngState=O(r),this.local=this.newLocal(),this.evalCtx={scopes:{},host:{nextRandom:this.rng,visits:s=>this.visitCounts.get(s)??0,patterVisits:s=>this.host.sharedVisits.get(s)??0},qualities:(s,i)=>this.stagesFor(s,i)}}context(){let e=this.host.registry;if(e.revision!==this.ctxRevision){let t=e.toEvalContext(),r=this.evalCtx.scopes;for(let s of Object.keys(r))delete r[s];Object.assign(r,t.scopes),r.patter=this.patterResolver,r.scene=this.sceneResolver,this.registryQualities=t.qualities,this.ctxRevision=e.revision}return this.evalCtx}stagesFor(e,t){let r=t.toLowerCase();if(e==="patter")return ne(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(r);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:ne(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(r)}return this.registryQualities?.(e,t)??ne(this.host,`host/${e}`,()=>this.host.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(r)}begin(e,t){if(this.host.journal&&!this.host.journal.opened.has(this))throw new Error("a flow can\'t be started or reset while a checkpoint is open");if(this.releaseBags(!1),this.host.registry.discardParked(S.flow(this.id)),this.mountLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.activeSnippet=null,this.beatIndex=0,this.clearPending(),this.started=!0,t){let r=this.host.blockIndex.get(t);if(!r)throw new Error(`unknown block: ${t}`);this.enterSceneSetup(r.sceneId),this.stack=[{sceneId:r.sceneId,containerId:t,index:0}],this.enter(t)}else{let r=e??Object.keys(this.host.bundle.scenes)[0],s=r?this.host.bundle.scenes[r]:void 0;if(!s)throw new Error(r?`unknown scene: ${r}`:"no scenes in bundle");this.enterSceneSetup(r);let i=s.blocks[0];i&&(this.stack=[{sceneId:r,containerId:i.id,index:0}],this.enter(i.id))}this.settle()}reset(e,t){this.begin(e,t)}start(e,t){this.reset(e,t)}goto(e,t){if(this.closed)return!1;if(this.touch(),e==="END")return this.started=!0,this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let r=se(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=ie(this.host,r,t),s===void 0)?!1:this.started?(this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??r,"jump"),this.settle(),!0):(this.begin(r,s),!0)}close(){this.releaseBags(!1),this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.clearPending()}clearPending(){this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null}touch(){let e=this.host.journal;if(!e||e.flows.has(this))return;e.flows.add(this);let{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p}=this,f=this.stack.map(g=>({...g}));e.undo.push(()=>{Object.assign(this,{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p,stack:f})})}get isClosed(){return this.closed}get currentScene(){return this.currentSceneId}advance(){if(this.closed)return{type:"end"};if(!this.started)throw new Error("flow has not been started");if(this.touch(),this.pendingPrompt){let e=this.pendingPrompt;return this.pendingPrompt=null,this.pendingPromptOwnerId=null,e.shown?this.promptResult(e.beat,e.shown):this.beatResult(e.beat)}return this.settle(),this.flowEnded?{type:"end"}:this.pendingChoice?{type:"choice",groupId:this.pendingChoice.groupId,options:this.pendingChoice.options}:this.activeSnippet?this.beatResult(this.activeSnippet.beats[this.beatIndex++]):(this.flowEnded=!0,{type:"end"})}advanceToStop(){let e=[];for(;;){let t=this.advance();if(t.type==="choice"||t.type==="end")return{played:e,stop:t};e.push(t)}}settle(){let e=0;for(;;){if(++e>1e4)throw new Error("flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content");if(this.flowEnded||this.pendingChoice)return;if(this.activeSnippet){if(this.beatIndex<(this.activeSnippet.beats?.length??0))return;this.runEffects(this.activeSnippet.onExit,this.activeSnippet.id);let i=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(i);continue}let t=this.stack[this.stack.length-1];if(!t){this.flowEnded=!0;return}t.sceneId!==this.currentSceneId&&(this.currentSceneId=t.sceneId);let r=this.childrenOf(t.containerId);if(!r){this.stack.pop();continue}let s=t.index;for(;t.index<r.length&&!this.eligible(r[t.index]);)t.index++;if(this.host.tracing&&t.index!==s&&this.emit({type:"select",group:t.containerId,selector:"run",children:r.slice(s,t.index+1).map((i,o)=>({id:i.id,eligible:s+o===t.index})),picked:r[t.index]?.id??null}),t.index>=r.length){this.stack.pop();continue}this.enterChild(r[t.index++])}}listBags(){let e=[{bag:this.local,pathPrefix:`${this.id}/@patter.`}];for(let[t,r]of this.sceneBags)e.push({bag:r,pathPrefix:`${this.id}/@scene:${t}.`});return e}log(){return this.flowLog}clearLog(){this.flowLog.length=0}emit(e){let t=this.currentSceneId??void 0;this.host.emitEngine(this.id,e,t),this.host.logEnabled&&this.flowLog.push({...e,seq:this.flowSeq++,...t?{scene:t}:{}})}getVisitCounts(){return Object.fromEntries(this.visitCounts)}getChoices(){return this.pendingChoice?.options??[]}choose(e){let t=this.pendingChoice;if(!t)throw new Error("no choice is pending");let r=t.options.find(o=>o.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);this.touch();let s=t.byId.get(e);this.host.tracing&&this.emit({type:"chose",group:t.groupId,option:e}),this.pendingChoice=null;let i=this.host.replayPromptOnChoose?this.authoredPromptOf(s):void 0;this.pendingPrompt=i&&r.prompt?{beat:i,shown:{...r.prompt}}:null,this.pendingPromptOwnerId=this.pendingPrompt?s.id:null,this.enterChild(s)}isEnded(){return this.flowEnded}getProperty(e){let{scope:t,name:r}=this.splitRef(e);return t==="patter"?this.patterResolver.get(r):t==="scene"?this.sceneResolver.get(r):this.host.registry.get(t,r)}setProperty(e,t){this.writeProperty(e,t,!0)}writeProperty(e,t,r){let{scope:s,name:i}=this.splitRef(e),o=this.host.journal;if(s==="patter"){if(o){let a=this.host.patterSharedNames.has(i),c=a?this.host.patterBag.get(i):this.local.get(i),l=this.local;c!==void 0&&o.undo.push(a?()=>this.host.registry.set("patter",i,c):()=>l.set(i,c))}this.patterResolver.set(i,t)}else if(s==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);if(o){let a=this.sceneBagFor(this.currentSceneId,i),c=a?.get(i);a&&c!==void 0&&o.undo.push(()=>a.set(i,c))}this.sceneResolver.set(i,t)}else{let a=o?this.host.registry.get(s,i):void 0;this.host.registry.set(s,i,t,r?{host:!0}:void 0),o&&a!==void 0&&o.undo.push(()=>this.host.registry.set(s,i,a,{host:!0}))}}snapshot(){return{rngState:this.rngState,visits:Object.fromEntries(this.visitCounts),cursor:{flowEnded:this.flowEnded,currentSceneId:this.currentSceneId,stack:this.stack.map(e=>{let t=this.childrenOf(e.containerId)?.[e.index];return t?{...e,nextId:t.id}:{...e}}),activeSnippetId:this.activeSnippet?.id??null,beatIndex:this.beatIndex,pendingChoice:this.pendingChoice?{groupId:this.pendingChoice.groupId,options:this.pendingChoice.options.map(e=>({...e}))}:null,pendingPromptOwnerId:this.pendingPromptOwnerId,...this.pendingPrompt?.shown?{pendingPrompt:ft(this.pendingPrompt.shown)}:{},selectors:Ve(this.selectors)}}}restore(e){if(this.host.journal)throw new Error("a flow can\'t be restored while a checkpoint is open");this.restoreCursor(e),this.releaseBags(!0),this.mountLocal();for(let t of new Set([this.currentSceneId,...this.stack.map(r=>r.sceneId)]))t!==null&&this.host.bundle.scenes[t]&&this.ensureSceneBags(t)}restoreCursor(e){this.rngState=O(e.rngState),this.visitCounts=new Map(Object.entries(e.visits??{}));let t=e.cursor;if(this.started=!0,this.flowEnded=t.flowEnded,this.beatIndex=t.beatIndex,this.currentSceneId=t.currentSceneId,this.stack=t.stack.map(r=>{let{nextId:s,...i}=r;if(s!==void 0){let o=this.childrenOf(i.containerId)?.findIndex(a=>a.id===s)??-1;if(o>=0)return{...i,index:o}}return{...i}}),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=je(t.selectors),this.pendingChoice=null,t.pendingChoice!==null){let r=new Map,s=[];for(let i of t.pendingChoice.options){let o=this.host.nodeIndex.get(i.id);if(!o)continue;r.set(i.id,o);let{prompt:a,...c}=i;s.push({...c,...a?{prompt:{...a,padAfter:a.padAfter??this.promptPadOf(o)}}:{}})}s.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:s,byId:r})}if(this.pendingPrompt=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId),s=r?this.authoredPromptOf(r):void 0;s?this.pendingPrompt={beat:s,...t.pendingPrompt?{shown:{...t.pendingPrompt,padAfter:t.pendingPrompt.padAfter??this.padOf(s.id)}}:{}}:this.pendingPromptOwnerId=null}}enterSceneSetup(e){let t=this.host.bundle.scenes[e];if(!t)throw new Error(`unknown scene: ${e}`);this.currentSceneId=e,this.enter(e),this.seedScene(t),this.runEffects(t.onEntry,t.id)}enterChild(e){if(this.enter(e.id),e.type==="snippet"){this.beginSnippet(e);return}let t=e.selector??"run";if(t==="run"){this.stack.push({sceneId:this.currentSceneId,containerId:e.id,index:0});return}if(t==="choice"){this.setupChoice(e);return}let r=this.selectChild(e);r&&this.enterChild(r)}childrenOf(e){let t=this.host.blockById.get(e);if(t)return t.children;let r=this.host.nodeIndex.get(e);if(r&&r.type==="group")return r.children}beginSnippet(e){this.runEffects(e.onEnter,e.id),this.activeSnippet=e,this.beatIndex=0}setupChoice(e){let t=[],r=new Map,s=[];for(let o of e.children){if(o.fallback===!0){s.push(o);continue}if(o.sticky!==!0&&(this.visitCounts.get(o.id)??0)>=1)continue;let a=this.eligible(o),c=o.secretUntilEligible===!0;!a&&c||(t.push({id:o.id,prompt:this.promptFor(o),eligible:a,gameData:o.gameData}),r.set(o.id,o))}if(t.some(o=>o.eligible)){this.host.tracing&&this.emit({type:"choice",group:e.id,options:t.map(o=>({id:o.id,eligible:o.eligible}))}),this.pendingChoice={groupId:e.id,options:t,byId:r};return}let i=s.find(o=>this.eligible(o));if(i){this.enterChild(i);return}this.host.tracing&&this.emit({type:"dry",group:e.id}),this.host.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(this.host.tracing&&this.emit({type:"jump",to:e,mode:t}),e==="END"){this.flowEnded=!0,this.stack=[];return}let r,s,i=this.host.bundle.scenes[e];if(i){this.enterSceneSetup(e);let a=i.blocks[0];if(!a){t==="jump"&&(this.stack=[]);return}r=e,s=a.id}else{let a=this.host.blockIndex.get(e);if(!a)throw new Error(`jump target not found: ${e}`);a.sceneId!==this.currentSceneId&&this.enterSceneSetup(a.sceneId),r=a.sceneId,s=e}this.enter(s);let o={sceneId:r,containerId:s,index:0};t==="call"?this.stack.push(o):this.stack=[o]}selectChild(e){let t=e.children.map(c=>({id:c.id,eligible:this.eligible(c)})),r=e.children.filter((c,l)=>t[l].eligible),s=e.options?.order??"sequential",i=e.options?.exhaust??"once",o=c=>(this.host.tracing&&this.emit({type:"select",group:e.id,selector:e.selector??"default",...e.selector==="sequence"?{order:s,exhaust:i}:{},children:t,picked:c?.id??null}),c);if(r.length===0)return o(null);let a=this.selectorState(e);switch(e.selector){case"branch":return o(r[0]);case"sequence":return o(s==="shuffle"?this.pickShuffle(r,i,a):s==="specificity"?this.pickSpecificity(r,i,a):this.pickSequential(r,i,a));default:return null}}pickSequential(e,t,r){let s=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,r){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(g=>g.id),a=new Set(e.map(g=>g.id));if(r.bag===void 0&&(r.bag=o()),!r.bag.some(g=>a.has(g))){if(t==="once")return null;if(i){let g=e[s-1];return r.last=g.id,g}if(r.bag=o(),r.bag.length===0)return null}let c=r.bag.filter(g=>a.has(g)),l=r.last!==void 0&&c.length>1?c.indexOf(r.last):-1,p=Math.floor(this.rng()*(l>=0?c.length-1:c.length));l>=0&&p>=l&&p++;let f=c[p];return r.bag.splice(r.bag.indexOf(f),1),r.last=f,e.find(g=>g.id===f)}pickSpecificity(e,t,r){let s=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(p=>p.id));let l=new Set(r.bag);if(s=e.filter(p=>l.has(p.id)),s.length===0)return t==="stick"&&r.last!==void 0?e.find(p=>p.id===r.last)??null:null}let i=-1,a=s.map(l=>{let p=this.specScore(l);return p>i&&(i=p),{c:l,s:p}}).filter(l=>l.s===i).map(l=>l.c),c;if(a.length===1)c=a[0];else{let l=r.last!==void 0?a.findIndex(f=>f.id===r.last):-1,p=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&p>=l&&p++,c=a[p]}return t!=="repeat"&&(r.bag=r.bag.filter(l=>l!==c.id)),r.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0,e):0}matchedSpec(e,t,r){return he(e,i=>{try{return Ge(q(i,this.context(),Z))}catch(o){return this.reportError("best-match",r.id,r.condition,o),!1}},{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=this.host.journal,s;if(r&&(s=e.shared?r.selectors:r.flowSelectors.get(this),s||(s=new Set,r.flowSelectors.set(this,s))),r&&s&&!s.has(e.id)){s.add(e.id);let o=t.get(e.id),a=o?{...o,...o.bag?{bag:[...o.bag]}:{}}:void 0;r.undo.push(()=>{a?t.set(e.id,a):t.delete(e.id)})}let i=t.get(e.id);return i||(i={},t.set(e.id,i)),i}runEffects(e,t){for(let r of e??[]){let s;try{s=this.evalExpr(r.value);let i=this.host.tracing?this.getProperty(r.target):void 0;this.writeProperty(r.target,s,!1),this.host.tracing&&this.emit({type:"write",target:r.target,value:s,...i!==void 0?{prev:i}:{}})}catch(i){this.reportError("effect",t,r.value,i)}}}eligible(e){if(!e.condition)return!0;try{return Ge(this.evalExpr(e.condition))}catch(t){return this.reportError("condition",e.id,e.condition,t),!1}}reportError(e,t,r,s){let i=s instanceof Error?s.message:String(s),o=r?.src;this.host.onError({flow:this.id,kind:e,node:t,...o?{source:o}:{},message:i}),this.host.tracing&&this.emit({type:"diagnostic",kind:e,node:t,...o?{source:o}:{},message:i})}evalExpr(e){return q(this.conditionAst(e),this.context(),Z)}conditionAst(e){let t=De.get(e);return t||(t=E(e.ast),De.set(e,t)),t}enter(e){let t=this.visitCounts,r=t.get(e);t.set(e,(r??0)+1);let s=this.host.sharedVisits,i=s.get(e);s.set(e,(i??0)+1),this.host.journal&&this.host.journal.undo.push(()=>{r===void 0?t.delete(e):t.set(e,r),i===void 0?s.delete(e):s.set(e,i)})}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=Y(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=I(this.host,e.id),r=t&&t.length?{tags:t}:{};switch(e.kind){case"gameEvent":return{type:"gameEvent",id:e.id,gameData:e.gameData,...r};case"text":return{type:"text",id:e.id,text:this.interpolate(this.resolveString(e.id)),padAfter:this.padOf(e.id),gameData:e.gameData,...r};case"line":{let s=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?s:this.interpolate(s)),c=i&&a.length===0;return{type:"line",id:e.id,text:a,character:c?void 0:e.character,characterName:c?void 0:this.resolveCharacterName(e.character),direction:c?void 0:e.direction,qualifier:c?void 0:e.qualifier,qualifierName:c?void 0:this.resolveQualifierName(e.qualifier),padAfter:this.padOf(e.id),gameData:e.gameData,...r}}}}interpolate(e){return ke(e,t=>this.getProperty(t))}stripCaptions(e){return xe(e,this.host.captionOpen,this.host.captionClose)}captionLine(e){return this.host.captionsOn?e:this.stripCaptions(e)}promptFor(e){let t=this.promptBeatOf(e);if(!t)return;let r=this.interpolate(this.resolveString(t.id)),s=this.padOf(t.id);return t.kind==="line"?{kind:"line",text:this.captionLine(r),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction,qualifier:t.qualifier,qualifierName:this.resolveQualifierName(t.qualifier),padAfter:s}:{kind:"text",text:r,padAfter:s}}authoredPromptOf(e){return e.type==="group"?e.prompt:void 0}promptResult(e,t){let r=I(this.host,e.id),s=r&&r.length?{tags:r}:{};return t.kind==="text"?{type:"text",id:e.id,text:t.text,padAfter:this.padOf(e.id),gameData:e.gameData,...s}:{type:"line",id:e.id,text:t.text,character:t.character,characterName:t.characterName,direction:t.direction,qualifier:t.qualifier,qualifierName:t.qualifierName,padAfter:this.padOf(e.id),gameData:e.gameData,...s}}promptBeatOf(e){return e.type==="group"&&e.prompt?e.prompt:((e.type==="snippet"?e:this.firstTextSnippetIn(e.children))?.beats??[]).find(r=>r.kind==="line"||r.kind==="text")}firstTextSnippetIn(e){let t;return R(e,r=>{!t&&r.type==="snippet"&&(r.beats??[]).some(s=>s.kind==="line"||s.kind==="text")&&(t=r)}),t}resolveString(e){if(this.host.emitIds)return e;let t=this.host.strings[e];if(t!==void 0)return t;let r=this.host.defaultStrings[e];return r!==void 0?`<Untranslated: ${e}> ${r}`:e}resolveCharacterName(e){if(e===void 0||this.host.emitIds)return;let t=ee(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}promptPadOf(e){let t=this.promptBeatOf(e);return t?this.padOf(t.id):x}padOf(e){return this.host.padIndex.get(e)?.resolved??x}resolveQualifierName(e){if(e===void 0||this.host.emitIds)return;let t=te(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.qualifierDisplay.get(e)}splitRef(e){return Le(this.host,e)}newLocal(){return new b(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=S.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:V}),this.registered.add(e)}releaseBags(e){for(let t of this.registered)this.host.registry.remove(t,{keep:e});this.registered.clear(),this.sceneBags.clear()}sceneBagFor(e,t){if(this.host.bundle.scenes[e])return this.ensureSceneBags(e),this.host.sceneSharedNames.get(e)?.has(t.toLowerCase())?this.host.stageBags.get(e):this.sceneBags.get(e)}ensureSceneBags(e){let t=this.host.sceneSharedNames.get(e)??new Set,r=this.host.bundle.scenes[e]?.sceneProps??[],s=this.host.journal;if(!this.sceneBags.has(e)){let i=new b(r.filter(c=>!t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),o=S.flowScene(this.id,e),a=Fe(this.host.registry,o,i,s!==null);this.registered.add(o),this.sceneBags.set(e,i),s&&s.undo.push(()=>{this.sceneBags.get(e)===i&&(this.host.registry.remove(o,a?{keep:!0}:void 0),this.registered.delete(o),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let i=new b(r.filter(a=>t.has(a.name.toLowerCase())),{pathPrefix:"@scene."}),o=Fe(this.host.registry,S.stage(e),i,s!==null);this.host.stageBags.set(e,i),s&&s.undo.push(()=>{this.host.stageBags.get(e)===i&&(this.host.registry.remove(S.stage(e),o?{keep:!0}:void 0),this.host.stageBags.delete(e))})}}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;this.ensureSceneBags(e.id);for(let r of e.sceneProps??[]){if(!r.temporary)continue;let s=r.name.toLowerCase(),i=t.has(s)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);if(!i)continue;let o=this.host.journal?i.get(s):void 0;o!==void 0&&this.host.journal.undo.push(()=>i.set(s,o)),i.set(s,B(r))}}};function Be(n,e){R(n,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let r of t.beats??[])r.kind==="line"&&r.character&&e.add(r.character)})}function Ae(n){return n&&Object.keys(n).length?{gameData:n}:{}}function ft(n){let e={kind:n.kind,text:n.text};return n.character!==void 0&&(e.character=n.character),n.characterName!==void 0&&(e.characterName=n.characterName),n.direction!==void 0&&(e.direction=n.direction),n.qualifier!==void 0&&(e.qualifier=n.qualifier),n.qualifierName!==void 0&&(e.qualifierName=n.qualifierName),typeof n.padAfter=="number"&&(e.padAfter=n.padAfter),e}function Ve(n){let e={};for(let[t,r]of n){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e[t]=s}return e}function je(n){let e=new Map;for(let[t,r]of Object.entries(n??{})){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e.set(t,s)}return e}function Fe(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:V}),r&&JSON.stringify(t.save())!==s}function Ne(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Te(n){return{...n.world?{world:n.world}:{},...n.hostScopes}}function ne(n,e,t){let r=n.qualityLadders.get(e);if(!r){r=new Map;for(let s of t()??[]){let i=s.name.toLowerCase();s.type==="quality"&&s.stages&&!r.has(i)&&r.set(i,s.stages)}n.qualityLadders.set(e,r)}return r}function Me(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function ht(n,e){let t=Me(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function se(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function ie(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function Le(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=be(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function mt(n){let e={};n.shared?.patter&&(e.patter=n.shared.patter);for(let[t,r]of Object.entries(n.stageBags??{}))e[S.stage(t)]=r;for(let[t,r]of Object.entries(n.flows??{}))Object.assign(e,qe(t,r));return e}function qe(n,e){let t={};e.scopes?.patter&&(t[S.flowGlobals(n)]=e.scopes.patter);for(let[r,s]of Object.entries(e.sceneBags??{}))t[S.flowScene(n,r)]=s;return t}function St(n,e){let t={scopes:{},sceneBags:{}},r=S.flow(n);for(let[s,i]of Object.entries(e)){if(!s.startsWith(r))continue;let o=s.slice(r.length);o==="patter"?t.scopes.patter=i:o.startsWith("scene/")&&(t.sceneBags[gt(o.slice(6))]=i)}return t}function Ge(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var yt=(n,e)=>n.shared??e;function oe(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:yt(n,e)}}function wt(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function vt(n,e){e.blocks++;let t=[...n.children];for(;t.length;){let r=t.pop();if(r.type==="group"){e.groups++,r.prompt&&e.prompts++,t.push(...r.children);continue}e.snippets++;for(let s of r.beats??[])e.beats++,s.kind==="gameEvent"&&e.gameEvents++}}function $e(n){let e={scenes:0,blocks:0,groups:0,snippets:0,beats:0,prompts:0,gameEvents:0,cast:n.cast?.length??0},t=[],r=[];for(let o of Object.values(n.scenes)){e.scenes++;let a=y(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:y(c),name:c.name}))});for(let c of o.blocks)vt(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>oe(c,!1))})}let s=(n.scopeRegistry?.scopes??[]).map(o=>({token:o.token,writable:o.writable??!0,opaque:o.declarations===void 0,properties:(o.declarations??[]).map(a=>oe(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(wt)}));return{identity:{schema:n.schema,project:n.content.project,...n.content.version!==void 0?{version:n.content.version}:{},...n.content.hash!==void 0?{hash:n.content.hash}:{},...n.content.structureHash!==void 0?{structureHash:n.content.structureHash}:{},voiced:n.voiced,defaultLocale:n.locales.default,locales:[...n.locales.included],localisation:n.localisation?.mode??"embedded",sourceDebug:n.localisation?.sourceDebug??!1},addresses:t,hostScopes:s,properties:{patter:(n.properties??[]).map(o=>oe(o,!0)),scene:r},gameData:i,counts:e}}function He(n,e){return n.gameDataFields?.[e]??[]}function ae(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function _e(n,e){let t={};for(let r of n){let s=ae(n,e,r.name);s!==void 0&&(t[r.name]=s)}for(let[r,s]of Object.entries(e??{}))r in t||(t[r]=s);return t}return Ze(bt);})();\n';
|
|
201372
|
+
var PLAYABLE_TIMING_JS = 'var GameSubtitles=(()=>{var l=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var u=(t,e)=>{for(var s in e)l(t,s,{get:e[s],enumerable:!0})},f=(t,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of c(e))!_.call(t,i)&&i!==s&&l(t,i,{get:()=>e[i],enumerable:!(n=a(e,i))||n.enumerable});return t};var p=t=>f(l({},"__esModule",{value:!0}),t);var d={};u(d,{estimateDuration:()=>h});function h(t,{charsPerSecond:e=14,minSeconds:s=1.5,maxSeconds:n=18}={}){let i=0;for(let o of t??"")o!=="\\xAD"&&i++;let r=e>0?e:14;return Math.min(Math.max(i/r,s),n)}return p(d);})();\n';
|
|
201373
|
+
|
|
201374
|
+
// ../ops/src/play-timeline.ts
|
|
201375
|
+
function createTimeline(defaultPad) {
|
|
201376
|
+
let anchor = 0;
|
|
201377
|
+
let pad2 = null;
|
|
201378
|
+
let floor = 0;
|
|
201379
|
+
let last2 = 0;
|
|
201380
|
+
const startOf = () => pad2 === null ? anchor : Math.max(anchor + pad2, floor);
|
|
201381
|
+
return {
|
|
201382
|
+
startOf,
|
|
201383
|
+
place(beat, duration) {
|
|
201384
|
+
const start = startOf();
|
|
201385
|
+
const end = start + Math.max(0, duration);
|
|
201386
|
+
anchor = end;
|
|
201387
|
+
pad2 = typeof beat.padAfter === "number" ? beat.padAfter : defaultPad;
|
|
201388
|
+
floor = start;
|
|
201389
|
+
last2 = Math.max(last2, end);
|
|
201390
|
+
return { start, end };
|
|
201391
|
+
},
|
|
201392
|
+
choicesAt: () => floor,
|
|
201393
|
+
endAt: () => Math.max(last2, anchor)
|
|
201394
|
+
};
|
|
201395
|
+
}
|
|
201396
|
+
function createClock(now, rate) {
|
|
201397
|
+
let wall = now;
|
|
201398
|
+
let script = 0;
|
|
201399
|
+
let r = rate;
|
|
201400
|
+
const position = (at) => r === Infinity ? script : script + (at - wall) * r / 1e3;
|
|
201401
|
+
return {
|
|
201402
|
+
at(seconds) {
|
|
201403
|
+
if (r === Infinity) {
|
|
201404
|
+
script = Math.max(script, seconds);
|
|
201405
|
+
return wall;
|
|
201406
|
+
}
|
|
201407
|
+
return wall + (seconds - script) * 1e3 / r;
|
|
201408
|
+
},
|
|
201409
|
+
position,
|
|
201410
|
+
setRate(at, rate2) {
|
|
201411
|
+
script = position(at);
|
|
201412
|
+
wall = at;
|
|
201413
|
+
r = rate2;
|
|
201414
|
+
},
|
|
201415
|
+
delay(ms) {
|
|
201416
|
+
if (ms > 0) wall += ms;
|
|
201417
|
+
}
|
|
201418
|
+
};
|
|
201419
|
+
}
|
|
201280
201420
|
|
|
201281
201421
|
// ../ops/src/export-html.ts
|
|
201282
201422
|
var esc = (s) => s.replace(/[&<>]/g, (c2) => ({ "&": "&", "<": "<", ">": ">" })[c2]);
|
|
@@ -201296,37 +201436,80 @@ var PLAYER_JS = String.raw`
|
|
|
201296
201436
|
function add(cls, html) { var d = document.createElement("div"); d.className = cls; d.innerHTML = html; stage.appendChild(d); return d; }
|
|
201297
201437
|
function esc(s) { return String(s == null ? "" : s).replace(/[&<>]/g, function (c) { return { "&": "&", "<": "<", ">": ">" }[c]; }); }
|
|
201298
201438
|
|
|
201299
|
-
// Pacing: a source-only reader carries no audio, so each line is
|
|
201300
|
-
//
|
|
201301
|
-
//
|
|
201302
|
-
//
|
|
201303
|
-
|
|
201439
|
+
// Pacing (line padding): a source-only reader carries no audio, so each line is timed by the estimate
|
|
201440
|
+
// Patterstage uses for a line with no recording (game-subtitles' estimateDuration), and the step's padAfter
|
|
201441
|
+
// says when the next one starts: after a pause, at once, or cutting in before this one ends. The player
|
|
201442
|
+
// rules are play-timeline.ts's, inlined below by their source: the first line starts at once, as does the
|
|
201443
|
+
// reply to a choice; a cut-in never starts before the line it cuts into; a game event is never held here,
|
|
201444
|
+
// so it's done at once; the choices appear as the last line before them starts (while it plays); and the
|
|
201445
|
+
// end comes when every line still playing has finished. Every line fades in (see CSS).
|
|
201446
|
+
// The Speed control scales the whole timing (half, normal, or double speed) or drops it (instant), from
|
|
201447
|
+
// the moment it's changed: the clock keeps the time already played and re-times only what's to come. It
|
|
201448
|
+
// is stored under the key Patterpad's Play window uses, so the two keep one choice; the old reading paces
|
|
201449
|
+
// carry over (slow is half speed, fast is double).
|
|
201450
|
+
// A run token cancels an in-flight wait the moment the player restarts, loads, or picks a choice.
|
|
201451
|
+
var createTimeline = (${createTimeline.toString()});
|
|
201452
|
+
var createClock = (${createClock.toString()});
|
|
201453
|
+
var DEFAULT_PAD = ${DEFAULT_PAD_AFTER}; // the built-in pause, for a bundle from before padding
|
|
201454
|
+
var SPEED_RATE = { half: 0.5, normal: 1, double: 2, instant: Infinity }, OLD_SPEED = { slow: "half", fast: "double" }, runToken = 0;
|
|
201455
|
+
function own(o, k) { return Object.prototype.hasOwnProperty.call(o, k); }
|
|
201304
201456
|
var speedKey = (function () { try { return localStorage.getItem("patter.playSpeed") || "normal"; } catch (e) { return "normal"; } })();
|
|
201305
|
-
if (
|
|
201306
|
-
|
|
201307
|
-
|
|
201308
|
-
|
|
201309
|
-
|
|
201310
|
-
|
|
201457
|
+
if (own(OLD_SPEED, speedKey)) speedKey = OLD_SPEED[speedKey];
|
|
201458
|
+
if (!own(SPEED_RATE, speedKey)) speedKey = "normal";
|
|
201459
|
+
var estimate = window.GameSubtitles.estimateDuration;
|
|
201460
|
+
var clock = null, wake = null; // the run's clock, and a hook that re-times the wait in flight
|
|
201461
|
+
// Call then() when script time "at" comes on the run's clock. A change of speed wakes the wait, which
|
|
201462
|
+
// works out the time again; a superseded run's wait just stops.
|
|
201463
|
+
function waitUntil(at, token, then) {
|
|
201464
|
+
if (token !== runToken) return;
|
|
201465
|
+
var ms = clock.at(at) - Date.now();
|
|
201466
|
+
if (ms <= 0) { wake = null; then(); return; }
|
|
201467
|
+
var t = setTimeout(function () { waitUntil(at, token, then); }, ms);
|
|
201468
|
+
wake = function () { clearTimeout(t); waitUntil(at, token, then); };
|
|
201311
201469
|
}
|
|
201312
201470
|
function scrollDown() { window.scrollTo(0, document.body.scrollHeight); }
|
|
201313
201471
|
var speedSel = document.getElementById("speed");
|
|
201314
201472
|
if (speedSel) {
|
|
201315
|
-
speedSel.value = speedKey;
|
|
201316
|
-
speedSel.onchange = function () {
|
|
201473
|
+
speedSel.value = speedKey;
|
|
201474
|
+
speedSel.onchange = function () {
|
|
201475
|
+
speedKey = speedSel.value;
|
|
201476
|
+
try { localStorage.setItem("patter.playSpeed", speedKey); } catch (e) {}
|
|
201477
|
+
if (clock) clock.setRate(Date.now(), SPEED_RATE[speedKey]);
|
|
201478
|
+
if (wake) wake();
|
|
201479
|
+
};
|
|
201317
201480
|
}
|
|
201318
201481
|
|
|
201319
201482
|
function build() { engine = new Engine(BUNDLE); flow = engine.openFlow("main", { scene: startScene }); }
|
|
201320
201483
|
|
|
201321
201484
|
function newGame() { stage.innerHTML = ""; controls.innerHTML = ""; build(); run(); }
|
|
201322
201485
|
|
|
201486
|
+
// The player reads one step ahead of what it shows, as a game's would, to know whether a choice, the end,
|
|
201487
|
+
// or another line follows the line on screen. While a step is read but not yet shown, the engine is past
|
|
201488
|
+
// what the reader sees, so Save keeps the game as it was before the read (resumeSave).
|
|
201489
|
+
var resumeSave = null;
|
|
201490
|
+
function readNext() {
|
|
201491
|
+
try { resumeSave = JSON.stringify(engine.saveGame()); } catch (e) { resumeSave = null; }
|
|
201492
|
+
var s = flow.advance();
|
|
201493
|
+
while (s && s.type === "gameEvent") s = flow.advance(); // a host cue with nothing to show, done at once
|
|
201494
|
+
return s;
|
|
201495
|
+
}
|
|
201496
|
+
|
|
201323
201497
|
function run() {
|
|
201324
201498
|
controls.innerHTML = "";
|
|
201325
201499
|
var token = ++runToken;
|
|
201326
|
-
|
|
201327
|
-
|
|
201328
|
-
|
|
201329
|
-
|
|
201500
|
+
var timeline = createTimeline(DEFAULT_PAD);
|
|
201501
|
+
clock = createClock(Date.now(), SPEED_RATE[speedKey]);
|
|
201502
|
+
// A step read ahead appears when its time comes: a line or text when its pause is over, the choices
|
|
201503
|
+
// as the last line starts, the end as the last line still playing ends.
|
|
201504
|
+
function next(s) {
|
|
201505
|
+
var at = !s || s.type === "end" ? timeline.endAt() : s.type === "choice" ? timeline.choicesAt() : timeline.startOf();
|
|
201506
|
+
waitUntil(at, token, function () { show(s); });
|
|
201507
|
+
}
|
|
201508
|
+
function show(s) {
|
|
201509
|
+
resumeSave = null; // what's shown is where the engine is
|
|
201510
|
+
if (!s || s.type === "end") { add("end", "The End"); return; } // no step is defensive: never hard-crash
|
|
201511
|
+
if (s.type === "choice") { renderChoice(s); return; }
|
|
201512
|
+
timeline.place(s, estimate(s.text));
|
|
201330
201513
|
if (s.type === "line") {
|
|
201331
201514
|
var who = s.characterName || s.character || "";
|
|
201332
201515
|
// A speaker qualifier follows the name, as the script's cue has it: TAM (O.S.).
|
|
@@ -201334,13 +201517,13 @@ var PLAYER_JS = String.raw`
|
|
|
201334
201517
|
if (qual && who) who += " (" + qual + ")";
|
|
201335
201518
|
var dir = s.direction ? '<em class="dir">(' + esc(s.direction) + ')</em> ' : "";
|
|
201336
201519
|
add("line", '<span class="who" style="color:hsl(' + hueOf(s.character || who) + ',55%,38%)">' + esc(who) + '</span>' + dir + esc(s.text));
|
|
201337
|
-
|
|
201338
|
-
|
|
201339
|
-
|
|
201340
|
-
|
|
201341
|
-
|
|
201342
|
-
|
|
201343
|
-
|
|
201520
|
+
} else {
|
|
201521
|
+
add("text", esc(s.text));
|
|
201522
|
+
}
|
|
201523
|
+
scrollDown();
|
|
201524
|
+
next(readNext());
|
|
201525
|
+
}
|
|
201526
|
+
next(readNext()); // the first line (or the reply to a choice) starts at once
|
|
201344
201527
|
}
|
|
201345
201528
|
|
|
201346
201529
|
function renderChoice(step) {
|
|
@@ -201358,7 +201541,7 @@ var PLAYER_JS = String.raw`
|
|
|
201358
201541
|
}
|
|
201359
201542
|
|
|
201360
201543
|
document.getElementById("restart").onclick = newGame;
|
|
201361
|
-
document.getElementById("save").onclick = function () { try { localStorage.setItem(SAVE_KEY, JSON.stringify(engine.saveGame())); flash("Saved"); } catch (e) {} };
|
|
201544
|
+
document.getElementById("save").onclick = function () { try { localStorage.setItem(SAVE_KEY, resumeSave || JSON.stringify(engine.saveGame())); flash("Saved"); } catch (e) {} };
|
|
201362
201545
|
document.getElementById("load").onclick = function () {
|
|
201363
201546
|
var blob; try { blob = localStorage.getItem(SAVE_KEY); } catch (e) {}
|
|
201364
201547
|
if (!blob) { flash("No save yet"); return; }
|
|
@@ -201430,10 +201613,10 @@ ${head}
|
|
|
201430
201613
|
<button id="save">Save</button>
|
|
201431
201614
|
<button id="load">Load</button>
|
|
201432
201615
|
<label class="speedlabel">Speed
|
|
201433
|
-
<select id="speed" title="
|
|
201434
|
-
<option value="
|
|
201616
|
+
<select id="speed" title="How fast the scene plays: its timing at half speed, as written, at double speed, or with no waits">
|
|
201617
|
+
<option value="half">Half</option>
|
|
201435
201618
|
<option value="normal">Normal</option>
|
|
201436
|
-
<option value="
|
|
201619
|
+
<option value="double">Double</option>
|
|
201437
201620
|
<option value="instant">Instant</option>
|
|
201438
201621
|
</select>
|
|
201439
201622
|
</label>
|
|
@@ -201451,6 +201634,7 @@ function runExportHtml(loaded) {
|
|
|
201451
201634
|
lang,
|
|
201452
201635
|
`<style>${STYLE}</style>`,
|
|
201453
201636
|
`<script>${PLAYABLE_RUNTIME_JS}</script>
|
|
201637
|
+
<script>${PLAYABLE_TIMING_JS}</script>
|
|
201454
201638
|
<script>window.PATTER_BUNDLE=${bundleJson};</script>
|
|
201455
201639
|
<script>${PLAYER_JS}</script>`
|
|
201456
201640
|
);
|
|
@@ -201523,6 +201707,44 @@ function buildTagIndex(bundle) {
|
|
|
201523
201707
|
return index2;
|
|
201524
201708
|
}
|
|
201525
201709
|
|
|
201710
|
+
// ../runtime/src/pads.ts
|
|
201711
|
+
var spoken = (b) => b.kind === "line" || b.kind === "text";
|
|
201712
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
201713
|
+
function buildPadIndex(bundle) {
|
|
201714
|
+
const index2 = /* @__PURE__ */ new Map();
|
|
201715
|
+
const set = (beat, inherited, clamp) => {
|
|
201716
|
+
const own2 = beat.kind === "gameEvent" ? void 0 : num(beat.padAfter);
|
|
201717
|
+
let resolved = own2 ?? inherited;
|
|
201718
|
+
if (clamp && resolved < 0) resolved = 0;
|
|
201719
|
+
index2.set(beat.id, own2 !== void 0 ? { own: own2, resolved } : { resolved });
|
|
201720
|
+
};
|
|
201721
|
+
const visit = (node, inherited) => {
|
|
201722
|
+
const def = num(node.padAfterDefault) ?? inherited;
|
|
201723
|
+
if (node.type === "group") {
|
|
201724
|
+
if (node.prompt) set(node.prompt, def, false);
|
|
201725
|
+
for (const child2 of node.children) visit(child2, def);
|
|
201726
|
+
return;
|
|
201727
|
+
}
|
|
201728
|
+
const beats = node.beats ?? [];
|
|
201729
|
+
let last2 = -1;
|
|
201730
|
+
beats.forEach((b, i) => {
|
|
201731
|
+
if (spoken(b)) last2 = i;
|
|
201732
|
+
});
|
|
201733
|
+
beats.forEach((b, i) => {
|
|
201734
|
+
if (spoken(b)) set(b, def, i === last2);
|
|
201735
|
+
});
|
|
201736
|
+
};
|
|
201737
|
+
const projectDefault = num(bundle.padAfterDefault) ?? DEFAULT_PAD_AFTER;
|
|
201738
|
+
for (const scene of Object.values(bundle.scenes)) {
|
|
201739
|
+
const sceneDefault = num(scene.padAfterDefault) ?? projectDefault;
|
|
201740
|
+
for (const block of scene.blocks) {
|
|
201741
|
+
const blockDefault = num(block.padAfterDefault) ?? sceneDefault;
|
|
201742
|
+
for (const child2 of block.children) visit(child2, blockDefault);
|
|
201743
|
+
}
|
|
201744
|
+
}
|
|
201745
|
+
return index2;
|
|
201746
|
+
}
|
|
201747
|
+
|
|
201526
201748
|
// ../runtime/src/engine.ts
|
|
201527
201749
|
var astCache = /* @__PURE__ */ new WeakMap();
|
|
201528
201750
|
var tagIndexCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -201534,11 +201756,21 @@ function tagIndexFor(bundle) {
|
|
|
201534
201756
|
}
|
|
201535
201757
|
return index2;
|
|
201536
201758
|
}
|
|
201759
|
+
var padIndexCache = /* @__PURE__ */ new WeakMap();
|
|
201760
|
+
function padIndexFor(bundle) {
|
|
201761
|
+
let index2 = padIndexCache.get(bundle);
|
|
201762
|
+
if (!index2) {
|
|
201763
|
+
index2 = buildPadIndex(bundle);
|
|
201764
|
+
padIndexCache.set(bundle, index2);
|
|
201765
|
+
}
|
|
201766
|
+
return index2;
|
|
201767
|
+
}
|
|
201537
201768
|
function tagsOf(host2, id) {
|
|
201538
201769
|
return host2.tagIndex.get(id)?.slice();
|
|
201539
201770
|
}
|
|
201540
201771
|
var OWNER = "Patter";
|
|
201541
201772
|
var esc2 = (id) => id.replace(/%/g, "%25").replace(/\//g, "%2F");
|
|
201773
|
+
var unesc = (key2) => key2.replace(/%2F/g, "/").replace(/%25/g, "%");
|
|
201542
201774
|
var OWNS_REGISTRY = /* @__PURE__ */ Symbol("ownsRegistry");
|
|
201543
201775
|
var keys = {
|
|
201544
201776
|
/** A scene's SHARED `@scene` props (one bag per scene, every flow's). */
|
|
@@ -201669,6 +201901,7 @@ var Engine = class _Engine {
|
|
|
201669
201901
|
blockGameIdToId: this.blockGameIdToId,
|
|
201670
201902
|
// same instances the engine resolves with
|
|
201671
201903
|
tagIndex: tagIndexFor(bundle),
|
|
201904
|
+
padIndex: padIndexFor(bundle),
|
|
201672
201905
|
registry,
|
|
201673
201906
|
ownsRegistry,
|
|
201674
201907
|
patterBag,
|
|
@@ -202073,6 +202306,9 @@ var Engine = class _Engine {
|
|
|
202073
202306
|
}
|
|
202074
202307
|
}
|
|
202075
202308
|
if (beat.kind === "line" || beat.kind === "text") {
|
|
202309
|
+
const pad2 = this.host.padIndex.get(beat.id);
|
|
202310
|
+
info.padAfter = pad2?.resolved ?? DEFAULT_PAD_AFTER;
|
|
202311
|
+
if (pad2?.own !== void 0) info.ownPadAfter = pad2.own;
|
|
202076
202312
|
const source2 = this.host.defaultStrings[beat.id];
|
|
202077
202313
|
if (source2 !== void 0) info.text = source2;
|
|
202078
202314
|
}
|
|
@@ -202163,6 +202399,45 @@ var Engine = class _Engine {
|
|
|
202163
202399
|
refuseInCheckpoint(what) {
|
|
202164
202400
|
if (this.host.journal) throw new Error(`${what} can't be undone, so it isn't allowed while a checkpoint is open`);
|
|
202165
202401
|
}
|
|
202402
|
+
/**
|
|
202403
|
+
* Take flow `id` out of the engine as plain data, to be put back later with {@link restoreFlow}: its snapshot as
|
|
202404
|
+
* `saveGame` writes it, and its own property values without its id (so it can come back under another). Shared
|
|
202405
|
+
* state is never in it. Doesn't close the flow; close it with {@link closeFlow} AFTER capturing, since closing
|
|
202406
|
+
* drops its values. Only reads, so it is allowed while a checkpoint is open.
|
|
202407
|
+
*/
|
|
202408
|
+
captureFlow(id) {
|
|
202409
|
+
const flow = this.flowsById.get(id);
|
|
202410
|
+
if (!flow) throw new Error(`no open flow '${id}' to capture`);
|
|
202411
|
+
return { version: SAVE_VERSION, ...flow.snapshot(), ...flowValuesFrom(id, this.host.registry.save()) };
|
|
202412
|
+
}
|
|
202413
|
+
/**
|
|
202414
|
+
* Open flow `id` from a {@link captureFlow} state, the per-flow half of `loadGame`: its values go into the
|
|
202415
|
+
* registry as a load's do, and the flow is restored as a load restores each flow. `id` needn't be the id it was
|
|
202416
|
+
* captured from. Against an edited bundle it re-finds its place by id, as a load does. Refused if a flow of
|
|
202417
|
+
* that id is open, or while a checkpoint is.
|
|
202418
|
+
*/
|
|
202419
|
+
restoreFlow(id, state) {
|
|
202420
|
+
this.refuseInCheckpoint("restoreFlow");
|
|
202421
|
+
const version2 = state.version;
|
|
202422
|
+
if (version2 !== 2 && version2 !== SAVE_VERSION) throw new Error(`unsupported flow state version: ${String(version2)}`);
|
|
202423
|
+
const cursor = state.cursor;
|
|
202424
|
+
if (typeof cursor !== "object" || cursor === null) throw new Error("malformed flow state: no cursor");
|
|
202425
|
+
if (this.flowsById.has(id)) throw new Error(`a flow '${id}' is already open; close it before restoring another under that id`);
|
|
202426
|
+
this.assertExternalScopes();
|
|
202427
|
+
const parked = keys.flow(id);
|
|
202428
|
+
this.host.registry.discardParked(parked);
|
|
202429
|
+
this.host.registry.load(flowSections(id, state), { keepParked: true });
|
|
202430
|
+
const flow = new Flow(id, this.host, this.defaultSeed);
|
|
202431
|
+
try {
|
|
202432
|
+
flow.restore(state);
|
|
202433
|
+
} catch (e) {
|
|
202434
|
+
flow.close();
|
|
202435
|
+
this.host.registry.discardParked(parked);
|
|
202436
|
+
throw e;
|
|
202437
|
+
}
|
|
202438
|
+
this.flowsById.set(id, flow);
|
|
202439
|
+
return flow;
|
|
202440
|
+
}
|
|
202166
202441
|
/** Close (remove) a flow. The flow object is FINISHED, not merely unregistered, so a host still
|
|
202167
202442
|
* holding it cannot keep advancing it into the shared world (see {@link Flow.close}). */
|
|
202168
202443
|
closeFlow(id) {
|
|
@@ -202866,7 +203141,8 @@ var Flow = class {
|
|
|
202866
203141
|
const node = this.host.nodeIndex.get(o.id);
|
|
202867
203142
|
if (!node) continue;
|
|
202868
203143
|
byId.set(o.id, node);
|
|
202869
|
-
|
|
203144
|
+
const { prompt: saved, ...rest } = o;
|
|
203145
|
+
options.push({ ...rest, ...saved ? { prompt: { ...saved, padAfter: saved.padAfter ?? this.promptPadOf(node) } } : {} });
|
|
202870
203146
|
}
|
|
202871
203147
|
if (options.length > 0) this.pendingChoice = { groupId: c2.pendingChoice.groupId, options, byId };
|
|
202872
203148
|
}
|
|
@@ -202875,7 +203151,7 @@ var Flow = class {
|
|
|
202875
203151
|
if (this.pendingPromptOwnerId) {
|
|
202876
203152
|
const owner = this.host.nodeIndex.get(this.pendingPromptOwnerId);
|
|
202877
203153
|
const beat = owner ? this.authoredPromptOf(owner) : void 0;
|
|
202878
|
-
if (beat) this.pendingPrompt = { beat, ...c2.pendingPrompt ? { shown: { ...c2.pendingPrompt } } : {} };
|
|
203154
|
+
if (beat) this.pendingPrompt = { beat, ...c2.pendingPrompt ? { shown: { ...c2.pendingPrompt, padAfter: c2.pendingPrompt.padAfter ?? this.padOf(beat.id) } } : {} };
|
|
202879
203155
|
else this.pendingPromptOwnerId = null;
|
|
202880
203156
|
}
|
|
202881
203157
|
}
|
|
@@ -203245,7 +203521,7 @@ var Flow = class {
|
|
|
203245
203521
|
case "gameEvent":
|
|
203246
203522
|
return { type: "gameEvent", id: beat.id, gameData: beat.gameData, ...withTags };
|
|
203247
203523
|
case "text":
|
|
203248
|
-
return { type: "text", id: beat.id, text: this.interpolate(this.resolveString(beat.id)), gameData: beat.gameData, ...withTags };
|
|
203524
|
+
return { type: "text", id: beat.id, text: this.interpolate(this.resolveString(beat.id)), padAfter: this.padOf(beat.id), gameData: beat.gameData, ...withTags };
|
|
203249
203525
|
case "line": {
|
|
203250
203526
|
const raw = this.resolveString(beat.id);
|
|
203251
203527
|
const off = !this.host.captionsOn;
|
|
@@ -203261,6 +203537,8 @@ var Flow = class {
|
|
|
203261
203537
|
direction: silent ? void 0 : beat.direction,
|
|
203262
203538
|
qualifier: silent ? void 0 : beat.qualifier,
|
|
203263
203539
|
qualifierName: silent ? void 0 : this.resolveQualifierName(beat.qualifier),
|
|
203540
|
+
padAfter: this.padOf(beat.id),
|
|
203541
|
+
// a silent line still fires, so it still carries its pause
|
|
203264
203542
|
gameData: beat.gameData,
|
|
203265
203543
|
...withTags
|
|
203266
203544
|
};
|
|
@@ -203299,6 +203577,7 @@ var Flow = class {
|
|
|
203299
203577
|
const beat = this.promptBeatOf(node);
|
|
203300
203578
|
if (!beat) return void 0;
|
|
203301
203579
|
const text = this.interpolate(this.resolveString(beat.id));
|
|
203580
|
+
const padAfter = this.padOf(beat.id);
|
|
203302
203581
|
return beat.kind === "line" ? {
|
|
203303
203582
|
kind: "line",
|
|
203304
203583
|
text: this.captionLine(text),
|
|
@@ -203306,8 +203585,9 @@ var Flow = class {
|
|
|
203306
203585
|
characterName: this.resolveCharacterName(beat.character),
|
|
203307
203586
|
direction: beat.direction,
|
|
203308
203587
|
qualifier: beat.qualifier,
|
|
203309
|
-
qualifierName: this.resolveQualifierName(beat.qualifier)
|
|
203310
|
-
|
|
203588
|
+
qualifierName: this.resolveQualifierName(beat.qualifier),
|
|
203589
|
+
padAfter
|
|
203590
|
+
} : { kind: "text", text, padAfter };
|
|
203311
203591
|
}
|
|
203312
203592
|
/** An option's AUTHORED prompt beat: an Option group's own `prompt`. The only prompt a replay speaks. */
|
|
203313
203593
|
authoredPromptOf(node) {
|
|
@@ -203318,7 +203598,7 @@ var Flow = class {
|
|
|
203318
203598
|
promptResult(beat, shown2) {
|
|
203319
203599
|
const tags = tagsOf(this.host, beat.id);
|
|
203320
203600
|
const withTags = tags && tags.length ? { tags } : {};
|
|
203321
|
-
if (shown2.kind === "text") return { type: "text", id: beat.id, text: shown2.text, gameData: beat.gameData, ...withTags };
|
|
203601
|
+
if (shown2.kind === "text") return { type: "text", id: beat.id, text: shown2.text, padAfter: this.padOf(beat.id), gameData: beat.gameData, ...withTags };
|
|
203322
203602
|
return {
|
|
203323
203603
|
type: "line",
|
|
203324
203604
|
id: beat.id,
|
|
@@ -203328,6 +203608,7 @@ var Flow = class {
|
|
|
203328
203608
|
direction: shown2.direction,
|
|
203329
203609
|
qualifier: shown2.qualifier,
|
|
203330
203610
|
qualifierName: shown2.qualifierName,
|
|
203611
|
+
padAfter: this.padOf(beat.id),
|
|
203331
203612
|
gameData: beat.gameData,
|
|
203332
203613
|
...withTags
|
|
203333
203614
|
};
|
|
@@ -203364,6 +203645,17 @@ var Flow = class {
|
|
|
203364
203645
|
const key2 = castStringKey(character);
|
|
203365
203646
|
return this.host.strings[key2] ?? this.host.defaultStrings[key2] ?? this.host.castDisplay.get(character);
|
|
203366
203647
|
}
|
|
203648
|
+
/** The resolved pause of the beat an option shows as its prompt (its authored prompt, else its first content
|
|
203649
|
+
* line), for a saved option that predates prompts carrying one. */
|
|
203650
|
+
promptPadOf(node) {
|
|
203651
|
+
const beat = this.promptBeatOf(node);
|
|
203652
|
+
return beat ? this.padOf(beat.id) : DEFAULT_PAD_AFTER;
|
|
203653
|
+
}
|
|
203654
|
+
/** A line or text beat's resolved pause (line padding): its own `padAfter`, else the nearest default above
|
|
203655
|
+
* it, else the project's, else the built-in one; clamped to zero on a snippet's last line. */
|
|
203656
|
+
padOf(beatId) {
|
|
203657
|
+
return this.host.padIndex.get(beatId)?.resolved ?? DEFAULT_PAD_AFTER;
|
|
203658
|
+
}
|
|
203367
203659
|
/** A speaker qualifier's shown name, resolved as a character's is: the `qualifier:<gameId>` string in the
|
|
203368
203660
|
* active locale, else the default locale, else the authored name. Undefined in IDs-only mode (the game
|
|
203369
203661
|
* maps the `qualifier` gameId itself). */
|
|
@@ -203471,6 +203763,7 @@ function savedPrompt(p) {
|
|
|
203471
203763
|
if (p.direction !== void 0) out.direction = p.direction;
|
|
203472
203764
|
if (p.qualifier !== void 0) out.qualifier = p.qualifier;
|
|
203473
203765
|
if (p.qualifierName !== void 0) out.qualifierName = p.qualifierName;
|
|
203766
|
+
if (typeof p.padAfter === "number") out.padAfter = p.padAfter;
|
|
203474
203767
|
return out;
|
|
203475
203768
|
}
|
|
203476
203769
|
function serialiseSelectors(map2) {
|
|
@@ -203548,9 +203841,23 @@ function sectionsFromV2(save) {
|
|
|
203548
203841
|
const out = {};
|
|
203549
203842
|
if (save.shared?.patter) out["patter"] = save.shared.patter;
|
|
203550
203843
|
for (const [s, v] of Object.entries(save.stageBags ?? {})) out[keys.stage(s)] = v;
|
|
203551
|
-
for (const [id, f] of Object.entries(save.flows ?? {}))
|
|
203552
|
-
|
|
203553
|
-
|
|
203844
|
+
for (const [id, f] of Object.entries(save.flows ?? {})) Object.assign(out, flowSections(id, f));
|
|
203845
|
+
return out;
|
|
203846
|
+
}
|
|
203847
|
+
function flowSections(id, f) {
|
|
203848
|
+
const out = {};
|
|
203849
|
+
if (f.scopes?.patter) out[keys.flowGlobals(id)] = f.scopes.patter;
|
|
203850
|
+
for (const [s, v] of Object.entries(f.sceneBags ?? {})) out[keys.flowScene(id, s)] = v;
|
|
203851
|
+
return out;
|
|
203852
|
+
}
|
|
203853
|
+
function flowValuesFrom(id, saved) {
|
|
203854
|
+
const out = { scopes: {}, sceneBags: {} };
|
|
203855
|
+
const prefix = keys.flow(id);
|
|
203856
|
+
for (const [key2, values] of Object.entries(saved)) {
|
|
203857
|
+
if (!key2.startsWith(prefix)) continue;
|
|
203858
|
+
const rest = key2.slice(prefix.length);
|
|
203859
|
+
if (rest === "patter") out.scopes.patter = values;
|
|
203860
|
+
else if (rest.startsWith("scene/")) out.sceneBags[unesc(rest.slice("scene/".length))] = values;
|
|
203554
203861
|
}
|
|
203555
203862
|
return out;
|
|
203556
203863
|
}
|
|
@@ -206183,8 +206490,8 @@ var require_base64_js = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
206183
206490
|
}
|
|
206184
206491
|
return arr;
|
|
206185
206492
|
}
|
|
206186
|
-
function tripletToBase64(
|
|
206187
|
-
return lookup[
|
|
206493
|
+
function tripletToBase64(num2) {
|
|
206494
|
+
return lookup[num2 >> 18 & 63] + lookup[num2 >> 12 & 63] + lookup[num2 >> 6 & 63] + lookup[num2 & 63];
|
|
206188
206495
|
}
|
|
206189
206496
|
function encodeChunk(uint82, start, end) {
|
|
206190
206497
|
var tmp;
|
|
@@ -211961,26 +212268,26 @@ var require_sax = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
211961
212268
|
function parseEntity(parser) {
|
|
211962
212269
|
var entity = parser.entity;
|
|
211963
212270
|
var entityLC = entity.toLowerCase();
|
|
211964
|
-
var
|
|
212271
|
+
var num2;
|
|
211965
212272
|
var numStr = "";
|
|
211966
212273
|
if (parser.ENTITIES[entity]) return parser.ENTITIES[entity];
|
|
211967
212274
|
if (parser.ENTITIES[entityLC]) return parser.ENTITIES[entityLC];
|
|
211968
212275
|
entity = entityLC;
|
|
211969
212276
|
if (entity.charAt(0) === "#") if (entity.charAt(1) === "x") {
|
|
211970
212277
|
entity = entity.slice(2);
|
|
211971
|
-
|
|
211972
|
-
numStr =
|
|
212278
|
+
num2 = parseInt(entity, 16);
|
|
212279
|
+
numStr = num2.toString(16);
|
|
211973
212280
|
} else {
|
|
211974
212281
|
entity = entity.slice(1);
|
|
211975
|
-
|
|
211976
|
-
numStr =
|
|
212282
|
+
num2 = parseInt(entity, 10);
|
|
212283
|
+
numStr = num2.toString(10);
|
|
211977
212284
|
}
|
|
211978
212285
|
entity = entity.replace(/^0+/, "");
|
|
211979
|
-
if (isNaN(
|
|
212286
|
+
if (isNaN(num2) || numStr.toLowerCase() !== entity) {
|
|
211980
212287
|
strictFail(parser, "Invalid character entity");
|
|
211981
212288
|
return "&" + parser.entity + ";";
|
|
211982
212289
|
}
|
|
211983
|
-
return String.fromCodePoint(
|
|
212290
|
+
return String.fromCodePoint(num2);
|
|
211984
212291
|
}
|
|
211985
212292
|
function beginWhiteSpace(parser, c2) {
|
|
211986
212293
|
if (c2 === "<") {
|
|
@@ -214032,20 +214339,20 @@ var require_utils3 = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
214032
214339
|
return al + bl + cl + dl + el >>> 0;
|
|
214033
214340
|
}
|
|
214034
214341
|
exports2.sum64_5_lo = sum64_5_lo;
|
|
214035
|
-
function rotr64_hi(ah, al,
|
|
214036
|
-
return (al << 32 -
|
|
214342
|
+
function rotr64_hi(ah, al, num2) {
|
|
214343
|
+
return (al << 32 - num2 | ah >>> num2) >>> 0;
|
|
214037
214344
|
}
|
|
214038
214345
|
exports2.rotr64_hi = rotr64_hi;
|
|
214039
|
-
function rotr64_lo(ah, al,
|
|
214040
|
-
return (ah << 32 -
|
|
214346
|
+
function rotr64_lo(ah, al, num2) {
|
|
214347
|
+
return (ah << 32 - num2 | al >>> num2) >>> 0;
|
|
214041
214348
|
}
|
|
214042
214349
|
exports2.rotr64_lo = rotr64_lo;
|
|
214043
|
-
function shr64_hi(ah, al,
|
|
214044
|
-
return ah >>>
|
|
214350
|
+
function shr64_hi(ah, al, num2) {
|
|
214351
|
+
return ah >>> num2;
|
|
214045
214352
|
}
|
|
214046
214353
|
exports2.shr64_hi = shr64_hi;
|
|
214047
|
-
function shr64_lo(ah, al,
|
|
214048
|
-
return (ah << 32 -
|
|
214354
|
+
function shr64_lo(ah, al, num2) {
|
|
214355
|
+
return (ah << 32 - num2 | al >>> num2) >>> 0;
|
|
214049
214356
|
}
|
|
214050
214357
|
exports2.shr64_lo = shr64_lo;
|
|
214051
214358
|
}));
|
|
@@ -241862,8 +242169,8 @@ var StandardFont = class extends PDFFont {
|
|
|
241862
242169
|
return name in STANDARD_FONTS;
|
|
241863
242170
|
}
|
|
241864
242171
|
};
|
|
241865
|
-
var toHex = function(
|
|
241866
|
-
return `0000${
|
|
242172
|
+
var toHex = function(num2) {
|
|
242173
|
+
return `0000${num2.toString(16)}`.slice(-4);
|
|
241867
242174
|
};
|
|
241868
242175
|
var EmbeddedFont = class extends PDFFont {
|
|
241869
242176
|
constructor(document2, font, id) {
|
|
@@ -246683,7 +246990,7 @@ async function exportEditableScript(loaded, opts) {
|
|
|
246683
246990
|
if (isEditable(el)) {
|
|
246684
246991
|
const code = markerOf.get(el);
|
|
246685
246992
|
const baseline = source2[el.id] ?? "";
|
|
246686
|
-
lines[code] = el.kind === "line" ? { id: el.id, kind: "line", character: el.character, ...el.qualifier ? { qualifier: el.qualifier } : {}, ...el.direction ? { direction: el.direction } : {}, baseline } : { id: el.id, kind: el.kind === "option" ? "option" : "narration", baseline };
|
|
246993
|
+
lines[code] = el.kind === "line" ? { id: el.id, kind: "line", character: el.character, ...el.qualifier ? { qualifier: el.qualifier, ...el.qualifierName ? { qualifierName: el.qualifierName } : {} } : {}, ...el.direction ? { direction: el.direction } : {}, baseline } : { id: el.id, kind: el.kind === "option" ? "option" : "narration", baseline };
|
|
246687
246994
|
skeleton.push({ kind: "box", marker: code });
|
|
246688
246995
|
} else {
|
|
246689
246996
|
skeleton.push({ kind: el.kind, node: el.id, text: contextText(el) });
|
|
@@ -247300,14 +247607,14 @@ function planEditableImport(loaded, returned, opts) {
|
|
|
247300
247607
|
const cue = sent.kind === "line" ? readLead(b.lead.proposed) : void 0;
|
|
247301
247608
|
let proposedCharacter, proposedQualifier, proposedDirection;
|
|
247302
247609
|
if (sent.kind === "line" && cue) {
|
|
247303
|
-
const read2 = readCue(cue.character, sent
|
|
247610
|
+
const read2 = readCue(cue.character, sent, cast, qualifiers);
|
|
247304
247611
|
if (read2.unknownSpeaker !== void 0) {
|
|
247305
247612
|
problems.push({ severity: "warning", kind: "unknown-speaker", marker: code, anchor: sent.id, message: `The speaker was changed to ${read2.unknownSpeaker}, who isn't in the cast; it's in a comment.` });
|
|
247306
|
-
comment(here ?? nearestLine(boxes.indexOf(b)), author, `Speaker changed from ${sentCue(sent
|
|
247613
|
+
comment(here ?? nearestLine(boxes.indexOf(b)), author, `Speaker changed from ${sentCue(sent, qualifiers) || "(none)"} to ${read2.unknownSpeaker}, who isn't in the cast.`, ts);
|
|
247307
247614
|
}
|
|
247308
247615
|
if (read2.unknownQualifier !== void 0) {
|
|
247309
247616
|
problems.push({ severity: "warning", kind: "unknown-qualifier", marker: code, anchor: sent.id, message: `The speaker qualifier was changed to (${read2.unknownQualifier}), which isn't one of the project's qualifiers; it's in a comment.` });
|
|
247310
|
-
comment(here ?? nearestLine(boxes.indexOf(b)), author, `Speaker changed from ${sentCue(sent
|
|
247617
|
+
comment(here ?? nearestLine(boxes.indexOf(b)), author, `Speaker changed from ${sentCue(sent, qualifiers) || "(none)"} to ${tidy(cue.character)}, but (${read2.unknownQualifier}) isn't one of the project's qualifiers.`, ts);
|
|
247311
247618
|
}
|
|
247312
247619
|
if (read2.character !== void 0 && read2.character !== (sent.character ?? "")) proposedCharacter = read2.character;
|
|
247313
247620
|
if (read2.qualifier !== void 0 && read2.qualifier !== (sent.qualifier ?? "")) proposedQualifier = read2.qualifier;
|
|
@@ -247420,16 +247727,21 @@ function readLead(text) {
|
|
|
247420
247727
|
const character = lines.find((l2) => l2 !== direction) ?? "";
|
|
247421
247728
|
return { character, direction: direction ? direction.slice(1, -1) : "" };
|
|
247422
247729
|
}
|
|
247423
|
-
function
|
|
247424
|
-
if (!
|
|
247425
|
-
|
|
247426
|
-
|
|
247730
|
+
function sentQualifierName(sent, qualifiers) {
|
|
247731
|
+
if (!sent.qualifier) return "";
|
|
247732
|
+
return sent.qualifierName ?? qualifiers.find((q) => q.gameId === sent.qualifier)?.name ?? sent.qualifier;
|
|
247733
|
+
}
|
|
247734
|
+
function sentCue(sent, qualifiers) {
|
|
247735
|
+
if (!sent.character) return "";
|
|
247736
|
+
const name = sentQualifierName(sent, qualifiers);
|
|
247737
|
+
return name ? `${sent.character} (${name})` : sent.character;
|
|
247427
247738
|
}
|
|
247428
|
-
function readCue(text,
|
|
247739
|
+
function readCue(text, sent, cast, qualifiers) {
|
|
247429
247740
|
const cue = tidy(text);
|
|
247430
247741
|
if (!cue) return {};
|
|
247431
247742
|
const upper = (s) => s.toUpperCase();
|
|
247432
|
-
if (upper(cue) === upper(sentCue(
|
|
247743
|
+
if (upper(cue) === upper(sentCue(sent, qualifiers))) return {};
|
|
247744
|
+
const sentCharacter = sent.character ?? "";
|
|
247433
247745
|
const speaker = (name) => cast.find((c2) => upper(c2.name) === upper(name))?.name ?? (sentCharacter && upper(name) === upper(sentCharacter) ? sentCharacter : void 0);
|
|
247434
247746
|
const whole = speaker(cue);
|
|
247435
247747
|
if (whole !== void 0) return { character: whole, qualifier: "" };
|
|
@@ -247437,6 +247749,7 @@ function readCue(text, sentCharacter, sentQualifier, cast, qualifiers) {
|
|
|
247437
247749
|
const rest = m ? speaker(m[1].trim()) : void 0;
|
|
247438
247750
|
if (!m || rest === void 0) return { unknownSpeaker: cue };
|
|
247439
247751
|
const bracket = m[2].trim();
|
|
247752
|
+
if (sent.qualifier && upper(bracket) === upper(sentQualifierName(sent, qualifiers))) return { character: rest, qualifier: sent.qualifier };
|
|
247440
247753
|
const q = qualifiers.find((x) => upper(x.name) === upper(bracket));
|
|
247441
247754
|
return q ? { character: rest, qualifier: q.gameId } : { character: rest, unknownQualifier: bracket };
|
|
247442
247755
|
}
|
|
@@ -249730,7 +250043,7 @@ function fileStatus(filePaths, options = {}) {
|
|
|
249730
250043
|
// package.json
|
|
249731
250044
|
var package_default = {
|
|
249732
250045
|
name: "@patterkit/cli",
|
|
249733
|
-
version: "0.10.
|
|
250046
|
+
version: "0.10.2",
|
|
249734
250047
|
description: "The `patter` CLI: validate, format, export, play, test coverage, localise, and merge Patter projects.",
|
|
249735
250048
|
type: "module",
|
|
249736
250049
|
license: "MIT",
|
|
@@ -249759,8 +250072,8 @@ var package_default = {
|
|
|
249759
250072
|
"build:standalone:others": "node scripts/build-standalone.mjs --targets=linux-x64,linux-arm64,windows-x64"
|
|
249760
250073
|
},
|
|
249761
250074
|
dependencies: {
|
|
249762
|
-
"@patterkit/core": "0.
|
|
249763
|
-
"@patterkit/ops": "0.
|
|
250075
|
+
"@patterkit/core": "0.6.1",
|
|
250076
|
+
"@patterkit/ops": "0.16.1",
|
|
249764
250077
|
"@wildwinter/simple-vc-lib": "^0.5.2"
|
|
249765
250078
|
}
|
|
249766
250079
|
};
|