@patterkit/cli 0.9.3 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +564 -134
- package/package.json +4 -4
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];
|
|
@@ -197883,6 +197883,32 @@ function effectiveGameId(entity) {
|
|
|
197883
197883
|
const g = entity.gameId?.trim();
|
|
197884
197884
|
return g ? g : gameIdify(entity.name);
|
|
197885
197885
|
}
|
|
197886
|
+
var DEFAULT_QUALIFIERS = [
|
|
197887
|
+
{ gameId: "vo", name: "V.O.", description: "Voice-over: the character isn't in the scene (a thought, narration, a voice in the ear)." },
|
|
197888
|
+
{ gameId: "os", name: "O.S.", description: "Off-screen: the character is there, but not seen (the next room, behind a door)." },
|
|
197889
|
+
{ gameId: "radio", name: "RADIO", description: "Heard through a radio, a phone, or a loudspeaker." }
|
|
197890
|
+
];
|
|
197891
|
+
function projectQualifiers(project) {
|
|
197892
|
+
return project.qualifiers ?? DEFAULT_QUALIFIERS;
|
|
197893
|
+
}
|
|
197894
|
+
function usedQualifiers(project, scenes) {
|
|
197895
|
+
const used = /* @__PURE__ */ new Set();
|
|
197896
|
+
for (const scene of scenes) {
|
|
197897
|
+
for (const block of scene.blocks) {
|
|
197898
|
+
walkNodes(block.children, (node) => {
|
|
197899
|
+
if (node.type === "group") {
|
|
197900
|
+
if (node.prompt?.kind === "line" && node.prompt.qualifier) used.add(node.prompt.qualifier);
|
|
197901
|
+
return;
|
|
197902
|
+
}
|
|
197903
|
+
for (const beat of node.beats ?? []) if (beat.kind === "line" && beat.qualifier) used.add(beat.qualifier);
|
|
197904
|
+
});
|
|
197905
|
+
}
|
|
197906
|
+
}
|
|
197907
|
+
return projectQualifiers(project).filter((q) => used.has(q.gameId));
|
|
197908
|
+
}
|
|
197909
|
+
var DEFAULT_PAD_AFTER = 0.6;
|
|
197910
|
+
var PAD_AFTER_MIN = -10;
|
|
197911
|
+
var PAD_AFTER_MAX = 60;
|
|
197886
197912
|
var DEFAULT_WRITING_STATUSES = [
|
|
197887
197913
|
{ name: "stub", colour: 0 },
|
|
197888
197914
|
// red
|
|
@@ -197927,6 +197953,9 @@ var PROJECT_LOCALE_SCENE = "@project";
|
|
|
197927
197953
|
function castStringKey(name) {
|
|
197928
197954
|
return `cast:${name}`;
|
|
197929
197955
|
}
|
|
197956
|
+
function qualifierStringKey(gameId) {
|
|
197957
|
+
return `qualifier:${gameId}`;
|
|
197958
|
+
}
|
|
197930
197959
|
var HANDOFF_SCHEMA = "patter/handoff@0";
|
|
197931
197960
|
var DEFAULT_CAPTION_DELIMITERS = { open: "[", close: "]" };
|
|
197932
197961
|
var DEFAULT_CAPTION_CHARACTER = "SFX";
|
|
@@ -198047,6 +198076,45 @@ function validateProject(input) {
|
|
|
198047
198076
|
const sceneGameIds = /* @__PURE__ */ new Map();
|
|
198048
198077
|
const jumps = [];
|
|
198049
198078
|
const castNames = new Set((project.cast ?? []).map((c2) => c2.name));
|
|
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
|
+
};
|
|
198109
|
+
const checkQualifier = (beat, what) => {
|
|
198110
|
+
if (beat.kind !== "line" || beat.qualifier === void 0) return;
|
|
198111
|
+
if (qualifierIds && !qualifierIds.has(beat.qualifier)) {
|
|
198112
|
+
issues.push({ code: "unknown-qualifier", message: `${what} '${beat.id}' qualifier '${beat.qualifier}' is not in the project's qualifiers`, id: beat.id });
|
|
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
|
+
}
|
|
198117
|
+
};
|
|
198050
198118
|
const checkTags = (tags, where, id) => {
|
|
198051
198119
|
for (const t of tags ?? []) {
|
|
198052
198120
|
if (typeof t !== "string" || t.length === 0 || /[\s,]/.test(t)) {
|
|
@@ -198071,6 +198139,7 @@ function validateProject(input) {
|
|
|
198071
198139
|
validateProjectFile(project, issues);
|
|
198072
198140
|
for (const scene of scenes) {
|
|
198073
198141
|
seeId(scene.id, "scene", "project");
|
|
198142
|
+
checkPad(scene.padAfterDefault, `scene '${scene.id}' default`, scene.id);
|
|
198074
198143
|
const where = `scene '${scene.id}'`;
|
|
198075
198144
|
if (typeof scene.id === "string") addressable.add(scene.id);
|
|
198076
198145
|
if (!scene.name?.trim()) {
|
|
@@ -198106,6 +198175,7 @@ function validateProject(input) {
|
|
|
198106
198175
|
}
|
|
198107
198176
|
for (const block of scene.blocks) {
|
|
198108
198177
|
seeId(block.id, "block", where);
|
|
198178
|
+
checkPad(block.padAfterDefault, `block '${block.id}' default`, block.id);
|
|
198109
198179
|
checkTags(block.tags, `block '${block.id}'`, block.id);
|
|
198110
198180
|
if (typeof block.id === "string") addressable.add(block.id);
|
|
198111
198181
|
if (!block.name?.trim()) {
|
|
@@ -198146,6 +198216,12 @@ function validateProject(input) {
|
|
|
198146
198216
|
walkNodes(block.children, (node) => {
|
|
198147
198217
|
const inBlock = `block '${block.id}'`;
|
|
198148
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
|
+
}
|
|
198149
198225
|
if (node.type === "group") {
|
|
198150
198226
|
seeId(node.id, "group", inBlock);
|
|
198151
198227
|
if (!Array.isArray(node.children)) {
|
|
@@ -198170,6 +198246,8 @@ function validateProject(input) {
|
|
|
198170
198246
|
if (p.kind === "line" && p.character && !castNames.has(p.character)) {
|
|
198171
198247
|
issues.push({ code: "unknown-character", message: `prompt '${p.id}' speaker '${p.character}' is not in the project cast`, id: p.id });
|
|
198172
198248
|
}
|
|
198249
|
+
checkQualifier(p, "prompt");
|
|
198250
|
+
checkPromptPad(opt);
|
|
198173
198251
|
}
|
|
198174
198252
|
}
|
|
198175
198253
|
if (fallbacks > 1) {
|
|
@@ -198199,6 +198277,7 @@ function validateProject(input) {
|
|
|
198199
198277
|
id: beat.id
|
|
198200
198278
|
});
|
|
198201
198279
|
}
|
|
198280
|
+
checkQualifier(beat, "beat");
|
|
198202
198281
|
}
|
|
198203
198282
|
if (node.jump) jumps.push({ to: node.jump.to, from: node.id });
|
|
198204
198283
|
});
|
|
@@ -198220,6 +198299,9 @@ function validateProject(input) {
|
|
|
198220
198299
|
}
|
|
198221
198300
|
return issues;
|
|
198222
198301
|
}
|
|
198302
|
+
function isQualifierEntry(q) {
|
|
198303
|
+
return typeof q === "object" && q !== null && !Array.isArray(q);
|
|
198304
|
+
}
|
|
198223
198305
|
function validateProjectFile(project, issues) {
|
|
198224
198306
|
checkDecls(project.properties, "project properties", issues);
|
|
198225
198307
|
for (const scope of project.scopeRegistry?.scopes ?? []) {
|
|
@@ -198271,6 +198353,30 @@ function validateProjectFile(project, issues) {
|
|
|
198271
198353
|
if (project.recordingStatuses) {
|
|
198272
198354
|
checkLadderNames(project.recordingStatuses.map((s) => s.name), "recordingStatuses", issues);
|
|
198273
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
|
+
}
|
|
198360
|
+
const seenQualifiers = /* @__PURE__ */ new Set();
|
|
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
|
+
}
|
|
198370
|
+
if (typeof q.name !== "string" || !q.name.trim()) {
|
|
198371
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q.gameId)}' has no name` });
|
|
198372
|
+
}
|
|
198373
|
+
if (typeof q.gameId !== "string" || !isValidGameId(q.gameId)) {
|
|
198374
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q.name)}' address '${String(q.gameId)}' is invalid (lowercase letters, digits, hyphens; no leading/trailing hyphen)` });
|
|
198375
|
+
} else if (seenQualifiers.has(q.gameId)) {
|
|
198376
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier address '${q.gameId}' is used more than once` });
|
|
198377
|
+
}
|
|
198378
|
+
if (typeof q.gameId === "string") seenQualifiers.add(q.gameId);
|
|
198379
|
+
}
|
|
198274
198380
|
for (const [kind, fields] of Object.entries(project.gameDataFields ?? {})) {
|
|
198275
198381
|
const seen = /* @__PURE__ */ new Set();
|
|
198276
198382
|
for (const f of fields ?? []) {
|
|
@@ -199701,6 +199807,8 @@ function compileNode(node, foreign) {
|
|
|
199701
199807
|
shared: node.shared,
|
|
199702
199808
|
options: node.options,
|
|
199703
199809
|
children: node.children.map((c2) => compileNode(c2, foreign)),
|
|
199810
|
+
padAfterDefault: node.padAfterDefault,
|
|
199811
|
+
// line padding: the default for the beats inside
|
|
199704
199812
|
gameData: node.gameData,
|
|
199705
199813
|
tags: node.tags,
|
|
199706
199814
|
// author tags (#215), accumulated at runtime
|
|
@@ -199718,6 +199826,8 @@ function compileNode(node, foreign) {
|
|
|
199718
199826
|
beats: node.beats,
|
|
199719
199827
|
onEnter: node.onEnter?.map((e) => compileEffect(e, foreign)),
|
|
199720
199828
|
onExit: node.onExit?.map((e) => compileEffect(e, foreign)),
|
|
199829
|
+
padAfterDefault: node.padAfterDefault,
|
|
199830
|
+
// line padding: the default for the beats inside
|
|
199721
199831
|
gameData: node.gameData,
|
|
199722
199832
|
tags: node.tags,
|
|
199723
199833
|
// author tags (#215)
|
|
@@ -199735,6 +199845,8 @@ function compileBlock(block, foreign) {
|
|
|
199735
199845
|
...block.gameId ? { gameId: block.gameId } : {},
|
|
199736
199846
|
// host-facing address; runtime falls back to the name slug
|
|
199737
199847
|
children: block.children.map((c2) => compileNode(c2, foreign)),
|
|
199848
|
+
padAfterDefault: block.padAfterDefault,
|
|
199849
|
+
// line padding: the default for the beats inside
|
|
199738
199850
|
gameData: block.gameData,
|
|
199739
199851
|
tags: block.tags
|
|
199740
199852
|
// author tags (#215)
|
|
@@ -199747,6 +199859,8 @@ function compileScene(scene, foreign) {
|
|
|
199747
199859
|
name: scene.name,
|
|
199748
199860
|
...scene.gameId ? { gameId: scene.gameId } : {},
|
|
199749
199861
|
// host-facing address; runtime falls back to the name slug
|
|
199862
|
+
padAfterDefault: scene.padAfterDefault,
|
|
199863
|
+
// line padding: the default for the beats inside
|
|
199750
199864
|
gameData: scene.gameData,
|
|
199751
199865
|
tags: scene.tags,
|
|
199752
199866
|
// author tags (#215)
|
|
@@ -199786,6 +199900,7 @@ function exportBundle(input) {
|
|
|
199786
199900
|
const scenesOut = {};
|
|
199787
199901
|
for (const scene of scenes) scenesOut[scene.id] = compileScene(scene, compileScopes);
|
|
199788
199902
|
const externalScopes = externalScopesIn(scenesOut, /* @__PURE__ */ new Set([...EXTERNAL_SCOPES, ...gameExternal.map((s) => s.token)]));
|
|
199903
|
+
const qualifiers = usedQualifiers(project, scenes).map((q) => ({ gameId: q.gameId, name: q.name }));
|
|
199789
199904
|
const strings = {};
|
|
199790
199905
|
for (const loc of locales) {
|
|
199791
199906
|
if (includeLocales && !includeLocales.includes(loc.locale)) continue;
|
|
@@ -199804,21 +199919,26 @@ function exportBundle(input) {
|
|
|
199804
199919
|
// Binds saves to the content they were taken against, and gates bundle
|
|
199805
199920
|
// staleness (schema §10.5). A stable strict-JSON fingerprint - independent
|
|
199806
199921
|
// of the source-form trailing-comma policy.
|
|
199807
|
-
// `scopeRegistry`
|
|
199808
|
-
// prior hash (no ripple to existing bundles / the conformance corpus).
|
|
199922
|
+
// `scopeRegistry` and `qualifiers` are folded in only when present, so projects without them keep
|
|
199923
|
+
// their exact prior hash (no ripple to existing bundles / the conformance corpus).
|
|
199809
199924
|
hash: hash32(canonicalStringify({
|
|
199810
199925
|
scenes: scenesOut,
|
|
199811
199926
|
strings,
|
|
199812
199927
|
properties: project.properties ?? [],
|
|
199813
|
-
...foreignScopes ? { scopeRegistry: foreignScopes } : {}
|
|
199928
|
+
...foreignScopes ? { scopeRegistry: foreignScopes } : {},
|
|
199929
|
+
...qualifiers.length > 0 ? { qualifiers } : {},
|
|
199930
|
+
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {}
|
|
199814
199931
|
}, { trailingComma: false })),
|
|
199815
199932
|
// Structure-only fingerprint (live bundle refresh): the same hash with the string tables left
|
|
199816
199933
|
// out. Same structureHash + a different hash = a text-only edit, safe to swap in place with
|
|
199817
199934
|
// `engine.replaceStrings()`; a changed structureHash needs the full save/load hot swap.
|
|
199935
|
+
// A qualifier's name rides in `qualifiers`, not the string tables, so renaming one is a structure change.
|
|
199818
199936
|
structureHash: hash32(canonicalStringify({
|
|
199819
199937
|
scenes: scenesOut,
|
|
199820
199938
|
properties: project.properties ?? [],
|
|
199821
|
-
...foreignScopes ? { scopeRegistry: foreignScopes } : {}
|
|
199939
|
+
...foreignScopes ? { scopeRegistry: foreignScopes } : {},
|
|
199940
|
+
...qualifiers.length > 0 ? { qualifiers } : {},
|
|
199941
|
+
...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {}
|
|
199822
199942
|
}, { trailingComma: false }))
|
|
199823
199943
|
},
|
|
199824
199944
|
voiced: project.voiced ?? false,
|
|
@@ -199834,6 +199954,9 @@ function exportBundle(input) {
|
|
|
199834
199954
|
if (c2.gameData !== void 0) m.gameData = c2.gameData;
|
|
199835
199955
|
return m;
|
|
199836
199956
|
}),
|
|
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 } : {},
|
|
199837
199960
|
properties: project.properties,
|
|
199838
199961
|
scopeRegistry: hostScopes,
|
|
199839
199962
|
gameDataFields: project.gameDataFields,
|
|
@@ -200447,6 +200570,9 @@ function mergeProject(base, ours, theirs) {
|
|
|
200447
200570
|
} else if (k === "gameDataFields") {
|
|
200448
200571
|
const v = mergeGameDataFields(asMap(base[k]), asMap(ours[k]), asMap(theirs[k]), conflicts);
|
|
200449
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;
|
|
200450
200576
|
} else if (k === "locales") {
|
|
200451
200577
|
merged.locales = mergeLocales(asMap(base.locales), asMap(ours.locales), asMap(theirs.locales), conflicts);
|
|
200452
200578
|
} else {
|
|
@@ -200464,6 +200590,23 @@ function mergeGameDataFields(b, o, t, conflicts) {
|
|
|
200464
200590
|
}
|
|
200465
200591
|
return out;
|
|
200466
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
|
+
}
|
|
200467
200610
|
function mergeLocales(b, o, t, conflicts) {
|
|
200468
200611
|
const out = {};
|
|
200469
200612
|
const def = merge3(b.default, o.default, t.default, "", "locales.default", conflicts);
|
|
@@ -201225,7 +201368,55 @@ function planBuild(loaded, opts = {}) {
|
|
|
201225
201368
|
}
|
|
201226
201369
|
|
|
201227
201370
|
// ../ops/src/playable-runtime.ts
|
|
201228
|
-
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var W=Object.defineProperty;var Me=Object.getOwnPropertyDescriptor;var $e=Object.getOwnPropertyNames;var He=Object.prototype.hasOwnProperty;var qe=(n,e)=>{for(var t in e)W(n,t,{get:e[t],enumerable:!0})},_e=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of $e(e))!He.call(n,s)&&s!==t&&W(n,s,{get:()=>e[s],enumerable:!(r=Me(e,s))||r.enumerable});return n};var Ue=n=>_e(W({},"__esModule",{value:!0}),n);var pt={};qe(pt,{Checkpoint:()=>T,Engine:()=>_,Flow:()=>D,buildTagIndex:()=>q,describeBundle:()=>Ae,effectiveGameData:()=>Ge,gameDataFields:()=>Te,gameDataValue:()=>se});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 d=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 d(`@${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 d(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=J(a,e);if(c===void 0)throw new d("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 d(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new d("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 d(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new d(`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 d(`\'and\' requires boolean operands, left is ${typeof f}`);if(!f)return!1;let u=s(i.right);if(typeof u!="boolean")throw new d(`\'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 d(`\'or\' requires boolean operands, left is ${typeof f}`);if(f)return!0;let u=s(i.right);if(typeof u!="boolean")throw new d(`\'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&&!We(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new d(`\'${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 d(`\'${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 pe(o,a);case"!=":return!pe(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 d(`\'+\' 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 d("division by zero");return o/a}}}};return s(n)}function pe(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 d(`\'${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 d(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new d(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var We=(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 K(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 Je={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},Ke=[Je];function de(n,e,t){let r=t?.countingCalls??Ke;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])=>ue(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 ue(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 ze=n=>n.toLowerCase(),z=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??ze,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,...ue(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(Ye(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 ge(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=ge(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:z,scopes:this.save()}}loadFragment(e){if(e.version!==z)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${z})`);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 ge(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function Ye(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 he=[{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 Y={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new d("random(a, b) requires exactly 2 arguments");let t=B(e).nextRandom;if(!t)throw new d("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 d("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new d("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:me("check_flags"),eval(n,e){let t=fe(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new d("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:me("set_flags"),eval(n,e){let t=[...fe(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new d("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:H("visits"),eval:(n,e)=>B(e).visits?.($(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:H("seen"),eval:(n,e)=>(B(e).visits?.($(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:H("patter_visits"),eval:(n,e)=>B(e).patterVisits?.($(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:H("patter_seen"),eval:(n,e)=>(B(e).patterVisits?.($(n,e,"patter_seen"))??0)>0}}},Rt=he.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function Se(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 d(`${t}(id) requires a string node id`);return r}function H(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 fe(n,e,t){if(!n)throw new d(`${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 d(`${t}() first argument must be a flags property`)}function me(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 Qe=/^@[A-Za-z0-9_.]+$/;function*Xe(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:Qe.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 Ze(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function et(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function tt(n){let e="",t=!1;for(let r of n){if(et(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function ye(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?tt(r):n}function ve(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of Xe(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?"":Ze(s)}return t}function I(n,e){for(let t of n){e(t);let r=t.children;r&&I(r,e)}}function rt(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||rt(n.name)}function Q(n){return`cast:${n}`}var X={open:"[",close:"]"},we="SFX";var Z=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 q(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 be=new WeakMap,xe=new WeakMap;function nt(n){let e=xe.get(n);return e||(e=q(n),xe.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"),ke=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 g of e.cast??[])g.displayName&&l.set(g.name,g.displayName);this.defaultSeed=(t.seed??2654435769)>>>0;let p=new Map,f=new Map,u=new Map;for(let[g,h]of Object.entries(e.scenes)){this.sceneGameIdToId.set(S(h),g);let v=new Map;for(let m of h.blocks)f.set(m.id,{sceneId:g}),u.set(m.id,m),v.set(S(m),m.id),I(m.children,j=>p.set(j.id,j));this.blockGameIdToId.set(g,v)}let ie=e.properties??[],U=ie.filter(g=>g.shared??!0).map(De),je=ie.filter(g=>!(g.shared??!0)).map(De),Ve=new Set(U.map(g=>g.name.toLowerCase())),x=t.registry??new M,oe=!t.registry||t[ke]===!0,ae=new b(U,{pathPrefix:"@patter."}),ce=[],G=[];try{x.mountOwned("patter",ae,{owner:A}),G.push("patter");let g=new Set;for(let[h,v]of Object.entries(Re(t))){let m=e.scopeRegistry?.scopes.find(Le=>Le.token===h),j=(m?.declarations??[]).map(Ne);x.defineForeign(h,v,j,{writable:m?.writable??!0,owner:A}),G.push(h),g.add(h)}for(let h of oe?e.scopeRegistry?.scopes??[]:[]){if(g.has(h.token)||x.has(h.token))continue;let v=(h.declarations??[]).map(m=>it(m,h.writable));x.defineOwned(h.token,v,{owner:A}),G.push(h.token),ce.push(h.token)}}catch(g){for(let h of G)x.remove(h,{keep:!0});throw g}let le=new Map;for(let[g,h]of Object.entries(e.scenes)){let v=new Set((h.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));le.set(g,v)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(g,h,v)=>this.emitEngine(g,h,v),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,nodeIndex:p,blockIndex:f,blockById:u,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:nt(e),registry:x,ownsRegistry:oe,patterBag:ae,hostScopes:ce,patterSharedDecls:U,patterLocalDecls:je,patterSharedNames:Ve,sceneSharedNames:le,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(g=>console.warn(`Patterplay: ${g.kind} on ${g.node} in flow \'${g.flow}\' failed, played through: ${g.message}`)),replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??X).open,captionClose:(e.closedCaptions??X).close,captionCharacter:e.closedCaptions?.character||we,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,[ke]: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=te(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=re(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 te(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:re(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)Ee(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 Ee(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,...Ce(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...S(t)?{gameId:S(t)}:{},name:t.name,...Ce(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[Q(e.character)]??this.host.castDisplay.get(e.character);s!==void 0&&(r.characterName=s)}e.direction!==void 0&&(r.direction=e.direction)}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(Re(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=Fe(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:Z,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:Pe(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==Z)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?ot(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 Be(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 ee(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(r);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:ee(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(r)}return this.registryQualities?.(e,t)??ee(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=te(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=re(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:st(this.pendingPrompt.shown)}:{},selectors:Pe(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=Be(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 de(e,i=>{try{return Oe(V(i,this.context(),Y))}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 Oe(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(),Y)}conditionAst(e){let t=be.get(e);return t||(t=k(e.ast),be.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=K(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,gameData:e.gameData,...r}}}}interpolate(e){return ve(e,t=>this.getProperty(t))}stripCaptions(e){return ye(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}:{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,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=Q(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}splitRef(e){return Fe(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=Ie(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=Ie(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 Ee(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 Ce(n){return n&&Object.keys(n).length?{gameData:n}:{}}function st(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),e}function Pe(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 Be(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 Ie(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 De(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Re(n){return{...n.world?{world:n.world}:{},...n.hostScopes}}function ee(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 Ne(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function it(n,e){let t=Ne(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function te(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function re(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function Fe(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=Se(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function ot(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 Oe(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var at=(n,e)=>n.shared??e;function ne(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:at(n,e)}}function ct(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function lt(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 Ae(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)lt(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>ne(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=>ne(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(ct)}));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=>ne(o,!0)),scene:r},gameData:i,counts:e}}function Te(n,e){return n.gameDataFields?.[e]??[]}function se(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function Ge(n,e){let t={};for(let r of n){let s=se(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 Ue(pt);})();\n';
|
|
201371
|
+
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var Q=Object.defineProperty;var Je=Object.getOwnPropertyDescriptor;var Qe=Object.getOwnPropertyNames;var Ke=Object.prototype.hasOwnProperty;var ze=(n,e)=>{for(var t in e)Q(n,t,{get:e[t],enumerable:!0})},Ye=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Qe(e))!Ke.call(n,s)&&s!==t&&Q(n,s,{get:()=>e[s],enumerable:!(r=Je(e,s))||r.enumerable});return n};var Xe=n=>Ye(Q({},"__esModule",{value:!0}),n);var yt={};ze(yt,{Checkpoint:()=>j,DEFAULT_PAD_AFTER:()=>x,Engine:()=>W,Flow:()=>R,buildTagIndex:()=>U,describeBundle:()=>qe,effectiveGameData:()=>He,gameDataFields:()=>$e,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 L(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=K(a,e);if(c===void 0)throw new u("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=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=K(i.left,e),l=K(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!Ze(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 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 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 K(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 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 Ze=(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 z(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 et={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},tt=[et];function he(n,e,t){let r=t?.countingCalls??tt;return q(n,t?.want??!0,e,r)}function q(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=q(n.left,e,t,r),i=q(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 q(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 rt=n=>n.toLowerCase(),Y=1,$=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??rt,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(nt(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:Y,scopes:this.save()}}loadFragment(e){if(e.version!==Y)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${Y})`);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 nt(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 X={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:we("check_flags"),eval(n,e){let t=ve(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:we("set_flags"),eval(n,e){let t=[...ve(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:_("visits"),eval:(n,e)=>A(e).visits?.(H(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:_("seen"),eval:(n,e)=>(A(e).visits?.(H(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:_("patter_visits"),eval:(n,e)=>A(e).patterVisits?.(H(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:_("patter_seen"),eval:(n,e)=>(A(e).patterVisits?.(H(n,e,"patter_seen"))??0)>0}}},Gt=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 H(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 _(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 ve(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 we(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 st=/^@[A-Za-z0-9_.]+$/;function*it(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:st.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 ot(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function at(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function ct(n){let e="",t=!1;for(let r of n){if(at(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?ct(r):n}function ke(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of it(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?"":ot(s)}return t}function I(n,e){for(let t of n){e(t);let r=t.children;r&&I(r,e)}}function lt(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||lt(n.name)}var x=.6;function Z(n){return`cast:${n}`}function ee(n){return`qualifier:${n}`}var te={open:"[",close:"]"},Ee="SFX";var re=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 U(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",F=n=>typeof n=="number"&&Number.isFinite(n)?n:void 0;function De(n){let e=new Map,t=(i,o,a)=>{let c=i.kind==="gameEvent"?void 0:F(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=F(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=F(n.padAfterDefault)??x;for(let i of Object.values(n.scenes)){let o=F(i.padAfterDefault)??s;for(let a of i.blocks){let c=F(a.padAfterDefault)??o;for(let l of a.children)r(l,c)}}return e}var Ie=new WeakMap,Re=new WeakMap;function pt(n){let e=Re.get(n);return e||(e=U(n),Re.set(n,e)),e}var Pe=new WeakMap;function dt(n){let e=Pe.get(n);return e||(e=De(n),Pe.set(n,e)),e}function D(n,e){return n.tagIndex.get(e)?.slice()}var G="Patter",T=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),Oe=Symbol("ownsRegistry"),y={stage:n=>`patter/scene/${T(n)}`,flow:n=>`patter/flow/${T(n)}/`,flowGlobals:n=>`patter/flow/${T(n)}/patter`,flowScene:(n,e)=>`patter/flow/${T(n)}/scene/${T(e)}`},j=class{constructor(e){this.journal=e}journal},W=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(S(h),d);let v=new Map;for(let m of h.blocks)g.set(m.id,{sceneId:d}),ce.set(m.id,m),v.set(S(m),m.id),I(m.children,V=>f.set(V.id,V));this.blockGameIdToId.set(d,v)}let le=e.properties??[],J=le.filter(d=>d.shared??!0).map(Fe),_e=le.filter(d=>!(d.shared??!0)).map(Fe),Ue=new Set(J.map(d=>d.name.toLowerCase())),k=t.registry??new $,pe=!t.registry||t[Oe]===!0,de=new b(J,{pathPrefix:"@patter."}),ue=[],M=[];try{k.mountOwned("patter",de,{owner:G}),M.push("patter");let d=new Set;for(let[h,v]of Object.entries(Te(t))){let m=e.scopeRegistry?.scopes.find(We=>We.token===h),V=(m?.declarations??[]).map(Ve);k.defineForeign(h,v,V,{writable:m?.writable??!0,owner:G}),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 v=(h.declarations??[]).map(m=>gt(m,h.writable));k.defineOwned(h.token,v,{owner:G}),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 v=new Set((h.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));ge.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:g,blockById:ce,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:pt(e),padIndex:dt(e),registry:k,ownsRegistry:pe,patterBag:de,hostScopes:ue,patterSharedDecls:J,patterLocalDecls:_e,patterSharedNames:Ue,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??te).open,captionClose:(e.closedCaptions??te).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 R(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?S(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?S(t):void 0}tagsForBeat(e){return D(this.host,e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?D(this.host,t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?D(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,...S(e)?{gameId:S(e)}:{},name:e.name,...Ae(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...S(t)?{gameId:S(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)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=D(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[Z(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[ee(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=D(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`)}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(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:re,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:je(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==re)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?ft(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 Me(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new R(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}}},R=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(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=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:ut(this.pendingPrompt.shown)}:{},selectors:je(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=Me(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(L(i,this.context(),X))}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 L(this.conditionAst(e),this.context(),X)}conditionAst(e){let t=Ie.get(e);return t||(t=E(e.ast),Ie.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=z(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=D(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=D(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 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=Z(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=ee(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=y.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:G}),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=Ne(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=Ne(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,B(r))}}};function Be(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 Ae(n){return n&&Object.keys(n).length?{gameData:n}:{}}function ut(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 je(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 Me(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 Ne(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:G}),r&&JSON.stringify(t.save())!==s}function Fe(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 Ve(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function gt(n,e){let t=Ve(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 ft(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 Ge(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var ht=(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:ht(n,e)}}function mt(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function St(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 qe(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)St(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(mt)}));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 $e(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 He(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 Xe(yt);})();\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
|
+
}
|
|
201229
201420
|
|
|
201230
201421
|
// ../ops/src/export-html.ts
|
|
201231
201422
|
var esc = (s) => s.replace(/[&<>]/g, (c2) => ({ "&": "&", "<": "<", ">": ">" })[c2]);
|
|
@@ -201245,48 +201436,94 @@ var PLAYER_JS = String.raw`
|
|
|
201245
201436
|
function add(cls, html) { var d = document.createElement("div"); d.className = cls; d.innerHTML = html; stage.appendChild(d); return d; }
|
|
201246
201437
|
function esc(s) { return String(s == null ? "" : s).replace(/[&<>]/g, function (c) { return { "&": "&", "<": "<", ">": ">" }[c]; }); }
|
|
201247
201438
|
|
|
201248
|
-
// Pacing: a source-only reader carries no audio, so each line is
|
|
201249
|
-
//
|
|
201250
|
-
//
|
|
201251
|
-
//
|
|
201252
|
-
|
|
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); }
|
|
201253
201456
|
var speedKey = (function () { try { return localStorage.getItem("patter.playSpeed") || "normal"; } catch (e) { return "normal"; } })();
|
|
201254
|
-
if (
|
|
201255
|
-
|
|
201256
|
-
|
|
201257
|
-
|
|
201258
|
-
|
|
201259
|
-
|
|
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); };
|
|
201260
201469
|
}
|
|
201261
201470
|
function scrollDown() { window.scrollTo(0, document.body.scrollHeight); }
|
|
201262
201471
|
var speedSel = document.getElementById("speed");
|
|
201263
201472
|
if (speedSel) {
|
|
201264
|
-
speedSel.value = speedKey;
|
|
201265
|
-
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
|
+
};
|
|
201266
201480
|
}
|
|
201267
201481
|
|
|
201268
201482
|
function build() { engine = new Engine(BUNDLE); flow = engine.openFlow("main", { scene: startScene }); }
|
|
201269
201483
|
|
|
201270
201484
|
function newGame() { stage.innerHTML = ""; controls.innerHTML = ""; build(); run(); }
|
|
201271
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
|
+
|
|
201272
201497
|
function run() {
|
|
201273
201498
|
controls.innerHTML = "";
|
|
201274
201499
|
var token = ++runToken;
|
|
201275
|
-
|
|
201276
|
-
|
|
201277
|
-
|
|
201278
|
-
|
|
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));
|
|
201279
201513
|
if (s.type === "line") {
|
|
201280
201514
|
var who = s.characterName || s.character || "";
|
|
201515
|
+
// A speaker qualifier follows the name, as the script's cue has it: TAM (O.S.).
|
|
201516
|
+
var qual = s.qualifierName || s.qualifier;
|
|
201517
|
+
if (qual && who) who += " (" + qual + ")";
|
|
201281
201518
|
var dir = s.direction ? '<em class="dir">(' + esc(s.direction) + ')</em> ' : "";
|
|
201282
201519
|
add("line", '<span class="who" style="color:hsl(' + hueOf(s.character || who) + ',55%,38%)">' + esc(who) + '</span>' + dir + esc(s.text));
|
|
201283
|
-
|
|
201284
|
-
|
|
201285
|
-
|
|
201286
|
-
|
|
201287
|
-
|
|
201288
|
-
|
|
201289
|
-
|
|
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
|
|
201290
201527
|
}
|
|
201291
201528
|
|
|
201292
201529
|
function renderChoice(step) {
|
|
@@ -201304,7 +201541,7 @@ var PLAYER_JS = String.raw`
|
|
|
201304
201541
|
}
|
|
201305
201542
|
|
|
201306
201543
|
document.getElementById("restart").onclick = newGame;
|
|
201307
|
-
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) {} };
|
|
201308
201545
|
document.getElementById("load").onclick = function () {
|
|
201309
201546
|
var blob; try { blob = localStorage.getItem(SAVE_KEY); } catch (e) {}
|
|
201310
201547
|
if (!blob) { flash("No save yet"); return; }
|
|
@@ -201376,10 +201613,10 @@ ${head}
|
|
|
201376
201613
|
<button id="save">Save</button>
|
|
201377
201614
|
<button id="load">Load</button>
|
|
201378
201615
|
<label class="speedlabel">Speed
|
|
201379
|
-
<select id="speed" title="
|
|
201380
|
-
<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>
|
|
201381
201618
|
<option value="normal">Normal</option>
|
|
201382
|
-
<option value="
|
|
201619
|
+
<option value="double">Double</option>
|
|
201383
201620
|
<option value="instant">Instant</option>
|
|
201384
201621
|
</select>
|
|
201385
201622
|
</label>
|
|
@@ -201397,6 +201634,7 @@ function runExportHtml(loaded) {
|
|
|
201397
201634
|
lang,
|
|
201398
201635
|
`<style>${STYLE}</style>`,
|
|
201399
201636
|
`<script>${PLAYABLE_RUNTIME_JS}</script>
|
|
201637
|
+
<script>${PLAYABLE_TIMING_JS}</script>
|
|
201400
201638
|
<script>window.PATTER_BUNDLE=${bundleJson};</script>
|
|
201401
201639
|
<script>${PLAYER_JS}</script>`
|
|
201402
201640
|
);
|
|
@@ -201469,6 +201707,44 @@ function buildTagIndex(bundle) {
|
|
|
201469
201707
|
return index2;
|
|
201470
201708
|
}
|
|
201471
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
|
+
|
|
201472
201748
|
// ../runtime/src/engine.ts
|
|
201473
201749
|
var astCache = /* @__PURE__ */ new WeakMap();
|
|
201474
201750
|
var tagIndexCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -201480,6 +201756,15 @@ function tagIndexFor(bundle) {
|
|
|
201480
201756
|
}
|
|
201481
201757
|
return index2;
|
|
201482
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
|
+
}
|
|
201483
201768
|
function tagsOf(host2, id) {
|
|
201484
201769
|
return host2.tagIndex.get(id)?.slice();
|
|
201485
201770
|
}
|
|
@@ -201545,6 +201830,8 @@ var Engine = class _Engine {
|
|
|
201545
201830
|
}
|
|
201546
201831
|
const castDisplay = /* @__PURE__ */ new Map();
|
|
201547
201832
|
for (const c2 of bundle.cast ?? []) if (c2.displayName) castDisplay.set(c2.name, c2.displayName);
|
|
201833
|
+
const qualifierDisplay = /* @__PURE__ */ new Map();
|
|
201834
|
+
for (const q of bundle.qualifiers ?? []) if (q?.gameId) qualifierDisplay.set(q.gameId, q.name);
|
|
201548
201835
|
this.defaultSeed = (options.seed ?? 2654435769) >>> 0;
|
|
201549
201836
|
const nodeIndex = /* @__PURE__ */ new Map();
|
|
201550
201837
|
const blockIndex = /* @__PURE__ */ new Map();
|
|
@@ -201605,6 +201892,7 @@ var Engine = class _Engine {
|
|
|
201605
201892
|
strings,
|
|
201606
201893
|
defaultStrings,
|
|
201607
201894
|
castDisplay,
|
|
201895
|
+
qualifierDisplay,
|
|
201608
201896
|
nodeIndex,
|
|
201609
201897
|
blockIndex,
|
|
201610
201898
|
blockById,
|
|
@@ -201612,6 +201900,7 @@ var Engine = class _Engine {
|
|
|
201612
201900
|
blockGameIdToId: this.blockGameIdToId,
|
|
201613
201901
|
// same instances the engine resolves with
|
|
201614
201902
|
tagIndex: tagIndexFor(bundle),
|
|
201903
|
+
padIndex: padIndexFor(bundle),
|
|
201615
201904
|
registry,
|
|
201616
201905
|
ownsRegistry,
|
|
201617
201906
|
patterBag,
|
|
@@ -202009,8 +202298,16 @@ var Engine = class _Engine {
|
|
|
202009
202298
|
if (name !== void 0) info.characterName = name;
|
|
202010
202299
|
}
|
|
202011
202300
|
if (beat.direction !== void 0) info.direction = beat.direction;
|
|
202301
|
+
if (beat.qualifier !== void 0) {
|
|
202302
|
+
info.qualifier = beat.qualifier;
|
|
202303
|
+
const name = this.host.defaultStrings[qualifierStringKey(beat.qualifier)] ?? this.host.qualifierDisplay.get(beat.qualifier);
|
|
202304
|
+
if (name !== void 0) info.qualifierName = name;
|
|
202305
|
+
}
|
|
202012
202306
|
}
|
|
202013
202307
|
if (beat.kind === "line" || beat.kind === "text") {
|
|
202308
|
+
const pad2 = this.host.padIndex.get(beat.id);
|
|
202309
|
+
info.padAfter = pad2?.resolved ?? DEFAULT_PAD_AFTER;
|
|
202310
|
+
if (pad2?.own !== void 0) info.ownPadAfter = pad2.own;
|
|
202014
202311
|
const source2 = this.host.defaultStrings[beat.id];
|
|
202015
202312
|
if (source2 !== void 0) info.text = source2;
|
|
202016
202313
|
}
|
|
@@ -202804,7 +203101,8 @@ var Flow = class {
|
|
|
202804
203101
|
const node = this.host.nodeIndex.get(o.id);
|
|
202805
203102
|
if (!node) continue;
|
|
202806
203103
|
byId.set(o.id, node);
|
|
202807
|
-
|
|
203104
|
+
const { prompt: saved, ...rest } = o;
|
|
203105
|
+
options.push({ ...rest, ...saved ? { prompt: { ...saved, padAfter: saved.padAfter ?? this.promptPadOf(node) } } : {} });
|
|
202808
203106
|
}
|
|
202809
203107
|
if (options.length > 0) this.pendingChoice = { groupId: c2.pendingChoice.groupId, options, byId };
|
|
202810
203108
|
}
|
|
@@ -202813,7 +203111,7 @@ var Flow = class {
|
|
|
202813
203111
|
if (this.pendingPromptOwnerId) {
|
|
202814
203112
|
const owner = this.host.nodeIndex.get(this.pendingPromptOwnerId);
|
|
202815
203113
|
const beat = owner ? this.authoredPromptOf(owner) : void 0;
|
|
202816
|
-
if (beat) this.pendingPrompt = { beat, ...c2.pendingPrompt ? { shown: { ...c2.pendingPrompt } } : {} };
|
|
203114
|
+
if (beat) this.pendingPrompt = { beat, ...c2.pendingPrompt ? { shown: { ...c2.pendingPrompt, padAfter: c2.pendingPrompt.padAfter ?? this.padOf(beat.id) } } : {} };
|
|
202817
203115
|
else this.pendingPromptOwnerId = null;
|
|
202818
203116
|
}
|
|
202819
203117
|
}
|
|
@@ -203183,7 +203481,7 @@ var Flow = class {
|
|
|
203183
203481
|
case "gameEvent":
|
|
203184
203482
|
return { type: "gameEvent", id: beat.id, gameData: beat.gameData, ...withTags };
|
|
203185
203483
|
case "text":
|
|
203186
|
-
return { type: "text", id: beat.id, text: this.interpolate(this.resolveString(beat.id)), gameData: beat.gameData, ...withTags };
|
|
203484
|
+
return { type: "text", id: beat.id, text: this.interpolate(this.resolveString(beat.id)), padAfter: this.padOf(beat.id), gameData: beat.gameData, ...withTags };
|
|
203187
203485
|
case "line": {
|
|
203188
203486
|
const raw = this.resolveString(beat.id);
|
|
203189
203487
|
const off = !this.host.captionsOn;
|
|
@@ -203197,6 +203495,10 @@ var Flow = class {
|
|
|
203197
203495
|
character: silent ? void 0 : beat.character,
|
|
203198
203496
|
characterName: silent ? void 0 : this.resolveCharacterName(beat.character),
|
|
203199
203497
|
direction: silent ? void 0 : beat.direction,
|
|
203498
|
+
qualifier: silent ? void 0 : beat.qualifier,
|
|
203499
|
+
qualifierName: silent ? void 0 : this.resolveQualifierName(beat.qualifier),
|
|
203500
|
+
padAfter: this.padOf(beat.id),
|
|
203501
|
+
// a silent line still fires, so it still carries its pause
|
|
203200
203502
|
gameData: beat.gameData,
|
|
203201
203503
|
...withTags
|
|
203202
203504
|
};
|
|
@@ -203235,7 +203537,17 @@ var Flow = class {
|
|
|
203235
203537
|
const beat = this.promptBeatOf(node);
|
|
203236
203538
|
if (!beat) return void 0;
|
|
203237
203539
|
const text = this.interpolate(this.resolveString(beat.id));
|
|
203238
|
-
|
|
203540
|
+
const padAfter = this.padOf(beat.id);
|
|
203541
|
+
return beat.kind === "line" ? {
|
|
203542
|
+
kind: "line",
|
|
203543
|
+
text: this.captionLine(text),
|
|
203544
|
+
character: beat.character,
|
|
203545
|
+
characterName: this.resolveCharacterName(beat.character),
|
|
203546
|
+
direction: beat.direction,
|
|
203547
|
+
qualifier: beat.qualifier,
|
|
203548
|
+
qualifierName: this.resolveQualifierName(beat.qualifier),
|
|
203549
|
+
padAfter
|
|
203550
|
+
} : { kind: "text", text, padAfter };
|
|
203239
203551
|
}
|
|
203240
203552
|
/** An option's AUTHORED prompt beat: an Option group's own `prompt`. The only prompt a replay speaks. */
|
|
203241
203553
|
authoredPromptOf(node) {
|
|
@@ -203246,7 +203558,7 @@ var Flow = class {
|
|
|
203246
203558
|
promptResult(beat, shown2) {
|
|
203247
203559
|
const tags = tagsOf(this.host, beat.id);
|
|
203248
203560
|
const withTags = tags && tags.length ? { tags } : {};
|
|
203249
|
-
if (shown2.kind === "text") return { type: "text", id: beat.id, text: shown2.text, gameData: beat.gameData, ...withTags };
|
|
203561
|
+
if (shown2.kind === "text") return { type: "text", id: beat.id, text: shown2.text, padAfter: this.padOf(beat.id), gameData: beat.gameData, ...withTags };
|
|
203250
203562
|
return {
|
|
203251
203563
|
type: "line",
|
|
203252
203564
|
id: beat.id,
|
|
@@ -203254,6 +203566,9 @@ var Flow = class {
|
|
|
203254
203566
|
character: shown2.character,
|
|
203255
203567
|
characterName: shown2.characterName,
|
|
203256
203568
|
direction: shown2.direction,
|
|
203569
|
+
qualifier: shown2.qualifier,
|
|
203570
|
+
qualifierName: shown2.qualifierName,
|
|
203571
|
+
padAfter: this.padOf(beat.id),
|
|
203257
203572
|
gameData: beat.gameData,
|
|
203258
203573
|
...withTags
|
|
203259
203574
|
};
|
|
@@ -203290,6 +203605,26 @@ var Flow = class {
|
|
|
203290
203605
|
const key2 = castStringKey(character);
|
|
203291
203606
|
return this.host.strings[key2] ?? this.host.defaultStrings[key2] ?? this.host.castDisplay.get(character);
|
|
203292
203607
|
}
|
|
203608
|
+
/** The resolved pause of the beat an option shows as its prompt (its authored prompt, else its first content
|
|
203609
|
+
* line), for a saved option that predates prompts carrying one. */
|
|
203610
|
+
promptPadOf(node) {
|
|
203611
|
+
const beat = this.promptBeatOf(node);
|
|
203612
|
+
return beat ? this.padOf(beat.id) : DEFAULT_PAD_AFTER;
|
|
203613
|
+
}
|
|
203614
|
+
/** A line or text beat's resolved pause (line padding): its own `padAfter`, else the nearest default above
|
|
203615
|
+
* it, else the project's, else the built-in one; clamped to zero on a snippet's last line. */
|
|
203616
|
+
padOf(beatId) {
|
|
203617
|
+
return this.host.padIndex.get(beatId)?.resolved ?? DEFAULT_PAD_AFTER;
|
|
203618
|
+
}
|
|
203619
|
+
/** A speaker qualifier's shown name, resolved as a character's is: the `qualifier:<gameId>` string in the
|
|
203620
|
+
* active locale, else the default locale, else the authored name. Undefined in IDs-only mode (the game
|
|
203621
|
+
* maps the `qualifier` gameId itself). */
|
|
203622
|
+
resolveQualifierName(qualifier) {
|
|
203623
|
+
if (qualifier === void 0) return void 0;
|
|
203624
|
+
if (this.host.emitIds) return void 0;
|
|
203625
|
+
const key2 = qualifierStringKey(qualifier);
|
|
203626
|
+
return this.host.strings[key2] ?? this.host.defaultStrings[key2] ?? this.host.qualifierDisplay.get(qualifier);
|
|
203627
|
+
}
|
|
203293
203628
|
/** Split a ref into scope + name. Tokens: `@scene`, registered tokens, else `@patter` (incl. bare `@name`). */
|
|
203294
203629
|
splitRef(ref) {
|
|
203295
203630
|
return splitHostRef(this.host, ref);
|
|
@@ -203386,6 +203721,9 @@ function savedPrompt(p) {
|
|
|
203386
203721
|
if (p.character !== void 0) out.character = p.character;
|
|
203387
203722
|
if (p.characterName !== void 0) out.characterName = p.characterName;
|
|
203388
203723
|
if (p.direction !== void 0) out.direction = p.direction;
|
|
203724
|
+
if (p.qualifier !== void 0) out.qualifier = p.qualifier;
|
|
203725
|
+
if (p.qualifierName !== void 0) out.qualifierName = p.qualifierName;
|
|
203726
|
+
if (typeof p.padAfter === "number") out.padAfter = p.padAfter;
|
|
203389
203727
|
return out;
|
|
203390
203728
|
}
|
|
203391
203729
|
function serialiseSelectors(map2) {
|
|
@@ -203584,9 +203922,11 @@ function renderPlay(result) {
|
|
|
203584
203922
|
const out = [];
|
|
203585
203923
|
for (const e of result.events) {
|
|
203586
203924
|
switch (e.type) {
|
|
203587
|
-
case "line":
|
|
203588
|
-
|
|
203925
|
+
case "line": {
|
|
203926
|
+
const q = e.qualifierName ?? e.qualifier;
|
|
203927
|
+
out.push(`${e.character ?? "?"}${q ? ` (${q})` : ""}: ${e.text}`);
|
|
203589
203928
|
break;
|
|
203929
|
+
}
|
|
203590
203930
|
case "text":
|
|
203591
203931
|
out.push(` ${e.text}`);
|
|
203592
203932
|
break;
|
|
@@ -204591,6 +204931,11 @@ function extractLoc(loaded, opts = {}) {
|
|
|
204591
204931
|
const g = context?.character ? genderOf.get(context.character) : void 0;
|
|
204592
204932
|
return g ? { ...context, gender: g } : context;
|
|
204593
204933
|
};
|
|
204934
|
+
const qualifierName = new Map(projectQualifiers(loaded.project).map((q) => [q.gameId, q.name]));
|
|
204935
|
+
const lineContext = (b) => {
|
|
204936
|
+
const q = b.kind === "line" && b.qualifier ? qualifierName.get(b.qualifier) ?? b.qualifier : void 0;
|
|
204937
|
+
return { character: b.kind === "line" ? b.character : void 0, kind: b.kind, ...q ? { qualifier: q } : {} };
|
|
204938
|
+
};
|
|
204594
204939
|
const entries = [];
|
|
204595
204940
|
const push2 = (id, scene, context) => {
|
|
204596
204941
|
const src = source2[id];
|
|
@@ -204603,11 +204948,11 @@ function extractLoc(loaded, opts = {}) {
|
|
|
204603
204948
|
walkNodes(block.children, (node) => {
|
|
204604
204949
|
if (node.type === "group") {
|
|
204605
204950
|
const p = node.prompt;
|
|
204606
|
-
if (p) push2(p.id, scene.id,
|
|
204951
|
+
if (p) push2(p.id, scene.id, lineContext(p));
|
|
204607
204952
|
return;
|
|
204608
204953
|
}
|
|
204609
204954
|
for (const beat of node.beats ?? []) {
|
|
204610
|
-
if (beat.kind !== "gameEvent") push2(beat.id, scene.id,
|
|
204955
|
+
if (beat.kind !== "gameEvent") push2(beat.id, scene.id, lineContext(beat));
|
|
204611
204956
|
}
|
|
204612
204957
|
});
|
|
204613
204958
|
}
|
|
@@ -204619,6 +204964,12 @@ function extractLoc(loaded, opts = {}) {
|
|
|
204619
204964
|
const translation = isTemplate ? "" : target[id] ?? "";
|
|
204620
204965
|
entries.push({ id, scene: PROJECT_LOCALE_SCENE, source: src, translation, comments: commentsOf(id), context: withGender({ character: c2.name }), stale: staleFor(id, translation) });
|
|
204621
204966
|
}
|
|
204967
|
+
for (const q of usedQualifiers(loaded.project, loaded.scenes)) {
|
|
204968
|
+
const id = qualifierStringKey(q.gameId);
|
|
204969
|
+
const src = source2[id] ?? q.name;
|
|
204970
|
+
const translation = isTemplate ? "" : target[id] ?? "";
|
|
204971
|
+
entries.push({ id, scene: PROJECT_LOCALE_SCENE, source: src, translation, comments: commentsOf(id), context: { kind: "qualifier" }, stale: staleFor(id, translation) });
|
|
204972
|
+
}
|
|
204622
204973
|
return { project: loaded.project.project.id, defaultLocale, locale: targetLocale, entries };
|
|
204623
204974
|
}
|
|
204624
204975
|
var layoutOf = (loaded) => projectLayout(loaded.project);
|
|
@@ -204754,7 +205105,8 @@ function catalogToPo(catalog) {
|
|
|
204754
205105
|
for (const e of catalog.entries) {
|
|
204755
205106
|
for (const c2 of e.comments) out.push(`#. ${c2}`);
|
|
204756
205107
|
if (e.context?.character || e.context?.kind) {
|
|
204757
|
-
|
|
205108
|
+
const speaker = e.context.character && e.context.qualifier ? `${e.context.character} (${e.context.qualifier})` : e.context.character;
|
|
205109
|
+
out.push(`#. [${[e.context.kind, speaker].filter(Boolean).join(" ")}]`);
|
|
204758
205110
|
}
|
|
204759
205111
|
if (e.context?.gender) out.push(`#. Gender: ${e.context.gender}`);
|
|
204760
205112
|
out.push(`#: ${e.scene}`);
|
|
@@ -204850,7 +205202,7 @@ function poToCatalog(text) {
|
|
|
204850
205202
|
}
|
|
204851
205203
|
|
|
204852
205204
|
// ../ops/src/loc-xlsx.ts
|
|
204853
|
-
var HEADERS = ["ID", "Source", "Translation", "Comments", "Status", "Gender", "Scene"];
|
|
205205
|
+
var HEADERS = ["ID", "Source", "Translation", "Comments", "Status", "Gender", "Scene", "Qualifier"];
|
|
204854
205206
|
var SCENE_COLUMN = HEADERS.indexOf("Scene") + 1;
|
|
204855
205207
|
var SHEET_NAME_MAX = 31;
|
|
204856
205208
|
function uniqueSheetName(scene, used) {
|
|
@@ -204893,7 +205245,9 @@ async function catalogToXlsx(catalog) {
|
|
|
204893
205245
|
{ header: "Status", key: "status", width: 10 },
|
|
204894
205246
|
{ header: "Gender", key: "gender", width: 12 },
|
|
204895
205247
|
// Hidden: the translator has no use for it, and it is the import's only faithful record of the scene.
|
|
204896
|
-
{ header: "Scene", key: "scene", width: 22, hidden: true }
|
|
205248
|
+
{ header: "Scene", key: "scene", width: 22, hidden: true },
|
|
205249
|
+
// After the hidden Scene column, so the columns an older reader finds by position stay where they were.
|
|
205250
|
+
{ header: "Qualifier", key: "qualifier", width: 11 }
|
|
204897
205251
|
];
|
|
204898
205252
|
ws.getRow(1).font = { bold: true };
|
|
204899
205253
|
ws.views = [{ state: "frozen", ySplit: 1 }];
|
|
@@ -204905,7 +205259,8 @@ async function catalogToXlsx(catalog) {
|
|
|
204905
205259
|
comments: e.comments.join("\n"),
|
|
204906
205260
|
status: e.stale ? "stale" : e.translation ? "translated" : "",
|
|
204907
205261
|
gender: e.context?.gender ?? "",
|
|
204908
|
-
scene
|
|
205262
|
+
scene,
|
|
205263
|
+
qualifier: e.context?.qualifier ?? ""
|
|
204909
205264
|
});
|
|
204910
205265
|
}
|
|
204911
205266
|
}
|
|
@@ -204969,6 +205324,7 @@ function runVoiceScript(loaded, opts = {}) {
|
|
|
204969
205324
|
const source2 = sourceStrings(loaded);
|
|
204970
205325
|
const actorOf = /* @__PURE__ */ new Map();
|
|
204971
205326
|
for (const c2 of project.cast ?? []) if (c2.actor) actorOf.set(c2.name, c2.actor);
|
|
205327
|
+
const qualifierName = new Map(projectQualifiers(project).map((q) => [q.gameId, q.name]));
|
|
204972
205328
|
const voClasses = classesForChannel(project.documentationClasses ?? DEFAULT_DOCUMENTATION_CLASSES, "vo");
|
|
204973
205329
|
const ownVo = (id) => (docsOf.get(id) ?? []).filter((d) => d.type !== void 0 && voClasses.has(d.type)).map((d) => d.text);
|
|
204974
205330
|
const lines = [];
|
|
@@ -204981,6 +205337,7 @@ function runVoiceScript(loaded, opts = {}) {
|
|
|
204981
205337
|
scope: scope.join(" \u203A "),
|
|
204982
205338
|
id: beat.id,
|
|
204983
205339
|
character: beat.character ?? "",
|
|
205340
|
+
...beat.qualifier ? { qualifier: qualifierName.get(beat.qualifier) ?? beat.qualifier } : {},
|
|
204984
205341
|
actor: beat.character ? actorOf.get(beat.character) : void 0,
|
|
204985
205342
|
text: plainVoice(source2[beat.id] ?? ""),
|
|
204986
205343
|
comments: leadingVo ? [...leadingVo, ...own2] : own2,
|
|
@@ -205028,6 +205385,7 @@ async function voiceScriptToXlsx(data) {
|
|
|
205028
205385
|
{ header: "Scope", key: "scope", width: 28 },
|
|
205029
205386
|
{ header: "Line ID", key: "id", width: 18 },
|
|
205030
205387
|
{ header: "Character", key: "character", width: 16 },
|
|
205388
|
+
{ header: "Qualifier", key: "qualifier", width: 11 },
|
|
205031
205389
|
{ header: "Actor", key: "actor", width: 16 },
|
|
205032
205390
|
{ header: "Text", key: "text", width: 60 },
|
|
205033
205391
|
{ header: "Comments", key: "comments", width: 36 },
|
|
@@ -205040,6 +205398,7 @@ async function voiceScriptToXlsx(data) {
|
|
|
205040
205398
|
scope: l2.scope,
|
|
205041
205399
|
id: l2.id,
|
|
205042
205400
|
character: l2.character,
|
|
205401
|
+
qualifier: l2.qualifier ?? "",
|
|
205043
205402
|
actor: l2.actor ?? "",
|
|
205044
205403
|
text: l2.text,
|
|
205045
205404
|
comments: l2.comments.join("\n"),
|
|
@@ -205096,6 +205455,13 @@ function colourIndex(name) {
|
|
|
205096
205455
|
function characterColour(name) {
|
|
205097
205456
|
return CHAR_PALETTE[colourIndex(name)];
|
|
205098
205457
|
}
|
|
205458
|
+
function cueLabel(character, qualifierName) {
|
|
205459
|
+
return qualifierName ? `${character.toUpperCase()} (${qualifierName.toUpperCase()})` : character.toUpperCase();
|
|
205460
|
+
}
|
|
205461
|
+
function qualifierNamer(project) {
|
|
205462
|
+
const names = new Map(projectQualifiers(project).map((q) => [q.gameId, q.name]));
|
|
205463
|
+
return (gameId) => gameId ? names.get(gameId) ?? gameId : void 0;
|
|
205464
|
+
}
|
|
205099
205465
|
var MARKUP_RE = /<(b|i|bi)>([\s\S]*?)<\/\1>/g;
|
|
205100
205466
|
var INTERP_RE = /\{@[^}]*\}/g;
|
|
205101
205467
|
function textRuns(text) {
|
|
@@ -205174,6 +205540,12 @@ function runScriptDoc(loaded, opts = {}) {
|
|
|
205174
205540
|
}
|
|
205175
205541
|
const humanize = (cond) => humanizeNodeRefs(cond, (id) => blockName.get(id) ?? sceneOf.get(id) ?? id);
|
|
205176
205542
|
const els = [];
|
|
205543
|
+
const nameOf = qualifierNamer(project);
|
|
205544
|
+
const speaker = (beat) => ({
|
|
205545
|
+
character: beat.character ?? "",
|
|
205546
|
+
...beat.qualifier ? { qualifier: beat.qualifier, qualifierName: nameOf(beat.qualifier) } : {},
|
|
205547
|
+
direction: beat.direction
|
|
205548
|
+
});
|
|
205177
205549
|
const textOf = (id) => source2[id] ?? "";
|
|
205178
205550
|
const notes = (id, indent, sid) => {
|
|
205179
205551
|
for (const l2 of ownNotes(id)) els.push({ kind: "note", id, indent, snippet: sid, text: l2.text, class: l2.type });
|
|
@@ -205187,7 +205559,7 @@ function runScriptDoc(loaded, opts = {}) {
|
|
|
205187
205559
|
const emitBeats = (node, indent, sid) => {
|
|
205188
205560
|
for (const beat of node.beats ?? []) {
|
|
205189
205561
|
if (cut.has(beat.id)) continue;
|
|
205190
|
-
if (beat.kind === "line") els.push({ kind: "line", id: beat.id, indent, snippet: sid,
|
|
205562
|
+
if (beat.kind === "line") els.push({ kind: "line", id: beat.id, indent, snippet: sid, ...speaker(beat), runs: textRuns(textOf(beat.id)) });
|
|
205191
205563
|
else if (beat.kind === "text") els.push({ kind: "narration", id: beat.id, indent, snippet: sid, runs: textRuns(textOf(beat.id)) });
|
|
205192
205564
|
else if (beat.kind === "gameEvent") els.push({ kind: "gameEvent", id: beat.id, indent, snippet: sid, text: gameEventLabel(beat) });
|
|
205193
205565
|
else continue;
|
|
@@ -205232,7 +205604,7 @@ function runScriptDoc(loaded, opts = {}) {
|
|
|
205232
205604
|
continue;
|
|
205233
205605
|
}
|
|
205234
205606
|
if (cut.has(beat.id)) continue;
|
|
205235
|
-
if (beat.kind === "line") els.push({ kind: "line", id: beat.id, indent: indent + 1, snippet: cid,
|
|
205607
|
+
if (beat.kind === "line") els.push({ kind: "line", id: beat.id, indent: indent + 1, snippet: cid, ...speaker(beat), runs: textRuns(textOf(beat.id)) });
|
|
205236
205608
|
else if (beat.kind === "text") els.push({ kind: "narration", id: beat.id, indent: indent + 1, snippet: cid, runs: textRuns(textOf(beat.id)) });
|
|
205237
205609
|
else if (beat.kind === "gameEvent") els.push({ kind: "gameEvent", id: beat.id, indent: indent + 1, snippet: cid, text: gameEventLabel(beat) });
|
|
205238
205610
|
else continue;
|
|
@@ -206064,8 +206436,8 @@ var require_base64_js = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
206064
206436
|
}
|
|
206065
206437
|
return arr;
|
|
206066
206438
|
}
|
|
206067
|
-
function tripletToBase64(
|
|
206068
|
-
return lookup[
|
|
206439
|
+
function tripletToBase64(num2) {
|
|
206440
|
+
return lookup[num2 >> 18 & 63] + lookup[num2 >> 12 & 63] + lookup[num2 >> 6 & 63] + lookup[num2 & 63];
|
|
206069
206441
|
}
|
|
206070
206442
|
function encodeChunk(uint82, start, end) {
|
|
206071
206443
|
var tmp;
|
|
@@ -211842,26 +212214,26 @@ var require_sax = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
211842
212214
|
function parseEntity(parser) {
|
|
211843
212215
|
var entity = parser.entity;
|
|
211844
212216
|
var entityLC = entity.toLowerCase();
|
|
211845
|
-
var
|
|
212217
|
+
var num2;
|
|
211846
212218
|
var numStr = "";
|
|
211847
212219
|
if (parser.ENTITIES[entity]) return parser.ENTITIES[entity];
|
|
211848
212220
|
if (parser.ENTITIES[entityLC]) return parser.ENTITIES[entityLC];
|
|
211849
212221
|
entity = entityLC;
|
|
211850
212222
|
if (entity.charAt(0) === "#") if (entity.charAt(1) === "x") {
|
|
211851
212223
|
entity = entity.slice(2);
|
|
211852
|
-
|
|
211853
|
-
numStr =
|
|
212224
|
+
num2 = parseInt(entity, 16);
|
|
212225
|
+
numStr = num2.toString(16);
|
|
211854
212226
|
} else {
|
|
211855
212227
|
entity = entity.slice(1);
|
|
211856
|
-
|
|
211857
|
-
numStr =
|
|
212228
|
+
num2 = parseInt(entity, 10);
|
|
212229
|
+
numStr = num2.toString(10);
|
|
211858
212230
|
}
|
|
211859
212231
|
entity = entity.replace(/^0+/, "");
|
|
211860
|
-
if (isNaN(
|
|
212232
|
+
if (isNaN(num2) || numStr.toLowerCase() !== entity) {
|
|
211861
212233
|
strictFail(parser, "Invalid character entity");
|
|
211862
212234
|
return "&" + parser.entity + ";";
|
|
211863
212235
|
}
|
|
211864
|
-
return String.fromCodePoint(
|
|
212236
|
+
return String.fromCodePoint(num2);
|
|
211865
212237
|
}
|
|
211866
212238
|
function beginWhiteSpace(parser, c2) {
|
|
211867
212239
|
if (c2 === "<") {
|
|
@@ -213913,20 +214285,20 @@ var require_utils3 = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
213913
214285
|
return al + bl + cl + dl + el >>> 0;
|
|
213914
214286
|
}
|
|
213915
214287
|
exports2.sum64_5_lo = sum64_5_lo;
|
|
213916
|
-
function rotr64_hi(ah, al,
|
|
213917
|
-
return (al << 32 -
|
|
214288
|
+
function rotr64_hi(ah, al, num2) {
|
|
214289
|
+
return (al << 32 - num2 | ah >>> num2) >>> 0;
|
|
213918
214290
|
}
|
|
213919
214291
|
exports2.rotr64_hi = rotr64_hi;
|
|
213920
|
-
function rotr64_lo(ah, al,
|
|
213921
|
-
return (ah << 32 -
|
|
214292
|
+
function rotr64_lo(ah, al, num2) {
|
|
214293
|
+
return (ah << 32 - num2 | al >>> num2) >>> 0;
|
|
213922
214294
|
}
|
|
213923
214295
|
exports2.rotr64_lo = rotr64_lo;
|
|
213924
|
-
function shr64_hi(ah, al,
|
|
213925
|
-
return ah >>>
|
|
214296
|
+
function shr64_hi(ah, al, num2) {
|
|
214297
|
+
return ah >>> num2;
|
|
213926
214298
|
}
|
|
213927
214299
|
exports2.shr64_hi = shr64_hi;
|
|
213928
|
-
function shr64_lo(ah, al,
|
|
213929
|
-
return (ah << 32 -
|
|
214300
|
+
function shr64_lo(ah, al, num2) {
|
|
214301
|
+
return (ah << 32 - num2 | al >>> num2) >>> 0;
|
|
213930
214302
|
}
|
|
213931
214303
|
exports2.shr64_lo = shr64_lo;
|
|
213932
214304
|
}));
|
|
@@ -223440,7 +223812,7 @@ function paragraph(el, prev) {
|
|
|
223440
223812
|
});
|
|
223441
223813
|
case "line": {
|
|
223442
223814
|
const kids2 = [];
|
|
223443
|
-
if (el.character) kids2.push(new TextRun({ text: el.character.
|
|
223815
|
+
if (el.character) kids2.push(new TextRun({ text: cueLabel(el.character, el.qualifierName), font: SANS, bold: true, allCaps: true, color: characterColour(el.character), size: S.cue }), new TextRun({ text: " ", font: SANS, size: S.cue }));
|
|
223444
223816
|
if (el.direction) kids2.push(new TextRun({ text: `(${el.direction}) `, font: SERIF, italics: true, color: TOKENS.muted, size: S.body }));
|
|
223445
223817
|
kids2.push(...bodyRuns(el.runs, TOKENS.inkRead));
|
|
223446
223818
|
return new Paragraph({ keepLines: true, spacing: { before, after: 20 }, indent: { left: leftOf(el) + DIALOGUE_INDENT + HANG, hanging: HANG }, children: kids2 });
|
|
@@ -241743,8 +242115,8 @@ var StandardFont = class extends PDFFont {
|
|
|
241743
242115
|
return name in STANDARD_FONTS;
|
|
241744
242116
|
}
|
|
241745
242117
|
};
|
|
241746
|
-
var toHex = function(
|
|
241747
|
-
return `0000${
|
|
242118
|
+
var toHex = function(num2) {
|
|
242119
|
+
return `0000${num2.toString(16)}`.slice(-4);
|
|
241748
242120
|
};
|
|
241749
242121
|
var EmbeddedFont = class extends PDFFont {
|
|
241750
242122
|
constructor(document2, font, id) {
|
|
@@ -245640,13 +246012,14 @@ async function scriptToPdf(doc) {
|
|
|
245640
246012
|
case "line": {
|
|
245641
246013
|
const cueX = cx2 + DIALOGUE_INDENT2;
|
|
245642
246014
|
let bodyX = cueX;
|
|
245643
|
-
|
|
246015
|
+
const cue = el.character ? cueLabel(el.character, el.qualifierName) : "";
|
|
246016
|
+
if (cue) {
|
|
245644
246017
|
pdf.font("Sans-Bold").fontSize(CUE);
|
|
245645
|
-
bodyX = cueX + pdf.widthOfString(
|
|
246018
|
+
bodyX = cueX + pdf.widthOfString(cue) + CUE_GAP;
|
|
245646
246019
|
}
|
|
245647
246020
|
const yTop = pdf.y;
|
|
245648
|
-
if (
|
|
245649
|
-
pdf.font("Sans-Bold").fontSize(CUE).fillColor(hex(characterColour(el.character))).text(
|
|
246021
|
+
if (cue) {
|
|
246022
|
+
pdf.font("Sans-Bold").fontSize(CUE).fillColor(hex(characterColour(el.character))).text(cue, cueX, yTop + ascentPt("Serif", BODY) - ascentPt("Sans-Bold", CUE), { lineBreak: false });
|
|
245650
246023
|
pdf.y = yTop;
|
|
245651
246024
|
}
|
|
245652
246025
|
pdf.fontSize(BODY);
|
|
@@ -246350,8 +246723,9 @@ function staleParts(s, liveText, line2) {
|
|
|
246350
246723
|
const stale = [];
|
|
246351
246724
|
if (liveText !== s.baseline) stale.push("the text");
|
|
246352
246725
|
if (s.proposedCharacter !== void 0 && (line2?.character ?? "") !== (s.baselineCharacter ?? "")) stale.push("the speaker");
|
|
246726
|
+
if (s.proposedQualifier !== void 0 && (line2?.qualifier ?? "") !== (s.baselineQualifier ?? "")) stale.push("the speaker qualifier");
|
|
246353
246727
|
if (s.proposedDirection !== void 0 && (line2?.direction ?? "") !== (s.baselineDirection ?? "")) stale.push("the direction");
|
|
246354
|
-
if ((s.proposedCharacter !== void 0 || s.proposedDirection !== void 0) && !line2) stale.push("the line (no longer a spoken line)");
|
|
246728
|
+
if ((s.proposedCharacter !== void 0 || s.proposedQualifier !== void 0 || s.proposedDirection !== void 0) && !line2) stale.push("the line (no longer a spoken line)");
|
|
246355
246729
|
return stale;
|
|
246356
246730
|
}
|
|
246357
246731
|
function listOpenSuggestions(loaded, filter2 = {}) {
|
|
@@ -246506,6 +246880,12 @@ function applySuggestionDecisions(loaded, decisions, opts = {}) {
|
|
|
246506
246880
|
work.touch(flow.path);
|
|
246507
246881
|
edited = true;
|
|
246508
246882
|
}
|
|
246883
|
+
if (line2 && flow && s.proposedQualifier !== void 0 && s.proposedQualifier !== (line2.qualifier ?? "")) {
|
|
246884
|
+
if (s.proposedQualifier === "") delete line2.qualifier;
|
|
246885
|
+
else line2.qualifier = s.proposedQualifier;
|
|
246886
|
+
work.touch(flow.path);
|
|
246887
|
+
edited = true;
|
|
246888
|
+
}
|
|
246509
246889
|
if (line2 && flow && s.proposedDirection !== void 0 && s.proposedDirection !== (line2.direction ?? "")) {
|
|
246510
246890
|
if (s.proposedDirection === "") delete line2.direction;
|
|
246511
246891
|
else line2.direction = s.proposedDirection;
|
|
@@ -246556,7 +246936,7 @@ async function exportEditableScript(loaded, opts) {
|
|
|
246556
246936
|
if (isEditable(el)) {
|
|
246557
246937
|
const code = markerOf.get(el);
|
|
246558
246938
|
const baseline = source2[el.id] ?? "";
|
|
246559
|
-
lines[code] = el.kind === "line" ? { id: el.id, kind: "line", character: el.character, ...el.direction ? { direction: el.direction } : {}, baseline } : { id: el.id, kind: el.kind === "option" ? "option" : "narration", baseline };
|
|
246939
|
+
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 };
|
|
246560
246940
|
skeleton.push({ kind: "box", marker: code });
|
|
246561
246941
|
} else {
|
|
246562
246942
|
skeleton.push({ kind: el.kind, node: el.id, text: contextText(el) });
|
|
@@ -246623,7 +247003,7 @@ function box(el, code, status) {
|
|
|
246623
247003
|
const textW = Math.max(MIN_TEXT, FULL - ind - LEAD2 - MARGIN2);
|
|
246624
247004
|
const cell = (children, width, extra = {}) => new TableCell({ width: { size: width, type: WidthType.DXA }, borders: { top: NONE, bottom: NONE, left: NONE, right: NONE }, margins: { top: 60, bottom: 60, left: 100, right: 100 }, children, ...extra });
|
|
246625
247005
|
const lead = [];
|
|
246626
|
-
if (el.kind === "line" && el.character) lead.push(new TextRun({ text: el.character.
|
|
247006
|
+
if (el.kind === "line" && el.character) lead.push(new TextRun({ text: cueLabel(el.character, el.qualifierName), font: SANS, bold: true, allCaps: true, size: S.cue, color: characterColour(el.character) }));
|
|
246627
247007
|
if (el.kind === "line" && el.direction) lead.push(new TextRun({ text: `(${el.direction})`, font: SERIF, italics: true, size: S.cue, color: TOKENS.muted, break: el.character ? 1 : 0 }));
|
|
246628
247008
|
if (el.kind === "option") lead.push(new TextRun({ text: "\u25C7", color: TOKENS.accent, size: S.body }));
|
|
246629
247009
|
const ink = el.kind === "narration" ? TOKENS.inkSoft : el.kind === "option" ? TOKENS.ink : TOKENS.inkRead;
|
|
@@ -247087,6 +247467,7 @@ function planEditableImport(loaded, returned, opts) {
|
|
|
247087
247467
|
const places = indexPlaces(loaded);
|
|
247088
247468
|
const live = sourceStrings(loaded);
|
|
247089
247469
|
const cast = loaded.project.cast ?? [];
|
|
247470
|
+
const qualifiers = projectQualifiers(loaded.project);
|
|
247090
247471
|
const suggestions = [];
|
|
247091
247472
|
const comments = [];
|
|
247092
247473
|
let unchanged = 0, stale = 0;
|
|
@@ -247170,22 +247551,25 @@ function planEditableImport(loaded, returned, opts) {
|
|
|
247170
247551
|
problems.push({ severity: "info", kind: "untracked-edits", marker: code, anchor: sent.id, message: "This line has tracked changes and some untracked ones too, so the credit may be incomplete." });
|
|
247171
247552
|
}
|
|
247172
247553
|
const cue = sent.kind === "line" ? readLead(b.lead.proposed) : void 0;
|
|
247173
|
-
let proposedCharacter, proposedDirection;
|
|
247554
|
+
let proposedCharacter, proposedQualifier, proposedDirection;
|
|
247174
247555
|
if (sent.kind === "line" && cue) {
|
|
247175
|
-
const
|
|
247176
|
-
if (
|
|
247177
|
-
|
|
247178
|
-
|
|
247179
|
-
else {
|
|
247180
|
-
problems.push({ severity: "warning", kind: "unknown-speaker", marker: code, anchor: sent.id, message: `The speaker was changed to ${cue.character}, who isn't in the cast; it's in a comment.` });
|
|
247181
|
-
comment(here ?? nearestLine(boxes.indexOf(b)), author, `Speaker changed from ${sent.character ?? "(none)"} to ${cue.character}, who isn't in the cast.`, ts);
|
|
247182
|
-
}
|
|
247556
|
+
const read2 = readCue(cue.character, sent, cast, qualifiers);
|
|
247557
|
+
if (read2.unknownSpeaker !== void 0) {
|
|
247558
|
+
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.` });
|
|
247559
|
+
comment(here ?? nearestLine(boxes.indexOf(b)), author, `Speaker changed from ${sentCue(sent, qualifiers) || "(none)"} to ${read2.unknownSpeaker}, who isn't in the cast.`, ts);
|
|
247183
247560
|
}
|
|
247561
|
+
if (read2.unknownQualifier !== void 0) {
|
|
247562
|
+
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.` });
|
|
247563
|
+
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);
|
|
247564
|
+
}
|
|
247565
|
+
if (read2.character !== void 0 && read2.character !== (sent.character ?? "")) proposedCharacter = read2.character;
|
|
247566
|
+
if (read2.qualifier !== void 0 && read2.qualifier !== (sent.qualifier ?? "")) proposedQualifier = read2.qualifier;
|
|
247184
247567
|
if (tidy(cue.direction) !== tidy(sent.direction ?? "")) proposedDirection = tidy(cue.direction);
|
|
247185
247568
|
}
|
|
247569
|
+
const cueChanged = !!proposedCharacter || proposedQualifier !== void 0 || proposedDirection !== void 0;
|
|
247186
247570
|
for (const note of notes) comment(here ?? nearestLine(boxes.indexOf(b)), author, note, ts);
|
|
247187
247571
|
if (!here) {
|
|
247188
|
-
if (textChanged ||
|
|
247572
|
+
if (textChanged || cueChanged) {
|
|
247189
247573
|
problems.push({ severity: "warning", kind: "line-gone", marker: code, message: `The line ${quote(sent.baseline)} is no longer in the project; the editor's version is in a comment nearby.` });
|
|
247190
247574
|
comment(nearestLine(boxes.indexOf(b)), author, `For a line no longer in the project (${quote(sent.baseline)}), the editor wrote: ${quote(proposed)}`, ts);
|
|
247191
247575
|
} else unchanged++;
|
|
@@ -247198,16 +247582,17 @@ function planEditableImport(loaded, returned, opts) {
|
|
|
247198
247582
|
if (textChanged && !sameList(placeholders(proposed), placeholders(sent.baseline))) {
|
|
247199
247583
|
problems.push({ severity: "warning", kind: "placeholders", marker: code, anchor: sent.id, message: `The {@\u2026} placeholders changed, so this line wasn't suggested; the editor's version is in a comment.` });
|
|
247200
247584
|
comment(here, author, `The editor's version changes the {@\u2026} placeholders (${placeholders(sent.baseline).join(", ") || "none"} became ${placeholders(proposed).join(", ") || "none"}), so it wasn't made a suggestion: ${quote(proposed)}`, ts);
|
|
247201
|
-
if (!
|
|
247585
|
+
if (!cueChanged) continue;
|
|
247202
247586
|
}
|
|
247203
247587
|
const keepText = textChanged && sameList(placeholders(proposed), placeholders(sent.baseline));
|
|
247204
|
-
if (!keepText && !
|
|
247588
|
+
if (!keepText && !cueChanged) {
|
|
247205
247589
|
unchanged++;
|
|
247206
247590
|
continue;
|
|
247207
247591
|
}
|
|
247208
247592
|
suggestions.push(suggestion2({
|
|
247209
247593
|
proposed: keepText ? proposed : sent.baseline,
|
|
247210
247594
|
...proposedCharacter ? { proposedCharacter, baselineCharacter: sent.character ?? "" } : {},
|
|
247595
|
+
...proposedQualifier !== void 0 ? { proposedQualifier, baselineQualifier: sent.qualifier ?? "" } : {},
|
|
247211
247596
|
...proposedDirection !== void 0 ? { proposedDirection, baselineDirection: sent.direction ?? "" } : {}
|
|
247212
247597
|
}));
|
|
247213
247598
|
}
|
|
@@ -247288,6 +247673,32 @@ function readLead(text) {
|
|
|
247288
247673
|
const character = lines.find((l2) => l2 !== direction) ?? "";
|
|
247289
247674
|
return { character, direction: direction ? direction.slice(1, -1) : "" };
|
|
247290
247675
|
}
|
|
247676
|
+
function sentQualifierName(sent, qualifiers) {
|
|
247677
|
+
if (!sent.qualifier) return "";
|
|
247678
|
+
return sent.qualifierName ?? qualifiers.find((q) => q.gameId === sent.qualifier)?.name ?? sent.qualifier;
|
|
247679
|
+
}
|
|
247680
|
+
function sentCue(sent, qualifiers) {
|
|
247681
|
+
if (!sent.character) return "";
|
|
247682
|
+
const name = sentQualifierName(sent, qualifiers);
|
|
247683
|
+
return name ? `${sent.character} (${name})` : sent.character;
|
|
247684
|
+
}
|
|
247685
|
+
function readCue(text, sent, cast, qualifiers) {
|
|
247686
|
+
const cue = tidy(text);
|
|
247687
|
+
if (!cue) return {};
|
|
247688
|
+
const upper = (s) => s.toUpperCase();
|
|
247689
|
+
if (upper(cue) === upper(sentCue(sent, qualifiers))) return {};
|
|
247690
|
+
const sentCharacter = sent.character ?? "";
|
|
247691
|
+
const speaker = (name) => cast.find((c2) => upper(c2.name) === upper(name))?.name ?? (sentCharacter && upper(name) === upper(sentCharacter) ? sentCharacter : void 0);
|
|
247692
|
+
const whole = speaker(cue);
|
|
247693
|
+
if (whole !== void 0) return { character: whole, qualifier: "" };
|
|
247694
|
+
const m = /^(.*?)\s*\(([^()]*)\)$/s.exec(cue);
|
|
247695
|
+
const rest = m ? speaker(m[1].trim()) : void 0;
|
|
247696
|
+
if (!m || rest === void 0) return { unknownSpeaker: cue };
|
|
247697
|
+
const bracket = m[2].trim();
|
|
247698
|
+
if (sent.qualifier && upper(bracket) === upper(sentQualifierName(sent, qualifiers))) return { character: rest, qualifier: sent.qualifier };
|
|
247699
|
+
const q = qualifiers.find((x) => upper(x.name) === upper(bracket));
|
|
247700
|
+
return q ? { character: rest, qualifier: q.gameId } : { character: rest, unknownQualifier: bracket };
|
|
247701
|
+
}
|
|
247291
247702
|
function rankAuthors(changes) {
|
|
247292
247703
|
const counts = /* @__PURE__ */ new Map();
|
|
247293
247704
|
for (const c2 of changes) if (c2.author) counts.set(c2.author, (counts.get(c2.author) ?? 0) + 1);
|
|
@@ -248846,8 +249257,8 @@ var PlasticProvider = class {
|
|
|
248846
249257
|
}
|
|
248847
249258
|
/**
|
|
248848
249259
|
* Status for a batch of files in ONE `cm status --machinereadable --all --ignored`
|
|
248849
|
-
* spawn. The machine format lists one item per line
|
|
248850
|
-
*
|
|
249260
|
+
* spawn. The machine format lists one item per line, its fields split by the separator
|
|
249261
|
+
* we ask for: code, absolute path (unquoted), and two merge flags (see parseCmStatusLine).
|
|
248851
249262
|
*
|
|
248852
249263
|
* Flag choice matters: `cm status` defaults to `--controlledchanged`, which omits
|
|
248853
249264
|
* a content-modified-but-not-checked-out file (CH) and local deletes/moves. `--all`
|
|
@@ -248859,8 +249270,9 @@ var PlasticProvider = class {
|
|
|
248859
249270
|
* files (plus one `cm whoami`) to fill `outOfDate` (loaded changeset < head) and
|
|
248860
249271
|
* lock holder (`openedByMe` when that's us, else `lockedBy: ["user@workspace"]`).
|
|
248861
249272
|
*
|
|
248862
|
-
* NOTE: status
|
|
248863
|
-
*
|
|
249273
|
+
* NOTE: the status line format follows the Unreal Plastic plugin (UEPlasticPlugin), which
|
|
249274
|
+
* runs against live workspaces; the first version here assumed quoted paths, matched no
|
|
249275
|
+
* file on a real workspace, and so checked out files already checked out.
|
|
248864
249276
|
*
|
|
248865
249277
|
* @param {string[]} filePaths
|
|
248866
249278
|
* @param {import('../vcStatus.js').VCStatusOptions} [options]
|
|
@@ -248881,7 +249293,7 @@ var PlasticProvider = class {
|
|
|
248881
249293
|
}
|
|
248882
249294
|
};
|
|
248883
249295
|
function plasticStatusArgs(filePaths) {
|
|
248884
|
-
return ["status", "--machinereadable", "--all", "--ignored", ...filePaths.map((p) => resolve8(p))];
|
|
249296
|
+
return ["status", "--machinereadable", `--fieldseparator=${STATUS_SEPARATOR}`, "--all", "--ignored", ...filePaths.map((p) => resolve8(p))];
|
|
248885
249297
|
}
|
|
248886
249298
|
function buildPlasticBase(result, filePaths) {
|
|
248887
249299
|
const byPath = /* @__PURE__ */ new Map();
|
|
@@ -248977,17 +249389,33 @@ var PLASTIC_DIRTY_CODES = /* @__PURE__ */ new Set([
|
|
|
248977
249389
|
]);
|
|
248978
249390
|
var PLASTIC_UNTRACKED_CODES = /* @__PURE__ */ new Set(["PR", "IG"]);
|
|
248979
249391
|
var PLASTIC_CHECKED_OUT_CODES = /* @__PURE__ */ new Set(["CO", "AD", "CP", "RP", "MV"]);
|
|
249392
|
+
var STATUS_SEPARATOR = ";";
|
|
248980
249393
|
function parseCmStatusLine(line2) {
|
|
248981
249394
|
const trimmed = line2.trim();
|
|
248982
|
-
|
|
248983
|
-
|
|
248984
|
-
|
|
249395
|
+
if (!trimmed) return null;
|
|
249396
|
+
let fields;
|
|
249397
|
+
if (trimmed.includes(STATUS_SEPARATOR)) {
|
|
249398
|
+
fields = trimmed.split(STATUS_SEPARATOR);
|
|
249399
|
+
} else {
|
|
249400
|
+
const space = trimmed.indexOf(" ");
|
|
249401
|
+
if (space === -1) return null;
|
|
249402
|
+
fields = [trimmed.slice(0, space), trimmed.slice(space + 1).replace(/\s+(True|False)\s+\S+$/i, "")];
|
|
249403
|
+
}
|
|
249404
|
+
const codes = fields[0].trim().split("+");
|
|
248985
249405
|
const dirty = codes.some((c2) => PLASTIC_DIRTY_CODES.has(c2));
|
|
248986
249406
|
const untracked = codes.some((c2) => PLASTIC_UNTRACKED_CODES.has(c2));
|
|
248987
249407
|
if (!dirty && !untracked) return null;
|
|
248988
|
-
|
|
248989
|
-
|
|
248990
|
-
|
|
249408
|
+
let rest = fields.slice(1);
|
|
249409
|
+
if (rest.length >= 3 && /^(true|false)$/i.test(rest[rest.length - 2].trim())) rest = rest.slice(0, -2);
|
|
249410
|
+
let paths;
|
|
249411
|
+
if (codes.includes("MV")) {
|
|
249412
|
+
if (rest.length > 0 && /^\d+%$/.test(rest[0].trim())) rest = rest.slice(1);
|
|
249413
|
+
paths = rest.length >= 2 ? [rest.slice(0, -1).join(STATUS_SEPARATOR), rest[rest.length - 1]] : rest;
|
|
249414
|
+
} else {
|
|
249415
|
+
paths = [rest.join(STATUS_SEPARATOR)];
|
|
249416
|
+
}
|
|
249417
|
+
paths = paths.map((p) => p.trim().replace(/^"(.*)"$/, "$1")).filter((p) => p.length > 0);
|
|
249418
|
+
if (paths.length === 0) return null;
|
|
248991
249419
|
const checkedOut = !untracked && codes.some((c2) => PLASTIC_CHECKED_OUT_CODES.has(c2));
|
|
248992
249420
|
return { info: { tracked: !untracked, dirty, checkedOut, ignored: codes.includes("IG") }, paths };
|
|
248993
249421
|
}
|
|
@@ -249561,7 +249989,7 @@ function fileStatus(filePaths, options = {}) {
|
|
|
249561
249989
|
// package.json
|
|
249562
249990
|
var package_default = {
|
|
249563
249991
|
name: "@patterkit/cli",
|
|
249564
|
-
version: "0.
|
|
249992
|
+
version: "0.10.1",
|
|
249565
249993
|
description: "The `patter` CLI: validate, format, export, play, test coverage, localise, and merge Patter projects.",
|
|
249566
249994
|
type: "module",
|
|
249567
249995
|
license: "MIT",
|
|
@@ -249590,9 +250018,9 @@ var package_default = {
|
|
|
249590
250018
|
"build:standalone:others": "node scripts/build-standalone.mjs --targets=linux-x64,linux-arm64,windows-x64"
|
|
249591
250019
|
},
|
|
249592
250020
|
dependencies: {
|
|
249593
|
-
"@patterkit/core": "0.
|
|
249594
|
-
"@patterkit/ops": "0.
|
|
249595
|
-
"@wildwinter/simple-vc-lib": "^0.5.
|
|
250021
|
+
"@patterkit/core": "0.6.0",
|
|
250022
|
+
"@patterkit/ops": "0.16.0",
|
|
250023
|
+
"@wildwinter/simple-vc-lib": "^0.5.2"
|
|
249596
250024
|
}
|
|
249597
250025
|
};
|
|
249598
250026
|
|
|
@@ -250152,7 +250580,8 @@ ${count} issue(s)${advisory}`);
|
|
|
250152
250580
|
console.log("no open suggestions");
|
|
250153
250581
|
return 0;
|
|
250154
250582
|
}
|
|
250155
|
-
|
|
250583
|
+
const nameOf = qualifierNamer(loaded.project);
|
|
250584
|
+
for (const o of open) console.log(describeSuggestion(o, nameOf));
|
|
250156
250585
|
const stale = open.filter((o) => o.stale.length).length;
|
|
250157
250586
|
console.log(`${open.length} open suggestion(s)${stale ? `, ${stale} out of date` : ""}`);
|
|
250158
250587
|
return 0;
|
|
@@ -250353,12 +250782,13 @@ function renderImportReport(plan) {
|
|
|
250353
250782
|
for (const p of r.problems) out.push(` [${p.severity}] ${p.message}${p.anchor ? ` (${p.anchor})` : ""}`);
|
|
250354
250783
|
return out.join("\n");
|
|
250355
250784
|
}
|
|
250356
|
-
function describeSuggestion(o) {
|
|
250785
|
+
function describeSuggestion(o, qualifierName) {
|
|
250357
250786
|
const s = o.suggestion;
|
|
250358
250787
|
const parts = [];
|
|
250359
250788
|
if (s.proposedCut) parts.push("cut this line");
|
|
250360
250789
|
if (s.proposed !== s.baseline) parts.push(`"${s.baseline}" -> "${s.proposed}"`);
|
|
250361
250790
|
if (s.proposedCharacter !== void 0) parts.push(`speaker ${s.baselineCharacter || "(none)"} -> ${s.proposedCharacter}`);
|
|
250791
|
+
if (s.proposedQualifier !== void 0) parts.push(`qualifier ${qualifierName(s.baselineQualifier) ?? "(none)"} -> ${qualifierName(s.proposedQualifier) ?? "(none)"}`);
|
|
250362
250792
|
if (s.proposedDirection !== void 0) parts.push(`direction "${s.baselineDirection ?? ""}" -> "${s.proposedDirection}"`);
|
|
250363
250793
|
const from = s.handoff ? ` [${s.handoff.id}]` : "";
|
|
250364
250794
|
const stale = o.stale.length ? ` OUT OF DATE (${o.stale.join(", ")})` : "";
|