@patterkit/cli 0.3.1 → 0.3.3
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 +180 -93
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -195009,7 +195009,7 @@ function bundleOutputPath(loaded) {
|
|
|
195009
195009
|
}
|
|
195010
195010
|
|
|
195011
195011
|
// ../ops/src/playable-runtime.ts
|
|
195012
|
-
var PLAYABLE_RUNTIME_JS = '"use strict";var Patterplay=(()=>{var G=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var be=Object.getOwnPropertyNames;var we=Object.prototype.hasOwnProperty;var xe=(n,e)=>{for(var t in e)G(n,t,{get:e[t],enumerable:!0})},ke=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of be(e))!we.call(n,s)&&s!==t&&G(n,s,{get:()=>e[s],enumerable:!(r=ve(e,s))||r.enumerable});return n};var Ce=n=>ke(G({},"__esModule",{value:!0}),n);var Le={};xe(Le,{Engine:()=>A,Flow:()=>k,buildTagIndex:()=>N,describeBundle:()=>fe,effectiveGameData:()=>me,gameDataFields:()=>he,gameDataValue:()=>K});function v(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:v(n[2])};case"bin":return{kind:"binary",op:n[1],left:v(n[2]),right:v(n[3])};case"call":{let e=n.slice(2).map(v);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var l=class extends Error{constructor(e){super(e),this.name="EvalError"}};function I(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 l(`@${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 l(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=V(a,e);if(c===void 0)throw new l("advance() needs a quality reference (@scope.name of a quality property)");let d=S(s(a),c,"advance");return c[Math.min(d+1,c.length-1)]}let o=t.functions[i.name];if(!o)throw new l(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new l("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 l(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new l(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let g=s(i.left);if(typeof g!="boolean")throw new l(`\'and\' requires boolean operands, left is ${typeof g}`);if(!g)return!1;let h=s(i.right);if(typeof h!="boolean")throw new l(`\'and\' requires boolean operands, right is ${typeof h}`);return h}if(i.op==="or"){let g=s(i.left);if(typeof g!="boolean")throw new l(`\'or\' requires boolean operands, left is ${typeof g}`);if(g)return!0;let h=s(i.right);if(typeof h!="boolean")throw new l(`\'or\' requires boolean operands, right is ${typeof h}`);return h}let o=s(i.left),a=s(i.right),c=V(i.left,e),d=V(i.right,e),p=c??d;if(p!==void 0){if(c&&d&&!Ee(c,d)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new l(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return S(o,p,">")>S(a,p,">");case">=":return S(o,p,">=")>=S(a,p,">=");case"<":return S(o,p,"<")<S(a,p,"<");case"<=":return S(o,p,"<=")<=S(a,p,"<=");case"+":case"-":case"*":case"/":throw new l(`\'${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 Z(o,a);case"!=":return!Z(o,a);case">":return b(o,a,">"),o>a;case">=":return b(o,a,">="),o>=a;case"<":return b(o,a,"<"),o<a;case"<=":return b(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new l(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return b(o,a,"-"),o-a;case"*":return b(o,a,"*"),o*a;case"/":if(b(o,a,"/"),a===0)throw new l("division by zero");return o/a}}}};return s(n)}function Z(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 b(n,e,t){if(typeof n!="number"||typeof e!="number")throw new l(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function V(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function S(n,e,t){if(typeof n!="string")throw new l(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new l(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var Ee=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function C(n){if(Number.isNaN(n)||!Number.isFinite(n))return 0;let e=Math.trunc(n)%4294967296;return e<0?e+4294967296:e}function M(n){let e=C(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 De={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},Ie=[De];function Q(n,e,t){let r=t?.countingCalls??Ie;return R(n,t?.want??!0,e,r)}function R(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=R(n.left,e,t,r),i=R(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 R(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 L=class n{values={};decls=new Map;subscribers=new Set;auditors=new Set;norm;constructor(e=[],t){this.norm=t?.normalise??(r=>r.toLowerCase()),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??te(t))}}get(e){return this.values[this.norm(e)]}set(e,t,r){let s=this.norm(e);if(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])=>ee(t,this.get(e),void 0,e))}declarations(){return[...this.decls.values()]}clone(){let e=new n([],{normalise:this.norm});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 ee(n,e,t,r){return{name:r??n.name.toLowerCase(),type:n.type,value:e,default:n.default??te(n),...n.values!==void 0?{values:n.values}:{},writable:t??n.writable??!0}}var j=1,E=class{scopes=new Map;defineOwned(e,t){return this.mountOwned(e,new L(t))}mountOwned(e,t){return this.assertFree(e),this.scopes.set(e,{kind:"owned",bag:t}),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){this.assertFree(e);let i=new Map;for(let o of r)i.set(o.name.toLowerCase(),o);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:i,scopeWritable:s}),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(t.toLowerCase())}set(e,t,r){let s=this.scopes.get(e);if(!s)throw new Error(`unknown scope \'@${e}\'`);if(s.kind==="owned"){try{s.bag.set(t,r)}catch{throw new Error(`\'@${e}.${t}\' is read-only`)}return}let i=t.toLowerCase();if(!this.foreignWritable(s,i))throw new Error(`\'@${e}.${t}\' is read-only`);s.resolver.set(i,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)if(r.kind==="owned")for(let s of r.bag.rows())e.push({scope:t,...s});else for(let s of r.decls.values())e.push({scope:t,...ee(s,r.resolver.get(s.name.toLowerCase()),this.foreignWritable(r,s.name.toLowerCase()))});return e}toEvalContext(e){let t={};for(let[s,i]of this.scopes)t[s]=i.kind==="owned"?i.bag.values:i.resolver;let r=this.qualityLadders();return r.size===0?{scopes:t,host:e}:{scopes:t,host:e,qualities:(s,i)=>r.get(s)?.get(i.toLowerCase())}}qualityLadders(){let e=new Map;for(let[t,r]of this.scopes){let s=r.kind==="owned"?r.bag.declarations():[...r.decls.values()];for(let i of s){if(i.type!=="quality"||i.stages===void 0)continue;let o=e.get(t);o||(o=new Map,e.set(t,o)),o.set(i.name.toLowerCase(),i.stages)}}return e}toSchema(){let e=new Map;for(let[t,r]of this.scopes){let s=r.kind==="owned"?r.bag.declarations():[...r.decls.values()];if(s.length===0)continue;let i=new Map;for(let o of s)i.set(o.name.toLowerCase(),{type:o.type,enumValues:o.values,...o.stages!==void 0?{stages:o.stages}:{}});e.set(t,i)}return{properties:e}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());return e}load(e){for(let[t,r]of Object.entries(e)){let s=this.scopes.get(t);s?.kind==="owned"&&s.bag.load(r)}}saveFragment(){return{version:j,scopes:this.save()}}loadFragment(e){if(e.version!==j)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${j})`);this.load(e.scopes)}assertFree(e){if(this.scopes.has(e))throw new Error(`scope \'@${e}\' is already registered`)}};function te(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]??""}}function D(n){return n.ctx.host??{}}var $={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new l("random(a, b) requires exactly 2 arguments");let t=D(e).nextRandom;if(!t)throw new l("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 l("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new l("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:ne("check_flags"),eval(n,e){let t=re(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new l("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:ne("set_flags"),eval(n,e){let t=[...re(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new l("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:O("visits"),eval:(n,e)=>D(e).visits?.(B(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:O("seen"),eval:(n,e)=>(D(e).visits?.(B(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:O("patter_visits"),eval:(n,e)=>D(e).patterVisits?.(B(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:O("patter_seen"),eval:(n,e)=>(D(e).patterVisits?.(B(n,e,"patter_seen"))??0)>0}}};function q(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 B(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new l(`${t}(id) requires a string node id`);return r}function O(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 re(n,e,t){if(!n)throw new l(`${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 l(`${t}() first argument must be a flags property`)}function ne(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 Re=/^@[A-Za-z0-9_.]+$/;function*Be(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:Re.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 Oe(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function Pe(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function Ne(n){let e="",t=!1;for(let r of n){if(Pe(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?Ne(r):n}function ie(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of Be(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?"":Oe(s)}return t}function x(n,e){for(let t of n){e(t);let r=t.children;r&&x(r,e)}}function Ae(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function f(n){let e=n.gameId?.trim();return e||Ae(n.name)}function _(n){return`cast:${n}`}var H={open:"[",close:"]"},oe="SFX";function P(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function N(n){let e=new Map,t=(r,s)=>{let i=P([...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,P([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=P(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=P([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var ae=new WeakMap,A=class n{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;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 d=new Map;for(let u of e.cast??[])u.displayName&&d.set(u.name,u.displayName);this.defaultSeed=(t.seed??2654435769)>>>0;let p=new Map,g=new Map,h=new Map;for(let[u,y]of Object.entries(e.scenes)){this.sceneGameIdToId.set(f(y),u);let w=new Map;for(let m of y.blocks)g.set(m.id,{sceneId:u}),h.set(m.id,m),w.set(f(m),m.id),x(m.children,X=>p.set(X.id,X));this.blockGameIdToId.set(u,w)}let J=e.properties??[],F=J.filter(u=>u.shared??!0).map(le),Se=J.filter(u=>!(u.shared??!0)).map(le),ye=new Set(F.map(u=>u.name.toLowerCase())),T=new E().defineOwned("patter",F),z=new Set;if(t.world){let u=e.scopeRegistry?.scopes.find(w=>w.token==="world"),y=(u?.declarations??[]).map(pe);T.defineForeign("world",t.world,y,u?.writable??!0),z.add("world")}for(let u of e.scopeRegistry?.scopes??[]){if(z.has(u.token))continue;let y=(u.declarations??[]).map(pe);T.defineForeign(u.token,Ge(u.declarations??[]),y,u.writable??!0)}let Y=new Map;for(let[u,y]of Object.entries(e.scenes)){let w=new Set((y.sceneProps??[]).filter(m=>m.shared??!1).map(m=>m.name.toLowerCase()));Y.set(u,w)}this.host={bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:d,nodeIndex:p,blockIndex:g,blockById:h,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:N(e),shared:T,patterSharedDecls:F,patterLocalDecls:Se,patterSharedNames:ye,sceneSharedNames:Y,sharedVisits:new Map,sharedSelectors:new Map,stageBags:new Map,customRng:t.rng,onDryChoice:t.onDryChoice,replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??H).open,captionClose:(e.closedCaptions??H).close,captionCharacter:e.closedCaptions?.character||oe,refSplitCache:new Map}}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){let t=this.saveGame(),r=i=>(i.setLocale(this.currentLocale),i.setClosedCaptions(this.host.captionsOn),i),s=new n(e,this.creationOptions);try{return s.loadGame(t),r(s)}catch{let i=new n(e,this.creationOptions);for(let[o,a]of Object.entries(t.flows)){let c=a.cursor.currentSceneId;try{i.openFlow(o,c!==null?{scene:c}:{})}catch{}}return r(i)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){let r=this.resolveSceneRef(t.scene),s=this.resolveBlockRef(r,t.block);this.flowsById.get(e)?.close();let i=new k(e,this.host,t.seed??this.defaultSeed);return this.flowsById.set(e,i),i.start(r,s),i}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}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?f(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?f(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)??[]}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)ce(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 ce(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...f(e)?{gameId:f(e)}:{},name:e.name,...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...f(t)?{gameId:f(t)}:{},name:t.name,...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)x(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[_(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()]}closeFlow(e){this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.shared.reseedOwned("patter",this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear(),this.host.stageBags.clear()}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.shared.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e);this.host.shared.set(r,s,t)}listProperties(){return this.host.patterSharedDecls.map(e=>({ref:`@${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:Fe(e)}))}splitShared(e){let t=this.host.refSplitCache.get(e);if(t||(t=q(e,r=>r==="scene"||this.host.shared.has(r)),this.host.refSplitCache.set(e,t)),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 this.host.shared.save()}load(e){this.host.shared.load(e)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:2,shared:this.host.shared.save(),sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:ue(this.host.sharedSelectors),stageBags:Object.fromEntries([...this.host.stageBags].map(([t,r])=>[t,{...r}])),flows:e}}loadGame(e){if(e.version!==2)throw new Error(`unsupported save version: ${e.version}`);this.host.shared.load(e.shared),this.host.sharedVisits.clear();for(let[t,r]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(t,r);this.host.sharedSelectors.clear();for(let[t,r]of ge(e.sharedSelectors))this.host.sharedSelectors.set(t,r);this.host.stageBags.clear();for(let[t,r]of Object.entries(e.stageBags??{}))this.host.stageBags.set(t,{...r});this.flowsById.clear();for(let[t,r]of Object.entries(e.flows)){let s=new k(t,this.host,this.defaultSeed);s.restore(r),this.flowsById.set(t,s)}}},k=class{id;host;local;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPromptBeat=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.shared.get("patter",e):this.local.get("patter",e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.shared.set("patter",e,t):this.local.set("patter",e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;return t===null?void 0:(this.host.sceneSharedNames.get(t)?.has(e)?this.host.stageBags.get(t):this.sceneBags.get(t))?.[e]},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.host.sceneSharedNames.get(r)?.has(e)?this.host.stageBags.get(r):this.sceneBags.get(r);s&&(s[e]=t)}};evalCtx;constructor(e,t,r){this.id=e,this.host=t,this.rngState=C(r),this.local=this.freshLocal();let s={...t.shared.toEvalContext().scopes};s.patter=this.patterResolver,s.scene=this.sceneResolver,this.evalCtx={scopes:s,host:{nextRandom:this.rng,visits:i=>this.visitCounts.get(i)??0,patterVisits:i=>this.host.sharedVisits.get(i)??0},qualities:(i,o)=>this.stagesFor(i,o)}}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 s(this.host.bundle.scopeRegistry?.scopes.find(i=>i.token===e)?.declarations)}start(e,t){if(this.sceneBags.clear(),this.local=this.freshLocal(),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(e==="END")return this.started=!0,this.pendingChoice=null,this.pendingPromptBeat=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.pendingPromptBeat=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.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.pendingChoice=null,this.pendingPromptBeat=null,this.pendingPromptOwnerId=null}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.pendingPromptBeat){let e=this.pendingPromptBeat;return this.pendingPromptBeat=null,this.pendingPromptOwnerId=null,this.beatResult(e)}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 s=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(s);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}for(;t.index<r.length&&!this.eligible(r[t.index]);)t.index++;if(t.index>=r.length){this.stack.pop();continue}this.enterChild(r[t.index++])}}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(i=>i.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);let s=t.byId.get(e);this.pendingChoice=null,this.pendingPromptBeat=this.host.replayPromptOnChoose?this.promptBeatOf(s)??null:null,this.pendingPromptOwnerId=this.pendingPromptBeat?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.shared.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitRef(e);if(r==="patter")this.patterResolver.set(s,t);else if(r==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);this.sceneResolver.set(s,t)}else this.host.shared.set(r,s,t)}snapshot(){return{scopes:this.local.save(),sceneBags:Object.fromEntries([...this.sceneBags].map(([e,t])=>[e,{...t}])),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,selectors:ue(this.selectors)}}}restore(e){this.rngState=C(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.sceneBags=new Map(Object.entries(e.sceneBags??{}).map(([r,s])=>[r,{...s}])),this.local=this.freshLocal(),this.local.load(e.scopes),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=ge(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.pendingPromptBeat=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId);this.pendingPromptBeat=r?this.promptBeatOf(r)??null:null,this.pendingPromptBeat||(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.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.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(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.filter(s=>this.eligible(s));if(t.length===0)return null;let r=this.selectorState(e);switch(e.selector){case"branch":return t[0];case"sequence":{let s=e.options?.order??"sequential",i=e.options?.exhaust??"once";return s==="shuffle"?this.pickShuffle(t,i,r):s==="specificity"?this.pickSpecificity(t,i,r):this.pickSequential(t,i,r)}default:return null}}pickSequential(e,t,r){let s=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,r){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(g=>g.id);if(r.bag===void 0&&(r.bag=o()),r.bag.length===0){if(t==="once")return null;if(i){let g=e[s-1];return r.last=g.id,g}r.bag=o()}let a=r.bag,c=r.last!==void 0&&a.length>1?a.indexOf(r.last):-1,d=Math.floor(this.rng()*(c>=0?a.length-1:a.length));c>=0&&d>=c&&d++;let p=a[d];return a.splice(d,1),r.last=p,e.find(g=>g.id===p)}pickSpecificity(e,t,r){let s=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(p=>p.id));let d=new Set(r.bag);if(s=e.filter(p=>d.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(d=>{let p=this.specScore(d);return p>i&&(i=p),{c:d,s:p}}).filter(d=>d.s===i).map(d=>d.c),c;if(a.length===1)c=a[0];else{let d=r.last!==void 0?a.findIndex(g=>g.id===r.last):-1,p=Math.floor(this.rng()*(d>=0?a.length-1:a.length));d>=0&&p>=d&&p++,c=a[p]}return t!=="repeat"&&(r.bag=r.bag.filter(d=>d!==c.id)),r.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0):0}matchedSpec(e,t){return Q(e,s=>de(I(s,this.evalCtx,$)),{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=t.get(e.id);return r||(r={},t.set(e.id,r)),r}runEffects(e){for(let t of e??[])this.setProperty(t.target,this.evalExpr(t.value))}eligible(e){return e.condition?de(this.evalExpr(e.condition)):!0}evalExpr(e){return I(this.conditionAst(e),this.evalCtx,$)}conditionAst(e){let t=ae.get(e);return t||(t=v(e.ast),ae.set(e,t)),t}enter(e){this.visitCounts.set(e,(this.visitCounts.get(e)??0)+1),this.host.sharedVisits.set(e,(this.host.sharedVisits.get(e)??0)+1)}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=M(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 ie(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}}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 x(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=_(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}splitRef(e){let t=this.host.refSplitCache.get(e);return t||(t=q(e,r=>r==="scene"||this.host.shared.has(r)),this.host.refSplitCache.set(e,t)),t}freshLocal(){return new E().defineOwned("patter",this.host.patterLocalDecls)}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;if(!this.sceneBags.has(e.id)){let r={};for(let s of e.sceneProps??[]){let i=s.name.toLowerCase();t.has(i)||(r[i]=U(s))}this.sceneBags.set(e.id,r)}if(!this.host.stageBags.has(e.id)){let r={};for(let s of e.sceneProps??[]){let i=s.name.toLowerCase();t.has(i)&&(r[i]=U(s))}this.host.stageBags.set(e.id,r)}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);i&&(i[s]=U(r))}}};function ce(n,e){x(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 ue(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 ge(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 le(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function Fe(n){if(n.default!==void 0)return n.default;switch(n.type){case"number":return 0;case"string":return"";case"flags":return[];case"enum":return n.values?.[0]??"";case"quality":return n.stages?.[0]??"";default:return!1}}function pe(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function Te(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"flags":return[];case"enum":return n.values?.[0]??"";case"quality":return n.stages?.[0]??""}}function Ge(n){let e=r=>r.toLowerCase(),t=new Map;for(let r of n)t.set(e(r.name),Te(r));return{get:r=>t.get(e(r)),set:(r,s)=>{t.set(e(r),s)}}}function U(n){if(n.default!==void 0)return n.default;switch(n.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"flags":return[];case"enum":return n.values?.[0]??"";case"quality":return n.stages?.[0]??""}}function de(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var Ve=(n,e)=>n.shared??e;function W(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:Ve(n,e)}}function Me(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function je(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 fe(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=f(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:f(c),name:c.name}))});for(let c of o.blocks)je(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>W(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=>W(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(Me)}));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=>W(o,!0)),scene:r},gameData:i,counts:e}}function he(n,e){return n.gameDataFields?.[e]??[]}function K(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function me(n,e){let t={};for(let r of n){let s=K(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 Ce(Le);})();\n//# sourceMappingURL=patterplay.min.js.map';
|
|
195012
|
+
var PLAYABLE_RUNTIME_JS = '"use strict";var Patterplay=(()=>{var V=Object.defineProperty;var Se=Object.getOwnPropertyDescriptor;var ve=Object.getOwnPropertyNames;var be=Object.prototype.hasOwnProperty;var we=(n,e)=>{for(var t in e)V(n,t,{get:e[t],enumerable:!0})},xe=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of ve(e))!be.call(n,s)&&s!==t&&V(n,s,{get:()=>e[s],enumerable:!(r=Se(e,s))||r.enumerable});return n};var Ee=n=>xe(V({},"__esModule",{value:!0}),n);var Le={};we(Le,{Engine:()=>F,Flow:()=>C,buildTagIndex:()=>A,describeBundle:()=>ue,effectiveGameData:()=>fe,gameDataFields:()=>he,gameDataValue:()=>K});function w(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:w(n[2])};case"bin":return{kind:"binary",op:n[1],left:w(n[2]),right:w(n[3])};case"call":{let e=n.slice(2).map(w);return{kind:"call",name:n[1],args:e}}case"fd":return{kind:"flagdelta",sign:n[1],name:n[2]}}}var l=class extends Error{constructor(e){super(e),this.name="EvalError"}};function B(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 l(`@${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 l(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=M(a,e);if(c===void 0)throw new l("advance() needs a quality reference (@scope.name of a quality property)");let d=S(s(a),c,"advance");return c[Math.min(d+1,c.length-1)]}let o=t.functions[i.name];if(!o)throw new l(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new l("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 l(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new l(`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 l(`\'and\' requires boolean operands, left is ${typeof u}`);if(!u)return!1;let f=s(i.right);if(typeof f!="boolean")throw new l(`\'and\' requires boolean operands, right is ${typeof f}`);return f}if(i.op==="or"){let u=s(i.left);if(typeof u!="boolean")throw new l(`\'or\' requires boolean operands, left is ${typeof u}`);if(u)return!0;let f=s(i.right);if(typeof f!="boolean")throw new l(`\'or\' requires boolean operands, right is ${typeof f}`);return f}let o=s(i.left),a=s(i.right),c=M(i.left,e),d=M(i.right,e),p=c??d;if(p!==void 0){if(c&&d&&!ke(c,d)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new l(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return S(o,p,">")>S(a,p,">");case">=":return S(o,p,">=")>=S(a,p,">=");case"<":return S(o,p,"<")<S(a,p,"<");case"<=":return S(o,p,"<=")<=S(a,p,"<=");case"+":case"-":case"*":case"/":throw new l(`\'${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 Z(o,a);case"!=":return!Z(o,a);case">":return x(o,a,">"),o>a;case">=":return x(o,a,">="),o>=a;case"<":return x(o,a,"<"),o<a;case"<=":return x(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new l(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return x(o,a,"-"),o-a;case"*":return x(o,a,"*"),o*a;case"/":if(x(o,a,"/"),a===0)throw new l("division by zero");return o/a}}}};return s(n)}function Z(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 x(n,e,t){if(typeof n!="number"||typeof e!="number")throw new l(`\'${t}\' requires numeric operands, got ${typeof n} and ${typeof e}`)}function M(n,e){if(!(n.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(n.scope,n.name)}function S(n,e,t){if(typeof n!="string")throw new l(`\'${t}\' on a quality compares stages, got ${typeof n}`);let r=e.indexOf(n);if(r<0)throw new l(`"${n}" is not a stage of this quality (stages: ${e.join(", ")})`);return r}var ke=(n,e)=>n.length===e.length&&n.every((t,r)=>t===e[r]);function I(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=I(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 Ce={name:"check_flags",count:n=>Math.max(1,n.args.length-1)},Ie=[Ce];function Q(n,e,t){let r=t?.countingCalls??Ie;return P(n,t?.want??!0,e,r)}function P(n,e,t,r){if(n.kind==="binary"&&(n.op==="and"||n.op==="or")){let s=P(n.left,e,t,r),i=P(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 P(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??E(t))}}get(e){return this.values[this.norm(e)]}set(e,t,r){let s=this.norm(e);if(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])=>ee(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 ee(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??E(n),...n.values!==void 0?{values:n.values}:{},...n.stages!==void 0?{stages:n.stages}:{},writable:t??n.writable??!0}}var $=1,D=class{scopes=new Map;defineOwned(e,t,r){return this.mountOwned(e,new b(t,{pathPrefix:r??`${e}.`}))}mountOwned(e,t){return this.assertFree(e),this.scopes.set(e,{kind:"owned",bag:t}),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){this.assertFree(e);let i=new Map;for(let o of r)i.set(o.name.toLowerCase(),o);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:i,scopeWritable:s}),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(t.toLowerCase())}set(e,t,r){let s=this.scopes.get(e);if(!s)throw new Error(`unknown scope \'@${e}\'`);if(s.kind==="owned"){try{s.bag.set(t,r)}catch{throw new Error(`\'@${e}.${t}\' is read-only`)}return}let i=t.toLowerCase();if(!this.foreignWritable(s,i))throw new Error(`\'@${e}.${t}\' is read-only`);s.resolver.set(i,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)if(r.kind==="owned")for(let s of r.bag.rows())e.push({scope:t,...s});else for(let s of r.decls.values())e.push({scope:t,...ee(s,r.resolver.get(s.name.toLowerCase()),this.foreignWritable(r,s.name.toLowerCase()),void 0,`${t}.`)});return e}toEvalContext(e){let t={};for(let[s,i]of this.scopes)t[s]=i.kind==="owned"?i.bag.values:i.resolver;let r=this.qualityLadders();return r.size===0?{scopes:t,host:e}:{scopes:t,host:e,qualities:(s,i)=>r.get(s)?.get(i.toLowerCase())}}qualityLadders(){let e=new Map;for(let[t,r]of this.scopes){let s=r.kind==="owned"?r.bag.declarations():[...r.decls.values()];for(let i of s){if(i.type!=="quality"||i.stages===void 0)continue;let o=e.get(t);o||(o=new Map,e.set(t,o)),o.set(i.name.toLowerCase(),i.stages)}}return e}toSchema(){let e=new Map;for(let[t,r]of this.scopes){let s=r.kind==="owned"?r.bag.declarations():[...r.decls.values()];if(s.length===0)continue;let i=new Map;for(let o of s)i.set(o.name.toLowerCase(),{type:o.type,enumValues:o.values,...o.stages!==void 0?{stages:o.stages}:{}});e.set(t,i)}return{properties:e}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]=r.bag.save());return e}load(e){for(let[t,r]of Object.entries(e)){let s=this.scopes.get(t);s?.kind==="owned"&&s.bag.load(r)}}saveFragment(){return{version:$,scopes:this.save()}}loadFragment(e){if(e.version!==$)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${$})`);this.load(e.scopes)}assertFree(e){if(this.scopes.has(e))throw new Error(`scope \'@${e}\' is already registered`)}};function E(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}}function R(n){return n.ctx.host??{}}var q={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(n,e){if(n.length!==2)throw new l("random(a, b) requires exactly 2 arguments");let t=R(e).nextRandom;if(!t)throw new l("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 l("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(s))throw new l("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:re("check_flags"),eval(n,e){let t=te(n[0],e,"check_flags");for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new l("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:re("set_flags"),eval(n,e){let t=[...te(n[0],e,"set_flags")];for(let r=1;r<n.length;r++){let s=n[r];if(s.kind!=="flagdelta")throw new l("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:N("visits"),eval:(n,e)=>R(e).visits?.(O(n,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:N("seen"),eval:(n,e)=>(R(e).visits?.(O(n,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:N("patter_visits"),eval:(n,e)=>R(e).patterVisits?.(O(n,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:N("patter_seen"),eval:(n,e)=>(R(e).patterVisits?.(O(n,e,"patter_seen"))??0)>0}}};function H(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 O(n,e,t){let r=e.evaluate(n[0]);if(typeof r!="string")throw new l(`${t}(id) requires a string node id`);return r}function N(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 te(n,e,t){if(!n)throw new l(`${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 l(`${t}() first argument must be a flags property`)}function re(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 De=/^@[A-Za-z0-9_.]+$/;function*Re(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:De.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 Be(n){return Array.isArray(n)?n.join(", "):typeof n=="boolean"?n?"true":"false":String(n)}function Pe(n){return n===" "||n===" "||n===`\n`||n==="\\r"||n==="\\f"||n==="\\v"}function Oe(n){let e="",t=!1;for(let r of n){if(Pe(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function ne(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?Oe(r):n}function se(n,e){if(n.indexOf("{")<0)return n;let t="";for(let r of Re(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?"":Be(s)}return t}function k(n,e){for(let t of n){e(t);let r=t.children;r&&k(r,e)}}function Ne(n){return n.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function h(n){let e=n.gameId?.trim();return e||Ne(n.name)}function _(n){return`cast:${n}`}var U={open:"[",close:"]"},ie="SFX";function T(n){let e=new Set,t=[];for(let r of n)e.has(r)||(e.add(r),t.push(r));return t}function A(n){let e=new Map,t=(r,s)=>{let i=T([...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,T([...i,...o.tags??[]]))};for(let r of Object.values(n.scenes)){let s=T(r.tags??[]);e.set(r.id,s);for(let i of r.blocks){let o=T([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var oe=new WeakMap,F=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 d=new Map;for(let g of e.cast??[])g.displayName&&d.set(g.name,g.displayName);this.defaultSeed=(t.seed??2654435769)>>>0;let p=new Map,u=new Map,f=new Map;for(let[g,m]of Object.entries(e.scenes)){this.sceneGameIdToId.set(h(m),g);let v=new Map;for(let y of m.blocks)u.set(y.id,{sceneId:g}),f.set(y.id,y),v.set(h(y),y.id),k(y.children,X=>p.set(X.id,X));this.blockGameIdToId.set(g,v)}let J=e.properties??[],G=J.filter(g=>g.shared??!0).map(ce),me=J.filter(g=>!(g.shared??!0)).map(ce),ye=new Set(G.map(g=>g.name.toLowerCase())),L=new D().defineOwned("patter",G,"@patter."),z=new Set;if(t.world){let g=e.scopeRegistry?.scopes.find(v=>v.token==="world"),m=(g?.declarations??[]).map(le);L.defineForeign("world",t.world,m,g?.writable??!0),z.add("world")}for(let g of e.scopeRegistry?.scopes??[]){if(z.has(g.token))continue;let m=(g.declarations??[]).map(le);L.defineForeign(g.token,Te(g.declarations??[]),m,g.writable??!0)}let Y=new Map;for(let[g,m]of Object.entries(e.scenes)){let v=new Set((m.sceneProps??[]).filter(y=>y.shared??!1).map(y=>y.name.toLowerCase()));Y.set(g,v)}this.host={logEnabled:t.log??!1,emitEngine:(g,m,v)=>this.emitEngine(g,m,v),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:d,nodeIndex:p,blockIndex:u,blockById:f,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:A(e),shared:L,patterSharedDecls:G,patterLocalDecls:me,patterSharedNames:ye,sceneSharedNames:Y,sharedVisits:new Map,sharedSelectors:new Map,stageBags:new Map,customRng:t.rng,onDryChoice:t.onDryChoice,replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??U).open,captionClose:(e.closedCaptions??U).close,captionCharacter:e.closedCaptions?.character||ie,refSplitCache:new Map}}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){let t=this.saveGame(),r=i=>(i.setLocale(this.currentLocale),i.setClosedCaptions(this.host.captionsOn),i),s=new n(e,this.creationOptions);try{return s.loadGame(t),r(s)}catch{let i=new n(e,this.creationOptions);for(let[o,a]of Object.entries(t.flows)){let c=a.cursor.currentSceneId;try{i.openFlow(o,c!==null?{scene:c}:{})}catch{}}return r(i)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){let r=this.resolveSceneRef(t.scene),s=this.resolveBlockRef(r,t.block);this.flowsById.get(e)?.close();let i=new C(e,this.host,t.seed??this.defaultSeed);return this.flowsById.set(e,i),i.start(r,s),i}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}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?h(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?h(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)??[]}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)ae(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 ae(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...h(e)?{gameId:h(e)}:{},name:e.name,...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...h(t)?{gameId:h(t)}:{},name:t.name,...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)k(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[_(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.shared.ownedBag("patter")}];for(let[t,r]of this.host.stageBags)e.push({bag:r,pathPrefix:`@scene:${t}.`});return e}closeFlow(e){this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.shared.reseedOwned("patter",this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear(),this.host.stageBags.clear()}getProperty(e){let{scope:t,name:r}=this.splitShared(e);return this.host.shared.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitShared(e);this.host.shared.set(r,s,t)}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:E(e),writable:e.writable??!0}))}splitShared(e){let t=this.host.refSplitCache.get(e);if(t||(t=H(e,r=>r==="scene"||this.host.shared.has(r)),this.host.refSplitCache.set(e,t)),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 this.host.shared.save()}load(e){this.host.shared.load(e)}saveGame(){let e={};for(let[t,r]of this.flowsById)e[t]=r.snapshot();return{version:2,shared:this.host.shared.save(),sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:de(this.host.sharedSelectors),stageBags:Object.fromEntries([...this.host.stageBags].map(([t,r])=>[t,r.save()])),flows:e}}loadGame(e){if(e.version!==2)throw new Error(`unsupported save version: ${e.version}`);this.host.shared.load(e.shared),this.host.sharedVisits.clear();for(let[t,r]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(t,r);this.host.sharedSelectors.clear();for(let[t,r]of ge(e.sharedSelectors))this.host.sharedSelectors.set(t,r);this.host.stageBags.clear();for(let[t,r]of Object.entries(e.stageBags??{})){let s=this.host.sceneSharedNames.get(t)??new Set,i=(this.host.bundle.scenes[t]?.sceneProps??[]).filter(a=>s.has(a.name.toLowerCase())),o=new b(i);o.load(r),this.host.stageBags.set(t,o)}this.flowsById.clear();for(let[t,r]of Object.entries(e.flows)){let s=new C(t,this.host,this.defaultSeed);s.restore(r),this.flowsById.set(t,s)}}},C=class{id;host;local;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPromptBeat=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.shared.get("patter",e):this.local.get("patter",e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.shared.set("patter",e,t):this.local.set("patter",e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;return t===null?void 0:(this.host.sceneSharedNames.get(t)?.has(e)?this.host.stageBags.get(t):this.sceneBags.get(t))?.get(e)},set:(e,t)=>{let r=this.currentSceneId;if(r===null)return;let s=this.host.sceneSharedNames.get(r)?.has(e)?this.host.stageBags.get(r):this.sceneBags.get(r);s&&s.set(e,t)}};evalCtx;flowLog=[];flowSeq=0;constructor(e,t,r){this.id=e,this.host=t,this.rngState=I(r),this.local=this.freshLocal();let s={...t.shared.toEvalContext().scopes};s.patter=this.patterResolver,s.scene=this.sceneResolver,this.evalCtx={scopes:s,host:{nextRandom:this.rng,visits:i=>this.visitCounts.get(i)??0,patterVisits:i=>this.host.sharedVisits.get(i)??0},qualities:(i,o)=>this.stagesFor(i,o)}}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 s(this.host.bundle.scopeRegistry?.scopes.find(i=>i.token===e)?.declarations)}start(e,t){if(this.sceneBags.clear(),this.local=this.freshLocal(),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(e==="END")return this.started=!0,this.pendingChoice=null,this.pendingPromptBeat=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.pendingPromptBeat=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.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.pendingChoice=null,this.pendingPromptBeat=null,this.pendingPromptOwnerId=null}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.pendingPromptBeat){let e=this.pendingPromptBeat;return this.pendingPromptBeat=null,this.pendingPromptOwnerId=null,this.beatResult(e)}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.ownedBag("patter"),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(i=>i.id===e);if(!r)throw new Error(`unknown choice option: ${e}`);if(!r.eligible)throw new Error(`choice option is not eligible: ${e}`);let s=t.byId.get(e);this.emit({type:"chose",group:t.groupId,option:e}),this.pendingChoice=null,this.pendingPromptBeat=this.host.replayPromptOnChoose?this.promptBeatOf(s)??null:null,this.pendingPromptOwnerId=this.pendingPromptBeat?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.shared.get(t,r)}setProperty(e,t){let{scope:r,name:s}=this.splitRef(e);if(r==="patter")this.patterResolver.set(s,t);else if(r==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);this.sceneResolver.set(s,t)}else this.host.shared.set(r,s,t)}snapshot(){return{scopes:this.local.save(),sceneBags:Object.fromEntries([...this.sceneBags].map(([e,t])=>[e,t.save()])),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,selectors:de(this.selectors)}}}restore(e){this.rngState=I(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.sceneBags=new Map(Object.entries(e.sceneBags??{}).map(([r,s])=>{let i=this.host.sceneSharedNames.get(r)??new Set,o=(this.host.bundle.scenes[r]?.sceneProps??[]).filter(c=>!i.has(c.name.toLowerCase())),a=new b(o);return a.load(s),[r,a]})),this.local=this.freshLocal(),this.local.load(e.scopes),this.activeSnippet=null,t.activeSnippetId!==null){let r=this.host.nodeIndex.get(t.activeSnippetId);r&&r.type==="snippet"&&(this.activeSnippet=r)}if(this.selectors=ge(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.pendingPromptBeat=null,this.pendingPromptOwnerId=t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let r=this.host.nodeIndex.get(this.pendingPromptOwnerId);this.pendingPromptBeat=r?this.promptBeatOf(r)??null:null,this.pendingPromptBeat||(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,d=Math.floor(this.rng()*(c>=0?a.length-1:a.length));c>=0&&d>=c&&d++;let p=a[d];return a.splice(d,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 d=new Set(r.bag);if(s=e.filter(p=>d.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(d=>{let p=this.specScore(d);return p>i&&(i=p),{c:d,s:p}}).filter(d=>d.s===i).map(d=>d.c),c;if(a.length===1)c=a[0];else{let d=r.last!==void 0?a.findIndex(u=>u.id===r.last):-1,p=Math.floor(this.rng()*(d>=0?a.length-1:a.length));d>=0&&p>=d&&p++,c=a[p]}return t!=="repeat"&&(r.bag=r.bag.filter(d=>d!==c.id)),r.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0):0}matchedSpec(e,t){return Q(e,s=>pe(B(s,this.evalCtx,q)),{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,r=t.get(e.id);return r||(r={},t.set(e.id,r)),r}runEffects(e){for(let t of e??[]){let r=this.evalExpr(t.value),s=this.host.logEnabled?this.getProperty(t.target):void 0;this.setProperty(t.target,r),this.emit({type:"write",target:t.target,value:r,...s!==void 0?{prev:s}:{}})}}eligible(e){return e.condition?pe(this.evalExpr(e.condition)):!0}evalExpr(e){return B(this.conditionAst(e),this.evalCtx,q)}conditionAst(e){let t=oe.get(e);return t||(t=w(e.ast),oe.set(e,t)),t}enter(e){this.visitCounts.set(e,(this.visitCounts.get(e)??0)+1),this.host.sharedVisits.set(e,(this.host.sharedVisits.get(e)??0)+1)}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 se(e,t=>this.getProperty(t))}stripCaptions(e){return ne(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}}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 k(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=_(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}splitRef(e){let t=this.host.refSplitCache.get(e);return t||(t=H(e,r=>r==="scene"||this.host.shared.has(r)),this.host.refSplitCache.set(e,t)),t}freshLocal(){return new D().defineOwned("patter",this.host.patterLocalDecls,"@patter.")}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set,r=e.sceneProps??[];this.sceneBags.has(e.id)||this.sceneBags.set(e.id,new b(r.filter(s=>!t.has(s.name.toLowerCase())))),this.host.stageBags.has(e.id)||this.host.stageBags.set(e.id,new b(r.filter(s=>t.has(s.name.toLowerCase()))));for(let s of e.sceneProps??[]){if(!s.temporary)continue;let i=s.name.toLowerCase(),o=t.has(i)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);o&&o.set(i,E(s))}}};function ae(n,e){k(n,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let r of t.beats??[])r.kind==="line"&&r.character&&e.add(r.character)})}function de(n){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 ge(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 ce(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default}}function le(n){return{name:n.name,type:n.type,values:n.values,stages:n.stages,default:n.default,writable:n.writable}}function Te(n){let e=r=>r.toLowerCase(),t=new Map;for(let r of n)t.set(e(r.name),E(r));return{get:r=>t.get(e(r)),set:(r,s)=>{t.set(e(r),s)}}}function pe(n){return typeof n=="boolean"?n:typeof n=="number"?n!==0:typeof n=="string"?n!=="":n.length>0}var Ae=(n,e)=>n.shared??e;function W(n,e){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.default!==void 0?{default:n.default}:{},shared:Ae(n,e)}}function Fe(n){return{name:n.name,type:n.type,hasDefault:n.default!==void 0,...n.values?{values:[...n.values]}:{},...n.purpose?{purpose:n.purpose}:{}}}function Ge(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 ue(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=h(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:h(c),name:c.name}))});for(let c of o.blocks)Ge(c,e);o.sceneProps?.length&&r.push({gameId:a,properties:o.sceneProps.map(c=>W(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=>W(a,!0))})),i=Object.entries(n.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(Fe)}));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=>W(o,!0)),scene:r},gameData:i,counts:e}}function he(n,e){return n.gameDataFields?.[e]??[]}function K(n,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:n.find(r=>r.name===t)?.default}function fe(n,e){let t={};for(let r of n){let s=K(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 Ee(Le);})();\n//# sourceMappingURL=patterplay.min.js.map';
|
|
195013
195013
|
|
|
195014
195014
|
// ../ops/src/export-html.ts
|
|
195015
195015
|
var esc = (s) => s.replace(/[&<>]/g, (c2) => ({ "&": "&", "<": "<", ">": ">" })[c2]);
|
|
@@ -195258,8 +195258,12 @@ var PropertyBag = class _PropertyBag {
|
|
|
195258
195258
|
* long-standing contract); a product whose names are case-significant
|
|
195259
195259
|
* passes identity. */
|
|
195260
195260
|
norm;
|
|
195261
|
+
/** The address prefix this bag's rows carry, separator included (`@`,
|
|
195262
|
+
* `@scene.`, `world.`, `deck.<id>.`). Empty means a row's path is its name. */
|
|
195263
|
+
pathPrefix;
|
|
195261
195264
|
constructor(declarations = [], opts) {
|
|
195262
195265
|
this.norm = opts?.normalise ?? ((n) => n.toLowerCase());
|
|
195266
|
+
this.pathPrefix = opts?.pathPrefix ?? "";
|
|
195263
195267
|
this.seed(declarations);
|
|
195264
195268
|
}
|
|
195265
195269
|
seed(declarations) {
|
|
@@ -195303,7 +195307,7 @@ var PropertyBag = class _PropertyBag {
|
|
|
195303
195307
|
/** Examiner rows: the declared surface only (stray values are storage,
|
|
195304
195308
|
* not surface). */
|
|
195305
195309
|
rows() {
|
|
195306
|
-
return [...this.decls.entries()].map(([name, d]) => rowFor(d, this.get(name), void 0, name));
|
|
195310
|
+
return [...this.decls.entries()].map(([name, d]) => rowFor(d, this.get(name), void 0, name, this.pathPrefix));
|
|
195307
195311
|
}
|
|
195308
195312
|
declarations() {
|
|
195309
195313
|
return [...this.decls.values()];
|
|
@@ -195312,7 +195316,7 @@ var PropertyBag = class _PropertyBag {
|
|
|
195312
195316
|
* duplicated, the normalisation policy carried, subscriptions NOT
|
|
195313
195317
|
* carried. */
|
|
195314
195318
|
clone() {
|
|
195315
|
-
const c2 = new _PropertyBag([], { normalise: this.norm });
|
|
195319
|
+
const c2 = new _PropertyBag([], { normalise: this.norm, pathPrefix: this.pathPrefix });
|
|
195316
195320
|
c2.decls = new Map(this.decls);
|
|
195317
195321
|
Object.assign(c2.values, structuredClone(this.values));
|
|
195318
195322
|
return c2;
|
|
@@ -195335,13 +195339,19 @@ var PropertyBag = class _PropertyBag {
|
|
|
195335
195339
|
for (const [k, v] of Object.entries(values)) this.values[this.norm(k)] = v;
|
|
195336
195340
|
}
|
|
195337
195341
|
};
|
|
195338
|
-
function rowFor(d, value, writable, name) {
|
|
195342
|
+
function rowFor(d, value, writable, name, pathPrefix = "") {
|
|
195343
|
+
const rowName = name ?? d.name.toLowerCase();
|
|
195339
195344
|
return {
|
|
195340
|
-
name:
|
|
195345
|
+
name: rowName,
|
|
195346
|
+
path: pathPrefix + rowName,
|
|
195341
195347
|
type: d.type,
|
|
195342
195348
|
value,
|
|
195343
195349
|
default: d.default ?? defaultFor(d),
|
|
195344
195350
|
...d.values !== void 0 ? { values: d.values } : {},
|
|
195351
|
+
// `stages` was added to the row so an examiner could offer a quality's ladder
|
|
195352
|
+
// instead of a free-text box, and then never populated here: every quality row
|
|
195353
|
+
// this function built came out without one. Fixed 2026-09-02.
|
|
195354
|
+
...d.stages !== void 0 ? { stages: d.stages } : {},
|
|
195345
195355
|
writable: writable ?? d.writable ?? true
|
|
195346
195356
|
};
|
|
195347
195357
|
}
|
|
@@ -195353,8 +195363,8 @@ var ScopeRegistry = class {
|
|
|
195353
195363
|
* each declaration's `default` (or a type default). Owned scopes are
|
|
195354
195364
|
* type-checked (declarations) and serialized by `save`/`load`.
|
|
195355
195365
|
*/
|
|
195356
|
-
defineOwned(token2, declarations) {
|
|
195357
|
-
return this.mountOwned(token2, new PropertyBag(declarations));
|
|
195366
|
+
defineOwned(token2, declarations, pathPrefix) {
|
|
195367
|
+
return this.mountOwned(token2, new PropertyBag(declarations, { pathPrefix: pathPrefix ?? `${token2}.` }));
|
|
195358
195368
|
}
|
|
195359
195369
|
/**
|
|
195360
195370
|
* Attach an EXISTING bag as an owned scope - the shared-container move: a
|
|
@@ -195439,7 +195449,13 @@ var ScopeRegistry = class {
|
|
|
195439
195449
|
for (const d of e.decls.values()) {
|
|
195440
195450
|
out.push({
|
|
195441
195451
|
scope: token2,
|
|
195442
|
-
...rowFor(
|
|
195452
|
+
...rowFor(
|
|
195453
|
+
d,
|
|
195454
|
+
e.resolver.get(d.name.toLowerCase()),
|
|
195455
|
+
this.foreignWritable(e, d.name.toLowerCase()),
|
|
195456
|
+
void 0,
|
|
195457
|
+
`${token2}.`
|
|
195458
|
+
)
|
|
195443
195459
|
});
|
|
195444
195460
|
}
|
|
195445
195461
|
}
|
|
@@ -195550,6 +195566,13 @@ function defaultFor(d) {
|
|
|
195550
195566
|
// A quality starts at the first rung of its ladder.
|
|
195551
195567
|
case "quality":
|
|
195552
195568
|
return d.stages?.[0] ?? "";
|
|
195569
|
+
// Unreachable for a well-typed declaration, and deliberately present anyway: a bundle
|
|
195570
|
+
// is DATA, and a hand-edited or newer-than-this-build one can carry a type string the
|
|
195571
|
+
// union does not have. Falling off the switch would seed `undefined`, which is not a
|
|
195572
|
+
// ScalarValue and travels a long way before it fails. Patterplay's copy of this had the
|
|
195573
|
+
// guard and this one did not, which is the drift you only find by removing a duplicate.
|
|
195574
|
+
default:
|
|
195575
|
+
return false;
|
|
195553
195576
|
}
|
|
195554
195577
|
}
|
|
195555
195578
|
|
|
@@ -195608,6 +195631,10 @@ var Engine = class _Engine {
|
|
|
195608
195631
|
/** The options this engine was built with - reused verbatim by `hotSwap` so the replacement
|
|
195609
195632
|
* engine keeps the same world resolver, custom RNG, and diagnostic hooks. */
|
|
195610
195633
|
creationOptions;
|
|
195634
|
+
/** The run's ordered stream: every flow's events, each naming its flow. Empty and
|
|
195635
|
+
* unwritten unless `options.log` asked for it. */
|
|
195636
|
+
engineLog = [];
|
|
195637
|
+
engineTraceHandlers = /* @__PURE__ */ new Set();
|
|
195611
195638
|
constructor(bundle, options = {}) {
|
|
195612
195639
|
this.creationOptions = options;
|
|
195613
195640
|
const locale = options.locale ?? bundle.locales.default;
|
|
@@ -195643,7 +195670,7 @@ var Engine = class _Engine {
|
|
|
195643
195670
|
const patterSharedDecls = props.filter((p) => p.shared ?? true).map(toDecl);
|
|
195644
195671
|
const patterLocalDecls = props.filter((p) => !(p.shared ?? true)).map(toDecl);
|
|
195645
195672
|
const patterSharedNames = new Set(patterSharedDecls.map((d) => d.name.toLowerCase()));
|
|
195646
|
-
const shared = new ScopeRegistry().defineOwned("patter", patterSharedDecls);
|
|
195673
|
+
const shared = new ScopeRegistry().defineOwned("patter", patterSharedDecls, "@patter.");
|
|
195647
195674
|
const hostBound = /* @__PURE__ */ new Set();
|
|
195648
195675
|
if (options.world) {
|
|
195649
195676
|
const worldSpec = bundle.scopeRegistry?.scopes.find((s) => s.token === "world");
|
|
@@ -195662,6 +195689,8 @@ var Engine = class _Engine {
|
|
|
195662
195689
|
sceneSharedNames.set(sceneId, names);
|
|
195663
195690
|
}
|
|
195664
195691
|
this.host = {
|
|
195692
|
+
logEnabled: options.log ?? false,
|
|
195693
|
+
emitEngine: (flow, event, scene) => this.emitEngine(flow, event, scene),
|
|
195665
195694
|
bundle,
|
|
195666
195695
|
emitIds,
|
|
195667
195696
|
strings,
|
|
@@ -196004,6 +196033,22 @@ var Engine = class _Engine {
|
|
|
196004
196033
|
flows() {
|
|
196005
196034
|
return [...this.flowsById.values()];
|
|
196006
196035
|
}
|
|
196036
|
+
/**
|
|
196037
|
+
* The SHARED kernel bags with the path each answers to in a log: the `@patter` globals,
|
|
196038
|
+
* and one per scene for the shared `@scene` props. Parity with the Storylet Engine's
|
|
196039
|
+
* listBags of the same name - it is what a state logger mounts.
|
|
196040
|
+
*
|
|
196041
|
+
* A stage bag's log path is `@scene:<sceneId>.`, not the bag's own `@scene.`: a property
|
|
196042
|
+
* is ADDRESSED relative to a flow's current scene, but a log spans scenes and has to say
|
|
196043
|
+
* which one. That is why a mount may override the bag's prefix.
|
|
196044
|
+
*
|
|
196045
|
+
* loadGame() replaces every bag, so re-enumerate after a load.
|
|
196046
|
+
*/
|
|
196047
|
+
listBags() {
|
|
196048
|
+
const mounts = [{ bag: this.host.shared.ownedBag("patter") }];
|
|
196049
|
+
for (const [sceneId, bag] of this.host.stageBags) mounts.push({ bag, pathPrefix: `@scene:${sceneId}.` });
|
|
196050
|
+
return mounts;
|
|
196051
|
+
}
|
|
196007
196052
|
/** Close (remove) a flow. The flow object is FINISHED, not merely unregistered, so a host still
|
|
196008
196053
|
* holding it cannot keep advancing it into the shared world (see {@link Flow.close}). */
|
|
196009
196054
|
closeFlow(id) {
|
|
@@ -196036,14 +196081,44 @@ var Engine = class _Engine {
|
|
|
196036
196081
|
}
|
|
196037
196082
|
/** The shared `@patter` properties, for a live state inspector: each with its ref, type, current
|
|
196038
196083
|
* value, declared default (for reset), and enum options. Mirrors the Unity / Godot ports. */
|
|
196084
|
+
/** The run's decisions, in order, each naming the flow it happened in. Empty unless the
|
|
196085
|
+
* run was opened with `log: true`. A flow's own log stays flow-local; this is the only
|
|
196086
|
+
* place a story spanning several flows reads as one sequence. */
|
|
196087
|
+
log() {
|
|
196088
|
+
return this.engineLog;
|
|
196089
|
+
}
|
|
196090
|
+
/** Drop the retained entries. `seq` does NOT restart: two reads of a log either side of a
|
|
196091
|
+
* clear still agree about what came first. */
|
|
196092
|
+
clearLog() {
|
|
196093
|
+
this.engineLog.length = 0;
|
|
196094
|
+
}
|
|
196095
|
+
/** Live tap on the run's decisions, for tooling that wants them as they happen rather than
|
|
196096
|
+
* retained. Returns its own unsubscribe. */
|
|
196097
|
+
onTrace(handler) {
|
|
196098
|
+
this.engineTraceHandlers.add(handler);
|
|
196099
|
+
return () => this.engineTraceHandlers.delete(handler);
|
|
196100
|
+
}
|
|
196101
|
+
emitEngine(flow, event, scene) {
|
|
196102
|
+
for (const h of this.engineTraceHandlers) h(flow, event);
|
|
196103
|
+
if (!this.host.logEnabled) return;
|
|
196104
|
+
this.engineLog.push({ ...event, flow, seq: this.engineLog.length, ...scene ? { scene } : {} });
|
|
196105
|
+
}
|
|
196039
196106
|
listProperties() {
|
|
196040
196107
|
return this.host.patterSharedDecls.map((d) => ({
|
|
196041
|
-
|
|
196108
|
+
name: d.name,
|
|
196109
|
+
// The qualified address, matching what the bag composes for every other scope.
|
|
196110
|
+
// `@gold` still resolves on input; it is the shorthand, not the address.
|
|
196111
|
+
path: `@patter.${d.name}`,
|
|
196042
196112
|
type: d.type,
|
|
196043
196113
|
values: d.values,
|
|
196044
196114
|
stages: d.stages,
|
|
196045
196115
|
value: this.getProperty(`@${d.name}`),
|
|
196046
|
-
default:
|
|
196116
|
+
default: defaultFor(d),
|
|
196117
|
+
// Part of the shared row. Always true here today: `toDecl` never sets it, because
|
|
196118
|
+
// Patter has no read-only shared property. The Storylet Engine does declare them
|
|
196119
|
+
// and its panels disable the editor accordingly, so the field is carried rather
|
|
196120
|
+
// than dropped - and the day a read-only @patter property exists, the row says so.
|
|
196121
|
+
writable: d.writable ?? true
|
|
196047
196122
|
}));
|
|
196048
196123
|
}
|
|
196049
196124
|
// @scene is scene-namespaced and needs a flow's current scene - silently
|
|
@@ -196076,7 +196151,7 @@ var Engine = class _Engine {
|
|
|
196076
196151
|
shared: this.host.shared.save(),
|
|
196077
196152
|
sharedVisits: Object.fromEntries(this.host.sharedVisits),
|
|
196078
196153
|
sharedSelectors: serialiseSelectors(this.host.sharedSelectors),
|
|
196079
|
-
stageBags: Object.fromEntries([...this.host.stageBags].map(([s, bag]) => [s,
|
|
196154
|
+
stageBags: Object.fromEntries([...this.host.stageBags].map(([s, bag]) => [s, bag.save()])),
|
|
196080
196155
|
flows
|
|
196081
196156
|
};
|
|
196082
196157
|
}
|
|
@@ -196089,7 +196164,13 @@ var Engine = class _Engine {
|
|
|
196089
196164
|
this.host.sharedSelectors.clear();
|
|
196090
196165
|
for (const [id, st] of deserialiseSelectors(save.sharedSelectors)) this.host.sharedSelectors.set(id, st);
|
|
196091
196166
|
this.host.stageBags.clear();
|
|
196092
|
-
for (const [s,
|
|
196167
|
+
for (const [s, values] of Object.entries(save.stageBags ?? {})) {
|
|
196168
|
+
const shared = this.host.sceneSharedNames.get(s) ?? /* @__PURE__ */ new Set();
|
|
196169
|
+
const decls = (this.host.bundle.scenes[s]?.sceneProps ?? []).filter((d) => shared.has(d.name.toLowerCase()));
|
|
196170
|
+
const bag = new PropertyBag(decls);
|
|
196171
|
+
bag.load(values);
|
|
196172
|
+
this.host.stageBags.set(s, bag);
|
|
196173
|
+
}
|
|
196093
196174
|
this.flowsById.clear();
|
|
196094
196175
|
for (const [id, snap] of Object.entries(save.flows)) {
|
|
196095
196176
|
const flow = new Flow(id, this.host, this.defaultSeed);
|
|
@@ -196132,6 +196213,7 @@ var Flow = class {
|
|
|
196132
196213
|
// The SHARED halves live on the host (`host.shared` / `host.stageBags`). Each
|
|
196133
196214
|
// resolver presents one merged scope, routing each property to its half by the
|
|
196134
196215
|
// declared `shared` flag.
|
|
196216
|
+
/** This flow's per-scene LOCAL scene props; see FlowHost.stageBags. */
|
|
196135
196217
|
sceneBags = /* @__PURE__ */ new Map();
|
|
196136
196218
|
patterResolver = {
|
|
196137
196219
|
get: (n) => this.host.patterSharedNames.has(n) ? this.host.shared.get("patter", n) : this.local.get("patter", n),
|
|
@@ -196145,13 +196227,13 @@ var Flow = class {
|
|
|
196145
196227
|
const s = this.currentSceneId;
|
|
196146
196228
|
if (s === null) return void 0;
|
|
196147
196229
|
const bag = this.host.sceneSharedNames.get(s)?.has(n) ? this.host.stageBags.get(s) : this.sceneBags.get(s);
|
|
196148
|
-
return bag?.
|
|
196230
|
+
return bag?.get(n);
|
|
196149
196231
|
},
|
|
196150
196232
|
set: (n, v) => {
|
|
196151
196233
|
const s = this.currentSceneId;
|
|
196152
196234
|
if (s === null) return;
|
|
196153
196235
|
const bag = this.host.sceneSharedNames.get(s)?.has(n) ? this.host.stageBags.get(s) : this.sceneBags.get(s);
|
|
196154
|
-
if (bag) bag
|
|
196236
|
+
if (bag) bag.set(n, v);
|
|
196155
196237
|
}
|
|
196156
196238
|
};
|
|
196157
196239
|
// The eval context is built ONCE: every constituent resolves live state at
|
|
@@ -196160,6 +196242,9 @@ var Flow = class {
|
|
|
196160
196242
|
// `local`/`sceneBags`/`currentSceneId`; the host callbacks read current flow
|
|
196161
196243
|
// fields). Rebuilding it per evaluation was the engine's hottest allocation.
|
|
196162
196244
|
evalCtx;
|
|
196245
|
+
flowLog = [];
|
|
196246
|
+
/** Monotonic across the flow's life; survives clearLog so two reads agree on order. */
|
|
196247
|
+
flowSeq = 0;
|
|
196163
196248
|
constructor(id, host2, seed) {
|
|
196164
196249
|
this.id = id;
|
|
196165
196250
|
this.host = host2;
|
|
@@ -196385,7 +196470,17 @@ var Flow = class {
|
|
|
196385
196470
|
this.stack.pop();
|
|
196386
196471
|
continue;
|
|
196387
196472
|
}
|
|
196473
|
+
const from = frame.index;
|
|
196388
196474
|
while (frame.index < children.length && !this.eligible(children[frame.index])) frame.index++;
|
|
196475
|
+
if (this.host.logEnabled && frame.index !== from) {
|
|
196476
|
+
this.emit({
|
|
196477
|
+
type: "select",
|
|
196478
|
+
group: frame.containerId,
|
|
196479
|
+
selector: "run",
|
|
196480
|
+
children: children.slice(from, frame.index + 1).map((c2, i) => ({ id: c2.id, eligible: from + i === frame.index })),
|
|
196481
|
+
picked: children[frame.index]?.id ?? null
|
|
196482
|
+
});
|
|
196483
|
+
}
|
|
196389
196484
|
if (frame.index >= children.length) {
|
|
196390
196485
|
this.stack.pop();
|
|
196391
196486
|
continue;
|
|
@@ -196393,6 +196488,34 @@ var Flow = class {
|
|
|
196393
196488
|
this.enterChild(children[frame.index++]);
|
|
196394
196489
|
}
|
|
196395
196490
|
}
|
|
196491
|
+
/**
|
|
196492
|
+
* THIS flow's own kernel bags: its not-shared `@patter` half and its per-scene `@scene`
|
|
196493
|
+
* props, each prefixed with the flow id so one path space holds every flow. The shared
|
|
196494
|
+
* halves are the Engine's listBags.
|
|
196495
|
+
*/
|
|
196496
|
+
listBags() {
|
|
196497
|
+
const mounts = [{ bag: this.local.ownedBag("patter"), pathPrefix: `${this.id}/@patter.` }];
|
|
196498
|
+
for (const [sceneId, bag] of this.sceneBags) mounts.push({ bag, pathPrefix: `${this.id}/@scene:${sceneId}.` });
|
|
196499
|
+
return mounts;
|
|
196500
|
+
}
|
|
196501
|
+
/** This flow's decisions, in order. Empty unless the run was opened with `log: true`.
|
|
196502
|
+
* The engine's log carries the same events tagged with the flow; this one is what a
|
|
196503
|
+
* single conversation reads as. */
|
|
196504
|
+
log() {
|
|
196505
|
+
return this.flowLog;
|
|
196506
|
+
}
|
|
196507
|
+
/** Drop the retained entries. `seq` keeps counting, so order survives a clear. */
|
|
196508
|
+
clearLog() {
|
|
196509
|
+
this.flowLog.length = 0;
|
|
196510
|
+
}
|
|
196511
|
+
/** Record one decision, on this flow's log and the engine's. Cheap to call with logging
|
|
196512
|
+
* off: the entry is never built. */
|
|
196513
|
+
emit(event) {
|
|
196514
|
+
const scene = this.currentSceneId ?? void 0;
|
|
196515
|
+
this.host.emitEngine(this.id, event, scene);
|
|
196516
|
+
if (!this.host.logEnabled) return;
|
|
196517
|
+
this.flowLog.push({ ...event, seq: this.flowSeq++, ...scene ? { scene } : {} });
|
|
196518
|
+
}
|
|
196396
196519
|
/** The options of a pending choice (empty when not at a choice point). */
|
|
196397
196520
|
getChoices() {
|
|
196398
196521
|
return this.pendingChoice?.options ?? [];
|
|
@@ -196405,6 +196528,7 @@ var Flow = class {
|
|
|
196405
196528
|
if (!option) throw new Error(`unknown choice option: ${id}`);
|
|
196406
196529
|
if (!option.eligible) throw new Error(`choice option is not eligible: ${id}`);
|
|
196407
196530
|
const node = choice.byId.get(id);
|
|
196531
|
+
this.emit({ type: "chose", group: choice.groupId, option: id });
|
|
196408
196532
|
this.pendingChoice = null;
|
|
196409
196533
|
this.pendingPromptBeat = this.host.replayPromptOnChoose ? this.promptBeatOf(node) ?? null : null;
|
|
196410
196534
|
this.pendingPromptOwnerId = this.pendingPromptBeat ? node.id : null;
|
|
@@ -196438,7 +196562,7 @@ var Flow = class {
|
|
|
196438
196562
|
return {
|
|
196439
196563
|
scopes: this.local.save(),
|
|
196440
196564
|
// owned scope "patter" = the NOT-shared globals (@scene saved separately)
|
|
196441
|
-
sceneBags: Object.fromEntries([...this.sceneBags].map(([s, bag]) => [s,
|
|
196565
|
+
sceneBags: Object.fromEntries([...this.sceneBags].map(([s, bag]) => [s, bag.save()])),
|
|
196442
196566
|
rngState: this.rngState,
|
|
196443
196567
|
visits: Object.fromEntries(this.visitCounts),
|
|
196444
196568
|
cursor: {
|
|
@@ -196476,7 +196600,13 @@ var Flow = class {
|
|
|
196476
196600
|
}
|
|
196477
196601
|
return { ...frame };
|
|
196478
196602
|
});
|
|
196479
|
-
this.sceneBags = new Map(Object.entries(snap.sceneBags ?? {}).map(([s,
|
|
196603
|
+
this.sceneBags = new Map(Object.entries(snap.sceneBags ?? {}).map(([s, values]) => {
|
|
196604
|
+
const shared = this.host.sceneSharedNames.get(s) ?? /* @__PURE__ */ new Set();
|
|
196605
|
+
const decls = (this.host.bundle.scenes[s]?.sceneProps ?? []).filter((d) => !shared.has(d.name.toLowerCase()));
|
|
196606
|
+
const bag = new PropertyBag(decls);
|
|
196607
|
+
bag.load(values);
|
|
196608
|
+
return [s, bag];
|
|
196609
|
+
}));
|
|
196480
196610
|
this.local = this.freshLocal();
|
|
196481
196611
|
this.local.load(snap.scopes);
|
|
196482
196612
|
this.activeSnippet = null;
|
|
@@ -196570,6 +196700,7 @@ var Flow = class {
|
|
|
196570
196700
|
byId.set(child.id, child);
|
|
196571
196701
|
}
|
|
196572
196702
|
if (options.length > 0) {
|
|
196703
|
+
this.emit({ type: "choice", group: group.id, options: options.map((o) => ({ id: o.id, eligible: o.eligible })) });
|
|
196573
196704
|
this.pendingChoice = { groupId: group.id, options, byId };
|
|
196574
196705
|
return;
|
|
196575
196706
|
}
|
|
@@ -196578,6 +196709,7 @@ var Flow = class {
|
|
|
196578
196709
|
this.enterChild(fallback2);
|
|
196579
196710
|
return;
|
|
196580
196711
|
}
|
|
196712
|
+
this.emit({ type: "dry", group: group.id });
|
|
196581
196713
|
this.host.onDryChoice?.(group.id);
|
|
196582
196714
|
}
|
|
196583
196715
|
// -- Jumps (jump / call-return) ----------------------------------------
|
|
@@ -196592,6 +196724,7 @@ var Flow = class {
|
|
|
196592
196724
|
* hard-ends the flow regardless of the callstack.
|
|
196593
196725
|
*/
|
|
196594
196726
|
enterTarget(to, mode) {
|
|
196727
|
+
this.emit({ type: "jump", to, mode });
|
|
196595
196728
|
if (to === "END") {
|
|
196596
196729
|
this.flowEnded = true;
|
|
196597
196730
|
this.stack = [];
|
|
@@ -196623,17 +196756,28 @@ var Flow = class {
|
|
|
196623
196756
|
}
|
|
196624
196757
|
// -- Selectors ------------------------------------------------------------
|
|
196625
196758
|
selectChild(group) {
|
|
196759
|
+
const verdicts = group.children.map((c2) => ({ id: c2.id, eligible: this.eligible(c2) }));
|
|
196626
196760
|
const eligible = group.children.filter((c2) => this.eligible(c2));
|
|
196627
|
-
|
|
196761
|
+
const order = group.options?.order ?? "sequential";
|
|
196762
|
+
const exhaust = group.options?.exhaust ?? "once";
|
|
196763
|
+
const trace = (picked) => {
|
|
196764
|
+
this.emit({
|
|
196765
|
+
type: "select",
|
|
196766
|
+
group: group.id,
|
|
196767
|
+
selector: group.selector ?? "default",
|
|
196768
|
+
...group.selector === "sequence" ? { order, exhaust } : {},
|
|
196769
|
+
children: verdicts,
|
|
196770
|
+
picked: picked?.id ?? null
|
|
196771
|
+
});
|
|
196772
|
+
return picked;
|
|
196773
|
+
};
|
|
196774
|
+
if (eligible.length === 0) return trace(null);
|
|
196628
196775
|
const st = this.selectorState(group);
|
|
196629
196776
|
switch (group.selector) {
|
|
196630
196777
|
case "branch":
|
|
196631
|
-
return eligible[0];
|
|
196632
|
-
case "sequence":
|
|
196633
|
-
|
|
196634
|
-
const exhaust = group.options?.exhaust ?? "once";
|
|
196635
|
-
return order === "shuffle" ? this.pickShuffle(eligible, exhaust, st) : order === "specificity" ? this.pickSpecificity(eligible, exhaust, st) : this.pickSequential(eligible, exhaust, st);
|
|
196636
|
-
}
|
|
196778
|
+
return trace(eligible[0]);
|
|
196779
|
+
case "sequence":
|
|
196780
|
+
return trace(order === "shuffle" ? this.pickShuffle(eligible, exhaust, st) : order === "specificity" ? this.pickSpecificity(eligible, exhaust, st) : this.pickSequential(eligible, exhaust, st));
|
|
196637
196781
|
case "run":
|
|
196638
196782
|
case "choice":
|
|
196639
196783
|
default:
|
|
@@ -196751,7 +196895,10 @@ var Flow = class {
|
|
|
196751
196895
|
// -- Effects + expressions ------------------------------------------------
|
|
196752
196896
|
runEffects(effects) {
|
|
196753
196897
|
for (const e of effects ?? []) {
|
|
196754
|
-
|
|
196898
|
+
const value = this.evalExpr(e.value);
|
|
196899
|
+
const prev = this.host.logEnabled ? this.getProperty(e.target) : void 0;
|
|
196900
|
+
this.setProperty(e.target, value);
|
|
196901
|
+
this.emit({ type: "write", target: e.target, value, ...prev !== void 0 ? { prev } : {} });
|
|
196755
196902
|
}
|
|
196756
196903
|
}
|
|
196757
196904
|
eligible(node) {
|
|
@@ -196895,7 +197042,7 @@ var Flow = class {
|
|
|
196895
197042
|
}
|
|
196896
197043
|
/** The per-flow registry: the NOT-shared `@patter` globals (the shared ones live on the host). */
|
|
196897
197044
|
freshLocal() {
|
|
196898
|
-
return new ScopeRegistry().defineOwned("patter", this.host.patterLocalDecls);
|
|
197045
|
+
return new ScopeRegistry().defineOwned("patter", this.host.patterLocalDecls, "@patter.");
|
|
196899
197046
|
}
|
|
196900
197047
|
/**
|
|
196901
197048
|
* Seed a scene's `@scene` props (spec §7). The not-shared props seed THIS flow's
|
|
@@ -196906,27 +197053,18 @@ var Flow = class {
|
|
|
196906
197053
|
*/
|
|
196907
197054
|
seedScene(scene) {
|
|
196908
197055
|
const shared = this.host.sceneSharedNames.get(scene.id) ?? /* @__PURE__ */ new Set();
|
|
197056
|
+
const props = scene.sceneProps ?? [];
|
|
196909
197057
|
if (!this.sceneBags.has(scene.id)) {
|
|
196910
|
-
|
|
196911
|
-
for (const decl of scene.sceneProps ?? []) {
|
|
196912
|
-
const name = decl.name.toLowerCase();
|
|
196913
|
-
if (!shared.has(name)) bag[name] = sceneDefault(decl);
|
|
196914
|
-
}
|
|
196915
|
-
this.sceneBags.set(scene.id, bag);
|
|
197058
|
+
this.sceneBags.set(scene.id, new PropertyBag(props.filter((d) => !shared.has(d.name.toLowerCase()))));
|
|
196916
197059
|
}
|
|
196917
197060
|
if (!this.host.stageBags.has(scene.id)) {
|
|
196918
|
-
|
|
196919
|
-
for (const decl of scene.sceneProps ?? []) {
|
|
196920
|
-
const name = decl.name.toLowerCase();
|
|
196921
|
-
if (shared.has(name)) bag[name] = sceneDefault(decl);
|
|
196922
|
-
}
|
|
196923
|
-
this.host.stageBags.set(scene.id, bag);
|
|
197061
|
+
this.host.stageBags.set(scene.id, new PropertyBag(props.filter((d) => shared.has(d.name.toLowerCase()))));
|
|
196924
197062
|
}
|
|
196925
197063
|
for (const decl of scene.sceneProps ?? []) {
|
|
196926
197064
|
if (!decl.temporary) continue;
|
|
196927
197065
|
const name = decl.name.toLowerCase();
|
|
196928
197066
|
const bag = shared.has(name) ? this.host.stageBags.get(scene.id) : this.sceneBags.get(scene.id);
|
|
196929
|
-
if (bag) bag
|
|
197067
|
+
if (bag) bag.set(name, defaultFor(decl));
|
|
196930
197068
|
}
|
|
196931
197069
|
}
|
|
196932
197070
|
};
|
|
@@ -196964,47 +197102,13 @@ function deserialiseSelectors(rec) {
|
|
|
196964
197102
|
function toDecl(decl) {
|
|
196965
197103
|
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default };
|
|
196966
197104
|
}
|
|
196967
|
-
function declDefault(d) {
|
|
196968
|
-
if (d.default !== void 0) return d.default;
|
|
196969
|
-
switch (d.type) {
|
|
196970
|
-
case "number":
|
|
196971
|
-
return 0;
|
|
196972
|
-
case "string":
|
|
196973
|
-
return "";
|
|
196974
|
-
case "flags":
|
|
196975
|
-
return [];
|
|
196976
|
-
case "enum":
|
|
196977
|
-
return d.values?.[0] ?? "";
|
|
196978
|
-
case "quality":
|
|
196979
|
-
return d.stages?.[0] ?? "";
|
|
196980
|
-
default:
|
|
196981
|
-
return false;
|
|
196982
|
-
}
|
|
196983
|
-
}
|
|
196984
197105
|
function toForeignDecl(decl) {
|
|
196985
197106
|
return { name: decl.name, type: decl.type, values: decl.values, stages: decl.stages, default: decl.default, writable: decl.writable };
|
|
196986
197107
|
}
|
|
196987
|
-
function hostScopeDefault(decl) {
|
|
196988
|
-
if (decl.default !== void 0) return decl.default;
|
|
196989
|
-
switch (decl.type) {
|
|
196990
|
-
case "boolean":
|
|
196991
|
-
return false;
|
|
196992
|
-
case "number":
|
|
196993
|
-
return 0;
|
|
196994
|
-
case "string":
|
|
196995
|
-
return "";
|
|
196996
|
-
case "flags":
|
|
196997
|
-
return [];
|
|
196998
|
-
case "enum":
|
|
196999
|
-
return decl.values?.[0] ?? "";
|
|
197000
|
-
case "quality":
|
|
197001
|
-
return decl.stages?.[0] ?? "";
|
|
197002
|
-
}
|
|
197003
|
-
}
|
|
197004
197108
|
function selfBackedResolver(decls) {
|
|
197005
197109
|
const key2 = (name) => name.toLowerCase();
|
|
197006
197110
|
const bag = /* @__PURE__ */ new Map();
|
|
197007
|
-
for (const d of decls) bag.set(key2(d.name),
|
|
197111
|
+
for (const d of decls) bag.set(key2(d.name), defaultFor(d));
|
|
197008
197112
|
return {
|
|
197009
197113
|
get: (name) => bag.get(key2(name)),
|
|
197010
197114
|
set: (name, value) => {
|
|
@@ -197012,23 +197116,6 @@ function selfBackedResolver(decls) {
|
|
|
197012
197116
|
}
|
|
197013
197117
|
};
|
|
197014
197118
|
}
|
|
197015
|
-
function sceneDefault(decl) {
|
|
197016
|
-
if (decl.default !== void 0) return decl.default;
|
|
197017
|
-
switch (decl.type) {
|
|
197018
|
-
case "boolean":
|
|
197019
|
-
return false;
|
|
197020
|
-
case "number":
|
|
197021
|
-
return 0;
|
|
197022
|
-
case "string":
|
|
197023
|
-
return "";
|
|
197024
|
-
case "flags":
|
|
197025
|
-
return [];
|
|
197026
|
-
case "enum":
|
|
197027
|
-
return decl.values?.[0] ?? "";
|
|
197028
|
-
case "quality":
|
|
197029
|
-
return decl.stages?.[0] ?? "";
|
|
197030
|
-
}
|
|
197031
|
-
}
|
|
197032
197119
|
function truthy(v) {
|
|
197033
197120
|
if (typeof v === "boolean") return v;
|
|
197034
197121
|
if (typeof v === "number") return v !== 0;
|
|
@@ -240856,7 +240943,7 @@ function writeTextFiles(files, encoding = "utf8") {
|
|
|
240856
240943
|
// package.json
|
|
240857
240944
|
var package_default = {
|
|
240858
240945
|
name: "@patterkit/cli",
|
|
240859
|
-
version: "0.3.
|
|
240946
|
+
version: "0.3.3",
|
|
240860
240947
|
description: "The `patter` CLI: validate, format, export, and play a Patter project.",
|
|
240861
240948
|
type: "module",
|
|
240862
240949
|
license: "MIT",
|
|
@@ -240886,7 +240973,7 @@ var package_default = {
|
|
|
240886
240973
|
},
|
|
240887
240974
|
dependencies: {
|
|
240888
240975
|
"@patterkit/core": "0.2.2",
|
|
240889
|
-
"@patterkit/ops": "0.
|
|
240976
|
+
"@patterkit/ops": "0.7.1",
|
|
240890
240977
|
"@wildwinter/simple-vc-lib": "^0.4.1"
|
|
240891
240978
|
}
|
|
240892
240979
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patterkit/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
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.2.2",
|
|
33
|
-
"@patterkit/ops": "0.
|
|
33
|
+
"@patterkit/ops": "0.7.1",
|
|
34
34
|
"@wildwinter/simple-vc-lib": "^0.4.1"
|
|
35
35
|
}
|
|
36
36
|
}
|