@patterkit/cli 0.10.0 → 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.
Files changed (2) hide show
  1. package/dist/cli.js +361 -102
  2. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -18844,17 +18844,17 @@ var require_saxes = __commonJS({
18844
18844
  this.fail(this.isName(entity) ? "undefined entity." : "disallowed character in entity name.");
18845
18845
  return `&${entity};`;
18846
18846
  }
18847
- let num = NaN;
18847
+ let num2 = NaN;
18848
18848
  if (entity[1] === "x" && /^#x[0-9a-f]+$/i.test(entity)) {
18849
- num = parseInt(entity.slice(2), 16);
18849
+ num2 = parseInt(entity.slice(2), 16);
18850
18850
  } else if (/^#[0-9]+$/.test(entity)) {
18851
- num = parseInt(entity.slice(1), 10);
18851
+ num2 = parseInt(entity.slice(1), 10);
18852
18852
  }
18853
- if (!this.isChar(num)) {
18853
+ if (!this.isChar(num2)) {
18854
18854
  this.fail("malformed character entity.");
18855
18855
  return `&${entity};`;
18856
18856
  }
18857
- return String.fromCodePoint(num);
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(num) {
45764
+ function bufferizeInt(num2) {
45765
45765
  var tmp = ensureBuffer(4);
45766
- tmp.writeInt32BE(num, 0);
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, num, offset, end) {
47153
+ var indexOf = function(block, num2, offset, end) {
47154
47154
  for (; offset < end; offset++) {
47155
- if (block[offset] === num) return 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(num) {
64136
- return lookup[num >> 18 & 63] + lookup[num >> 12 & 63] + lookup[num >> 6 & 63] + lookup[num & 63];
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, num) {
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 < num; ++i) t.table[lengths2[off + 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 < num; ++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, num, base) {
64268
- if (!num)
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 - num;
64275
- d.tag >>>= num;
64276
- d.bitcount -= num;
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, num, length;
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 (num = 0; num < hlit + hdist; ) {
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[num - 1];
64313
+ var prev = lengths[num2 - 1];
64314
64314
  for (length = tinf_read_bits(d, 2, 3); length; --length) {
64315
- lengths[num++] = prev;
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[num++] = 0;
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[num++] = 0;
64325
+ lengths[num2++] = 0;
64326
64326
  }
64327
64327
  break;
64328
64328
  default:
64329
- lengths[num++] = sym;
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(num) {
190963
- return lookup.charAt(num);
190962
+ function encode(num2) {
190963
+ return lookup.charAt(num2);
190964
190964
  }
190965
- function tripletToBase64(num) {
190966
- return encode(num >> 18 & 63) + encode(num >> 12 & 63) + encode(num >> 6 & 63) + encode(num & 63);
190965
+ function tripletToBase64(num2) {
190966
+ return encode(num2 >> 18 & 63) + encode(num2 >> 12 & 63) + encode(num2 >> 6 & 63) + encode(num2 & 63);
190967
190967
  }
190968
190968
  for (i = 0, length = uint82.length - extraBytes; i < length; i += 3) {
190969
190969
  temp = (uint82[i] << 16) + (uint82[i + 1] << 8) + uint82[i + 2];
@@ -197906,6 +197906,9 @@ function usedQualifiers(project, scenes) {
197906
197906
  }
197907
197907
  return projectQualifiers(project).filter((q) => used.has(q.gameId));
197908
197908
  }
197909
+ var DEFAULT_PAD_AFTER = 0.6;
197910
+ var PAD_AFTER_MIN = -10;
197911
+ var PAD_AFTER_MAX = 60;
197909
197912
  var DEFAULT_WRITING_STATUSES = [
197910
197913
  { name: "stub", colour: 0 },
197911
197914
  // red
@@ -198073,11 +198076,44 @@ function validateProject(input) {
198073
198076
  const sceneGameIds = /* @__PURE__ */ new Map();
198074
198077
  const jumps = [];
198075
198078
  const castNames = new Set((project.cast ?? []).map((c2) => c2.name));
198076
- const qualifierIds = new Set(projectQualifiers(project).map((q) => q.gameId));
198079
+ const qualifierIds = project.qualifiers === void 0 || Array.isArray(project.qualifiers) ? new Set(projectQualifiers(project).flatMap((q) => isQualifierEntry(q) && typeof q.gameId === "string" ? [q.gameId] : [])) : null;
198080
+ const checkPad = (value, what, id) => {
198081
+ if (value === void 0) return;
198082
+ if (typeof value !== "number" || !Number.isFinite(value) || value < PAD_AFTER_MIN || value > PAD_AFTER_MAX) {
198083
+ issues.push({ code: "invalid-pad", message: `${what} pause '${String(value)}' must be a number of seconds from ${PAD_AFTER_MIN} to ${PAD_AFTER_MAX}`, id });
198084
+ }
198085
+ };
198086
+ const checkSeamPad = (beats) => {
198087
+ const last2 = (beats ?? []).at(-1);
198088
+ if (!last2 || last2.kind !== "line" && last2.kind !== "text" || typeof last2.padAfter !== "number" || last2.padAfter >= 0) return;
198089
+ issues.push({
198090
+ code: "pad-overlaps-seam",
198091
+ severity: "warning",
198092
+ id: last2.id,
198093
+ message: `beat '${last2.id}' is its snippet's last line, so its negative pause can't cut in on what follows; it plays as no pause`
198094
+ });
198095
+ };
198096
+ const checkPromptPad = (option) => {
198097
+ const p = option.prompt;
198098
+ if (!p || p.kind !== "line" && p.kind !== "text" || typeof p.padAfter !== "number" || p.padAfter >= 0) return;
198099
+ if ((option.selector ?? "run") !== "run" || !Array.isArray(option.children)) return;
198100
+ const first2 = option.children[0];
198101
+ if (first2 && (first2.type !== "snippet" || first2.condition || (first2.beats?.length ?? 0) > 0)) return;
198102
+ issues.push({
198103
+ code: "prompt-pad-without-beat",
198104
+ severity: "warning",
198105
+ id: p.id,
198106
+ message: `prompt '${p.id}' has a negative pause, but its option plays nothing before it moves on, so nothing cuts in on it`
198107
+ });
198108
+ };
198077
198109
  const checkQualifier = (beat, what) => {
198078
- if (beat.kind === "line" && beat.qualifier !== void 0 && !qualifierIds.has(beat.qualifier)) {
198110
+ if (beat.kind !== "line" || beat.qualifier === void 0) return;
198111
+ if (qualifierIds && !qualifierIds.has(beat.qualifier)) {
198079
198112
  issues.push({ code: "unknown-qualifier", message: `${what} '${beat.id}' qualifier '${beat.qualifier}' is not in the project's qualifiers`, id: beat.id });
198080
198113
  }
198114
+ if (!beat.character) {
198115
+ issues.push({ code: "qualifier-without-speaker", severity: "warning", message: `${what} '${beat.id}' has the qualifier '${beat.qualifier}' but no speaker`, id: beat.id });
198116
+ }
198081
198117
  };
198082
198118
  const checkTags = (tags, where, id) => {
198083
198119
  for (const t of tags ?? []) {
@@ -198103,6 +198139,7 @@ function validateProject(input) {
198103
198139
  validateProjectFile(project, issues);
198104
198140
  for (const scene of scenes) {
198105
198141
  seeId(scene.id, "scene", "project");
198142
+ checkPad(scene.padAfterDefault, `scene '${scene.id}' default`, scene.id);
198106
198143
  const where = `scene '${scene.id}'`;
198107
198144
  if (typeof scene.id === "string") addressable.add(scene.id);
198108
198145
  if (!scene.name?.trim()) {
@@ -198138,6 +198175,7 @@ function validateProject(input) {
198138
198175
  }
198139
198176
  for (const block of scene.blocks) {
198140
198177
  seeId(block.id, "block", where);
198178
+ checkPad(block.padAfterDefault, `block '${block.id}' default`, block.id);
198141
198179
  checkTags(block.tags, `block '${block.id}'`, block.id);
198142
198180
  if (typeof block.id === "string") addressable.add(block.id);
198143
198181
  if (!block.name?.trim()) {
@@ -198178,6 +198216,12 @@ function validateProject(input) {
198178
198216
  walkNodes(block.children, (node) => {
198179
198217
  const inBlock = `block '${block.id}'`;
198180
198218
  checkTags(node.tags, `${node.type} '${node.id}'`, node.id);
198219
+ checkPad(node.padAfterDefault, `${node.type} '${node.id}' default`, node.id);
198220
+ if (node.type === "group" && node.prompt) checkPad(node.prompt.padAfter, `prompt '${node.prompt.id}'`, node.prompt.id);
198221
+ if (node.type === "snippet") {
198222
+ for (const beat of node.beats ?? []) if (beat.kind !== "gameEvent") checkPad(beat.padAfter, `beat '${beat.id}'`, beat.id);
198223
+ checkSeamPad(node.beats);
198224
+ }
198181
198225
  if (node.type === "group") {
198182
198226
  seeId(node.id, "group", inBlock);
198183
198227
  if (!Array.isArray(node.children)) {
@@ -198203,6 +198247,7 @@ function validateProject(input) {
198203
198247
  issues.push({ code: "unknown-character", message: `prompt '${p.id}' speaker '${p.character}' is not in the project cast`, id: p.id });
198204
198248
  }
198205
198249
  checkQualifier(p, "prompt");
198250
+ checkPromptPad(opt);
198206
198251
  }
198207
198252
  }
198208
198253
  if (fallbacks > 1) {
@@ -198254,6 +198299,9 @@ function validateProject(input) {
198254
198299
  }
198255
198300
  return issues;
198256
198301
  }
198302
+ function isQualifierEntry(q) {
198303
+ return typeof q === "object" && q !== null && !Array.isArray(q);
198304
+ }
198257
198305
  function validateProjectFile(project, issues) {
198258
198306
  checkDecls(project.properties, "project properties", issues);
198259
198307
  for (const scope of project.scopeRegistry?.scopes ?? []) {
@@ -198305,8 +198353,20 @@ function validateProjectFile(project, issues) {
198305
198353
  if (project.recordingStatuses) {
198306
198354
  checkLadderNames(project.recordingStatuses.map((s) => s.name), "recordingStatuses", issues);
198307
198355
  }
198356
+ const pad2 = project.padAfterDefault;
198357
+ if (pad2 !== void 0 && (typeof pad2 !== "number" || !Number.isFinite(pad2) || pad2 < PAD_AFTER_MIN || pad2 > PAD_AFTER_MAX)) {
198358
+ issues.push({ code: "invalid-pad", message: `the project default pause '${String(pad2)}' must be a number of seconds from ${PAD_AFTER_MIN} to ${PAD_AFTER_MAX}` });
198359
+ }
198308
198360
  const seenQualifiers = /* @__PURE__ */ new Set();
198309
- for (const q of project.qualifiers ?? []) {
198361
+ const qualifiers = project.qualifiers;
198362
+ if (qualifiers !== void 0 && !Array.isArray(qualifiers)) {
198363
+ issues.push({ code: "invalid-qualifier", message: "qualifiers must be a list" });
198364
+ }
198365
+ for (const q of Array.isArray(qualifiers) ? qualifiers : []) {
198366
+ if (!isQualifierEntry(q)) {
198367
+ issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q)}' is not a qualifier (a name and an address)` });
198368
+ continue;
198369
+ }
198310
198370
  if (typeof q.name !== "string" || !q.name.trim()) {
198311
198371
  issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q.gameId)}' has no name` });
198312
198372
  }
@@ -199747,6 +199807,8 @@ function compileNode(node, foreign) {
199747
199807
  shared: node.shared,
199748
199808
  options: node.options,
199749
199809
  children: node.children.map((c2) => compileNode(c2, foreign)),
199810
+ padAfterDefault: node.padAfterDefault,
199811
+ // line padding: the default for the beats inside
199750
199812
  gameData: node.gameData,
199751
199813
  tags: node.tags,
199752
199814
  // author tags (#215), accumulated at runtime
@@ -199764,6 +199826,8 @@ function compileNode(node, foreign) {
199764
199826
  beats: node.beats,
199765
199827
  onEnter: node.onEnter?.map((e) => compileEffect(e, foreign)),
199766
199828
  onExit: node.onExit?.map((e) => compileEffect(e, foreign)),
199829
+ padAfterDefault: node.padAfterDefault,
199830
+ // line padding: the default for the beats inside
199767
199831
  gameData: node.gameData,
199768
199832
  tags: node.tags,
199769
199833
  // author tags (#215)
@@ -199781,6 +199845,8 @@ function compileBlock(block, foreign) {
199781
199845
  ...block.gameId ? { gameId: block.gameId } : {},
199782
199846
  // host-facing address; runtime falls back to the name slug
199783
199847
  children: block.children.map((c2) => compileNode(c2, foreign)),
199848
+ padAfterDefault: block.padAfterDefault,
199849
+ // line padding: the default for the beats inside
199784
199850
  gameData: block.gameData,
199785
199851
  tags: block.tags
199786
199852
  // author tags (#215)
@@ -199793,6 +199859,8 @@ function compileScene(scene, foreign) {
199793
199859
  name: scene.name,
199794
199860
  ...scene.gameId ? { gameId: scene.gameId } : {},
199795
199861
  // host-facing address; runtime falls back to the name slug
199862
+ padAfterDefault: scene.padAfterDefault,
199863
+ // line padding: the default for the beats inside
199796
199864
  gameData: scene.gameData,
199797
199865
  tags: scene.tags,
199798
199866
  // author tags (#215)
@@ -199858,7 +199926,8 @@ function exportBundle(input) {
199858
199926
  strings,
199859
199927
  properties: project.properties ?? [],
199860
199928
  ...foreignScopes ? { scopeRegistry: foreignScopes } : {},
199861
- ...qualifiers.length > 0 ? { qualifiers } : {}
199929
+ ...qualifiers.length > 0 ? { qualifiers } : {},
199930
+ ...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {}
199862
199931
  }, { trailingComma: false })),
199863
199932
  // Structure-only fingerprint (live bundle refresh): the same hash with the string tables left
199864
199933
  // out. Same structureHash + a different hash = a text-only edit, safe to swap in place with
@@ -199868,7 +199937,8 @@ function exportBundle(input) {
199868
199937
  scenes: scenesOut,
199869
199938
  properties: project.properties ?? [],
199870
199939
  ...foreignScopes ? { scopeRegistry: foreignScopes } : {},
199871
- ...qualifiers.length > 0 ? { qualifiers } : {}
199940
+ ...qualifiers.length > 0 ? { qualifiers } : {},
199941
+ ...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {}
199872
199942
  }, { trailingComma: false }))
199873
199943
  },
199874
199944
  voiced: project.voiced ?? false,
@@ -199885,6 +199955,8 @@ function exportBundle(input) {
199885
199955
  return m;
199886
199956
  }),
199887
199957
  ...qualifiers.length > 0 ? { qualifiers } : {},
199958
+ // The project's own line-padding default, only when it sets one: absent, every runtime uses the built-in one.
199959
+ ...typeof project.padAfterDefault === "number" ? { padAfterDefault: project.padAfterDefault } : {},
199888
199960
  properties: project.properties,
199889
199961
  scopeRegistry: hostScopes,
199890
199962
  gameDataFields: project.gameDataFields,
@@ -200498,6 +200570,9 @@ function mergeProject(base, ours, theirs) {
200498
200570
  } else if (k === "gameDataFields") {
200499
200571
  const v = mergeGameDataFields(asMap(base[k]), asMap(ours[k]), asMap(theirs[k]), conflicts);
200500
200572
  if (Object.keys(v).length > 0) merged[k] = v;
200573
+ } else if (k === "qualifiers") {
200574
+ const v = mergeQualifiers(base[k], ours[k], theirs[k], conflicts);
200575
+ if (v !== void 0) merged[k] = v;
200501
200576
  } else if (k === "locales") {
200502
200577
  merged.locales = mergeLocales(asMap(base.locales), asMap(ours.locales), asMap(theirs.locales), conflicts);
200503
200578
  } else {
@@ -200515,6 +200590,23 @@ function mergeGameDataFields(b, o, t, conflicts) {
200515
200590
  }
200516
200591
  return out;
200517
200592
  }
200593
+ function mergeQualifiers(base, ours, theirs, conflicts) {
200594
+ if (base === void 0 && ours === void 0 && theirs === void 0) return void 0;
200595
+ const keyed = (arr) => new Set(arr.map((q) => isObj(q) && typeof q.gameId === "string" ? q.gameId : null)).size === arr.length && arr.every((q) => isObj(q) && typeof q.gameId === "string");
200596
+ const list = (v) => v === void 0 ? DEFAULT_QUALIFIERS : Array.isArray(v) && keyed(v) ? v : null;
200597
+ const b = list(base), o = list(ours), t = list(theirs);
200598
+ if (!b || !o || !t) return merge3(base, ours, theirs, "", "qualifiers", conflicts);
200599
+ const byId = (arr) => {
200600
+ const m = {};
200601
+ for (const q of arr) if (isObj(q) && typeof q.gameId === "string") m[q.gameId] = q;
200602
+ return m;
200603
+ };
200604
+ const merged = mergeMap(byId(b), byId(o), byId(t), "qualifiers", conflicts);
200605
+ const order = [];
200606
+ for (const q of [...o, ...t]) if (isObj(q) && typeof q.gameId === "string" && q.gameId in merged && !order.includes(q.gameId)) order.push(q.gameId);
200607
+ const out = order.map((id) => merged[id]);
200608
+ return eq(out, DEFAULT_QUALIFIERS) ? void 0 : out;
200609
+ }
200518
200610
  function mergeLocales(b, o, t, conflicts) {
200519
200611
  const out = {};
200520
200612
  const def = merge3(b.default, o.default, t.default, "", "locales.default", conflicts);
@@ -201276,7 +201368,55 @@ function planBuild(loaded, opts = {}) {
201276
201368
  }
201277
201369
 
201278
201370
  // ../ops/src/playable-runtime.ts
201279
- var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var W=Object.defineProperty;var $e=Object.getOwnPropertyDescriptor;var He=Object.getOwnPropertyNames;var _e=Object.prototype.hasOwnProperty;var Ue=(n,e)=>{for(var t in e)W(n,t,{get:e[t],enumerable:!0})},We=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of He(e))!_e.call(n,s)&&s!==t&&W(n,s,{get:()=>e[s],enumerable:!(r=$e(e,s))||r.enumerable});return n};var Je=n=>We(W({},"__esModule",{value:!0}),n);var gt={};Ue(gt,{Checkpoint:()=>T,Engine:()=>_,Flow:()=>D,buildTagIndex:()=>H,describeBundle:()=>Ge,effectiveGameData:()=>Ve,gameDataFields:()=>je,gameDataValue:()=>ie});function R(n,e){return n!==void 0&&Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0}function k(n){switch(n[0]){case"b":return{kind:"bool",value:n[1]};case"n":return{kind:"number",value:n[1]};case"s":return{kind:"string",value:n[1]};case"sv":return{kind:"scopedvar",scope:n[1],name:n[2]};case"u":return{kind:"unary",op:n[1],operand:k(n[2])};case"bin":return{kind:"binary",op:n[1],left:k(n[2]),right:k(n[3])};case"call":{let e=n.slice(2).map(k);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var g=class extends Error{constructor(e){super(e),this.name="EvalError"}};function V(n,e,t){let r=new Map(t.scopes.map(i=>[i.token,i.missing??"false"])),s=i=>{switch(i.kind){case"bool":return i.value;case"number":return i.value;case"string":return i.value;case"scopedvar":{let o=R(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):R(o,i.name);if(a===void 0){if(r.get(i.scope)==="throw")throw new g(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{if(i.name==="advance"&&!R(t.functions,i.name)){let a=i.args[0];if(i.args.length!==1||a===void 0)throw new g(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=J(a,e);if(c===void 0)throw new g("advance() needs a quality reference (@scope.name of a quality property)");let l=w(s(a),c,"advance");return c[Math.min(l+1,c.length-1)]}let o=R(t.functions,i.name);if(!o)throw new g(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new g("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=s(i.operand);if(typeof a!="boolean")throw new g(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new g(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let f=s(i.left);if(typeof f!="boolean")throw new g(`\'and\' requires boolean operands, left is ${typeof f}`);if(!f)return!1;let u=s(i.right);if(typeof u!="boolean")throw new g(`\'and\' requires boolean operands, right is ${typeof u}`);return u}if(i.op==="or"){let f=s(i.left);if(typeof f!="boolean")throw new g(`\'or\' requires boolean operands, left is ${typeof f}`);if(f)return!0;let u=s(i.right);if(typeof u!="boolean")throw new g(`\'or\' requires boolean operands, right is ${typeof u}`);return u}let o=s(i.left),a=s(i.right),c=J(i.left,e),l=J(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!Qe(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new g(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return w(o,p,">")>w(a,p,">");case">=":return w(o,p,">=")>=w(a,p,">=");case"<":return w(o,p,"<")<w(a,p,"<");case"<=":return w(o,p,"<=")<=w(a,p,"<=");case"+":case"-":case"*":case"/":throw new g(`\'${i.op}\' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(i.op){case"==":return ge(o,a);case"!=":return!ge(o,a);case">":return E(o,a,">"),o>a;case">=":return E(o,a,">="),o>=a;case"<":return E(o,a,"<"),o<a;case"<=":return E(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new g(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return E(o,a,"-"),o-a;case"*":return E(o,a,"*"),o*a;case"/":if(E(o,a,"/"),a===0)throw new g("division by zero");return o/a}}}};return s(n)}function ge(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;let t=[...n].sort(),r=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==r[s])return!1;return!0}return n===e}function E(n,e,t){if(typeof n!="number"||typeof e!="number")throw new g(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function J(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function w(n,e,t){if(typeof n!="string")throw new g(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new g(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var Qe=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function O(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function Q(n){let e=O(n);return{next(){e=e+1831565813>>>0;let t=Math.imul(e^e>>>15,1|e);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296},state(){return e}}}var Ke={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},ze=[Ke];function ue(n,e,t){let r=t?.countingCalls??ze;return L(n,t?.want??!0,e,r)}function L(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=L(n.left,e,t,r),i=L(n.right,e,t,r);return n.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(n.kind==="unary"&&n.op==="not")return L(n.operand,!e,t,r);if(n.kind==="call"){let s=r.find(i=>i.name===n.name);if(s){let i=s.count(n),o=t(n);return e?o?i:0:o?0:1}}return t(n)===e?1:0}var b=class n{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let r=this.norm(t.name);this.decls.set(r,t),this.values[r]=structuredClone(t.default??P(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,r){let s=this.norm(e);if(!r?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:r?.silent??!1,reason:r?.reason};this.values[s]=t;for(let o of[...this.auditors])o(i);if(!i.silent)for(let o of[...this.subscribers])o(i);return i}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}onAudit(e){return this.auditors.add(e),()=>this.auditors.delete(e)}rows(){return[...this.decls.entries()].map(([e,t])=>fe(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm,pathPrefix:this.pathPrefix});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e){for(let[t,r]of Object.entries(e))this.values[this.norm(t)]=r}};function fe(n,e,t,r,s=""){let i=r??n.name.toLowerCase();return{name:i,path:s+i,type:n.type,value:e,default:n.default??P(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var Ye=n=>n.toLowerCase(),K=1,M=class{scopes=new Map;parked=new Map;rev=0;get revision(){return this.rev}defineOwned(e,t,r){let s=typeof r=="string"?{pathPrefix:r}:r??{},i=new b(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,i,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,r){this.assertFree(e,r?.owner),this.scopes.set(e,{kind:"owned",bag:t,...r?.owner!==void 0?{owner:r.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(t.load(s),this.parked.delete(e)),this}remove(e,t){let r=this.scopes.get(e);if(!r)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&r.kind==="owned"&&this.parked.set(e,r.bag.save()),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&this.parked.delete(t);return this}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??Ye,a=new Map;for(let c of r)a.set(o(c.name),c);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:i.writable??!0,norm:o,...i.owner!==void 0?{owner:i.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let r=this.scopes.get(e);if(r)return r.kind==="owned"?r.bag.get(t):r.resolver.get(r.norm(t))}set(e,t,r,s){let i=this.scopes.get(e);if(!i)throw new Error(`unknown scope \'@${e}\'`);if(i.kind==="owned"){if(!s?.host&&!i.bag.writable(t))throw new Error(`\'@${e}.${t}\' is read-only`);i.bag.set(t,r,s?.host?{host:!0}:void 0);return}let o=i.norm(t);if(!i.resolver.set)throw new Error(`\'@${e}.${t}\' is read-only`);if(!s?.host&&!this.foreignWritable(i,o))throw new Error(`\'@${e}.${t}\' is read-only`);i.resolver.set(o,r)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,r]of this.scopes){let s=r.owner!==void 0?{owner:r.owner}:{};if(r.kind==="owned")for(let i of r.bag.rows())e.push({scope:t,...s,...i});else for(let[i,o]of r.decls)e.push({scope:t,...s,...fe(o,r.resolver.get(i),this.foreignWritable(r,i),i,`${t}.`)})}return e}toEvalContext(e,t){let r=this.view(t?.aliases),s={};for(let[o,a]of r)s[o]=a.kind==="owned"?a.bag.values:a.resolver;let i=this.qualityLadders(r);return i.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(o,a)=>{let c=r.get(o);return c?i.get(o)?.get(Xe(c)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[r,s]of Object.entries(e??{})){let i=this.scopes.get(s);if(!i)throw new Error(`alias \'@${r}\' names \'${s}\', which is not registered`);t.set(r,i)}return t}qualityLadders(e){let t=new Map;for(let[r,s]of e)for(let[i,o]of he(s)){if(o.type!=="quality"||o.stages===void 0)continue;let a=t.get(r);a||(a=new Map,t.set(r,a)),a.set(i,o.stages)}return t}toSchema(e){let t=new Map;for(let[r,s]of this.view(e?.aliases)){let i=he(s);if(i.length===0)continue;let o=new Map;for(let[a,c]of i)o.set(a,{type:c.type,enumValues:c.values,...c.stages!==void 0?{stages:c.stages}:{}});t.set(r,o)}return{properties:t}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());for(let[t,r]of this.parked)e[t]=structuredClone(r);return e}load(e,t){t?.keepParked||this.parked.clear();for(let[r,s]of Object.entries(e)){let i=this.scopes.get(r);i?.kind==="owned"?i.bag.load(s):i||this.parked.set(r,structuredClone(s))}}saveFragment(){return{version:K,scopes:this.save()}}loadFragment(e){if(e.version!==K)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${K})`);this.load(e.scopes)}assertFree(e,t){let r=this.scopes.get(e);if(!r)return;let s=r.owner!==void 0?` by ${r.owner}`:"",i=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope \'@${e}\' is already registered${s}${i}`)}};function he(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function Xe(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function P(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return n.values?.[0]??"";case"flags":return[];case"quality":return n.stages?.[0]??"";default:return!1}}var me=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function B(n){return n.ctx.host??{}}var z={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new g("random(a, b) requires exactly 2 arguments");let t=B(e).nextRandom;if(!t)throw new g("random() called without a PRNG in context");let r=e.evaluate(n[0]),s=e.evaluate(n[1]);if(typeof r!="number"||typeof s!="number")throw new g("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new g("random(a, b) arguments must be integers");let i=Math.min(r,s),o=Math.max(r,s);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:ye("check_flags"),eval(n,e){let t=Se(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new g("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:ye("set_flags"),eval(n,e){let t=[...Se(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new g("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let i=t.indexOf(s.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:$("visits"),eval:(n,e)=>B(e).visits?.(q(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:$("seen"),eval:(n,e)=>(B(e).visits?.(q(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:$("patter_visits"),eval:(n,e)=>B(e).patterVisits?.(q(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:$("patter_seen"),eval:(n,e)=>(B(e).patterVisits?.(q(n,e,"patter_seen"))??0)>0}}},Pt=me.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function ve(n,e){let t=n.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function q(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new g(`${t}(id) requires a string node id`);return r}function $(n){return(e,t)=>{let r=e[0];r&&r.kind!=="string"&&t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(id): the argument must be a string id literal (a scene / block / node id)`})}}function Se(n,e,t){if(!n)throw new g(`${t}() requires at least one argument (the flags variable)`);let r=e.evaluate(n);if(Array.isArray(r))return r;if(r===!1||r===null||r===void 0)return[];throw new g(`${t}() first argument must be a flags property`)}function ye(n){return(e,t)=>{if(e.length===0)return;let r=e[0];if(r.kind!=="scopedvar"){t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): first argument must be a flags property reference (@name or @scope.name)`});return}let s=t.schema.properties.get(r.scope)?.get(r.name);if(s&&s.type!=="flags"){let i=r.scope===t.defaultScope?r.name:`${r.scope}.${r.name}`;t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): \'@${i}\' is not a flags property (got ${s.type})`});return}for(let i=1;i<e.length;i++){let o=e[i];o.kind!=="flagdelta"?t.report({path:[...t.path,"args",i],kind:"wrong-arg-type",severity:"error",message:`${n}(): argument ${i+1} must be +flagName or -flagName`}):s?.type==="flags"&&s.enumValues&&!s.enumValues.includes(o.name)&&t.report({path:[...t.path,"args",i],kind:"unknown-flag-name",severity:"error",message:`${n}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var Ze=/^@[A-Za-z0-9_.]+$/;function*et(n){let e="",t=0;for(;t<n.length;){let r=n[t];if(r==="{"&&n[t+1]==="{"){e+="{",t+=2;continue}if(r==="}"&&n[t+1]==="}"){e+="}",t+=2;continue}if(r==="{"){let s=n.indexOf("}",t+1);if(s!==-1){let i=n.slice(t,s+1),o=n.slice(t+1,s).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:Ze.test(o)?o:void 0},t=s+1;continue}e+=i,t=s+1;continue}}e+=r,t+=1}e&&(yield{kind:"text",value:e})}function tt(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function rt(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function nt(n){let e="",t=!1;for(let r of n){if(rt(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function we(n,e,t){if(e.length===0||n.indexOf(e)<0)return n;let r="",s=0,i=!1;for(;s<n.length;){if(n.startsWith(e,s)){let o=n.indexOf(t,s+e.length);if(o>=0){s=o+t.length,i=!0;continue}r+=n.slice(s);break}r+=n[s],s+=1}return i?nt(r):n}function be(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of et(n)){if(r.kind==="text"){t+=r.value;continue}if(!r.ref){t+=r.raw;continue}let s=e(r.ref);t+=s===void 0?"":tt(s)}return t}function I(n,e){for(let t of n){e(t);let r=t.children;r&&I(r,e)}}function st(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function S(n){let e=n.gameId?.trim();return e||st(n.name)}function Y(n){return`cast:${n}`}function X(n){return`qualifier:${n}`}var Z={open:"[",close:"]"},xe="SFX";var ee=3;function N(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function H(n){let e=new Map,t=(r,s)=>{let i=N([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group"&&r.prompt&&e.set(r.prompt.id,N([...i,...r.prompt.tags??[]])),r.type==="group")for(let o of r.children)t(o,i);else for(let o of r.beats??[])e.set(o.id,N([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=N(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=N([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var ke=new WeakMap,Ee=new WeakMap;function it(n){let e=Ee.get(n);return e||(e=H(n),Ee.set(n,e)),e}function C(n,e){return n.tagIndex.get(e)?.slice()}var A="Patter",F=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),Ce=Symbol("ownsRegistry"),y={stage:n=>`patter/scene/${F(n)}`,flow:n=>`patter/flow/${F(n)}/`,flowGlobals:n=>`patter/flow/${F(n)}/patter`,flowScene:(n,e)=>`patter/flow/${F(n)}/scene/${F(e)}`},T=class{constructor(e){this.journal=e}journal},_=class n{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;engineLog=[];engineSeq=0;engineTraceHandlers=new Set;constructor(e,t={}){this.creationOptions=t;let r=t.locale??e.locales.default,s=e.strings;this.allStrings=s,this.currentLocale=r;let i=s[r]??{},o=s[e.locales.default]??{},a=e.localisation,c=a?.mode==="ids"&&!a.sourceDebug;this.sourceDebug=a?.mode==="ids"&&!!a.sourceDebug,this.sourceDebug&&typeof console<"u"&&console.warn("[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.");let l=new Map;for(let d of e.cast??[])d.displayName&&l.set(d.name,d.displayName);let p=new Map;for(let d of e.qualifiers??[])d?.gameId&&p.set(d.gameId,d.name);this.defaultSeed=(t.seed??2654435769)>>>0;let f=new Map,u=new Map,oe=new Map;for(let[d,h]of Object.entries(e.scenes)){this.sceneGameIdToId.set(S(h),d);let v=new Map;for(let m of h.blocks)u.set(m.id,{sceneId:d}),oe.set(m.id,m),v.set(S(m),m.id),I(m.children,j=>f.set(j.id,j));this.blockGameIdToId.set(d,v)}let ae=e.properties??[],U=ae.filter(d=>d.shared??!0).map(Oe),Le=ae.filter(d=>!(d.shared??!0)).map(Oe),Me=new Set(U.map(d=>d.name.toLowerCase())),x=t.registry??new M,ce=!t.registry||t[Ce]===!0,le=new b(U,{pathPrefix:"@patter."}),pe=[],G=[];try{x.mountOwned("patter",le,{owner:A}),G.push("patter");let d=new Set;for(let[h,v]of Object.entries(Pe(t))){let m=e.scopeRegistry?.scopes.find(qe=>qe.token===h),j=(m?.declarations??[]).map(Ae);x.defineForeign(h,v,j,{writable:m?.writable??!0,owner:A}),G.push(h),d.add(h)}for(let h of ce?e.scopeRegistry?.scopes??[]:[]){if(d.has(h.token)||x.has(h.token))continue;let v=(h.declarations??[]).map(m=>at(m,h.writable));x.defineOwned(h.token,v,{owner:A}),G.push(h.token),pe.push(h.token)}}catch(d){for(let h of G)x.remove(h,{keep:!0});throw d}let de=new Map;for(let[d,h]of Object.entries(e.scenes)){let v=new Set((h.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));de.set(d,v)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(d,h,v)=>this.emitEngine(d,h,v),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:p,nodeIndex:f,blockIndex:u,blockById:oe,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:it(e),registry:x,ownsRegistry:ce,patterBag:le,hostScopes:pe,patterSharedDecls:U,patterLocalDecls:Le,patterSharedNames:Me,sceneSharedNames:de,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(d=>console.warn(`Patterplay: ${d.kind} on ${d.node} in flow \'${d.flow}\' failed, played through: ${d.message}`)),replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??Z).open,captionClose:(e.closedCaptions??Z).close,captionCharacter:e.closedCaptions?.character||xe,refSplitCache:new Map,refSplitRevision:x.revision}}get locale(){return this.currentLocale}get isSourceDebug(){return this.sourceDebug}get buildId(){return this.host.bundle.content?.hash??""}setLocale(e){this.currentLocale=e,this.host.strings=this.allStrings[e]??{}}replaceStrings(e){this.allStrings=e.strings,this.host.strings=this.allStrings[this.currentLocale]??{},this.host.defaultStrings=this.allStrings[this.host.bundle.locales.default]??{}}hotSwap(e){this.refuseInCheckpoint("hotSwap");let t=this.saveGame(),r=o=>(o.setLocale(this.currentLocale),o.setClosedCaptions(this.host.captionsOn),o),s=()=>new n(e,{...this.creationOptions,registry:this.host.registry,[Ce]:this.host.ownsRegistry});this.release(!0);let i=s();try{return i.loadGame(t),r(i)}catch{i.release(!0);let o=s();for(let[a,c]of Object.entries(t.flows)){let l=c.cursor.currentSceneId;try{o.openFlow(a,l!==null?{scene:l}:{})}catch{}}return r(o)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:r,blockId:s}=this.resolveOpenAddress(t.scene,t.block),i=this.flowsById.get(e),o=this.host.journal;if(o&&i&&!i.isClosed)throw new Error(`openFlow would replace the open flow \'${e}\', which can\'t be undone while a checkpoint is open`);i?.close();let a=new D(e,this.host,t.seed??this.defaultSeed);return o&&(o.flows.add(a),o.opened.add(a),o.undo.push(()=>{a.close(),i?this.flowsById.set(e,i):this.flowsById.delete(e)})),this.flowsById.set(e,a),a.begin(r,s),a}runFlow(e,t,r){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:r}).advanceToStop().played;if(!s.goto(t,r))throw new Error(`runFlow: address not found: ${t}${r===void 0?"":` / ${r}`}`);return s.advanceToStop().played}assertExternalScopes(){for(let e of this.host.bundle.externalScopes??[])if(!this.host.registry.has(e))throw new Error(`this content names @${e}, which no engine on this registry registered: give every engine the game\'s one registry`)}resolveOpenAddress(e,t){if(e!==void 0){let r=re(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=ne(this.host,r,t);if(s===void 0)throw new Error(`unknown block: ${t}`);return{sceneId:r,blockId:s}}if(t!==void 0){if(!this.host.blockIndex.has(t))throw new Error(`unknown block: ${t}`);return{blockId:t}}if(Object.keys(this.host.bundle.scenes).length===0)throw new Error("no scenes in bundle");return{}}resolveSceneRef(e){if(e!=null)return re(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:ne(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?S(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?S(t):void 0}tagsForBeat(e){return C(this.host,e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?C(this.host,t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?C(this.host,s):void 0)??[]}gameDataForScene(e){let t=this.resolveSceneRef(e);return{...(t!=null?this.host.bundle.scenes[t]:void 0)?.gameData}}gameDataForBlock(e,t){let r=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(r!=null?this.host.blockById.get(r):void 0)?.gameData}}getCast(){let e=[];for(let t of this.host.bundle.cast??[])t?.name&&e.push(t.name);return e}castForScene(e){let t=this.resolveSceneRef(e),r=t!=null?this.host.bundle.scenes[t]:void 0;if(!r)return[];let s=new Set;for(let i of r.blocks)Ie(i.children,s);return[...s]}castForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t),i=s!=null?this.host.blockById.get(s):void 0;if(!i)return[];let o=new Set;return Ie(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...S(e)?{gameId:S(e)}:{},name:e.name,...De(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...S(t)?{gameId:S(t)}:{},name:t.name,...De(t.gameData),...this.tagsField(t.id),children:t.children.map(r=>this.outlineNode(r))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let r of t.blocks)I(r.children,s=>{if(s.type==="snippet")for(let i of s.beats??[])e.push({sceneId:t.id,blockId:r.id,snippetId:s.id,beat:this.beatInfo(i)})});return e}outlineNode(e){return e.type==="group"?{type:"group",id:e.id,...this.tagsField(e.id),...e.selector?{selector:e.selector}:{},...e.prompt?{prompt:this.beatInfo(e.prompt)}:{},children:e.children.map(t=>this.outlineNode(t))}:{type:"snippet",id:e.id,...this.tagsField(e.id),beats:(e.beats??[]).map(t=>this.beatInfo(t)),...e.jump?{jumpTo:e.jump.to,...e.jump.mode?{jumpMode:e.jump.mode}:{}}:{}}}beatInfo(e){let t=C(this.host,e.id),r={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){r.character=e.character;let s=this.host.defaultStrings[Y(e.character)]??this.host.castDisplay.get(e.character);s!==void 0&&(r.characterName=s)}if(e.direction!==void 0&&(r.direction=e.direction),e.qualifier!==void 0){r.qualifier=e.qualifier;let s=this.host.defaultStrings[X(e.qualifier)]??this.host.qualifierDisplay.get(e.qualifier);s!==void 0&&(r.qualifierName=s)}}if(e.kind==="line"||e.kind==="text"){let s=this.host.defaultStrings[e.id];s!==void 0&&(r.text=s)}return e.gameData&&Object.keys(e.gameData).length&&(r.gameData=e.gameData),t&&t.length&&(r.tags=t),r}tagsField(e){let t=C(this.host,e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}listBags(){let e=[{bag:this.host.patterBag}];for(let[t,r]of this.host.stageBags)e.push({bag:r,pathPrefix:`@scene:${t}.`});return e}checkpoint(){if(this.host.journal)throw new Error("a checkpoint is already open: roll it back or commit it first");let e={undo:[],flows:new Set,opened:new Set,selectors:new Set,flowSelectors:new Map};return this.host.journal=e,new T(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let r=t.length-1;r>=0;r--)t[r]();n.forget(e.journal)}commit(e){this.endCheckpoint(e),n.forget(e.journal)}get inCheckpoint(){return this.host.journal!==null}endCheckpoint(e){if(this.host.journal!==e.journal)throw new Error("that checkpoint is not the open one");this.host.journal=null}static forget(e){e.undo=[],e.flows.clear(),e.opened.clear(),e.selectors.clear(),e.flowSelectors.clear()}refuseInCheckpoint(e){if(this.host.journal)throw new Error(`${e} can\'t be undone, so it isn\'t allowed while a checkpoint is open`)}closeFlow(e){this.refuseInCheckpoint("closeFlow"),this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){this.refuseInCheckpoint("reset");for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.patterBag.reseed(this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear();for(let e of this.host.stageBags.keys())this.host.registry.remove(y.stage(e));this.host.stageBags.clear(),this.host.registry.discardParked("patter/")}release(e){for(let r of this.flowsById.values())r.releaseBags(e),r.close();this.flowsById.clear();let t=this.host.registry;for(let r of this.host.stageBags.keys())t.remove(y.stage(r),{keep:e});this.host.stageBags.clear();for(let r of["patter",...this.host.hostScopes])t.has(r)&&t.remove(r,{keep:e});for(let r of Object.keys(Pe(this.creationOptions)))t.has(r)&&t.remove(r)}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.registry.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e),i=this.host.journal?this.host.registry.get(r,s):void 0;this.host.registry.set(r,s,t,{host:!0}),this.host.journal&&i!==void 0&&this.host.journal.undo.push(()=>this.host.registry.set(r,s,i,{host:!0}))}log(){return this.engineLog}clearLog(){this.engineLog.length=0}onTrace(e){return this.engineTraceHandlers.add(e),this.host.tracing=!0,()=>{this.engineTraceHandlers.delete(e),this.host.tracing=this.host.logEnabled||this.engineTraceHandlers.size>0}}emitEngine(e,t,r){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineSeq++,...r?{scene:r}:{}})}getVisitCounts(){return Object.fromEntries(this.host.sharedVisits)}listProperties(){return this.host.patterSharedDecls.map(e=>({name:e.name,path:`@patter.${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:P(e),writable:e.writable??!0}))}splitShared(e){let t=Te(this.host,e);if(t.scope==="scene")throw new Error(`\'${e}\': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);return t}save(){return{patter:this.host.patterBag.save()}}load(e){e.patter&&this.host.patterBag.load(e.patter)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:ee,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:Ne(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==ee)throw new Error(`unsupported save version: ${String(t)}`);let r=e.flows;if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("malformed save: no flows");this.assertExternalScopes();let s=this.host.registry;for(let[o,a]of this.flowsById)a.releaseBags(o in e.flows),a.close();this.flowsById.clear();for(let o of this.host.stageBags.keys())s.remove(y.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.version===2?ct(e):e.registry;i&&(this.host.ownsRegistry?s.load(i):s.load(i,{keepParked:!0})),this.host.sharedVisits.clear();for(let[o,a]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(o,a);this.host.sharedSelectors.clear();for(let[o,a]of Fe(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new D(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}}},D=class{id;host;local;registered=new Set;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPrompt=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.patterBag.get(e):this.local.get(e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.registry.set("patter",e,t):this.local.set(e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;if(t!==null)return this.sceneBagFor(t,e)?.get(e)},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.sceneBagFor(r,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,r){this.id=e,this.host=t,this.rngState=O(r),this.local=this.newLocal(),this.evalCtx={scopes:{},host:{nextRandom:this.rng,visits:s=>this.visitCounts.get(s)??0,patterVisits:s=>this.host.sharedVisits.get(s)??0},qualities:(s,i)=>this.stagesFor(s,i)}}context(){let e=this.host.registry;if(e.revision!==this.ctxRevision){let t=e.toEvalContext(),r=this.evalCtx.scopes;for(let s of Object.keys(r))delete r[s];Object.assign(r,t.scopes),r.patter=this.patterResolver,r.scene=this.sceneResolver,this.registryQualities=t.qualities,this.ctxRevision=e.revision}return this.evalCtx}stagesFor(e,t){let r=t.toLowerCase();if(e==="patter")return te(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(r);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:te(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(r)}return this.registryQualities?.(e,t)??te(this.host,`host/${e}`,()=>this.host.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(r)}begin(e,t){if(this.host.journal&&!this.host.journal.opened.has(this))throw new Error("a flow can\'t be started or reset while a checkpoint is open");if(this.releaseBags(!1),this.host.registry.discardParked(y.flow(this.id)),this.mountLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.activeSnippet=null,this.beatIndex=0,this.clearPending(),this.started=!0,t){let r=this.host.blockIndex.get(t);if(!r)throw new Error(`unknown block: ${t}`);this.enterSceneSetup(r.sceneId),this.stack=[{sceneId:r.sceneId,containerId:t,index:0}],this.enter(t)}else{let r=e??Object.keys(this.host.bundle.scenes)[0],s=r?this.host.bundle.scenes[r]:void 0;if(!s)throw new Error(r?`unknown scene: ${r}`:"no scenes in bundle");this.enterSceneSetup(r);let i=s.blocks[0];i&&(this.stack=[{sceneId:r,containerId:i.id,index:0}],this.enter(i.id))}this.settle()}reset(e,t){this.begin(e,t)}start(e,t){this.reset(e,t)}goto(e,t){if(this.closed)return!1;if(this.touch(),e==="END")return this.started=!0,this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let r=re(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=ne(this.host,r,t),s===void 0)?!1:this.started?(this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??r,"jump"),this.settle(),!0):(this.begin(r,s),!0)}close(){this.releaseBags(!1),this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.clearPending()}clearPending(){this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null}touch(){let e=this.host.journal;if(!e||e.flows.has(this))return;e.flows.add(this);let{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p}=this,f=this.stack.map(u=>({...u}));e.undo.push(()=>{Object.assign(this,{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p,stack:f})})}get isClosed(){return this.closed}get currentScene(){return this.currentSceneId}advance(){if(this.closed)return{type:"end"};if(!this.started)throw new Error("flow has not been started");if(this.touch(),this.pendingPrompt){let e=this.pendingPrompt;return this.pendingPrompt=null,this.pendingPromptOwnerId=null,e.shown?this.promptResult(e.beat,e.shown):this.beatResult(e.beat)}return this.settle(),this.flowEnded?{type:"end"}:this.pendingChoice?{type:"choice",groupId:this.pendingChoice.groupId,options:this.pendingChoice.options}:this.activeSnippet?this.beatResult(this.activeSnippet.beats[this.beatIndex++]):(this.flowEnded=!0,{type:"end"})}advanceToStop(){let e=[];for(;;){let t=this.advance();if(t.type==="choice"||t.type==="end")return{played:e,stop:t};e.push(t)}}settle(){let e=0;for(;;){if(++e>1e4)throw new Error("flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content");if(this.flowEnded||this.pendingChoice)return;if(this.activeSnippet){if(this.beatIndex<(this.activeSnippet.beats?.length??0))return;this.runEffects(this.activeSnippet.onExit,this.activeSnippet.id);let i=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(i);continue}let t=this.stack[this.stack.length-1];if(!t){this.flowEnded=!0;return}t.sceneId!==this.currentSceneId&&(this.currentSceneId=t.sceneId);let r=this.childrenOf(t.containerId);if(!r){this.stack.pop();continue}let s=t.index;for(;t.index<r.length&&!this.eligible(r[t.index]);)t.index++;if(this.host.tracing&&t.index!==s&&this.emit({type:"select",group:t.containerId,selector:"run",children:r.slice(s,t.index+1).map((i,o)=>({id:i.id,eligible:s+o===t.index})),picked:r[t.index]?.id??null}),t.index>=r.length){this.stack.pop();continue}this.enterChild(r[t.index++])}}listBags(){let e=[{bag:this.local,pathPrefix:`${this.id}/@patter.`}];for(let[t,r]of this.sceneBags)e.push({bag:r,pathPrefix:`${this.id}/@scene:${t}.`});return e}log(){return this.flowLog}clearLog(){this.flowLog.length=0}emit(e){let t=this.currentSceneId??void 0;this.host.emitEngine(this.id,e,t),this.host.logEnabled&&this.flowLog.push({...e,seq:this.flowSeq++,...t?{scene:t}:{}})}getVisitCounts(){return Object.fromEntries(this.visitCounts)}getChoices(){return this.pendingChoice?.options??[]}choose(e){let t=this.pendingChoice;if(!t)throw new Error("no choice is pending");let r=t.options.find(o=>o.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);this.touch();let s=t.byId.get(e);this.host.tracing&&this.emit({type:"chose",group:t.groupId,option:e}),this.pendingChoice=null;let i=this.host.replayPromptOnChoose?this.authoredPromptOf(s):void 0;this.pendingPrompt=i&&r.prompt?{beat:i,shown:{...r.prompt}}:null,this.pendingPromptOwnerId=this.pendingPrompt?s.id:null,this.enterChild(s)}isEnded(){return this.flowEnded}getProperty(e){let{scope:t,name:r}=this.splitRef(e);return t==="patter"?this.patterResolver.get(r):t==="scene"?this.sceneResolver.get(r):this.host.registry.get(t,r)}setProperty(e,t){this.writeProperty(e,t,!0)}writeProperty(e,t,r){let{scope:s,name:i}=this.splitRef(e),o=this.host.journal;if(s==="patter"){if(o){let a=this.host.patterSharedNames.has(i),c=a?this.host.patterBag.get(i):this.local.get(i),l=this.local;c!==void 0&&o.undo.push(a?()=>this.host.registry.set("patter",i,c):()=>l.set(i,c))}this.patterResolver.set(i,t)}else if(s==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);if(o){let a=this.sceneBagFor(this.currentSceneId,i),c=a?.get(i);a&&c!==void 0&&o.undo.push(()=>a.set(i,c))}this.sceneResolver.set(i,t)}else{let a=o?this.host.registry.get(s,i):void 0;this.host.registry.set(s,i,t,r?{host:!0}:void 0),o&&a!==void 0&&o.undo.push(()=>this.host.registry.set(s,i,a,{host:!0}))}}snapshot(){return{rngState:this.rngState,visits:Object.fromEntries(this.visitCounts),cursor:{flowEnded:this.flowEnded,currentSceneId:this.currentSceneId,stack:this.stack.map(e=>{let t=this.childrenOf(e.containerId)?.[e.index];return t?{...e,nextId:t.id}:{...e}}),activeSnippetId:this.activeSnippet?.id??null,beatIndex:this.beatIndex,pendingChoice:this.pendingChoice?{groupId:this.pendingChoice.groupId,options:this.pendingChoice.options.map(e=>({...e}))}:null,pendingPromptOwnerId:this.pendingPromptOwnerId,...this.pendingPrompt?.shown?{pendingPrompt:ot(this.pendingPrompt.shown)}:{},selectors:Ne(this.selectors)}}}restore(e){if(this.host.journal)throw new Error("a flow can\'t be restored while a checkpoint is open");this.restoreCursor(e),this.releaseBags(!0),this.mountLocal();for(let t of new Set([this.currentSceneId,...this.stack.map(r=>r.sceneId)]))t!==null&&this.host.bundle.scenes[t]&&this.ensureSceneBags(t)}restoreCursor(e){this.rngState=O(e.rngState),this.visitCounts=new Map(Object.entries(e.visits??{}));let t=e.cursor;if(this.started=!0,this.flowEnded=t.flowEnded,this.beatIndex=t.beatIndex,this.currentSceneId=t.currentSceneId,this.stack=t.stack.map(r=>{let{nextId:s,...i}=r;if(s!==void 0){let o=this.childrenOf(i.containerId)?.findIndex(a=>a.id===s)??-1;if(o>=0)return{...i,index:o}}return{...i}}),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=Fe(t.selectors),this.pendingChoice=null,t.pendingChoice!==null){let r=new Map,s=[];for(let i of t.pendingChoice.options){let o=this.host.nodeIndex.get(i.id);o&&(r.set(i.id,o),s.push({...i}))}s.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:s,byId:r})}if(this.pendingPrompt=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId),s=r?this.authoredPromptOf(r):void 0;s?this.pendingPrompt={beat:s,...t.pendingPrompt?{shown:{...t.pendingPrompt}}:{}}:this.pendingPromptOwnerId=null}}enterSceneSetup(e){let t=this.host.bundle.scenes[e];if(!t)throw new Error(`unknown scene: ${e}`);this.currentSceneId=e,this.enter(e),this.seedScene(t),this.runEffects(t.onEntry,t.id)}enterChild(e){if(this.enter(e.id),e.type==="snippet"){this.beginSnippet(e);return}let t=e.selector??"run";if(t==="run"){this.stack.push({sceneId:this.currentSceneId,containerId:e.id,index:0});return}if(t==="choice"){this.setupChoice(e);return}let r=this.selectChild(e);r&&this.enterChild(r)}childrenOf(e){let t=this.host.blockById.get(e);if(t)return t.children;let r=this.host.nodeIndex.get(e);if(r&&r.type==="group")return r.children}beginSnippet(e){this.runEffects(e.onEnter,e.id),this.activeSnippet=e,this.beatIndex=0}setupChoice(e){let t=[],r=new Map,s=[];for(let o of e.children){if(o.fallback===!0){s.push(o);continue}if(o.sticky!==!0&&(this.visitCounts.get(o.id)??0)>=1)continue;let a=this.eligible(o),c=o.secretUntilEligible===!0;!a&&c||(t.push({id:o.id,prompt:this.promptFor(o),eligible:a,gameData:o.gameData}),r.set(o.id,o))}if(t.some(o=>o.eligible)){this.host.tracing&&this.emit({type:"choice",group:e.id,options:t.map(o=>({id:o.id,eligible:o.eligible}))}),this.pendingChoice={groupId:e.id,options:t,byId:r};return}let i=s.find(o=>this.eligible(o));if(i){this.enterChild(i);return}this.host.tracing&&this.emit({type:"dry",group:e.id}),this.host.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(this.host.tracing&&this.emit({type:"jump",to:e,mode:t}),e==="END"){this.flowEnded=!0,this.stack=[];return}let r,s,i=this.host.bundle.scenes[e];if(i){this.enterSceneSetup(e);let a=i.blocks[0];if(!a){t==="jump"&&(this.stack=[]);return}r=e,s=a.id}else{let a=this.host.blockIndex.get(e);if(!a)throw new Error(`jump target not found: ${e}`);a.sceneId!==this.currentSceneId&&this.enterSceneSetup(a.sceneId),r=a.sceneId,s=e}this.enter(s);let o={sceneId:r,containerId:s,index:0};t==="call"?this.stack.push(o):this.stack=[o]}selectChild(e){let t=e.children.map(c=>({id:c.id,eligible:this.eligible(c)})),r=e.children.filter((c,l)=>t[l].eligible),s=e.options?.order??"sequential",i=e.options?.exhaust??"once",o=c=>(this.host.tracing&&this.emit({type:"select",group:e.id,selector:e.selector??"default",...e.selector==="sequence"?{order:s,exhaust:i}:{},children:t,picked:c?.id??null}),c);if(r.length===0)return o(null);let a=this.selectorState(e);switch(e.selector){case"branch":return o(r[0]);case"sequence":return o(s==="shuffle"?this.pickShuffle(r,i,a):s==="specificity"?this.pickSpecificity(r,i,a):this.pickSequential(r,i,a));default:return null}}pickSequential(e,t,r){let s=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,r){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(u=>u.id),a=new Set(e.map(u=>u.id));if(r.bag===void 0&&(r.bag=o()),!r.bag.some(u=>a.has(u))){if(t==="once")return null;if(i){let u=e[s-1];return r.last=u.id,u}if(r.bag=o(),r.bag.length===0)return null}let c=r.bag.filter(u=>a.has(u)),l=r.last!==void 0&&c.length>1?c.indexOf(r.last):-1,p=Math.floor(this.rng()*(l>=0?c.length-1:c.length));l>=0&&p>=l&&p++;let f=c[p];return r.bag.splice(r.bag.indexOf(f),1),r.last=f,e.find(u=>u.id===f)}pickSpecificity(e,t,r){let s=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(p=>p.id));let l=new Set(r.bag);if(s=e.filter(p=>l.has(p.id)),s.length===0)return t==="stick"&&r.last!==void 0?e.find(p=>p.id===r.last)??null:null}let i=-1,a=s.map(l=>{let p=this.specScore(l);return p>i&&(i=p),{c:l,s:p}}).filter(l=>l.s===i).map(l=>l.c),c;if(a.length===1)c=a[0];else{let l=r.last!==void 0?a.findIndex(f=>f.id===r.last):-1,p=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&p>=l&&p++,c=a[p]}return t!=="repeat"&&(r.bag=r.bag.filter(l=>l!==c.id)),r.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0,e):0}matchedSpec(e,t,r){return ue(e,i=>{try{return Be(V(i,this.context(),z))}catch(o){return this.reportError("best-match",r.id,r.condition,o),!1}},{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=this.host.journal,s;if(r&&(s=e.shared?r.selectors:r.flowSelectors.get(this),s||(s=new Set,r.flowSelectors.set(this,s))),r&&s&&!s.has(e.id)){s.add(e.id);let o=t.get(e.id),a=o?{...o,...o.bag?{bag:[...o.bag]}:{}}:void 0;r.undo.push(()=>{a?t.set(e.id,a):t.delete(e.id)})}let i=t.get(e.id);return i||(i={},t.set(e.id,i)),i}runEffects(e,t){for(let r of e??[]){let s;try{s=this.evalExpr(r.value);let i=this.host.tracing?this.getProperty(r.target):void 0;this.writeProperty(r.target,s,!1),this.host.tracing&&this.emit({type:"write",target:r.target,value:s,...i!==void 0?{prev:i}:{}})}catch(i){this.reportError("effect",t,r.value,i)}}}eligible(e){if(!e.condition)return!0;try{return Be(this.evalExpr(e.condition))}catch(t){return this.reportError("condition",e.id,e.condition,t),!1}}reportError(e,t,r,s){let i=s instanceof Error?s.message:String(s),o=r?.src;this.host.onError({flow:this.id,kind:e,node:t,...o?{source:o}:{},message:i}),this.host.tracing&&this.emit({type:"diagnostic",kind:e,node:t,...o?{source:o}:{},message:i})}evalExpr(e){return V(this.conditionAst(e),this.context(),z)}conditionAst(e){let t=ke.get(e);return t||(t=k(e.ast),ke.set(e,t)),t}enter(e){let t=this.visitCounts,r=t.get(e);t.set(e,(r??0)+1);let s=this.host.sharedVisits,i=s.get(e);s.set(e,(i??0)+1),this.host.journal&&this.host.journal.undo.push(()=>{r===void 0?t.delete(e):t.set(e,r),i===void 0?s.delete(e):s.set(e,i)})}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=Q(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=C(this.host,e.id),r=t&&t.length?{tags:t}:{};switch(e.kind){case"gameEvent":return{type:"gameEvent",id:e.id,gameData:e.gameData,...r};case"text":return{type:"text",id:e.id,text:this.interpolate(this.resolveString(e.id)),gameData:e.gameData,...r};case"line":{let s=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?s:this.interpolate(s)),c=i&&a.length===0;return{type:"line",id:e.id,text:a,character:c?void 0:e.character,characterName:c?void 0:this.resolveCharacterName(e.character),direction:c?void 0:e.direction,qualifier:c?void 0:e.qualifier,qualifierName:c?void 0:this.resolveQualifierName(e.qualifier),gameData:e.gameData,...r}}}}interpolate(e){return be(e,t=>this.getProperty(t))}stripCaptions(e){return we(e,this.host.captionOpen,this.host.captionClose)}captionLine(e){return this.host.captionsOn?e:this.stripCaptions(e)}promptFor(e){let t=this.promptBeatOf(e);if(!t)return;let r=this.interpolate(this.resolveString(t.id));return t.kind==="line"?{kind:"line",text:this.captionLine(r),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction,qualifier:t.qualifier,qualifierName:this.resolveQualifierName(t.qualifier)}:{kind:"text",text:r}}authoredPromptOf(e){return e.type==="group"?e.prompt:void 0}promptResult(e,t){let r=C(this.host,e.id),s=r&&r.length?{tags:r}:{};return t.kind==="text"?{type:"text",id:e.id,text:t.text,gameData:e.gameData,...s}:{type:"line",id:e.id,text:t.text,character:t.character,characterName:t.characterName,direction:t.direction,qualifier:t.qualifier,qualifierName:t.qualifierName,gameData:e.gameData,...s}}promptBeatOf(e){return e.type==="group"&&e.prompt?e.prompt:((e.type==="snippet"?e:this.firstTextSnippetIn(e.children))?.beats??[]).find(r=>r.kind==="line"||r.kind==="text")}firstTextSnippetIn(e){let t;return I(e,r=>{!t&&r.type==="snippet"&&(r.beats??[]).some(s=>s.kind==="line"||s.kind==="text")&&(t=r)}),t}resolveString(e){if(this.host.emitIds)return e;let t=this.host.strings[e];if(t!==void 0)return t;let r=this.host.defaultStrings[e];return r!==void 0?`<Untranslated: ${e}> ${r}`:e}resolveCharacterName(e){if(e===void 0||this.host.emitIds)return;let t=Y(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}resolveQualifierName(e){if(e===void 0||this.host.emitIds)return;let t=X(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.qualifierDisplay.get(e)}splitRef(e){return Te(this.host,e)}newLocal(){return new b(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=y.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:A}),this.registered.add(e)}releaseBags(e){for(let t of this.registered)this.host.registry.remove(t,{keep:e});this.registered.clear(),this.sceneBags.clear()}sceneBagFor(e,t){if(this.host.bundle.scenes[e])return this.ensureSceneBags(e),this.host.sceneSharedNames.get(e)?.has(t.toLowerCase())?this.host.stageBags.get(e):this.sceneBags.get(e)}ensureSceneBags(e){let t=this.host.sceneSharedNames.get(e)??new Set,r=this.host.bundle.scenes[e]?.sceneProps??[],s=this.host.journal;if(!this.sceneBags.has(e)){let i=new b(r.filter(c=>!t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),o=y.flowScene(this.id,e),a=Re(this.host.registry,o,i,s!==null);this.registered.add(o),this.sceneBags.set(e,i),s&&s.undo.push(()=>{this.sceneBags.get(e)===i&&(this.host.registry.remove(o,a?{keep:!0}:void 0),this.registered.delete(o),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let i=new b(r.filter(a=>t.has(a.name.toLowerCase())),{pathPrefix:"@scene."}),o=Re(this.host.registry,y.stage(e),i,s!==null);this.host.stageBags.set(e,i),s&&s.undo.push(()=>{this.host.stageBags.get(e)===i&&(this.host.registry.remove(y.stage(e),o?{keep:!0}:void 0),this.host.stageBags.delete(e))})}}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;this.ensureSceneBags(e.id);for(let r of e.sceneProps??[]){if(!r.temporary)continue;let s=r.name.toLowerCase(),i=t.has(s)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);if(!i)continue;let o=this.host.journal?i.get(s):void 0;o!==void 0&&this.host.journal.undo.push(()=>i.set(s,o)),i.set(s,P(r))}}};function Ie(n,e){I(n,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let r of t.beats??[])r.kind==="line"&&r.character&&e.add(r.character)})}function De(n){return n&&Object.keys(n).length?{gameData:n}:{}}function ot(n){let e={kind:n.kind,text:n.text};return n.character!==void 0&&(e.character=n.character),n.characterName!==void 0&&(e.characterName=n.characterName),n.direction!==void 0&&(e.direction=n.direction),n.qualifier!==void 0&&(e.qualifier=n.qualifier),n.qualifierName!==void 0&&(e.qualifierName=n.qualifierName),e}function Ne(n){let e={};for(let[t,r]of n){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e[t]=s}return e}function Fe(n){let e=new Map;for(let[t,r]of Object.entries(n??{})){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e.set(t,s)}return e}function Re(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:A}),r&&JSON.stringify(t.save())!==s}function Oe(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Pe(n){return{...n.world?{world:n.world}:{},...n.hostScopes}}function te(n,e,t){let r=n.qualityLadders.get(e);if(!r){r=new Map;for(let s of t()??[]){let i=s.name.toLowerCase();s.type==="quality"&&s.stages&&!r.has(i)&&r.set(i,s.stages)}n.qualityLadders.set(e,r)}return r}function Ae(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function at(n,e){let t=Ae(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function re(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function ne(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function Te(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=ve(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function ct(n){let e={};n.shared?.patter&&(e.patter=n.shared.patter);for(let[t,r]of Object.entries(n.stageBags??{}))e[y.stage(t)]=r;for(let[t,r]of Object.entries(n.flows??{})){r.scopes?.patter&&(e[y.flowGlobals(t)]=r.scopes.patter);for(let[s,i]of Object.entries(r.sceneBags??{}))e[y.flowScene(t,s)]=i}return e}function Be(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var lt=(n,e)=>n.shared??e;function se(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:lt(n,e)}}function pt(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function dt(n,e){e.blocks++;let t=[...n.children];for(;t.length;){let r=t.pop();if(r.type==="group"){e.groups++,r.prompt&&e.prompts++,t.push(...r.children);continue}e.snippets++;for(let s of r.beats??[])e.beats++,s.kind==="gameEvent"&&e.gameEvents++}}function Ge(n){let e={scenes:0,blocks:0,groups:0,snippets:0,beats:0,prompts:0,gameEvents:0,cast:n.cast?.length??0},t=[],r=[];for(let o of Object.values(n.scenes)){e.scenes++;let a=S(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:S(c),name:c.name}))});for(let c of o.blocks)dt(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>se(c,!1))})}let s=(n.scopeRegistry?.scopes??[]).map(o=>({token:o.token,writable:o.writable??!0,opaque:o.declarations===void 0,properties:(o.declarations??[]).map(a=>se(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(pt)}));return{identity:{schema:n.schema,project:n.content.project,...n.content.version!==void 0?{version:n.content.version}:{},...n.content.hash!==void 0?{hash:n.content.hash}:{},...n.content.structureHash!==void 0?{structureHash:n.content.structureHash}:{},voiced:n.voiced,defaultLocale:n.locales.default,locales:[...n.locales.included],localisation:n.localisation?.mode??"embedded",sourceDebug:n.localisation?.sourceDebug??!1},addresses:t,hostScopes:s,properties:{patter:(n.properties??[]).map(o=>se(o,!0)),scene:r},gameData:i,counts:e}}function je(n,e){return n.gameDataFields?.[e]??[]}function ie(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function Ve(n,e){let t={};for(let r of n){let s=ie(n,e,r.name);s!==void 0&&(t[r.name]=s)}for(let[r,s]of Object.entries(e??{}))r in t||(t[r]=s);return t}return Je(gt);})();\n';
201371
+ var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var 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
+ }
201280
201420
 
201281
201421
  // ../ops/src/export-html.ts
201282
201422
  var esc = (s) => s.replace(/[&<>]/g, (c2) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" })[c2]);
@@ -201296,37 +201436,80 @@ var PLAYER_JS = String.raw`
201296
201436
  function add(cls, html) { var d = document.createElement("div"); d.className = cls; d.innerHTML = html; stage.appendChild(d); return d; }
201297
201437
  function esc(s) { return String(s == null ? "" : s).replace(/[&<>]/g, function (c) { return { "&": "&amp;", "<": "&lt;", ">": "&gt;" }[c]; }); }
201298
201438
 
201299
- // Pacing: a source-only reader carries no audio, so each line is held on screen for a reading-length delay
201300
- // (proportional to its word count) before the next appears, and every line fades in (see CSS). The Speed
201301
- // control sets the reading pace (words/min); "Instant" drops the delay entirely. The choice is persisted.
201302
- // A run token cancels an in-flight paced read the moment the player restarts, loads, or picks a choice.
201303
- var SPEED_WPM = { slow: 300, normal: 500, fast: 800, instant: 0 }, runToken = 0;
201439
+ // Pacing (line padding): a source-only reader carries no audio, so each line is timed by the estimate
201440
+ // Patterstage uses for a line with no recording (game-subtitles' estimateDuration), and the step's padAfter
201441
+ // says when the next one starts: after a pause, at once, or cutting in before this one ends. The player
201442
+ // rules are play-timeline.ts's, inlined below by their source: the first line starts at once, as does the
201443
+ // reply to a choice; a cut-in never starts before the line it cuts into; a game event is never held here,
201444
+ // so it's done at once; the choices appear as the last line before them starts (while it plays); and the
201445
+ // end comes when every line still playing has finished. Every line fades in (see CSS).
201446
+ // The Speed control scales the whole timing (half, normal, or double speed) or drops it (instant), from
201447
+ // the moment it's changed: the clock keeps the time already played and re-times only what's to come. It
201448
+ // is stored under the key Patterpad's Play window uses, so the two keep one choice; the old reading paces
201449
+ // carry over (slow is half speed, fast is double).
201450
+ // A run token cancels an in-flight wait the moment the player restarts, loads, or picks a choice.
201451
+ var createTimeline = (${createTimeline.toString()});
201452
+ var createClock = (${createClock.toString()});
201453
+ var DEFAULT_PAD = ${DEFAULT_PAD_AFTER}; // the built-in pause, for a bundle from before padding
201454
+ var SPEED_RATE = { half: 0.5, normal: 1, double: 2, instant: Infinity }, OLD_SPEED = { slow: "half", fast: "double" }, runToken = 0;
201455
+ function own(o, k) { return Object.prototype.hasOwnProperty.call(o, k); }
201304
201456
  var speedKey = (function () { try { return localStorage.getItem("patter.playSpeed") || "normal"; } catch (e) { return "normal"; } })();
201305
- if (SPEED_WPM[speedKey] === undefined) speedKey = "normal";
201306
- function fakeDuration(text) {
201307
- var wpm = SPEED_WPM[speedKey];
201308
- if (!wpm) return 0; // "Instant" - reveal the next line immediately
201309
- var words = String(text == null ? "" : text).trim().split(/\s+/).filter(Boolean).length;
201310
- return Math.max(450, Math.round((words / wpm) * 60000)); // a beat needs to land
201457
+ if (own(OLD_SPEED, speedKey)) speedKey = OLD_SPEED[speedKey];
201458
+ if (!own(SPEED_RATE, speedKey)) speedKey = "normal";
201459
+ var estimate = window.GameSubtitles.estimateDuration;
201460
+ var clock = null, wake = null; // the run's clock, and a hook that re-times the wait in flight
201461
+ // Call then() when script time "at" comes on the run's clock. A change of speed wakes the wait, which
201462
+ // works out the time again; a superseded run's wait just stops.
201463
+ function waitUntil(at, token, then) {
201464
+ if (token !== runToken) return;
201465
+ var ms = clock.at(at) - Date.now();
201466
+ if (ms <= 0) { wake = null; then(); return; }
201467
+ var t = setTimeout(function () { waitUntil(at, token, then); }, ms);
201468
+ wake = function () { clearTimeout(t); waitUntil(at, token, then); };
201311
201469
  }
201312
201470
  function scrollDown() { window.scrollTo(0, document.body.scrollHeight); }
201313
201471
  var speedSel = document.getElementById("speed");
201314
201472
  if (speedSel) {
201315
- speedSel.value = speedKey; // a fresh fakeDuration() reads speedKey each line, so a change applies next line
201316
- speedSel.onchange = function () { speedKey = speedSel.value; try { localStorage.setItem("patter.playSpeed", speedKey); } catch (e) {} };
201473
+ speedSel.value = speedKey;
201474
+ speedSel.onchange = function () {
201475
+ speedKey = speedSel.value;
201476
+ try { localStorage.setItem("patter.playSpeed", speedKey); } catch (e) {}
201477
+ if (clock) clock.setRate(Date.now(), SPEED_RATE[speedKey]);
201478
+ if (wake) wake();
201479
+ };
201317
201480
  }
201318
201481
 
201319
201482
  function build() { engine = new Engine(BUNDLE); flow = engine.openFlow("main", { scene: startScene }); }
201320
201483
 
201321
201484
  function newGame() { stage.innerHTML = ""; controls.innerHTML = ""; build(); run(); }
201322
201485
 
201486
+ // The player reads one step ahead of what it shows, as a game's would, to know whether a choice, the end,
201487
+ // or another line follows the line on screen. While a step is read but not yet shown, the engine is past
201488
+ // what the reader sees, so Save keeps the game as it was before the read (resumeSave).
201489
+ var resumeSave = null;
201490
+ function readNext() {
201491
+ try { resumeSave = JSON.stringify(engine.saveGame()); } catch (e) { resumeSave = null; }
201492
+ var s = flow.advance();
201493
+ while (s && s.type === "gameEvent") s = flow.advance(); // a host cue with nothing to show, done at once
201494
+ return s;
201495
+ }
201496
+
201323
201497
  function run() {
201324
201498
  controls.innerHTML = "";
201325
201499
  var token = ++runToken;
201326
- (function step() {
201327
- if (token !== runToken) return; // superseded by a restart / load / choice pick
201328
- var s = flow.advance();
201329
- if (!s) { add("end", "The End"); return; } // defensive: a step should always be returned; never hard-crash
201500
+ var timeline = createTimeline(DEFAULT_PAD);
201501
+ clock = createClock(Date.now(), SPEED_RATE[speedKey]);
201502
+ // A step read ahead appears when its time comes: a line or text when its pause is over, the choices
201503
+ // as the last line starts, the end as the last line still playing ends.
201504
+ function next(s) {
201505
+ var at = !s || s.type === "end" ? timeline.endAt() : s.type === "choice" ? timeline.choicesAt() : timeline.startOf();
201506
+ waitUntil(at, token, function () { show(s); });
201507
+ }
201508
+ function show(s) {
201509
+ resumeSave = null; // what's shown is where the engine is
201510
+ if (!s || s.type === "end") { add("end", "The End"); return; } // no step is defensive: never hard-crash
201511
+ if (s.type === "choice") { renderChoice(s); return; }
201512
+ timeline.place(s, estimate(s.text));
201330
201513
  if (s.type === "line") {
201331
201514
  var who = s.characterName || s.character || "";
201332
201515
  // A speaker qualifier follows the name, as the script's cue has it: TAM (O.S.).
@@ -201334,13 +201517,13 @@ var PLAYER_JS = String.raw`
201334
201517
  if (qual && who) who += " (" + qual + ")";
201335
201518
  var dir = s.direction ? '<em class="dir">(' + esc(s.direction) + ')</em> ' : "";
201336
201519
  add("line", '<span class="who" style="color:hsl(' + hueOf(s.character || who) + ',55%,38%)">' + esc(who) + '</span>' + dir + esc(s.text));
201337
- scrollDown(); setTimeout(step, fakeDuration(s.text));
201338
- } else if (s.type === "text") {
201339
- add("text", esc(s.text)); scrollDown(); setTimeout(step, fakeDuration(s.text));
201340
- } else if (s.type === "choice") { renderChoice(s); }
201341
- else if (s.type === "end") { add("end", "The End"); }
201342
- else { step(); } // game-event beat: a host cue with no player-facing text - skip it, advance at once
201343
- })();
201520
+ } else {
201521
+ add("text", esc(s.text));
201522
+ }
201523
+ scrollDown();
201524
+ next(readNext());
201525
+ }
201526
+ next(readNext()); // the first line (or the reply to a choice) starts at once
201344
201527
  }
201345
201528
 
201346
201529
  function renderChoice(step) {
@@ -201358,7 +201541,7 @@ var PLAYER_JS = String.raw`
201358
201541
  }
201359
201542
 
201360
201543
  document.getElementById("restart").onclick = newGame;
201361
- document.getElementById("save").onclick = function () { try { localStorage.setItem(SAVE_KEY, JSON.stringify(engine.saveGame())); flash("Saved"); } catch (e) {} };
201544
+ document.getElementById("save").onclick = function () { try { localStorage.setItem(SAVE_KEY, resumeSave || JSON.stringify(engine.saveGame())); flash("Saved"); } catch (e) {} };
201362
201545
  document.getElementById("load").onclick = function () {
201363
201546
  var blob; try { blob = localStorage.getItem(SAVE_KEY); } catch (e) {}
201364
201547
  if (!blob) { flash("No save yet"); return; }
@@ -201430,10 +201613,10 @@ ${head}
201430
201613
  <button id="save">Save</button>
201431
201614
  <button id="load">Load</button>
201432
201615
  <label class="speedlabel">Speed
201433
- <select id="speed" title="Reading speed: how long each line is held before the next appears">
201434
- <option value="slow">Slow</option>
201616
+ <select id="speed" title="How fast the scene plays: its timing at half speed, as written, at double speed, or with no waits">
201617
+ <option value="half">Half</option>
201435
201618
  <option value="normal">Normal</option>
201436
- <option value="fast">Fast</option>
201619
+ <option value="double">Double</option>
201437
201620
  <option value="instant">Instant</option>
201438
201621
  </select>
201439
201622
  </label>
@@ -201451,6 +201634,7 @@ function runExportHtml(loaded) {
201451
201634
  lang,
201452
201635
  `<style>${STYLE}</style>`,
201453
201636
  `<script>${PLAYABLE_RUNTIME_JS}</script>
201637
+ <script>${PLAYABLE_TIMING_JS}</script>
201454
201638
  <script>window.PATTER_BUNDLE=${bundleJson};</script>
201455
201639
  <script>${PLAYER_JS}</script>`
201456
201640
  );
@@ -201523,6 +201707,44 @@ function buildTagIndex(bundle) {
201523
201707
  return index2;
201524
201708
  }
201525
201709
 
201710
+ // ../runtime/src/pads.ts
201711
+ var spoken = (b) => b.kind === "line" || b.kind === "text";
201712
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
201713
+ function buildPadIndex(bundle) {
201714
+ const index2 = /* @__PURE__ */ new Map();
201715
+ const set = (beat, inherited, clamp) => {
201716
+ const own2 = beat.kind === "gameEvent" ? void 0 : num(beat.padAfter);
201717
+ let resolved = own2 ?? inherited;
201718
+ if (clamp && resolved < 0) resolved = 0;
201719
+ index2.set(beat.id, own2 !== void 0 ? { own: own2, resolved } : { resolved });
201720
+ };
201721
+ const visit = (node, inherited) => {
201722
+ const def = num(node.padAfterDefault) ?? inherited;
201723
+ if (node.type === "group") {
201724
+ if (node.prompt) set(node.prompt, def, false);
201725
+ for (const child2 of node.children) visit(child2, def);
201726
+ return;
201727
+ }
201728
+ const beats = node.beats ?? [];
201729
+ let last2 = -1;
201730
+ beats.forEach((b, i) => {
201731
+ if (spoken(b)) last2 = i;
201732
+ });
201733
+ beats.forEach((b, i) => {
201734
+ if (spoken(b)) set(b, def, i === last2);
201735
+ });
201736
+ };
201737
+ const projectDefault = num(bundle.padAfterDefault) ?? DEFAULT_PAD_AFTER;
201738
+ for (const scene of Object.values(bundle.scenes)) {
201739
+ const sceneDefault = num(scene.padAfterDefault) ?? projectDefault;
201740
+ for (const block of scene.blocks) {
201741
+ const blockDefault = num(block.padAfterDefault) ?? sceneDefault;
201742
+ for (const child2 of block.children) visit(child2, blockDefault);
201743
+ }
201744
+ }
201745
+ return index2;
201746
+ }
201747
+
201526
201748
  // ../runtime/src/engine.ts
201527
201749
  var astCache = /* @__PURE__ */ new WeakMap();
201528
201750
  var tagIndexCache = /* @__PURE__ */ new WeakMap();
@@ -201534,6 +201756,15 @@ function tagIndexFor(bundle) {
201534
201756
  }
201535
201757
  return index2;
201536
201758
  }
201759
+ var padIndexCache = /* @__PURE__ */ new WeakMap();
201760
+ function padIndexFor(bundle) {
201761
+ let index2 = padIndexCache.get(bundle);
201762
+ if (!index2) {
201763
+ index2 = buildPadIndex(bundle);
201764
+ padIndexCache.set(bundle, index2);
201765
+ }
201766
+ return index2;
201767
+ }
201537
201768
  function tagsOf(host2, id) {
201538
201769
  return host2.tagIndex.get(id)?.slice();
201539
201770
  }
@@ -201669,6 +201900,7 @@ var Engine = class _Engine {
201669
201900
  blockGameIdToId: this.blockGameIdToId,
201670
201901
  // same instances the engine resolves with
201671
201902
  tagIndex: tagIndexFor(bundle),
201903
+ padIndex: padIndexFor(bundle),
201672
201904
  registry,
201673
201905
  ownsRegistry,
201674
201906
  patterBag,
@@ -202073,6 +202305,9 @@ var Engine = class _Engine {
202073
202305
  }
202074
202306
  }
202075
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;
202076
202311
  const source2 = this.host.defaultStrings[beat.id];
202077
202312
  if (source2 !== void 0) info.text = source2;
202078
202313
  }
@@ -202866,7 +203101,8 @@ var Flow = class {
202866
203101
  const node = this.host.nodeIndex.get(o.id);
202867
203102
  if (!node) continue;
202868
203103
  byId.set(o.id, node);
202869
- options.push({ ...o });
203104
+ const { prompt: saved, ...rest } = o;
203105
+ options.push({ ...rest, ...saved ? { prompt: { ...saved, padAfter: saved.padAfter ?? this.promptPadOf(node) } } : {} });
202870
203106
  }
202871
203107
  if (options.length > 0) this.pendingChoice = { groupId: c2.pendingChoice.groupId, options, byId };
202872
203108
  }
@@ -202875,7 +203111,7 @@ var Flow = class {
202875
203111
  if (this.pendingPromptOwnerId) {
202876
203112
  const owner = this.host.nodeIndex.get(this.pendingPromptOwnerId);
202877
203113
  const beat = owner ? this.authoredPromptOf(owner) : void 0;
202878
- 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) } } : {} };
202879
203115
  else this.pendingPromptOwnerId = null;
202880
203116
  }
202881
203117
  }
@@ -203245,7 +203481,7 @@ var Flow = class {
203245
203481
  case "gameEvent":
203246
203482
  return { type: "gameEvent", id: beat.id, gameData: beat.gameData, ...withTags };
203247
203483
  case "text":
203248
- 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 };
203249
203485
  case "line": {
203250
203486
  const raw = this.resolveString(beat.id);
203251
203487
  const off = !this.host.captionsOn;
@@ -203261,6 +203497,8 @@ var Flow = class {
203261
203497
  direction: silent ? void 0 : beat.direction,
203262
203498
  qualifier: silent ? void 0 : beat.qualifier,
203263
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
203264
203502
  gameData: beat.gameData,
203265
203503
  ...withTags
203266
203504
  };
@@ -203299,6 +203537,7 @@ var Flow = class {
203299
203537
  const beat = this.promptBeatOf(node);
203300
203538
  if (!beat) return void 0;
203301
203539
  const text = this.interpolate(this.resolveString(beat.id));
203540
+ const padAfter = this.padOf(beat.id);
203302
203541
  return beat.kind === "line" ? {
203303
203542
  kind: "line",
203304
203543
  text: this.captionLine(text),
@@ -203306,8 +203545,9 @@ var Flow = class {
203306
203545
  characterName: this.resolveCharacterName(beat.character),
203307
203546
  direction: beat.direction,
203308
203547
  qualifier: beat.qualifier,
203309
- qualifierName: this.resolveQualifierName(beat.qualifier)
203310
- } : { kind: "text", text };
203548
+ qualifierName: this.resolveQualifierName(beat.qualifier),
203549
+ padAfter
203550
+ } : { kind: "text", text, padAfter };
203311
203551
  }
203312
203552
  /** An option's AUTHORED prompt beat: an Option group's own `prompt`. The only prompt a replay speaks. */
203313
203553
  authoredPromptOf(node) {
@@ -203318,7 +203558,7 @@ var Flow = class {
203318
203558
  promptResult(beat, shown2) {
203319
203559
  const tags = tagsOf(this.host, beat.id);
203320
203560
  const withTags = tags && tags.length ? { tags } : {};
203321
- 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 };
203322
203562
  return {
203323
203563
  type: "line",
203324
203564
  id: beat.id,
@@ -203328,6 +203568,7 @@ var Flow = class {
203328
203568
  direction: shown2.direction,
203329
203569
  qualifier: shown2.qualifier,
203330
203570
  qualifierName: shown2.qualifierName,
203571
+ padAfter: this.padOf(beat.id),
203331
203572
  gameData: beat.gameData,
203332
203573
  ...withTags
203333
203574
  };
@@ -203364,6 +203605,17 @@ var Flow = class {
203364
203605
  const key2 = castStringKey(character);
203365
203606
  return this.host.strings[key2] ?? this.host.defaultStrings[key2] ?? this.host.castDisplay.get(character);
203366
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
+ }
203367
203619
  /** A speaker qualifier's shown name, resolved as a character's is: the `qualifier:<gameId>` string in the
203368
203620
  * active locale, else the default locale, else the authored name. Undefined in IDs-only mode (the game
203369
203621
  * maps the `qualifier` gameId itself). */
@@ -203471,6 +203723,7 @@ function savedPrompt(p) {
203471
203723
  if (p.direction !== void 0) out.direction = p.direction;
203472
203724
  if (p.qualifier !== void 0) out.qualifier = p.qualifier;
203473
203725
  if (p.qualifierName !== void 0) out.qualifierName = p.qualifierName;
203726
+ if (typeof p.padAfter === "number") out.padAfter = p.padAfter;
203474
203727
  return out;
203475
203728
  }
203476
203729
  function serialiseSelectors(map2) {
@@ -206183,8 +206436,8 @@ var require_base64_js = /* @__PURE__ */ __commonJSMin(((exports2) => {
206183
206436
  }
206184
206437
  return arr;
206185
206438
  }
206186
- function tripletToBase64(num) {
206187
- return lookup[num >> 18 & 63] + lookup[num >> 12 & 63] + lookup[num >> 6 & 63] + lookup[num & 63];
206439
+ function tripletToBase64(num2) {
206440
+ return lookup[num2 >> 18 & 63] + lookup[num2 >> 12 & 63] + lookup[num2 >> 6 & 63] + lookup[num2 & 63];
206188
206441
  }
206189
206442
  function encodeChunk(uint82, start, end) {
206190
206443
  var tmp;
@@ -211961,26 +212214,26 @@ var require_sax = /* @__PURE__ */ __commonJSMin(((exports2) => {
211961
212214
  function parseEntity(parser) {
211962
212215
  var entity = parser.entity;
211963
212216
  var entityLC = entity.toLowerCase();
211964
- var num;
212217
+ var num2;
211965
212218
  var numStr = "";
211966
212219
  if (parser.ENTITIES[entity]) return parser.ENTITIES[entity];
211967
212220
  if (parser.ENTITIES[entityLC]) return parser.ENTITIES[entityLC];
211968
212221
  entity = entityLC;
211969
212222
  if (entity.charAt(0) === "#") if (entity.charAt(1) === "x") {
211970
212223
  entity = entity.slice(2);
211971
- num = parseInt(entity, 16);
211972
- numStr = num.toString(16);
212224
+ num2 = parseInt(entity, 16);
212225
+ numStr = num2.toString(16);
211973
212226
  } else {
211974
212227
  entity = entity.slice(1);
211975
- num = parseInt(entity, 10);
211976
- numStr = num.toString(10);
212228
+ num2 = parseInt(entity, 10);
212229
+ numStr = num2.toString(10);
211977
212230
  }
211978
212231
  entity = entity.replace(/^0+/, "");
211979
- if (isNaN(num) || numStr.toLowerCase() !== entity) {
212232
+ if (isNaN(num2) || numStr.toLowerCase() !== entity) {
211980
212233
  strictFail(parser, "Invalid character entity");
211981
212234
  return "&" + parser.entity + ";";
211982
212235
  }
211983
- return String.fromCodePoint(num);
212236
+ return String.fromCodePoint(num2);
211984
212237
  }
211985
212238
  function beginWhiteSpace(parser, c2) {
211986
212239
  if (c2 === "<") {
@@ -214032,20 +214285,20 @@ var require_utils3 = /* @__PURE__ */ __commonJSMin(((exports2) => {
214032
214285
  return al + bl + cl + dl + el >>> 0;
214033
214286
  }
214034
214287
  exports2.sum64_5_lo = sum64_5_lo;
214035
- function rotr64_hi(ah, al, num) {
214036
- return (al << 32 - num | ah >>> num) >>> 0;
214288
+ function rotr64_hi(ah, al, num2) {
214289
+ return (al << 32 - num2 | ah >>> num2) >>> 0;
214037
214290
  }
214038
214291
  exports2.rotr64_hi = rotr64_hi;
214039
- function rotr64_lo(ah, al, num) {
214040
- return (ah << 32 - num | al >>> num) >>> 0;
214292
+ function rotr64_lo(ah, al, num2) {
214293
+ return (ah << 32 - num2 | al >>> num2) >>> 0;
214041
214294
  }
214042
214295
  exports2.rotr64_lo = rotr64_lo;
214043
- function shr64_hi(ah, al, num) {
214044
- return ah >>> num;
214296
+ function shr64_hi(ah, al, num2) {
214297
+ return ah >>> num2;
214045
214298
  }
214046
214299
  exports2.shr64_hi = shr64_hi;
214047
- function shr64_lo(ah, al, num) {
214048
- return (ah << 32 - num | al >>> num) >>> 0;
214300
+ function shr64_lo(ah, al, num2) {
214301
+ return (ah << 32 - num2 | al >>> num2) >>> 0;
214049
214302
  }
214050
214303
  exports2.shr64_lo = shr64_lo;
214051
214304
  }));
@@ -241862,8 +242115,8 @@ var StandardFont = class extends PDFFont {
241862
242115
  return name in STANDARD_FONTS;
241863
242116
  }
241864
242117
  };
241865
- var toHex = function(num) {
241866
- return `0000${num.toString(16)}`.slice(-4);
242118
+ var toHex = function(num2) {
242119
+ return `0000${num2.toString(16)}`.slice(-4);
241867
242120
  };
241868
242121
  var EmbeddedFont = class extends PDFFont {
241869
242122
  constructor(document2, font, id) {
@@ -246683,7 +246936,7 @@ async function exportEditableScript(loaded, opts) {
246683
246936
  if (isEditable(el)) {
246684
246937
  const code = markerOf.get(el);
246685
246938
  const baseline = source2[el.id] ?? "";
246686
- lines[code] = el.kind === "line" ? { id: el.id, kind: "line", character: el.character, ...el.qualifier ? { qualifier: el.qualifier } : {}, ...el.direction ? { direction: el.direction } : {}, baseline } : { id: el.id, kind: el.kind === "option" ? "option" : "narration", baseline };
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 };
246687
246940
  skeleton.push({ kind: "box", marker: code });
246688
246941
  } else {
246689
246942
  skeleton.push({ kind: el.kind, node: el.id, text: contextText(el) });
@@ -247300,14 +247553,14 @@ function planEditableImport(loaded, returned, opts) {
247300
247553
  const cue = sent.kind === "line" ? readLead(b.lead.proposed) : void 0;
247301
247554
  let proposedCharacter, proposedQualifier, proposedDirection;
247302
247555
  if (sent.kind === "line" && cue) {
247303
- const read2 = readCue(cue.character, sent.character ?? "", sent.qualifier ?? "", cast, qualifiers);
247556
+ const read2 = readCue(cue.character, sent, cast, qualifiers);
247304
247557
  if (read2.unknownSpeaker !== void 0) {
247305
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.` });
247306
- comment(here ?? nearestLine(boxes.indexOf(b)), author, `Speaker changed from ${sentCue(sent.character, sent.qualifier, qualifiers) || "(none)"} to ${read2.unknownSpeaker}, who isn't in the cast.`, ts);
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);
247307
247560
  }
247308
247561
  if (read2.unknownQualifier !== void 0) {
247309
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.` });
247310
- comment(here ?? nearestLine(boxes.indexOf(b)), author, `Speaker changed from ${sentCue(sent.character, sent.qualifier, qualifiers) || "(none)"} to ${tidy(cue.character)}, but (${read2.unknownQualifier}) isn't one of the project's qualifiers.`, ts);
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);
247311
247564
  }
247312
247565
  if (read2.character !== void 0 && read2.character !== (sent.character ?? "")) proposedCharacter = read2.character;
247313
247566
  if (read2.qualifier !== void 0 && read2.qualifier !== (sent.qualifier ?? "")) proposedQualifier = read2.qualifier;
@@ -247420,16 +247673,21 @@ function readLead(text) {
247420
247673
  const character = lines.find((l2) => l2 !== direction) ?? "";
247421
247674
  return { character, direction: direction ? direction.slice(1, -1) : "" };
247422
247675
  }
247423
- function sentCue(character, qualifier, qualifiers) {
247424
- if (!character) return "";
247425
- if (!qualifier) return character;
247426
- return `${character} (${qualifiers.find((q) => q.gameId === qualifier)?.name ?? qualifier})`;
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;
247427
247684
  }
247428
- function readCue(text, sentCharacter, sentQualifier, cast, qualifiers) {
247685
+ function readCue(text, sent, cast, qualifiers) {
247429
247686
  const cue = tidy(text);
247430
247687
  if (!cue) return {};
247431
247688
  const upper = (s) => s.toUpperCase();
247432
- if (upper(cue) === upper(sentCue(sentCharacter, sentQualifier, qualifiers))) return {};
247689
+ if (upper(cue) === upper(sentCue(sent, qualifiers))) return {};
247690
+ const sentCharacter = sent.character ?? "";
247433
247691
  const speaker = (name) => cast.find((c2) => upper(c2.name) === upper(name))?.name ?? (sentCharacter && upper(name) === upper(sentCharacter) ? sentCharacter : void 0);
247434
247692
  const whole = speaker(cue);
247435
247693
  if (whole !== void 0) return { character: whole, qualifier: "" };
@@ -247437,6 +247695,7 @@ function readCue(text, sentCharacter, sentQualifier, cast, qualifiers) {
247437
247695
  const rest = m ? speaker(m[1].trim()) : void 0;
247438
247696
  if (!m || rest === void 0) return { unknownSpeaker: cue };
247439
247697
  const bracket = m[2].trim();
247698
+ if (sent.qualifier && upper(bracket) === upper(sentQualifierName(sent, qualifiers))) return { character: rest, qualifier: sent.qualifier };
247440
247699
  const q = qualifiers.find((x) => upper(x.name) === upper(bracket));
247441
247700
  return q ? { character: rest, qualifier: q.gameId } : { character: rest, unknownQualifier: bracket };
247442
247701
  }
@@ -249730,7 +249989,7 @@ function fileStatus(filePaths, options = {}) {
249730
249989
  // package.json
249731
249990
  var package_default = {
249732
249991
  name: "@patterkit/cli",
249733
- version: "0.10.0",
249992
+ version: "0.10.1",
249734
249993
  description: "The `patter` CLI: validate, format, export, play, test coverage, localise, and merge Patter projects.",
249735
249994
  type: "module",
249736
249995
  license: "MIT",
@@ -249759,8 +250018,8 @@ var package_default = {
249759
250018
  "build:standalone:others": "node scripts/build-standalone.mjs --targets=linux-x64,linux-arm64,windows-x64"
249760
250019
  },
249761
250020
  dependencies: {
249762
- "@patterkit/core": "0.5.0",
249763
- "@patterkit/ops": "0.15.0",
250021
+ "@patterkit/core": "0.6.0",
250022
+ "@patterkit/ops": "0.16.0",
249764
250023
  "@wildwinter/simple-vc-lib": "^0.5.2"
249765
250024
  }
249766
250025
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patterkit/cli",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "The `patter` CLI: validate, format, export, play, test coverage, localise, and merge Patter projects.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,8 +29,8 @@
29
29
  "build:standalone:others": "node scripts/build-standalone.mjs --targets=linux-x64,linux-arm64,windows-x64"
30
30
  },
31
31
  "dependencies": {
32
- "@patterkit/core": "0.5.0",
33
- "@patterkit/ops": "0.15.0",
32
+ "@patterkit/core": "0.6.0",
33
+ "@patterkit/ops": "0.16.0",
34
34
  "@wildwinter/simple-vc-lib": "^0.5.2"
35
35
  }
36
36
  }