@patterkit/ops 0.16.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -697,22 +697,37 @@ function mergeEditRecord(o, t) {
697
697
  if (Object.keys(loc).length > 0) merged.localisedAt = loc;
698
698
  return merged;
699
699
  }
700
- function mergeKeyedByName(base, ours, theirs, path, conflicts) {
700
+ function mergeKeyedByName(base, ours, theirs, path, conflicts, resolve7) {
701
701
  const byName = (arr) => {
702
702
  const m = {};
703
703
  for (const p of arr) if (isObj(p) && typeof p.name === "string") m[p.name] = p;
704
704
  return m;
705
705
  };
706
- const merged = mergeMap(byName(base), byName(ours), byName(theirs), path, conflicts);
706
+ const merged = mergeMap(byName(base), byName(ours), byName(theirs), path, conflicts, resolve7?.(path, conflicts));
707
707
  return Object.keys(merged).sort().map((name) => merged[name]);
708
708
  }
709
+ function mergeCastMember(path, conflicts) {
710
+ return (name, b, o, t) => {
711
+ if (!isObj(o) || !isObj(t)) return { resolved: false, kind: deletedKind(b, o, t) };
712
+ const base = asMap(b);
713
+ const out = {};
714
+ for (const f of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(o), ...Object.keys(t)])) {
715
+ const v = f === "qualifierLabels" ? mergeMap(asMap(base[f]), asMap(o[f]), asMap(t[f]), `${path}.${name}.${f}`, conflicts) : merge3(base[f], o[f], t[f], name, `${path}.${name}.${f}`, conflicts);
716
+ if (f === "qualifierLabels" ? Object.keys(v).length > 0 : v !== void 0) out[f] = v;
717
+ }
718
+ return { resolved: true, value: out };
719
+ };
720
+ }
709
721
  function mergeProject(base, ours, theirs) {
710
722
  const conflicts = [];
711
723
  const merged = {};
712
724
  for (const k of /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)])) {
713
- if (k === "properties" || k === "cast") {
725
+ if (k === "properties") {
714
726
  const v = mergeKeyedByName(asArr(base[k]), asArr(ours[k]), asArr(theirs[k]), k, conflicts);
715
727
  if (v.length > 0) merged[k] = v;
728
+ } else if (k === "cast") {
729
+ const v = mergeKeyedByName(asArr(base[k]), asArr(ours[k]), asArr(theirs[k]), k, conflicts, mergeCastMember);
730
+ if (v.length > 0) merged[k] = v;
716
731
  } else if (k === "gameDataFields") {
717
732
  const v = mergeGameDataFields(asMap(base[k]), asMap(ours[k]), asMap(theirs[k]), conflicts);
718
733
  if (Object.keys(v).length > 0) merged[k] = v;
@@ -1123,10 +1138,10 @@ function reachabilityIssues(loaded, compiled) {
1123
1138
  return bare.startsWith("@scene.");
1124
1139
  };
1125
1140
  const monotonic = (key) => latched.has(key) && !broken.has(key) && !preset.has(key) && projectOwned(key);
1126
- const cache = /* @__PURE__ */ new Map();
1141
+ const cache2 = /* @__PURE__ */ new Map();
1127
1142
  const inFlight = /* @__PURE__ */ new Set();
1128
1143
  const mustHold = (key) => {
1129
- const done = cache.get(key);
1144
+ const done = cache2.get(key);
1130
1145
  if (done !== void 0) return { need: done, cut: false };
1131
1146
  if (inFlight.has(key)) return { need: /* @__PURE__ */ new Set(), cut: true };
1132
1147
  const routes = writers.get(key);
@@ -1147,7 +1162,7 @@ function reachabilityIssues(loaded, compiled) {
1147
1162
  }
1148
1163
  inFlight.delete(key);
1149
1164
  const result = shared ?? /* @__PURE__ */ new Set();
1150
- if (!cut) cache.set(key, result);
1165
+ if (!cut) cache2.set(key, result);
1151
1166
  return { need: result, cut };
1152
1167
  };
1153
1168
  const refute = (gate) => {
@@ -1616,7 +1631,7 @@ import { canonicalStringify as canonicalStringify4 } from "@patterkit/core";
1616
1631
  import { DEFAULT_PAD_AFTER } from "@patterkit/model";
1617
1632
 
1618
1633
  // src/playable-runtime.ts
1619
- var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var K=Object.defineProperty;var Qe=Object.getOwnPropertyDescriptor;var Ke=Object.getOwnPropertyNames;var ze=Object.prototype.hasOwnProperty;var Ye=(n,e)=>{for(var t in e)K(n,t,{get:e[t],enumerable:!0})},Xe=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Ke(e))!ze.call(n,s)&&s!==t&&K(n,s,{get:()=>e[s],enumerable:!(r=Qe(e,s))||r.enumerable});return n};var Ze=n=>Xe(K({},"__esModule",{value:!0}),n);var bt={};Ye(bt,{Checkpoint:()=>j,DEFAULT_PAD_AFTER:()=>x,Engine:()=>J,Flow:()=>D,buildTagIndex:()=>W,describeBundle:()=>$e,effectiveGameData:()=>_e,gameDataFields:()=>He,gameDataValue:()=>ae});function P(n,e){return n!==void 0&&Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0}function E(n){switch(n[0]){case"b":return{kind:"bool",value:n[1]};case"n":return{kind:"number",value:n[1]};case"s":return{kind:"string",value:n[1]};case"sv":return{kind:"scopedvar",scope:n[1],name:n[2]};case"u":return{kind:"unary",op:n[1],operand:E(n[2])};case"bin":return{kind:"binary",op:n[1],left:E(n[2]),right:E(n[3])};case"call":{let e=n.slice(2).map(E);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var u=class extends Error{constructor(e){super(e),this.name="EvalError"}};function q(n,e,t){let r=new Map(t.scopes.map(i=>[i.token,i.missing??"false"])),s=i=>{switch(i.kind){case"bool":return i.value;case"number":return i.value;case"string":return i.value;case"scopedvar":{let o=P(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):P(o,i.name);if(a===void 0){if(r.get(i.scope)==="throw")throw new u(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{if(i.name==="advance"&&!P(t.functions,i.name)){let a=i.args[0];if(i.args.length!==1||a===void 0)throw new u(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=z(a,e);if(c===void 0)throw new u("advance() needs a quality reference (@scope.name of a quality property)");let l=v(s(a),c,"advance");return c[Math.min(l+1,c.length-1)]}let o=P(t.functions,i.name);if(!o)throw new u(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new u("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=s(i.operand);if(typeof a!="boolean")throw new u(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new u(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let f=s(i.left);if(typeof f!="boolean")throw new u(`\'and\' requires boolean operands, left is ${typeof f}`);if(!f)return!1;let g=s(i.right);if(typeof g!="boolean")throw new u(`\'and\' requires boolean operands, right is ${typeof g}`);return g}if(i.op==="or"){let f=s(i.left);if(typeof f!="boolean")throw new u(`\'or\' requires boolean operands, left is ${typeof f}`);if(f)return!0;let g=s(i.right);if(typeof g!="boolean")throw new u(`\'or\' requires boolean operands, right is ${typeof g}`);return g}let o=s(i.left),a=s(i.right),c=z(i.left,e),l=z(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!et(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new u(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return v(o,p,">")>v(a,p,">");case">=":return v(o,p,">=")>=v(a,p,">=");case"<":return v(o,p,"<")<v(a,p,"<");case"<=":return v(o,p,"<=")<=v(a,p,"<=");case"+":case"-":case"*":case"/":throw new u(`\'${i.op}\' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(i.op){case"==":return fe(o,a);case"!=":return!fe(o,a);case">":return C(o,a,">"),o>a;case">=":return C(o,a,">="),o>=a;case"<":return C(o,a,"<"),o<a;case"<=":return C(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new u(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return C(o,a,"-"),o-a;case"*":return C(o,a,"*"),o*a;case"/":if(C(o,a,"/"),a===0)throw new u("division by zero");return o/a}}}};return s(n)}function fe(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;let t=[...n].sort(),r=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==r[s])return!1;return!0}return n===e}function C(n,e,t){if(typeof n!="number"||typeof e!="number")throw new u(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function z(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function v(n,e,t){if(typeof n!="string")throw new u(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new u(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var et=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function O(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function Y(n){let e=O(n);return{next(){e=e+1831565813>>>0;let t=Math.imul(e^e>>>15,1|e);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296},state(){return e}}}var tt={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},rt=[tt];function he(n,e,t){let r=t?.countingCalls??rt;return $(n,t?.want??!0,e,r)}function $(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=$(n.left,e,t,r),i=$(n.right,e,t,r);return n.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(n.kind==="unary"&&n.op==="not")return $(n.operand,!e,t,r);if(n.kind==="call"){let s=r.find(i=>i.name===n.name);if(s){let i=s.count(n),o=t(n);return e?o?i:0:o?0:1}}return t(n)===e?1:0}var b=class n{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let r=this.norm(t.name);this.decls.set(r,t),this.values[r]=structuredClone(t.default??B(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,r){let s=this.norm(e);if(!r?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:r?.silent??!1,reason:r?.reason};this.values[s]=t;for(let o of[...this.auditors])o(i);if(!i.silent)for(let o of[...this.subscribers])o(i);return i}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}onAudit(e){return this.auditors.add(e),()=>this.auditors.delete(e)}rows(){return[...this.decls.entries()].map(([e,t])=>Se(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm,pathPrefix:this.pathPrefix});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e){for(let[t,r]of Object.entries(e))this.values[this.norm(t)]=r}};function Se(n,e,t,r,s=""){let i=r??n.name.toLowerCase();return{name:i,path:s+i,type:n.type,value:e,default:n.default??B(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var nt=n=>n.toLowerCase(),X=1,H=class{scopes=new Map;parked=new Map;rev=0;get revision(){return this.rev}defineOwned(e,t,r){let s=typeof r=="string"?{pathPrefix:r}:r??{},i=new b(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,i,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,r){this.assertFree(e,r?.owner),this.scopes.set(e,{kind:"owned",bag:t,...r?.owner!==void 0?{owner:r.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(t.load(s),this.parked.delete(e)),this}remove(e,t){let r=this.scopes.get(e);if(!r)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&r.kind==="owned"&&this.parked.set(e,r.bag.save()),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&this.parked.delete(t);return this}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??nt,a=new Map;for(let c of r)a.set(o(c.name),c);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:i.writable??!0,norm:o,...i.owner!==void 0?{owner:i.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let r=this.scopes.get(e);if(r)return r.kind==="owned"?r.bag.get(t):r.resolver.get(r.norm(t))}set(e,t,r,s){let i=this.scopes.get(e);if(!i)throw new Error(`unknown scope \'@${e}\'`);if(i.kind==="owned"){if(!s?.host&&!i.bag.writable(t))throw new Error(`\'@${e}.${t}\' is read-only`);i.bag.set(t,r,s?.host?{host:!0}:void 0);return}let o=i.norm(t);if(!i.resolver.set)throw new Error(`\'@${e}.${t}\' is read-only`);if(!s?.host&&!this.foreignWritable(i,o))throw new Error(`\'@${e}.${t}\' is read-only`);i.resolver.set(o,r)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,r]of this.scopes){let s=r.owner!==void 0?{owner:r.owner}:{};if(r.kind==="owned")for(let i of r.bag.rows())e.push({scope:t,...s,...i});else for(let[i,o]of r.decls)e.push({scope:t,...s,...Se(o,r.resolver.get(i),this.foreignWritable(r,i),i,`${t}.`)})}return e}toEvalContext(e,t){let r=this.view(t?.aliases),s={};for(let[o,a]of r)s[o]=a.kind==="owned"?a.bag.values:a.resolver;let i=this.qualityLadders(r);return i.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(o,a)=>{let c=r.get(o);return c?i.get(o)?.get(st(c)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[r,s]of Object.entries(e??{})){let i=this.scopes.get(s);if(!i)throw new Error(`alias \'@${r}\' names \'${s}\', which is not registered`);t.set(r,i)}return t}qualityLadders(e){let t=new Map;for(let[r,s]of e)for(let[i,o]of me(s)){if(o.type!=="quality"||o.stages===void 0)continue;let a=t.get(r);a||(a=new Map,t.set(r,a)),a.set(i,o.stages)}return t}toSchema(e){let t=new Map;for(let[r,s]of this.view(e?.aliases)){let i=me(s);if(i.length===0)continue;let o=new Map;for(let[a,c]of i)o.set(a,{type:c.type,enumValues:c.values,...c.stages!==void 0?{stages:c.stages}:{}});t.set(r,o)}return{properties:t}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());for(let[t,r]of this.parked)e[t]=structuredClone(r);return e}load(e,t){t?.keepParked||this.parked.clear();for(let[r,s]of Object.entries(e)){let i=this.scopes.get(r);i?.kind==="owned"?i.bag.load(s):i||this.parked.set(r,structuredClone(s))}}saveFragment(){return{version:X,scopes:this.save()}}loadFragment(e){if(e.version!==X)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${X})`);this.load(e.scopes)}assertFree(e,t){let r=this.scopes.get(e);if(!r)return;let s=r.owner!==void 0?` by ${r.owner}`:"",i=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope \'@${e}\' is already registered${s}${i}`)}};function me(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function st(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function B(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return n.values?.[0]??"";case"flags":return[];case"quality":return n.stages?.[0]??"";default:return!1}}var ye=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function A(n){return n.ctx.host??{}}var Z={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new u("random(a, b) requires exactly 2 arguments");let t=A(e).nextRandom;if(!t)throw new u("random() called without a PRNG in context");let r=e.evaluate(n[0]),s=e.evaluate(n[1]);if(typeof r!="number"||typeof s!="number")throw new u("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new u("random(a, b) arguments must be integers");let i=Math.min(r,s),o=Math.max(r,s);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:ve("check_flags"),eval(n,e){let t=we(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new u("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:ve("set_flags"),eval(n,e){let t=[...we(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new u("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let i=t.indexOf(s.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:U("visits"),eval:(n,e)=>A(e).visits?.(_(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:U("seen"),eval:(n,e)=>(A(e).visits?.(_(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:U("patter_visits"),eval:(n,e)=>A(e).patterVisits?.(_(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:U("patter_seen"),eval:(n,e)=>(A(e).patterVisits?.(_(n,e,"patter_seen"))??0)>0}}},Mt=ye.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function be(n,e){let t=n.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function _(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new u(`${t}(id) requires a string node id`);return r}function U(n){return(e,t)=>{let r=e[0];r&&r.kind!=="string"&&t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(id): the argument must be a string id literal (a scene / block / node id)`})}}function we(n,e,t){if(!n)throw new u(`${t}() requires at least one argument (the flags variable)`);let r=e.evaluate(n);if(Array.isArray(r))return r;if(r===!1||r===null||r===void 0)return[];throw new u(`${t}() first argument must be a flags property`)}function ve(n){return(e,t)=>{if(e.length===0)return;let r=e[0];if(r.kind!=="scopedvar"){t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): first argument must be a flags property reference (@name or @scope.name)`});return}let s=t.schema.properties.get(r.scope)?.get(r.name);if(s&&s.type!=="flags"){let i=r.scope===t.defaultScope?r.name:`${r.scope}.${r.name}`;t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): \'@${i}\' is not a flags property (got ${s.type})`});return}for(let i=1;i<e.length;i++){let o=e[i];o.kind!=="flagdelta"?t.report({path:[...t.path,"args",i],kind:"wrong-arg-type",severity:"error",message:`${n}(): argument ${i+1} must be +flagName or -flagName`}):s?.type==="flags"&&s.enumValues&&!s.enumValues.includes(o.name)&&t.report({path:[...t.path,"args",i],kind:"unknown-flag-name",severity:"error",message:`${n}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var it=/^@[A-Za-z0-9_.]+$/;function*ot(n){let e="",t=0;for(;t<n.length;){let r=n[t];if(r==="{"&&n[t+1]==="{"){e+="{",t+=2;continue}if(r==="}"&&n[t+1]==="}"){e+="}",t+=2;continue}if(r==="{"){let s=n.indexOf("}",t+1);if(s!==-1){let i=n.slice(t,s+1),o=n.slice(t+1,s).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:it.test(o)?o:void 0},t=s+1;continue}e+=i,t=s+1;continue}}e+=r,t+=1}e&&(yield{kind:"text",value:e})}function at(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function ct(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function lt(n){let e="",t=!1;for(let r of n){if(ct(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function xe(n,e,t){if(e.length===0||n.indexOf(e)<0)return n;let r="",s=0,i=!1;for(;s<n.length;){if(n.startsWith(e,s)){let o=n.indexOf(t,s+e.length);if(o>=0){s=o+t.length,i=!0;continue}r+=n.slice(s);break}r+=n[s],s+=1}return i?lt(r):n}function ke(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of ot(n)){if(r.kind==="text"){t+=r.value;continue}if(!r.ref){t+=r.raw;continue}let s=e(r.ref);t+=s===void 0?"":at(s)}return t}function R(n,e){for(let t of n){e(t);let r=t.children;r&&R(r,e)}}function pt(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function y(n){let e=n.gameId?.trim();return e||pt(n.name)}var x=.6;function ee(n){return`cast:${n}`}function te(n){return`qualifier:${n}`}var re={open:"[",close:"]"},Ee="SFX";var F=3;function N(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function W(n){let e=new Map,t=(r,s)=>{let i=N([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group"&&r.prompt&&e.set(r.prompt.id,N([...i,...r.prompt.tags??[]])),r.type==="group")for(let o of r.children)t(o,i);else for(let o of r.beats??[])e.set(o.id,N([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=N(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=N([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var Ce=n=>n.kind==="line"||n.kind==="text",T=n=>typeof n=="number"&&Number.isFinite(n)?n:void 0;function Ie(n){let e=new Map,t=(i,o,a)=>{let c=i.kind==="gameEvent"?void 0:T(i.padAfter),l=c??o;a&&l<0&&(l=0),e.set(i.id,c!==void 0?{own:c,resolved:l}:{resolved:l})},r=(i,o)=>{let a=T(i.padAfterDefault)??o;if(i.type==="group"){i.prompt&&t(i.prompt,a,!1);for(let p of i.children)r(p,a);return}let c=i.beats??[],l=-1;c.forEach((p,f)=>{Ce(p)&&(l=f)}),c.forEach((p,f)=>{Ce(p)&&t(p,a,f===l)})},s=T(n.padAfterDefault)??x;for(let i of Object.values(n.scenes)){let o=T(i.padAfterDefault)??s;for(let a of i.blocks){let c=T(a.padAfterDefault)??o;for(let l of a.children)r(l,c)}}return e}var De=new WeakMap,Re=new WeakMap;function dt(n){let e=Re.get(n);return e||(e=W(n),Re.set(n,e)),e}var Pe=new WeakMap;function ut(n){let e=Pe.get(n);return e||(e=Ie(n),Pe.set(n,e)),e}function I(n,e){return n.tagIndex.get(e)?.slice()}var V="Patter",G=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),gt=n=>n.replace(/%2F/g,"/").replace(/%25/g,"%"),Oe=Symbol("ownsRegistry"),S={stage:n=>`patter/scene/${G(n)}`,flow:n=>`patter/flow/${G(n)}/`,flowGlobals:n=>`patter/flow/${G(n)}/patter`,flowScene:(n,e)=>`patter/flow/${G(n)}/scene/${G(e)}`},j=class{constructor(e){this.journal=e}journal},J=class n{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;engineLog=[];engineSeq=0;engineTraceHandlers=new Set;constructor(e,t={}){this.creationOptions=t;let r=t.locale??e.locales.default,s=e.strings;this.allStrings=s,this.currentLocale=r;let i=s[r]??{},o=s[e.locales.default]??{},a=e.localisation,c=a?.mode==="ids"&&!a.sourceDebug;this.sourceDebug=a?.mode==="ids"&&!!a.sourceDebug,this.sourceDebug&&typeof console<"u"&&console.warn("[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.");let l=new Map;for(let d of e.cast??[])d.displayName&&l.set(d.name,d.displayName);let p=new Map;for(let d of e.qualifiers??[])d?.gameId&&p.set(d.gameId,d.name);this.defaultSeed=(t.seed??2654435769)>>>0;let f=new Map,g=new Map,ce=new Map;for(let[d,h]of Object.entries(e.scenes)){this.sceneGameIdToId.set(y(h),d);let w=new Map;for(let m of h.blocks)g.set(m.id,{sceneId:d}),ce.set(m.id,m),w.set(y(m),m.id),R(m.children,L=>f.set(L.id,L));this.blockGameIdToId.set(d,w)}let le=e.properties??[],Q=le.filter(d=>d.shared??!0).map(Ne),Ue=le.filter(d=>!(d.shared??!0)).map(Ne),We=new Set(Q.map(d=>d.name.toLowerCase())),k=t.registry??new H,pe=!t.registry||t[Oe]===!0,de=new b(Q,{pathPrefix:"@patter."}),ue=[],M=[];try{k.mountOwned("patter",de,{owner:V}),M.push("patter");let d=new Set;for(let[h,w]of Object.entries(Te(t))){let m=e.scopeRegistry?.scopes.find(Je=>Je.token===h),L=(m?.declarations??[]).map(Me);k.defineForeign(h,w,L,{writable:m?.writable??!0,owner:V}),M.push(h),d.add(h)}for(let h of pe?e.scopeRegistry?.scopes??[]:[]){if(d.has(h.token)||k.has(h.token))continue;let w=(h.declarations??[]).map(m=>ht(m,h.writable));k.defineOwned(h.token,w,{owner:V}),M.push(h.token),ue.push(h.token)}}catch(d){for(let h of M)k.remove(h,{keep:!0});throw d}let ge=new Map;for(let[d,h]of Object.entries(e.scenes)){let w=new Set((h.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));ge.set(d,w)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(d,h,w)=>this.emitEngine(d,h,w),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:p,nodeIndex:f,blockIndex:g,blockById:ce,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:dt(e),padIndex:ut(e),registry:k,ownsRegistry:pe,patterBag:de,hostScopes:ue,patterSharedDecls:Q,patterLocalDecls:Ue,patterSharedNames:We,sceneSharedNames:ge,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(d=>console.warn(`Patterplay: ${d.kind} on ${d.node} in flow \'${d.flow}\' failed, played through: ${d.message}`)),replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??re).open,captionClose:(e.closedCaptions??re).close,captionCharacter:e.closedCaptions?.character||Ee,refSplitCache:new Map,refSplitRevision:k.revision}}get locale(){return this.currentLocale}get isSourceDebug(){return this.sourceDebug}get buildId(){return this.host.bundle.content?.hash??""}setLocale(e){this.currentLocale=e,this.host.strings=this.allStrings[e]??{}}replaceStrings(e){this.allStrings=e.strings,this.host.strings=this.allStrings[this.currentLocale]??{},this.host.defaultStrings=this.allStrings[this.host.bundle.locales.default]??{}}hotSwap(e){this.refuseInCheckpoint("hotSwap");let t=this.saveGame(),r=o=>(o.setLocale(this.currentLocale),o.setClosedCaptions(this.host.captionsOn),o),s=()=>new n(e,{...this.creationOptions,registry:this.host.registry,[Oe]:this.host.ownsRegistry});this.release(!0);let i=s();try{return i.loadGame(t),r(i)}catch{i.release(!0);let o=s();for(let[a,c]of Object.entries(t.flows)){let l=c.cursor.currentSceneId;try{o.openFlow(a,l!==null?{scene:l}:{})}catch{}}return r(o)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:r,blockId:s}=this.resolveOpenAddress(t.scene,t.block),i=this.flowsById.get(e),o=this.host.journal;if(o&&i&&!i.isClosed)throw new Error(`openFlow would replace the open flow \'${e}\', which can\'t be undone while a checkpoint is open`);i?.close();let a=new D(e,this.host,t.seed??this.defaultSeed);return o&&(o.flows.add(a),o.opened.add(a),o.undo.push(()=>{a.close(),i?this.flowsById.set(e,i):this.flowsById.delete(e)})),this.flowsById.set(e,a),a.begin(r,s),a}runFlow(e,t,r){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:r}).advanceToStop().played;if(!s.goto(t,r))throw new Error(`runFlow: address not found: ${t}${r===void 0?"":` / ${r}`}`);return s.advanceToStop().played}assertExternalScopes(){for(let e of this.host.bundle.externalScopes??[])if(!this.host.registry.has(e))throw new Error(`this content names @${e}, which no engine on this registry registered: give every engine the game\'s one registry`)}resolveOpenAddress(e,t){if(e!==void 0){let r=se(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=ie(this.host,r,t);if(s===void 0)throw new Error(`unknown block: ${t}`);return{sceneId:r,blockId:s}}if(t!==void 0){if(!this.host.blockIndex.has(t))throw new Error(`unknown block: ${t}`);return{blockId:t}}if(Object.keys(this.host.bundle.scenes).length===0)throw new Error("no scenes in bundle");return{}}resolveSceneRef(e){if(e!=null)return se(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:ie(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?y(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?y(t):void 0}tagsForBeat(e){return I(this.host,e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?I(this.host,t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?I(this.host,s):void 0)??[]}gameDataForScene(e){let t=this.resolveSceneRef(e);return{...(t!=null?this.host.bundle.scenes[t]:void 0)?.gameData}}gameDataForBlock(e,t){let r=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(r!=null?this.host.blockById.get(r):void 0)?.gameData}}getCast(){let e=[];for(let t of this.host.bundle.cast??[])t?.name&&e.push(t.name);return e}castForScene(e){let t=this.resolveSceneRef(e),r=t!=null?this.host.bundle.scenes[t]:void 0;if(!r)return[];let s=new Set;for(let i of r.blocks)Be(i.children,s);return[...s]}castForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t),i=s!=null?this.host.blockById.get(s):void 0;if(!i)return[];let o=new Set;return Be(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...y(e)?{gameId:y(e)}:{},name:e.name,...Ae(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...y(t)?{gameId:y(t)}:{},name:t.name,...Ae(t.gameData),...this.tagsField(t.id),children:t.children.map(r=>this.outlineNode(r))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let r of t.blocks)R(r.children,s=>{if(s.type==="snippet")for(let i of s.beats??[])e.push({sceneId:t.id,blockId:r.id,snippetId:s.id,beat:this.beatInfo(i)})});return e}outlineNode(e){return e.type==="group"?{type:"group",id:e.id,...this.tagsField(e.id),...e.selector?{selector:e.selector}:{},...e.prompt?{prompt:this.beatInfo(e.prompt)}:{},children:e.children.map(t=>this.outlineNode(t))}:{type:"snippet",id:e.id,...this.tagsField(e.id),beats:(e.beats??[]).map(t=>this.beatInfo(t)),...e.jump?{jumpTo:e.jump.to,...e.jump.mode?{jumpMode:e.jump.mode}:{}}:{}}}beatInfo(e){let t=I(this.host,e.id),r={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){r.character=e.character;let s=this.host.defaultStrings[ee(e.character)]??this.host.castDisplay.get(e.character);s!==void 0&&(r.characterName=s)}if(e.direction!==void 0&&(r.direction=e.direction),e.qualifier!==void 0){r.qualifier=e.qualifier;let s=this.host.defaultStrings[te(e.qualifier)]??this.host.qualifierDisplay.get(e.qualifier);s!==void 0&&(r.qualifierName=s)}}if(e.kind==="line"||e.kind==="text"){let s=this.host.padIndex.get(e.id);r.padAfter=s?.resolved??x,s?.own!==void 0&&(r.ownPadAfter=s.own);let i=this.host.defaultStrings[e.id];i!==void 0&&(r.text=i)}return e.gameData&&Object.keys(e.gameData).length&&(r.gameData=e.gameData),t&&t.length&&(r.tags=t),r}tagsField(e){let t=I(this.host,e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}listBags(){let e=[{bag:this.host.patterBag}];for(let[t,r]of this.host.stageBags)e.push({bag:r,pathPrefix:`@scene:${t}.`});return e}checkpoint(){if(this.host.journal)throw new Error("a checkpoint is already open: roll it back or commit it first");let e={undo:[],flows:new Set,opened:new Set,selectors:new Set,flowSelectors:new Map};return this.host.journal=e,new j(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let r=t.length-1;r>=0;r--)t[r]();n.forget(e.journal)}commit(e){this.endCheckpoint(e),n.forget(e.journal)}get inCheckpoint(){return this.host.journal!==null}endCheckpoint(e){if(this.host.journal!==e.journal)throw new Error("that checkpoint is not the open one");this.host.journal=null}static forget(e){e.undo=[],e.flows.clear(),e.opened.clear(),e.selectors.clear(),e.flowSelectors.clear()}refuseInCheckpoint(e){if(this.host.journal)throw new Error(`${e} can\'t be undone, so it isn\'t allowed while a checkpoint is open`)}captureFlow(e){let t=this.flowsById.get(e);if(!t)throw new Error(`no open flow \'${e}\' to capture`);return{version:F,...t.snapshot(),...St(e,this.host.registry.save())}}restoreFlow(e,t){this.refuseInCheckpoint("restoreFlow");let r=t.version;if(r!==2&&r!==F)throw new Error(`unsupported flow state version: ${String(r)}`);let s=t.cursor;if(typeof s!="object"||s===null)throw new Error("malformed flow state: no cursor");if(this.flowsById.has(e))throw new Error(`a flow \'${e}\' is already open; close it before restoring another under that id`);this.assertExternalScopes();let i=S.flow(e);this.host.registry.discardParked(i),this.host.registry.load(qe(e,t),{keepParked:!0});let o=new D(e,this.host,this.defaultSeed);try{o.restore(t)}catch(a){throw o.close(),this.host.registry.discardParked(i),a}return this.flowsById.set(e,o),o}closeFlow(e){this.refuseInCheckpoint("closeFlow"),this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){this.refuseInCheckpoint("reset");for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.patterBag.reseed(this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear();for(let e of this.host.stageBags.keys())this.host.registry.remove(S.stage(e));this.host.stageBags.clear(),this.host.registry.discardParked("patter/")}release(e){for(let r of this.flowsById.values())r.releaseBags(e),r.close();this.flowsById.clear();let t=this.host.registry;for(let r of this.host.stageBags.keys())t.remove(S.stage(r),{keep:e});this.host.stageBags.clear();for(let r of["patter",...this.host.hostScopes])t.has(r)&&t.remove(r,{keep:e});for(let r of Object.keys(Te(this.creationOptions)))t.has(r)&&t.remove(r)}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.registry.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e),i=this.host.journal?this.host.registry.get(r,s):void 0;this.host.registry.set(r,s,t,{host:!0}),this.host.journal&&i!==void 0&&this.host.journal.undo.push(()=>this.host.registry.set(r,s,i,{host:!0}))}log(){return this.engineLog}clearLog(){this.engineLog.length=0}onTrace(e){return this.engineTraceHandlers.add(e),this.host.tracing=!0,()=>{this.engineTraceHandlers.delete(e),this.host.tracing=this.host.logEnabled||this.engineTraceHandlers.size>0}}emitEngine(e,t,r){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineSeq++,...r?{scene:r}:{}})}getVisitCounts(){return Object.fromEntries(this.host.sharedVisits)}listProperties(){return this.host.patterSharedDecls.map(e=>({name:e.name,path:`@patter.${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:B(e),writable:e.writable??!0}))}splitShared(e){let t=Le(this.host,e);if(t.scope==="scene")throw new Error(`\'${e}\': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);return t}save(){return{patter:this.host.patterBag.save()}}load(e){e.patter&&this.host.patterBag.load(e.patter)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:F,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:Ve(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==F)throw new Error(`unsupported save version: ${String(t)}`);let r=e.flows;if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("malformed save: no flows");this.assertExternalScopes();let s=this.host.registry;for(let[o,a]of this.flowsById)a.releaseBags(o in e.flows),a.close();this.flowsById.clear();for(let o of this.host.stageBags.keys())s.remove(S.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.version===2?mt(e):e.registry;i&&(this.host.ownsRegistry?s.load(i):s.load(i,{keepParked:!0})),this.host.sharedVisits.clear();for(let[o,a]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(o,a);this.host.sharedSelectors.clear();for(let[o,a]of je(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new D(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}}},D=class{id;host;local;registered=new Set;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPrompt=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.patterBag.get(e):this.local.get(e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.registry.set("patter",e,t):this.local.set(e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;if(t!==null)return this.sceneBagFor(t,e)?.get(e)},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.sceneBagFor(r,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,r){this.id=e,this.host=t,this.rngState=O(r),this.local=this.newLocal(),this.evalCtx={scopes:{},host:{nextRandom:this.rng,visits:s=>this.visitCounts.get(s)??0,patterVisits:s=>this.host.sharedVisits.get(s)??0},qualities:(s,i)=>this.stagesFor(s,i)}}context(){let e=this.host.registry;if(e.revision!==this.ctxRevision){let t=e.toEvalContext(),r=this.evalCtx.scopes;for(let s of Object.keys(r))delete r[s];Object.assign(r,t.scopes),r.patter=this.patterResolver,r.scene=this.sceneResolver,this.registryQualities=t.qualities,this.ctxRevision=e.revision}return this.evalCtx}stagesFor(e,t){let r=t.toLowerCase();if(e==="patter")return ne(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(r);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:ne(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(r)}return this.registryQualities?.(e,t)??ne(this.host,`host/${e}`,()=>this.host.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(r)}begin(e,t){if(this.host.journal&&!this.host.journal.opened.has(this))throw new Error("a flow can\'t be started or reset while a checkpoint is open");if(this.releaseBags(!1),this.host.registry.discardParked(S.flow(this.id)),this.mountLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.activeSnippet=null,this.beatIndex=0,this.clearPending(),this.started=!0,t){let r=this.host.blockIndex.get(t);if(!r)throw new Error(`unknown block: ${t}`);this.enterSceneSetup(r.sceneId),this.stack=[{sceneId:r.sceneId,containerId:t,index:0}],this.enter(t)}else{let r=e??Object.keys(this.host.bundle.scenes)[0],s=r?this.host.bundle.scenes[r]:void 0;if(!s)throw new Error(r?`unknown scene: ${r}`:"no scenes in bundle");this.enterSceneSetup(r);let i=s.blocks[0];i&&(this.stack=[{sceneId:r,containerId:i.id,index:0}],this.enter(i.id))}this.settle()}reset(e,t){this.begin(e,t)}start(e,t){this.reset(e,t)}goto(e,t){if(this.closed)return!1;if(this.touch(),e==="END")return this.started=!0,this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let r=se(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=ie(this.host,r,t),s===void 0)?!1:this.started?(this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??r,"jump"),this.settle(),!0):(this.begin(r,s),!0)}close(){this.releaseBags(!1),this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.clearPending()}clearPending(){this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null}touch(){let e=this.host.journal;if(!e||e.flows.has(this))return;e.flows.add(this);let{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p}=this,f=this.stack.map(g=>({...g}));e.undo.push(()=>{Object.assign(this,{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p,stack:f})})}get isClosed(){return this.closed}get currentScene(){return this.currentSceneId}advance(){if(this.closed)return{type:"end"};if(!this.started)throw new Error("flow has not been started");if(this.touch(),this.pendingPrompt){let e=this.pendingPrompt;return this.pendingPrompt=null,this.pendingPromptOwnerId=null,e.shown?this.promptResult(e.beat,e.shown):this.beatResult(e.beat)}return this.settle(),this.flowEnded?{type:"end"}:this.pendingChoice?{type:"choice",groupId:this.pendingChoice.groupId,options:this.pendingChoice.options}:this.activeSnippet?this.beatResult(this.activeSnippet.beats[this.beatIndex++]):(this.flowEnded=!0,{type:"end"})}advanceToStop(){let e=[];for(;;){let t=this.advance();if(t.type==="choice"||t.type==="end")return{played:e,stop:t};e.push(t)}}settle(){let e=0;for(;;){if(++e>1e4)throw new Error("flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content");if(this.flowEnded||this.pendingChoice)return;if(this.activeSnippet){if(this.beatIndex<(this.activeSnippet.beats?.length??0))return;this.runEffects(this.activeSnippet.onExit,this.activeSnippet.id);let i=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(i);continue}let t=this.stack[this.stack.length-1];if(!t){this.flowEnded=!0;return}t.sceneId!==this.currentSceneId&&(this.currentSceneId=t.sceneId);let r=this.childrenOf(t.containerId);if(!r){this.stack.pop();continue}let s=t.index;for(;t.index<r.length&&!this.eligible(r[t.index]);)t.index++;if(this.host.tracing&&t.index!==s&&this.emit({type:"select",group:t.containerId,selector:"run",children:r.slice(s,t.index+1).map((i,o)=>({id:i.id,eligible:s+o===t.index})),picked:r[t.index]?.id??null}),t.index>=r.length){this.stack.pop();continue}this.enterChild(r[t.index++])}}listBags(){let e=[{bag:this.local,pathPrefix:`${this.id}/@patter.`}];for(let[t,r]of this.sceneBags)e.push({bag:r,pathPrefix:`${this.id}/@scene:${t}.`});return e}log(){return this.flowLog}clearLog(){this.flowLog.length=0}emit(e){let t=this.currentSceneId??void 0;this.host.emitEngine(this.id,e,t),this.host.logEnabled&&this.flowLog.push({...e,seq:this.flowSeq++,...t?{scene:t}:{}})}getVisitCounts(){return Object.fromEntries(this.visitCounts)}getChoices(){return this.pendingChoice?.options??[]}choose(e){let t=this.pendingChoice;if(!t)throw new Error("no choice is pending");let r=t.options.find(o=>o.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);this.touch();let s=t.byId.get(e);this.host.tracing&&this.emit({type:"chose",group:t.groupId,option:e}),this.pendingChoice=null;let i=this.host.replayPromptOnChoose?this.authoredPromptOf(s):void 0;this.pendingPrompt=i&&r.prompt?{beat:i,shown:{...r.prompt}}:null,this.pendingPromptOwnerId=this.pendingPrompt?s.id:null,this.enterChild(s)}isEnded(){return this.flowEnded}getProperty(e){let{scope:t,name:r}=this.splitRef(e);return t==="patter"?this.patterResolver.get(r):t==="scene"?this.sceneResolver.get(r):this.host.registry.get(t,r)}setProperty(e,t){this.writeProperty(e,t,!0)}writeProperty(e,t,r){let{scope:s,name:i}=this.splitRef(e),o=this.host.journal;if(s==="patter"){if(o){let a=this.host.patterSharedNames.has(i),c=a?this.host.patterBag.get(i):this.local.get(i),l=this.local;c!==void 0&&o.undo.push(a?()=>this.host.registry.set("patter",i,c):()=>l.set(i,c))}this.patterResolver.set(i,t)}else if(s==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);if(o){let a=this.sceneBagFor(this.currentSceneId,i),c=a?.get(i);a&&c!==void 0&&o.undo.push(()=>a.set(i,c))}this.sceneResolver.set(i,t)}else{let a=o?this.host.registry.get(s,i):void 0;this.host.registry.set(s,i,t,r?{host:!0}:void 0),o&&a!==void 0&&o.undo.push(()=>this.host.registry.set(s,i,a,{host:!0}))}}snapshot(){return{rngState:this.rngState,visits:Object.fromEntries(this.visitCounts),cursor:{flowEnded:this.flowEnded,currentSceneId:this.currentSceneId,stack:this.stack.map(e=>{let t=this.childrenOf(e.containerId)?.[e.index];return t?{...e,nextId:t.id}:{...e}}),activeSnippetId:this.activeSnippet?.id??null,beatIndex:this.beatIndex,pendingChoice:this.pendingChoice?{groupId:this.pendingChoice.groupId,options:this.pendingChoice.options.map(e=>({...e}))}:null,pendingPromptOwnerId:this.pendingPromptOwnerId,...this.pendingPrompt?.shown?{pendingPrompt:ft(this.pendingPrompt.shown)}:{},selectors:Ve(this.selectors)}}}restore(e){if(this.host.journal)throw new Error("a flow can\'t be restored while a checkpoint is open");this.restoreCursor(e),this.releaseBags(!0),this.mountLocal();for(let t of new Set([this.currentSceneId,...this.stack.map(r=>r.sceneId)]))t!==null&&this.host.bundle.scenes[t]&&this.ensureSceneBags(t)}restoreCursor(e){this.rngState=O(e.rngState),this.visitCounts=new Map(Object.entries(e.visits??{}));let t=e.cursor;if(this.started=!0,this.flowEnded=t.flowEnded,this.beatIndex=t.beatIndex,this.currentSceneId=t.currentSceneId,this.stack=t.stack.map(r=>{let{nextId:s,...i}=r;if(s!==void 0){let o=this.childrenOf(i.containerId)?.findIndex(a=>a.id===s)??-1;if(o>=0)return{...i,index:o}}return{...i}}),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=je(t.selectors),this.pendingChoice=null,t.pendingChoice!==null){let r=new Map,s=[];for(let i of t.pendingChoice.options){let o=this.host.nodeIndex.get(i.id);if(!o)continue;r.set(i.id,o);let{prompt:a,...c}=i;s.push({...c,...a?{prompt:{...a,padAfter:a.padAfter??this.promptPadOf(o)}}:{}})}s.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:s,byId:r})}if(this.pendingPrompt=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId),s=r?this.authoredPromptOf(r):void 0;s?this.pendingPrompt={beat:s,...t.pendingPrompt?{shown:{...t.pendingPrompt,padAfter:t.pendingPrompt.padAfter??this.padOf(s.id)}}:{}}:this.pendingPromptOwnerId=null}}enterSceneSetup(e){let t=this.host.bundle.scenes[e];if(!t)throw new Error(`unknown scene: ${e}`);this.currentSceneId=e,this.enter(e),this.seedScene(t),this.runEffects(t.onEntry,t.id)}enterChild(e){if(this.enter(e.id),e.type==="snippet"){this.beginSnippet(e);return}let t=e.selector??"run";if(t==="run"){this.stack.push({sceneId:this.currentSceneId,containerId:e.id,index:0});return}if(t==="choice"){this.setupChoice(e);return}let r=this.selectChild(e);r&&this.enterChild(r)}childrenOf(e){let t=this.host.blockById.get(e);if(t)return t.children;let r=this.host.nodeIndex.get(e);if(r&&r.type==="group")return r.children}beginSnippet(e){this.runEffects(e.onEnter,e.id),this.activeSnippet=e,this.beatIndex=0}setupChoice(e){let t=[],r=new Map,s=[];for(let o of e.children){if(o.fallback===!0){s.push(o);continue}if(o.sticky!==!0&&(this.visitCounts.get(o.id)??0)>=1)continue;let a=this.eligible(o),c=o.secretUntilEligible===!0;!a&&c||(t.push({id:o.id,prompt:this.promptFor(o),eligible:a,gameData:o.gameData}),r.set(o.id,o))}if(t.some(o=>o.eligible)){this.host.tracing&&this.emit({type:"choice",group:e.id,options:t.map(o=>({id:o.id,eligible:o.eligible}))}),this.pendingChoice={groupId:e.id,options:t,byId:r};return}let i=s.find(o=>this.eligible(o));if(i){this.enterChild(i);return}this.host.tracing&&this.emit({type:"dry",group:e.id}),this.host.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(this.host.tracing&&this.emit({type:"jump",to:e,mode:t}),e==="END"){this.flowEnded=!0,this.stack=[];return}let r,s,i=this.host.bundle.scenes[e];if(i){this.enterSceneSetup(e);let a=i.blocks[0];if(!a){t==="jump"&&(this.stack=[]);return}r=e,s=a.id}else{let a=this.host.blockIndex.get(e);if(!a)throw new Error(`jump target not found: ${e}`);a.sceneId!==this.currentSceneId&&this.enterSceneSetup(a.sceneId),r=a.sceneId,s=e}this.enter(s);let o={sceneId:r,containerId:s,index:0};t==="call"?this.stack.push(o):this.stack=[o]}selectChild(e){let t=e.children.map(c=>({id:c.id,eligible:this.eligible(c)})),r=e.children.filter((c,l)=>t[l].eligible),s=e.options?.order??"sequential",i=e.options?.exhaust??"once",o=c=>(this.host.tracing&&this.emit({type:"select",group:e.id,selector:e.selector??"default",...e.selector==="sequence"?{order:s,exhaust:i}:{},children:t,picked:c?.id??null}),c);if(r.length===0)return o(null);let a=this.selectorState(e);switch(e.selector){case"branch":return o(r[0]);case"sequence":return o(s==="shuffle"?this.pickShuffle(r,i,a):s==="specificity"?this.pickSpecificity(r,i,a):this.pickSequential(r,i,a));default:return null}}pickSequential(e,t,r){let s=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,r){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(g=>g.id),a=new Set(e.map(g=>g.id));if(r.bag===void 0&&(r.bag=o()),!r.bag.some(g=>a.has(g))){if(t==="once")return null;if(i){let g=e[s-1];return r.last=g.id,g}if(r.bag=o(),r.bag.length===0)return null}let c=r.bag.filter(g=>a.has(g)),l=r.last!==void 0&&c.length>1?c.indexOf(r.last):-1,p=Math.floor(this.rng()*(l>=0?c.length-1:c.length));l>=0&&p>=l&&p++;let f=c[p];return r.bag.splice(r.bag.indexOf(f),1),r.last=f,e.find(g=>g.id===f)}pickSpecificity(e,t,r){let s=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(p=>p.id));let l=new Set(r.bag);if(s=e.filter(p=>l.has(p.id)),s.length===0)return t==="stick"&&r.last!==void 0?e.find(p=>p.id===r.last)??null:null}let i=-1,a=s.map(l=>{let p=this.specScore(l);return p>i&&(i=p),{c:l,s:p}}).filter(l=>l.s===i).map(l=>l.c),c;if(a.length===1)c=a[0];else{let l=r.last!==void 0?a.findIndex(f=>f.id===r.last):-1,p=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&p>=l&&p++,c=a[p]}return t!=="repeat"&&(r.bag=r.bag.filter(l=>l!==c.id)),r.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0,e):0}matchedSpec(e,t,r){return he(e,i=>{try{return Ge(q(i,this.context(),Z))}catch(o){return this.reportError("best-match",r.id,r.condition,o),!1}},{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=this.host.journal,s;if(r&&(s=e.shared?r.selectors:r.flowSelectors.get(this),s||(s=new Set,r.flowSelectors.set(this,s))),r&&s&&!s.has(e.id)){s.add(e.id);let o=t.get(e.id),a=o?{...o,...o.bag?{bag:[...o.bag]}:{}}:void 0;r.undo.push(()=>{a?t.set(e.id,a):t.delete(e.id)})}let i=t.get(e.id);return i||(i={},t.set(e.id,i)),i}runEffects(e,t){for(let r of e??[]){let s;try{s=this.evalExpr(r.value);let i=this.host.tracing?this.getProperty(r.target):void 0;this.writeProperty(r.target,s,!1),this.host.tracing&&this.emit({type:"write",target:r.target,value:s,...i!==void 0?{prev:i}:{}})}catch(i){this.reportError("effect",t,r.value,i)}}}eligible(e){if(!e.condition)return!0;try{return Ge(this.evalExpr(e.condition))}catch(t){return this.reportError("condition",e.id,e.condition,t),!1}}reportError(e,t,r,s){let i=s instanceof Error?s.message:String(s),o=r?.src;this.host.onError({flow:this.id,kind:e,node:t,...o?{source:o}:{},message:i}),this.host.tracing&&this.emit({type:"diagnostic",kind:e,node:t,...o?{source:o}:{},message:i})}evalExpr(e){return q(this.conditionAst(e),this.context(),Z)}conditionAst(e){let t=De.get(e);return t||(t=E(e.ast),De.set(e,t)),t}enter(e){let t=this.visitCounts,r=t.get(e);t.set(e,(r??0)+1);let s=this.host.sharedVisits,i=s.get(e);s.set(e,(i??0)+1),this.host.journal&&this.host.journal.undo.push(()=>{r===void 0?t.delete(e):t.set(e,r),i===void 0?s.delete(e):s.set(e,i)})}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=Y(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=I(this.host,e.id),r=t&&t.length?{tags:t}:{};switch(e.kind){case"gameEvent":return{type:"gameEvent",id:e.id,gameData:e.gameData,...r};case"text":return{type:"text",id:e.id,text:this.interpolate(this.resolveString(e.id)),padAfter:this.padOf(e.id),gameData:e.gameData,...r};case"line":{let s=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?s:this.interpolate(s)),c=i&&a.length===0;return{type:"line",id:e.id,text:a,character:c?void 0:e.character,characterName:c?void 0:this.resolveCharacterName(e.character),direction:c?void 0:e.direction,qualifier:c?void 0:e.qualifier,qualifierName:c?void 0:this.resolveQualifierName(e.qualifier),padAfter:this.padOf(e.id),gameData:e.gameData,...r}}}}interpolate(e){return ke(e,t=>this.getProperty(t))}stripCaptions(e){return xe(e,this.host.captionOpen,this.host.captionClose)}captionLine(e){return this.host.captionsOn?e:this.stripCaptions(e)}promptFor(e){let t=this.promptBeatOf(e);if(!t)return;let r=this.interpolate(this.resolveString(t.id)),s=this.padOf(t.id);return t.kind==="line"?{kind:"line",text:this.captionLine(r),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction,qualifier:t.qualifier,qualifierName:this.resolveQualifierName(t.qualifier),padAfter:s}:{kind:"text",text:r,padAfter:s}}authoredPromptOf(e){return e.type==="group"?e.prompt:void 0}promptResult(e,t){let r=I(this.host,e.id),s=r&&r.length?{tags:r}:{};return t.kind==="text"?{type:"text",id:e.id,text:t.text,padAfter:this.padOf(e.id),gameData:e.gameData,...s}:{type:"line",id:e.id,text:t.text,character:t.character,characterName:t.characterName,direction:t.direction,qualifier:t.qualifier,qualifierName:t.qualifierName,padAfter:this.padOf(e.id),gameData:e.gameData,...s}}promptBeatOf(e){return e.type==="group"&&e.prompt?e.prompt:((e.type==="snippet"?e:this.firstTextSnippetIn(e.children))?.beats??[]).find(r=>r.kind==="line"||r.kind==="text")}firstTextSnippetIn(e){let t;return R(e,r=>{!t&&r.type==="snippet"&&(r.beats??[]).some(s=>s.kind==="line"||s.kind==="text")&&(t=r)}),t}resolveString(e){if(this.host.emitIds)return e;let t=this.host.strings[e];if(t!==void 0)return t;let r=this.host.defaultStrings[e];return r!==void 0?`<Untranslated: ${e}> ${r}`:e}resolveCharacterName(e){if(e===void 0||this.host.emitIds)return;let t=ee(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}promptPadOf(e){let t=this.promptBeatOf(e);return t?this.padOf(t.id):x}padOf(e){return this.host.padIndex.get(e)?.resolved??x}resolveQualifierName(e){if(e===void 0||this.host.emitIds)return;let t=te(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.qualifierDisplay.get(e)}splitRef(e){return Le(this.host,e)}newLocal(){return new b(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=S.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:V}),this.registered.add(e)}releaseBags(e){for(let t of this.registered)this.host.registry.remove(t,{keep:e});this.registered.clear(),this.sceneBags.clear()}sceneBagFor(e,t){if(this.host.bundle.scenes[e])return this.ensureSceneBags(e),this.host.sceneSharedNames.get(e)?.has(t.toLowerCase())?this.host.stageBags.get(e):this.sceneBags.get(e)}ensureSceneBags(e){let t=this.host.sceneSharedNames.get(e)??new Set,r=this.host.bundle.scenes[e]?.sceneProps??[],s=this.host.journal;if(!this.sceneBags.has(e)){let i=new b(r.filter(c=>!t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),o=S.flowScene(this.id,e),a=Fe(this.host.registry,o,i,s!==null);this.registered.add(o),this.sceneBags.set(e,i),s&&s.undo.push(()=>{this.sceneBags.get(e)===i&&(this.host.registry.remove(o,a?{keep:!0}:void 0),this.registered.delete(o),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let i=new b(r.filter(a=>t.has(a.name.toLowerCase())),{pathPrefix:"@scene."}),o=Fe(this.host.registry,S.stage(e),i,s!==null);this.host.stageBags.set(e,i),s&&s.undo.push(()=>{this.host.stageBags.get(e)===i&&(this.host.registry.remove(S.stage(e),o?{keep:!0}:void 0),this.host.stageBags.delete(e))})}}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;this.ensureSceneBags(e.id);for(let r of e.sceneProps??[]){if(!r.temporary)continue;let s=r.name.toLowerCase(),i=t.has(s)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);if(!i)continue;let o=this.host.journal?i.get(s):void 0;o!==void 0&&this.host.journal.undo.push(()=>i.set(s,o)),i.set(s,B(r))}}};function Be(n,e){R(n,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let r of t.beats??[])r.kind==="line"&&r.character&&e.add(r.character)})}function Ae(n){return n&&Object.keys(n).length?{gameData:n}:{}}function ft(n){let e={kind:n.kind,text:n.text};return n.character!==void 0&&(e.character=n.character),n.characterName!==void 0&&(e.characterName=n.characterName),n.direction!==void 0&&(e.direction=n.direction),n.qualifier!==void 0&&(e.qualifier=n.qualifier),n.qualifierName!==void 0&&(e.qualifierName=n.qualifierName),typeof n.padAfter=="number"&&(e.padAfter=n.padAfter),e}function Ve(n){let e={};for(let[t,r]of n){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e[t]=s}return e}function je(n){let e=new Map;for(let[t,r]of Object.entries(n??{})){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e.set(t,s)}return e}function Fe(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:V}),r&&JSON.stringify(t.save())!==s}function Ne(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Te(n){return{...n.world?{world:n.world}:{},...n.hostScopes}}function ne(n,e,t){let r=n.qualityLadders.get(e);if(!r){r=new Map;for(let s of t()??[]){let i=s.name.toLowerCase();s.type==="quality"&&s.stages&&!r.has(i)&&r.set(i,s.stages)}n.qualityLadders.set(e,r)}return r}function Me(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function ht(n,e){let t=Me(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function se(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function ie(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function Le(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=be(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function mt(n){let e={};n.shared?.patter&&(e.patter=n.shared.patter);for(let[t,r]of Object.entries(n.stageBags??{}))e[S.stage(t)]=r;for(let[t,r]of Object.entries(n.flows??{}))Object.assign(e,qe(t,r));return e}function qe(n,e){let t={};e.scopes?.patter&&(t[S.flowGlobals(n)]=e.scopes.patter);for(let[r,s]of Object.entries(e.sceneBags??{}))t[S.flowScene(n,r)]=s;return t}function St(n,e){let t={scopes:{},sceneBags:{}},r=S.flow(n);for(let[s,i]of Object.entries(e)){if(!s.startsWith(r))continue;let o=s.slice(r.length);o==="patter"?t.scopes.patter=i:o.startsWith("scene/")&&(t.sceneBags[gt(o.slice(6))]=i)}return t}function Ge(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var yt=(n,e)=>n.shared??e;function oe(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:yt(n,e)}}function wt(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function vt(n,e){e.blocks++;let t=[...n.children];for(;t.length;){let r=t.pop();if(r.type==="group"){e.groups++,r.prompt&&e.prompts++,t.push(...r.children);continue}e.snippets++;for(let s of r.beats??[])e.beats++,s.kind==="gameEvent"&&e.gameEvents++}}function $e(n){let e={scenes:0,blocks:0,groups:0,snippets:0,beats:0,prompts:0,gameEvents:0,cast:n.cast?.length??0},t=[],r=[];for(let o of Object.values(n.scenes)){e.scenes++;let a=y(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:y(c),name:c.name}))});for(let c of o.blocks)vt(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>oe(c,!1))})}let s=(n.scopeRegistry?.scopes??[]).map(o=>({token:o.token,writable:o.writable??!0,opaque:o.declarations===void 0,properties:(o.declarations??[]).map(a=>oe(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(wt)}));return{identity:{schema:n.schema,project:n.content.project,...n.content.version!==void 0?{version:n.content.version}:{},...n.content.hash!==void 0?{hash:n.content.hash}:{},...n.content.structureHash!==void 0?{structureHash:n.content.structureHash}:{},voiced:n.voiced,defaultLocale:n.locales.default,locales:[...n.locales.included],localisation:n.localisation?.mode??"embedded",sourceDebug:n.localisation?.sourceDebug??!1},addresses:t,hostScopes:s,properties:{patter:(n.properties??[]).map(o=>oe(o,!0)),scene:r},gameData:i,counts:e}}function He(n,e){return n.gameDataFields?.[e]??[]}function ae(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function _e(n,e){let t={};for(let r of n){let s=ae(n,e,r.name);s!==void 0&&(t[r.name]=s)}for(let[r,s]of Object.entries(e??{}))r in t||(t[r]=s);return t}return Ze(bt);})();\n';
1634
+ var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var X=Object.defineProperty;var tt=Object.getOwnPropertyDescriptor;var rt=Object.getOwnPropertyNames;var nt=Object.prototype.hasOwnProperty;var st=(n,e)=>{for(var t in e)X(n,t,{get:e[t],enumerable:!0})},it=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of rt(e))!nt.call(n,s)&&s!==t&&X(n,s,{get:()=>e[s],enumerable:!(r=tt(e,s))||r.enumerable});return n};var ot=n=>it(X({},"__esModule",{value:!0}),n);var Pt={};st(Pt,{Checkpoint:()=>V,DEFAULT_PAD_AFTER:()=>x,Engine:()=>z,Flow:()=>I,buildTagIndex:()=>Q,describeBundle:()=>Qe,effectiveGameData:()=>Ye,gameDataFields:()=>ze,gameDataValue:()=>ce});function R(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 $(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 u(`@${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 u(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=Z(a,e);if(c===void 0)throw new u("advance() needs a quality reference (@scope.name of a quality property)");let l=v(s(a),c,"advance");return c[Math.min(l+1,c.length-1)]}let o=R(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 h=s(i.left);if(typeof h!="boolean")throw new u(`\'and\' requires boolean operands, left is ${typeof h}`);if(!h)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 h=s(i.left);if(typeof h!="boolean")throw new u(`\'or\' requires boolean operands, left is ${typeof h}`);if(h)return!0;let g=s(i.right);if(typeof g!="boolean")throw new u(`\'or\' requires boolean operands, right is ${typeof g}`);return g}let o=s(i.left),a=s(i.right),c=Z(i.left,e),l=Z(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!at(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new u(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return v(o,p,">")>v(a,p,">");case">=":return v(o,p,">=")>=v(a,p,">=");case"<":return v(o,p,"<")<v(a,p,"<");case"<=":return v(o,p,"<=")<=v(a,p,"<=");case"+":case"-":case"*":case"/":throw new u(`\'${i.op}\' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(i.op){case"==":return Se(o,a);case"!=":return!Se(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 Se(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;let t=[...n].sort(),r=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==r[s])return!1;return!0}return n===e}function C(n,e,t){if(typeof n!="number"||typeof e!="number")throw new u(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function Z(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function v(n,e,t){if(typeof n!="string")throw new u(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new u(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var at=(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 ee(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 ct={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},lt=[ct];function ye(n,e,t){let r=t?.countingCalls??lt;return _(n,t?.want??!0,e,r)}function _(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=_(n.left,e,t,r),i=_(n.right,e,t,r);return n.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(n.kind==="unary"&&n.op==="not")return _(n.operand,!e,t,r);if(n.kind==="call"){let s=r.find(i=>i.name===n.name);if(s){let i=s.count(n),o=t(n);return e?o?i:0:o?0:1}}return t(n)===e?1:0}var b=class n{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let r=this.norm(t.name);this.decls.set(r,t),this.values[r]=structuredClone(t.default??B(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,r){let s=this.norm(e);if(!r?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:r?.silent??!1,reason:r?.reason};this.values[s]=t;for(let o of[...this.auditors])o(i);if(!i.silent)for(let o of[...this.subscribers])o(i);return i}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}onAudit(e){return this.auditors.add(e),()=>this.auditors.delete(e)}rows(){return[...this.decls.entries()].map(([e,t])=>ve(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 ve(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 pt=n=>n.toLowerCase(),te=1,H=class{scopes=new Map;parked=new Map;rev=0;get revision(){return this.rev}defineOwned(e,t,r){let s=typeof r=="string"?{pathPrefix:r}:r??{},i=new b(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,i,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,r){this.assertFree(e,r?.owner),this.scopes.set(e,{kind:"owned",bag:t,...r?.owner!==void 0?{owner:r.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(t.load(s),this.parked.delete(e)),this}remove(e,t){let r=this.scopes.get(e);if(!r)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&r.kind==="owned"&&this.parked.set(e,r.bag.save()),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&this.parked.delete(t);return this}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??pt,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,...ve(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(dt(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 we(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=we(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:te,scopes:this.save()}}loadFragment(e){if(e.version!==te)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${te})`);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 we(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function dt(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 be=[{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 re={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:ke("check_flags"),eval(n,e){let t=xe(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:ke("set_flags"),eval(n,e){let t=[...xe(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:W("visits"),eval:(n,e)=>A(e).visits?.(U(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:W("seen"),eval:(n,e)=>(A(e).visits?.(U(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:W("patter_visits"),eval:(n,e)=>A(e).patterVisits?.(U(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:W("patter_seen"),eval:(n,e)=>(A(e).patterVisits?.(U(n,e,"patter_seen"))??0)>0}}},Wt=be.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function Ee(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 U(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 W(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 xe(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 ke(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 ut=/^@[A-Za-z0-9_.]+$/;function*gt(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:ut.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 ft(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function ht(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function mt(n){let e="",t=!1;for(let r of n){if(ht(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function Ce(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?mt(r):n}function De(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of gt(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?"":ft(s)}return t}function P(n,e){for(let t of n){e(t);let r=t.children;r&&P(r,e)}}function St(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function w(n){let e=n.gameId?.trim();return e||St(n.name)}var x=.6;function K(n){return`cast:${n}`}function J(n){return`qualifier:${n}`}function Ie(n,e){return`speaker:${n}:${e}`}var Pe="speaker:pattern",Re="{name} ({qualifier})";function Oe(n,e,t){return n.split("{name}").map(r=>r.split("{qualifier}").join(t)).join(e)}var ne={open:"[",close:"]"},Be="SFX";var N=3;function F(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function Q(n){let e=new Map,t=(r,s)=>{let i=F([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group"&&r.prompt&&e.set(r.prompt.id,F([...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,F([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=F(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=F([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var Ae=n=>n.kind==="line"||n.kind==="text",T=n=>typeof n=="number"&&Number.isFinite(n)?n:void 0;function Ne(n){let e=new Map,t=(i,o,a)=>{let c=i.kind==="gameEvent"?void 0:T(i.padAfter),l=c??o;a&&l<0&&(l=0),e.set(i.id,c!==void 0?{own:c,resolved:l}:{resolved:l})},r=(i,o)=>{let a=T(i.padAfterDefault)??o;if(i.type==="group"){i.prompt&&t(i.prompt,a,!1);for(let p of i.children)r(p,a);return}let c=i.beats??[],l=-1;c.forEach((p,h)=>{Ae(p)&&(l=h)}),c.forEach((p,h)=>{Ae(p)&&t(p,a,h===l)})},s=T(n.padAfterDefault)??x;for(let i of Object.values(n.scenes)){let o=T(i.padAfterDefault)??s;for(let a of i.blocks){let c=T(a.padAfterDefault)??o;for(let l of a.children)r(l,c)}}return e}var Fe=new WeakMap,Te=new WeakMap;function yt(n){let e=Te.get(n);return e||(e=Q(n),Te.set(n,e)),e}var Le=new WeakMap;function wt(n){let e=Le.get(n);return e||(e=Ne(n),Le.set(n,e)),e}function D(n,e){return n.tagIndex.get(e)?.slice()}var j="Patter",L=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),vt=n=>n.replace(/%2F/g,"/").replace(/%25/g,"%"),Ge=Symbol("ownsRegistry"),y={stage:n=>`patter/scene/${L(n)}`,flow:n=>`patter/flow/${L(n)}/`,flowGlobals:n=>`patter/flow/${L(n)}/patter`,flowScene:(n,e)=>`patter/flow/${L(n)}/scene/${L(e)}`},V=class{constructor(e){this.journal=e}journal},z=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);let h=new Map;for(let d of e.cast??[])for(let[f,S]of Object.entries(d.qualifierLabels??{}))typeof S=="string"&&h.set(`${d.name}\\0${f}`,S);let g=e.speakerPattern??Re;this.defaultSeed=(t.seed??2654435769)>>>0;let le=new Map,pe=new Map,de=new Map;for(let[d,f]of Object.entries(e.scenes)){this.sceneGameIdToId.set(w(f),d);let S=new Map;for(let m of f.blocks)pe.set(m.id,{sceneId:d}),de.set(m.id,m),S.set(w(m),m.id),P(m.children,q=>le.set(q.id,q));this.blockGameIdToId.set(d,S)}let ue=e.properties??[],Y=ue.filter(d=>d.shared??!0).map(qe),Xe=ue.filter(d=>!(d.shared??!0)).map(qe),Ze=new Set(Y.map(d=>d.name.toLowerCase())),k=t.registry??new H,ge=!t.registry||t[Ge]===!0,fe=new b(Y,{pathPrefix:"@patter."}),he=[],M=[];try{k.mountOwned("patter",fe,{owner:j}),M.push("patter");let d=new Set;for(let[f,S]of Object.entries($e(t))){let m=e.scopeRegistry?.scopes.find(et=>et.token===f),q=(m?.declarations??[]).map(We);k.defineForeign(f,S,q,{writable:m?.writable??!0,owner:j}),M.push(f),d.add(f)}for(let f of ge?e.scopeRegistry?.scopes??[]:[]){if(d.has(f.token)||k.has(f.token))continue;let S=(f.declarations??[]).map(m=>xt(m,f.writable));k.defineOwned(f.token,S,{owner:j}),M.push(f.token),he.push(f.token)}}catch(d){for(let f of M)k.remove(f,{keep:!0});throw d}let me=new Map;for(let[d,f]of Object.entries(e.scenes)){let S=new Set((f.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));me.set(d,S)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(d,f,S)=>this.emitEngine(d,f,S),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:p,speakerLabels:h,speakerPattern:g,nodeIndex:le,blockIndex:pe,blockById:de,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:yt(e),padIndex:wt(e),registry:k,ownsRegistry:ge,patterBag:fe,hostScopes:he,patterSharedDecls:Y,patterLocalDecls:Xe,patterSharedNames:Ze,sceneSharedNames:me,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??ne).open,captionClose:(e.closedCaptions??ne).close,captionCharacter:e.closedCaptions?.character||Be,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,[Ge]: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 I(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=ie(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=oe(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 ie(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:oe(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?w(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?w(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)je(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 je(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...w(e)?{gameId:w(e)}:{},name:e.name,...Ve(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...w(t)?{gameId:w(t)}:{},name:t.name,...Ve(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)P(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 i=this.host.defaultStrings[K(e.character)]??this.host.castDisplay.get(e.character);i!==void 0&&(r.characterName=i)}if(e.direction!==void 0&&(r.direction=e.direction),e.qualifier!==void 0){r.qualifier=e.qualifier;let i=this.host.defaultStrings[J(e.qualifier)]??this.host.qualifierDisplay.get(e.qualifier);i!==void 0&&(r.qualifierName=i)}let s=G(this.host,e.character,e.qualifier,!0);s!==void 0&&(r.speakerLabel=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 V(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let r=t.length-1;r>=0;r--)t[r]();n.forget(e.journal)}commit(e){this.endCheckpoint(e),n.forget(e.journal)}get inCheckpoint(){return this.host.journal!==null}endCheckpoint(e){if(this.host.journal!==e.journal)throw new Error("that checkpoint is not the open one");this.host.journal=null}static forget(e){e.undo=[],e.flows.clear(),e.opened.clear(),e.selectors.clear(),e.flowSelectors.clear()}refuseInCheckpoint(e){if(this.host.journal)throw new Error(`${e} can\'t be undone, so it isn\'t allowed while a checkpoint is open`)}captureFlow(e){let t=this.flowsById.get(e);if(!t)throw new Error(`no open flow \'${e}\' to capture`);return{version:N,...t.snapshot(),...Et(e,this.host.registry.save())}}restoreFlow(e,t){this.refuseInCheckpoint("restoreFlow");let r=t.version;if(r!==2&&r!==N)throw new Error(`unsupported flow state version: ${String(r)}`);let s=t.cursor;if(typeof s!="object"||s===null)throw new Error("malformed flow state: no cursor");if(this.flowsById.has(e))throw new Error(`a flow \'${e}\' is already open; close it before restoring another under that id`);this.assertExternalScopes();let i=y.flow(e);this.host.registry.discardParked(i),this.host.registry.load(Je(e,t),{keepParked:!0});let o=new I(e,this.host,this.defaultSeed);try{o.restore(t)}catch(a){throw o.close(),this.host.registry.discardParked(i),a}return this.flowsById.set(e,o),o}closeFlow(e){this.refuseInCheckpoint("closeFlow"),this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){this.refuseInCheckpoint("reset");for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.patterBag.reseed(this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear();for(let e of this.host.stageBags.keys())this.host.registry.remove(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($e(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=Ke(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:N,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:He(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==N)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?kt(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 Ue(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new I(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}}},I=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 se(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(r);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:se(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(r)}return this.registryQualities?.(e,t)??se(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=ie(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=oe(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,h=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:h})})}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:bt(this.pendingPrompt.shown)}:{},selectors:He(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=Ue(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,l=a?.kind==="line"&&a.speakerLabel===void 0?G(this.host,a.character,a.qualifier):void 0;s.push({...c,...a?{prompt:{...a,padAfter:a.padAfter??this.promptPadOf(o),...l!==void 0?{speakerLabel:l}:{}}}:{}})}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,i=t.pendingPrompt,o=i?.kind==="line"&&i.speakerLabel===void 0?G(this.host,i.character,i.qualifier):void 0;s?this.pendingPrompt={beat:s,...i?{shown:{...i,padAfter:i.padAfter??this.padOf(s.id),...o!==void 0?{speakerLabel:o}:{}}}:{}}: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 h=c[p];return r.bag.splice(r.bag.indexOf(h),1),r.last=h,e.find(g=>g.id===h)}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(h=>h.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 ye(e,i=>{try{return _e($(i,this.context(),re))}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 _e(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 $(this.conditionAst(e),this.context(),re)}conditionAst(e){let t=Fe.get(e);return t||(t=E(e.ast),Fe.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=ee(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),speakerLabel:c?void 0:G(this.host,e.character,e.qualifier),padAfter:this.padOf(e.id),gameData:e.gameData,...r}}}}interpolate(e){return De(e,t=>this.getProperty(t))}stripCaptions(e){return Ce(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),speakerLabel:G(this.host,t.character,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,speakerLabel:t.speakerLabel,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 P(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=K(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=J(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.qualifierDisplay.get(e)}splitRef(e){return Ke(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:j}),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=Me(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=Me(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 je(n,e){P(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 G(n,e,t,r=!1){if(!e||n.emitIds)return;let s=c=>(r?void 0:n.strings[c])??n.defaultStrings[c],i=s(K(e))??n.castDisplay.get(e)??e;if(t===void 0)return i;let o=n.speakerLabels.get(`${e}\\0${t}`);if(o!==void 0)return s(Ie(e,t))??o;let a=s(J(t))??n.qualifierDisplay.get(t)??t;return Oe(s(Pe)??n.speakerPattern,i,a)}function Ve(n){return n&&Object.keys(n).length?{gameData:n}:{}}function bt(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),n.speakerLabel!==void 0&&(e.speakerLabel=n.speakerLabel),typeof n.padAfter=="number"&&(e.padAfter=n.padAfter),e}function He(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 Ue(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 Me(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:j}),r&&JSON.stringify(t.save())!==s}function qe(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function $e(n){return{...n.world?{world:n.world}:{},...n.hostScopes}}function se(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 We(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function xt(n,e){let t=We(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function ie(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function oe(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function Ke(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=Ee(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function kt(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??{}))Object.assign(e,Je(t,r));return e}function Je(n,e){let t={};e.scopes?.patter&&(t[y.flowGlobals(n)]=e.scopes.patter);for(let[r,s]of Object.entries(e.sceneBags??{}))t[y.flowScene(n,r)]=s;return t}function Et(n,e){let t={scopes:{},sceneBags:{}},r=y.flow(n);for(let[s,i]of Object.entries(e)){if(!s.startsWith(r))continue;let o=s.slice(r.length);o==="patter"?t.scopes.patter=i:o.startsWith("scene/")&&(t.sceneBags[vt(o.slice(6))]=i)}return t}function _e(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var Ct=(n,e)=>n.shared??e;function ae(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:Ct(n,e)}}function Dt(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function It(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=w(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:w(c),name:c.name}))});for(let c of o.blocks)It(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>ae(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=>ae(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(Dt)}));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=>ae(o,!0)),scene:r},gameData:i,counts:e}}function ze(n,e){return n.gameDataFields?.[e]??[]}function ce(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function Ye(n,e){let t={};for(let r of n){let s=ce(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 ot(Pt);})();\n';
1620
1635
  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';
1621
1636
 
1622
1637
  // src/play-timeline.ts
@@ -1759,10 +1774,10 @@ var PLAYER_JS = String.raw`
1759
1774
  if (s.type === "choice") { renderChoice(s); return; }
1760
1775
  timeline.place(s, estimate(s.text));
1761
1776
  if (s.type === "line") {
1762
- var who = s.characterName || s.character || "";
1763
- // A speaker qualifier follows the name, as the script's cue has it: TAM (O.S.).
1764
- var qual = s.qualifierName || s.qualifier;
1765
- if (qual && who) who += " (" + qual + ")";
1777
+ // The speaker's label as the runtime resolved it (TAM (O.S.), or a pair's own label): this page never
1778
+ // puts a name and a qualifier together itself. (Its bundle always carries the strings, so a line with a
1779
+ // speaker always has one.)
1780
+ var who = s.speakerLabel || s.characterName || s.character || "";
1766
1781
  var dir = s.direction ? '<em class="dir">(' + esc(s.direction) + ')</em> ' : "";
1767
1782
  add("line", '<span class="who" style="color:hsl(' + hueOf(s.character || who) + ',55%,38%)">' + esc(who) + '</span>' + dir + esc(s.text));
1768
1783
  } else {
@@ -2981,7 +2996,21 @@ async function runReportXlsx(data) {
2981
2996
  // src/localisation.ts
2982
2997
  import { basename as basename5, join as join7, sep as sep3 } from "path";
2983
2998
  import { canonicalStringify as canonicalStringify6 } from "@patterkit/core";
2984
- import { walkNodes as walkNodes2, castStringKey, qualifierStringKey, usedQualifiers, projectQualifiers, PROJECT_LOCALE_SCENE as PROJECT_LOCALE_SCENE2, projectLayout as projectLayout3, STRINGS_SCHEMA, AUTHORING_SCHEMA } from "@patterkit/model";
2999
+ import {
3000
+ walkNodes as walkNodes2,
3001
+ castStringKey,
3002
+ qualifierStringKey,
3003
+ speakerLabelKey,
3004
+ usedQualifiers,
3005
+ usedSpeakerPairs,
3006
+ projectQualifiers,
3007
+ SPEAKER_PATTERN_KEY,
3008
+ DEFAULT_SPEAKER_PATTERN,
3009
+ PROJECT_LOCALE_SCENE as PROJECT_LOCALE_SCENE2,
3010
+ projectLayout as projectLayout3,
3011
+ STRINGS_SCHEMA,
3012
+ AUTHORING_SCHEMA
3013
+ } from "@patterkit/model";
2985
3014
 
2986
3015
  // src/documentation.ts
2987
3016
  import { DEFAULT_DOCUMENTATION_CLASSES } from "@patterkit/model";
@@ -3025,6 +3054,19 @@ function resolveDocumentation(loaded, channel) {
3025
3054
  }
3026
3055
 
3027
3056
  // src/localisation.ts
3057
+ function projectStringSources(project, sourceTable) {
3058
+ const out = {};
3059
+ const put = (id, authored) => {
3060
+ if (authored) out[id] = sourceTable[id] ?? authored;
3061
+ };
3062
+ for (const c of project.cast ?? []) {
3063
+ put(castStringKey(c.name), c.displayName);
3064
+ for (const [q, label] of Object.entries(c.qualifierLabels ?? {})) put(speakerLabelKey(c.name, q), label);
3065
+ }
3066
+ for (const q of projectQualifiers(project)) put(qualifierStringKey(q.gameId), q.name);
3067
+ put(SPEAKER_PATTERN_KEY, project.speakerPattern ?? DEFAULT_SPEAKER_PATTERN);
3068
+ return out;
3069
+ }
3028
3070
  function extractLoc(loaded, opts = {}) {
3029
3071
  const defaultLocale = loaded.project.locales.default;
3030
3072
  const isTemplate = opts.locale === void 0 || opts.locale === defaultLocale;
@@ -3032,6 +3074,7 @@ function extractLoc(loaded, opts = {}) {
3032
3074
  const source = tableFor(loaded, defaultLocale);
3033
3075
  const target = targetLocale ? tableFor(loaded, targetLocale) : {};
3034
3076
  const edits = mergeAuthoring2(loaded).edits;
3077
+ const projectSources = projectStringSources(loaded.project, source);
3035
3078
  const docs = resolveDocumentation(loaded, "loc");
3036
3079
  const commentsOf = (id) => (docs.get(id) ?? []).map((d) => d.text);
3037
3080
  const staleFor = (id, translation) => {
@@ -3075,18 +3118,36 @@ function extractLoc(loaded, opts = {}) {
3075
3118
  for (const c of loaded.project.cast ?? []) {
3076
3119
  if (!c.displayName) continue;
3077
3120
  const id = castStringKey(c.name);
3078
- const src = source[id] ?? c.displayName;
3121
+ const src = projectSources[id];
3079
3122
  const translation = isTemplate ? "" : target[id] ?? "";
3080
3123
  entries.push({ id, scene: PROJECT_LOCALE_SCENE2, source: src, translation, comments: commentsOf(id), context: withGender({ character: c.name }), stale: staleFor(id, translation) });
3081
3124
  }
3082
3125
  for (const q of usedQualifiers(loaded.project, loaded.scenes)) {
3083
3126
  const id = qualifierStringKey(q.gameId);
3084
- const src = source[id] ?? q.name;
3127
+ const src = projectSources[id] ?? q.name;
3085
3128
  const translation = isTemplate ? "" : target[id] ?? "";
3086
3129
  entries.push({ id, scene: PROJECT_LOCALE_SCENE2, source: src, translation, comments: commentsOf(id), context: { kind: "qualifier" }, stale: staleFor(id, translation) });
3087
3130
  }
3131
+ const pairs = usedSpeakerPairs(loaded.scenes);
3132
+ if (pairs.length > 0) {
3133
+ const id = SPEAKER_PATTERN_KEY;
3134
+ const src = projectSources[id];
3135
+ const translation = isTemplate ? "" : target[id] ?? "";
3136
+ entries.push({ id, scene: PROJECT_LOCALE_SCENE2, source: src, translation, comments: [SPEAKER_PATTERN_NOTE, ...commentsOf(id)], context: { kind: "speaker pattern" }, stale: staleFor(id, translation) });
3137
+ const labelsOf = new Map((loaded.project.cast ?? []).map((c) => [c.name, c.qualifierLabels]));
3138
+ for (const p of pairs) {
3139
+ const label = labelsOf.get(p.character)?.[p.qualifier];
3140
+ if (!label) continue;
3141
+ const pid = speakerLabelKey(p.character, p.qualifier);
3142
+ const psrc = projectSources[pid] ?? label;
3143
+ const ptranslation = isTemplate ? "" : target[pid] ?? "";
3144
+ const qualifier = qualifierName.get(p.qualifier) ?? p.qualifier;
3145
+ entries.push({ id: pid, scene: PROJECT_LOCALE_SCENE2, source: psrc, translation: ptranslation, comments: commentsOf(pid), context: withGender({ character: p.character, kind: "speaker", qualifier }), stale: staleFor(pid, ptranslation) });
3146
+ }
3147
+ }
3088
3148
  return { project: loaded.project.project.id, defaultLocale, locale: targetLocale, entries };
3089
3149
  }
3150
+ var SPEAKER_PATTERN_NOTE = "{name} and {qualifier} are filled in with the speaker's name and the qualifier, so keep both as they are. You can put them in any order.";
3090
3151
  var layoutOf = (loaded) => projectLayout3(loaded.project);
3091
3152
  function existingLocPath(loaded, scene, locale) {
3092
3153
  for (let i = 0; i < loaded.locales.length; i++) {
@@ -3847,6 +3908,114 @@ async function scriptToDocx(doc) {
3847
3908
 
3848
3909
  // src/script-pdf.ts
3849
3910
  import PDFDocument from "pdfkit";
3911
+
3912
+ // src/script-fonts.ts
3913
+ import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync7 } from "fs";
3914
+ import { createRequire } from "module";
3915
+ import { join as join8 } from "path";
3916
+
3917
+ // src/script-fonts.json
3918
+ var script_fonts_default = {
3919
+ serif: [
3920
+ "@fontsource/newsreader/files/newsreader-latin-400-normal.woff",
3921
+ "@fontsource/newsreader/files/newsreader-latin-ext-400-normal.woff",
3922
+ "@fontsource/newsreader/files/newsreader-vietnamese-400-normal.woff"
3923
+ ],
3924
+ serifBold: [
3925
+ "@fontsource/newsreader/files/newsreader-latin-600-normal.woff",
3926
+ "@fontsource/newsreader/files/newsreader-latin-ext-600-normal.woff",
3927
+ "@fontsource/newsreader/files/newsreader-vietnamese-600-normal.woff"
3928
+ ],
3929
+ serifItalic: [
3930
+ "@fontsource/newsreader/files/newsreader-latin-400-italic.woff",
3931
+ "@fontsource/newsreader/files/newsreader-latin-ext-400-italic.woff",
3932
+ "@fontsource/newsreader/files/newsreader-vietnamese-400-italic.woff"
3933
+ ],
3934
+ serifBoldItalic: [
3935
+ "@fontsource/newsreader/files/newsreader-latin-600-italic.woff",
3936
+ "@fontsource/newsreader/files/newsreader-latin-ext-600-italic.woff",
3937
+ "@fontsource/newsreader/files/newsreader-vietnamese-600-italic.woff"
3938
+ ],
3939
+ sans: [
3940
+ "@fontsource/inter/files/inter-latin-400-normal.woff",
3941
+ "@fontsource/inter/files/inter-latin-ext-400-normal.woff",
3942
+ "@fontsource/inter/files/inter-greek-400-normal.woff",
3943
+ "@fontsource/inter/files/inter-greek-ext-400-normal.woff",
3944
+ "@fontsource/inter/files/inter-cyrillic-400-normal.woff",
3945
+ "@fontsource/inter/files/inter-cyrillic-ext-400-normal.woff",
3946
+ "@fontsource/inter/files/inter-vietnamese-400-normal.woff"
3947
+ ],
3948
+ sansBold: [
3949
+ "@fontsource/inter/files/inter-latin-600-normal.woff",
3950
+ "@fontsource/inter/files/inter-latin-ext-600-normal.woff",
3951
+ "@fontsource/inter/files/inter-greek-600-normal.woff",
3952
+ "@fontsource/inter/files/inter-greek-ext-600-normal.woff",
3953
+ "@fontsource/inter/files/inter-cyrillic-600-normal.woff",
3954
+ "@fontsource/inter/files/inter-cyrillic-ext-600-normal.woff",
3955
+ "@fontsource/inter/files/inter-vietnamese-600-normal.woff"
3956
+ ],
3957
+ mono: [
3958
+ "@ibm/plex-mono/fonts/complete/woff/IBMPlexMono-Regular.woff"
3959
+ ],
3960
+ fallback: [
3961
+ "@fontsource/noto-sans/files/noto-sans-latin-400-normal.woff",
3962
+ "@fontsource/noto-sans/files/noto-sans-latin-ext-400-normal.woff",
3963
+ "@fontsource/noto-sans/files/noto-sans-greek-400-normal.woff",
3964
+ "@fontsource/noto-sans/files/noto-sans-greek-ext-400-normal.woff",
3965
+ "@fontsource/noto-sans/files/noto-sans-cyrillic-400-normal.woff",
3966
+ "@fontsource/noto-sans/files/noto-sans-cyrillic-ext-400-normal.woff",
3967
+ "@fontsource/noto-sans/files/noto-sans-vietnamese-400-normal.woff",
3968
+ "@fontsource/noto-sans/files/noto-sans-devanagari-400-normal.woff",
3969
+ "@fontsource/noto-sans-math/files/noto-sans-math-latin-400-normal.woff"
3970
+ ],
3971
+ emoji: [
3972
+ "@fontsource/noto-emoji/files/noto-emoji-emoji-400-normal.woff",
3973
+ "@fontsource/noto-emoji/files/noto-emoji-0-400-normal.woff",
3974
+ "@fontsource/noto-emoji/files/noto-emoji-1-400-normal.woff",
3975
+ "@fontsource/noto-emoji/files/noto-emoji-2-400-normal.woff",
3976
+ "@fontsource/noto-emoji/files/noto-emoji-3-400-normal.woff",
3977
+ "@fontsource/noto-emoji/files/noto-emoji-4-400-normal.woff",
3978
+ "@fontsource/noto-emoji/files/noto-emoji-5-400-normal.woff",
3979
+ "@fontsource/noto-emoji/files/noto-emoji-6-400-normal.woff",
3980
+ "@fontsource/noto-emoji/files/noto-emoji-7-400-normal.woff",
3981
+ "@fontsource/noto-emoji/files/noto-emoji-8-400-normal.woff",
3982
+ "@fontsource/noto-emoji/files/noto-emoji-9-400-normal.woff",
3983
+ "@fontsource/noto-emoji/files/noto-emoji-10-400-normal.woff"
3984
+ ]
3985
+ };
3986
+
3987
+ // src/script-fonts.ts
3988
+ var SCRIPT_FONT_FILES = script_fonts_default;
3989
+ var SCRIPT_FONT_PACKAGES = [
3990
+ ...new Set(Object.values(script_fonts_default).flat().map((spec) => spec.split("/").slice(0, spec.startsWith("@") ? 2 : 1).join("/")))
3991
+ ];
3992
+ var require2 = createRequire(import.meta.url);
3993
+ var located = {};
3994
+ var licences = {};
3995
+ var cache = /* @__PURE__ */ new Map();
3996
+ function useScriptFontFiles(paths, licencePaths = {}) {
3997
+ located = { ...paths };
3998
+ licences = { ...licencePaths };
3999
+ cache.clear();
4000
+ }
4001
+ function scriptFontLicencePath(pkg) {
4002
+ const dir = (require2.resolve.paths(pkg) ?? []).map((p) => join8(p, pkg)).find((d) => existsSync5(d));
4003
+ const file = dir && readdirSync4(dir).find((f) => /^(licen[cs]e|ofl)/i.test(f));
4004
+ if (!dir || !file) throw new Error(`no licence file found for ${pkg}`);
4005
+ return join8(dir, file);
4006
+ }
4007
+ function scriptFontLicences() {
4008
+ return SCRIPT_FONT_PACKAGES.map((pkg) => ({ package: pkg, text: readFileSync7(licences[pkg] ?? scriptFontLicencePath(pkg), "utf8") }));
4009
+ }
4010
+ function scriptFontFiles(face) {
4011
+ return script_fonts_default[face].map((spec) => {
4012
+ let bytes = cache.get(spec);
4013
+ if (!bytes) cache.set(spec, bytes = readFileSync7(located[spec] ?? require2.resolve(spec)));
4014
+ return bytes;
4015
+ });
4016
+ }
4017
+
4018
+ // src/script-pdf.ts
3850
4019
  var MARGIN = 64;
3851
4020
  var INDENT_STEP = 20;
3852
4021
  var EDGE_INSET = 12;
@@ -3871,36 +4040,47 @@ var LINE = hex(TOKENS.line);
3871
4040
  var EDGE = "#d7d0c3";
3872
4041
  var SERIF2 = (bold, italic) => bold && italic ? "Serif-BoldItalic" : bold ? "Serif-Bold" : italic ? "Serif-Italic" : "Serif";
3873
4042
  var isEmoji = (cp) => cp >= 126976 || cp >= 9728 && cp <= 10175;
4043
+ var ROLES = {
4044
+ "Serif": "serif",
4045
+ "Serif-Bold": "serifBold",
4046
+ "Serif-Italic": "serifItalic",
4047
+ "Serif-BoldItalic": "serifBoldItalic",
4048
+ "Sans": "sans",
4049
+ "Sans-Bold": "sansBold",
4050
+ "Mono": "mono"
4051
+ };
3874
4052
  async function scriptToPdf(doc) {
3875
- const { scriptFont } = await import("./script-fonts-AQNQOADI.js");
3876
4053
  return new Promise((resolve7, reject) => {
3877
4054
  const pdf = new PDFDocument({ size: "A4", margins: { top: MARGIN, bottom: MARGIN, left: MARGIN, right: MARGIN }, info: { Title: doc.project, Author: "Patter" }, font: null });
3878
- pdf.registerFont("Serif", scriptFont("serif"));
3879
- pdf.registerFont("Serif-Bold", scriptFont("serifBold"));
3880
- pdf.registerFont("Serif-Italic", scriptFont("serifItalic"));
3881
- pdf.registerFont("Serif-BoldItalic", scriptFont("serifBoldItalic"));
3882
- pdf.registerFont("Sans", scriptFont("sans"));
3883
- pdf.registerFont("Sans-Bold", scriptFont("sansBold"));
3884
- pdf.registerFont("Mono", scriptFont("mono"));
3885
- pdf.registerFont("Symbol", scriptFont("symbol"));
3886
- pdf.registerFont("Emoji", scriptFont("emoji"));
4055
+ const register = (prefix, face, firstIsPrefix) => scriptFontFiles(face).map((bytes, i) => {
4056
+ const name = firstIsPrefix && i === 0 ? prefix : `${prefix}~${i}`;
4057
+ pdf.registerFont(name, bytes);
4058
+ return name;
4059
+ });
4060
+ const roleFiles = {};
4061
+ for (const [role, face] of Object.entries(ROLES)) roleFiles[role] = register(role, face, true);
4062
+ const fallback = register("Fallback", "fallback", false);
4063
+ const emoji = register("Emoji", "emoji", false);
3887
4064
  const coverage = {};
3888
4065
  const ascender = {};
3889
- for (const name of ["Serif", "Serif-Bold", "Serif-Italic", "Serif-BoldItalic", "Sans", "Sans-Bold", "Mono"]) {
4066
+ for (const name of [...Object.values(roleFiles).flat(), ...fallback, ...emoji]) {
3890
4067
  pdf.font(name);
3891
4068
  const f = pdf._font;
3892
4069
  coverage[name] = (cp) => f.font.hasGlyphForCodePoint(cp);
3893
4070
  ascender[name] = f.ascender;
3894
4071
  }
3895
4072
  const ascentPt = (name, size) => ascender[name] / 1e3 * size;
4073
+ const chains = {};
4074
+ for (const role of Object.keys(ROLES)) chains[role] = [...roleFiles[role], ...fallback];
4075
+ const pick = (chain, cp) => chain.find((n) => coverage[n](cp));
3896
4076
  const split = (text, primary, color) => {
3897
- const cov = coverage[primary];
4077
+ const chain = chains[primary];
3898
4078
  const out = [];
3899
4079
  let buf = "";
3900
4080
  let cur = primary;
3901
4081
  for (const ch of text) {
3902
4082
  const cp = ch.codePointAt(0);
3903
- const f = isEmoji(cp) ? "Emoji" : cov(cp) ? primary : "Symbol";
4083
+ const f = (isEmoji(cp) ? pick(emoji, cp) : void 0) ?? pick(chain, cp) ?? primary;
3904
4084
  if (f !== cur) {
3905
4085
  if (buf) out.push({ text: buf, font: cur, color });
3906
4086
  buf = "";
@@ -4066,8 +4246,8 @@ async function scriptToPdf(doc) {
4066
4246
  }
4067
4247
 
4068
4248
  // src/init.ts
4069
- import { join as join8, basename as basename6, resolve as resolve5 } from "path";
4070
- import { readdirSync as readdirSync4, existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
4249
+ import { join as join9, basename as basename6, resolve as resolve5 } from "path";
4250
+ import { readdirSync as readdirSync5, existsSync as existsSync6, readFileSync as readFileSync8 } from "fs";
4071
4251
  import { newId, slug, canonicalStringify as canonicalStringify7 } from "@patterkit/core";
4072
4252
  import { FLOW_SCHEMA, PROJECT_SCHEMA, STRINGS_SCHEMA as STRINGS_SCHEMA2 } from "@patterkit/model";
4073
4253
  function runInit(opts) {
@@ -4076,7 +4256,7 @@ function runInit(opts) {
4076
4256
  const fileSlug = slug(name);
4077
4257
  let existing = [];
4078
4258
  try {
4079
- existing = readdirSync4(dir).filter((f) => f.endsWith(".patterproj"));
4259
+ existing = readdirSync5(dir).filter((f) => f.endsWith(".patterproj"));
4080
4260
  } catch {
4081
4261
  }
4082
4262
  if (existing.length > 0) throw new Error(`a project already exists here: ${existing[0]}`);
@@ -4120,31 +4300,31 @@ function runInit(opts) {
4120
4300
  strings: { [beatId]: `Welcome to ${name}. This is the first line of your story - replace it with your own.` }
4121
4301
  };
4122
4302
  const bundle = opts.bundle ?? "commit";
4123
- const projectFile = join8(dir, `${fileSlug}.patterproj`);
4303
+ const projectFile = join9(dir, `${fileSlug}.patterproj`);
4124
4304
  const writes = [
4125
- { path: join8(dir, "scenes", "start.patterflow"), content: canonicalStringify7(flow) },
4126
- { path: join8(dir, "loc", "en", "start.patterloc"), content: canonicalStringify7(locale) },
4127
- { path: join8(dir, ".editorconfig"), content: EDITORCONFIG },
4305
+ { path: join9(dir, "scenes", "start.patterflow"), content: canonicalStringify7(flow) },
4306
+ { path: join9(dir, "loc", "en", "start.patterloc"), content: canonicalStringify7(locale) },
4307
+ { path: join9(dir, ".editorconfig"), content: EDITORCONFIG },
4128
4308
  ...vcsConfigWrites(dir, opts.vcs, bundle),
4129
4309
  { path: projectFile, content: canonicalStringify7(project) }
4130
4310
  ];
4131
- const collisions = writes.map((w) => w.path).filter((p) => existsSync5(p));
4311
+ const collisions = writes.map((w) => w.path).filter((p) => existsSync6(p));
4132
4312
  if (collisions.length > 0) {
4133
4313
  throw new Error(`refusing to overwrite existing file(s): ${collisions.join(", ")}`);
4134
4314
  }
4135
4315
  return { writes, projectFile, name };
4136
4316
  }
4137
4317
  function vcsConfigWrites(dir, vcs, bundle = "commit") {
4138
- const writes = [{ path: join8(dir, "vcs-setup.md"), content: vcsSetup(vcs, bundle) }];
4139
- if (vcs === "git") writes.push({ path: join8(dir, ".gitattributes"), content: GITATTRIBUTES });
4318
+ const writes = [{ path: join9(dir, "vcs-setup.md"), content: vcsSetup(vcs, bundle) }];
4319
+ if (vcs === "git") writes.push({ path: join9(dir, ".gitattributes"), content: GITATTRIBUTES });
4140
4320
  const ignore = ignoreFileFor(vcs);
4141
- if (ignore) writes.push({ path: join8(dir, ignore), content: ignoreContent(bundle) });
4321
+ if (ignore) writes.push({ path: join9(dir, ignore), content: ignoreContent(bundle) });
4142
4322
  return writes;
4143
4323
  }
4144
4324
  function currentBundlePosture(dir, vcs) {
4145
4325
  const read = (name) => {
4146
4326
  try {
4147
- return readFileSync7(join8(dir, name), "utf8");
4327
+ return readFileSync8(join9(dir, name), "utf8");
4148
4328
  } catch {
4149
4329
  return "";
4150
4330
  }
@@ -4640,7 +4820,7 @@ function runReplace(loaded, opts) {
4640
4820
  }
4641
4821
 
4642
4822
  // src/pin.ts
4643
- import { readFileSync as readFileSync8 } from "fs";
4823
+ import { readFileSync as readFileSync9 } from "fs";
4644
4824
  import { canonicalStringify as canonicalStringify9, parseSource as parseSource5 } from "@patterkit/core";
4645
4825
  import { effectiveGameId as effectiveGameId3, gameIdify } from "@patterkit/model";
4646
4826
  function toPin(node) {
@@ -4668,7 +4848,7 @@ function planPins(loaded) {
4668
4848
  });
4669
4849
  if (!changed) continue;
4670
4850
  const next = { ...scene, ...sceneGameId !== void 0 ? { gameId: sceneGameId } : {}, blocks };
4671
- const file = parseSource5(readFileSync8(path, "utf8"));
4851
+ const file = parseSource5(readFileSync9(path, "utf8"));
4672
4852
  plan.writes.push({ path, content: canonicalStringify9({ ...file, scene: next }) });
4673
4853
  plan.scenes.push(next);
4674
4854
  }
@@ -4677,8 +4857,8 @@ function planPins(loaded) {
4677
4857
 
4678
4858
  // src/unpack.ts
4679
4859
  import JSZip2 from "jszip";
4680
- import { join as join9, resolve as resolve6, sep as sep4 } from "path";
4681
- import { existsSync as existsSync6, readFileSync as readFileSync9, readdirSync as readdirSync5 } from "fs";
4860
+ import { join as join10, resolve as resolve6, sep as sep4 } from "path";
4861
+ import { existsSync as existsSync7, readFileSync as readFileSync10, readdirSync as readdirSync6 } from "fs";
4682
4862
  import { parseSource as parseSource6, canonicalStringify as canonicalStringify10 } from "@patterkit/core";
4683
4863
  import { isUnsafeEntry } from "@wildwinter/toolkit/archive";
4684
4864
  import { SCOPES_FILE_SUFFIX as SCOPES_FILE_SUFFIX2 } from "@wildwinter/scoperegistry/scopes";
@@ -4746,13 +4926,13 @@ async function runUnpack(bytes, targetDir) {
4746
4926
  function projectIdIn(dir) {
4747
4927
  let names;
4748
4928
  try {
4749
- names = readdirSync5(dir).filter((n) => n.endsWith(".patterproj"));
4929
+ names = readdirSync6(dir).filter((n) => n.endsWith(".patterproj"));
4750
4930
  } catch {
4751
4931
  return void 0;
4752
4932
  }
4753
4933
  for (const n of names) {
4754
4934
  try {
4755
- const id = parseSource6(readFileSync9(join9(dir, n), "utf8")).project?.id;
4935
+ const id = parseSource6(readFileSync10(join10(dir, n), "utf8")).project?.id;
4756
4936
  if (id) return id;
4757
4937
  } catch {
4758
4938
  }
@@ -4779,7 +4959,7 @@ function checkProvenance(returned, base, target) {
4779
4959
  }
4780
4960
  function targetProjectId(projectDir) {
4781
4961
  try {
4782
- const pf = parseSource6(readFileSync9(findProjectFile(projectDir), "utf8"));
4962
+ const pf = parseSource6(readFileSync10(findProjectFile(projectDir), "utf8"));
4783
4963
  return pf.project?.id;
4784
4964
  } catch {
4785
4965
  return void 0;
@@ -4800,13 +4980,13 @@ async function runUnpackMerge(returnedBytes, baseBytes, projectDir) {
4800
4980
  for (const [rel, theirText] of [...theirs.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
4801
4981
  const outPath = containedWrite(projectDir, rel);
4802
4982
  const baseText = base.get(rel);
4803
- if (!existsSync6(outPath)) {
4983
+ if (!existsSync7(outPath)) {
4804
4984
  if (baseText === theirText) continue;
4805
4985
  writes.push({ path: outPath, content: theirText });
4806
4986
  shards.push({ path: rel, added: true, changed: true });
4807
4987
  continue;
4808
4988
  }
4809
- const oursText = readFileSync9(outPath, "utf8");
4989
+ const oursText = readFileSync10(outPath, "utf8");
4810
4990
  if (theirText === baseText || theirText === oursText) {
4811
4991
  if (rel === projectRel && baseText !== void 0) {
4812
4992
  const read = (t) => parseSource6(t);
@@ -4847,17 +5027,17 @@ async function runUnpackMerge(returnedBytes, baseBytes, projectDir) {
4847
5027
  if (world.write) {
4848
5028
  writes.push(world.write);
4849
5029
  gameScopes = { path: world.write.path };
4850
- } else if (world.error) gameScopes = { path: join9(local2.dir, GAME_SCOPES_FILE), error: world.error };
5030
+ } else if (world.error) gameScopes = { path: join10(local2.dir, GAME_SCOPES_FILE), error: world.error };
4851
5031
  }
4852
5032
  const handoffs = [];
4853
5033
  for (const [rel, theirText] of [...returnedDoc.handoffs.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
4854
5034
  const outPath = containedWrite(projectDir, rel);
4855
- if (!existsSync6(outPath)) {
5035
+ if (!existsSync7(outPath)) {
4856
5036
  writes.push({ path: outPath, content: theirText });
4857
5037
  handoffs.push(rel);
4858
5038
  continue;
4859
5039
  }
4860
- const merged = mergeHandoff(parseHandoff(readFileSync9(outPath, "utf8")), parseHandoff(theirText));
5040
+ const merged = mergeHandoff(parseHandoff(readFileSync10(outPath, "utf8")), parseHandoff(theirText));
4861
5041
  if (merged) {
4862
5042
  writes.push({ path: outPath, content: serialiseHandoff(merged) });
4863
5043
  handoffs.push(rel);
@@ -4880,7 +5060,7 @@ function projectEntry(shards) {
4880
5060
  }
4881
5061
  function containedWrite(dir, name) {
4882
5062
  const root = resolve6(dir);
4883
- const full = resolve6(join9(dir, name));
5063
+ const full = resolve6(join10(dir, name));
4884
5064
  if (full !== root && !full.startsWith(root + sep4)) {
4885
5065
  throw new UnsafeEntryError(`document entry escapes the target directory: ${name}`);
4886
5066
  }
@@ -4888,7 +5068,7 @@ function containedWrite(dir, name) {
4888
5068
  }
4889
5069
 
4890
5070
  // src/suggestions.ts
4891
- import { readFileSync as readFileSync10 } from "fs";
5071
+ import { readFileSync as readFileSync11 } from "fs";
4892
5072
  import { canonicalStringify as canonicalStringify11, parseSource as parseSource7 } from "@patterkit/core";
4893
5073
  import { walkNodes as walkNodes5, AUTHORING_SCHEMA as AUTHORING_SCHEMA3 } from "@patterkit/model";
4894
5074
  function staleParts(s, liveText, line) {
@@ -4975,7 +5155,7 @@ var Working = class {
4975
5155
  if (!path) return void 0;
4976
5156
  let file = this.flows.get(path);
4977
5157
  if (!file) {
4978
- const onDisk = parseSource7(readFileSync10(path, "utf8"));
5158
+ const onDisk = parseSource7(readFileSync11(path, "utf8"));
4979
5159
  const scene = this.loaded.scenes.find((s) => s.id === sceneId);
4980
5160
  file = { ...onDisk, scene: structuredClone(scene ?? onDisk.scene) };
4981
5161
  this.flows.set(path, file);
@@ -5452,11 +5632,11 @@ function readParagraphs(ps, joiner) {
5452
5632
  c.walk(p, "both");
5453
5633
  return c;
5454
5634
  });
5455
- const join10 = (view) => parts.map((c) => c.text(view)).filter((t) => t !== "").join(joiner);
5635
+ const join11 = (view) => parts.map((c) => c.text(view)).filter((t) => t !== "").join(joiner);
5456
5636
  return {
5457
5637
  text: {
5458
- proposed: join10("proposed"),
5459
- original: join10("original"),
5638
+ proposed: join11("proposed"),
5639
+ original: join11("original"),
5460
5640
  changes: parts.flatMap((c) => c.changes),
5461
5641
  comments: [...new Set(parts.flatMap((c) => c.comments))],
5462
5642
  droppedFormatting: parts.reduce((n, c) => n + c.dropped, 0)
@@ -6027,6 +6207,8 @@ export {
6027
6207
  PATTER_SCOPES_OWNER,
6028
6208
  RARE_REACH_PCT,
6029
6209
  REFUSE_MISSING_SHARE,
6210
+ SCRIPT_FONT_FILES,
6211
+ SCRIPT_FONT_PACKAGES,
6030
6212
  SHARD_EXTENSIONS,
6031
6213
  UnsafeEntryError,
6032
6214
  UnsupportedMergeError,
@@ -6038,6 +6220,7 @@ export {
6038
6220
  audioFoldersActive,
6039
6221
  audioManifest,
6040
6222
  audioManifestWrite,
6223
+ authoringPath,
6041
6224
  besideBundlePath,
6042
6225
  bundleOutputPath,
6043
6226
  catalogToJson,
@@ -6086,6 +6269,7 @@ export {
6086
6269
  planWorldSave,
6087
6270
  poToCatalog,
6088
6271
  previewRegistry,
6272
+ projectStringSources,
6089
6273
  proposeCoverageDrivers,
6090
6274
  qualifierNamer,
6091
6275
  reachabilityIssues,
@@ -6126,6 +6310,8 @@ export {
6126
6310
  scanAudio,
6127
6311
  scanAudioStatus,
6128
6312
  sceneIdForShard,
6313
+ scriptFontLicencePath,
6314
+ scriptFontLicences,
6129
6315
  scriptToDocx,
6130
6316
  scriptToPdf,
6131
6317
  serialiseHandoff,
@@ -6133,6 +6319,7 @@ export {
6133
6319
  setCut,
6134
6320
  sidecarIssues,
6135
6321
  staleParts,
6322
+ useScriptFontFiles,
6136
6323
  vcsConfigWrites,
6137
6324
  voiceScriptToXlsx,
6138
6325
  worldSettingsScopes,