@patterkit/cli 0.9.2 → 0.10.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/cli.js +300 -112
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -197883,6 +197883,29 @@ function effectiveGameId(entity) {
|
|
|
197883
197883
|
const g = entity.gameId?.trim();
|
|
197884
197884
|
return g ? g : gameIdify(entity.name);
|
|
197885
197885
|
}
|
|
197886
|
+
var DEFAULT_QUALIFIERS = [
|
|
197887
|
+
{ gameId: "vo", name: "V.O.", description: "Voice-over: the character isn't in the scene (a thought, narration, a voice in the ear)." },
|
|
197888
|
+
{ gameId: "os", name: "O.S.", description: "Off-screen: the character is there, but not seen (the next room, behind a door)." },
|
|
197889
|
+
{ gameId: "radio", name: "RADIO", description: "Heard through a radio, a phone, or a loudspeaker." }
|
|
197890
|
+
];
|
|
197891
|
+
function projectQualifiers(project) {
|
|
197892
|
+
return project.qualifiers ?? DEFAULT_QUALIFIERS;
|
|
197893
|
+
}
|
|
197894
|
+
function usedQualifiers(project, scenes) {
|
|
197895
|
+
const used = /* @__PURE__ */ new Set();
|
|
197896
|
+
for (const scene of scenes) {
|
|
197897
|
+
for (const block of scene.blocks) {
|
|
197898
|
+
walkNodes(block.children, (node) => {
|
|
197899
|
+
if (node.type === "group") {
|
|
197900
|
+
if (node.prompt?.kind === "line" && node.prompt.qualifier) used.add(node.prompt.qualifier);
|
|
197901
|
+
return;
|
|
197902
|
+
}
|
|
197903
|
+
for (const beat of node.beats ?? []) if (beat.kind === "line" && beat.qualifier) used.add(beat.qualifier);
|
|
197904
|
+
});
|
|
197905
|
+
}
|
|
197906
|
+
}
|
|
197907
|
+
return projectQualifiers(project).filter((q) => used.has(q.gameId));
|
|
197908
|
+
}
|
|
197886
197909
|
var DEFAULT_WRITING_STATUSES = [
|
|
197887
197910
|
{ name: "stub", colour: 0 },
|
|
197888
197911
|
// red
|
|
@@ -197927,6 +197950,9 @@ var PROJECT_LOCALE_SCENE = "@project";
|
|
|
197927
197950
|
function castStringKey(name) {
|
|
197928
197951
|
return `cast:${name}`;
|
|
197929
197952
|
}
|
|
197953
|
+
function qualifierStringKey(gameId) {
|
|
197954
|
+
return `qualifier:${gameId}`;
|
|
197955
|
+
}
|
|
197930
197956
|
var HANDOFF_SCHEMA = "patter/handoff@0";
|
|
197931
197957
|
var DEFAULT_CAPTION_DELIMITERS = { open: "[", close: "]" };
|
|
197932
197958
|
var DEFAULT_CAPTION_CHARACTER = "SFX";
|
|
@@ -198047,6 +198073,12 @@ function validateProject(input) {
|
|
|
198047
198073
|
const sceneGameIds = /* @__PURE__ */ new Map();
|
|
198048
198074
|
const jumps = [];
|
|
198049
198075
|
const castNames = new Set((project.cast ?? []).map((c2) => c2.name));
|
|
198076
|
+
const qualifierIds = new Set(projectQualifiers(project).map((q) => q.gameId));
|
|
198077
|
+
const checkQualifier = (beat, what) => {
|
|
198078
|
+
if (beat.kind === "line" && beat.qualifier !== void 0 && !qualifierIds.has(beat.qualifier)) {
|
|
198079
|
+
issues.push({ code: "unknown-qualifier", message: `${what} '${beat.id}' qualifier '${beat.qualifier}' is not in the project's qualifiers`, id: beat.id });
|
|
198080
|
+
}
|
|
198081
|
+
};
|
|
198050
198082
|
const checkTags = (tags, where, id) => {
|
|
198051
198083
|
for (const t of tags ?? []) {
|
|
198052
198084
|
if (typeof t !== "string" || t.length === 0 || /[\s,]/.test(t)) {
|
|
@@ -198170,6 +198202,7 @@ function validateProject(input) {
|
|
|
198170
198202
|
if (p.kind === "line" && p.character && !castNames.has(p.character)) {
|
|
198171
198203
|
issues.push({ code: "unknown-character", message: `prompt '${p.id}' speaker '${p.character}' is not in the project cast`, id: p.id });
|
|
198172
198204
|
}
|
|
198205
|
+
checkQualifier(p, "prompt");
|
|
198173
198206
|
}
|
|
198174
198207
|
}
|
|
198175
198208
|
if (fallbacks > 1) {
|
|
@@ -198199,6 +198232,7 @@ function validateProject(input) {
|
|
|
198199
198232
|
id: beat.id
|
|
198200
198233
|
});
|
|
198201
198234
|
}
|
|
198235
|
+
checkQualifier(beat, "beat");
|
|
198202
198236
|
}
|
|
198203
198237
|
if (node.jump) jumps.push({ to: node.jump.to, from: node.id });
|
|
198204
198238
|
});
|
|
@@ -198271,6 +198305,18 @@ function validateProjectFile(project, issues) {
|
|
|
198271
198305
|
if (project.recordingStatuses) {
|
|
198272
198306
|
checkLadderNames(project.recordingStatuses.map((s) => s.name), "recordingStatuses", issues);
|
|
198273
198307
|
}
|
|
198308
|
+
const seenQualifiers = /* @__PURE__ */ new Set();
|
|
198309
|
+
for (const q of project.qualifiers ?? []) {
|
|
198310
|
+
if (typeof q.name !== "string" || !q.name.trim()) {
|
|
198311
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q.gameId)}' has no name` });
|
|
198312
|
+
}
|
|
198313
|
+
if (typeof q.gameId !== "string" || !isValidGameId(q.gameId)) {
|
|
198314
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q.name)}' address '${String(q.gameId)}' is invalid (lowercase letters, digits, hyphens; no leading/trailing hyphen)` });
|
|
198315
|
+
} else if (seenQualifiers.has(q.gameId)) {
|
|
198316
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier address '${q.gameId}' is used more than once` });
|
|
198317
|
+
}
|
|
198318
|
+
if (typeof q.gameId === "string") seenQualifiers.add(q.gameId);
|
|
198319
|
+
}
|
|
198274
198320
|
for (const [kind, fields] of Object.entries(project.gameDataFields ?? {})) {
|
|
198275
198321
|
const seen = /* @__PURE__ */ new Set();
|
|
198276
198322
|
for (const f of fields ?? []) {
|
|
@@ -199786,6 +199832,7 @@ function exportBundle(input) {
|
|
|
199786
199832
|
const scenesOut = {};
|
|
199787
199833
|
for (const scene of scenes) scenesOut[scene.id] = compileScene(scene, compileScopes);
|
|
199788
199834
|
const externalScopes = externalScopesIn(scenesOut, /* @__PURE__ */ new Set([...EXTERNAL_SCOPES, ...gameExternal.map((s) => s.token)]));
|
|
199835
|
+
const qualifiers = usedQualifiers(project, scenes).map((q) => ({ gameId: q.gameId, name: q.name }));
|
|
199789
199836
|
const strings = {};
|
|
199790
199837
|
for (const loc of locales) {
|
|
199791
199838
|
if (includeLocales && !includeLocales.includes(loc.locale)) continue;
|
|
@@ -199804,21 +199851,24 @@ function exportBundle(input) {
|
|
|
199804
199851
|
// Binds saves to the content they were taken against, and gates bundle
|
|
199805
199852
|
// staleness (schema §10.5). A stable strict-JSON fingerprint - independent
|
|
199806
199853
|
// of the source-form trailing-comma policy.
|
|
199807
|
-
// `scopeRegistry`
|
|
199808
|
-
// prior hash (no ripple to existing bundles / the conformance corpus).
|
|
199854
|
+
// `scopeRegistry` and `qualifiers` are folded in only when present, so projects without them keep
|
|
199855
|
+
// their exact prior hash (no ripple to existing bundles / the conformance corpus).
|
|
199809
199856
|
hash: hash32(canonicalStringify({
|
|
199810
199857
|
scenes: scenesOut,
|
|
199811
199858
|
strings,
|
|
199812
199859
|
properties: project.properties ?? [],
|
|
199813
|
-
...foreignScopes ? { scopeRegistry: foreignScopes } : {}
|
|
199860
|
+
...foreignScopes ? { scopeRegistry: foreignScopes } : {},
|
|
199861
|
+
...qualifiers.length > 0 ? { qualifiers } : {}
|
|
199814
199862
|
}, { trailingComma: false })),
|
|
199815
199863
|
// Structure-only fingerprint (live bundle refresh): the same hash with the string tables left
|
|
199816
199864
|
// out. Same structureHash + a different hash = a text-only edit, safe to swap in place with
|
|
199817
199865
|
// `engine.replaceStrings()`; a changed structureHash needs the full save/load hot swap.
|
|
199866
|
+
// A qualifier's name rides in `qualifiers`, not the string tables, so renaming one is a structure change.
|
|
199818
199867
|
structureHash: hash32(canonicalStringify({
|
|
199819
199868
|
scenes: scenesOut,
|
|
199820
199869
|
properties: project.properties ?? [],
|
|
199821
|
-
...foreignScopes ? { scopeRegistry: foreignScopes } : {}
|
|
199870
|
+
...foreignScopes ? { scopeRegistry: foreignScopes } : {},
|
|
199871
|
+
...qualifiers.length > 0 ? { qualifiers } : {}
|
|
199822
199872
|
}, { trailingComma: false }))
|
|
199823
199873
|
},
|
|
199824
199874
|
voiced: project.voiced ?? false,
|
|
@@ -199834,6 +199884,7 @@ function exportBundle(input) {
|
|
|
199834
199884
|
if (c2.gameData !== void 0) m.gameData = c2.gameData;
|
|
199835
199885
|
return m;
|
|
199836
199886
|
}),
|
|
199887
|
+
...qualifiers.length > 0 ? { qualifiers } : {},
|
|
199837
199888
|
properties: project.properties,
|
|
199838
199889
|
scopeRegistry: hostScopes,
|
|
199839
199890
|
gameDataFields: project.gameDataFields,
|
|
@@ -201225,7 +201276,7 @@ function planBuild(loaded, opts = {}) {
|
|
|
201225
201276
|
}
|
|
201226
201277
|
|
|
201227
201278
|
// ../ops/src/playable-runtime.ts
|
|
201228
|
-
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var W=Object.defineProperty;var Me=Object.getOwnPropertyDescriptor;var $e=Object.getOwnPropertyNames;var He=Object.prototype.hasOwnProperty;var qe=(n,e)=>{for(var t in e)W(n,t,{get:e[t],enumerable:!0})},_e=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of $e(e))!He.call(n,s)&&s!==t&&W(n,s,{get:()=>e[s],enumerable:!(r=Me(e,s))||r.enumerable});return n};var Ue=n=>_e(W({},"__esModule",{value:!0}),n);var pt={};qe(pt,{Checkpoint:()=>T,Engine:()=>_,Flow:()=>D,buildTagIndex:()=>q,describeBundle:()=>Ae,effectiveGameData:()=>Ge,gameDataFields:()=>Te,gameDataValue:()=>se});function R(n,e){return n!==void 0&&Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0}function k(n){switch(n[0]){case"b":return{kind:"bool",value:n[1]};case"n":return{kind:"number",value:n[1]};case"s":return{kind:"string",value:n[1]};case"sv":return{kind:"scopedvar",scope:n[1],name:n[2]};case"u":return{kind:"unary",op:n[1],operand:k(n[2])};case"bin":return{kind:"binary",op:n[1],left:k(n[2]),right:k(n[3])};case"call":{let e=n.slice(2).map(k);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var d=class extends Error{constructor(e){super(e),this.name="EvalError"}};function V(n,e,t){let r=new Map(t.scopes.map(i=>[i.token,i.missing??"false"])),s=i=>{switch(i.kind){case"bool":return i.value;case"number":return i.value;case"string":return i.value;case"scopedvar":{let o=R(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):R(o,i.name);if(a===void 0){if(r.get(i.scope)==="throw")throw new d(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{if(i.name==="advance"&&!R(t.functions,i.name)){let a=i.args[0];if(i.args.length!==1||a===void 0)throw new d(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=J(a,e);if(c===void 0)throw new d("advance() needs a quality reference (@scope.name of a quality property)");let l=w(s(a),c,"advance");return c[Math.min(l+1,c.length-1)]}let o=R(t.functions,i.name);if(!o)throw new d(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new d("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=s(i.operand);if(typeof a!="boolean")throw new d(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new d(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let f=s(i.left);if(typeof f!="boolean")throw new d(`\'and\' requires boolean operands, left is ${typeof f}`);if(!f)return!1;let u=s(i.right);if(typeof u!="boolean")throw new d(`\'and\' requires boolean operands, right is ${typeof u}`);return u}if(i.op==="or"){let f=s(i.left);if(typeof f!="boolean")throw new d(`\'or\' requires boolean operands, left is ${typeof f}`);if(f)return!0;let u=s(i.right);if(typeof u!="boolean")throw new d(`\'or\' requires boolean operands, right is ${typeof u}`);return u}let o=s(i.left),a=s(i.right),c=J(i.left,e),l=J(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!We(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new d(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return w(o,p,">")>w(a,p,">");case">=":return w(o,p,">=")>=w(a,p,">=");case"<":return w(o,p,"<")<w(a,p,"<");case"<=":return w(o,p,"<=")<=w(a,p,"<=");case"+":case"-":case"*":case"/":throw new d(`\'${i.op}\' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(i.op){case"==":return pe(o,a);case"!=":return!pe(o,a);case">":return E(o,a,">"),o>a;case">=":return E(o,a,">="),o>=a;case"<":return E(o,a,"<"),o<a;case"<=":return E(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new d(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return E(o,a,"-"),o-a;case"*":return E(o,a,"*"),o*a;case"/":if(E(o,a,"/"),a===0)throw new d("division by zero");return o/a}}}};return s(n)}function pe(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;let t=[...n].sort(),r=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==r[s])return!1;return!0}return n===e}function E(n,e,t){if(typeof n!="number"||typeof e!="number")throw new d(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function J(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function w(n,e,t){if(typeof n!="string")throw new d(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new d(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var We=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function O(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function K(n){let e=O(n);return{next(){e=e+1831565813>>>0;let t=Math.imul(e^e>>>15,1|e);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296},state(){return e}}}var Je={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},Ke=[Je];function de(n,e,t){let r=t?.countingCalls??Ke;return L(n,t?.want??!0,e,r)}function L(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=L(n.left,e,t,r),i=L(n.right,e,t,r);return n.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(n.kind==="unary"&&n.op==="not")return L(n.operand,!e,t,r);if(n.kind==="call"){let s=r.find(i=>i.name===n.name);if(s){let i=s.count(n),o=t(n);return e?o?i:0:o?0:1}}return t(n)===e?1:0}var b=class n{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let r=this.norm(t.name);this.decls.set(r,t),this.values[r]=structuredClone(t.default??P(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,r){let s=this.norm(e);if(!r?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:r?.silent??!1,reason:r?.reason};this.values[s]=t;for(let o of[...this.auditors])o(i);if(!i.silent)for(let o of[...this.subscribers])o(i);return i}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}onAudit(e){return this.auditors.add(e),()=>this.auditors.delete(e)}rows(){return[...this.decls.entries()].map(([e,t])=>ue(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm,pathPrefix:this.pathPrefix});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e){for(let[t,r]of Object.entries(e))this.values[this.norm(t)]=r}};function ue(n,e,t,r,s=""){let i=r??n.name.toLowerCase();return{name:i,path:s+i,type:n.type,value:e,default:n.default??P(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var ze=n=>n.toLowerCase(),z=1,M=class{scopes=new Map;parked=new Map;rev=0;get revision(){return this.rev}defineOwned(e,t,r){let s=typeof r=="string"?{pathPrefix:r}:r??{},i=new b(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,i,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,r){this.assertFree(e,r?.owner),this.scopes.set(e,{kind:"owned",bag:t,...r?.owner!==void 0?{owner:r.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(t.load(s),this.parked.delete(e)),this}remove(e,t){let r=this.scopes.get(e);if(!r)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&r.kind==="owned"&&this.parked.set(e,r.bag.save()),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&this.parked.delete(t);return this}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??ze,a=new Map;for(let c of r)a.set(o(c.name),c);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:i.writable??!0,norm:o,...i.owner!==void 0?{owner:i.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let r=this.scopes.get(e);if(r)return r.kind==="owned"?r.bag.get(t):r.resolver.get(r.norm(t))}set(e,t,r,s){let i=this.scopes.get(e);if(!i)throw new Error(`unknown scope \'@${e}\'`);if(i.kind==="owned"){if(!s?.host&&!i.bag.writable(t))throw new Error(`\'@${e}.${t}\' is read-only`);i.bag.set(t,r,s?.host?{host:!0}:void 0);return}let o=i.norm(t);if(!i.resolver.set)throw new Error(`\'@${e}.${t}\' is read-only`);if(!s?.host&&!this.foreignWritable(i,o))throw new Error(`\'@${e}.${t}\' is read-only`);i.resolver.set(o,r)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,r]of this.scopes){let s=r.owner!==void 0?{owner:r.owner}:{};if(r.kind==="owned")for(let i of r.bag.rows())e.push({scope:t,...s,...i});else for(let[i,o]of r.decls)e.push({scope:t,...s,...ue(o,r.resolver.get(i),this.foreignWritable(r,i),i,`${t}.`)})}return e}toEvalContext(e,t){let r=this.view(t?.aliases),s={};for(let[o,a]of r)s[o]=a.kind==="owned"?a.bag.values:a.resolver;let i=this.qualityLadders(r);return i.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(o,a)=>{let c=r.get(o);return c?i.get(o)?.get(Ye(c)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[r,s]of Object.entries(e??{})){let i=this.scopes.get(s);if(!i)throw new Error(`alias \'@${r}\' names \'${s}\', which is not registered`);t.set(r,i)}return t}qualityLadders(e){let t=new Map;for(let[r,s]of e)for(let[i,o]of ge(s)){if(o.type!=="quality"||o.stages===void 0)continue;let a=t.get(r);a||(a=new Map,t.set(r,a)),a.set(i,o.stages)}return t}toSchema(e){let t=new Map;for(let[r,s]of this.view(e?.aliases)){let i=ge(s);if(i.length===0)continue;let o=new Map;for(let[a,c]of i)o.set(a,{type:c.type,enumValues:c.values,...c.stages!==void 0?{stages:c.stages}:{}});t.set(r,o)}return{properties:t}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());for(let[t,r]of this.parked)e[t]=structuredClone(r);return e}load(e,t){t?.keepParked||this.parked.clear();for(let[r,s]of Object.entries(e)){let i=this.scopes.get(r);i?.kind==="owned"?i.bag.load(s):i||this.parked.set(r,structuredClone(s))}}saveFragment(){return{version:z,scopes:this.save()}}loadFragment(e){if(e.version!==z)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${z})`);this.load(e.scopes)}assertFree(e,t){let r=this.scopes.get(e);if(!r)return;let s=r.owner!==void 0?` by ${r.owner}`:"",i=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope \'@${e}\' is already registered${s}${i}`)}};function ge(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function Ye(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function P(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return n.values?.[0]??"";case"flags":return[];case"quality":return n.stages?.[0]??"";default:return!1}}var he=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function B(n){return n.ctx.host??{}}var Y={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new d("random(a, b) requires exactly 2 arguments");let t=B(e).nextRandom;if(!t)throw new d("random() called without a PRNG in context");let r=e.evaluate(n[0]),s=e.evaluate(n[1]);if(typeof r!="number"||typeof s!="number")throw new d("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new d("random(a, b) arguments must be integers");let i=Math.min(r,s),o=Math.max(r,s);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:me("check_flags"),eval(n,e){let t=fe(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new d("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:me("set_flags"),eval(n,e){let t=[...fe(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new d("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let i=t.indexOf(s.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:H("visits"),eval:(n,e)=>B(e).visits?.($(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:H("seen"),eval:(n,e)=>(B(e).visits?.($(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:H("patter_visits"),eval:(n,e)=>B(e).patterVisits?.($(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:H("patter_seen"),eval:(n,e)=>(B(e).patterVisits?.($(n,e,"patter_seen"))??0)>0}}},Rt=he.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function Se(n,e){let t=n.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function $(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new d(`${t}(id) requires a string node id`);return r}function H(n){return(e,t)=>{let r=e[0];r&&r.kind!=="string"&&t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(id): the argument must be a string id literal (a scene / block / node id)`})}}function fe(n,e,t){if(!n)throw new d(`${t}() requires at least one argument (the flags variable)`);let r=e.evaluate(n);if(Array.isArray(r))return r;if(r===!1||r===null||r===void 0)return[];throw new d(`${t}() first argument must be a flags property`)}function me(n){return(e,t)=>{if(e.length===0)return;let r=e[0];if(r.kind!=="scopedvar"){t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): first argument must be a flags property reference (@name or @scope.name)`});return}let s=t.schema.properties.get(r.scope)?.get(r.name);if(s&&s.type!=="flags"){let i=r.scope===t.defaultScope?r.name:`${r.scope}.${r.name}`;t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): \'@${i}\' is not a flags property (got ${s.type})`});return}for(let i=1;i<e.length;i++){let o=e[i];o.kind!=="flagdelta"?t.report({path:[...t.path,"args",i],kind:"wrong-arg-type",severity:"error",message:`${n}(): argument ${i+1} must be +flagName or -flagName`}):s?.type==="flags"&&s.enumValues&&!s.enumValues.includes(o.name)&&t.report({path:[...t.path,"args",i],kind:"unknown-flag-name",severity:"error",message:`${n}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var Qe=/^@[A-Za-z0-9_.]+$/;function*Xe(n){let e="",t=0;for(;t<n.length;){let r=n[t];if(r==="{"&&n[t+1]==="{"){e+="{",t+=2;continue}if(r==="}"&&n[t+1]==="}"){e+="}",t+=2;continue}if(r==="{"){let s=n.indexOf("}",t+1);if(s!==-1){let i=n.slice(t,s+1),o=n.slice(t+1,s).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:Qe.test(o)?o:void 0},t=s+1;continue}e+=i,t=s+1;continue}}e+=r,t+=1}e&&(yield{kind:"text",value:e})}function Ze(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function et(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function tt(n){let e="",t=!1;for(let r of n){if(et(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function ye(n,e,t){if(e.length===0||n.indexOf(e)<0)return n;let r="",s=0,i=!1;for(;s<n.length;){if(n.startsWith(e,s)){let o=n.indexOf(t,s+e.length);if(o>=0){s=o+t.length,i=!0;continue}r+=n.slice(s);break}r+=n[s],s+=1}return i?tt(r):n}function ve(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of Xe(n)){if(r.kind==="text"){t+=r.value;continue}if(!r.ref){t+=r.raw;continue}let s=e(r.ref);t+=s===void 0?"":Ze(s)}return t}function I(n,e){for(let t of n){e(t);let r=t.children;r&&I(r,e)}}function rt(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function S(n){let e=n.gameId?.trim();return e||rt(n.name)}function Q(n){return`cast:${n}`}var X={open:"[",close:"]"},we="SFX";var Z=3;function N(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function q(n){let e=new Map,t=(r,s)=>{let i=N([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group"&&r.prompt&&e.set(r.prompt.id,N([...i,...r.prompt.tags??[]])),r.type==="group")for(let o of r.children)t(o,i);else for(let o of r.beats??[])e.set(o.id,N([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=N(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=N([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var be=new WeakMap,xe=new WeakMap;function nt(n){let e=xe.get(n);return e||(e=q(n),xe.set(n,e)),e}function C(n,e){return n.tagIndex.get(e)?.slice()}var A="Patter",F=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),ke=Symbol("ownsRegistry"),y={stage:n=>`patter/scene/${F(n)}`,flow:n=>`patter/flow/${F(n)}/`,flowGlobals:n=>`patter/flow/${F(n)}/patter`,flowScene:(n,e)=>`patter/flow/${F(n)}/scene/${F(e)}`},T=class{constructor(e){this.journal=e}journal},_=class n{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;engineLog=[];engineSeq=0;engineTraceHandlers=new Set;constructor(e,t={}){this.creationOptions=t;let r=t.locale??e.locales.default,s=e.strings;this.allStrings=s,this.currentLocale=r;let i=s[r]??{},o=s[e.locales.default]??{},a=e.localisation,c=a?.mode==="ids"&&!a.sourceDebug;this.sourceDebug=a?.mode==="ids"&&!!a.sourceDebug,this.sourceDebug&&typeof console<"u"&&console.warn("[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.");let l=new Map;for(let g of e.cast??[])g.displayName&&l.set(g.name,g.displayName);this.defaultSeed=(t.seed??2654435769)>>>0;let p=new Map,f=new Map,u=new Map;for(let[g,h]of Object.entries(e.scenes)){this.sceneGameIdToId.set(S(h),g);let v=new Map;for(let m of h.blocks)f.set(m.id,{sceneId:g}),u.set(m.id,m),v.set(S(m),m.id),I(m.children,j=>p.set(j.id,j));this.blockGameIdToId.set(g,v)}let ie=e.properties??[],U=ie.filter(g=>g.shared??!0).map(De),je=ie.filter(g=>!(g.shared??!0)).map(De),Ve=new Set(U.map(g=>g.name.toLowerCase())),x=t.registry??new M,oe=!t.registry||t[ke]===!0,ae=new b(U,{pathPrefix:"@patter."}),ce=[],G=[];try{x.mountOwned("patter",ae,{owner:A}),G.push("patter");let g=new Set;for(let[h,v]of Object.entries(Re(t))){let m=e.scopeRegistry?.scopes.find(Le=>Le.token===h),j=(m?.declarations??[]).map(Ne);x.defineForeign(h,v,j,{writable:m?.writable??!0,owner:A}),G.push(h),g.add(h)}for(let h of oe?e.scopeRegistry?.scopes??[]:[]){if(g.has(h.token)||x.has(h.token))continue;let v=(h.declarations??[]).map(m=>it(m,h.writable));x.defineOwned(h.token,v,{owner:A}),G.push(h.token),ce.push(h.token)}}catch(g){for(let h of G)x.remove(h,{keep:!0});throw g}let le=new Map;for(let[g,h]of Object.entries(e.scenes)){let v=new Set((h.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));le.set(g,v)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(g,h,v)=>this.emitEngine(g,h,v),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,nodeIndex:p,blockIndex:f,blockById:u,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:nt(e),registry:x,ownsRegistry:oe,patterBag:ae,hostScopes:ce,patterSharedDecls:U,patterLocalDecls:je,patterSharedNames:Ve,sceneSharedNames:le,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(g=>console.warn(`Patterplay: ${g.kind} on ${g.node} in flow \'${g.flow}\' failed, played through: ${g.message}`)),replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??X).open,captionClose:(e.closedCaptions??X).close,captionCharacter:e.closedCaptions?.character||we,refSplitCache:new Map,refSplitRevision:x.revision}}get locale(){return this.currentLocale}get isSourceDebug(){return this.sourceDebug}get buildId(){return this.host.bundle.content?.hash??""}setLocale(e){this.currentLocale=e,this.host.strings=this.allStrings[e]??{}}replaceStrings(e){this.allStrings=e.strings,this.host.strings=this.allStrings[this.currentLocale]??{},this.host.defaultStrings=this.allStrings[this.host.bundle.locales.default]??{}}hotSwap(e){this.refuseInCheckpoint("hotSwap");let t=this.saveGame(),r=o=>(o.setLocale(this.currentLocale),o.setClosedCaptions(this.host.captionsOn),o),s=()=>new n(e,{...this.creationOptions,registry:this.host.registry,[ke]:this.host.ownsRegistry});this.release(!0);let i=s();try{return i.loadGame(t),r(i)}catch{i.release(!0);let o=s();for(let[a,c]of Object.entries(t.flows)){let l=c.cursor.currentSceneId;try{o.openFlow(a,l!==null?{scene:l}:{})}catch{}}return r(o)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:r,blockId:s}=this.resolveOpenAddress(t.scene,t.block),i=this.flowsById.get(e),o=this.host.journal;if(o&&i&&!i.isClosed)throw new Error(`openFlow would replace the open flow \'${e}\', which can\'t be undone while a checkpoint is open`);i?.close();let a=new D(e,this.host,t.seed??this.defaultSeed);return o&&(o.flows.add(a),o.opened.add(a),o.undo.push(()=>{a.close(),i?this.flowsById.set(e,i):this.flowsById.delete(e)})),this.flowsById.set(e,a),a.begin(r,s),a}runFlow(e,t,r){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:r}).advanceToStop().played;if(!s.goto(t,r))throw new Error(`runFlow: address not found: ${t}${r===void 0?"":` / ${r}`}`);return s.advanceToStop().played}assertExternalScopes(){for(let e of this.host.bundle.externalScopes??[])if(!this.host.registry.has(e))throw new Error(`this content names @${e}, which no engine on this registry registered: give every engine the game\'s one registry`)}resolveOpenAddress(e,t){if(e!==void 0){let r=te(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=re(this.host,r,t);if(s===void 0)throw new Error(`unknown block: ${t}`);return{sceneId:r,blockId:s}}if(t!==void 0){if(!this.host.blockIndex.has(t))throw new Error(`unknown block: ${t}`);return{blockId:t}}if(Object.keys(this.host.bundle.scenes).length===0)throw new Error("no scenes in bundle");return{}}resolveSceneRef(e){if(e!=null)return te(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:re(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?S(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?S(t):void 0}tagsForBeat(e){return C(this.host,e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?C(this.host,t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?C(this.host,s):void 0)??[]}gameDataForScene(e){let t=this.resolveSceneRef(e);return{...(t!=null?this.host.bundle.scenes[t]:void 0)?.gameData}}gameDataForBlock(e,t){let r=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(r!=null?this.host.blockById.get(r):void 0)?.gameData}}getCast(){let e=[];for(let t of this.host.bundle.cast??[])t?.name&&e.push(t.name);return e}castForScene(e){let t=this.resolveSceneRef(e),r=t!=null?this.host.bundle.scenes[t]:void 0;if(!r)return[];let s=new Set;for(let i of r.blocks)Ee(i.children,s);return[...s]}castForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t),i=s!=null?this.host.blockById.get(s):void 0;if(!i)return[];let o=new Set;return Ee(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...S(e)?{gameId:S(e)}:{},name:e.name,...Ce(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...S(t)?{gameId:S(t)}:{},name:t.name,...Ce(t.gameData),...this.tagsField(t.id),children:t.children.map(r=>this.outlineNode(r))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let r of t.blocks)I(r.children,s=>{if(s.type==="snippet")for(let i of s.beats??[])e.push({sceneId:t.id,blockId:r.id,snippetId:s.id,beat:this.beatInfo(i)})});return e}outlineNode(e){return e.type==="group"?{type:"group",id:e.id,...this.tagsField(e.id),...e.selector?{selector:e.selector}:{},...e.prompt?{prompt:this.beatInfo(e.prompt)}:{},children:e.children.map(t=>this.outlineNode(t))}:{type:"snippet",id:e.id,...this.tagsField(e.id),beats:(e.beats??[]).map(t=>this.beatInfo(t)),...e.jump?{jumpTo:e.jump.to,...e.jump.mode?{jumpMode:e.jump.mode}:{}}:{}}}beatInfo(e){let t=C(this.host,e.id),r={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){r.character=e.character;let s=this.host.defaultStrings[Q(e.character)]??this.host.castDisplay.get(e.character);s!==void 0&&(r.characterName=s)}e.direction!==void 0&&(r.direction=e.direction)}if(e.kind==="line"||e.kind==="text"){let s=this.host.defaultStrings[e.id];s!==void 0&&(r.text=s)}return e.gameData&&Object.keys(e.gameData).length&&(r.gameData=e.gameData),t&&t.length&&(r.tags=t),r}tagsField(e){let t=C(this.host,e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}listBags(){let e=[{bag:this.host.patterBag}];for(let[t,r]of this.host.stageBags)e.push({bag:r,pathPrefix:`@scene:${t}.`});return e}checkpoint(){if(this.host.journal)throw new Error("a checkpoint is already open: roll it back or commit it first");let e={undo:[],flows:new Set,opened:new Set,selectors:new Set,flowSelectors:new Map};return this.host.journal=e,new T(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let r=t.length-1;r>=0;r--)t[r]();n.forget(e.journal)}commit(e){this.endCheckpoint(e),n.forget(e.journal)}get inCheckpoint(){return this.host.journal!==null}endCheckpoint(e){if(this.host.journal!==e.journal)throw new Error("that checkpoint is not the open one");this.host.journal=null}static forget(e){e.undo=[],e.flows.clear(),e.opened.clear(),e.selectors.clear(),e.flowSelectors.clear()}refuseInCheckpoint(e){if(this.host.journal)throw new Error(`${e} can\'t be undone, so it isn\'t allowed while a checkpoint is open`)}closeFlow(e){this.refuseInCheckpoint("closeFlow"),this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){this.refuseInCheckpoint("reset");for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.patterBag.reseed(this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear();for(let e of this.host.stageBags.keys())this.host.registry.remove(y.stage(e));this.host.stageBags.clear(),this.host.registry.discardParked("patter/")}release(e){for(let r of this.flowsById.values())r.releaseBags(e),r.close();this.flowsById.clear();let t=this.host.registry;for(let r of this.host.stageBags.keys())t.remove(y.stage(r),{keep:e});this.host.stageBags.clear();for(let r of["patter",...this.host.hostScopes])t.has(r)&&t.remove(r,{keep:e});for(let r of Object.keys(Re(this.creationOptions)))t.has(r)&&t.remove(r)}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.registry.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e),i=this.host.journal?this.host.registry.get(r,s):void 0;this.host.registry.set(r,s,t,{host:!0}),this.host.journal&&i!==void 0&&this.host.journal.undo.push(()=>this.host.registry.set(r,s,i,{host:!0}))}log(){return this.engineLog}clearLog(){this.engineLog.length=0}onTrace(e){return this.engineTraceHandlers.add(e),this.host.tracing=!0,()=>{this.engineTraceHandlers.delete(e),this.host.tracing=this.host.logEnabled||this.engineTraceHandlers.size>0}}emitEngine(e,t,r){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineSeq++,...r?{scene:r}:{}})}getVisitCounts(){return Object.fromEntries(this.host.sharedVisits)}listProperties(){return this.host.patterSharedDecls.map(e=>({name:e.name,path:`@patter.${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:P(e),writable:e.writable??!0}))}splitShared(e){let t=Fe(this.host,e);if(t.scope==="scene")throw new Error(`\'${e}\': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);return t}save(){return{patter:this.host.patterBag.save()}}load(e){e.patter&&this.host.patterBag.load(e.patter)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:Z,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:Pe(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==Z)throw new Error(`unsupported save version: ${String(t)}`);let r=e.flows;if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("malformed save: no flows");this.assertExternalScopes();let s=this.host.registry;for(let[o,a]of this.flowsById)a.releaseBags(o in e.flows),a.close();this.flowsById.clear();for(let o of this.host.stageBags.keys())s.remove(y.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.version===2?ot(e):e.registry;i&&(this.host.ownsRegistry?s.load(i):s.load(i,{keepParked:!0})),this.host.sharedVisits.clear();for(let[o,a]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(o,a);this.host.sharedSelectors.clear();for(let[o,a]of Be(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new D(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}}},D=class{id;host;local;registered=new Set;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPrompt=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.patterBag.get(e):this.local.get(e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.registry.set("patter",e,t):this.local.set(e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;if(t!==null)return this.sceneBagFor(t,e)?.get(e)},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.sceneBagFor(r,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,r){this.id=e,this.host=t,this.rngState=O(r),this.local=this.newLocal(),this.evalCtx={scopes:{},host:{nextRandom:this.rng,visits:s=>this.visitCounts.get(s)??0,patterVisits:s=>this.host.sharedVisits.get(s)??0},qualities:(s,i)=>this.stagesFor(s,i)}}context(){let e=this.host.registry;if(e.revision!==this.ctxRevision){let t=e.toEvalContext(),r=this.evalCtx.scopes;for(let s of Object.keys(r))delete r[s];Object.assign(r,t.scopes),r.patter=this.patterResolver,r.scene=this.sceneResolver,this.registryQualities=t.qualities,this.ctxRevision=e.revision}return this.evalCtx}stagesFor(e,t){let r=t.toLowerCase();if(e==="patter")return ee(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(r);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:ee(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(r)}return this.registryQualities?.(e,t)??ee(this.host,`host/${e}`,()=>this.host.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(r)}begin(e,t){if(this.host.journal&&!this.host.journal.opened.has(this))throw new Error("a flow can\'t be started or reset while a checkpoint is open");if(this.releaseBags(!1),this.host.registry.discardParked(y.flow(this.id)),this.mountLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.activeSnippet=null,this.beatIndex=0,this.clearPending(),this.started=!0,t){let r=this.host.blockIndex.get(t);if(!r)throw new Error(`unknown block: ${t}`);this.enterSceneSetup(r.sceneId),this.stack=[{sceneId:r.sceneId,containerId:t,index:0}],this.enter(t)}else{let r=e??Object.keys(this.host.bundle.scenes)[0],s=r?this.host.bundle.scenes[r]:void 0;if(!s)throw new Error(r?`unknown scene: ${r}`:"no scenes in bundle");this.enterSceneSetup(r);let i=s.blocks[0];i&&(this.stack=[{sceneId:r,containerId:i.id,index:0}],this.enter(i.id))}this.settle()}reset(e,t){this.begin(e,t)}start(e,t){this.reset(e,t)}goto(e,t){if(this.closed)return!1;if(this.touch(),e==="END")return this.started=!0,this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let r=te(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=re(this.host,r,t),s===void 0)?!1:this.started?(this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??r,"jump"),this.settle(),!0):(this.begin(r,s),!0)}close(){this.releaseBags(!1),this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.clearPending()}clearPending(){this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null}touch(){let e=this.host.journal;if(!e||e.flows.has(this))return;e.flows.add(this);let{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p}=this,f=this.stack.map(u=>({...u}));e.undo.push(()=>{Object.assign(this,{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p,stack:f})})}get isClosed(){return this.closed}get currentScene(){return this.currentSceneId}advance(){if(this.closed)return{type:"end"};if(!this.started)throw new Error("flow has not been started");if(this.touch(),this.pendingPrompt){let e=this.pendingPrompt;return this.pendingPrompt=null,this.pendingPromptOwnerId=null,e.shown?this.promptResult(e.beat,e.shown):this.beatResult(e.beat)}return this.settle(),this.flowEnded?{type:"end"}:this.pendingChoice?{type:"choice",groupId:this.pendingChoice.groupId,options:this.pendingChoice.options}:this.activeSnippet?this.beatResult(this.activeSnippet.beats[this.beatIndex++]):(this.flowEnded=!0,{type:"end"})}advanceToStop(){let e=[];for(;;){let t=this.advance();if(t.type==="choice"||t.type==="end")return{played:e,stop:t};e.push(t)}}settle(){let e=0;for(;;){if(++e>1e4)throw new Error("flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content");if(this.flowEnded||this.pendingChoice)return;if(this.activeSnippet){if(this.beatIndex<(this.activeSnippet.beats?.length??0))return;this.runEffects(this.activeSnippet.onExit,this.activeSnippet.id);let i=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(i);continue}let t=this.stack[this.stack.length-1];if(!t){this.flowEnded=!0;return}t.sceneId!==this.currentSceneId&&(this.currentSceneId=t.sceneId);let r=this.childrenOf(t.containerId);if(!r){this.stack.pop();continue}let s=t.index;for(;t.index<r.length&&!this.eligible(r[t.index]);)t.index++;if(this.host.tracing&&t.index!==s&&this.emit({type:"select",group:t.containerId,selector:"run",children:r.slice(s,t.index+1).map((i,o)=>({id:i.id,eligible:s+o===t.index})),picked:r[t.index]?.id??null}),t.index>=r.length){this.stack.pop();continue}this.enterChild(r[t.index++])}}listBags(){let e=[{bag:this.local,pathPrefix:`${this.id}/@patter.`}];for(let[t,r]of this.sceneBags)e.push({bag:r,pathPrefix:`${this.id}/@scene:${t}.`});return e}log(){return this.flowLog}clearLog(){this.flowLog.length=0}emit(e){let t=this.currentSceneId??void 0;this.host.emitEngine(this.id,e,t),this.host.logEnabled&&this.flowLog.push({...e,seq:this.flowSeq++,...t?{scene:t}:{}})}getVisitCounts(){return Object.fromEntries(this.visitCounts)}getChoices(){return this.pendingChoice?.options??[]}choose(e){let t=this.pendingChoice;if(!t)throw new Error("no choice is pending");let r=t.options.find(o=>o.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);this.touch();let s=t.byId.get(e);this.host.tracing&&this.emit({type:"chose",group:t.groupId,option:e}),this.pendingChoice=null;let i=this.host.replayPromptOnChoose?this.authoredPromptOf(s):void 0;this.pendingPrompt=i&&r.prompt?{beat:i,shown:{...r.prompt}}:null,this.pendingPromptOwnerId=this.pendingPrompt?s.id:null,this.enterChild(s)}isEnded(){return this.flowEnded}getProperty(e){let{scope:t,name:r}=this.splitRef(e);return t==="patter"?this.patterResolver.get(r):t==="scene"?this.sceneResolver.get(r):this.host.registry.get(t,r)}setProperty(e,t){this.writeProperty(e,t,!0)}writeProperty(e,t,r){let{scope:s,name:i}=this.splitRef(e),o=this.host.journal;if(s==="patter"){if(o){let a=this.host.patterSharedNames.has(i),c=a?this.host.patterBag.get(i):this.local.get(i),l=this.local;c!==void 0&&o.undo.push(a?()=>this.host.registry.set("patter",i,c):()=>l.set(i,c))}this.patterResolver.set(i,t)}else if(s==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);if(o){let a=this.sceneBagFor(this.currentSceneId,i),c=a?.get(i);a&&c!==void 0&&o.undo.push(()=>a.set(i,c))}this.sceneResolver.set(i,t)}else{let a=o?this.host.registry.get(s,i):void 0;this.host.registry.set(s,i,t,r?{host:!0}:void 0),o&&a!==void 0&&o.undo.push(()=>this.host.registry.set(s,i,a,{host:!0}))}}snapshot(){return{rngState:this.rngState,visits:Object.fromEntries(this.visitCounts),cursor:{flowEnded:this.flowEnded,currentSceneId:this.currentSceneId,stack:this.stack.map(e=>{let t=this.childrenOf(e.containerId)?.[e.index];return t?{...e,nextId:t.id}:{...e}}),activeSnippetId:this.activeSnippet?.id??null,beatIndex:this.beatIndex,pendingChoice:this.pendingChoice?{groupId:this.pendingChoice.groupId,options:this.pendingChoice.options.map(e=>({...e}))}:null,pendingPromptOwnerId:this.pendingPromptOwnerId,...this.pendingPrompt?.shown?{pendingPrompt:st(this.pendingPrompt.shown)}:{},selectors:Pe(this.selectors)}}}restore(e){if(this.host.journal)throw new Error("a flow can\'t be restored while a checkpoint is open");this.restoreCursor(e),this.releaseBags(!0),this.mountLocal();for(let t of new Set([this.currentSceneId,...this.stack.map(r=>r.sceneId)]))t!==null&&this.host.bundle.scenes[t]&&this.ensureSceneBags(t)}restoreCursor(e){this.rngState=O(e.rngState),this.visitCounts=new Map(Object.entries(e.visits??{}));let t=e.cursor;if(this.started=!0,this.flowEnded=t.flowEnded,this.beatIndex=t.beatIndex,this.currentSceneId=t.currentSceneId,this.stack=t.stack.map(r=>{let{nextId:s,...i}=r;if(s!==void 0){let o=this.childrenOf(i.containerId)?.findIndex(a=>a.id===s)??-1;if(o>=0)return{...i,index:o}}return{...i}}),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=Be(t.selectors),this.pendingChoice=null,t.pendingChoice!==null){let r=new Map,s=[];for(let i of t.pendingChoice.options){let o=this.host.nodeIndex.get(i.id);o&&(r.set(i.id,o),s.push({...i}))}s.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:s,byId:r})}if(this.pendingPrompt=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId),s=r?this.authoredPromptOf(r):void 0;s?this.pendingPrompt={beat:s,...t.pendingPrompt?{shown:{...t.pendingPrompt}}:{}}:this.pendingPromptOwnerId=null}}enterSceneSetup(e){let t=this.host.bundle.scenes[e];if(!t)throw new Error(`unknown scene: ${e}`);this.currentSceneId=e,this.enter(e),this.seedScene(t),this.runEffects(t.onEntry,t.id)}enterChild(e){if(this.enter(e.id),e.type==="snippet"){this.beginSnippet(e);return}let t=e.selector??"run";if(t==="run"){this.stack.push({sceneId:this.currentSceneId,containerId:e.id,index:0});return}if(t==="choice"){this.setupChoice(e);return}let r=this.selectChild(e);r&&this.enterChild(r)}childrenOf(e){let t=this.host.blockById.get(e);if(t)return t.children;let r=this.host.nodeIndex.get(e);if(r&&r.type==="group")return r.children}beginSnippet(e){this.runEffects(e.onEnter,e.id),this.activeSnippet=e,this.beatIndex=0}setupChoice(e){let t=[],r=new Map,s=[];for(let o of e.children){if(o.fallback===!0){s.push(o);continue}if(o.sticky!==!0&&(this.visitCounts.get(o.id)??0)>=1)continue;let a=this.eligible(o),c=o.secretUntilEligible===!0;!a&&c||(t.push({id:o.id,prompt:this.promptFor(o),eligible:a,gameData:o.gameData}),r.set(o.id,o))}if(t.some(o=>o.eligible)){this.host.tracing&&this.emit({type:"choice",group:e.id,options:t.map(o=>({id:o.id,eligible:o.eligible}))}),this.pendingChoice={groupId:e.id,options:t,byId:r};return}let i=s.find(o=>this.eligible(o));if(i){this.enterChild(i);return}this.host.tracing&&this.emit({type:"dry",group:e.id}),this.host.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(this.host.tracing&&this.emit({type:"jump",to:e,mode:t}),e==="END"){this.flowEnded=!0,this.stack=[];return}let r,s,i=this.host.bundle.scenes[e];if(i){this.enterSceneSetup(e);let a=i.blocks[0];if(!a){t==="jump"&&(this.stack=[]);return}r=e,s=a.id}else{let a=this.host.blockIndex.get(e);if(!a)throw new Error(`jump target not found: ${e}`);a.sceneId!==this.currentSceneId&&this.enterSceneSetup(a.sceneId),r=a.sceneId,s=e}this.enter(s);let o={sceneId:r,containerId:s,index:0};t==="call"?this.stack.push(o):this.stack=[o]}selectChild(e){let t=e.children.map(c=>({id:c.id,eligible:this.eligible(c)})),r=e.children.filter((c,l)=>t[l].eligible),s=e.options?.order??"sequential",i=e.options?.exhaust??"once",o=c=>(this.host.tracing&&this.emit({type:"select",group:e.id,selector:e.selector??"default",...e.selector==="sequence"?{order:s,exhaust:i}:{},children:t,picked:c?.id??null}),c);if(r.length===0)return o(null);let a=this.selectorState(e);switch(e.selector){case"branch":return o(r[0]);case"sequence":return o(s==="shuffle"?this.pickShuffle(r,i,a):s==="specificity"?this.pickSpecificity(r,i,a):this.pickSequential(r,i,a));default:return null}}pickSequential(e,t,r){let s=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,r){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(u=>u.id),a=new Set(e.map(u=>u.id));if(r.bag===void 0&&(r.bag=o()),!r.bag.some(u=>a.has(u))){if(t==="once")return null;if(i){let u=e[s-1];return r.last=u.id,u}if(r.bag=o(),r.bag.length===0)return null}let c=r.bag.filter(u=>a.has(u)),l=r.last!==void 0&&c.length>1?c.indexOf(r.last):-1,p=Math.floor(this.rng()*(l>=0?c.length-1:c.length));l>=0&&p>=l&&p++;let f=c[p];return r.bag.splice(r.bag.indexOf(f),1),r.last=f,e.find(u=>u.id===f)}pickSpecificity(e,t,r){let s=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(p=>p.id));let l=new Set(r.bag);if(s=e.filter(p=>l.has(p.id)),s.length===0)return t==="stick"&&r.last!==void 0?e.find(p=>p.id===r.last)??null:null}let i=-1,a=s.map(l=>{let p=this.specScore(l);return p>i&&(i=p),{c:l,s:p}}).filter(l=>l.s===i).map(l=>l.c),c;if(a.length===1)c=a[0];else{let l=r.last!==void 0?a.findIndex(f=>f.id===r.last):-1,p=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&p>=l&&p++,c=a[p]}return t!=="repeat"&&(r.bag=r.bag.filter(l=>l!==c.id)),r.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0,e):0}matchedSpec(e,t,r){return de(e,i=>{try{return Oe(V(i,this.context(),Y))}catch(o){return this.reportError("best-match",r.id,r.condition,o),!1}},{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=this.host.journal,s;if(r&&(s=e.shared?r.selectors:r.flowSelectors.get(this),s||(s=new Set,r.flowSelectors.set(this,s))),r&&s&&!s.has(e.id)){s.add(e.id);let o=t.get(e.id),a=o?{...o,...o.bag?{bag:[...o.bag]}:{}}:void 0;r.undo.push(()=>{a?t.set(e.id,a):t.delete(e.id)})}let i=t.get(e.id);return i||(i={},t.set(e.id,i)),i}runEffects(e,t){for(let r of e??[]){let s;try{s=this.evalExpr(r.value);let i=this.host.tracing?this.getProperty(r.target):void 0;this.writeProperty(r.target,s,!1),this.host.tracing&&this.emit({type:"write",target:r.target,value:s,...i!==void 0?{prev:i}:{}})}catch(i){this.reportError("effect",t,r.value,i)}}}eligible(e){if(!e.condition)return!0;try{return Oe(this.evalExpr(e.condition))}catch(t){return this.reportError("condition",e.id,e.condition,t),!1}}reportError(e,t,r,s){let i=s instanceof Error?s.message:String(s),o=r?.src;this.host.onError({flow:this.id,kind:e,node:t,...o?{source:o}:{},message:i}),this.host.tracing&&this.emit({type:"diagnostic",kind:e,node:t,...o?{source:o}:{},message:i})}evalExpr(e){return V(this.conditionAst(e),this.context(),Y)}conditionAst(e){let t=be.get(e);return t||(t=k(e.ast),be.set(e,t)),t}enter(e){let t=this.visitCounts,r=t.get(e);t.set(e,(r??0)+1);let s=this.host.sharedVisits,i=s.get(e);s.set(e,(i??0)+1),this.host.journal&&this.host.journal.undo.push(()=>{r===void 0?t.delete(e):t.set(e,r),i===void 0?s.delete(e):s.set(e,i)})}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=K(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=C(this.host,e.id),r=t&&t.length?{tags:t}:{};switch(e.kind){case"gameEvent":return{type:"gameEvent",id:e.id,gameData:e.gameData,...r};case"text":return{type:"text",id:e.id,text:this.interpolate(this.resolveString(e.id)),gameData:e.gameData,...r};case"line":{let s=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?s:this.interpolate(s)),c=i&&a.length===0;return{type:"line",id:e.id,text:a,character:c?void 0:e.character,characterName:c?void 0:this.resolveCharacterName(e.character),direction:c?void 0:e.direction,gameData:e.gameData,...r}}}}interpolate(e){return ve(e,t=>this.getProperty(t))}stripCaptions(e){return ye(e,this.host.captionOpen,this.host.captionClose)}captionLine(e){return this.host.captionsOn?e:this.stripCaptions(e)}promptFor(e){let t=this.promptBeatOf(e);if(!t)return;let r=this.interpolate(this.resolveString(t.id));return t.kind==="line"?{kind:"line",text:this.captionLine(r),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction}:{kind:"text",text:r}}authoredPromptOf(e){return e.type==="group"?e.prompt:void 0}promptResult(e,t){let r=C(this.host,e.id),s=r&&r.length?{tags:r}:{};return t.kind==="text"?{type:"text",id:e.id,text:t.text,gameData:e.gameData,...s}:{type:"line",id:e.id,text:t.text,character:t.character,characterName:t.characterName,direction:t.direction,gameData:e.gameData,...s}}promptBeatOf(e){return e.type==="group"&&e.prompt?e.prompt:((e.type==="snippet"?e:this.firstTextSnippetIn(e.children))?.beats??[]).find(r=>r.kind==="line"||r.kind==="text")}firstTextSnippetIn(e){let t;return I(e,r=>{!t&&r.type==="snippet"&&(r.beats??[]).some(s=>s.kind==="line"||s.kind==="text")&&(t=r)}),t}resolveString(e){if(this.host.emitIds)return e;let t=this.host.strings[e];if(t!==void 0)return t;let r=this.host.defaultStrings[e];return r!==void 0?`<Untranslated: ${e}> ${r}`:e}resolveCharacterName(e){if(e===void 0||this.host.emitIds)return;let t=Q(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}splitRef(e){return Fe(this.host,e)}newLocal(){return new b(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=y.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:A}),this.registered.add(e)}releaseBags(e){for(let t of this.registered)this.host.registry.remove(t,{keep:e});this.registered.clear(),this.sceneBags.clear()}sceneBagFor(e,t){if(this.host.bundle.scenes[e])return this.ensureSceneBags(e),this.host.sceneSharedNames.get(e)?.has(t.toLowerCase())?this.host.stageBags.get(e):this.sceneBags.get(e)}ensureSceneBags(e){let t=this.host.sceneSharedNames.get(e)??new Set,r=this.host.bundle.scenes[e]?.sceneProps??[],s=this.host.journal;if(!this.sceneBags.has(e)){let i=new b(r.filter(c=>!t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),o=y.flowScene(this.id,e),a=Ie(this.host.registry,o,i,s!==null);this.registered.add(o),this.sceneBags.set(e,i),s&&s.undo.push(()=>{this.sceneBags.get(e)===i&&(this.host.registry.remove(o,a?{keep:!0}:void 0),this.registered.delete(o),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let i=new b(r.filter(a=>t.has(a.name.toLowerCase())),{pathPrefix:"@scene."}),o=Ie(this.host.registry,y.stage(e),i,s!==null);this.host.stageBags.set(e,i),s&&s.undo.push(()=>{this.host.stageBags.get(e)===i&&(this.host.registry.remove(y.stage(e),o?{keep:!0}:void 0),this.host.stageBags.delete(e))})}}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;this.ensureSceneBags(e.id);for(let r of e.sceneProps??[]){if(!r.temporary)continue;let s=r.name.toLowerCase(),i=t.has(s)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);if(!i)continue;let o=this.host.journal?i.get(s):void 0;o!==void 0&&this.host.journal.undo.push(()=>i.set(s,o)),i.set(s,P(r))}}};function Ee(n,e){I(n,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let r of t.beats??[])r.kind==="line"&&r.character&&e.add(r.character)})}function Ce(n){return n&&Object.keys(n).length?{gameData:n}:{}}function st(n){let e={kind:n.kind,text:n.text};return n.character!==void 0&&(e.character=n.character),n.characterName!==void 0&&(e.characterName=n.characterName),n.direction!==void 0&&(e.direction=n.direction),e}function Pe(n){let e={};for(let[t,r]of n){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e[t]=s}return e}function Be(n){let e=new Map;for(let[t,r]of Object.entries(n??{})){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e.set(t,s)}return e}function Ie(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:A}),r&&JSON.stringify(t.save())!==s}function De(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Re(n){return{...n.world?{world:n.world}:{},...n.hostScopes}}function ee(n,e,t){let r=n.qualityLadders.get(e);if(!r){r=new Map;for(let s of t()??[]){let i=s.name.toLowerCase();s.type==="quality"&&s.stages&&!r.has(i)&&r.set(i,s.stages)}n.qualityLadders.set(e,r)}return r}function Ne(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function it(n,e){let t=Ne(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function te(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function re(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function Fe(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=Se(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function ot(n){let e={};n.shared?.patter&&(e.patter=n.shared.patter);for(let[t,r]of Object.entries(n.stageBags??{}))e[y.stage(t)]=r;for(let[t,r]of Object.entries(n.flows??{})){r.scopes?.patter&&(e[y.flowGlobals(t)]=r.scopes.patter);for(let[s,i]of Object.entries(r.sceneBags??{}))e[y.flowScene(t,s)]=i}return e}function Oe(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var at=(n,e)=>n.shared??e;function ne(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:at(n,e)}}function ct(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function lt(n,e){e.blocks++;let t=[...n.children];for(;t.length;){let r=t.pop();if(r.type==="group"){e.groups++,r.prompt&&e.prompts++,t.push(...r.children);continue}e.snippets++;for(let s of r.beats??[])e.beats++,s.kind==="gameEvent"&&e.gameEvents++}}function Ae(n){let e={scenes:0,blocks:0,groups:0,snippets:0,beats:0,prompts:0,gameEvents:0,cast:n.cast?.length??0},t=[],r=[];for(let o of Object.values(n.scenes)){e.scenes++;let a=S(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:S(c),name:c.name}))});for(let c of o.blocks)lt(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>ne(c,!1))})}let s=(n.scopeRegistry?.scopes??[]).map(o=>({token:o.token,writable:o.writable??!0,opaque:o.declarations===void 0,properties:(o.declarations??[]).map(a=>ne(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(ct)}));return{identity:{schema:n.schema,project:n.content.project,...n.content.version!==void 0?{version:n.content.version}:{},...n.content.hash!==void 0?{hash:n.content.hash}:{},...n.content.structureHash!==void 0?{structureHash:n.content.structureHash}:{},voiced:n.voiced,defaultLocale:n.locales.default,locales:[...n.locales.included],localisation:n.localisation?.mode??"embedded",sourceDebug:n.localisation?.sourceDebug??!1},addresses:t,hostScopes:s,properties:{patter:(n.properties??[]).map(o=>ne(o,!0)),scene:r},gameData:i,counts:e}}function Te(n,e){return n.gameDataFields?.[e]??[]}function se(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function Ge(n,e){let t={};for(let r of n){let s=se(n,e,r.name);s!==void 0&&(t[r.name]=s)}for(let[r,s]of Object.entries(e??{}))r in t||(t[r]=s);return t}return Ue(pt);})();\n';
|
|
201279
|
+
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var W=Object.defineProperty;var $e=Object.getOwnPropertyDescriptor;var He=Object.getOwnPropertyNames;var _e=Object.prototype.hasOwnProperty;var Ue=(n,e)=>{for(var t in e)W(n,t,{get:e[t],enumerable:!0})},We=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of He(e))!_e.call(n,s)&&s!==t&&W(n,s,{get:()=>e[s],enumerable:!(r=$e(e,s))||r.enumerable});return n};var Je=n=>We(W({},"__esModule",{value:!0}),n);var gt={};Ue(gt,{Checkpoint:()=>T,Engine:()=>_,Flow:()=>D,buildTagIndex:()=>H,describeBundle:()=>Ge,effectiveGameData:()=>Ve,gameDataFields:()=>je,gameDataValue:()=>ie});function R(n,e){return n!==void 0&&Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0}function k(n){switch(n[0]){case"b":return{kind:"bool",value:n[1]};case"n":return{kind:"number",value:n[1]};case"s":return{kind:"string",value:n[1]};case"sv":return{kind:"scopedvar",scope:n[1],name:n[2]};case"u":return{kind:"unary",op:n[1],operand:k(n[2])};case"bin":return{kind:"binary",op:n[1],left:k(n[2]),right:k(n[3])};case"call":{let e=n.slice(2).map(k);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var g=class extends Error{constructor(e){super(e),this.name="EvalError"}};function V(n,e,t){let r=new Map(t.scopes.map(i=>[i.token,i.missing??"false"])),s=i=>{switch(i.kind){case"bool":return i.value;case"number":return i.value;case"string":return i.value;case"scopedvar":{let o=R(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):R(o,i.name);if(a===void 0){if(r.get(i.scope)==="throw")throw new g(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{if(i.name==="advance"&&!R(t.functions,i.name)){let a=i.args[0];if(i.args.length!==1||a===void 0)throw new g(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=J(a,e);if(c===void 0)throw new g("advance() needs a quality reference (@scope.name of a quality property)");let l=w(s(a),c,"advance");return c[Math.min(l+1,c.length-1)]}let o=R(t.functions,i.name);if(!o)throw new g(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new g("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=s(i.operand);if(typeof a!="boolean")throw new g(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new g(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let f=s(i.left);if(typeof f!="boolean")throw new g(`\'and\' requires boolean operands, left is ${typeof f}`);if(!f)return!1;let u=s(i.right);if(typeof u!="boolean")throw new g(`\'and\' requires boolean operands, right is ${typeof u}`);return u}if(i.op==="or"){let f=s(i.left);if(typeof f!="boolean")throw new g(`\'or\' requires boolean operands, left is ${typeof f}`);if(f)return!0;let u=s(i.right);if(typeof u!="boolean")throw new g(`\'or\' requires boolean operands, right is ${typeof u}`);return u}let o=s(i.left),a=s(i.right),c=J(i.left,e),l=J(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!Qe(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new g(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return w(o,p,">")>w(a,p,">");case">=":return w(o,p,">=")>=w(a,p,">=");case"<":return w(o,p,"<")<w(a,p,"<");case"<=":return w(o,p,"<=")<=w(a,p,"<=");case"+":case"-":case"*":case"/":throw new g(`\'${i.op}\' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(i.op){case"==":return ge(o,a);case"!=":return!ge(o,a);case">":return E(o,a,">"),o>a;case">=":return E(o,a,">="),o>=a;case"<":return E(o,a,"<"),o<a;case"<=":return E(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new g(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return E(o,a,"-"),o-a;case"*":return E(o,a,"*"),o*a;case"/":if(E(o,a,"/"),a===0)throw new g("division by zero");return o/a}}}};return s(n)}function ge(n,e){if(Array.isArray(n)||Array.isArray(e)){if(!Array.isArray(n)||!Array.isArray(e)||n.length!==e.length)return!1;let t=[...n].sort(),r=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==r[s])return!1;return!0}return n===e}function E(n,e,t){if(typeof n!="number"||typeof e!="number")throw new g(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function J(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function w(n,e,t){if(typeof n!="string")throw new g(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new g(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var Qe=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function O(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function Q(n){let e=O(n);return{next(){e=e+1831565813>>>0;let t=Math.imul(e^e>>>15,1|e);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296},state(){return e}}}var Ke={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},ze=[Ke];function ue(n,e,t){let r=t?.countingCalls??ze;return L(n,t?.want??!0,e,r)}function L(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=L(n.left,e,t,r),i=L(n.right,e,t,r);return n.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(n.kind==="unary"&&n.op==="not")return L(n.operand,!e,t,r);if(n.kind==="call"){let s=r.find(i=>i.name===n.name);if(s){let i=s.count(n),o=t(n);return e?o?i:0:o?0:1}}return t(n)===e?1:0}var b=class n{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let r=this.norm(t.name);this.decls.set(r,t),this.values[r]=structuredClone(t.default??P(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,r){let s=this.norm(e);if(!r?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:r?.silent??!1,reason:r?.reason};this.values[s]=t;for(let o of[...this.auditors])o(i);if(!i.silent)for(let o of[...this.subscribers])o(i);return i}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}onAudit(e){return this.auditors.add(e),()=>this.auditors.delete(e)}rows(){return[...this.decls.entries()].map(([e,t])=>fe(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm,pathPrefix:this.pathPrefix});return e.decls=new Map(this.decls),Object.assign(e.values,structuredClone(this.values)),e}reseed(e){for(let t of Object.keys(this.values))delete this.values[t];this.decls.clear(),this.seed(e)}save(){return structuredClone(this.values)}load(e){for(let[t,r]of Object.entries(e))this.values[this.norm(t)]=r}};function fe(n,e,t,r,s=""){let i=r??n.name.toLowerCase();return{name:i,path:s+i,type:n.type,value:e,default:n.default??P(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var Ye=n=>n.toLowerCase(),K=1,M=class{scopes=new Map;parked=new Map;rev=0;get revision(){return this.rev}defineOwned(e,t,r){let s=typeof r=="string"?{pathPrefix:r}:r??{},i=new b(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,i,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,r){this.assertFree(e,r?.owner),this.scopes.set(e,{kind:"owned",bag:t,...r?.owner!==void 0?{owner:r.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(t.load(s),this.parked.delete(e)),this}remove(e,t){let r=this.scopes.get(e);if(!r)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&r.kind==="owned"&&this.parked.set(e,r.bag.save()),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&this.parked.delete(t);return this}ownedBag(e){let t=this.scopes.get(e);if(!t||t.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);return t.bag}reseedOwned(e,t){return this.ownedBag(e).reseed(t),this}defineForeign(e,t,r=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??Ye,a=new Map;for(let c of r)a.set(o(c.name),c);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:i.writable??!0,norm:o,...i.owner!==void 0?{owner:i.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let r=this.scopes.get(e);if(r)return r.kind==="owned"?r.bag.get(t):r.resolver.get(r.norm(t))}set(e,t,r,s){let i=this.scopes.get(e);if(!i)throw new Error(`unknown scope \'@${e}\'`);if(i.kind==="owned"){if(!s?.host&&!i.bag.writable(t))throw new Error(`\'@${e}.${t}\' is read-only`);i.bag.set(t,r,s?.host?{host:!0}:void 0);return}let o=i.norm(t);if(!i.resolver.set)throw new Error(`\'@${e}.${t}\' is read-only`);if(!s?.host&&!this.foreignWritable(i,o))throw new Error(`\'@${e}.${t}\' is read-only`);i.resolver.set(o,r)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,r]of this.scopes){let s=r.owner!==void 0?{owner:r.owner}:{};if(r.kind==="owned")for(let i of r.bag.rows())e.push({scope:t,...s,...i});else for(let[i,o]of r.decls)e.push({scope:t,...s,...fe(o,r.resolver.get(i),this.foreignWritable(r,i),i,`${t}.`)})}return e}toEvalContext(e,t){let r=this.view(t?.aliases),s={};for(let[o,a]of r)s[o]=a.kind==="owned"?a.bag.values:a.resolver;let i=this.qualityLadders(r);return i.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(o,a)=>{let c=r.get(o);return c?i.get(o)?.get(Xe(c)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[r,s]of Object.entries(e??{})){let i=this.scopes.get(s);if(!i)throw new Error(`alias \'@${r}\' names \'${s}\', which is not registered`);t.set(r,i)}return t}qualityLadders(e){let t=new Map;for(let[r,s]of e)for(let[i,o]of he(s)){if(o.type!=="quality"||o.stages===void 0)continue;let a=t.get(r);a||(a=new Map,t.set(r,a)),a.set(i,o.stages)}return t}toSchema(e){let t=new Map;for(let[r,s]of this.view(e?.aliases)){let i=he(s);if(i.length===0)continue;let o=new Map;for(let[a,c]of i)o.set(a,{type:c.type,enumValues:c.values,...c.stages!==void 0?{stages:c.stages}:{}});t.set(r,o)}return{properties:t}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());for(let[t,r]of this.parked)e[t]=structuredClone(r);return e}load(e,t){t?.keepParked||this.parked.clear();for(let[r,s]of Object.entries(e)){let i=this.scopes.get(r);i?.kind==="owned"?i.bag.load(s):i||this.parked.set(r,structuredClone(s))}}saveFragment(){return{version:K,scopes:this.save()}}loadFragment(e){if(e.version!==K)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${K})`);this.load(e.scopes)}assertFree(e,t){let r=this.scopes.get(e);if(!r)return;let s=r.owner!==void 0?` by ${r.owner}`:"",i=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope \'@${e}\' is already registered${s}${i}`)}};function he(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function Xe(n){return n.kind==="foreign"?n.norm:e=>n.bag.normalise(e)}function P(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return n.values?.[0]??"";case"flags":return[];case"quality":return n.stages?.[0]??"";default:return!1}}var me=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function B(n){return n.ctx.host??{}}var z={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new g("random(a, b) requires exactly 2 arguments");let t=B(e).nextRandom;if(!t)throw new g("random() called without a PRNG in context");let r=e.evaluate(n[0]),s=e.evaluate(n[1]);if(typeof r!="number"||typeof s!="number")throw new g("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new g("random(a, b) arguments must be integers");let i=Math.min(r,s),o=Math.max(r,s);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:ye("check_flags"),eval(n,e){let t=Se(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new g("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:ye("set_flags"),eval(n,e){let t=[...Se(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new g("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let i=t.indexOf(s.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:$("visits"),eval:(n,e)=>B(e).visits?.(q(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:$("seen"),eval:(n,e)=>(B(e).visits?.(q(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:$("patter_visits"),eval:(n,e)=>B(e).patterVisits?.(q(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:$("patter_seen"),eval:(n,e)=>(B(e).patterVisits?.(q(n,e,"patter_seen"))??0)>0}}},Pt=me.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function ve(n,e){let t=n.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function q(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new g(`${t}(id) requires a string node id`);return r}function $(n){return(e,t)=>{let r=e[0];r&&r.kind!=="string"&&t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(id): the argument must be a string id literal (a scene / block / node id)`})}}function Se(n,e,t){if(!n)throw new g(`${t}() requires at least one argument (the flags variable)`);let r=e.evaluate(n);if(Array.isArray(r))return r;if(r===!1||r===null||r===void 0)return[];throw new g(`${t}() first argument must be a flags property`)}function ye(n){return(e,t)=>{if(e.length===0)return;let r=e[0];if(r.kind!=="scopedvar"){t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): first argument must be a flags property reference (@name or @scope.name)`});return}let s=t.schema.properties.get(r.scope)?.get(r.name);if(s&&s.type!=="flags"){let i=r.scope===t.defaultScope?r.name:`${r.scope}.${r.name}`;t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${n}(): \'@${i}\' is not a flags property (got ${s.type})`});return}for(let i=1;i<e.length;i++){let o=e[i];o.kind!=="flagdelta"?t.report({path:[...t.path,"args",i],kind:"wrong-arg-type",severity:"error",message:`${n}(): argument ${i+1} must be +flagName or -flagName`}):s?.type==="flags"&&s.enumValues&&!s.enumValues.includes(o.name)&&t.report({path:[...t.path,"args",i],kind:"unknown-flag-name",severity:"error",message:`${n}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var Ze=/^@[A-Za-z0-9_.]+$/;function*et(n){let e="",t=0;for(;t<n.length;){let r=n[t];if(r==="{"&&n[t+1]==="{"){e+="{",t+=2;continue}if(r==="}"&&n[t+1]==="}"){e+="}",t+=2;continue}if(r==="{"){let s=n.indexOf("}",t+1);if(s!==-1){let i=n.slice(t,s+1),o=n.slice(t+1,s).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:Ze.test(o)?o:void 0},t=s+1;continue}e+=i,t=s+1;continue}}e+=r,t+=1}e&&(yield{kind:"text",value:e})}function tt(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function rt(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function nt(n){let e="",t=!1;for(let r of n){if(rt(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function we(n,e,t){if(e.length===0||n.indexOf(e)<0)return n;let r="",s=0,i=!1;for(;s<n.length;){if(n.startsWith(e,s)){let o=n.indexOf(t,s+e.length);if(o>=0){s=o+t.length,i=!0;continue}r+=n.slice(s);break}r+=n[s],s+=1}return i?nt(r):n}function be(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of et(n)){if(r.kind==="text"){t+=r.value;continue}if(!r.ref){t+=r.raw;continue}let s=e(r.ref);t+=s===void 0?"":tt(s)}return t}function I(n,e){for(let t of n){e(t);let r=t.children;r&&I(r,e)}}function st(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function S(n){let e=n.gameId?.trim();return e||st(n.name)}function Y(n){return`cast:${n}`}function X(n){return`qualifier:${n}`}var Z={open:"[",close:"]"},xe="SFX";var ee=3;function N(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function H(n){let e=new Map,t=(r,s)=>{let i=N([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group"&&r.prompt&&e.set(r.prompt.id,N([...i,...r.prompt.tags??[]])),r.type==="group")for(let o of r.children)t(o,i);else for(let o of r.beats??[])e.set(o.id,N([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=N(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=N([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var ke=new WeakMap,Ee=new WeakMap;function it(n){let e=Ee.get(n);return e||(e=H(n),Ee.set(n,e)),e}function C(n,e){return n.tagIndex.get(e)?.slice()}var A="Patter",F=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),Ce=Symbol("ownsRegistry"),y={stage:n=>`patter/scene/${F(n)}`,flow:n=>`patter/flow/${F(n)}/`,flowGlobals:n=>`patter/flow/${F(n)}/patter`,flowScene:(n,e)=>`patter/flow/${F(n)}/scene/${F(e)}`},T=class{constructor(e){this.journal=e}journal},_=class n{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;engineLog=[];engineSeq=0;engineTraceHandlers=new Set;constructor(e,t={}){this.creationOptions=t;let r=t.locale??e.locales.default,s=e.strings;this.allStrings=s,this.currentLocale=r;let i=s[r]??{},o=s[e.locales.default]??{},a=e.localisation,c=a?.mode==="ids"&&!a.sourceDebug;this.sourceDebug=a?.mode==="ids"&&!!a.sourceDebug,this.sourceDebug&&typeof console<"u"&&console.warn("[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.");let l=new Map;for(let d of e.cast??[])d.displayName&&l.set(d.name,d.displayName);let p=new Map;for(let d of e.qualifiers??[])d?.gameId&&p.set(d.gameId,d.name);this.defaultSeed=(t.seed??2654435769)>>>0;let f=new Map,u=new Map,oe=new Map;for(let[d,h]of Object.entries(e.scenes)){this.sceneGameIdToId.set(S(h),d);let v=new Map;for(let m of h.blocks)u.set(m.id,{sceneId:d}),oe.set(m.id,m),v.set(S(m),m.id),I(m.children,j=>f.set(j.id,j));this.blockGameIdToId.set(d,v)}let ae=e.properties??[],U=ae.filter(d=>d.shared??!0).map(Oe),Le=ae.filter(d=>!(d.shared??!0)).map(Oe),Me=new Set(U.map(d=>d.name.toLowerCase())),x=t.registry??new M,ce=!t.registry||t[Ce]===!0,le=new b(U,{pathPrefix:"@patter."}),pe=[],G=[];try{x.mountOwned("patter",le,{owner:A}),G.push("patter");let d=new Set;for(let[h,v]of Object.entries(Pe(t))){let m=e.scopeRegistry?.scopes.find(qe=>qe.token===h),j=(m?.declarations??[]).map(Ae);x.defineForeign(h,v,j,{writable:m?.writable??!0,owner:A}),G.push(h),d.add(h)}for(let h of ce?e.scopeRegistry?.scopes??[]:[]){if(d.has(h.token)||x.has(h.token))continue;let v=(h.declarations??[]).map(m=>at(m,h.writable));x.defineOwned(h.token,v,{owner:A}),G.push(h.token),pe.push(h.token)}}catch(d){for(let h of G)x.remove(h,{keep:!0});throw d}let de=new Map;for(let[d,h]of Object.entries(e.scenes)){let v=new Set((h.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));de.set(d,v)}this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(d,h,v)=>this.emitEngine(d,h,v),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:p,nodeIndex:f,blockIndex:u,blockById:oe,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:it(e),registry:x,ownsRegistry:ce,patterBag:le,hostScopes:pe,patterSharedDecls:U,patterLocalDecls:Le,patterSharedNames:Me,sceneSharedNames:de,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(d=>console.warn(`Patterplay: ${d.kind} on ${d.node} in flow \'${d.flow}\' failed, played through: ${d.message}`)),replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??Z).open,captionClose:(e.closedCaptions??Z).close,captionCharacter:e.closedCaptions?.character||xe,refSplitCache:new Map,refSplitRevision:x.revision}}get locale(){return this.currentLocale}get isSourceDebug(){return this.sourceDebug}get buildId(){return this.host.bundle.content?.hash??""}setLocale(e){this.currentLocale=e,this.host.strings=this.allStrings[e]??{}}replaceStrings(e){this.allStrings=e.strings,this.host.strings=this.allStrings[this.currentLocale]??{},this.host.defaultStrings=this.allStrings[this.host.bundle.locales.default]??{}}hotSwap(e){this.refuseInCheckpoint("hotSwap");let t=this.saveGame(),r=o=>(o.setLocale(this.currentLocale),o.setClosedCaptions(this.host.captionsOn),o),s=()=>new n(e,{...this.creationOptions,registry:this.host.registry,[Ce]:this.host.ownsRegistry});this.release(!0);let i=s();try{return i.loadGame(t),r(i)}catch{i.release(!0);let o=s();for(let[a,c]of Object.entries(t.flows)){let l=c.cursor.currentSceneId;try{o.openFlow(a,l!==null?{scene:l}:{})}catch{}}return r(o)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:r,blockId:s}=this.resolveOpenAddress(t.scene,t.block),i=this.flowsById.get(e),o=this.host.journal;if(o&&i&&!i.isClosed)throw new Error(`openFlow would replace the open flow \'${e}\', which can\'t be undone while a checkpoint is open`);i?.close();let a=new D(e,this.host,t.seed??this.defaultSeed);return o&&(o.flows.add(a),o.opened.add(a),o.undo.push(()=>{a.close(),i?this.flowsById.set(e,i):this.flowsById.delete(e)})),this.flowsById.set(e,a),a.begin(r,s),a}runFlow(e,t,r){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:r}).advanceToStop().played;if(!s.goto(t,r))throw new Error(`runFlow: address not found: ${t}${r===void 0?"":` / ${r}`}`);return s.advanceToStop().played}assertExternalScopes(){for(let e of this.host.bundle.externalScopes??[])if(!this.host.registry.has(e))throw new Error(`this content names @${e}, which no engine on this registry registered: give every engine the game\'s one registry`)}resolveOpenAddress(e,t){if(e!==void 0){let r=re(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=ne(this.host,r,t);if(s===void 0)throw new Error(`unknown block: ${t}`);return{sceneId:r,blockId:s}}if(t!==void 0){if(!this.host.blockIndex.has(t))throw new Error(`unknown block: ${t}`);return{blockId:t}}if(Object.keys(this.host.bundle.scenes).length===0)throw new Error("no scenes in bundle");return{}}resolveSceneRef(e){if(e!=null)return re(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:ne(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?S(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?S(t):void 0}tagsForBeat(e){return C(this.host,e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?C(this.host,t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?C(this.host,s):void 0)??[]}gameDataForScene(e){let t=this.resolveSceneRef(e);return{...(t!=null?this.host.bundle.scenes[t]:void 0)?.gameData}}gameDataForBlock(e,t){let r=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(r!=null?this.host.blockById.get(r):void 0)?.gameData}}getCast(){let e=[];for(let t of this.host.bundle.cast??[])t?.name&&e.push(t.name);return e}castForScene(e){let t=this.resolveSceneRef(e),r=t!=null?this.host.bundle.scenes[t]:void 0;if(!r)return[];let s=new Set;for(let i of r.blocks)Ie(i.children,s);return[...s]}castForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t),i=s!=null?this.host.blockById.get(s):void 0;if(!i)return[];let o=new Set;return Ie(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...S(e)?{gameId:S(e)}:{},name:e.name,...De(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...S(t)?{gameId:S(t)}:{},name:t.name,...De(t.gameData),...this.tagsField(t.id),children:t.children.map(r=>this.outlineNode(r))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let r of t.blocks)I(r.children,s=>{if(s.type==="snippet")for(let i of s.beats??[])e.push({sceneId:t.id,blockId:r.id,snippetId:s.id,beat:this.beatInfo(i)})});return e}outlineNode(e){return e.type==="group"?{type:"group",id:e.id,...this.tagsField(e.id),...e.selector?{selector:e.selector}:{},...e.prompt?{prompt:this.beatInfo(e.prompt)}:{},children:e.children.map(t=>this.outlineNode(t))}:{type:"snippet",id:e.id,...this.tagsField(e.id),beats:(e.beats??[]).map(t=>this.beatInfo(t)),...e.jump?{jumpTo:e.jump.to,...e.jump.mode?{jumpMode:e.jump.mode}:{}}:{}}}beatInfo(e){let t=C(this.host,e.id),r={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){r.character=e.character;let s=this.host.defaultStrings[Y(e.character)]??this.host.castDisplay.get(e.character);s!==void 0&&(r.characterName=s)}if(e.direction!==void 0&&(r.direction=e.direction),e.qualifier!==void 0){r.qualifier=e.qualifier;let s=this.host.defaultStrings[X(e.qualifier)]??this.host.qualifierDisplay.get(e.qualifier);s!==void 0&&(r.qualifierName=s)}}if(e.kind==="line"||e.kind==="text"){let s=this.host.defaultStrings[e.id];s!==void 0&&(r.text=s)}return e.gameData&&Object.keys(e.gameData).length&&(r.gameData=e.gameData),t&&t.length&&(r.tags=t),r}tagsField(e){let t=C(this.host,e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}listBags(){let e=[{bag:this.host.patterBag}];for(let[t,r]of this.host.stageBags)e.push({bag:r,pathPrefix:`@scene:${t}.`});return e}checkpoint(){if(this.host.journal)throw new Error("a checkpoint is already open: roll it back or commit it first");let e={undo:[],flows:new Set,opened:new Set,selectors:new Set,flowSelectors:new Map};return this.host.journal=e,new T(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let r=t.length-1;r>=0;r--)t[r]();n.forget(e.journal)}commit(e){this.endCheckpoint(e),n.forget(e.journal)}get inCheckpoint(){return this.host.journal!==null}endCheckpoint(e){if(this.host.journal!==e.journal)throw new Error("that checkpoint is not the open one");this.host.journal=null}static forget(e){e.undo=[],e.flows.clear(),e.opened.clear(),e.selectors.clear(),e.flowSelectors.clear()}refuseInCheckpoint(e){if(this.host.journal)throw new Error(`${e} can\'t be undone, so it isn\'t allowed while a checkpoint is open`)}closeFlow(e){this.refuseInCheckpoint("closeFlow"),this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){this.refuseInCheckpoint("reset");for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.patterBag.reseed(this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear();for(let e of this.host.stageBags.keys())this.host.registry.remove(y.stage(e));this.host.stageBags.clear(),this.host.registry.discardParked("patter/")}release(e){for(let r of this.flowsById.values())r.releaseBags(e),r.close();this.flowsById.clear();let t=this.host.registry;for(let r of this.host.stageBags.keys())t.remove(y.stage(r),{keep:e});this.host.stageBags.clear();for(let r of["patter",...this.host.hostScopes])t.has(r)&&t.remove(r,{keep:e});for(let r of Object.keys(Pe(this.creationOptions)))t.has(r)&&t.remove(r)}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.registry.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e),i=this.host.journal?this.host.registry.get(r,s):void 0;this.host.registry.set(r,s,t,{host:!0}),this.host.journal&&i!==void 0&&this.host.journal.undo.push(()=>this.host.registry.set(r,s,i,{host:!0}))}log(){return this.engineLog}clearLog(){this.engineLog.length=0}onTrace(e){return this.engineTraceHandlers.add(e),this.host.tracing=!0,()=>{this.engineTraceHandlers.delete(e),this.host.tracing=this.host.logEnabled||this.engineTraceHandlers.size>0}}emitEngine(e,t,r){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineSeq++,...r?{scene:r}:{}})}getVisitCounts(){return Object.fromEntries(this.host.sharedVisits)}listProperties(){return this.host.patterSharedDecls.map(e=>({name:e.name,path:`@patter.${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:P(e),writable:e.writable??!0}))}splitShared(e){let t=Te(this.host,e);if(t.scope==="scene")throw new Error(`\'${e}\': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);return t}save(){return{patter:this.host.patterBag.save()}}load(e){e.patter&&this.host.patterBag.load(e.patter)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:ee,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:Ne(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==ee)throw new Error(`unsupported save version: ${String(t)}`);let r=e.flows;if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("malformed save: no flows");this.assertExternalScopes();let s=this.host.registry;for(let[o,a]of this.flowsById)a.releaseBags(o in e.flows),a.close();this.flowsById.clear();for(let o of this.host.stageBags.keys())s.remove(y.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.version===2?ct(e):e.registry;i&&(this.host.ownsRegistry?s.load(i):s.load(i,{keepParked:!0})),this.host.sharedVisits.clear();for(let[o,a]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(o,a);this.host.sharedSelectors.clear();for(let[o,a]of Fe(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new D(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}}},D=class{id;host;local;registered=new Set;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPrompt=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.patterBag.get(e):this.local.get(e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.registry.set("patter",e,t):this.local.set(e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;if(t!==null)return this.sceneBagFor(t,e)?.get(e)},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.sceneBagFor(r,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,r){this.id=e,this.host=t,this.rngState=O(r),this.local=this.newLocal(),this.evalCtx={scopes:{},host:{nextRandom:this.rng,visits:s=>this.visitCounts.get(s)??0,patterVisits:s=>this.host.sharedVisits.get(s)??0},qualities:(s,i)=>this.stagesFor(s,i)}}context(){let e=this.host.registry;if(e.revision!==this.ctxRevision){let t=e.toEvalContext(),r=this.evalCtx.scopes;for(let s of Object.keys(r))delete r[s];Object.assign(r,t.scopes),r.patter=this.patterResolver,r.scene=this.sceneResolver,this.registryQualities=t.qualities,this.ctxRevision=e.revision}return this.evalCtx}stagesFor(e,t){let r=t.toLowerCase();if(e==="patter")return te(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(r);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:te(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(r)}return this.registryQualities?.(e,t)??te(this.host,`host/${e}`,()=>this.host.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(r)}begin(e,t){if(this.host.journal&&!this.host.journal.opened.has(this))throw new Error("a flow can\'t be started or reset while a checkpoint is open");if(this.releaseBags(!1),this.host.registry.discardParked(y.flow(this.id)),this.mountLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.activeSnippet=null,this.beatIndex=0,this.clearPending(),this.started=!0,t){let r=this.host.blockIndex.get(t);if(!r)throw new Error(`unknown block: ${t}`);this.enterSceneSetup(r.sceneId),this.stack=[{sceneId:r.sceneId,containerId:t,index:0}],this.enter(t)}else{let r=e??Object.keys(this.host.bundle.scenes)[0],s=r?this.host.bundle.scenes[r]:void 0;if(!s)throw new Error(r?`unknown scene: ${r}`:"no scenes in bundle");this.enterSceneSetup(r);let i=s.blocks[0];i&&(this.stack=[{sceneId:r,containerId:i.id,index:0}],this.enter(i.id))}this.settle()}reset(e,t){this.begin(e,t)}start(e,t){this.reset(e,t)}goto(e,t){if(this.closed)return!1;if(this.touch(),e==="END")return this.started=!0,this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let r=re(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=ne(this.host,r,t),s===void 0)?!1:this.started?(this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??r,"jump"),this.settle(),!0):(this.begin(r,s),!0)}close(){this.releaseBags(!1),this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.clearPending()}clearPending(){this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null}touch(){let e=this.host.journal;if(!e||e.flows.has(this))return;e.flows.add(this);let{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p}=this,f=this.stack.map(u=>({...u}));e.undo.push(()=>{Object.assign(this,{rngState:t,started:r,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:p,stack:f})})}get isClosed(){return this.closed}get currentScene(){return this.currentSceneId}advance(){if(this.closed)return{type:"end"};if(!this.started)throw new Error("flow has not been started");if(this.touch(),this.pendingPrompt){let e=this.pendingPrompt;return this.pendingPrompt=null,this.pendingPromptOwnerId=null,e.shown?this.promptResult(e.beat,e.shown):this.beatResult(e.beat)}return this.settle(),this.flowEnded?{type:"end"}:this.pendingChoice?{type:"choice",groupId:this.pendingChoice.groupId,options:this.pendingChoice.options}:this.activeSnippet?this.beatResult(this.activeSnippet.beats[this.beatIndex++]):(this.flowEnded=!0,{type:"end"})}advanceToStop(){let e=[];for(;;){let t=this.advance();if(t.type==="choice"||t.type==="end")return{played:e,stop:t};e.push(t)}}settle(){let e=0;for(;;){if(++e>1e4)throw new Error("flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content");if(this.flowEnded||this.pendingChoice)return;if(this.activeSnippet){if(this.beatIndex<(this.activeSnippet.beats?.length??0))return;this.runEffects(this.activeSnippet.onExit,this.activeSnippet.id);let i=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(i);continue}let t=this.stack[this.stack.length-1];if(!t){this.flowEnded=!0;return}t.sceneId!==this.currentSceneId&&(this.currentSceneId=t.sceneId);let r=this.childrenOf(t.containerId);if(!r){this.stack.pop();continue}let s=t.index;for(;t.index<r.length&&!this.eligible(r[t.index]);)t.index++;if(this.host.tracing&&t.index!==s&&this.emit({type:"select",group:t.containerId,selector:"run",children:r.slice(s,t.index+1).map((i,o)=>({id:i.id,eligible:s+o===t.index})),picked:r[t.index]?.id??null}),t.index>=r.length){this.stack.pop();continue}this.enterChild(r[t.index++])}}listBags(){let e=[{bag:this.local,pathPrefix:`${this.id}/@patter.`}];for(let[t,r]of this.sceneBags)e.push({bag:r,pathPrefix:`${this.id}/@scene:${t}.`});return e}log(){return this.flowLog}clearLog(){this.flowLog.length=0}emit(e){let t=this.currentSceneId??void 0;this.host.emitEngine(this.id,e,t),this.host.logEnabled&&this.flowLog.push({...e,seq:this.flowSeq++,...t?{scene:t}:{}})}getVisitCounts(){return Object.fromEntries(this.visitCounts)}getChoices(){return this.pendingChoice?.options??[]}choose(e){let t=this.pendingChoice;if(!t)throw new Error("no choice is pending");let r=t.options.find(o=>o.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);this.touch();let s=t.byId.get(e);this.host.tracing&&this.emit({type:"chose",group:t.groupId,option:e}),this.pendingChoice=null;let i=this.host.replayPromptOnChoose?this.authoredPromptOf(s):void 0;this.pendingPrompt=i&&r.prompt?{beat:i,shown:{...r.prompt}}:null,this.pendingPromptOwnerId=this.pendingPrompt?s.id:null,this.enterChild(s)}isEnded(){return this.flowEnded}getProperty(e){let{scope:t,name:r}=this.splitRef(e);return t==="patter"?this.patterResolver.get(r):t==="scene"?this.sceneResolver.get(r):this.host.registry.get(t,r)}setProperty(e,t){this.writeProperty(e,t,!0)}writeProperty(e,t,r){let{scope:s,name:i}=this.splitRef(e),o=this.host.journal;if(s==="patter"){if(o){let a=this.host.patterSharedNames.has(i),c=a?this.host.patterBag.get(i):this.local.get(i),l=this.local;c!==void 0&&o.undo.push(a?()=>this.host.registry.set("patter",i,c):()=>l.set(i,c))}this.patterResolver.set(i,t)}else if(s==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);if(o){let a=this.sceneBagFor(this.currentSceneId,i),c=a?.get(i);a&&c!==void 0&&o.undo.push(()=>a.set(i,c))}this.sceneResolver.set(i,t)}else{let a=o?this.host.registry.get(s,i):void 0;this.host.registry.set(s,i,t,r?{host:!0}:void 0),o&&a!==void 0&&o.undo.push(()=>this.host.registry.set(s,i,a,{host:!0}))}}snapshot(){return{rngState:this.rngState,visits:Object.fromEntries(this.visitCounts),cursor:{flowEnded:this.flowEnded,currentSceneId:this.currentSceneId,stack:this.stack.map(e=>{let t=this.childrenOf(e.containerId)?.[e.index];return t?{...e,nextId:t.id}:{...e}}),activeSnippetId:this.activeSnippet?.id??null,beatIndex:this.beatIndex,pendingChoice:this.pendingChoice?{groupId:this.pendingChoice.groupId,options:this.pendingChoice.options.map(e=>({...e}))}:null,pendingPromptOwnerId:this.pendingPromptOwnerId,...this.pendingPrompt?.shown?{pendingPrompt:ot(this.pendingPrompt.shown)}:{},selectors:Ne(this.selectors)}}}restore(e){if(this.host.journal)throw new Error("a flow can\'t be restored while a checkpoint is open");this.restoreCursor(e),this.releaseBags(!0),this.mountLocal();for(let t of new Set([this.currentSceneId,...this.stack.map(r=>r.sceneId)]))t!==null&&this.host.bundle.scenes[t]&&this.ensureSceneBags(t)}restoreCursor(e){this.rngState=O(e.rngState),this.visitCounts=new Map(Object.entries(e.visits??{}));let t=e.cursor;if(this.started=!0,this.flowEnded=t.flowEnded,this.beatIndex=t.beatIndex,this.currentSceneId=t.currentSceneId,this.stack=t.stack.map(r=>{let{nextId:s,...i}=r;if(s!==void 0){let o=this.childrenOf(i.containerId)?.findIndex(a=>a.id===s)??-1;if(o>=0)return{...i,index:o}}return{...i}}),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=Fe(t.selectors),this.pendingChoice=null,t.pendingChoice!==null){let r=new Map,s=[];for(let i of t.pendingChoice.options){let o=this.host.nodeIndex.get(i.id);o&&(r.set(i.id,o),s.push({...i}))}s.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:s,byId:r})}if(this.pendingPrompt=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId),s=r?this.authoredPromptOf(r):void 0;s?this.pendingPrompt={beat:s,...t.pendingPrompt?{shown:{...t.pendingPrompt}}:{}}:this.pendingPromptOwnerId=null}}enterSceneSetup(e){let t=this.host.bundle.scenes[e];if(!t)throw new Error(`unknown scene: ${e}`);this.currentSceneId=e,this.enter(e),this.seedScene(t),this.runEffects(t.onEntry,t.id)}enterChild(e){if(this.enter(e.id),e.type==="snippet"){this.beginSnippet(e);return}let t=e.selector??"run";if(t==="run"){this.stack.push({sceneId:this.currentSceneId,containerId:e.id,index:0});return}if(t==="choice"){this.setupChoice(e);return}let r=this.selectChild(e);r&&this.enterChild(r)}childrenOf(e){let t=this.host.blockById.get(e);if(t)return t.children;let r=this.host.nodeIndex.get(e);if(r&&r.type==="group")return r.children}beginSnippet(e){this.runEffects(e.onEnter,e.id),this.activeSnippet=e,this.beatIndex=0}setupChoice(e){let t=[],r=new Map,s=[];for(let o of e.children){if(o.fallback===!0){s.push(o);continue}if(o.sticky!==!0&&(this.visitCounts.get(o.id)??0)>=1)continue;let a=this.eligible(o),c=o.secretUntilEligible===!0;!a&&c||(t.push({id:o.id,prompt:this.promptFor(o),eligible:a,gameData:o.gameData}),r.set(o.id,o))}if(t.some(o=>o.eligible)){this.host.tracing&&this.emit({type:"choice",group:e.id,options:t.map(o=>({id:o.id,eligible:o.eligible}))}),this.pendingChoice={groupId:e.id,options:t,byId:r};return}let i=s.find(o=>this.eligible(o));if(i){this.enterChild(i);return}this.host.tracing&&this.emit({type:"dry",group:e.id}),this.host.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(this.host.tracing&&this.emit({type:"jump",to:e,mode:t}),e==="END"){this.flowEnded=!0,this.stack=[];return}let r,s,i=this.host.bundle.scenes[e];if(i){this.enterSceneSetup(e);let a=i.blocks[0];if(!a){t==="jump"&&(this.stack=[]);return}r=e,s=a.id}else{let a=this.host.blockIndex.get(e);if(!a)throw new Error(`jump target not found: ${e}`);a.sceneId!==this.currentSceneId&&this.enterSceneSetup(a.sceneId),r=a.sceneId,s=e}this.enter(s);let o={sceneId:r,containerId:s,index:0};t==="call"?this.stack.push(o):this.stack=[o]}selectChild(e){let t=e.children.map(c=>({id:c.id,eligible:this.eligible(c)})),r=e.children.filter((c,l)=>t[l].eligible),s=e.options?.order??"sequential",i=e.options?.exhaust??"once",o=c=>(this.host.tracing&&this.emit({type:"select",group:e.id,selector:e.selector??"default",...e.selector==="sequence"?{order:s,exhaust:i}:{},children:t,picked:c?.id??null}),c);if(r.length===0)return o(null);let a=this.selectorState(e);switch(e.selector){case"branch":return o(r[0]);case"sequence":return o(s==="shuffle"?this.pickShuffle(r,i,a):s==="specificity"?this.pickSpecificity(r,i,a):this.pickSequential(r,i,a));default:return null}}pickSequential(e,t,r){let s=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,r){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(u=>u.id),a=new Set(e.map(u=>u.id));if(r.bag===void 0&&(r.bag=o()),!r.bag.some(u=>a.has(u))){if(t==="once")return null;if(i){let u=e[s-1];return r.last=u.id,u}if(r.bag=o(),r.bag.length===0)return null}let c=r.bag.filter(u=>a.has(u)),l=r.last!==void 0&&c.length>1?c.indexOf(r.last):-1,p=Math.floor(this.rng()*(l>=0?c.length-1:c.length));l>=0&&p>=l&&p++;let f=c[p];return r.bag.splice(r.bag.indexOf(f),1),r.last=f,e.find(u=>u.id===f)}pickSpecificity(e,t,r){let s=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(p=>p.id));let l=new Set(r.bag);if(s=e.filter(p=>l.has(p.id)),s.length===0)return t==="stick"&&r.last!==void 0?e.find(p=>p.id===r.last)??null:null}let i=-1,a=s.map(l=>{let p=this.specScore(l);return p>i&&(i=p),{c:l,s:p}}).filter(l=>l.s===i).map(l=>l.c),c;if(a.length===1)c=a[0];else{let l=r.last!==void 0?a.findIndex(f=>f.id===r.last):-1,p=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&p>=l&&p++,c=a[p]}return t!=="repeat"&&(r.bag=r.bag.filter(l=>l!==c.id)),r.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0,e):0}matchedSpec(e,t,r){return ue(e,i=>{try{return Be(V(i,this.context(),z))}catch(o){return this.reportError("best-match",r.id,r.condition,o),!1}},{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=this.host.journal,s;if(r&&(s=e.shared?r.selectors:r.flowSelectors.get(this),s||(s=new Set,r.flowSelectors.set(this,s))),r&&s&&!s.has(e.id)){s.add(e.id);let o=t.get(e.id),a=o?{...o,...o.bag?{bag:[...o.bag]}:{}}:void 0;r.undo.push(()=>{a?t.set(e.id,a):t.delete(e.id)})}let i=t.get(e.id);return i||(i={},t.set(e.id,i)),i}runEffects(e,t){for(let r of e??[]){let s;try{s=this.evalExpr(r.value);let i=this.host.tracing?this.getProperty(r.target):void 0;this.writeProperty(r.target,s,!1),this.host.tracing&&this.emit({type:"write",target:r.target,value:s,...i!==void 0?{prev:i}:{}})}catch(i){this.reportError("effect",t,r.value,i)}}}eligible(e){if(!e.condition)return!0;try{return Be(this.evalExpr(e.condition))}catch(t){return this.reportError("condition",e.id,e.condition,t),!1}}reportError(e,t,r,s){let i=s instanceof Error?s.message:String(s),o=r?.src;this.host.onError({flow:this.id,kind:e,node:t,...o?{source:o}:{},message:i}),this.host.tracing&&this.emit({type:"diagnostic",kind:e,node:t,...o?{source:o}:{},message:i})}evalExpr(e){return V(this.conditionAst(e),this.context(),z)}conditionAst(e){let t=ke.get(e);return t||(t=k(e.ast),ke.set(e,t)),t}enter(e){let t=this.visitCounts,r=t.get(e);t.set(e,(r??0)+1);let s=this.host.sharedVisits,i=s.get(e);s.set(e,(i??0)+1),this.host.journal&&this.host.journal.undo.push(()=>{r===void 0?t.delete(e):t.set(e,r),i===void 0?s.delete(e):s.set(e,i)})}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=Q(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=C(this.host,e.id),r=t&&t.length?{tags:t}:{};switch(e.kind){case"gameEvent":return{type:"gameEvent",id:e.id,gameData:e.gameData,...r};case"text":return{type:"text",id:e.id,text:this.interpolate(this.resolveString(e.id)),gameData:e.gameData,...r};case"line":{let s=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?s:this.interpolate(s)),c=i&&a.length===0;return{type:"line",id:e.id,text:a,character:c?void 0:e.character,characterName:c?void 0:this.resolveCharacterName(e.character),direction:c?void 0:e.direction,qualifier:c?void 0:e.qualifier,qualifierName:c?void 0:this.resolveQualifierName(e.qualifier),gameData:e.gameData,...r}}}}interpolate(e){return be(e,t=>this.getProperty(t))}stripCaptions(e){return we(e,this.host.captionOpen,this.host.captionClose)}captionLine(e){return this.host.captionsOn?e:this.stripCaptions(e)}promptFor(e){let t=this.promptBeatOf(e);if(!t)return;let r=this.interpolate(this.resolveString(t.id));return t.kind==="line"?{kind:"line",text:this.captionLine(r),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction,qualifier:t.qualifier,qualifierName:this.resolveQualifierName(t.qualifier)}:{kind:"text",text:r}}authoredPromptOf(e){return e.type==="group"?e.prompt:void 0}promptResult(e,t){let r=C(this.host,e.id),s=r&&r.length?{tags:r}:{};return t.kind==="text"?{type:"text",id:e.id,text:t.text,gameData:e.gameData,...s}:{type:"line",id:e.id,text:t.text,character:t.character,characterName:t.characterName,direction:t.direction,qualifier:t.qualifier,qualifierName:t.qualifierName,gameData:e.gameData,...s}}promptBeatOf(e){return e.type==="group"&&e.prompt?e.prompt:((e.type==="snippet"?e:this.firstTextSnippetIn(e.children))?.beats??[]).find(r=>r.kind==="line"||r.kind==="text")}firstTextSnippetIn(e){let t;return I(e,r=>{!t&&r.type==="snippet"&&(r.beats??[]).some(s=>s.kind==="line"||s.kind==="text")&&(t=r)}),t}resolveString(e){if(this.host.emitIds)return e;let t=this.host.strings[e];if(t!==void 0)return t;let r=this.host.defaultStrings[e];return r!==void 0?`<Untranslated: ${e}> ${r}`:e}resolveCharacterName(e){if(e===void 0||this.host.emitIds)return;let t=Y(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}resolveQualifierName(e){if(e===void 0||this.host.emitIds)return;let t=X(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.qualifierDisplay.get(e)}splitRef(e){return Te(this.host,e)}newLocal(){return new b(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=y.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:A}),this.registered.add(e)}releaseBags(e){for(let t of this.registered)this.host.registry.remove(t,{keep:e});this.registered.clear(),this.sceneBags.clear()}sceneBagFor(e,t){if(this.host.bundle.scenes[e])return this.ensureSceneBags(e),this.host.sceneSharedNames.get(e)?.has(t.toLowerCase())?this.host.stageBags.get(e):this.sceneBags.get(e)}ensureSceneBags(e){let t=this.host.sceneSharedNames.get(e)??new Set,r=this.host.bundle.scenes[e]?.sceneProps??[],s=this.host.journal;if(!this.sceneBags.has(e)){let i=new b(r.filter(c=>!t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),o=y.flowScene(this.id,e),a=Re(this.host.registry,o,i,s!==null);this.registered.add(o),this.sceneBags.set(e,i),s&&s.undo.push(()=>{this.sceneBags.get(e)===i&&(this.host.registry.remove(o,a?{keep:!0}:void 0),this.registered.delete(o),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let i=new b(r.filter(a=>t.has(a.name.toLowerCase())),{pathPrefix:"@scene."}),o=Re(this.host.registry,y.stage(e),i,s!==null);this.host.stageBags.set(e,i),s&&s.undo.push(()=>{this.host.stageBags.get(e)===i&&(this.host.registry.remove(y.stage(e),o?{keep:!0}:void 0),this.host.stageBags.delete(e))})}}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;this.ensureSceneBags(e.id);for(let r of e.sceneProps??[]){if(!r.temporary)continue;let s=r.name.toLowerCase(),i=t.has(s)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);if(!i)continue;let o=this.host.journal?i.get(s):void 0;o!==void 0&&this.host.journal.undo.push(()=>i.set(s,o)),i.set(s,P(r))}}};function Ie(n,e){I(n,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let r of t.beats??[])r.kind==="line"&&r.character&&e.add(r.character)})}function De(n){return n&&Object.keys(n).length?{gameData:n}:{}}function ot(n){let e={kind:n.kind,text:n.text};return n.character!==void 0&&(e.character=n.character),n.characterName!==void 0&&(e.characterName=n.characterName),n.direction!==void 0&&(e.direction=n.direction),n.qualifier!==void 0&&(e.qualifier=n.qualifier),n.qualifierName!==void 0&&(e.qualifierName=n.qualifierName),e}function Ne(n){let e={};for(let[t,r]of n){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e[t]=s}return e}function Fe(n){let e=new Map;for(let[t,r]of Object.entries(n??{})){let s={};r.seq!==void 0&&(s.seq=r.seq),r.bag&&(s.bag=[...r.bag]),r.last!==void 0&&(s.last=r.last),e.set(t,s)}return e}function Re(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:A}),r&&JSON.stringify(t.save())!==s}function Oe(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Pe(n){return{...n.world?{world:n.world}:{},...n.hostScopes}}function te(n,e,t){let r=n.qualityLadders.get(e);if(!r){r=new Map;for(let s of t()??[]){let i=s.name.toLowerCase();s.type==="quality"&&s.stages&&!r.has(i)&&r.set(i,s.stages)}n.qualityLadders.set(e,r)}return r}function Ae(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function at(n,e){let t=Ae(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function re(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function ne(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function Te(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=ve(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function ct(n){let e={};n.shared?.patter&&(e.patter=n.shared.patter);for(let[t,r]of Object.entries(n.stageBags??{}))e[y.stage(t)]=r;for(let[t,r]of Object.entries(n.flows??{})){r.scopes?.patter&&(e[y.flowGlobals(t)]=r.scopes.patter);for(let[s,i]of Object.entries(r.sceneBags??{}))e[y.flowScene(t,s)]=i}return e}function Be(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var lt=(n,e)=>n.shared??e;function se(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:lt(n,e)}}function pt(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function dt(n,e){e.blocks++;let t=[...n.children];for(;t.length;){let r=t.pop();if(r.type==="group"){e.groups++,r.prompt&&e.prompts++,t.push(...r.children);continue}e.snippets++;for(let s of r.beats??[])e.beats++,s.kind==="gameEvent"&&e.gameEvents++}}function Ge(n){let e={scenes:0,blocks:0,groups:0,snippets:0,beats:0,prompts:0,gameEvents:0,cast:n.cast?.length??0},t=[],r=[];for(let o of Object.values(n.scenes)){e.scenes++;let a=S(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:S(c),name:c.name}))});for(let c of o.blocks)dt(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>se(c,!1))})}let s=(n.scopeRegistry?.scopes??[]).map(o=>({token:o.token,writable:o.writable??!0,opaque:o.declarations===void 0,properties:(o.declarations??[]).map(a=>se(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(pt)}));return{identity:{schema:n.schema,project:n.content.project,...n.content.version!==void 0?{version:n.content.version}:{},...n.content.hash!==void 0?{hash:n.content.hash}:{},...n.content.structureHash!==void 0?{structureHash:n.content.structureHash}:{},voiced:n.voiced,defaultLocale:n.locales.default,locales:[...n.locales.included],localisation:n.localisation?.mode??"embedded",sourceDebug:n.localisation?.sourceDebug??!1},addresses:t,hostScopes:s,properties:{patter:(n.properties??[]).map(o=>se(o,!0)),scene:r},gameData:i,counts:e}}function je(n,e){return n.gameDataFields?.[e]??[]}function ie(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function Ve(n,e){let t={};for(let r of n){let s=ie(n,e,r.name);s!==void 0&&(t[r.name]=s)}for(let[r,s]of Object.entries(e??{}))r in t||(t[r]=s);return t}return Je(gt);})();\n';
|
|
201229
201280
|
|
|
201230
201281
|
// ../ops/src/export-html.ts
|
|
201231
201282
|
var esc = (s) => s.replace(/[&<>]/g, (c2) => ({ "&": "&", "<": "<", ">": ">" })[c2]);
|
|
@@ -201278,6 +201329,9 @@ var PLAYER_JS = String.raw`
|
|
|
201278
201329
|
if (!s) { add("end", "The End"); return; } // defensive: a step should always be returned; never hard-crash
|
|
201279
201330
|
if (s.type === "line") {
|
|
201280
201331
|
var who = s.characterName || s.character || "";
|
|
201332
|
+
// A speaker qualifier follows the name, as the script's cue has it: TAM (O.S.).
|
|
201333
|
+
var qual = s.qualifierName || s.qualifier;
|
|
201334
|
+
if (qual && who) who += " (" + qual + ")";
|
|
201281
201335
|
var dir = s.direction ? '<em class="dir">(' + esc(s.direction) + ')</em> ' : "";
|
|
201282
201336
|
add("line", '<span class="who" style="color:hsl(' + hueOf(s.character || who) + ',55%,38%)">' + esc(who) + '</span>' + dir + esc(s.text));
|
|
201283
201337
|
scrollDown(); setTimeout(step, fakeDuration(s.text));
|
|
@@ -201545,6 +201599,8 @@ var Engine = class _Engine {
|
|
|
201545
201599
|
}
|
|
201546
201600
|
const castDisplay = /* @__PURE__ */ new Map();
|
|
201547
201601
|
for (const c2 of bundle.cast ?? []) if (c2.displayName) castDisplay.set(c2.name, c2.displayName);
|
|
201602
|
+
const qualifierDisplay = /* @__PURE__ */ new Map();
|
|
201603
|
+
for (const q of bundle.qualifiers ?? []) if (q?.gameId) qualifierDisplay.set(q.gameId, q.name);
|
|
201548
201604
|
this.defaultSeed = (options.seed ?? 2654435769) >>> 0;
|
|
201549
201605
|
const nodeIndex = /* @__PURE__ */ new Map();
|
|
201550
201606
|
const blockIndex = /* @__PURE__ */ new Map();
|
|
@@ -201605,6 +201661,7 @@ var Engine = class _Engine {
|
|
|
201605
201661
|
strings,
|
|
201606
201662
|
defaultStrings,
|
|
201607
201663
|
castDisplay,
|
|
201664
|
+
qualifierDisplay,
|
|
201608
201665
|
nodeIndex,
|
|
201609
201666
|
blockIndex,
|
|
201610
201667
|
blockById,
|
|
@@ -202009,6 +202066,11 @@ var Engine = class _Engine {
|
|
|
202009
202066
|
if (name !== void 0) info.characterName = name;
|
|
202010
202067
|
}
|
|
202011
202068
|
if (beat.direction !== void 0) info.direction = beat.direction;
|
|
202069
|
+
if (beat.qualifier !== void 0) {
|
|
202070
|
+
info.qualifier = beat.qualifier;
|
|
202071
|
+
const name = this.host.defaultStrings[qualifierStringKey(beat.qualifier)] ?? this.host.qualifierDisplay.get(beat.qualifier);
|
|
202072
|
+
if (name !== void 0) info.qualifierName = name;
|
|
202073
|
+
}
|
|
202012
202074
|
}
|
|
202013
202075
|
if (beat.kind === "line" || beat.kind === "text") {
|
|
202014
202076
|
const source2 = this.host.defaultStrings[beat.id];
|
|
@@ -203197,6 +203259,8 @@ var Flow = class {
|
|
|
203197
203259
|
character: silent ? void 0 : beat.character,
|
|
203198
203260
|
characterName: silent ? void 0 : this.resolveCharacterName(beat.character),
|
|
203199
203261
|
direction: silent ? void 0 : beat.direction,
|
|
203262
|
+
qualifier: silent ? void 0 : beat.qualifier,
|
|
203263
|
+
qualifierName: silent ? void 0 : this.resolveQualifierName(beat.qualifier),
|
|
203200
203264
|
gameData: beat.gameData,
|
|
203201
203265
|
...withTags
|
|
203202
203266
|
};
|
|
@@ -203235,7 +203299,15 @@ var Flow = class {
|
|
|
203235
203299
|
const beat = this.promptBeatOf(node);
|
|
203236
203300
|
if (!beat) return void 0;
|
|
203237
203301
|
const text = this.interpolate(this.resolveString(beat.id));
|
|
203238
|
-
return beat.kind === "line" ? {
|
|
203302
|
+
return beat.kind === "line" ? {
|
|
203303
|
+
kind: "line",
|
|
203304
|
+
text: this.captionLine(text),
|
|
203305
|
+
character: beat.character,
|
|
203306
|
+
characterName: this.resolveCharacterName(beat.character),
|
|
203307
|
+
direction: beat.direction,
|
|
203308
|
+
qualifier: beat.qualifier,
|
|
203309
|
+
qualifierName: this.resolveQualifierName(beat.qualifier)
|
|
203310
|
+
} : { kind: "text", text };
|
|
203239
203311
|
}
|
|
203240
203312
|
/** An option's AUTHORED prompt beat: an Option group's own `prompt`. The only prompt a replay speaks. */
|
|
203241
203313
|
authoredPromptOf(node) {
|
|
@@ -203254,6 +203326,8 @@ var Flow = class {
|
|
|
203254
203326
|
character: shown2.character,
|
|
203255
203327
|
characterName: shown2.characterName,
|
|
203256
203328
|
direction: shown2.direction,
|
|
203329
|
+
qualifier: shown2.qualifier,
|
|
203330
|
+
qualifierName: shown2.qualifierName,
|
|
203257
203331
|
gameData: beat.gameData,
|
|
203258
203332
|
...withTags
|
|
203259
203333
|
};
|
|
@@ -203290,6 +203364,15 @@ var Flow = class {
|
|
|
203290
203364
|
const key2 = castStringKey(character);
|
|
203291
203365
|
return this.host.strings[key2] ?? this.host.defaultStrings[key2] ?? this.host.castDisplay.get(character);
|
|
203292
203366
|
}
|
|
203367
|
+
/** A speaker qualifier's shown name, resolved as a character's is: the `qualifier:<gameId>` string in the
|
|
203368
|
+
* active locale, else the default locale, else the authored name. Undefined in IDs-only mode (the game
|
|
203369
|
+
* maps the `qualifier` gameId itself). */
|
|
203370
|
+
resolveQualifierName(qualifier) {
|
|
203371
|
+
if (qualifier === void 0) return void 0;
|
|
203372
|
+
if (this.host.emitIds) return void 0;
|
|
203373
|
+
const key2 = qualifierStringKey(qualifier);
|
|
203374
|
+
return this.host.strings[key2] ?? this.host.defaultStrings[key2] ?? this.host.qualifierDisplay.get(qualifier);
|
|
203375
|
+
}
|
|
203293
203376
|
/** Split a ref into scope + name. Tokens: `@scene`, registered tokens, else `@patter` (incl. bare `@name`). */
|
|
203294
203377
|
splitRef(ref) {
|
|
203295
203378
|
return splitHostRef(this.host, ref);
|
|
@@ -203386,6 +203469,8 @@ function savedPrompt(p) {
|
|
|
203386
203469
|
if (p.character !== void 0) out.character = p.character;
|
|
203387
203470
|
if (p.characterName !== void 0) out.characterName = p.characterName;
|
|
203388
203471
|
if (p.direction !== void 0) out.direction = p.direction;
|
|
203472
|
+
if (p.qualifier !== void 0) out.qualifier = p.qualifier;
|
|
203473
|
+
if (p.qualifierName !== void 0) out.qualifierName = p.qualifierName;
|
|
203389
203474
|
return out;
|
|
203390
203475
|
}
|
|
203391
203476
|
function serialiseSelectors(map2) {
|
|
@@ -203584,9 +203669,11 @@ function renderPlay(result) {
|
|
|
203584
203669
|
const out = [];
|
|
203585
203670
|
for (const e of result.events) {
|
|
203586
203671
|
switch (e.type) {
|
|
203587
|
-
case "line":
|
|
203588
|
-
|
|
203672
|
+
case "line": {
|
|
203673
|
+
const q = e.qualifierName ?? e.qualifier;
|
|
203674
|
+
out.push(`${e.character ?? "?"}${q ? ` (${q})` : ""}: ${e.text}`);
|
|
203589
203675
|
break;
|
|
203676
|
+
}
|
|
203590
203677
|
case "text":
|
|
203591
203678
|
out.push(` ${e.text}`);
|
|
203592
203679
|
break;
|
|
@@ -204591,6 +204678,11 @@ function extractLoc(loaded, opts = {}) {
|
|
|
204591
204678
|
const g = context?.character ? genderOf.get(context.character) : void 0;
|
|
204592
204679
|
return g ? { ...context, gender: g } : context;
|
|
204593
204680
|
};
|
|
204681
|
+
const qualifierName = new Map(projectQualifiers(loaded.project).map((q) => [q.gameId, q.name]));
|
|
204682
|
+
const lineContext = (b) => {
|
|
204683
|
+
const q = b.kind === "line" && b.qualifier ? qualifierName.get(b.qualifier) ?? b.qualifier : void 0;
|
|
204684
|
+
return { character: b.kind === "line" ? b.character : void 0, kind: b.kind, ...q ? { qualifier: q } : {} };
|
|
204685
|
+
};
|
|
204594
204686
|
const entries = [];
|
|
204595
204687
|
const push2 = (id, scene, context) => {
|
|
204596
204688
|
const src = source2[id];
|
|
@@ -204603,11 +204695,11 @@ function extractLoc(loaded, opts = {}) {
|
|
|
204603
204695
|
walkNodes(block.children, (node) => {
|
|
204604
204696
|
if (node.type === "group") {
|
|
204605
204697
|
const p = node.prompt;
|
|
204606
|
-
if (p) push2(p.id, scene.id,
|
|
204698
|
+
if (p) push2(p.id, scene.id, lineContext(p));
|
|
204607
204699
|
return;
|
|
204608
204700
|
}
|
|
204609
204701
|
for (const beat of node.beats ?? []) {
|
|
204610
|
-
if (beat.kind !== "gameEvent") push2(beat.id, scene.id,
|
|
204702
|
+
if (beat.kind !== "gameEvent") push2(beat.id, scene.id, lineContext(beat));
|
|
204611
204703
|
}
|
|
204612
204704
|
});
|
|
204613
204705
|
}
|
|
@@ -204619,6 +204711,12 @@ function extractLoc(loaded, opts = {}) {
|
|
|
204619
204711
|
const translation = isTemplate ? "" : target[id] ?? "";
|
|
204620
204712
|
entries.push({ id, scene: PROJECT_LOCALE_SCENE, source: src, translation, comments: commentsOf(id), context: withGender({ character: c2.name }), stale: staleFor(id, translation) });
|
|
204621
204713
|
}
|
|
204714
|
+
for (const q of usedQualifiers(loaded.project, loaded.scenes)) {
|
|
204715
|
+
const id = qualifierStringKey(q.gameId);
|
|
204716
|
+
const src = source2[id] ?? q.name;
|
|
204717
|
+
const translation = isTemplate ? "" : target[id] ?? "";
|
|
204718
|
+
entries.push({ id, scene: PROJECT_LOCALE_SCENE, source: src, translation, comments: commentsOf(id), context: { kind: "qualifier" }, stale: staleFor(id, translation) });
|
|
204719
|
+
}
|
|
204622
204720
|
return { project: loaded.project.project.id, defaultLocale, locale: targetLocale, entries };
|
|
204623
204721
|
}
|
|
204624
204722
|
var layoutOf = (loaded) => projectLayout(loaded.project);
|
|
@@ -204754,7 +204852,8 @@ function catalogToPo(catalog) {
|
|
|
204754
204852
|
for (const e of catalog.entries) {
|
|
204755
204853
|
for (const c2 of e.comments) out.push(`#. ${c2}`);
|
|
204756
204854
|
if (e.context?.character || e.context?.kind) {
|
|
204757
|
-
|
|
204855
|
+
const speaker = e.context.character && e.context.qualifier ? `${e.context.character} (${e.context.qualifier})` : e.context.character;
|
|
204856
|
+
out.push(`#. [${[e.context.kind, speaker].filter(Boolean).join(" ")}]`);
|
|
204758
204857
|
}
|
|
204759
204858
|
if (e.context?.gender) out.push(`#. Gender: ${e.context.gender}`);
|
|
204760
204859
|
out.push(`#: ${e.scene}`);
|
|
@@ -204850,7 +204949,7 @@ function poToCatalog(text) {
|
|
|
204850
204949
|
}
|
|
204851
204950
|
|
|
204852
204951
|
// ../ops/src/loc-xlsx.ts
|
|
204853
|
-
var HEADERS = ["ID", "Source", "Translation", "Comments", "Status", "Gender", "Scene"];
|
|
204952
|
+
var HEADERS = ["ID", "Source", "Translation", "Comments", "Status", "Gender", "Scene", "Qualifier"];
|
|
204854
204953
|
var SCENE_COLUMN = HEADERS.indexOf("Scene") + 1;
|
|
204855
204954
|
var SHEET_NAME_MAX = 31;
|
|
204856
204955
|
function uniqueSheetName(scene, used) {
|
|
@@ -204893,7 +204992,9 @@ async function catalogToXlsx(catalog) {
|
|
|
204893
204992
|
{ header: "Status", key: "status", width: 10 },
|
|
204894
204993
|
{ header: "Gender", key: "gender", width: 12 },
|
|
204895
204994
|
// Hidden: the translator has no use for it, and it is the import's only faithful record of the scene.
|
|
204896
|
-
{ header: "Scene", key: "scene", width: 22, hidden: true }
|
|
204995
|
+
{ header: "Scene", key: "scene", width: 22, hidden: true },
|
|
204996
|
+
// After the hidden Scene column, so the columns an older reader finds by position stay where they were.
|
|
204997
|
+
{ header: "Qualifier", key: "qualifier", width: 11 }
|
|
204897
204998
|
];
|
|
204898
204999
|
ws.getRow(1).font = { bold: true };
|
|
204899
205000
|
ws.views = [{ state: "frozen", ySplit: 1 }];
|
|
@@ -204905,7 +205006,8 @@ async function catalogToXlsx(catalog) {
|
|
|
204905
205006
|
comments: e.comments.join("\n"),
|
|
204906
205007
|
status: e.stale ? "stale" : e.translation ? "translated" : "",
|
|
204907
205008
|
gender: e.context?.gender ?? "",
|
|
204908
|
-
scene
|
|
205009
|
+
scene,
|
|
205010
|
+
qualifier: e.context?.qualifier ?? ""
|
|
204909
205011
|
});
|
|
204910
205012
|
}
|
|
204911
205013
|
}
|
|
@@ -204969,6 +205071,7 @@ function runVoiceScript(loaded, opts = {}) {
|
|
|
204969
205071
|
const source2 = sourceStrings(loaded);
|
|
204970
205072
|
const actorOf = /* @__PURE__ */ new Map();
|
|
204971
205073
|
for (const c2 of project.cast ?? []) if (c2.actor) actorOf.set(c2.name, c2.actor);
|
|
205074
|
+
const qualifierName = new Map(projectQualifiers(project).map((q) => [q.gameId, q.name]));
|
|
204972
205075
|
const voClasses = classesForChannel(project.documentationClasses ?? DEFAULT_DOCUMENTATION_CLASSES, "vo");
|
|
204973
205076
|
const ownVo = (id) => (docsOf.get(id) ?? []).filter((d) => d.type !== void 0 && voClasses.has(d.type)).map((d) => d.text);
|
|
204974
205077
|
const lines = [];
|
|
@@ -204981,6 +205084,7 @@ function runVoiceScript(loaded, opts = {}) {
|
|
|
204981
205084
|
scope: scope.join(" \u203A "),
|
|
204982
205085
|
id: beat.id,
|
|
204983
205086
|
character: beat.character ?? "",
|
|
205087
|
+
...beat.qualifier ? { qualifier: qualifierName.get(beat.qualifier) ?? beat.qualifier } : {},
|
|
204984
205088
|
actor: beat.character ? actorOf.get(beat.character) : void 0,
|
|
204985
205089
|
text: plainVoice(source2[beat.id] ?? ""),
|
|
204986
205090
|
comments: leadingVo ? [...leadingVo, ...own2] : own2,
|
|
@@ -205028,6 +205132,7 @@ async function voiceScriptToXlsx(data) {
|
|
|
205028
205132
|
{ header: "Scope", key: "scope", width: 28 },
|
|
205029
205133
|
{ header: "Line ID", key: "id", width: 18 },
|
|
205030
205134
|
{ header: "Character", key: "character", width: 16 },
|
|
205135
|
+
{ header: "Qualifier", key: "qualifier", width: 11 },
|
|
205031
205136
|
{ header: "Actor", key: "actor", width: 16 },
|
|
205032
205137
|
{ header: "Text", key: "text", width: 60 },
|
|
205033
205138
|
{ header: "Comments", key: "comments", width: 36 },
|
|
@@ -205040,6 +205145,7 @@ async function voiceScriptToXlsx(data) {
|
|
|
205040
205145
|
scope: l2.scope,
|
|
205041
205146
|
id: l2.id,
|
|
205042
205147
|
character: l2.character,
|
|
205148
|
+
qualifier: l2.qualifier ?? "",
|
|
205043
205149
|
actor: l2.actor ?? "",
|
|
205044
205150
|
text: l2.text,
|
|
205045
205151
|
comments: l2.comments.join("\n"),
|
|
@@ -205096,6 +205202,13 @@ function colourIndex(name) {
|
|
|
205096
205202
|
function characterColour(name) {
|
|
205097
205203
|
return CHAR_PALETTE[colourIndex(name)];
|
|
205098
205204
|
}
|
|
205205
|
+
function cueLabel(character, qualifierName) {
|
|
205206
|
+
return qualifierName ? `${character.toUpperCase()} (${qualifierName.toUpperCase()})` : character.toUpperCase();
|
|
205207
|
+
}
|
|
205208
|
+
function qualifierNamer(project) {
|
|
205209
|
+
const names = new Map(projectQualifiers(project).map((q) => [q.gameId, q.name]));
|
|
205210
|
+
return (gameId) => gameId ? names.get(gameId) ?? gameId : void 0;
|
|
205211
|
+
}
|
|
205099
205212
|
var MARKUP_RE = /<(b|i|bi)>([\s\S]*?)<\/\1>/g;
|
|
205100
205213
|
var INTERP_RE = /\{@[^}]*\}/g;
|
|
205101
205214
|
function textRuns(text) {
|
|
@@ -205174,6 +205287,12 @@ function runScriptDoc(loaded, opts = {}) {
|
|
|
205174
205287
|
}
|
|
205175
205288
|
const humanize = (cond) => humanizeNodeRefs(cond, (id) => blockName.get(id) ?? sceneOf.get(id) ?? id);
|
|
205176
205289
|
const els = [];
|
|
205290
|
+
const nameOf = qualifierNamer(project);
|
|
205291
|
+
const speaker = (beat) => ({
|
|
205292
|
+
character: beat.character ?? "",
|
|
205293
|
+
...beat.qualifier ? { qualifier: beat.qualifier, qualifierName: nameOf(beat.qualifier) } : {},
|
|
205294
|
+
direction: beat.direction
|
|
205295
|
+
});
|
|
205177
205296
|
const textOf = (id) => source2[id] ?? "";
|
|
205178
205297
|
const notes = (id, indent, sid) => {
|
|
205179
205298
|
for (const l2 of ownNotes(id)) els.push({ kind: "note", id, indent, snippet: sid, text: l2.text, class: l2.type });
|
|
@@ -205187,7 +205306,7 @@ function runScriptDoc(loaded, opts = {}) {
|
|
|
205187
205306
|
const emitBeats = (node, indent, sid) => {
|
|
205188
205307
|
for (const beat of node.beats ?? []) {
|
|
205189
205308
|
if (cut.has(beat.id)) continue;
|
|
205190
|
-
if (beat.kind === "line") els.push({ kind: "line", id: beat.id, indent, snippet: sid,
|
|
205309
|
+
if (beat.kind === "line") els.push({ kind: "line", id: beat.id, indent, snippet: sid, ...speaker(beat), runs: textRuns(textOf(beat.id)) });
|
|
205191
205310
|
else if (beat.kind === "text") els.push({ kind: "narration", id: beat.id, indent, snippet: sid, runs: textRuns(textOf(beat.id)) });
|
|
205192
205311
|
else if (beat.kind === "gameEvent") els.push({ kind: "gameEvent", id: beat.id, indent, snippet: sid, text: gameEventLabel(beat) });
|
|
205193
205312
|
else continue;
|
|
@@ -205232,7 +205351,7 @@ function runScriptDoc(loaded, opts = {}) {
|
|
|
205232
205351
|
continue;
|
|
205233
205352
|
}
|
|
205234
205353
|
if (cut.has(beat.id)) continue;
|
|
205235
|
-
if (beat.kind === "line") els.push({ kind: "line", id: beat.id, indent: indent + 1, snippet: cid,
|
|
205354
|
+
if (beat.kind === "line") els.push({ kind: "line", id: beat.id, indent: indent + 1, snippet: cid, ...speaker(beat), runs: textRuns(textOf(beat.id)) });
|
|
205236
205355
|
else if (beat.kind === "text") els.push({ kind: "narration", id: beat.id, indent: indent + 1, snippet: cid, runs: textRuns(textOf(beat.id)) });
|
|
205237
205356
|
else if (beat.kind === "gameEvent") els.push({ kind: "gameEvent", id: beat.id, indent: indent + 1, snippet: cid, text: gameEventLabel(beat) });
|
|
205238
205357
|
else continue;
|
|
@@ -223440,7 +223559,7 @@ function paragraph(el, prev) {
|
|
|
223440
223559
|
});
|
|
223441
223560
|
case "line": {
|
|
223442
223561
|
const kids2 = [];
|
|
223443
|
-
if (el.character) kids2.push(new TextRun({ text: el.character.
|
|
223562
|
+
if (el.character) kids2.push(new TextRun({ text: cueLabel(el.character, el.qualifierName), font: SANS, bold: true, allCaps: true, color: characterColour(el.character), size: S.cue }), new TextRun({ text: " ", font: SANS, size: S.cue }));
|
|
223444
223563
|
if (el.direction) kids2.push(new TextRun({ text: `(${el.direction}) `, font: SERIF, italics: true, color: TOKENS.muted, size: S.body }));
|
|
223445
223564
|
kids2.push(...bodyRuns(el.runs, TOKENS.inkRead));
|
|
223446
223565
|
return new Paragraph({ keepLines: true, spacing: { before, after: 20 }, indent: { left: leftOf(el) + DIALOGUE_INDENT + HANG, hanging: HANG }, children: kids2 });
|
|
@@ -245640,13 +245759,14 @@ async function scriptToPdf(doc) {
|
|
|
245640
245759
|
case "line": {
|
|
245641
245760
|
const cueX = cx2 + DIALOGUE_INDENT2;
|
|
245642
245761
|
let bodyX = cueX;
|
|
245643
|
-
|
|
245762
|
+
const cue = el.character ? cueLabel(el.character, el.qualifierName) : "";
|
|
245763
|
+
if (cue) {
|
|
245644
245764
|
pdf.font("Sans-Bold").fontSize(CUE);
|
|
245645
|
-
bodyX = cueX + pdf.widthOfString(
|
|
245765
|
+
bodyX = cueX + pdf.widthOfString(cue) + CUE_GAP;
|
|
245646
245766
|
}
|
|
245647
245767
|
const yTop = pdf.y;
|
|
245648
|
-
if (
|
|
245649
|
-
pdf.font("Sans-Bold").fontSize(CUE).fillColor(hex(characterColour(el.character))).text(
|
|
245768
|
+
if (cue) {
|
|
245769
|
+
pdf.font("Sans-Bold").fontSize(CUE).fillColor(hex(characterColour(el.character))).text(cue, cueX, yTop + ascentPt("Serif", BODY) - ascentPt("Sans-Bold", CUE), { lineBreak: false });
|
|
245650
245770
|
pdf.y = yTop;
|
|
245651
245771
|
}
|
|
245652
245772
|
pdf.fontSize(BODY);
|
|
@@ -246350,8 +246470,9 @@ function staleParts(s, liveText, line2) {
|
|
|
246350
246470
|
const stale = [];
|
|
246351
246471
|
if (liveText !== s.baseline) stale.push("the text");
|
|
246352
246472
|
if (s.proposedCharacter !== void 0 && (line2?.character ?? "") !== (s.baselineCharacter ?? "")) stale.push("the speaker");
|
|
246473
|
+
if (s.proposedQualifier !== void 0 && (line2?.qualifier ?? "") !== (s.baselineQualifier ?? "")) stale.push("the speaker qualifier");
|
|
246353
246474
|
if (s.proposedDirection !== void 0 && (line2?.direction ?? "") !== (s.baselineDirection ?? "")) stale.push("the direction");
|
|
246354
|
-
if ((s.proposedCharacter !== void 0 || s.proposedDirection !== void 0) && !line2) stale.push("the line (no longer a spoken line)");
|
|
246475
|
+
if ((s.proposedCharacter !== void 0 || s.proposedQualifier !== void 0 || s.proposedDirection !== void 0) && !line2) stale.push("the line (no longer a spoken line)");
|
|
246355
246476
|
return stale;
|
|
246356
246477
|
}
|
|
246357
246478
|
function listOpenSuggestions(loaded, filter2 = {}) {
|
|
@@ -246506,6 +246627,12 @@ function applySuggestionDecisions(loaded, decisions, opts = {}) {
|
|
|
246506
246627
|
work.touch(flow.path);
|
|
246507
246628
|
edited = true;
|
|
246508
246629
|
}
|
|
246630
|
+
if (line2 && flow && s.proposedQualifier !== void 0 && s.proposedQualifier !== (line2.qualifier ?? "")) {
|
|
246631
|
+
if (s.proposedQualifier === "") delete line2.qualifier;
|
|
246632
|
+
else line2.qualifier = s.proposedQualifier;
|
|
246633
|
+
work.touch(flow.path);
|
|
246634
|
+
edited = true;
|
|
246635
|
+
}
|
|
246509
246636
|
if (line2 && flow && s.proposedDirection !== void 0 && s.proposedDirection !== (line2.direction ?? "")) {
|
|
246510
246637
|
if (s.proposedDirection === "") delete line2.direction;
|
|
246511
246638
|
else line2.direction = s.proposedDirection;
|
|
@@ -246556,7 +246683,7 @@ async function exportEditableScript(loaded, opts) {
|
|
|
246556
246683
|
if (isEditable(el)) {
|
|
246557
246684
|
const code = markerOf.get(el);
|
|
246558
246685
|
const baseline = source2[el.id] ?? "";
|
|
246559
|
-
lines[code] = el.kind === "line" ? { id: el.id, kind: "line", character: el.character, ...el.direction ? { direction: el.direction } : {}, baseline } : { id: el.id, kind: el.kind === "option" ? "option" : "narration", baseline };
|
|
246686
|
+
lines[code] = el.kind === "line" ? { id: el.id, kind: "line", character: el.character, ...el.qualifier ? { qualifier: el.qualifier } : {}, ...el.direction ? { direction: el.direction } : {}, baseline } : { id: el.id, kind: el.kind === "option" ? "option" : "narration", baseline };
|
|
246560
246687
|
skeleton.push({ kind: "box", marker: code });
|
|
246561
246688
|
} else {
|
|
246562
246689
|
skeleton.push({ kind: el.kind, node: el.id, text: contextText(el) });
|
|
@@ -246623,7 +246750,7 @@ function box(el, code, status) {
|
|
|
246623
246750
|
const textW = Math.max(MIN_TEXT, FULL - ind - LEAD2 - MARGIN2);
|
|
246624
246751
|
const cell = (children, width, extra = {}) => new TableCell({ width: { size: width, type: WidthType.DXA }, borders: { top: NONE, bottom: NONE, left: NONE, right: NONE }, margins: { top: 60, bottom: 60, left: 100, right: 100 }, children, ...extra });
|
|
246625
246752
|
const lead = [];
|
|
246626
|
-
if (el.kind === "line" && el.character) lead.push(new TextRun({ text: el.character.
|
|
246753
|
+
if (el.kind === "line" && el.character) lead.push(new TextRun({ text: cueLabel(el.character, el.qualifierName), font: SANS, bold: true, allCaps: true, size: S.cue, color: characterColour(el.character) }));
|
|
246627
246754
|
if (el.kind === "line" && el.direction) lead.push(new TextRun({ text: `(${el.direction})`, font: SERIF, italics: true, size: S.cue, color: TOKENS.muted, break: el.character ? 1 : 0 }));
|
|
246628
246755
|
if (el.kind === "option") lead.push(new TextRun({ text: "\u25C7", color: TOKENS.accent, size: S.body }));
|
|
246629
246756
|
const ink = el.kind === "narration" ? TOKENS.inkSoft : el.kind === "option" ? TOKENS.ink : TOKENS.inkRead;
|
|
@@ -247087,6 +247214,7 @@ function planEditableImport(loaded, returned, opts) {
|
|
|
247087
247214
|
const places = indexPlaces(loaded);
|
|
247088
247215
|
const live = sourceStrings(loaded);
|
|
247089
247216
|
const cast = loaded.project.cast ?? [];
|
|
247217
|
+
const qualifiers = projectQualifiers(loaded.project);
|
|
247090
247218
|
const suggestions = [];
|
|
247091
247219
|
const comments = [];
|
|
247092
247220
|
let unchanged = 0, stale = 0;
|
|
@@ -247165,27 +247293,30 @@ function planEditableImport(loaded, returned, opts) {
|
|
|
247165
247293
|
const { text: proposedRaw, notes } = takeNotes(b.rowDeleted ? "" : b.text.proposed, sent.baseline);
|
|
247166
247294
|
const proposed = tidy(proposedRaw);
|
|
247167
247295
|
const textChanged = normalise(proposed, !!opts.strictQuotes) !== normalise(sent.baseline, !!opts.strictQuotes);
|
|
247168
|
-
const
|
|
247169
|
-
if (
|
|
247296
|
+
const tracked2 = b.text.changes.length > 0;
|
|
247297
|
+
if (tracked2 && normalise(b.text.original, !!opts.strictQuotes) !== normalise(sent.baseline, !!opts.strictQuotes)) {
|
|
247170
247298
|
problems.push({ severity: "info", kind: "untracked-edits", marker: code, anchor: sent.id, message: "This line has tracked changes and some untracked ones too, so the credit may be incomplete." });
|
|
247171
247299
|
}
|
|
247172
247300
|
const cue = sent.kind === "line" ? readLead(b.lead.proposed) : void 0;
|
|
247173
|
-
let proposedCharacter, proposedDirection;
|
|
247301
|
+
let proposedCharacter, proposedQualifier, proposedDirection;
|
|
247174
247302
|
if (sent.kind === "line" && cue) {
|
|
247175
|
-
const
|
|
247176
|
-
if (
|
|
247177
|
-
|
|
247178
|
-
|
|
247179
|
-
else {
|
|
247180
|
-
problems.push({ severity: "warning", kind: "unknown-speaker", marker: code, anchor: sent.id, message: `The speaker was changed to ${cue.character}, who isn't in the cast; it's in a comment.` });
|
|
247181
|
-
comment(here ?? nearestLine(boxes.indexOf(b)), author, `Speaker changed from ${sent.character ?? "(none)"} to ${cue.character}, who isn't in the cast.`, ts);
|
|
247182
|
-
}
|
|
247303
|
+
const read2 = readCue(cue.character, sent.character ?? "", sent.qualifier ?? "", cast, qualifiers);
|
|
247304
|
+
if (read2.unknownSpeaker !== void 0) {
|
|
247305
|
+
problems.push({ severity: "warning", kind: "unknown-speaker", marker: code, anchor: sent.id, message: `The speaker was changed to ${read2.unknownSpeaker}, who isn't in the cast; it's in a comment.` });
|
|
247306
|
+
comment(here ?? nearestLine(boxes.indexOf(b)), author, `Speaker changed from ${sentCue(sent.character, sent.qualifier, qualifiers) || "(none)"} to ${read2.unknownSpeaker}, who isn't in the cast.`, ts);
|
|
247183
247307
|
}
|
|
247308
|
+
if (read2.unknownQualifier !== void 0) {
|
|
247309
|
+
problems.push({ severity: "warning", kind: "unknown-qualifier", marker: code, anchor: sent.id, message: `The speaker qualifier was changed to (${read2.unknownQualifier}), which isn't one of the project's qualifiers; it's in a comment.` });
|
|
247310
|
+
comment(here ?? nearestLine(boxes.indexOf(b)), author, `Speaker changed from ${sentCue(sent.character, sent.qualifier, qualifiers) || "(none)"} to ${tidy(cue.character)}, but (${read2.unknownQualifier}) isn't one of the project's qualifiers.`, ts);
|
|
247311
|
+
}
|
|
247312
|
+
if (read2.character !== void 0 && read2.character !== (sent.character ?? "")) proposedCharacter = read2.character;
|
|
247313
|
+
if (read2.qualifier !== void 0 && read2.qualifier !== (sent.qualifier ?? "")) proposedQualifier = read2.qualifier;
|
|
247184
247314
|
if (tidy(cue.direction) !== tidy(sent.direction ?? "")) proposedDirection = tidy(cue.direction);
|
|
247185
247315
|
}
|
|
247316
|
+
const cueChanged = !!proposedCharacter || proposedQualifier !== void 0 || proposedDirection !== void 0;
|
|
247186
247317
|
for (const note of notes) comment(here ?? nearestLine(boxes.indexOf(b)), author, note, ts);
|
|
247187
247318
|
if (!here) {
|
|
247188
|
-
if (textChanged ||
|
|
247319
|
+
if (textChanged || cueChanged) {
|
|
247189
247320
|
problems.push({ severity: "warning", kind: "line-gone", marker: code, message: `The line ${quote(sent.baseline)} is no longer in the project; the editor's version is in a comment nearby.` });
|
|
247190
247321
|
comment(nearestLine(boxes.indexOf(b)), author, `For a line no longer in the project (${quote(sent.baseline)}), the editor wrote: ${quote(proposed)}`, ts);
|
|
247191
247322
|
} else unchanged++;
|
|
@@ -247198,16 +247329,17 @@ function planEditableImport(loaded, returned, opts) {
|
|
|
247198
247329
|
if (textChanged && !sameList(placeholders(proposed), placeholders(sent.baseline))) {
|
|
247199
247330
|
problems.push({ severity: "warning", kind: "placeholders", marker: code, anchor: sent.id, message: `The {@\u2026} placeholders changed, so this line wasn't suggested; the editor's version is in a comment.` });
|
|
247200
247331
|
comment(here, author, `The editor's version changes the {@\u2026} placeholders (${placeholders(sent.baseline).join(", ") || "none"} became ${placeholders(proposed).join(", ") || "none"}), so it wasn't made a suggestion: ${quote(proposed)}`, ts);
|
|
247201
|
-
if (!
|
|
247332
|
+
if (!cueChanged) continue;
|
|
247202
247333
|
}
|
|
247203
247334
|
const keepText = textChanged && sameList(placeholders(proposed), placeholders(sent.baseline));
|
|
247204
|
-
if (!keepText && !
|
|
247335
|
+
if (!keepText && !cueChanged) {
|
|
247205
247336
|
unchanged++;
|
|
247206
247337
|
continue;
|
|
247207
247338
|
}
|
|
247208
247339
|
suggestions.push(suggestion2({
|
|
247209
247340
|
proposed: keepText ? proposed : sent.baseline,
|
|
247210
247341
|
...proposedCharacter ? { proposedCharacter, baselineCharacter: sent.character ?? "" } : {},
|
|
247342
|
+
...proposedQualifier !== void 0 ? { proposedQualifier, baselineQualifier: sent.qualifier ?? "" } : {},
|
|
247211
247343
|
...proposedDirection !== void 0 ? { proposedDirection, baselineDirection: sent.direction ?? "" } : {}
|
|
247212
247344
|
}));
|
|
247213
247345
|
}
|
|
@@ -247288,6 +247420,26 @@ function readLead(text) {
|
|
|
247288
247420
|
const character = lines.find((l2) => l2 !== direction) ?? "";
|
|
247289
247421
|
return { character, direction: direction ? direction.slice(1, -1) : "" };
|
|
247290
247422
|
}
|
|
247423
|
+
function sentCue(character, qualifier, qualifiers) {
|
|
247424
|
+
if (!character) return "";
|
|
247425
|
+
if (!qualifier) return character;
|
|
247426
|
+
return `${character} (${qualifiers.find((q) => q.gameId === qualifier)?.name ?? qualifier})`;
|
|
247427
|
+
}
|
|
247428
|
+
function readCue(text, sentCharacter, sentQualifier, cast, qualifiers) {
|
|
247429
|
+
const cue = tidy(text);
|
|
247430
|
+
if (!cue) return {};
|
|
247431
|
+
const upper = (s) => s.toUpperCase();
|
|
247432
|
+
if (upper(cue) === upper(sentCue(sentCharacter, sentQualifier, qualifiers))) return {};
|
|
247433
|
+
const speaker = (name) => cast.find((c2) => upper(c2.name) === upper(name))?.name ?? (sentCharacter && upper(name) === upper(sentCharacter) ? sentCharacter : void 0);
|
|
247434
|
+
const whole = speaker(cue);
|
|
247435
|
+
if (whole !== void 0) return { character: whole, qualifier: "" };
|
|
247436
|
+
const m = /^(.*?)\s*\(([^()]*)\)$/s.exec(cue);
|
|
247437
|
+
const rest = m ? speaker(m[1].trim()) : void 0;
|
|
247438
|
+
if (!m || rest === void 0) return { unknownSpeaker: cue };
|
|
247439
|
+
const bracket = m[2].trim();
|
|
247440
|
+
const q = qualifiers.find((x) => upper(x.name) === upper(bracket));
|
|
247441
|
+
return q ? { character: rest, qualifier: q.gameId } : { character: rest, unknownQualifier: bracket };
|
|
247442
|
+
}
|
|
247291
247443
|
function rankAuthors(changes) {
|
|
247292
247444
|
const counts = /* @__PURE__ */ new Map();
|
|
247293
247445
|
for (const c2 of changes) if (c2.author) counts.set(c2.author, (counts.get(c2.author) ?? 0) + 1);
|
|
@@ -248613,15 +248765,61 @@ function cmAsync(args) {
|
|
|
248613
248765
|
return runCommandAsync("cm", args);
|
|
248614
248766
|
}
|
|
248615
248767
|
function isTracked$1(filePath) {
|
|
248616
|
-
return
|
|
248768
|
+
return tracked(localState(filePath));
|
|
248617
248769
|
}
|
|
248618
248770
|
async function isTrackedAsync$1(filePath) {
|
|
248619
|
-
return
|
|
248771
|
+
return tracked(await localStateAsync(filePath));
|
|
248772
|
+
}
|
|
248773
|
+
function tracked(state) {
|
|
248774
|
+
return state === "controlled" || state === "checkedOut";
|
|
248775
|
+
}
|
|
248776
|
+
function localStateArgs(filePath) {
|
|
248777
|
+
return plasticStatusArgs([filePath]);
|
|
248778
|
+
}
|
|
248779
|
+
function localState(filePath) {
|
|
248780
|
+
return localStateFrom(cm(localStateArgs(filePath)), filePath);
|
|
248781
|
+
}
|
|
248782
|
+
async function localStateAsync(filePath) {
|
|
248783
|
+
return localStateFrom(await cmAsync(localStateArgs(filePath)), filePath);
|
|
248784
|
+
}
|
|
248785
|
+
function localStateFrom(result, filePath) {
|
|
248786
|
+
if (result.exitCode !== 0) return "outside";
|
|
248787
|
+
const info = findStatusInfo(result.output, filePath);
|
|
248788
|
+
if (!info) return "controlled";
|
|
248789
|
+
if (!info.tracked) return info.ignored ? "ignored" : "private";
|
|
248790
|
+
return info.checkedOut ? "checkedOut" : "controlled";
|
|
248791
|
+
}
|
|
248792
|
+
function findStatusInfo(output, filePath) {
|
|
248793
|
+
const key2 = pathKey(filePath);
|
|
248794
|
+
const sameName = [];
|
|
248795
|
+
for (const line2 of output.split("\n")) {
|
|
248796
|
+
const parsed = parseCmStatusLine(line2);
|
|
248797
|
+
if (!parsed) continue;
|
|
248798
|
+
for (const p of parsed.paths) {
|
|
248799
|
+
if (pathKey(p) === key2) return parsed.info;
|
|
248800
|
+
if (basename7(resolve8(p)) === basename7(resolve8(filePath))) sameName.push(parsed.info);
|
|
248801
|
+
}
|
|
248802
|
+
}
|
|
248803
|
+
return sameName.length === 1 ? sameName[0] : void 0;
|
|
248620
248804
|
}
|
|
248621
|
-
function
|
|
248622
|
-
|
|
248623
|
-
|
|
248624
|
-
|
|
248805
|
+
function pathKey(p) {
|
|
248806
|
+
const abs = resolve8(p);
|
|
248807
|
+
return process.platform === "win32" ? abs.toLowerCase() : abs;
|
|
248808
|
+
}
|
|
248809
|
+
function checkoutResult(filePath, result) {
|
|
248810
|
+
if (result.exitCode === 0) return okResult();
|
|
248811
|
+
const combined = (result.output + " " + result.error).toLowerCase();
|
|
248812
|
+
if (combined.includes("locked") || combined.includes("exclusive")) {
|
|
248813
|
+
return errorResult("locked", `'${filePath}' is locked`);
|
|
248814
|
+
}
|
|
248815
|
+
if (combined.includes("out of date") || combined.includes("not latest")) {
|
|
248816
|
+
return errorResult("outOfDate", `'${filePath}' is out of date \u2014 update before editing`);
|
|
248817
|
+
}
|
|
248818
|
+
return errorResult("error", `Cannot check out '${filePath}': ${result.error || result.output}`);
|
|
248819
|
+
}
|
|
248820
|
+
function addResult(filePath, result) {
|
|
248821
|
+
if (result.exitCode === 0) return okResult("File added to Plastic SCM");
|
|
248822
|
+
return errorResult("error", `Cannot add '${filePath}' to Plastic SCM: ${result.error || result.output}`);
|
|
248625
248823
|
}
|
|
248626
248824
|
function undoCheckoutResult(filePath, result) {
|
|
248627
248825
|
if (result.exitCode === 0) return okResult("Checkout undone in Plastic SCM");
|
|
@@ -248641,67 +248839,36 @@ var PlasticProvider = class {
|
|
|
248641
248839
|
const u = await plasticWhoamiAsync();
|
|
248642
248840
|
return u === "" ? void 0 : u;
|
|
248643
248841
|
}
|
|
248842
|
+
/**
|
|
248843
|
+
* Check a controlled file out before it is written. A file already checked out (or
|
|
248844
|
+
* otherwise pending) in this workspace is not checked out again: a second `cm co` on
|
|
248845
|
+
* it fails, and under lock rules it fails as "locked" by our own checkout. Private,
|
|
248846
|
+
* ignored, and out-of-workspace files only need to be writable.
|
|
248847
|
+
*/
|
|
248644
248848
|
prepareToWrite(filePath) {
|
|
248645
248849
|
if (!existsSync7(filePath)) return okResult();
|
|
248646
|
-
if (
|
|
248647
|
-
|
|
248648
|
-
}
|
|
248649
|
-
const result = cm(["co", filePath]);
|
|
248650
|
-
if (result.exitCode === 0) return okResult();
|
|
248651
|
-
const combined = (result.output + " " + result.error).toLowerCase();
|
|
248652
|
-
if (combined.includes("locked") || combined.includes("exclusive")) {
|
|
248653
|
-
return errorResult("locked", `'${filePath}' is locked`);
|
|
248654
|
-
}
|
|
248655
|
-
if (combined.includes("out of date") || combined.includes("not latest")) {
|
|
248656
|
-
return errorResult("outOfDate", `'${filePath}' is out of date \u2014 update before editing`);
|
|
248657
|
-
}
|
|
248658
|
-
return errorResult("error", `Cannot check out '${filePath}': ${result.error || result.output}`);
|
|
248850
|
+
if (localState(filePath) !== "controlled") return fs$1.prepareToWrite(filePath);
|
|
248851
|
+
return checkoutResult(filePath, cm(["co", filePath]));
|
|
248659
248852
|
}
|
|
248853
|
+
/** Add a new private file to Plastic SCM after it is written. */
|
|
248660
248854
|
finishedWrite(filePath) {
|
|
248661
248855
|
if (!existsSync7(filePath))
|
|
248662
248856
|
return errorResult("error", `'${filePath}' does not exist after write`);
|
|
248663
|
-
|
|
248664
|
-
|
|
248665
|
-
return fs$1.finishedWrite(filePath);
|
|
248666
|
-
const lines = statusResult.output.split("\n").filter((l2) => l2.trim());
|
|
248667
|
-
const tracked = lines.length > 0 && !lines[0].startsWith("?");
|
|
248668
|
-
if (tracked) return okResult();
|
|
248669
|
-
const result = cm(["add", filePath]);
|
|
248670
|
-
if (result.exitCode === 0) return okResult("File added to Plastic SCM");
|
|
248671
|
-
const combined = (result.output + " " + result.error).toLowerCase();
|
|
248672
|
-
if (combined.includes("ignored")) return fs$1.finishedWrite(filePath);
|
|
248673
|
-
return errorResult("error", `Cannot add '${filePath}' to Plastic SCM: ${result.error || result.output}`);
|
|
248857
|
+
if (localState(filePath) !== "private") return fs$1.finishedWrite(filePath);
|
|
248858
|
+
return addResult(filePath, cm(["add", filePath]));
|
|
248674
248859
|
}
|
|
248675
248860
|
/** Async twin of {@link prepareToWrite}. */
|
|
248676
248861
|
async prepareToWriteAsync(filePath) {
|
|
248677
248862
|
if (!existsSync7(filePath)) return okResult();
|
|
248678
|
-
if (
|
|
248679
|
-
|
|
248680
|
-
}
|
|
248681
|
-
const result = await cmAsync(["co", filePath]);
|
|
248682
|
-
if (result.exitCode === 0) return okResult();
|
|
248683
|
-
const combined = (result.output + " " + result.error).toLowerCase();
|
|
248684
|
-
if (combined.includes("locked") || combined.includes("exclusive")) {
|
|
248685
|
-
return errorResult("locked", `'${filePath}' is locked`);
|
|
248686
|
-
}
|
|
248687
|
-
if (combined.includes("out of date") || combined.includes("not latest")) {
|
|
248688
|
-
return errorResult("outOfDate", `'${filePath}' is out of date \u2014 update before editing`);
|
|
248689
|
-
}
|
|
248690
|
-
return errorResult("error", `Cannot check out '${filePath}': ${result.error || result.output}`);
|
|
248863
|
+
if (await localStateAsync(filePath) !== "controlled") return fs$1.prepareToWriteAsync(filePath);
|
|
248864
|
+
return checkoutResult(filePath, await cmAsync(["co", filePath]));
|
|
248691
248865
|
}
|
|
248692
248866
|
/** Async twin of {@link finishedWrite}. */
|
|
248693
248867
|
async finishedWriteAsync(filePath) {
|
|
248694
248868
|
if (!existsSync7(filePath))
|
|
248695
248869
|
return errorResult("error", `'${filePath}' does not exist after write`);
|
|
248696
|
-
|
|
248697
|
-
|
|
248698
|
-
return fs$1.finishedWriteAsync(filePath);
|
|
248699
|
-
if (trackedFromShortStatus(statusResult)) return okResult();
|
|
248700
|
-
const result = await cmAsync(["add", filePath]);
|
|
248701
|
-
if (result.exitCode === 0) return okResult("File added to Plastic SCM");
|
|
248702
|
-
const combined = (result.output + " " + result.error).toLowerCase();
|
|
248703
|
-
if (combined.includes("ignored")) return fs$1.finishedWriteAsync(filePath);
|
|
248704
|
-
return errorResult("error", `Cannot add '${filePath}' to Plastic SCM: ${result.error || result.output}`);
|
|
248870
|
+
if (await localStateAsync(filePath) !== "private") return fs$1.finishedWriteAsync(filePath);
|
|
248871
|
+
return addResult(filePath, await cmAsync(["add", filePath]));
|
|
248705
248872
|
}
|
|
248706
248873
|
/**
|
|
248707
248874
|
* Undo what {@link prepareToWrite} did, for an all-or-nothing batch that has to back
|
|
@@ -248831,8 +248998,8 @@ var PlasticProvider = class {
|
|
|
248831
248998
|
}
|
|
248832
248999
|
/**
|
|
248833
249000
|
* Status for a batch of files in ONE `cm status --machinereadable --all --ignored`
|
|
248834
|
-
* spawn. The machine format lists one item per line
|
|
248835
|
-
*
|
|
249001
|
+
* spawn. The machine format lists one item per line, its fields split by the separator
|
|
249002
|
+
* we ask for: code, absolute path (unquoted), and two merge flags (see parseCmStatusLine).
|
|
248836
249003
|
*
|
|
248837
249004
|
* Flag choice matters: `cm status` defaults to `--controlledchanged`, which omits
|
|
248838
249005
|
* a content-modified-but-not-checked-out file (CH) and local deletes/moves. `--all`
|
|
@@ -248844,8 +249011,9 @@ var PlasticProvider = class {
|
|
|
248844
249011
|
* files (plus one `cm whoami`) to fill `outOfDate` (loaded changeset < head) and
|
|
248845
249012
|
* lock holder (`openedByMe` when that's us, else `lockedBy: ["user@workspace"]`).
|
|
248846
249013
|
*
|
|
248847
|
-
* NOTE: status
|
|
248848
|
-
*
|
|
249014
|
+
* NOTE: the status line format follows the Unreal Plastic plugin (UEPlasticPlugin), which
|
|
249015
|
+
* runs against live workspaces; the first version here assumed quoted paths, matched no
|
|
249016
|
+
* file on a real workspace, and so checked out files already checked out.
|
|
248849
249017
|
*
|
|
248850
249018
|
* @param {string[]} filePaths
|
|
248851
249019
|
* @param {import('../vcStatus.js').VCStatusOptions} [options]
|
|
@@ -248866,7 +249034,7 @@ var PlasticProvider = class {
|
|
|
248866
249034
|
}
|
|
248867
249035
|
};
|
|
248868
249036
|
function plasticStatusArgs(filePaths) {
|
|
248869
|
-
return ["status", "--machinereadable", "--all", "--ignored", ...filePaths.map((p) => resolve8(p))];
|
|
249037
|
+
return ["status", "--machinereadable", `--fieldseparator=${STATUS_SEPARATOR}`, "--all", "--ignored", ...filePaths.map((p) => resolve8(p))];
|
|
248870
249038
|
}
|
|
248871
249039
|
function buildPlasticBase(result, filePaths) {
|
|
248872
249040
|
const byPath = /* @__PURE__ */ new Map();
|
|
@@ -248877,7 +249045,7 @@ function buildPlasticBase(result, filePaths) {
|
|
|
248877
249045
|
if (!parsed) continue;
|
|
248878
249046
|
for (const p of parsed.paths) {
|
|
248879
249047
|
const abs = resolve8(p);
|
|
248880
|
-
byPath.set(abs, parsed.info);
|
|
249048
|
+
byPath.set(pathKey(abs), parsed.info);
|
|
248881
249049
|
const base = basename7(abs);
|
|
248882
249050
|
if (!byBase.has(base)) byBase.set(base, []);
|
|
248883
249051
|
byBase.get(base).push(parsed.info);
|
|
@@ -248887,7 +249055,7 @@ function buildPlasticBase(result, filePaths) {
|
|
|
248887
249055
|
return filePaths.map((filePath) => {
|
|
248888
249056
|
const abs = resolve8(filePath);
|
|
248889
249057
|
const status = { filePath: abs, system: "plastic", writable: writableBit(abs) };
|
|
248890
|
-
let info = byPath.get(abs);
|
|
249058
|
+
let info = byPath.get(pathKey(abs));
|
|
248891
249059
|
if (!info) {
|
|
248892
249060
|
const sameName = byBase.get(basename7(abs));
|
|
248893
249061
|
if (sameName && sameName.length === 1) info = sameName[0];
|
|
@@ -248961,18 +249129,36 @@ var PLASTIC_DIRTY_CODES = /* @__PURE__ */ new Set([
|
|
|
248961
249129
|
"LM"
|
|
248962
249130
|
]);
|
|
248963
249131
|
var PLASTIC_UNTRACKED_CODES = /* @__PURE__ */ new Set(["PR", "IG"]);
|
|
249132
|
+
var PLASTIC_CHECKED_OUT_CODES = /* @__PURE__ */ new Set(["CO", "AD", "CP", "RP", "MV"]);
|
|
249133
|
+
var STATUS_SEPARATOR = ";";
|
|
248964
249134
|
function parseCmStatusLine(line2) {
|
|
248965
249135
|
const trimmed = line2.trim();
|
|
248966
|
-
|
|
248967
|
-
|
|
248968
|
-
|
|
248969
|
-
|
|
248970
|
-
|
|
249136
|
+
if (!trimmed) return null;
|
|
249137
|
+
let fields;
|
|
249138
|
+
if (trimmed.includes(STATUS_SEPARATOR)) {
|
|
249139
|
+
fields = trimmed.split(STATUS_SEPARATOR);
|
|
249140
|
+
} else {
|
|
249141
|
+
const space = trimmed.indexOf(" ");
|
|
249142
|
+
if (space === -1) return null;
|
|
249143
|
+
fields = [trimmed.slice(0, space), trimmed.slice(space + 1).replace(/\s+(True|False)\s+\S+$/i, "")];
|
|
249144
|
+
}
|
|
249145
|
+
const codes = fields[0].trim().split("+");
|
|
249146
|
+
const dirty = codes.some((c2) => PLASTIC_DIRTY_CODES.has(c2));
|
|
249147
|
+
const untracked = codes.some((c2) => PLASTIC_UNTRACKED_CODES.has(c2));
|
|
248971
249148
|
if (!dirty && !untracked) return null;
|
|
248972
|
-
|
|
248973
|
-
|
|
248974
|
-
|
|
248975
|
-
|
|
249149
|
+
let rest = fields.slice(1);
|
|
249150
|
+
if (rest.length >= 3 && /^(true|false)$/i.test(rest[rest.length - 2].trim())) rest = rest.slice(0, -2);
|
|
249151
|
+
let paths;
|
|
249152
|
+
if (codes.includes("MV")) {
|
|
249153
|
+
if (rest.length > 0 && /^\d+%$/.test(rest[0].trim())) rest = rest.slice(1);
|
|
249154
|
+
paths = rest.length >= 2 ? [rest.slice(0, -1).join(STATUS_SEPARATOR), rest[rest.length - 1]] : rest;
|
|
249155
|
+
} else {
|
|
249156
|
+
paths = [rest.join(STATUS_SEPARATOR)];
|
|
249157
|
+
}
|
|
249158
|
+
paths = paths.map((p) => p.trim().replace(/^"(.*)"$/, "$1")).filter((p) => p.length > 0);
|
|
249159
|
+
if (paths.length === 0) return null;
|
|
249160
|
+
const checkedOut = !untracked && codes.some((c2) => PLASTIC_CHECKED_OUT_CODES.has(c2));
|
|
249161
|
+
return { info: { tracked: !untracked, dirty, checkedOut, ignored: codes.includes("IG") }, paths };
|
|
248976
249162
|
}
|
|
248977
249163
|
var fs3 = new FilesystemProvider();
|
|
248978
249164
|
function svn(args) {
|
|
@@ -249544,7 +249730,7 @@ function fileStatus(filePaths, options = {}) {
|
|
|
249544
249730
|
// package.json
|
|
249545
249731
|
var package_default = {
|
|
249546
249732
|
name: "@patterkit/cli",
|
|
249547
|
-
version: "0.
|
|
249733
|
+
version: "0.10.0",
|
|
249548
249734
|
description: "The `patter` CLI: validate, format, export, play, test coverage, localise, and merge Patter projects.",
|
|
249549
249735
|
type: "module",
|
|
249550
249736
|
license: "MIT",
|
|
@@ -249573,9 +249759,9 @@ var package_default = {
|
|
|
249573
249759
|
"build:standalone:others": "node scripts/build-standalone.mjs --targets=linux-x64,linux-arm64,windows-x64"
|
|
249574
249760
|
},
|
|
249575
249761
|
dependencies: {
|
|
249576
|
-
"@patterkit/core": "0.
|
|
249577
|
-
"@patterkit/ops": "0.
|
|
249578
|
-
"@wildwinter/simple-vc-lib": "^0.5.
|
|
249762
|
+
"@patterkit/core": "0.5.0",
|
|
249763
|
+
"@patterkit/ops": "0.15.0",
|
|
249764
|
+
"@wildwinter/simple-vc-lib": "^0.5.2"
|
|
249579
249765
|
}
|
|
249580
249766
|
};
|
|
249581
249767
|
|
|
@@ -250135,7 +250321,8 @@ ${count} issue(s)${advisory}`);
|
|
|
250135
250321
|
console.log("no open suggestions");
|
|
250136
250322
|
return 0;
|
|
250137
250323
|
}
|
|
250138
|
-
|
|
250324
|
+
const nameOf = qualifierNamer(loaded.project);
|
|
250325
|
+
for (const o of open) console.log(describeSuggestion(o, nameOf));
|
|
250139
250326
|
const stale = open.filter((o) => o.stale.length).length;
|
|
250140
250327
|
console.log(`${open.length} open suggestion(s)${stale ? `, ${stale} out of date` : ""}`);
|
|
250141
250328
|
return 0;
|
|
@@ -250336,12 +250523,13 @@ function renderImportReport(plan) {
|
|
|
250336
250523
|
for (const p of r.problems) out.push(` [${p.severity}] ${p.message}${p.anchor ? ` (${p.anchor})` : ""}`);
|
|
250337
250524
|
return out.join("\n");
|
|
250338
250525
|
}
|
|
250339
|
-
function describeSuggestion(o) {
|
|
250526
|
+
function describeSuggestion(o, qualifierName) {
|
|
250340
250527
|
const s = o.suggestion;
|
|
250341
250528
|
const parts = [];
|
|
250342
250529
|
if (s.proposedCut) parts.push("cut this line");
|
|
250343
250530
|
if (s.proposed !== s.baseline) parts.push(`"${s.baseline}" -> "${s.proposed}"`);
|
|
250344
250531
|
if (s.proposedCharacter !== void 0) parts.push(`speaker ${s.baselineCharacter || "(none)"} -> ${s.proposedCharacter}`);
|
|
250532
|
+
if (s.proposedQualifier !== void 0) parts.push(`qualifier ${qualifierName(s.baselineQualifier) ?? "(none)"} -> ${qualifierName(s.proposedQualifier) ?? "(none)"}`);
|
|
250345
250533
|
if (s.proposedDirection !== void 0) parts.push(`direction "${s.baselineDirection ?? ""}" -> "${s.proposedDirection}"`);
|
|
250346
250534
|
const from = s.handoff ? ` [${s.handoff.id}]` : "";
|
|
250347
250535
|
const stale = o.stale.length ? ` OUT OF DATE (${o.stale.join(", ")})` : "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patterkit/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "The `patter` CLI: validate, format, export, play, test coverage, localise, and merge Patter projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"build:standalone:others": "node scripts/build-standalone.mjs --targets=linux-x64,linux-arm64,windows-x64"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@patterkit/core": "0.
|
|
33
|
-
"@patterkit/ops": "0.
|
|
34
|
-
"@wildwinter/simple-vc-lib": "^0.5.
|
|
32
|
+
"@patterkit/core": "0.5.0",
|
|
33
|
+
"@patterkit/ops": "0.15.0",
|
|
34
|
+
"@wildwinter/simple-vc-lib": "^0.5.2"
|
|
35
35
|
}
|
|
36
36
|
}
|