@patterkit/cli 0.7.5 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +249 -119
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -196888,6 +196888,11 @@ function hash32(input) {
|
|
|
196888
196888
|
return fnv32(input).toString(36).padStart(7, "0");
|
|
196889
196889
|
}
|
|
196890
196890
|
|
|
196891
|
+
// ../../../expr/packages/expr/src/own.ts
|
|
196892
|
+
function own(record, key2) {
|
|
196893
|
+
return record !== void 0 && Object.prototype.hasOwnProperty.call(record, key2) ? record[key2] : void 0;
|
|
196894
|
+
}
|
|
196895
|
+
|
|
196891
196896
|
// ../../../expr/packages/expr/src/parser.ts
|
|
196892
196897
|
var ParseError = class extends Error {
|
|
196893
196898
|
constructor(message, pos2, source2) {
|
|
@@ -197050,7 +197055,7 @@ function tokenise(src) {
|
|
|
197050
197055
|
if (/[a-zA-Z_]/.test(ch)) {
|
|
197051
197056
|
const m = src.slice(i).match(/^[a-zA-Z_][a-zA-Z0-9_]*/);
|
|
197052
197057
|
const word = m[0];
|
|
197053
|
-
const kw = KEYWORDS
|
|
197058
|
+
const kw = own(KEYWORDS, word);
|
|
197054
197059
|
tokens.push({ kind: kw ?? "IDENT", value: word, pos: pos2 });
|
|
197055
197060
|
i += word.length;
|
|
197056
197061
|
continue;
|
|
@@ -197334,11 +197339,11 @@ function evaluate(node, ctx, dialect) {
|
|
|
197334
197339
|
case "string":
|
|
197335
197340
|
return n.value;
|
|
197336
197341
|
case "scopedvar": {
|
|
197337
|
-
const scope = ctx.scopes
|
|
197342
|
+
const scope = own(ctx.scopes, n.scope);
|
|
197338
197343
|
if (scope === void 0) {
|
|
197339
197344
|
return false;
|
|
197340
197345
|
}
|
|
197341
|
-
const val = typeof scope.get === "function" ? scope.get(n.name) : scope
|
|
197346
|
+
const val = typeof scope.get === "function" ? scope.get(n.name) : own(scope, n.name);
|
|
197342
197347
|
if (val === void 0) {
|
|
197343
197348
|
if (missingPolicy.get(n.scope) === "throw") {
|
|
197344
197349
|
throw new EvalError2(`@${n.scope}.${n.name} is not declared on the current ${n.scope}.`);
|
|
@@ -197348,7 +197353,7 @@ function evaluate(node, ctx, dialect) {
|
|
|
197348
197353
|
return val;
|
|
197349
197354
|
}
|
|
197350
197355
|
case "call": {
|
|
197351
|
-
if (n.name === "advance" && !dialect.functions
|
|
197356
|
+
if (n.name === "advance" && !own(dialect.functions, n.name)) {
|
|
197352
197357
|
const arg = n.args[0];
|
|
197353
197358
|
if (n.args.length !== 1 || arg === void 0) {
|
|
197354
197359
|
throw new EvalError2(`advance() takes exactly 1 argument, got ${n.args.length}`);
|
|
@@ -197360,7 +197365,7 @@ function evaluate(node, ctx, dialect) {
|
|
|
197360
197365
|
const current = stageIndex(rec(arg), ladder, "advance");
|
|
197361
197366
|
return ladder[Math.min(current + 1, ladder.length - 1)];
|
|
197362
197367
|
}
|
|
197363
|
-
const def = dialect.functions
|
|
197368
|
+
const def = own(dialect.functions, n.name);
|
|
197364
197369
|
if (!def) throw new EvalError2(`unknown function '${n.name}'`);
|
|
197365
197370
|
return def.eval(n.args, { evaluate: rec, ctx });
|
|
197366
197371
|
}
|
|
@@ -197611,7 +197616,7 @@ function walkValidate(node, schema, dialect, path, issues) {
|
|
|
197611
197616
|
return;
|
|
197612
197617
|
}
|
|
197613
197618
|
case "call": {
|
|
197614
|
-
if (node.name === "advance" && !dialect.functions
|
|
197619
|
+
if (node.name === "advance" && !own(dialect.functions, node.name)) {
|
|
197615
197620
|
if (node.args.length !== 1) {
|
|
197616
197621
|
issues.push({ path, kind: "wrong-arg-count", severity: "error", message: `advance() takes exactly 1 argument, got ${node.args.length}` });
|
|
197617
197622
|
}
|
|
@@ -197629,7 +197634,7 @@ function walkValidate(node, schema, dialect, path, issues) {
|
|
|
197629
197634
|
}
|
|
197630
197635
|
return;
|
|
197631
197636
|
}
|
|
197632
|
-
const def = dialect.functions
|
|
197637
|
+
const def = own(dialect.functions, node.name);
|
|
197633
197638
|
if (!def) {
|
|
197634
197639
|
issues.push({ path, kind: "unknown-function", severity: "error", message: `unknown function '${node.name}'` });
|
|
197635
197640
|
return;
|
|
@@ -197743,7 +197748,7 @@ function typeOf(node, schema, dialect) {
|
|
|
197743
197748
|
}
|
|
197744
197749
|
case "call": {
|
|
197745
197750
|
if (node.name === "advance") return "quality";
|
|
197746
|
-
const def = dialect.functions
|
|
197751
|
+
const def = own(dialect.functions, node.name);
|
|
197747
197752
|
return def ? inferredFromReturn(def.returnType) : "unknown";
|
|
197748
197753
|
}
|
|
197749
197754
|
case "unary":
|
|
@@ -198354,8 +198359,12 @@ import { dirname, join, relative, resolve } from "path";
|
|
|
198354
198359
|
var PropertyBag = class _PropertyBag {
|
|
198355
198360
|
/** The live values record (stable identity across reseed, so an
|
|
198356
198361
|
* EvalContext built over it stays valid). Read-path for evaluation;
|
|
198357
|
-
* writes go through `set` so the firing rule applies.
|
|
198358
|
-
|
|
198362
|
+
* writes go through `set` so the firing rule applies.
|
|
198363
|
+
*
|
|
198364
|
+
* It has no prototype, so a scope answers only its own properties: on a plain
|
|
198365
|
+
* object, `@world.constructor` read the built-in `Object` function rather than
|
|
198366
|
+
* "missing", and a write to `__proto__` vanished (October 2026 review). */
|
|
198367
|
+
values = /* @__PURE__ */ Object.create(null);
|
|
198359
198368
|
decls = /* @__PURE__ */ new Map();
|
|
198360
198369
|
subscribers = /* @__PURE__ */ new Set();
|
|
198361
198370
|
auditors = /* @__PURE__ */ new Set();
|
|
@@ -198388,6 +198397,13 @@ var PropertyBag = class _PropertyBag {
|
|
|
198388
198397
|
normalise(name) {
|
|
198389
198398
|
return this.norm(name);
|
|
198390
198399
|
}
|
|
198400
|
+
/** Whether a STORY write to this property is allowed: false only for a
|
|
198401
|
+
* declaration marked `writable: false`. A host write is always allowed. The
|
|
198402
|
+
* registry asks before writing, so it never has to read a refusal out of an
|
|
198403
|
+
* error that a listener might have raised instead. */
|
|
198404
|
+
writable(name) {
|
|
198405
|
+
return this.decls.get(this.norm(name))?.writable !== false;
|
|
198406
|
+
}
|
|
198391
198407
|
/** Write a property. Engine writes (the default) notify subscribers;
|
|
198392
198408
|
* pass `silent: true` for a host write, which reaches only the audit
|
|
198393
198409
|
* hook. Throws on a read-only property unless the caller says it is the
|
|
@@ -198397,7 +198413,7 @@ var PropertyBag = class _PropertyBag {
|
|
|
198397
198413
|
* it. Returns the change. */
|
|
198398
198414
|
set(name, value, opts) {
|
|
198399
198415
|
const n = this.norm(name);
|
|
198400
|
-
if (!opts?.host && this.
|
|
198416
|
+
if (!opts?.host && !this.writable(n)) throw new Error(`'${name}' is read-only`);
|
|
198401
198417
|
const change = {
|
|
198402
198418
|
name: n,
|
|
198403
198419
|
prev: this.values[n],
|
|
@@ -198406,8 +198422,8 @@ var PropertyBag = class _PropertyBag {
|
|
|
198406
198422
|
reason: opts?.reason
|
|
198407
198423
|
};
|
|
198408
198424
|
this.values[n] = value;
|
|
198409
|
-
for (const audit of this.auditors) audit(change);
|
|
198410
|
-
if (!change.silent) for (const fn of this.subscribers) fn(change);
|
|
198425
|
+
for (const audit of [...this.auditors]) audit(change);
|
|
198426
|
+
if (!change.silent) for (const fn of [...this.subscribers]) fn(change);
|
|
198411
198427
|
return change;
|
|
198412
198428
|
}
|
|
198413
198429
|
/** Notified of engine (non-silent) writes. Returns the unsubscribe. */
|
|
@@ -198611,11 +198627,8 @@ var ScopeRegistry = class {
|
|
|
198611
198627
|
const e = this.scopes.get(scope);
|
|
198612
198628
|
if (!e) throw new Error(`unknown scope '@${scope}'`);
|
|
198613
198629
|
if (e.kind === "owned") {
|
|
198614
|
-
|
|
198615
|
-
|
|
198616
|
-
} catch {
|
|
198617
|
-
throw new Error(`'@${scope}.${name}' is read-only`);
|
|
198618
|
-
}
|
|
198630
|
+
if (!opts?.host && !e.bag.writable(name)) throw new Error(`'@${scope}.${name}' is read-only`);
|
|
198631
|
+
e.bag.set(name, value, opts?.host ? { host: true } : void 0);
|
|
198619
198632
|
return;
|
|
198620
198633
|
}
|
|
198621
198634
|
const n = e.norm(name);
|
|
@@ -199319,14 +199332,14 @@ function sameScope(a, b) {
|
|
|
199319
199332
|
return norm(a) === norm(b);
|
|
199320
199333
|
}
|
|
199321
199334
|
function projectScopes(project, merged) {
|
|
199322
|
-
const
|
|
199323
|
-
if (!merged) return { host:
|
|
199335
|
+
const own2 = project.scopeRegistry;
|
|
199336
|
+
if (!merged) return { host: own2, external: [], notes: [] };
|
|
199324
199337
|
const notes = [];
|
|
199325
199338
|
const hosted = [];
|
|
199326
199339
|
const bundled = /* @__PURE__ */ new Set();
|
|
199327
199340
|
let changed = false;
|
|
199328
199341
|
const shared = (token2) => token2 === PATTER_SCOPE ? void 0 : merged.spec.scopes.find((s) => s.token === token2);
|
|
199329
|
-
for (const h of
|
|
199342
|
+
for (const h of own2?.scopes ?? []) {
|
|
199330
199343
|
const s = shared(h.token);
|
|
199331
199344
|
const who2 = merged.owners.get(h.token);
|
|
199332
199345
|
if (!s || !who2) {
|
|
@@ -199362,7 +199375,7 @@ function projectScopes(project, merged) {
|
|
|
199362
199375
|
changed = true;
|
|
199363
199376
|
}
|
|
199364
199377
|
const external = merged.spec.scopes.filter((s) => s.token !== PATTER_SCOPE && !bundled.has(s.token));
|
|
199365
|
-
const host2 = !changed ?
|
|
199378
|
+
const host2 = !changed ? own2 : hosted.length ? { version: own2?.version ?? 1, scopes: hosted } : void 0;
|
|
199366
199379
|
return { host: host2, external, notes };
|
|
199367
199380
|
}
|
|
199368
199381
|
function externalGameScopes(strict, merged) {
|
|
@@ -199869,10 +199882,10 @@ function ownedDecls(s) {
|
|
|
199869
199882
|
});
|
|
199870
199883
|
}
|
|
199871
199884
|
function drivableScopes(loaded, bundle) {
|
|
199872
|
-
const
|
|
199873
|
-
if (!loaded.gameScopes || !bundle.externalScopes?.length) return
|
|
199874
|
-
const have = new Set(
|
|
199875
|
-
return [...
|
|
199885
|
+
const own2 = bundle.scopeRegistry?.scopes ?? [];
|
|
199886
|
+
if (!loaded.gameScopes || !bundle.externalScopes?.length) return own2;
|
|
199887
|
+
const have = new Set(own2.map((s) => s.token));
|
|
199888
|
+
return [...own2, ...loaded.gameScopes.merged.spec.scopes.filter((s) => s.token !== PATTER_SCOPE && !have.has(s.token))];
|
|
199876
199889
|
}
|
|
199877
199890
|
var hostScopeTokens = (loaded, bundle) => new Set(drivableScopes(loaded, bundle).map((s) => s.token));
|
|
199878
199891
|
var asHostScope2 = (s) => ({
|
|
@@ -200592,25 +200605,25 @@ function reachabilityIssues(loaded, compiled) {
|
|
|
200592
200605
|
const issues = [];
|
|
200593
200606
|
const report = (nodes, sceneId, gate) => {
|
|
200594
200607
|
for (const node of nodes) {
|
|
200595
|
-
const
|
|
200596
|
-
const branches =
|
|
200608
|
+
const own2 = node.condition;
|
|
200609
|
+
const branches = own2 ? disjuncts(own2.ast) : [void 0];
|
|
200597
200610
|
let reason;
|
|
200598
200611
|
const refuted = branches.every((b) => {
|
|
200599
200612
|
const r = refute([...gate, ...b ? termsOfAst(b, sceneId) : []]);
|
|
200600
200613
|
reason ??= r;
|
|
200601
200614
|
return r !== void 0;
|
|
200602
200615
|
});
|
|
200603
|
-
if (refuted && reason !== void 0 &&
|
|
200616
|
+
if (refuted && reason !== void 0 && own2) {
|
|
200604
200617
|
issues.push({
|
|
200605
200618
|
nodeId: node.id,
|
|
200606
200619
|
field: "condition",
|
|
200607
|
-
src:
|
|
200620
|
+
src: own2.src,
|
|
200608
200621
|
severity: "warning",
|
|
200609
200622
|
message: `this can never run: ${reason}`
|
|
200610
200623
|
});
|
|
200611
200624
|
continue;
|
|
200612
200625
|
}
|
|
200613
|
-
if (node.type === "group") report(node.children, sceneId, [...gate, ...requirementsOf(
|
|
200626
|
+
if (node.type === "group") report(node.children, sceneId, [...gate, ...requirementsOf(own2, sceneId)]);
|
|
200614
200627
|
}
|
|
200615
200628
|
};
|
|
200616
200629
|
for (const scene of Object.values(bundle.scenes)) {
|
|
@@ -200791,7 +200804,7 @@ function bundleOutputPath(loaded) {
|
|
|
200791
200804
|
}
|
|
200792
200805
|
|
|
200793
200806
|
// ../ops/src/playable-runtime.ts
|
|
200794
|
-
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var q=Object.defineProperty;var Be=Object.getOwnPropertyDescriptor;var Ne=Object.getOwnPropertyNames;var Fe=Object.prototype.hasOwnProperty;var Ae=(n,e)=>{for(var t in e)q(n,t,{get:e[t],enumerable:!0})},Te=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Ne(e))!Fe.call(n,s)&&s!==t&&q(n,s,{get:()=>e[s],enumerable:!(r=Be(e,s))||r.enumerable});return n};var Ge=n=>Te(q({},"__esModule",{value:!0}),n);var et={};Ae(et,{Checkpoint:()=>N,Engine:()=>$,Flow:()=>D,buildTagIndex:()=>M,describeBundle:()=>Ie,effectiveGameData:()=>Re,gameDataFields:()=>De,gameDataValue:()=>X});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 A(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=e.scopes[i.scope];if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):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"&&!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=_(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=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 u=s(i.left);if(typeof u!="boolean")throw new d(`\'and\' requires boolean operands, left is ${typeof u}`);if(!u)return!1;let m=s(i.right);if(typeof m!="boolean")throw new d(`\'and\' requires boolean operands, right is ${typeof m}`);return m}if(i.op==="or"){let u=s(i.left);if(typeof u!="boolean")throw new d(`\'or\' requires boolean operands, left is ${typeof u}`);if(u)return!0;let m=s(i.right);if(typeof m!="boolean")throw new d(`\'or\' requires boolean operands, right is ${typeof m}`);return m}let o=s(i.left),a=s(i.right),c=_(i.left,e),l=_(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!je(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 ie(o,a);case"!=":return!ie(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 ie(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 _(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 je=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function R(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function U(n){let e=R(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 Ve={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},Le=[Ve];function oe(n,e,t){let r=t?.countingCalls??Le;return T(n,t?.want??!0,e,r)}function T(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=T(n.left,e,t,r),i=T(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 T(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={};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)}set(e,t,r){let s=this.norm(e);if(!r?.host&&this.decls.get(s)?.writable===!1)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])=>ce(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 ce(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 Me=n=>n.toLowerCase(),W=1,G=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??Me,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"){try{i.bag.set(t,r,s?.host?{host:!0}:void 0)}catch{throw new Error(`\'@${e}.${t}\' is read-only`)}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,...ce(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($e(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 ae(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=ae(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:W,scopes:this.save()}}loadFragment(e){if(e.version!==W)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${W})`);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 ae(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function $e(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 le=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function O(n){return n.ctx.host??{}}var K={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=O(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:de("check_flags"),eval(n,e){let t=pe(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:de("set_flags"),eval(n,e){let t=[...pe(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:V("visits"),eval:(n,e)=>O(e).visits?.(j(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:V("seen"),eval:(n,e)=>(O(e).visits?.(j(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:V("patter_visits"),eval:(n,e)=>O(e).patterVisits?.(j(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:V("patter_seen"),eval:(n,e)=>(O(e).patterVisits?.(j(n,e,"patter_seen"))??0)>0}}},mt=le.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function ge(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 j(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 V(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 pe(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 de(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 He=/^@[A-Za-z0-9_.]+$/;function*qe(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:He.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 _e(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function Ue(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function We(n){let e="",t=!1;for(let r of n){if(Ue(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function ue(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?We(r):n}function he(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of qe(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?"":_e(s)}return t}function C(n,e){for(let t of n){e(t);let r=t.children;r&&C(r,e)}}function Ke(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||Ke(n.name)}function J(n){return`cast:${n}`}var z={open:"[",close:"]"},fe="SFX";var Y=3;function L(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function M(n){let e=new Map,t=(r,s)=>{let i=L([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group")for(let o of r.children)t(o,i);else for(let o of r.beats??[])e.set(o.id,L([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=L(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=L([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var me=new WeakMap,I="Patter",B=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),Se=Symbol("ownsRegistry"),y={stage:n=>`patter/scene/${B(n)}`,flow:n=>`patter/flow/${B(n)}/`,flowGlobals:n=>`patter/flow/${B(n)}/patter`,flowScene:(n,e)=>`patter/flow/${B(n)}/scene/${B(e)}`},N=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=[];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,u=new Map,m=new Map;for(let[g,h]of Object.entries(e.scenes)){this.sceneGameIdToId.set(S(h),g);let v=new Map;for(let f of h.blocks)u.set(f.id,{sceneId:g}),m.set(f.id,f),v.set(S(f),f.id),C(f.children,se=>p.set(se.id,se));this.blockGameIdToId.set(g,v)}let Z=e.properties??[],H=Z.filter(g=>g.shared??!0).map(we),Pe=Z.filter(g=>!(g.shared??!0)).map(we),Oe=new Set(H.map(g=>g.name.toLowerCase())),x=t.registry??new G,ee=!t.registry||t[Se]===!0,te=new b(H,{pathPrefix:"@patter."}),re=[],F=[];try{x.mountOwned("patter",te,{owner:I}),F.push("patter");let g=new Set;if(t.world){let h=e.scopeRegistry?.scopes.find(f=>f.token==="world"),v=(h?.declarations??[]).map(Ee);x.defineForeign("world",t.world,v,{writable:h?.writable??!0,owner:I}),F.push("world"),g.add("world")}for(let h of ee?e.scopeRegistry?.scopes??[]:[]){if(g.has(h.token)||x.has(h.token))continue;let v=(h.declarations??[]).map(f=>ze(f,h.writable));x.defineOwned(h.token,v,{owner:I}),F.push(h.token),re.push(h.token)}}catch(g){for(let h of F)x.remove(h,{keep:!0});throw g}let ne=new Map;for(let[g,h]of Object.entries(e.scenes)){let v=new Set((h.sceneProps??[]).filter(f=>f.shared??!1).map(f=>f.name.toLowerCase()));ne.set(g,v)}this.host={logEnabled: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:u,blockById:m,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:M(e),registry:x,ownsRegistry:ee,patterBag:te,hostScopes:re,patterSharedDecls:H,patterLocalDecls:Pe,patterSharedNames:Oe,sceneSharedNames:ne,sharedVisits:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??z).open,captionClose:(e.closedCaptions??z).close,captionCharacter:e.closedCaptions?.character||fe,refSplitCache:new Map,refSplitRevision:x.revision}}get locale(){return this.currentLocale}get isSourceDebug(){return this.sourceDebug}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,[Se]: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.start(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=this.resolveSceneRef(e);if(!this.host.bundle.scenes[r])throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=this.blockGameIdToId.get(r)?.get(t)??(this.host.blockIndex.get(t)?.sceneId===r?t:void 0);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 this.host.bundle.scenes[e]?e:this.sceneGameIdToId.get(e)??e}resolveBlockRef(e,t){if(t!=null){if(this.host.blockById.has(t))return t;if(e!=null){let r=this.blockGameIdToId.get(e)?.get(t);if(r)return r}return 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 this.host.tagIndex.get(e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?this.host.tagIndex.get(t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?this.host.tagIndex.get(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)ye(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 ye(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,...ve(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...S(t)?{gameId:S(t)}:{},name:t.name,...ve(t.gameData),...this.tagsField(t.id),children:t.children.map(r=>this.outlineNode(r))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let r of t.blocks)C(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=this.host.tagIndex.get(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[J(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=this.host.tagIndex.get(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 N(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});this.creationOptions.world&&t.has("world")&&t.remove("world")}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.engineTraceHandlers.delete(e)}emitEngine(e,t,r){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineLog.length,...r?{scene:r}:{}})}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=Ce(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:Y,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:xe(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==Y)throw new Error(`unsupported save version: ${String(t)}`);this.assertExternalScopes();let r=this.host.registry;for(let[i,o]of this.flowsById)o.releaseBags(i in e.flows),o.close();this.flowsById.clear();for(let i of this.host.stageBags.keys())r.remove(y.stage(i),{keep:!0});this.host.stageBags.clear();let s=e.version===2?Ye(e):e.registry;s&&(this.host.ownsRegistry?r.load(s):r.load(s,{keepParked:!0})),this.host.sharedVisits.clear();for(let[i,o]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(i,o);this.host.sharedSelectors.clear();for(let[i,o]of ke(e.sharedSelectors))this.host.sharedSelectors.set(i,o);for(let[i,o]of Object.entries(e.flows)){let a=new D(i,this.host,this.defaultSeed);a.restore(o),this.flowsById.set(i,a)}}},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=R(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(),s=i=>i?.find(o=>o.name.toLowerCase()===r&&o.type==="quality")?.stages;if(e==="patter")return s(this.host.patterSharedDecls)??s(this.host.patterLocalDecls);if(e==="scene"){let i=this.currentSceneId!=null?this.host.bundle.scenes[this.currentSceneId]:void 0;return s(i?.sceneProps)}return this.registryQualities?.(e,t)??s(this.host.bundle.scopeRegistry?.scopes.find(i=>i.token===e)?.declarations)}start(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.pendingChoice=null,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.start(e,t)}goto(e,t){if(this.closed)return!1;if(this.touch(),e==="END")return this.started=!0,this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null,this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let r=this.host.sceneGameIdToId.get(e)??(this.host.bundle.scenes[e]?e:void 0);if(r===void 0)return!1;let s;return t!==void 0&&(s=this.host.blockGameIdToId.get(r)?.get(t)??(this.host.blockIndex.get(t)?.sceneId===r?t:void 0),s===void 0)?!1:this.started?(this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null,this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??r,"jump"),this.settle(),!0):(this.start(r,s),!0)}close(){this.releaseBags(!1),this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,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,u=this.stack.map(m=>({...m}));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:u})})}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);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.logEnabled&&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}:{}})}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.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:Je(this.pendingPrompt.shown)}:{},selectors:xe(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=R(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=ke(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)}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),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.length>0){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.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.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=>this.eligible(c)),s=e.options?.order??"sequential",i=e.options?.exhaust??"once",o=c=>(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);if(r.bag===void 0&&(r.bag=o()),r.bag.length===0){if(t==="once")return null;if(i){let u=e[s-1];return r.last=u.id,u}r.bag=o()}let a=r.bag,c=r.last!==void 0&&a.length>1?a.indexOf(r.last):-1,l=Math.floor(this.rng()*(c>=0?a.length-1:a.length));c>=0&&l>=c&&l++;let p=a[l];return a.splice(l,1),r.last=p,e.find(u=>u.id===p)}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(u=>u.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):0}matchedSpec(e,t){return oe(e,s=>be(A(s,this.context(),K)),{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){for(let t of e??[]){let r=this.evalExpr(t.value),s=this.host.logEnabled?this.getProperty(t.target):void 0;this.writeProperty(t.target,r,!1),this.emit({type:"write",target:t.target,value:r,...s!==void 0?{prev:s}:{}})}}eligible(e){return e.condition?be(this.evalExpr(e.condition)):!0}evalExpr(e){return A(this.conditionAst(e),this.context(),K)}conditionAst(e){let t=me.get(e);return t||(t=k(e.ast),me.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=U(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=this.host.tagIndex.get(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 he(e,t=>this.getProperty(t))}stripCaptions(e){return ue(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=this.host.tagIndex.get(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 C(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=J(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}splitRef(e){return Ce(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:I}),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(a=>!t.has(a.name.toLowerCase())),{pathPrefix:"@scene."}),o=y.flowScene(this.id,e);this.host.registry.mountOwned(o,i,{owner:I}),this.registered.add(o),this.sceneBags.set(e,i),s&&s.undo.push(()=>{this.sceneBags.get(e)===i&&(this.host.registry.remove(o),this.registered.delete(o),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let i=new b(r.filter(o=>t.has(o.name.toLowerCase())),{pathPrefix:"@scene."});this.host.registry.mountOwned(y.stage(e),i,{owner:I}),this.host.stageBags.set(e,i),s&&s.undo.push(()=>{this.host.stageBags.get(e)===i&&(this.host.registry.remove(y.stage(e)),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 ye(n,e){C(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 ve(n){return n&&Object.keys(n).length?{gameData:n}:{}}function Je(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 xe(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 ke(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 we(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Ee(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function ze(n,e){let t=Ee(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function Ce(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=ge(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function Ye(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 Qe=(n,e)=>n.shared??e;function Q(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:Qe(n,e)}}function Xe(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function Ze(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 Ie(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)Ze(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>Q(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=>Q(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(Xe)}));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=>Q(o,!0)),scene:r},gameData:i,counts:e}}function De(n,e){return n.gameDataFields?.[e]??[]}function X(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function Re(n,e){let t={};for(let r of n){let s=X(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 Ge(et);})();\n';
|
|
200807
|
+
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var U=Object.defineProperty;var Ve=Object.getOwnPropertyDescriptor;var Le=Object.getOwnPropertyNames;var Me=Object.prototype.hasOwnProperty;var $e=(n,e)=>{for(var t in e)U(n,t,{get:e[t],enumerable:!0})},He=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Le(e))!Me.call(n,s)&&s!==t&&U(n,s,{get:()=>e[s],enumerable:!(r=Ve(e,s))||r.enumerable});return n};var qe=n=>He(U({},"__esModule",{value:!0}),n);var at={};$e(at,{Checkpoint:()=>A,Engine:()=>q,Flow:()=>I,buildTagIndex:()=>H,describeBundle:()=>Ne,effectiveGameData:()=>Ae,gameDataFields:()=>Fe,gameDataValue:()=>ne});function D(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 j(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=D(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):D(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"&&!D(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=W(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=D(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=W(i.left,e),l=W(i.right,e),p=c??l;if(p!==void 0){if(c&&l&&!_e(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 le(o,a);case"!=":return!le(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 le(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 W(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 _e=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function R(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function J(n){let e=R(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 Ue={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},We=[Ue];function pe(n,e,t){let r=t?.countingCalls??We;return V(n,t?.want??!0,e,r)}function V(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=V(n.left,e,t,r),i=V(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 V(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])=>ge(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 ge(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 Je=n=>n.toLowerCase(),K=1,L=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??Je,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,...ge(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(Ke(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 de(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=de(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 de(n){return n.kind==="foreign"?[...n.decls.entries()]:n.bag.declarations().map(e=>[n.bag.normalise(e.name),e])}function Ke(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 ue=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function O(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 d("random(a, b) requires exactly 2 arguments");let t=O(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:fe("check_flags"),eval(n,e){let t=he(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:fe("set_flags"),eval(n,e){let t=[...he(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:$("visits"),eval:(n,e)=>O(e).visits?.(M(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:$("seen"),eval:(n,e)=>(O(e).visits?.(M(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:$("patter_visits"),eval:(n,e)=>O(e).patterVisits?.(M(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:$("patter_seen"),eval:(n,e)=>(O(e).patterVisits?.(M(n,e,"patter_seen"))??0)>0}}},Ct=ue.map(n=>n.token).filter(n=>n!=="patter"&&n!=="scene");function me(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 M(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 $(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 he(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 fe(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*Ye(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 Qe(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function Xe(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function Ze(n){let e="",t=!1;for(let r of n){if(Xe(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function Se(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?Ze(r):n}function ye(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of Ye(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?"":Qe(s)}return t}function C(n,e){for(let t of n){e(t);let r=t.children;r&&C(r,e)}}function et(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||et(n.name)}function Y(n){return`cast:${n}`}var Q={open:"[",close:"]"},ve="SFX";var X=3;function B(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=B([...s,...r.tags??[]]);if(e.set(r.id,i),r.type==="group"&&r.prompt&&e.set(r.prompt.id,B([...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,B([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=B(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=B([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var we=new WeakMap,F="Patter",N=n=>n.replace(/%/g,"%25").replace(/\\//g,"%2F"),be=Symbol("ownsRegistry"),y={stage:n=>`patter/scene/${N(n)}`,flow:n=>`patter/flow/${N(n)}/`,flowGlobals:n=>`patter/flow/${N(n)}/patter`,flowScene:(n,e)=>`patter/flow/${N(n)}/scene/${N(e)}`},A=class{constructor(e){this.journal=e}journal},q=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),C(m.children,G=>p.set(G.id,G));this.blockGameIdToId.set(g,v)}let se=e.properties??[],_=se.filter(g=>g.shared??!0).map(Ce),Te=se.filter(g=>!(g.shared??!0)).map(Ce),Ge=new Set(_.map(g=>g.name.toLowerCase())),x=t.registry??new L,ie=!t.registry||t[be]===!0,oe=new b(_,{pathPrefix:"@patter."}),ae=[],T=[];try{x.mountOwned("patter",oe,{owner:F}),T.push("patter");let g=new Set;for(let[h,v]of Object.entries(Ie(t))){let m=e.scopeRegistry?.scopes.find(je=>je.token===h),G=(m?.declarations??[]).map(Oe);x.defineForeign(h,v,G,{writable:m?.writable??!0,owner:F}),T.push(h),g.add(h)}for(let h of ie?e.scopeRegistry?.scopes??[]:[]){if(g.has(h.token)||x.has(h.token))continue;let v=(h.declarations??[]).map(m=>rt(m,h.writable));x.defineOwned(h.token,v,{owner:F}),T.push(h.token),ae.push(h.token)}}catch(g){for(let h of T)x.remove(h,{keep:!0});throw g}let ce=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()));ce.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:H(e),registry:x,ownsRegistry:ie,patterBag:oe,hostScopes:ae,patterSharedDecls:_,patterLocalDecls:Te,patterSharedNames:Ge,sceneSharedNames:ce,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??Q).open,captionClose:(e.closedCaptions??Q).close,captionCharacter:e.closedCaptions?.character||ve,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,[be]:this.host.ownsRegistry});this.release(!0);let i=s();try{return i.loadGame(t),r(i)}catch{i.release(!0);let o=s();for(let[a,c]of Object.entries(t.flows)){let l=c.cursor.currentSceneId;try{o.openFlow(a,l!==null?{scene:l}:{})}catch{}}return r(o)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:r,blockId:s}=this.resolveOpenAddress(t.scene,t.block),i=this.flowsById.get(e),o=this.host.journal;if(o&&i&&!i.isClosed)throw new Error(`openFlow would replace the open flow \'${e}\', which can\'t be undone while a checkpoint is open`);i?.close();let a=new I(e,this.host,t.seed??this.defaultSeed);return o&&(o.flows.add(a),o.opened.add(a),o.undo.push(()=>{a.close(),i?this.flowsById.set(e,i):this.flowsById.delete(e)})),this.flowsById.set(e,a),a.begin(r,s),a}runFlow(e,t,r){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:r}).advanceToStop().played;if(!s.goto(t,r))throw new Error(`runFlow: address not found: ${t}${r===void 0?"":` / ${r}`}`);return s.advanceToStop().played}assertExternalScopes(){for(let e of this.host.bundle.externalScopes??[])if(!this.host.registry.has(e))throw new Error(`this content names @${e}, which no engine on this registry registered: give every engine the game\'s one registry`)}resolveOpenAddress(e,t){if(e!==void 0){let r=ee(this.host,e);if(r===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:r};let s=te(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 ee(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:te(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 this.host.tagIndex.get(e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?this.host.tagIndex.get(t):void 0)??[]}tagsForBlock(e,t){let r=this.resolveSceneRef(e),s=this.resolveBlockRef(r,t);return(s!=null?this.host.tagIndex.get(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)xe(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 xe(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,...ke(e.gameData),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...S(t)?{gameId:S(t)}:{},name:t.name,...ke(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)C(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=this.host.tagIndex.get(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)}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=this.host.tagIndex.get(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 A(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(Ie(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=Be(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:X,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:Re(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==2&&t!==X)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?nt(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 Pe(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new I(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}}},I=class{id;host;local;registered=new Set;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPrompt=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.patterBag.get(e):this.local.get(e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.registry.set("patter",e,t):this.local.set(e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;if(t!==null)return this.sceneBagFor(t,e)?.get(e)},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.sceneBagFor(r,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,r){this.id=e,this.host=t,this.rngState=R(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 Z(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(r);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:Z(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(r)}return this.registryQualities?.(e,t)??Z(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=ee(this.host,e);if(r===void 0)return!1;let s;return t!==void 0&&(s=te(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:tt(this.pendingPrompt.shown)}:{},selectors:Re(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=R(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=Pe(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 pe(e,i=>{try{return De(j(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 De(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 j(this.conditionAst(e),this.context(),z)}conditionAst(e){let t=we.get(e);return t||(t=k(e.ast),we.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=J(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=this.host.tagIndex.get(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 ye(e,t=>this.getProperty(t))}stripCaptions(e){return Se(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=this.host.tagIndex.get(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 C(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)}splitRef(e){return Be(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:F}),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=Ee(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=Ee(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 xe(n,e){C(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 ke(n){return n&&Object.keys(n).length?{gameData:n}:{}}function tt(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 Re(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 Pe(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 Ee(n,e,t,r){let s=r?JSON.stringify(t.save()):"";return n.mountOwned(e,t,{owner:F}),r&&JSON.stringify(t.save())!==s}function Ce(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Ie(n){return{...n.world?{world:n.world}:{},...n.hostScopes}}function Z(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 Oe(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function rt(n,e){let t=Oe(n),r=n.writable??e;return r===void 0?t:{...t,writable:r}}function ee(n,e){return n.sceneGameIdToId.get(e)??(n.bundle.scenes[e]?e:void 0)}function te(n,e,t){return n.blockGameIdToId.get(e)?.get(t)??(n.blockIndex.get(t)?.sceneId===e?t:void 0)}function Be(n,e){n.refSplitRevision!==n.registry.revision&&(n.refSplitCache.clear(),n.refSplitRevision=n.registry.revision);let t=n.refSplitCache.get(e);return t||(t=me(e,r=>r==="scene"||n.registry.has(r)||(n.bundle.externalScopes?.includes(r)??!1)),n.refSplitCache.set(e,t)),t}function nt(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 De(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var st=(n,e)=>n.shared??e;function re(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:st(n,e)}}function it(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function ot(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 Ne(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)ot(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>re(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=>re(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(it)}));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=>re(o,!0)),scene:r},gameData:i,counts:e}}function Fe(n,e){return n.gameDataFields?.[e]??[]}function ne(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function Ae(n,e){let t={};for(let r of n){let s=ne(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 qe(at);})();\n';
|
|
200795
200808
|
|
|
200796
200809
|
// ../ops/src/export-html.ts
|
|
200797
200810
|
var esc = (s) => s.replace(/[&<>]/g, (c2) => ({ "&": "&", "<": "<", ">": ">" })[c2]);
|
|
@@ -201042,6 +201055,7 @@ function buildTagIndex(bundle) {
|
|
|
201042
201055
|
const visit = (node, inherited) => {
|
|
201043
201056
|
const acc = dedupe([...inherited, ...node.tags ?? []]);
|
|
201044
201057
|
index2.set(node.id, acc);
|
|
201058
|
+
if (node.type === "group" && node.prompt) index2.set(node.prompt.id, dedupe([...acc, ...node.prompt.tags ?? []]));
|
|
201045
201059
|
if (node.type === "group") {
|
|
201046
201060
|
for (const child2 of node.children) visit(child2, acc);
|
|
201047
201061
|
} else {
|
|
@@ -201100,11 +201114,13 @@ var Engine = class _Engine {
|
|
|
201100
201114
|
sceneGameIdToId = /* @__PURE__ */ new Map();
|
|
201101
201115
|
blockGameIdToId = /* @__PURE__ */ new Map();
|
|
201102
201116
|
/** The options this engine was built with - reused verbatim by `hotSwap` so the replacement
|
|
201103
|
-
* engine keeps the same
|
|
201117
|
+
* engine keeps the same host scopes, custom RNG, and diagnostic hooks. */
|
|
201104
201118
|
creationOptions;
|
|
201105
201119
|
/** The run's ordered stream: every flow's events, each naming its flow. Empty and
|
|
201106
201120
|
* unwritten unless `options.log` asked for it. */
|
|
201107
201121
|
engineLog = [];
|
|
201122
|
+
/** The next engine-log `seq`: its own counter, since the log's length restarts after a clear. */
|
|
201123
|
+
engineSeq = 0;
|
|
201108
201124
|
engineTraceHandlers = /* @__PURE__ */ new Set();
|
|
201109
201125
|
constructor(bundle, options = {}) {
|
|
201110
201126
|
this.creationOptions = options;
|
|
@@ -201150,12 +201166,12 @@ var Engine = class _Engine {
|
|
|
201150
201166
|
registry.mountOwned("patter", patterBag, { owner: OWNER });
|
|
201151
201167
|
registered.push("patter");
|
|
201152
201168
|
const hostBound = /* @__PURE__ */ new Set();
|
|
201153
|
-
|
|
201154
|
-
const
|
|
201155
|
-
const decls = (
|
|
201156
|
-
registry.defineForeign(
|
|
201157
|
-
registered.push(
|
|
201158
|
-
hostBound.add(
|
|
201169
|
+
for (const [token2, resolver] of Object.entries(boundScopes(options))) {
|
|
201170
|
+
const spec = bundle.scopeRegistry?.scopes.find((s) => s.token === token2);
|
|
201171
|
+
const decls = (spec?.declarations ?? []).map(toForeignDecl);
|
|
201172
|
+
registry.defineForeign(token2, resolver, decls, { writable: spec?.writable ?? true, owner: OWNER });
|
|
201173
|
+
registered.push(token2);
|
|
201174
|
+
hostBound.add(token2);
|
|
201159
201175
|
}
|
|
201160
201176
|
for (const spec of ownsRegistry ? bundle.scopeRegistry?.scopes ?? [] : []) {
|
|
201161
201177
|
if (hostBound.has(spec.token) || registry.has(spec.token)) continue;
|
|
@@ -201175,6 +201191,7 @@ var Engine = class _Engine {
|
|
|
201175
201191
|
}
|
|
201176
201192
|
this.host = {
|
|
201177
201193
|
logEnabled: options.log ?? false,
|
|
201194
|
+
tracing: options.log ?? false,
|
|
201178
201195
|
emitEngine: (flow, event, scene) => this.emitEngine(flow, event, scene),
|
|
201179
201196
|
bundle,
|
|
201180
201197
|
emitIds,
|
|
@@ -201197,11 +201214,13 @@ var Engine = class _Engine {
|
|
|
201197
201214
|
patterSharedNames,
|
|
201198
201215
|
sceneSharedNames,
|
|
201199
201216
|
sharedVisits: /* @__PURE__ */ new Map(),
|
|
201217
|
+
qualityLadders: /* @__PURE__ */ new Map(),
|
|
201200
201218
|
sharedSelectors: /* @__PURE__ */ new Map(),
|
|
201201
201219
|
stageBags: /* @__PURE__ */ new Map(),
|
|
201202
201220
|
journal: null,
|
|
201203
201221
|
customRng: options.rng,
|
|
201204
201222
|
onDryChoice: options.onDryChoice,
|
|
201223
|
+
onError: options.onError ?? ((e) => console.warn(`Patterplay: ${e.kind} on ${e.node} in flow '${e.flow}' failed, played through: ${e.message}`)),
|
|
201205
201224
|
replayPromptOnChoose: options.replayPromptOnChoose ?? false,
|
|
201206
201225
|
captionsOn: options.closedCaptions ?? true,
|
|
201207
201226
|
// captions shown by default (full text)
|
|
@@ -201222,6 +201241,11 @@ var Engine = class _Engine {
|
|
|
201222
201241
|
get isSourceDebug() {
|
|
201223
201242
|
return this.sourceDebug;
|
|
201224
201243
|
}
|
|
201244
|
+
/** The bundle's build identity (its content hash), the one a debug link handshakes with, or an empty
|
|
201245
|
+
* string for a bundle compiled without one. */
|
|
201246
|
+
get buildId() {
|
|
201247
|
+
return this.host.bundle.content?.hash ?? "";
|
|
201248
|
+
}
|
|
201225
201249
|
/**
|
|
201226
201250
|
* Switch the active locale LIVE - a real game's "language" setting can change mid-session. Subsequent
|
|
201227
201251
|
* string lookups (new beats, re-resolved character names, `{@ref}` interpolation) render in the new
|
|
@@ -201342,7 +201366,7 @@ var Engine = class _Engine {
|
|
|
201342
201366
|
});
|
|
201343
201367
|
}
|
|
201344
201368
|
this.flowsById.set(id, flow);
|
|
201345
|
-
flow.
|
|
201369
|
+
flow.begin(sceneId, blockId);
|
|
201346
201370
|
return flow;
|
|
201347
201371
|
}
|
|
201348
201372
|
/**
|
|
@@ -201395,10 +201419,10 @@ var Engine = class _Engine {
|
|
|
201395
201419
|
*/
|
|
201396
201420
|
resolveOpenAddress(scene, block) {
|
|
201397
201421
|
if (scene !== void 0) {
|
|
201398
|
-
const sceneId = this.
|
|
201399
|
-
if (
|
|
201422
|
+
const sceneId = resolveScene(this.host, scene);
|
|
201423
|
+
if (sceneId === void 0) throw new Error(`unknown scene: ${scene}`);
|
|
201400
201424
|
if (block === void 0) return { sceneId };
|
|
201401
|
-
const blockId =
|
|
201425
|
+
const blockId = resolveBlock(this.host, sceneId, block);
|
|
201402
201426
|
if (blockId === void 0) throw new Error(`unknown block: ${block}`);
|
|
201403
201427
|
return { sceneId, blockId };
|
|
201404
201428
|
}
|
|
@@ -201409,21 +201433,19 @@ var Engine = class _Engine {
|
|
|
201409
201433
|
if (Object.keys(this.host.bundle.scenes).length === 0) throw new Error("no scenes in bundle");
|
|
201410
201434
|
return {};
|
|
201411
201435
|
}
|
|
201412
|
-
/** Resolve a scene reference
|
|
201436
|
+
/** Resolve a scene reference to its internal id by the one address rule (see resolveScene); an
|
|
201437
|
+
* unknown one passes through, for the caller to find nothing. */
|
|
201413
201438
|
resolveSceneRef(ref) {
|
|
201414
201439
|
if (ref == null) return void 0;
|
|
201415
|
-
|
|
201416
|
-
return this.sceneGameIdToId.get(ref) ?? ref;
|
|
201440
|
+
return resolveScene(this.host, ref) ?? ref;
|
|
201417
201441
|
}
|
|
201418
|
-
/** Resolve a block reference
|
|
201442
|
+
/** Resolve a block reference to its internal id by the one address rule (see resolveBlock): within its
|
|
201443
|
+
* scene, so a block of another scene resolves to nothing. With no scene, only an internal id resolves,
|
|
201444
|
+
* since a block's gameId is only unique within its scene. */
|
|
201419
201445
|
resolveBlockRef(sceneId, ref) {
|
|
201420
201446
|
if (ref == null) return void 0;
|
|
201421
|
-
if (
|
|
201422
|
-
|
|
201423
|
-
const id = this.blockGameIdToId.get(sceneId)?.get(ref);
|
|
201424
|
-
if (id) return id;
|
|
201425
|
-
}
|
|
201426
|
-
return ref;
|
|
201447
|
+
if (sceneId == null) return ref;
|
|
201448
|
+
return resolveBlock(this.host, sceneId, ref);
|
|
201427
201449
|
}
|
|
201428
201450
|
/** The host-facing address (gameId) of a scene / block by internal id, or undefined if unknown.
|
|
201429
201451
|
* The inverse of the resolve helpers - for a host that wants to display / log the address. */
|
|
@@ -201708,7 +201730,7 @@ var Engine = class _Engine {
|
|
|
201708
201730
|
for (const s of this.host.stageBags.keys()) reg.remove(keys.stage(s), { keep });
|
|
201709
201731
|
this.host.stageBags.clear();
|
|
201710
201732
|
for (const t of ["patter", ...this.host.hostScopes]) if (reg.has(t)) reg.remove(t, { keep });
|
|
201711
|
-
|
|
201733
|
+
for (const t of Object.keys(boundScopes(this.creationOptions))) if (reg.has(t)) reg.remove(t);
|
|
201712
201734
|
}
|
|
201713
201735
|
/** Read a shared (`@patter` / foreign) property by ref. `@scene` refs are rejected (flow-level). */
|
|
201714
201736
|
getProperty(ref) {
|
|
@@ -201745,12 +201767,21 @@ var Engine = class _Engine {
|
|
|
201745
201767
|
* retained. Returns its own unsubscribe. */
|
|
201746
201768
|
onTrace(handler) {
|
|
201747
201769
|
this.engineTraceHandlers.add(handler);
|
|
201748
|
-
|
|
201770
|
+
this.host.tracing = true;
|
|
201771
|
+
return () => {
|
|
201772
|
+
this.engineTraceHandlers.delete(handler);
|
|
201773
|
+
this.host.tracing = this.host.logEnabled || this.engineTraceHandlers.size > 0;
|
|
201774
|
+
};
|
|
201749
201775
|
}
|
|
201750
201776
|
emitEngine(flow, event, scene) {
|
|
201751
201777
|
for (const h of this.engineTraceHandlers) h(flow, event);
|
|
201752
201778
|
if (!this.host.logEnabled) return;
|
|
201753
|
-
this.engineLog.push({ ...event, flow, seq: this.
|
|
201779
|
+
this.engineLog.push({ ...event, flow, seq: this.engineSeq++, ...scene ? { scene } : {} });
|
|
201780
|
+
}
|
|
201781
|
+
/** How many times each node has been entered across every flow, by node id: the shared count. With a
|
|
201782
|
+
* flow's own {@link Flow.getVisitCounts}, every visit count the run keeps, read without a save. */
|
|
201783
|
+
getVisitCounts() {
|
|
201784
|
+
return Object.fromEntries(this.host.sharedVisits);
|
|
201754
201785
|
}
|
|
201755
201786
|
listProperties() {
|
|
201756
201787
|
return this.host.patterSharedDecls.map((d) => ({
|
|
@@ -201816,6 +201847,8 @@ var Engine = class _Engine {
|
|
|
201816
201847
|
this.refuseInCheckpoint("loadGame");
|
|
201817
201848
|
const version2 = save.version;
|
|
201818
201849
|
if (version2 !== 2 && version2 !== SAVE_VERSION) throw new Error(`unsupported save version: ${String(version2)}`);
|
|
201850
|
+
const flows = save.flows;
|
|
201851
|
+
if (typeof flows !== "object" || flows === null || Array.isArray(flows)) throw new Error("malformed save: no flows");
|
|
201819
201852
|
this.assertExternalScopes();
|
|
201820
201853
|
const reg = this.host.registry;
|
|
201821
201854
|
for (const [id, flow] of this.flowsById) {
|
|
@@ -201954,19 +201987,21 @@ var Flow = class {
|
|
|
201954
201987
|
* lowercase, as the compiler emits references (the selfBackedResolver lesson). */
|
|
201955
201988
|
stagesFor(scope, name) {
|
|
201956
201989
|
const key2 = name.toLowerCase();
|
|
201957
|
-
const fromDecls = (decls) => decls?.find((d) => d.name.toLowerCase() === key2 && d.type === "quality")?.stages;
|
|
201958
201990
|
if (scope === "patter") {
|
|
201959
|
-
return
|
|
201991
|
+
return ladders(this.host, "patter", () => [...this.host.patterSharedDecls, ...this.host.patterLocalDecls]).get(key2);
|
|
201960
201992
|
}
|
|
201961
201993
|
if (scope === "scene") {
|
|
201962
|
-
const
|
|
201963
|
-
|
|
201994
|
+
const sceneId = this.currentSceneId;
|
|
201995
|
+
if (sceneId == null) return void 0;
|
|
201996
|
+
return ladders(this.host, `scene/${sceneId}`, () => this.host.bundle.scenes[sceneId]?.sceneProps).get(key2);
|
|
201964
201997
|
}
|
|
201965
|
-
return this.registryQualities?.(scope, name) ??
|
|
201998
|
+
return this.registryQualities?.(scope, name) ?? ladders(this.host, `host/${scope}`, () => this.host.bundle.scopeRegistry?.scopes.find((s) => s.token === scope)?.declarations).get(key2);
|
|
201966
201999
|
}
|
|
201967
202000
|
// -- Host API -------------------------------------------------------------
|
|
201968
|
-
/** Begin this flow at a scene (and optionally a specific block within it).
|
|
201969
|
-
|
|
202001
|
+
/** Begin this flow at a scene (and optionally a specific block within it). The engine's own entry point:
|
|
202002
|
+
* openFlow and a goto on an unstarted flow begin a flow here. A game calls {@link reset}.
|
|
202003
|
+
* @internal */
|
|
202004
|
+
begin(sceneId, blockId) {
|
|
201970
202005
|
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");
|
|
201971
202006
|
this.releaseBags(false);
|
|
201972
202007
|
this.host.registry.discardParked(keys.flow(this.id));
|
|
@@ -201978,7 +202013,7 @@ var Flow = class {
|
|
|
201978
202013
|
this.flowEnded = false;
|
|
201979
202014
|
this.activeSnippet = null;
|
|
201980
202015
|
this.beatIndex = 0;
|
|
201981
|
-
this.
|
|
202016
|
+
this.clearPending();
|
|
201982
202017
|
this.started = true;
|
|
201983
202018
|
if (blockId) {
|
|
201984
202019
|
const loc = this.host.blockIndex.get(blockId);
|
|
@@ -202003,10 +202038,15 @@ var Flow = class {
|
|
|
202003
202038
|
* Forget everything in this flow and begin again - its per-flow state (not-shared
|
|
202004
202039
|
* `@patter` globals + `@scene` props), cursor, callstack, selector cursors, and
|
|
202005
202040
|
* visit counts. Shared state (shared `@patter` / `@scene`, world visit counts) is
|
|
202006
|
-
* untouched.
|
|
202041
|
+
* untouched. The one public way to begin a flow again (openFlow begins a new one).
|
|
202007
202042
|
*/
|
|
202008
202043
|
reset(sceneId, blockId) {
|
|
202009
|
-
this.
|
|
202044
|
+
this.begin(sceneId, blockId);
|
|
202045
|
+
}
|
|
202046
|
+
/** @deprecated Use {@link reset}, the one public name for beginning a flow again on every runtime. `start`
|
|
202047
|
+
* was the same call under a second name, and goes in a later release. */
|
|
202048
|
+
start(sceneId, blockId) {
|
|
202049
|
+
this.reset(sceneId, blockId);
|
|
202010
202050
|
}
|
|
202011
202051
|
/**
|
|
202012
202052
|
* Send this flow's cursor to an ADDRESS, exactly as an authored `go` jump would: the target scene's
|
|
@@ -202030,29 +202070,25 @@ var Flow = class {
|
|
|
202030
202070
|
this.touch();
|
|
202031
202071
|
if (scene === "END") {
|
|
202032
202072
|
this.started = true;
|
|
202033
|
-
this.
|
|
202034
|
-
this.pendingPrompt = null;
|
|
202035
|
-
this.pendingPromptOwnerId = null;
|
|
202073
|
+
this.clearPending();
|
|
202036
202074
|
this.activeSnippet = null;
|
|
202037
202075
|
this.beatIndex = 0;
|
|
202038
202076
|
this.flowEnded = true;
|
|
202039
202077
|
this.stack = [];
|
|
202040
202078
|
return true;
|
|
202041
202079
|
}
|
|
202042
|
-
const sceneId =
|
|
202080
|
+
const sceneId = resolveScene(this.host, scene);
|
|
202043
202081
|
if (sceneId === void 0) return false;
|
|
202044
202082
|
let blockId;
|
|
202045
202083
|
if (block !== void 0) {
|
|
202046
|
-
blockId =
|
|
202084
|
+
blockId = resolveBlock(this.host, sceneId, block);
|
|
202047
202085
|
if (blockId === void 0) return false;
|
|
202048
202086
|
}
|
|
202049
202087
|
if (!this.started) {
|
|
202050
|
-
this.
|
|
202088
|
+
this.begin(sceneId, blockId);
|
|
202051
202089
|
return true;
|
|
202052
202090
|
}
|
|
202053
|
-
this.
|
|
202054
|
-
this.pendingPrompt = null;
|
|
202055
|
-
this.pendingPromptOwnerId = null;
|
|
202091
|
+
this.clearPending();
|
|
202056
202092
|
this.activeSnippet = null;
|
|
202057
202093
|
this.beatIndex = 0;
|
|
202058
202094
|
this.flowEnded = false;
|
|
@@ -202077,6 +202113,15 @@ var Flow = class {
|
|
|
202077
202113
|
this.stack = [];
|
|
202078
202114
|
this.activeSnippet = null;
|
|
202079
202115
|
this.beatIndex = 0;
|
|
202116
|
+
this.clearPending();
|
|
202117
|
+
}
|
|
202118
|
+
/**
|
|
202119
|
+
* Drop everything waiting to be delivered: an open choice, and a chosen option's prompt still to be
|
|
202120
|
+
* replayed. Every move that abandons the flow's place (start and reset, goto, close) does this, so none
|
|
202121
|
+
* leaves a stale prompt behind. start() cleared only the choice, so a reset between choose() and the
|
|
202122
|
+
* next advance() replayed the abandoned run's prompt as the new run's first beat.
|
|
202123
|
+
*/
|
|
202124
|
+
clearPending() {
|
|
202080
202125
|
this.pendingChoice = null;
|
|
202081
202126
|
this.pendingPrompt = null;
|
|
202082
202127
|
this.pendingPromptOwnerId = null;
|
|
@@ -202153,7 +202198,7 @@ var Flow = class {
|
|
|
202153
202198
|
if (this.flowEnded || this.pendingChoice) return;
|
|
202154
202199
|
if (this.activeSnippet) {
|
|
202155
202200
|
if (this.beatIndex < (this.activeSnippet.beats?.length ?? 0)) return;
|
|
202156
|
-
this.runEffects(this.activeSnippet.onExit);
|
|
202201
|
+
this.runEffects(this.activeSnippet.onExit, this.activeSnippet.id);
|
|
202157
202202
|
const jump2 = this.activeSnippet.jump;
|
|
202158
202203
|
this.activeSnippet = null;
|
|
202159
202204
|
this.beatIndex = 0;
|
|
@@ -202173,7 +202218,7 @@ var Flow = class {
|
|
|
202173
202218
|
}
|
|
202174
202219
|
const from = frame.index;
|
|
202175
202220
|
while (frame.index < children.length && !this.eligible(children[frame.index])) frame.index++;
|
|
202176
|
-
if (this.host.
|
|
202221
|
+
if (this.host.tracing && frame.index !== from) {
|
|
202177
202222
|
this.emit({
|
|
202178
202223
|
type: "select",
|
|
202179
202224
|
group: frame.containerId,
|
|
@@ -202209,14 +202254,18 @@ var Flow = class {
|
|
|
202209
202254
|
clearLog() {
|
|
202210
202255
|
this.flowLog.length = 0;
|
|
202211
202256
|
}
|
|
202212
|
-
/** Record one decision, on this flow's log and the engine's
|
|
202213
|
-
*
|
|
202257
|
+
/** Record one decision, on this flow's log and the engine's, and hand it to any `onTrace`. Each call
|
|
202258
|
+
* site checks `host.tracing` first, so with nothing logging or tracing the entry is never built. */
|
|
202214
202259
|
emit(event) {
|
|
202215
202260
|
const scene = this.currentSceneId ?? void 0;
|
|
202216
202261
|
this.host.emitEngine(this.id, event, scene);
|
|
202217
202262
|
if (!this.host.logEnabled) return;
|
|
202218
202263
|
this.flowLog.push({ ...event, seq: this.flowSeq++, ...scene ? { scene } : {} });
|
|
202219
202264
|
}
|
|
202265
|
+
/** How many times this flow has entered each node, by node id. */
|
|
202266
|
+
getVisitCounts() {
|
|
202267
|
+
return Object.fromEntries(this.visitCounts);
|
|
202268
|
+
}
|
|
202220
202269
|
/** The options of a pending choice (empty when not at a choice point). */
|
|
202221
202270
|
getChoices() {
|
|
202222
202271
|
return this.pendingChoice?.options ?? [];
|
|
@@ -202230,7 +202279,7 @@ var Flow = class {
|
|
|
202230
202279
|
if (!option.eligible) throw new Error(`choice option is not eligible: ${id}`);
|
|
202231
202280
|
this.touch();
|
|
202232
202281
|
const node = choice.byId.get(id);
|
|
202233
|
-
this.emit({ type: "chose", group: choice.groupId, option: id });
|
|
202282
|
+
if (this.host.tracing) this.emit({ type: "chose", group: choice.groupId, option: id });
|
|
202234
202283
|
this.pendingChoice = null;
|
|
202235
202284
|
const authored = this.host.replayPromptOnChoose ? this.authoredPromptOf(node) : void 0;
|
|
202236
202285
|
this.pendingPrompt = authored && option.prompt ? { beat: authored, shown: { ...option.prompt } } : null;
|
|
@@ -202369,7 +202418,7 @@ var Flow = class {
|
|
|
202369
202418
|
this.currentSceneId = sceneId;
|
|
202370
202419
|
this.enter(sceneId);
|
|
202371
202420
|
this.seedScene(scene);
|
|
202372
|
-
this.runEffects(scene.onEntry);
|
|
202421
|
+
this.runEffects(scene.onEntry, scene.id);
|
|
202373
202422
|
}
|
|
202374
202423
|
/**
|
|
202375
202424
|
* Play one child of the active run. A snippet begins delivering. A group is
|
|
@@ -202405,7 +202454,7 @@ var Flow = class {
|
|
|
202405
202454
|
return void 0;
|
|
202406
202455
|
}
|
|
202407
202456
|
beginSnippet(snippet) {
|
|
202408
|
-
this.runEffects(snippet.onEnter);
|
|
202457
|
+
this.runEffects(snippet.onEnter, snippet.id);
|
|
202409
202458
|
this.activeSnippet = snippet;
|
|
202410
202459
|
this.beatIndex = 0;
|
|
202411
202460
|
}
|
|
@@ -202425,8 +202474,8 @@ var Flow = class {
|
|
|
202425
202474
|
options.push({ id: child2.id, prompt: this.promptFor(child2), eligible, gameData: child2.gameData });
|
|
202426
202475
|
byId.set(child2.id, child2);
|
|
202427
202476
|
}
|
|
202428
|
-
if (options.
|
|
202429
|
-
this.emit({ type: "choice", group: group.id, options: options.map((o) => ({ id: o.id, eligible: o.eligible })) });
|
|
202477
|
+
if (options.some((o) => o.eligible)) {
|
|
202478
|
+
if (this.host.tracing) this.emit({ type: "choice", group: group.id, options: options.map((o) => ({ id: o.id, eligible: o.eligible })) });
|
|
202430
202479
|
this.pendingChoice = { groupId: group.id, options, byId };
|
|
202431
202480
|
return;
|
|
202432
202481
|
}
|
|
@@ -202435,7 +202484,7 @@ var Flow = class {
|
|
|
202435
202484
|
this.enterChild(fallback2);
|
|
202436
202485
|
return;
|
|
202437
202486
|
}
|
|
202438
|
-
this.emit({ type: "dry", group: group.id });
|
|
202487
|
+
if (this.host.tracing) this.emit({ type: "dry", group: group.id });
|
|
202439
202488
|
this.host.onDryChoice?.(group.id);
|
|
202440
202489
|
}
|
|
202441
202490
|
// -- Jumps (jump / call-return) ----------------------------------------
|
|
@@ -202450,7 +202499,7 @@ var Flow = class {
|
|
|
202450
202499
|
* hard-ends the flow regardless of the callstack.
|
|
202451
202500
|
*/
|
|
202452
202501
|
enterTarget(to, mode) {
|
|
202453
|
-
this.emit({ type: "jump", to, mode });
|
|
202502
|
+
if (this.host.tracing) this.emit({ type: "jump", to, mode });
|
|
202454
202503
|
if (to === "END") {
|
|
202455
202504
|
this.flowEnded = true;
|
|
202456
202505
|
this.stack = [];
|
|
@@ -202483,11 +202532,11 @@ var Flow = class {
|
|
|
202483
202532
|
// -- Selectors ------------------------------------------------------------
|
|
202484
202533
|
selectChild(group) {
|
|
202485
202534
|
const verdicts = group.children.map((c2) => ({ id: c2.id, eligible: this.eligible(c2) }));
|
|
202486
|
-
const eligible = group.children.filter((
|
|
202535
|
+
const eligible = group.children.filter((_2, i) => verdicts[i].eligible);
|
|
202487
202536
|
const order = group.options?.order ?? "sequential";
|
|
202488
202537
|
const exhaust = group.options?.exhaust ?? "once";
|
|
202489
202538
|
const trace = (picked) => {
|
|
202490
|
-
this.emit({
|
|
202539
|
+
if (this.host.tracing) this.emit({
|
|
202491
202540
|
type: "select",
|
|
202492
202541
|
group: group.id,
|
|
202493
202542
|
selector: group.selector ?? "default",
|
|
@@ -202530,8 +202579,9 @@ var Flow = class {
|
|
|
202530
202579
|
const len = eligible.length;
|
|
202531
202580
|
const stick = exhaust === "stick";
|
|
202532
202581
|
const fill = () => (stick ? eligible.slice(0, len - 1) : eligible).map((c2) => c2.id);
|
|
202582
|
+
const eligibleIds = new Set(eligible.map((c2) => c2.id));
|
|
202533
202583
|
if (st.bag === void 0) st.bag = fill();
|
|
202534
|
-
if (st.bag.
|
|
202584
|
+
if (!st.bag.some((id2) => eligibleIds.has(id2))) {
|
|
202535
202585
|
if (exhaust === "once") return null;
|
|
202536
202586
|
if (stick) {
|
|
202537
202587
|
const last2 = eligible[len - 1];
|
|
@@ -202539,13 +202589,14 @@ var Flow = class {
|
|
|
202539
202589
|
return last2;
|
|
202540
202590
|
}
|
|
202541
202591
|
st.bag = fill();
|
|
202592
|
+
if (st.bag.length === 0) return null;
|
|
202542
202593
|
}
|
|
202543
|
-
const pool = st.bag;
|
|
202594
|
+
const pool = st.bag.filter((id2) => eligibleIds.has(id2));
|
|
202544
202595
|
const p = st.last !== void 0 && pool.length > 1 ? pool.indexOf(st.last) : -1;
|
|
202545
202596
|
let i = Math.floor(this.rng() * (p >= 0 ? pool.length - 1 : pool.length));
|
|
202546
202597
|
if (p >= 0 && i >= p) i++;
|
|
202547
202598
|
const id = pool[i];
|
|
202548
|
-
|
|
202599
|
+
st.bag.splice(st.bag.indexOf(id), 1);
|
|
202549
202600
|
st.last = id;
|
|
202550
202601
|
return eligible.find((c2) => c2.id === id);
|
|
202551
202602
|
}
|
|
@@ -202594,7 +202645,7 @@ var Flow = class {
|
|
|
202594
202645
|
/** A child's Best-match score against the current state: 0 when it has no condition (the filler
|
|
202595
202646
|
* tier), else the specificity of its (already-passing) condition. */
|
|
202596
202647
|
specScore(node) {
|
|
202597
|
-
return node.condition ? this.matchedSpec(this.conditionAst(node.condition), true) : 0;
|
|
202648
|
+
return node.condition ? this.matchedSpec(this.conditionAst(node.condition), true, node) : 0;
|
|
202598
202649
|
}
|
|
202599
202650
|
/**
|
|
202600
202651
|
* The **matched-specificity** metric (parity contract): how many atomic constraints are actively
|
|
@@ -202604,8 +202655,15 @@ var Flow = class {
|
|
|
202604
202655
|
* to hold?" (true at the root). Only `and`/`or`/`not`/`check_flags` are structural; every other
|
|
202605
202656
|
* node (comparisons, scoped vars, literals, other calls) is an atom, evaluated whole.
|
|
202606
202657
|
*/
|
|
202607
|
-
matchedSpec(node, want) {
|
|
202608
|
-
const evalTruthy = (n) =>
|
|
202658
|
+
matchedSpec(node, want, owner) {
|
|
202659
|
+
const evalTruthy = (n) => {
|
|
202660
|
+
try {
|
|
202661
|
+
return truthy(evaluate(n, this.context(), patterDialect));
|
|
202662
|
+
} catch (err) {
|
|
202663
|
+
this.reportError("best-match", owner.id, owner.condition, err);
|
|
202664
|
+
return false;
|
|
202665
|
+
}
|
|
202666
|
+
};
|
|
202609
202667
|
return matchedSpecificity(node, evalTruthy, { want });
|
|
202610
202668
|
}
|
|
202611
202669
|
/** A selector's cursor state - shared across flows (`group.shared`) or this flow's own. */
|
|
@@ -202637,17 +202695,36 @@ var Flow = class {
|
|
|
202637
202695
|
return st;
|
|
202638
202696
|
}
|
|
202639
202697
|
// -- Effects + expressions ------------------------------------------------
|
|
202640
|
-
|
|
202698
|
+
/** Run an effect list. `owner` is the snippet or scene it belongs to, for an error report. */
|
|
202699
|
+
runEffects(effects, owner) {
|
|
202641
202700
|
for (const e of effects ?? []) {
|
|
202642
|
-
|
|
202643
|
-
|
|
202644
|
-
|
|
202645
|
-
|
|
202701
|
+
let value;
|
|
202702
|
+
try {
|
|
202703
|
+
value = this.evalExpr(e.value);
|
|
202704
|
+
const prev = this.host.tracing ? this.getProperty(e.target) : void 0;
|
|
202705
|
+
this.writeProperty(e.target, value, false);
|
|
202706
|
+
if (this.host.tracing) this.emit({ type: "write", target: e.target, value, ...prev !== void 0 ? { prev } : {} });
|
|
202707
|
+
} catch (err) {
|
|
202708
|
+
this.reportError("effect", owner, e.value, err);
|
|
202709
|
+
}
|
|
202646
202710
|
}
|
|
202647
202711
|
}
|
|
202712
|
+
/** Whether a node's condition holds. A condition that fails to evaluate counts as false (see PlayError). */
|
|
202648
202713
|
eligible(node) {
|
|
202649
202714
|
if (!node.condition) return true;
|
|
202650
|
-
|
|
202715
|
+
try {
|
|
202716
|
+
return truthy(this.evalExpr(node.condition));
|
|
202717
|
+
} catch (err) {
|
|
202718
|
+
this.reportError("condition", node.id, node.condition, err);
|
|
202719
|
+
return false;
|
|
202720
|
+
}
|
|
202721
|
+
}
|
|
202722
|
+
/** Report a content error the engine is playing through: to the host's onError, and to the log. */
|
|
202723
|
+
reportError(kind, node, expr, err) {
|
|
202724
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
202725
|
+
const source2 = expr?.src;
|
|
202726
|
+
this.host.onError({ flow: this.id, kind, node, ...source2 ? { source: source2 } : {}, message });
|
|
202727
|
+
if (this.host.tracing) this.emit({ type: "diagnostic", kind, node, ...source2 ? { source: source2 } : {}, message });
|
|
202651
202728
|
}
|
|
202652
202729
|
evalExpr(expr) {
|
|
202653
202730
|
return evaluate(this.conditionAst(expr), this.context(), patterDialect);
|
|
@@ -202664,15 +202741,15 @@ var Flow = class {
|
|
|
202664
202741
|
}
|
|
202665
202742
|
/** Record an entry of a node (entered-only; spec §7): bumps the flow + world counts. */
|
|
202666
202743
|
enter(id) {
|
|
202667
|
-
const
|
|
202668
|
-
const ownBefore =
|
|
202669
|
-
|
|
202744
|
+
const own2 = this.visitCounts;
|
|
202745
|
+
const ownBefore = own2.get(id);
|
|
202746
|
+
own2.set(id, (ownBefore ?? 0) + 1);
|
|
202670
202747
|
const shared = this.host.sharedVisits;
|
|
202671
202748
|
const before = shared.get(id);
|
|
202672
202749
|
shared.set(id, (before ?? 0) + 1);
|
|
202673
202750
|
if (this.host.journal) this.host.journal.undo.push(() => {
|
|
202674
|
-
if (ownBefore === void 0)
|
|
202675
|
-
else
|
|
202751
|
+
if (ownBefore === void 0) own2.delete(id);
|
|
202752
|
+
else own2.set(id, ownBefore);
|
|
202676
202753
|
if (before === void 0) shared.delete(id);
|
|
202677
202754
|
else shared.set(id, before);
|
|
202678
202755
|
});
|
|
@@ -202843,23 +202920,23 @@ var Flow = class {
|
|
|
202843
202920
|
if (!this.sceneBags.has(s)) {
|
|
202844
202921
|
const bag = new PropertyBag(props.filter((d) => !shared.has(d.name.toLowerCase())), { pathPrefix: "@scene." });
|
|
202845
202922
|
const key2 = keys.flowScene(this.id, s);
|
|
202846
|
-
this.host.registry
|
|
202923
|
+
const claimed = mountClaims(this.host.registry, key2, bag, journal !== null);
|
|
202847
202924
|
this.registered.add(key2);
|
|
202848
202925
|
this.sceneBags.set(s, bag);
|
|
202849
202926
|
if (journal) journal.undo.push(() => {
|
|
202850
202927
|
if (this.sceneBags.get(s) !== bag) return;
|
|
202851
|
-
this.host.registry.remove(key2);
|
|
202928
|
+
this.host.registry.remove(key2, claimed ? { keep: true } : void 0);
|
|
202852
202929
|
this.registered.delete(key2);
|
|
202853
202930
|
this.sceneBags.delete(s);
|
|
202854
202931
|
});
|
|
202855
202932
|
}
|
|
202856
202933
|
if (!this.host.stageBags.has(s)) {
|
|
202857
202934
|
const bag = new PropertyBag(props.filter((d) => shared.has(d.name.toLowerCase())), { pathPrefix: "@scene." });
|
|
202858
|
-
this.host.registry
|
|
202935
|
+
const claimed = mountClaims(this.host.registry, keys.stage(s), bag, journal !== null);
|
|
202859
202936
|
this.host.stageBags.set(s, bag);
|
|
202860
202937
|
if (journal) journal.undo.push(() => {
|
|
202861
202938
|
if (this.host.stageBags.get(s) !== bag) return;
|
|
202862
|
-
this.host.registry.remove(keys.stage(s));
|
|
202939
|
+
this.host.registry.remove(keys.stage(s), claimed ? { keep: true } : void 0);
|
|
202863
202940
|
this.host.stageBags.delete(s);
|
|
202864
202941
|
});
|
|
202865
202942
|
}
|
|
@@ -202926,9 +203003,29 @@ function deserialiseSelectors(rec) {
|
|
|
202926
203003
|
}
|
|
202927
203004
|
return map2;
|
|
202928
203005
|
}
|
|
203006
|
+
function mountClaims(registry, key2, bag, ask) {
|
|
203007
|
+
const before = ask ? JSON.stringify(bag.save()) : "";
|
|
203008
|
+
registry.mountOwned(key2, bag, { owner: OWNER });
|
|
203009
|
+
return ask && JSON.stringify(bag.save()) !== before;
|
|
203010
|
+
}
|
|
202929
203011
|
function toDecl(decl) {
|
|
202930
203012
|
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default };
|
|
202931
203013
|
}
|
|
203014
|
+
function boundScopes(options) {
|
|
203015
|
+
return { ...options.world ? { world: options.world } : {}, ...options.hostScopes };
|
|
203016
|
+
}
|
|
203017
|
+
function ladders(host2, cacheKey, decls) {
|
|
203018
|
+
let found = host2.qualityLadders.get(cacheKey);
|
|
203019
|
+
if (!found) {
|
|
203020
|
+
found = /* @__PURE__ */ new Map();
|
|
203021
|
+
for (const d of decls() ?? []) {
|
|
203022
|
+
const k = d.name.toLowerCase();
|
|
203023
|
+
if (d.type === "quality" && d.stages && !found.has(k)) found.set(k, d.stages);
|
|
203024
|
+
}
|
|
203025
|
+
host2.qualityLadders.set(cacheKey, found);
|
|
203026
|
+
}
|
|
203027
|
+
return found;
|
|
203028
|
+
}
|
|
202932
203029
|
function toForeignDecl(decl) {
|
|
202933
203030
|
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default, writable: decl.writable };
|
|
202934
203031
|
}
|
|
@@ -202937,6 +203034,12 @@ function selfBackedDecl(decl, scopeWritable) {
|
|
|
202937
203034
|
const writable = decl.writable ?? scopeWritable;
|
|
202938
203035
|
return writable === void 0 ? d : { ...d, writable };
|
|
202939
203036
|
}
|
|
203037
|
+
function resolveScene(host2, ref) {
|
|
203038
|
+
return host2.sceneGameIdToId.get(ref) ?? (host2.bundle.scenes[ref] ? ref : void 0);
|
|
203039
|
+
}
|
|
203040
|
+
function resolveBlock(host2, sceneId, ref) {
|
|
203041
|
+
return host2.blockGameIdToId.get(sceneId)?.get(ref) ?? (host2.blockIndex.get(ref)?.sceneId === sceneId ? ref : void 0);
|
|
203042
|
+
}
|
|
202940
203043
|
function splitHostRef(host2, ref) {
|
|
202941
203044
|
if (host2.refSplitRevision !== host2.registry.revision) {
|
|
202942
203045
|
host2.refSplitCache.clear();
|
|
@@ -203019,7 +203122,7 @@ function runPlay(loaded, opts = {}) {
|
|
|
203019
203122
|
const bundle = exportBundle({ project: loaded.project, scenes: loaded.scenes, locales: loaded.locales, gameScopes: loaded.gameScopes?.merged });
|
|
203020
203123
|
const events = [];
|
|
203021
203124
|
const registry = previewRegistry(loaded.gameScopes, bundle);
|
|
203022
|
-
const engine = new Engine(bundle, { seed: opts.seed, ...registry ? { registry } : {} });
|
|
203125
|
+
const engine = new Engine(bundle, { seed: opts.seed, onError: (error) => events.push({ type: "error", error }), ...registry ? { registry } : {} });
|
|
203023
203126
|
const start = resolveStart(loaded, opts);
|
|
203024
203127
|
const flow = engine.openFlow("main", { scene: start.scene, block: start.block });
|
|
203025
203128
|
const scripted = [...opts.choices ?? []];
|
|
@@ -203051,6 +203154,11 @@ function renderPlay(result) {
|
|
|
203051
203154
|
case "gameEvent":
|
|
203052
203155
|
out.push(` (game event ${JSON.stringify(e.gameData ?? {})})`);
|
|
203053
203156
|
break;
|
|
203157
|
+
case "error": {
|
|
203158
|
+
const { kind, node, source: source2, message } = e.error;
|
|
203159
|
+
out.push(` ! ${kind} on '${node}'${source2 ? ` (${source2})` : ""} failed, played through: ${message}`);
|
|
203160
|
+
break;
|
|
203161
|
+
}
|
|
203054
203162
|
case "choice":
|
|
203055
203163
|
for (const o of e.options) out.push(` ${o.eligible ? "[ ]" : "[x]"} ${o.prompt?.text ?? "(no label)"} (${o.id})`);
|
|
203056
203164
|
if (e.picked !== void 0) out.push(` > ${e.picked}`);
|
|
@@ -203271,9 +203379,12 @@ function* sweep(loaded, options = {}, hooks = {}) {
|
|
|
203271
203379
|
const order = [];
|
|
203272
203380
|
const meta = /* @__PURE__ */ new Map();
|
|
203273
203381
|
const choiceScene = /* @__PURE__ */ new Map();
|
|
203382
|
+
const nodeScene = /* @__PURE__ */ new Map();
|
|
203274
203383
|
for (const scene of loaded.scenes) {
|
|
203384
|
+
nodeScene.set(scene.id, scene.id);
|
|
203275
203385
|
for (const block of scene.blocks) {
|
|
203276
203386
|
walkNodes(block.children, (node) => {
|
|
203387
|
+
nodeScene.set(node.id, scene.id);
|
|
203277
203388
|
if (node.type === "group") {
|
|
203278
203389
|
if (node.selector === "choice") choiceScene.set(node.id, scene.id);
|
|
203279
203390
|
return;
|
|
@@ -203294,6 +203405,7 @@ function* sweep(loaded, options = {}, hooks = {}) {
|
|
|
203294
203405
|
const hitCount = new Map(order.map((id) => [id, 0]));
|
|
203295
203406
|
const reachedRuns = new Map(order.map((id) => [id, 0]));
|
|
203296
203407
|
const dryRuns = /* @__PURE__ */ new Map();
|
|
203408
|
+
const errorRuns = /* @__PURE__ */ new Map();
|
|
203297
203409
|
const termination = { ended: 0, capped: 0, stalled: 0, evalError: 0 };
|
|
203298
203410
|
const bundle = exportBundle({ project: loaded.project, scenes: loaded.scenes, locales: loaded.locales, gameScopes: loaded.gameScopes?.merged });
|
|
203299
203411
|
const rng2 = mulberry32(seed);
|
|
@@ -203314,10 +203426,15 @@ function* sweep(loaded, options = {}, hooks = {}) {
|
|
|
203314
203426
|
break;
|
|
203315
203427
|
}
|
|
203316
203428
|
const dryThisRun = /* @__PURE__ */ new Set();
|
|
203429
|
+
const errorsThisRun = /* @__PURE__ */ new Map();
|
|
203317
203430
|
const registry = previewRegistry(loaded.gameScopes, bundle);
|
|
203318
203431
|
const engine = new Engine(bundle, {
|
|
203319
203432
|
seed: Math.floor(rng2() * 4294967296),
|
|
203320
203433
|
onDryChoice: (groupId) => dryThisRun.add(groupId),
|
|
203434
|
+
onError: (e) => {
|
|
203435
|
+
const key2 = `${e.kind}|${e.node}|${e.message}`;
|
|
203436
|
+
if (!errorsThisRun.has(key2)) errorsThisRun.set(key2, { kind: e.kind, node: e.node, ...e.source ? { source: e.source } : {}, message: e.message });
|
|
203437
|
+
},
|
|
203321
203438
|
...registry ? { registry } : {}
|
|
203322
203439
|
});
|
|
203323
203440
|
for (const d of initialDrivers) engine.setProperty(d.ref, pick(d.values));
|
|
@@ -203353,6 +203470,11 @@ function* sweep(loaded, options = {}, hooks = {}) {
|
|
|
203353
203470
|
}
|
|
203354
203471
|
for (const id of seenThisRun) reachedRuns.set(id, reachedRuns.get(id) + 1);
|
|
203355
203472
|
for (const id of dryThisRun) dryRuns.set(id, (dryRuns.get(id) ?? 0) + 1);
|
|
203473
|
+
for (const [key2, e] of errorsThisRun) {
|
|
203474
|
+
const seen = errorRuns.get(key2);
|
|
203475
|
+
if (seen) seen.runs++;
|
|
203476
|
+
else errorRuns.set(key2, { ...e, scene: nodeScene.get(e.node) ?? "", runs: 1 });
|
|
203477
|
+
}
|
|
203356
203478
|
termination[term]++;
|
|
203357
203479
|
executed++;
|
|
203358
203480
|
if ((run2 & 255) === 0) hooks.onProgress?.(executed, runs);
|
|
@@ -203405,6 +203527,7 @@ function* sweep(loaded, options = {}, hooks = {}) {
|
|
|
203405
203527
|
drivers,
|
|
203406
203528
|
unwrittenInputs: [...unwrittenInputs].sort(),
|
|
203407
203529
|
dryChoices,
|
|
203530
|
+
contentErrors: [...errorRuns.values()].sort((a, b) => b.runs - a.runs || a.node.localeCompare(b.node)),
|
|
203408
203531
|
cancelled
|
|
203409
203532
|
};
|
|
203410
203533
|
}
|
|
@@ -203426,7 +203549,7 @@ function renderCoverageText(report, sceneName = (id) => id, opts = {}) {
|
|
|
203426
203549
|
);
|
|
203427
203550
|
out.push(`${report.runs} run(s) - ${report.maxSteps} max steps - seed ${report.seed}${report.cancelled ? " - CANCELLED" : ""}`);
|
|
203428
203551
|
const term = report.termination;
|
|
203429
|
-
out.push(`runs ended: ${term.ended} reached the end, ${term.stalled} stalled at a choice with nothing to pick, ${term.capped} hit the step limit, ${term.evalError} errored`);
|
|
203552
|
+
out.push(`runs ended: ${term.ended} reached the end, ${term.stalled ? `${term.stalled} stalled at a choice with nothing to pick, ` : ""}${term.capped} hit the step limit, ${term.evalError} errored`);
|
|
203430
203553
|
if (report.drivers.length) out.push(`input drivers: ${report.drivers.map((d) => d.ref).join(", ")}`);
|
|
203431
203554
|
if (report.unwrittenInputs.length) {
|
|
203432
203555
|
out.push(`? = gated on an input nothing writes/drives: ${report.unwrittenInputs.join(", ")} (add a coverage driver?)`);
|
|
@@ -203438,6 +203561,13 @@ function renderCoverageText(report, sceneName = (id) => id, opts = {}) {
|
|
|
203438
203561
|
out.push(` \u203C ${String(d.runs).padStart(6)} run(s) ${sceneName(d.scene)} choice '${d.id}'`);
|
|
203439
203562
|
}
|
|
203440
203563
|
}
|
|
203564
|
+
if (report.contentErrors.length) {
|
|
203565
|
+
out.push("");
|
|
203566
|
+
out.push(`content errors (a condition or effect failed and play went on without it - fix the expression): ${report.contentErrors.length}`);
|
|
203567
|
+
for (const e of report.contentErrors) {
|
|
203568
|
+
out.push(` \u203C ${String(e.runs).padStart(6)} run(s) ${sceneName(e.scene)} ${e.kind} on '${e.node}'${e.source ? ` (${e.source})` : ""}: ${e.message}`);
|
|
203569
|
+
}
|
|
203570
|
+
}
|
|
203441
203571
|
if (!report.beats.length) return out;
|
|
203442
203572
|
const clip = (s, n = 48) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
|
|
203443
203573
|
const scenes = new Set(report.beats.map((b) => b.scene));
|
|
@@ -203883,10 +204013,10 @@ function resolveDocumentation(loaded, channel) {
|
|
|
203883
204013
|
const classes = loaded.project.documentationClasses ?? DEFAULT_DOCUMENTATION_CLASSES;
|
|
203884
204014
|
const allowed = classesForChannel(classes, channel);
|
|
203885
204015
|
const docsOf = mergeAuthoring2(loaded).documentation;
|
|
203886
|
-
const
|
|
204016
|
+
const own2 = (id) => (docsOf.get(id) ?? []).filter((l2) => l2.type !== void 0 && allowed.has(l2.type));
|
|
203887
204017
|
const out = /* @__PURE__ */ new Map();
|
|
203888
204018
|
const visit = (id, inherited) => {
|
|
203889
|
-
const mine =
|
|
204019
|
+
const mine = own2(id);
|
|
203890
204020
|
if (inherited.length === 0 && mine.length === 0) return inherited;
|
|
203891
204021
|
const resolved = [...inherited, ...mine];
|
|
203892
204022
|
out.set(id, resolved);
|
|
@@ -204282,14 +204412,14 @@ function runVoiceScript(loaded, opts = {}) {
|
|
|
204282
204412
|
if (beat.kind !== "line" || cutSet.has(beat.id)) continue;
|
|
204283
204413
|
const ws = writingOf.get(beat.id) ?? stub;
|
|
204284
204414
|
if (!everything && recordThreshold !== -1 && (writingIndex.get(ws) ?? 0) < recordThreshold) continue;
|
|
204285
|
-
const
|
|
204415
|
+
const own2 = ownVo(beat.id);
|
|
204286
204416
|
lines.push({
|
|
204287
204417
|
scope: scope.join(" \u203A "),
|
|
204288
204418
|
id: beat.id,
|
|
204289
204419
|
character: beat.character ?? "",
|
|
204290
204420
|
actor: beat.character ? actorOf.get(beat.character) : void 0,
|
|
204291
204421
|
text: plainVoice(source2[beat.id] ?? ""),
|
|
204292
|
-
comments: leading ? [...ancestorVo, ...
|
|
204422
|
+
comments: leading ? [...ancestorVo, ...own2] : own2,
|
|
204293
204423
|
// first line of the run gets the enclosing context
|
|
204294
204424
|
recordingStatus: recordingOf(beat.id)
|
|
204295
204425
|
});
|
|
@@ -248910,7 +249040,7 @@ function fileStatus(filePaths, options = {}) {
|
|
|
248910
249040
|
// package.json
|
|
248911
249041
|
var package_default = {
|
|
248912
249042
|
name: "@patterkit/cli",
|
|
248913
|
-
version: "0.
|
|
249043
|
+
version: "0.8.1",
|
|
248914
249044
|
description: "The `patter` CLI: validate, format, export, and play a Patter project.",
|
|
248915
249045
|
type: "module",
|
|
248916
249046
|
license: "MIT",
|
|
@@ -248940,7 +249070,7 @@ var package_default = {
|
|
|
248940
249070
|
},
|
|
248941
249071
|
dependencies: {
|
|
248942
249072
|
"@patterkit/core": "0.3.2",
|
|
248943
|
-
"@patterkit/ops": "0.
|
|
249073
|
+
"@patterkit/ops": "0.13.1",
|
|
248944
249074
|
"@wildwinter/simple-vc-lib": "^0.5.0"
|
|
248945
249075
|
}
|
|
248946
249076
|
};
|
|
@@ -249276,7 +249406,7 @@ ${count} issue(s)${advisory}`);
|
|
|
249276
249406
|
seed
|
|
249277
249407
|
});
|
|
249278
249408
|
for (const line2 of renderPlay(result)) console.log(line2);
|
|
249279
|
-
return result.outcome === "end" ? 0 : 1;
|
|
249409
|
+
return result.outcome === "end" && !result.events.some((e) => e.type === "error") ? 0 : 1;
|
|
249280
249410
|
}
|
|
249281
249411
|
case "coverage": {
|
|
249282
249412
|
const numbers = {};
|
|
@@ -249683,7 +249813,7 @@ function describeSuggestion(o) {
|
|
|
249683
249813
|
}
|
|
249684
249814
|
|
|
249685
249815
|
// src/cli.ts
|
|
249686
|
-
process.
|
|
249816
|
+
process.exitCode = await main(process.argv.slice(2));
|
|
249687
249817
|
/*! Bundled license information:
|
|
249688
249818
|
|
|
249689
249819
|
xmlchars/xml/1.0/ed5.js:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patterkit/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "The `patter` CLI: validate, format, export, and play a Patter project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@patterkit/core": "0.3.2",
|
|
33
|
-
"@patterkit/ops": "0.
|
|
33
|
+
"@patterkit/ops": "0.13.1",
|
|
34
34
|
"@wildwinter/simple-vc-lib": "^0.5.0"
|
|
35
35
|
}
|
|
36
36
|
}
|