@patterkit/ops 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -2
- package/dist/index.js +35 -16
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ProjectFile, Scene, LocaleFile, AuthoringFile, Bundle, GameData, CoverageDriver, DocumentationClass, DocLine } from '@patterkit/model';
|
|
1
|
+
import { ProjectFile, Scene, LocaleFile, AuthoringFile, Bundle, GameData, CoverageDriver, GrammaticalGender, DocumentationClass, DocLine } from '@patterkit/model';
|
|
2
2
|
import { ValidationIssue } from '@patterkit/core';
|
|
3
3
|
import { ConditionIssue } from '@patterkit/compiler';
|
|
4
4
|
import { ChoiceOption } from '@patterkit/runtime';
|
|
@@ -406,10 +406,13 @@ interface LocEntry {
|
|
|
406
406
|
translation: string;
|
|
407
407
|
/** Localiser-channel notes for this id (DocLine text, ancestors outermost-first). */
|
|
408
408
|
comments: string[];
|
|
409
|
-
/** Best-effort context for the translator.
|
|
409
|
+
/** Best-effort context for the translator. `gender` is the speaker's grammatical gender, looked up
|
|
410
|
+
* from the cast - what a gendered language needs to inflect the line. Export-only: `applyLoc` never
|
|
411
|
+
* reads it back, it is regenerated from the cast on every export. */
|
|
410
412
|
context?: {
|
|
411
413
|
character?: string;
|
|
412
414
|
kind?: string;
|
|
415
|
+
gender?: GrammaticalGender;
|
|
413
416
|
};
|
|
414
417
|
/** Translated, but the source changed since (source modifiedAt > localisedAt[locale]). */
|
|
415
418
|
stale: boolean;
|
package/dist/index.js
CHANGED
|
@@ -332,7 +332,7 @@ function bundleOutputPath(loaded) {
|
|
|
332
332
|
import { canonicalStringify } from "@patterkit/core";
|
|
333
333
|
|
|
334
334
|
// src/playable-runtime.ts
|
|
335
|
-
var PLAYABLE_RUNTIME_JS = '"use strict";var Patterplay=(()=>{var A=Object.defineProperty;var ae=Object.getOwnPropertyDescriptor;var ce=Object.getOwnPropertyNames;var le=Object.prototype.hasOwnProperty;var pe=(s,e)=>{for(var t in e)A(s,t,{get:e[t],enumerable:!0})},de=(s,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of ce(e))!le.call(s,n)&&n!==t&&A(s,n,{get:()=>e[n],enumerable:!(r=ae(e,n))||r.enumerable});return s};var ge=s=>de(A({},"__esModule",{value:!0}),s);var xe={};pe(xe,{Engine:()=>B,Flow:()=>v,buildTagIndex:()=>O,effectiveGameData:()=>se,gameDataFields:()=>ne,gameDataValue:()=>j});function S(s){switch(s[0]){case"b":return{kind:"bool",value:s[1]};case"n":return{kind:"number",value:s[1]};case"s":return{kind:"string",value:s[1]};case"sv":return{kind:"scopedvar",scope:s[1],name:s[2]};case"u":return{kind:"unary",op:s[1],operand:S(s[2])};case"bin":return{kind:"binary",op:s[1],left:S(s[2]),right:S(s[3])};case"call":{let e=s.slice(2).map(S);return{kind:"call",name:s[1],args:e}}case"fd":return{kind:"flagdelta",sign:s[1],name:s[2]}}}var p=class extends Error{constructor(e){super(e),this.name="EvalError"}};function w(s,e,t){let r=new Map(t.scopes.map(i=>[i.token,i.missing??"false"])),n=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 p(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{let o=t.functions[i.name];if(!o)throw new p(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:n,ctx:e})}case"flagdelta":throw new p("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=n(i.operand);if(typeof a!="boolean")throw new p(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=n(i.operand);if(typeof o!="number")throw new p(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let l=n(i.left);if(typeof l!="boolean")throw new p(`\'and\' requires boolean operands, left is ${typeof l}`);if(!l)return!1;let c=n(i.right);if(typeof c!="boolean")throw new p(`\'and\' requires boolean operands, right is ${typeof c}`);return c}if(i.op==="or"){let l=n(i.left);if(typeof l!="boolean")throw new p(`\'or\' requires boolean operands, left is ${typeof l}`);if(l)return!0;let c=n(i.right);if(typeof c!="boolean")throw new p(`\'or\' requires boolean operands, right is ${typeof c}`);return c}let o=n(i.left),a=n(i.right);switch(i.op){case"==":return U(o,a);case"!=":return!U(o,a);case">":return y(o,a,">"),o>a;case">=":return y(o,a,">="),o>=a;case"<":return y(o,a,"<"),o<a;case"<=":return y(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new p(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return y(o,a,"-"),o-a;case"*":return y(o,a,"*"),o*a;case"/":if(y(o,a,"/"),a===0)throw new p("division by zero");return o/a}}}};return n(s)}function U(s,e){if(Array.isArray(s)||Array.isArray(e)){if(!Array.isArray(s)||!Array.isArray(e)||s.length!==e.length)return!1;for(let t=0;t<s.length;t++)if(s[t]!==e[t])return!1;return!0}return s===e}function y(s,e,t){if(typeof s!="number"||typeof e!="number")throw new p(`\'${t}\' requires numeric operands, got ${typeof s} and ${typeof e}`)}var x=class{scopes=new Map;defineOwned(e,t){this.assertFree(e);let r={},n=new Map;for(let i of t){let o=i.name.toLowerCase();n.set(o,i),r[o]=i.default??W(i)}return this.scopes.set(e,{kind:"owned",bag:r,decls:n}),this}reseedOwned(e,t){let r=this.scopes.get(e);if(!r||r.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);for(let n of Object.keys(r.bag))delete r.bag[n];r.decls.clear();for(let n of t){let i=n.name.toLowerCase();r.decls.set(i,n),r.bag[i]=n.default??W(n)}return this}defineForeign(e,t,r=[],n=!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:n}),this}has(e){return this.scopes.has(e)}get(e,t){let r=this.scopes.get(e);if(!r)return;let n=t.toLowerCase();return r.kind==="owned"?r.bag[n]:r.resolver.get(n)}set(e,t,r){let n=this.scopes.get(e);if(!n)throw new Error(`unknown scope \'@${e}\'`);let i=t.toLowerCase();if(!this.writable(n,i))throw new Error(`\'@${e}.${t}\' is read-only`);n.kind==="owned"?n.bag[i]=r:n.resolver.set(i,r)}writable(e,t){return e.kind==="owned"?e.decls.get(t)?.writable??!0:e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}toEvalContext(e){let t={};for(let[r,n]of this.scopes)t[r]=n.kind==="owned"?n.bag:n.resolver;return{scopes:t,host:e}}toSchema(){let e=new Map;for(let[t,r]of this.scopes){if(r.decls.size===0)continue;let n=new Map;for(let[i,o]of r.decls)n.set(i,{type:o.type,enumValues:o.values});e.set(t,n)}return{properties:e}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]={...r.bag});return e}load(e){for(let[t,r]of Object.entries(e)){let n=this.scopes.get(t);n?.kind==="owned"&&Object.assign(n.bag,r)}}assertFree(e){if(this.scopes.has(e))throw new Error(`scope \'@${e}\' is already registered`)}};function W(s){if(s.default!==void 0)return s.default;switch(s.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return s.values?.[0]??"";case"flags":return[]}}function k(s){return s.ctx.host??{}}var I={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(s,e){if(s.length!==2)throw new p("random(a, b) requires exactly 2 arguments");let t=k(e).nextRandom;if(!t)throw new p("random() called without a PRNG in context");let r=e.evaluate(s[0]),n=e.evaluate(s[1]);if(typeof r!="number"||typeof n!="number")throw new p("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(n))throw new p("random(a, b) arguments must be integers");let i=Math.min(r,n),o=Math.max(r,n);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:K("check_flags"),eval(s,e){let t=J(s[0],e,"check_flags");for(let r=1;r<s.length;r++){let n=s[r];if(n.kind!=="flagdelta")throw new p("check_flags() flag args must be +flagName or -flagName");if(n.sign==="+"?!t.includes(n.name):t.includes(n.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:K("set_flags"),eval(s,e){let t=[...J(s[0],e,"set_flags")];for(let r=1;r<s.length;r++){let n=s[r];if(n.kind!=="flagdelta")throw new p("set_flags() flag args must be +flagName or -flagName");if(n.sign==="+")t.includes(n.name)||t.push(n.name);else{let i=t.indexOf(n.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:D("visits"),eval:(s,e)=>k(e).visits?.(C(s,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:D("seen"),eval:(s,e)=>(k(e).visits?.(C(s,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:D("patter_visits"),eval:(s,e)=>k(e).patterVisits?.(C(s,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:D("patter_seen"),eval:(s,e)=>(k(e).patterVisits?.(C(s,e,"patter_seen"))??0)>0}}};function V(s,e){let t=s.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function C(s,e,t){let r=e.evaluate(s[0]);if(typeof r!="string")throw new p(`${t}(id) requires a string node id`);return r}function D(s){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:`${s}(id): the argument must be a string id literal (a scene / block / node id)`})}}function J(s,e,t){if(!s)throw new p(`${t}() requires at least one argument (the flags variable)`);let r=e.evaluate(s);if(Array.isArray(r))return r;if(r===!1||r===null||r===void 0)return[];throw new p(`${t}() first argument must be a flags property`)}function K(s){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:`${s}(): first argument must be a flags property reference (@name or @scope.name)`});return}let n=t.schema.properties.get(r.scope)?.get(r.name);if(n&&n.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:`${s}(): \'@${i}\' is not a flags property (got ${n.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:`${s}(): argument ${i+1} must be +flagName or -flagName`}):n?.type==="flags"&&n.enumValues&&!n.enumValues.includes(o.name)&&t.report({path:[...t.path,"args",i],kind:"unknown-flag-name",severity:"error",message:`${s}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var ue=/^@[A-Za-z0-9_.]+$/;function*fe(s){let e="",t=0;for(;t<s.length;){let r=s[t];if(r==="{"&&s[t+1]==="{"){e+="{",t+=2;continue}if(r==="}"&&s[t+1]==="}"){e+="}",t+=2;continue}if(r==="{"){let n=s.indexOf("}",t+1);if(n!==-1){let i=s.slice(t,n+1),o=s.slice(t+1,n).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:ue.test(o)?o:void 0},t=n+1;continue}e+=i,t=n+1;continue}}e+=r,t+=1}e&&(yield{kind:"text",value:e})}function he(s){return Array.isArray(s)?s.join(", "):typeof s=="boolean"?s?"true":"false":String(s)}function me(s){return s===" "||s===" "||s===`\n`||s==="\\r"||s==="\\f"||s==="\\v"}function Se(s){let e="",t=!1;for(let r of s){if(me(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function z(s,e,t){if(e.length===0||s.indexOf(e)<0)return s;let r="",n=0,i=!1;for(;n<s.length;){if(s.startsWith(e,n)){let o=s.indexOf(t,n+e.length);if(o>=0){n=o+t.length,i=!0;continue}r+=s.slice(n);break}r+=s[n],n+=1}return i?Se(r):s}function X(s,e){if(s.indexOf("{")<0)return s;let t="";for(let r of fe(s)){if(r.kind==="text"){t+=r.value;continue}if(!r.ref){t+=r.raw;continue}let n=e(r.ref);t+=n===void 0?"":he(n)}return t}function E(s,e){for(let t of s){e(t);let r=t.children;r&&E(r,e)}}function ye(s){return s.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function h(s){let e=s.gameId?.trim();return e||ye(s.name)}function F(s){return`cast:${s}`}var T={open:"[",close:"]"},Z="SFX";function R(s){let e=new Set,t=[];for(let r of s)e.has(r)||(e.add(r),t.push(r));return t}function O(s){let e=new Map,t=(r,n)=>{let i=R([...n,...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,R([...i,...o.tags??[]]))};for(let r of Object.values(s.scenes)){let n=R(r.tags??[]);e.set(r.id,n);for(let i of r.blocks){let o=R([...n,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var Q=new WeakMap,B=class s{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,n=e.strings;this.allStrings=n,this.currentLocale=r;let i=n[r]??{},o=n[e.locales.default]??{},a=e.localisation,l=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 c=new Map;for(let d of e.cast??[])d.displayName&&c.set(d.name,d.displayName);this.defaultSeed=(t.seed??2654435769)>>>0;let g=new Map,u=new Map,L=new Map;for(let[d,m]of Object.entries(e.scenes)){this.sceneGameIdToId.set(h(m),d);let b=new Map;for(let f of m.blocks)u.set(f.id,{sceneId:d}),L.set(f.id,f),b.set(h(f),f.id),E(f.children,q=>g.set(q.id,q));this.blockGameIdToId.set(d,b)}let $=e.properties??[],N=$.filter(d=>d.shared??!0).map(Y),ie=$.filter(d=>!(d.shared??!0)).map(Y),oe=new Set(N.map(d=>d.name.toLowerCase())),P=new x().defineOwned("patter",N),H=new Set;if(t.world){let d=e.scopeRegistry?.scopes.find(b=>b.token==="world"),m=(d?.declarations??[]).map(ee);P.defineForeign("world",t.world,m,d?.writable??!0),H.add("world")}for(let d of e.scopeRegistry?.scopes??[]){if(H.has(d.token))continue;let m=(d.declarations??[]).map(ee);P.defineForeign(d.token,we(d.declarations??[]),m,d.writable??!0)}let _=new Map;for(let[d,m]of Object.entries(e.scenes)){let b=new Set((m.sceneProps??[]).filter(f=>f.shared??!1).map(f=>f.name.toLowerCase()));_.set(d,b)}this.host={bundle:e,emitIds:l,strings:i,defaultStrings:o,castDisplay:c,nodeIndex:g,blockIndex:u,blockById:L,tagIndex:O(e),shared:P,patterSharedDecls:N,patterLocalDecls:ie,patterSharedNames:oe,sceneSharedNames:_,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??T).open,captionClose:(e.closedCaptions??T).close,captionCharacter:e.closedCaptions?.character||Z,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),n=new s(e,this.creationOptions);try{return n.loadGame(t),r(n)}catch{let i=new s(e,this.creationOptions);for(let[o,a]of Object.entries(t.flows)){let l=a.cursor.currentSceneId;try{i.openFlow(o,l!==null?{scene:l}:{})}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),n=this.resolveBlockRef(r,t.block),i=new v(e,this.host,t.seed??this.defaultSeed);return this.flowsById.set(e,i),i.start(r,n),i}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),n=this.resolveBlockRef(r,t);return(n!=null?this.host.tagIndex.get(n):void 0)??[]}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)E(r.children,n=>{if(n.type==="snippet")for(let i of n.beats??[])e.push({sceneId:t.id,blockId:r.id,snippetId:n.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 n=this.host.defaultStrings[F(e.character)]??this.host.castDisplay.get(e.character);n!==void 0&&(r.characterName=n)}e.direction!==void 0&&(r.direction=e.direction)}if(e.kind==="line"||e.kind==="text"){let n=this.host.defaultStrings[e.id];n!==void 0&&(r.text=n)}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.delete(e)}reset(){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:n}=this.splitShared(e);this.host.shared.set(r,n,t)}listProperties(){return this.host.patterSharedDecls.map(e=>({ref:`@${e.name}`,type:e.type,values:e.values,value:this.getProperty(`@${e.name}`),default:be(e)}))}splitShared(e){let t=this.host.refSplitCache.get(e);if(t||(t=V(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:te(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 re(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 n=new v(t,this.host,this.defaultSeed);n.restore(r),this.flowsById.set(t,n)}}},v=class{id;host;local;rngState;started=!1;flowEnded=!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 n=this.host.sceneSharedNames.get(r)?.has(e)?this.host.stageBags.get(r):this.sceneBags.get(r);n&&(n[e]=t)}};evalCtx;constructor(e,t,r){this.id=e,this.host=t,this.rngState=r>>>0,this.local=this.freshLocal();let n={...t.shared.toEvalContext().scopes};n.patter=this.patterResolver,n.scene=this.sceneResolver,this.evalCtx={scopes:n,host:{nextRandom:this.rng,visits:i=>this.visitCounts.get(i)??0,patterVisits:i=>this.host.sharedVisits.get(i)??0}}}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],n=r?this.host.bundle.scenes[r]:void 0;if(!n)throw new Error(r?`unknown scene: ${r}`:"no scenes in bundle");this.enterSceneSetup(r);let i=n.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)}get currentScene(){return this.currentSceneId}advance(){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 n=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(n);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 n=t.byId.get(e);this.pendingChoice=null,this.pendingPromptBeat=this.host.replayPromptOnChoose?this.promptBeatOf(n)??null:null,this.pendingPromptOwnerId=this.pendingPromptBeat?n.id:null,this.enterChild(n)}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:n}=this.splitRef(e);if(r==="patter")this.patterResolver.set(n,t);else if(r==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);this.sceneResolver.set(n,t)}else this.host.shared.set(r,n,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:te(this.selectors)}}}restore(e){this.rngState=e.rngState>>>0,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:n,...i}=r;if(n!==void 0){let o=this.childrenOf(i.containerId)?.findIndex(a=>a.id===n)??-1;if(o>=0)return{...i,index:o}}return{...i}}),this.sceneBags=new Map(Object.entries(e.sceneBags??{}).map(([r,n])=>[r,{...n}])),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=re(t.selectors),this.pendingChoice=null,t.pendingChoice!==null){let r=new Map,n=[];for(let i of t.pendingChoice.options){let o=this.host.nodeIndex.get(i.id);o&&(r.set(i.id,o),n.push({...i}))}n.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:n,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,n=[];for(let o of e.children){if(o.fallback===!0){n.push(o);continue}if(o.sticky!==!0&&(this.visitCounts.get(o.id)??0)>=1)continue;let a=this.eligible(o),l=o.secretUntilEligible===!0;!a&&l||(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=n.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,n,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,n=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,n=e}this.enter(n);let o={sceneId:r,containerId:n,index:0};t==="call"?this.stack.push(o):this.stack=[o]}selectChild(e){let t=e.children.filter(n=>this.eligible(n));if(t.length===0)return null;let r=this.selectorState(e);switch(e.selector){case"branch":return t[0];case"sequence":{let n=e.options?.order??"sequential",i=e.options?.exhaust??"once";return n==="shuffle"?this.pickShuffle(t,i,r):n==="specificity"?this.pickSpecificity(t,i,r):this.pickSequential(t,i,r)}default:return null}}pickSequential(e,t,r){let n=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%n]:i<n?e[i]:t==="stick"?e[n-1]:null}pickShuffle(e,t,r){let n=e.length,i=t==="stick",o=()=>(i?e.slice(0,n-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[n-1];return r.last=u.id,u}r.bag=o()}let a=r.bag,l=r.last!==void 0&&a.length>1?a.indexOf(r.last):-1,c=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&c>=l&&c++;let g=a[c];return a.splice(c,1),r.last=g,e.find(u=>u.id===g)}pickSpecificity(e,t,r){let n=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(g=>g.id));let c=new Set(r.bag);if(n=e.filter(g=>c.has(g.id)),n.length===0)return t==="stick"&&r.last!==void 0?e.find(g=>g.id===r.last)??null:null}let i=-1,a=n.map(c=>{let g=this.specScore(c);return g>i&&(i=g),{c,s:g}}).filter(c=>c.s===i).map(c=>c.c),l;if(a.length===1)l=a[0];else{let c=r.last!==void 0?a.findIndex(u=>u.id===r.last):-1,g=Math.floor(this.rng()*(c>=0?a.length-1:a.length));c>=0&&g>=c&&g++,l=a[g]}return t!=="repeat"&&(r.bag=r.bag.filter(c=>c!==l.id)),r.last=l.id,l}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0):0}matchedSpec(e,t){if(e.kind==="binary"&&(e.op==="and"||e.op==="or")){let r=e.op==="and"===t,n=this.matchedSpec(e.left,t),i=this.matchedSpec(e.right,t);return r?n>0&&i>0?n+i:0:Math.max(n,i)}if(e.kind==="unary"&&e.op==="not")return this.matchedSpec(e.operand,!t);if(e.kind==="call"&&e.name==="check_flags"){let r=Math.max(1,e.args.length-1),n=M(w(e,this.evalCtx,I));return t?n?r:0:n?0:1}return M(w(e,this.evalCtx,I))===t?1:0}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?M(this.evalExpr(e.condition)):!0}evalExpr(e){return w(this.conditionAst(e),this.evalCtx,I)}conditionAst(e){let t=Q.get(e);return t||(t=S(e.ast),Q.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=this.rngState+1831565813|0;this.rngState=e;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};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 n=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?n:this.interpolate(n)),l=i&&a.length===0;return{type:"line",id:e.id,text:a,character:l?void 0:e.character,characterName:l?void 0:this.resolveCharacterName(e.character),direction:l?void 0:e.direction,gameData:e.gameData,...r}}}}interpolate(e){return X(e,t=>this.getProperty(t))}stripCaptions(e){return z(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 E(e,r=>{!t&&r.type==="snippet"&&(r.beats??[]).some(n=>n.kind==="line"||n.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=F(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=V(e,r=>r==="scene"||this.host.shared.has(r)),this.host.refSplitCache.set(e,t)),t}freshLocal(){return new x().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 n of e.sceneProps??[]){let i=n.name.toLowerCase();t.has(i)||(r[i]=G(n))}this.sceneBags.set(e.id,r)}if(!this.host.stageBags.has(e.id)){let r={};for(let n of e.sceneProps??[]){let i=n.name.toLowerCase();t.has(i)&&(r[i]=G(n))}this.host.stageBags.set(e.id,r)}for(let r of e.sceneProps??[]){if(!r.temporary)continue;let n=r.name.toLowerCase(),i=t.has(n)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);i&&(i[n]=G(r))}}};function te(s){let e={};for(let[t,r]of s){let n={};r.seq!==void 0&&(n.seq=r.seq),r.bag&&(n.bag=[...r.bag]),r.last!==void 0&&(n.last=r.last),e[t]=n}return e}function re(s){let e=new Map;for(let[t,r]of Object.entries(s??{})){let n={};r.seq!==void 0&&(n.seq=r.seq),r.bag&&(n.bag=[...r.bag]),r.last!==void 0&&(n.last=r.last),e.set(t,n)}return e}function Y(s){return{name:s.name,type:s.type,values:s.values,default:s.default}}function be(s){if(s.default!==void 0)return s.default;switch(s.type){case"number":return 0;case"string":return"";case"flags":return[];case"enum":return s.values?.[0]??"";default:return!1}}function ee(s){return{name:s.name,type:s.type,values:s.values,default:s.default,writable:s.writable}}function ve(s){if(s.default!==void 0)return s.default;switch(s.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"flags":return[];case"enum":return s.values?.[0]??""}}function we(s){let e=new Map;for(let t of s)e.set(t.name,ve(t));return{get:t=>e.get(t),set:(t,r)=>{e.set(t,r)}}}function G(s){if(s.default!==void 0)return s.default;switch(s.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"flags":return[];case"enum":return s.values?.[0]??""}}function M(s){return typeof s=="boolean"?s:typeof s=="number"?s!==0:typeof s=="string"?s!=="":s.length>0}function ne(s,e){return s.gameDataFields?.[e]??[]}function j(s,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:s.find(r=>r.name===t)?.default}function se(s,e){let t={};for(let r of s){let n=j(s,e,r.name);n!==void 0&&(t[r.name]=n)}for(let[r,n]of Object.entries(e??{}))r in t||(t[r]=n);return t}return ge(xe);})();\n//# sourceMappingURL=patterplay.min.js.map';
|
|
335
|
+
var PLAYABLE_RUNTIME_JS = '"use strict";var Patterplay=(()=>{var P=Object.defineProperty;var le=Object.getOwnPropertyDescriptor;var pe=Object.getOwnPropertyNames;var de=Object.prototype.hasOwnProperty;var ue=(s,e)=>{for(var t in e)P(s,t,{get:e[t],enumerable:!0})},ge=(s,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of pe(e))!de.call(s,n)&&n!==t&&P(s,n,{get:()=>e[n],enumerable:!(r=le(e,n))||r.enumerable});return s};var fe=s=>ge(P({},"__esModule",{value:!0}),s);var De={};ue(De,{Engine:()=>B,Flow:()=>v,buildTagIndex:()=>O,effectiveGameData:()=>oe,gameDataFields:()=>ie,gameDataValue:()=>j});function S(s){switch(s[0]){case"b":return{kind:"bool",value:s[1]};case"n":return{kind:"number",value:s[1]};case"s":return{kind:"string",value:s[1]};case"sv":return{kind:"scopedvar",scope:s[1],name:s[2]};case"u":return{kind:"unary",op:s[1],operand:S(s[2])};case"bin":return{kind:"binary",op:s[1],left:S(s[2]),right:S(s[3])};case"call":{let e=s.slice(2).map(S);return{kind:"call",name:s[1],args:e}}case"fd":return{kind:"flagdelta",sign:s[1],name:s[2]}}}var p=class extends Error{constructor(e){super(e),this.name="EvalError"}};function E(s,e,t){let r=new Map(t.scopes.map(i=>[i.token,i.missing??"false"])),n=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 p(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{let o=t.functions[i.name];if(!o)throw new p(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:n,ctx:e})}case"flagdelta":throw new p("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=n(i.operand);if(typeof a!="boolean")throw new p(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=n(i.operand);if(typeof o!="number")throw new p(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let l=n(i.left);if(typeof l!="boolean")throw new p(`\'and\' requires boolean operands, left is ${typeof l}`);if(!l)return!1;let c=n(i.right);if(typeof c!="boolean")throw new p(`\'and\' requires boolean operands, right is ${typeof c}`);return c}if(i.op==="or"){let l=n(i.left);if(typeof l!="boolean")throw new p(`\'or\' requires boolean operands, left is ${typeof l}`);if(l)return!0;let c=n(i.right);if(typeof c!="boolean")throw new p(`\'or\' requires boolean operands, right is ${typeof c}`);return c}let o=n(i.left),a=n(i.right);switch(i.op){case"==":return U(o,a);case"!=":return!U(o,a);case">":return y(o,a,">"),o>a;case">=":return y(o,a,">="),o>=a;case"<":return y(o,a,"<"),o<a;case"<=":return y(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new p(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return y(o,a,"-"),o-a;case"*":return y(o,a,"*"),o*a;case"/":if(y(o,a,"/"),a===0)throw new p("division by zero");return o/a}}}};return n(s)}function U(s,e){if(Array.isArray(s)||Array.isArray(e)){if(!Array.isArray(s)||!Array.isArray(e)||s.length!==e.length)return!1;for(let t=0;t<s.length;t++)if(s[t]!==e[t])return!1;return!0}return s===e}function y(s,e,t){if(typeof s!="number"||typeof e!="number")throw new p(`\'${t}\' requires numeric operands, got ${typeof s} and ${typeof e}`)}var he={name:"check_flags",count:s=>Math.max(1,s.args.length-1)},me=[he];function W(s,e,t){let r=t?.countingCalls??me;return C(s,t?.want??!0,e,r)}function C(s,e,t,r){if(s.kind==="binary"&&(s.op==="and"||s.op==="or")){let n=C(s.left,e,t,r),i=C(s.right,e,t,r);return s.op==="and"===e?n>0&&i>0?n+i:0:Math.max(n,i)}if(s.kind==="unary"&&s.op==="not")return C(s.operand,!e,t,r);if(s.kind==="call"){let n=r.find(i=>i.name===s.name);if(n){let i=n.count(s),o=t(s);return e?o?i:0:o?0:1}}return t(s)===e?1:0}var w=class{scopes=new Map;defineOwned(e,t){this.assertFree(e);let r={},n=new Map;for(let i of t){let o=i.name.toLowerCase();n.set(o,i),r[o]=i.default??K(i)}return this.scopes.set(e,{kind:"owned",bag:r,decls:n}),this}reseedOwned(e,t){let r=this.scopes.get(e);if(!r||r.kind!=="owned")throw new Error(`\'@${e}\' is not an owned scope`);for(let n of Object.keys(r.bag))delete r.bag[n];r.decls.clear();for(let n of t){let i=n.name.toLowerCase();r.decls.set(i,n),r.bag[i]=n.default??K(n)}return this}defineForeign(e,t,r=[],n=!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:n}),this}has(e){return this.scopes.has(e)}get(e,t){let r=this.scopes.get(e);if(!r)return;let n=t.toLowerCase();return r.kind==="owned"?r.bag[n]:r.resolver.get(n)}set(e,t,r){let n=this.scopes.get(e);if(!n)throw new Error(`unknown scope \'@${e}\'`);let i=t.toLowerCase();if(!this.writable(n,i))throw new Error(`\'@${e}.${t}\' is read-only`);n.kind==="owned"?n.bag[i]=r:n.resolver.set(i,r)}writable(e,t){return e.kind==="owned"?e.decls.get(t)?.writable??!0:e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}toEvalContext(e){let t={};for(let[r,n]of this.scopes)t[r]=n.kind==="owned"?n.bag:n.resolver;return{scopes:t,host:e}}toSchema(){let e=new Map;for(let[t,r]of this.scopes){if(r.decls.size===0)continue;let n=new Map;for(let[i,o]of r.decls)n.set(i,{type:o.type,enumValues:o.values});e.set(t,n)}return{properties:e}}save(){let e={};for(let[t,r]of this.scopes)r.kind==="owned"&&(e[t]={...r.bag});return e}load(e){for(let[t,r]of Object.entries(e)){let n=this.scopes.get(t);n?.kind==="owned"&&Object.assign(n.bag,r)}}assertFree(e){if(this.scopes.has(e))throw new Error(`scope \'@${e}\' is already registered`)}};function K(s){if(s.default!==void 0)return s.default;switch(s.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"enum":return s.values?.[0]??"";case"flags":return[]}}function x(s){return s.ctx.host??{}}var T={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(s,e){if(s.length!==2)throw new p("random(a, b) requires exactly 2 arguments");let t=x(e).nextRandom;if(!t)throw new p("random() called without a PRNG in context");let r=e.evaluate(s[0]),n=e.evaluate(s[1]);if(typeof r!="number"||typeof n!="number")throw new p("random(a, b) arguments must be numbers");if(!Number.isInteger(r)||!Number.isInteger(n))throw new p("random(a, b) arguments must be integers");let i=Math.min(r,n),o=Math.max(r,n);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:z("check_flags"),eval(s,e){let t=J(s[0],e,"check_flags");for(let r=1;r<s.length;r++){let n=s[r];if(n.kind!=="flagdelta")throw new p("check_flags() flag args must be +flagName or -flagName");if(n.sign==="+"?!t.includes(n.name):t.includes(n.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:z("set_flags"),eval(s,e){let t=[...J(s[0],e,"set_flags")];for(let r=1;r<s.length;r++){let n=s[r];if(n.kind!=="flagdelta")throw new p("set_flags() flag args must be +flagName or -flagName");if(n.sign==="+")t.includes(n.name)||t.push(n.name);else{let i=t.indexOf(n.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:I("visits"),eval:(s,e)=>x(e).visits?.(D(s,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:I("seen"),eval:(s,e)=>(x(e).visits?.(D(s,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:I("patter_visits"),eval:(s,e)=>x(e).patterVisits?.(D(s,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:I("patter_seen"),eval:(s,e)=>(x(e).patterVisits?.(D(s,e,"patter_seen"))??0)>0}}};function F(s,e){let t=s.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function D(s,e,t){let r=e.evaluate(s[0]);if(typeof r!="string")throw new p(`${t}(id) requires a string node id`);return r}function I(s){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:`${s}(id): the argument must be a string id literal (a scene / block / node id)`})}}function J(s,e,t){if(!s)throw new p(`${t}() requires at least one argument (the flags variable)`);let r=e.evaluate(s);if(Array.isArray(r))return r;if(r===!1||r===null||r===void 0)return[];throw new p(`${t}() first argument must be a flags property`)}function z(s){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:`${s}(): first argument must be a flags property reference (@name or @scope.name)`});return}let n=t.schema.properties.get(r.scope)?.get(r.name);if(n&&n.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:`${s}(): \'@${i}\' is not a flags property (got ${n.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:`${s}(): argument ${i+1} must be +flagName or -flagName`}):n?.type==="flags"&&n.enumValues&&!n.enumValues.includes(o.name)&&t.report({path:[...t.path,"args",i],kind:"unknown-flag-name",severity:"error",message:`${s}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var Se=/^@[A-Za-z0-9_.]+$/;function*ye(s){let e="",t=0;for(;t<s.length;){let r=s[t];if(r==="{"&&s[t+1]==="{"){e+="{",t+=2;continue}if(r==="}"&&s[t+1]==="}"){e+="}",t+=2;continue}if(r==="{"){let n=s.indexOf("}",t+1);if(n!==-1){let i=s.slice(t,n+1),o=s.slice(t+1,n).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:Se.test(o)?o:void 0},t=n+1;continue}e+=i,t=n+1;continue}}e+=r,t+=1}e&&(yield{kind:"text",value:e})}function be(s){return Array.isArray(s)?s.join(", "):typeof s=="boolean"?s?"true":"false":String(s)}function ve(s){return s===" "||s===" "||s===`\n`||s==="\\r"||s==="\\f"||s==="\\v"}function we(s){let e="",t=!1;for(let r of s){if(ve(r)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=r}return e}function X(s,e,t){if(e.length===0||s.indexOf(e)<0)return s;let r="",n=0,i=!1;for(;n<s.length;){if(s.startsWith(e,n)){let o=s.indexOf(t,n+e.length);if(o>=0){n=o+t.length,i=!0;continue}r+=s.slice(n);break}r+=s[n],n+=1}return i?we(r):s}function Z(s,e){if(s.indexOf("{")<0)return s;let t="";for(let r of ye(s)){if(r.kind==="text"){t+=r.value;continue}if(!r.ref){t+=r.raw;continue}let n=e(r.ref);t+=n===void 0?"":be(n)}return t}function k(s,e){for(let t of s){e(t);let r=t.children;r&&k(r,e)}}function xe(s){return s.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function h(s){let e=s.gameId?.trim();return e||xe(s.name)}function V(s){return`cast:${s}`}var G={open:"[",close:"]"},Q="SFX";function R(s){let e=new Set,t=[];for(let r of s)e.has(r)||(e.add(r),t.push(r));return t}function O(s){let e=new Map,t=(r,n)=>{let i=R([...n,...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,R([...i,...o.tags??[]]))};for(let r of Object.values(s.scenes)){let n=R(r.tags??[]);e.set(r.id,n);for(let i of r.blocks){let o=R([...n,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var Y=new WeakMap,B=class s{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,n=e.strings;this.allStrings=n,this.currentLocale=r;let i=n[r]??{},o=n[e.locales.default]??{},a=e.localisation,l=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 c=new Map;for(let d of e.cast??[])d.displayName&&c.set(d.name,d.displayName);this.defaultSeed=(t.seed??2654435769)>>>0;let u=new Map,g=new Map,L=new Map;for(let[d,m]of Object.entries(e.scenes)){this.sceneGameIdToId.set(h(m),d);let b=new Map;for(let f of m.blocks)g.set(f.id,{sceneId:d}),L.set(f.id,f),b.set(h(f),f.id),k(f.children,q=>u.set(q.id,q));this.blockGameIdToId.set(d,b)}let $=e.properties??[],N=$.filter(d=>d.shared??!0).map(ee),ae=$.filter(d=>!(d.shared??!0)).map(ee),ce=new Set(N.map(d=>d.name.toLowerCase())),A=new w().defineOwned("patter",N),_=new Set;if(t.world){let d=e.scopeRegistry?.scopes.find(b=>b.token==="world"),m=(d?.declarations??[]).map(te);A.defineForeign("world",t.world,m,d?.writable??!0),_.add("world")}for(let d of e.scopeRegistry?.scopes??[]){if(_.has(d.token))continue;let m=(d.declarations??[]).map(te);A.defineForeign(d.token,Ce(d.declarations??[]),m,d.writable??!0)}let H=new Map;for(let[d,m]of Object.entries(e.scenes)){let b=new Set((m.sceneProps??[]).filter(f=>f.shared??!1).map(f=>f.name.toLowerCase()));H.set(d,b)}this.host={bundle:e,emitIds:l,strings:i,defaultStrings:o,castDisplay:c,nodeIndex:u,blockIndex:g,blockById:L,tagIndex:O(e),shared:A,patterSharedDecls:N,patterLocalDecls:ae,patterSharedNames:ce,sceneSharedNames:H,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??G).open,captionClose:(e.closedCaptions??G).close,captionCharacter:e.closedCaptions?.character||Q,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),n=new s(e,this.creationOptions);try{return n.loadGame(t),r(n)}catch{let i=new s(e,this.creationOptions);for(let[o,a]of Object.entries(t.flows)){let l=a.cursor.currentSceneId;try{i.openFlow(o,l!==null?{scene:l}:{})}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),n=this.resolveBlockRef(r,t.block),i=new v(e,this.host,t.seed??this.defaultSeed);return this.flowsById.set(e,i),i.start(r,n),i}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),n=this.resolveBlockRef(r,t);return(n!=null?this.host.tagIndex.get(n):void 0)??[]}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,n=>{if(n.type==="snippet")for(let i of n.beats??[])e.push({sceneId:t.id,blockId:r.id,snippetId:n.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 n=this.host.defaultStrings[V(e.character)]??this.host.castDisplay.get(e.character);n!==void 0&&(r.characterName=n)}e.direction!==void 0&&(r.direction=e.direction)}if(e.kind==="line"||e.kind==="text"){let n=this.host.defaultStrings[e.id];n!==void 0&&(r.text=n)}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.delete(e)}reset(){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:n}=this.splitShared(e);this.host.shared.set(r,n,t)}listProperties(){return this.host.patterSharedDecls.map(e=>({ref:`@${e.name}`,type:e.type,values:e.values,value:this.getProperty(`@${e.name}`),default:ke(e)}))}splitShared(e){let t=this.host.refSplitCache.get(e);if(t||(t=F(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:ne(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 se(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 n=new v(t,this.host,this.defaultSeed);n.restore(r),this.flowsById.set(t,n)}}},v=class{id;host;local;rngState;started=!1;flowEnded=!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 n=this.host.sceneSharedNames.get(r)?.has(e)?this.host.stageBags.get(r):this.sceneBags.get(r);n&&(n[e]=t)}};evalCtx;constructor(e,t,r){this.id=e,this.host=t,this.rngState=r>>>0,this.local=this.freshLocal();let n={...t.shared.toEvalContext().scopes};n.patter=this.patterResolver,n.scene=this.sceneResolver,this.evalCtx={scopes:n,host:{nextRandom:this.rng,visits:i=>this.visitCounts.get(i)??0,patterVisits:i=>this.host.sharedVisits.get(i)??0}}}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],n=r?this.host.bundle.scenes[r]:void 0;if(!n)throw new Error(r?`unknown scene: ${r}`:"no scenes in bundle");this.enterSceneSetup(r);let i=n.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)}get currentScene(){return this.currentSceneId}advance(){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 n=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(n);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 n=t.byId.get(e);this.pendingChoice=null,this.pendingPromptBeat=this.host.replayPromptOnChoose?this.promptBeatOf(n)??null:null,this.pendingPromptOwnerId=this.pendingPromptBeat?n.id:null,this.enterChild(n)}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:n}=this.splitRef(e);if(r==="patter")this.patterResolver.set(n,t);else if(r==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);this.sceneResolver.set(n,t)}else this.host.shared.set(r,n,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:ne(this.selectors)}}}restore(e){this.rngState=e.rngState>>>0,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:n,...i}=r;if(n!==void 0){let o=this.childrenOf(i.containerId)?.findIndex(a=>a.id===n)??-1;if(o>=0)return{...i,index:o}}return{...i}}),this.sceneBags=new Map(Object.entries(e.sceneBags??{}).map(([r,n])=>[r,{...n}])),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=se(t.selectors),this.pendingChoice=null,t.pendingChoice!==null){let r=new Map,n=[];for(let i of t.pendingChoice.options){let o=this.host.nodeIndex.get(i.id);o&&(r.set(i.id,o),n.push({...i}))}n.length>0&&(this.pendingChoice={groupId:t.pendingChoice.groupId,options:n,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,n=[];for(let o of e.children){if(o.fallback===!0){n.push(o);continue}if(o.sticky!==!0&&(this.visitCounts.get(o.id)??0)>=1)continue;let a=this.eligible(o),l=o.secretUntilEligible===!0;!a&&l||(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=n.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,n,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,n=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,n=e}this.enter(n);let o={sceneId:r,containerId:n,index:0};t==="call"?this.stack.push(o):this.stack=[o]}selectChild(e){let t=e.children.filter(n=>this.eligible(n));if(t.length===0)return null;let r=this.selectorState(e);switch(e.selector){case"branch":return t[0];case"sequence":{let n=e.options?.order??"sequential",i=e.options?.exhaust??"once";return n==="shuffle"?this.pickShuffle(t,i,r):n==="specificity"?this.pickSpecificity(t,i,r):this.pickSequential(t,i,r)}default:return null}}pickSequential(e,t,r){let n=e.length,i=r.seq??0;return r.seq=i+1,t==="repeat"?e[i%n]:i<n?e[i]:t==="stick"?e[n-1]:null}pickShuffle(e,t,r){let n=e.length,i=t==="stick",o=()=>(i?e.slice(0,n-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[n-1];return r.last=g.id,g}r.bag=o()}let a=r.bag,l=r.last!==void 0&&a.length>1?a.indexOf(r.last):-1,c=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&c>=l&&c++;let u=a[c];return a.splice(c,1),r.last=u,e.find(g=>g.id===u)}pickSpecificity(e,t,r){let n=e;if(t!=="repeat"){r.bag===void 0&&(r.bag=e.map(u=>u.id));let c=new Set(r.bag);if(n=e.filter(u=>c.has(u.id)),n.length===0)return t==="stick"&&r.last!==void 0?e.find(u=>u.id===r.last)??null:null}let i=-1,a=n.map(c=>{let u=this.specScore(c);return u>i&&(i=u),{c,s:u}}).filter(c=>c.s===i).map(c=>c.c),l;if(a.length===1)l=a[0];else{let c=r.last!==void 0?a.findIndex(g=>g.id===r.last):-1,u=Math.floor(this.rng()*(c>=0?a.length-1:a.length));c>=0&&u>=c&&u++,l=a[u]}return t!=="repeat"&&(r.bag=r.bag.filter(c=>c!==l.id)),r.last=l.id,l}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0):0}matchedSpec(e,t){return W(e,n=>re(E(n,this.evalCtx,T)),{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?re(this.evalExpr(e.condition)):!0}evalExpr(e){return E(this.conditionAst(e),this.evalCtx,T)}conditionAst(e){let t=Y.get(e);return t||(t=S(e.ast),Y.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=this.rngState+1831565813|0;this.rngState=e;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};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 n=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?n:this.interpolate(n)),l=i&&a.length===0;return{type:"line",id:e.id,text:a,character:l?void 0:e.character,characterName:l?void 0:this.resolveCharacterName(e.character),direction:l?void 0:e.direction,gameData:e.gameData,...r}}}}interpolate(e){return Z(e,t=>this.getProperty(t))}stripCaptions(e){return X(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(n=>n.kind==="line"||n.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=V(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=F(e,r=>r==="scene"||this.host.shared.has(r)),this.host.refSplitCache.set(e,t)),t}freshLocal(){return new w().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 n of e.sceneProps??[]){let i=n.name.toLowerCase();t.has(i)||(r[i]=M(n))}this.sceneBags.set(e.id,r)}if(!this.host.stageBags.has(e.id)){let r={};for(let n of e.sceneProps??[]){let i=n.name.toLowerCase();t.has(i)&&(r[i]=M(n))}this.host.stageBags.set(e.id,r)}for(let r of e.sceneProps??[]){if(!r.temporary)continue;let n=r.name.toLowerCase(),i=t.has(n)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);i&&(i[n]=M(r))}}};function ne(s){let e={};for(let[t,r]of s){let n={};r.seq!==void 0&&(n.seq=r.seq),r.bag&&(n.bag=[...r.bag]),r.last!==void 0&&(n.last=r.last),e[t]=n}return e}function se(s){let e=new Map;for(let[t,r]of Object.entries(s??{})){let n={};r.seq!==void 0&&(n.seq=r.seq),r.bag&&(n.bag=[...r.bag]),r.last!==void 0&&(n.last=r.last),e.set(t,n)}return e}function ee(s){return{name:s.name,type:s.type,values:s.values,default:s.default}}function ke(s){if(s.default!==void 0)return s.default;switch(s.type){case"number":return 0;case"string":return"";case"flags":return[];case"enum":return s.values?.[0]??"";default:return!1}}function te(s){return{name:s.name,type:s.type,values:s.values,default:s.default,writable:s.writable}}function Ee(s){if(s.default!==void 0)return s.default;switch(s.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"flags":return[];case"enum":return s.values?.[0]??""}}function Ce(s){let e=new Map;for(let t of s)e.set(t.name,Ee(t));return{get:t=>e.get(t),set:(t,r)=>{e.set(t,r)}}}function M(s){if(s.default!==void 0)return s.default;switch(s.type){case"boolean":return!1;case"number":return 0;case"string":return"";case"flags":return[];case"enum":return s.values?.[0]??""}}function re(s){return typeof s=="boolean"?s:typeof s=="number"?s!==0:typeof s=="string"?s!=="":s.length>0}function ie(s,e){return s.gameDataFields?.[e]??[]}function j(s,e,t){return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:s.find(r=>r.name===t)?.default}function oe(s,e){let t={};for(let r of s){let n=j(s,e,r.name);n!==void 0&&(t[r.name]=n)}for(let[r,n]of Object.entries(e??{}))r in t||(t[r]=n);return t}return fe(De);})();\n//# sourceMappingURL=patterplay.min.js.map';
|
|
336
336
|
|
|
337
337
|
// src/export-html.ts
|
|
338
338
|
var esc = (s) => s.replace(/[&<>]/g, (c) => ({ "&": "&", "<": "<", ">": ">" })[c]);
|
|
@@ -560,6 +560,10 @@ import { exportBundle as exportBundle3 } from "@patterkit/compiler";
|
|
|
560
560
|
import { Engine } from "@patterkit/runtime";
|
|
561
561
|
|
|
562
562
|
// src/loaded-helpers.ts
|
|
563
|
+
import { RERECORD_STATUS } from "@patterkit/model";
|
|
564
|
+
function effectiveRecording(id, base, rerecord, lowest) {
|
|
565
|
+
return rerecord.has(id) ? RERECORD_STATUS : base.get(id) ?? lowest;
|
|
566
|
+
}
|
|
563
567
|
function tableFor(loaded, locale) {
|
|
564
568
|
const table = {};
|
|
565
569
|
for (const l of loaded.locales) if (l.locale === locale) Object.assign(table, l.strings);
|
|
@@ -586,12 +590,14 @@ function mergeAuthoring(loaded) {
|
|
|
586
590
|
const writing = /* @__PURE__ */ new Map();
|
|
587
591
|
const recording = /* @__PURE__ */ new Map();
|
|
588
592
|
const cut = /* @__PURE__ */ new Set();
|
|
593
|
+
const rerecord = /* @__PURE__ */ new Set();
|
|
589
594
|
const documentation = /* @__PURE__ */ new Map();
|
|
590
595
|
const edits = /* @__PURE__ */ new Map();
|
|
591
596
|
for (const a of loaded.authoring) {
|
|
592
597
|
for (const [id, v] of Object.entries(a.writing ?? {})) writing.set(id, v);
|
|
593
598
|
for (const [id, v] of Object.entries(a.recording ?? {})) recording.set(id, v);
|
|
594
599
|
for (const [id, v] of Object.entries(a.cut ?? {})) if (v) cut.add(id);
|
|
600
|
+
for (const [id, v] of Object.entries(a.rerecord ?? {})) if (v) rerecord.add(id);
|
|
595
601
|
for (const [id, lines] of Object.entries(a.documentation ?? {})) {
|
|
596
602
|
const cur = documentation.get(id);
|
|
597
603
|
if (cur) cur.push(...lines);
|
|
@@ -599,7 +605,7 @@ function mergeAuthoring(loaded) {
|
|
|
599
605
|
}
|
|
600
606
|
for (const [id, v] of Object.entries(a.edits ?? {})) edits.set(id, v);
|
|
601
607
|
}
|
|
602
|
-
return { writing, recording, cut, documentation, edits };
|
|
608
|
+
return { writing, recording, cut, rerecord, documentation, edits };
|
|
603
609
|
}
|
|
604
610
|
|
|
605
611
|
// src/play.ts
|
|
@@ -991,7 +997,7 @@ function runFormat(files) {
|
|
|
991
997
|
}
|
|
992
998
|
|
|
993
999
|
// src/report.ts
|
|
994
|
-
import { walkNodes as walkNodes2, DEFAULT_WRITING_STATUSES, DEFAULT_RECORDING_STATUSES as DEFAULT_RECORDING_STATUSES2 } from "@patterkit/model";
|
|
1000
|
+
import { walkNodes as walkNodes2, DEFAULT_WRITING_STATUSES, DEFAULT_RECORDING_STATUSES as DEFAULT_RECORDING_STATUSES2, RERECORD_STATUS as RERECORD_STATUS2 } from "@patterkit/model";
|
|
995
1001
|
var DEFAULT_WORDS_PER_LINE = 6;
|
|
996
1002
|
var UNATTRIBUTED = "(unattributed)";
|
|
997
1003
|
function allocateLargestRemainder(total, weights) {
|
|
@@ -1021,8 +1027,9 @@ function runReport(loaded, recordingOverride) {
|
|
|
1021
1027
|
const thresholdIdx = estimatingOn ? writingIndex.get(est?.thresholdStatus ?? "") ?? 0 : -1;
|
|
1022
1028
|
const defaultLines = est?.defaultLines ?? 0;
|
|
1023
1029
|
const tagMap = new Map((est?.tagEstimates ?? []).map((t) => [t.tag, t.lines]));
|
|
1024
|
-
const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, edits: editsOf } = mergeAuthoring(loaded);
|
|
1025
|
-
const
|
|
1030
|
+
const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, rerecord: rerecordSet, edits: editsOf } = mergeAuthoring(loaded);
|
|
1031
|
+
const recordingBase = recordingOverride ?? manualRecordingOf;
|
|
1032
|
+
const recordingOf = (id) => effectiveRecording(id, recordingBase, rerecordSet, recordingLadder[0]);
|
|
1026
1033
|
const byLocale = stringsByLocale(loaded);
|
|
1027
1034
|
const sourceStrings2 = byLocale.get(project.locales.default) ?? {};
|
|
1028
1035
|
const wordsOf = (id) => {
|
|
@@ -1135,7 +1142,7 @@ function runReport(loaded, recordingOverride) {
|
|
|
1135
1142
|
const wi = writingIndex.get(ws) ?? 0;
|
|
1136
1143
|
if (recordThreshold !== -1 && wi >= recordThreshold) voiced.readyToRecord++;
|
|
1137
1144
|
if (shipThreshold !== -1 && wi >= shipThreshold) voiced.readyToShip++;
|
|
1138
|
-
const rec = recordingOf
|
|
1145
|
+
const rec = recordingOf(u.id);
|
|
1139
1146
|
voiced.byRecording[rec] = (voiced.byRecording[rec] ?? 0) + 1;
|
|
1140
1147
|
const speaker = u.character ?? "(narrator)";
|
|
1141
1148
|
const ch = getChar(speaker);
|
|
@@ -1170,13 +1177,14 @@ function runReport(loaded, recordingOverride) {
|
|
|
1170
1177
|
const scenesByStatus = Object.fromEntries(writingLadder.map((s) => [s, 0]));
|
|
1171
1178
|
for (const s of reports) if (s.status) scenesByStatus[s.status] = (scenesByStatus[s.status] ?? 0) + 1;
|
|
1172
1179
|
const coverage = { totalScenes: reports.length, estimated: reports.filter((s) => s.estimated).length };
|
|
1180
|
+
const reportedRecordingLadder = (totals.voiced.byRecording[RERECORD_STATUS2] ?? 0) > 0 ? [...recordingLadder, RERECORD_STATUS2] : recordingLadder;
|
|
1173
1181
|
return {
|
|
1174
1182
|
project: { id: project.project.id, name: project.project.name },
|
|
1175
1183
|
voiced: project.voiced ?? false,
|
|
1176
1184
|
// Recording tracking is opt-in (default off) even for a voiced project (#206).
|
|
1177
1185
|
recordingTracked: (project.voiced ?? false) && (project.trackAudioStatus ?? false),
|
|
1178
1186
|
writingLadder,
|
|
1179
|
-
recordingLadder,
|
|
1187
|
+
recordingLadder: reportedRecordingLadder,
|
|
1180
1188
|
scenes: reports,
|
|
1181
1189
|
characters: [...characters.values()].sort((a, b) => b.lines + b.estimatedLines - (a.lines + a.estimatedLines)),
|
|
1182
1190
|
locales,
|
|
@@ -1420,12 +1428,18 @@ function extractLoc(loaded, opts = {}) {
|
|
|
1420
1428
|
const localised = e?.localisedAt?.[targetLocale];
|
|
1421
1429
|
return !!(e?.modifiedAt && localised && e.modifiedAt > localised);
|
|
1422
1430
|
};
|
|
1431
|
+
const genderOf = /* @__PURE__ */ new Map();
|
|
1432
|
+
for (const c of loaded.project.cast ?? []) if (c.gender) genderOf.set(c.name, c.gender);
|
|
1433
|
+
const withGender = (context) => {
|
|
1434
|
+
const g = context?.character ? genderOf.get(context.character) : void 0;
|
|
1435
|
+
return g ? { ...context, gender: g } : context;
|
|
1436
|
+
};
|
|
1423
1437
|
const entries = [];
|
|
1424
1438
|
const push = (id, scene, context) => {
|
|
1425
1439
|
const src = source[id];
|
|
1426
1440
|
if (src === void 0) return;
|
|
1427
1441
|
const translation = isTemplate ? "" : target[id] ?? "";
|
|
1428
|
-
entries.push({ id, scene, source: src, translation, comments: commentsOf(id), context, stale: staleFor(id, translation) });
|
|
1442
|
+
entries.push({ id, scene, source: src, translation, comments: commentsOf(id), context: withGender(context), stale: staleFor(id, translation) });
|
|
1429
1443
|
};
|
|
1430
1444
|
for (const scene of loaded.scenes) {
|
|
1431
1445
|
for (const block of scene.blocks) {
|
|
@@ -1446,7 +1460,7 @@ function extractLoc(loaded, opts = {}) {
|
|
|
1446
1460
|
const id = castStringKey(c.name);
|
|
1447
1461
|
const src = source[id] ?? c.displayName;
|
|
1448
1462
|
const translation = isTemplate ? "" : target[id] ?? "";
|
|
1449
|
-
entries.push({ id, scene: PROJECT_LOCALE_SCENE, source: src, translation, comments: commentsOf(id), context: { character: c.name }, stale: staleFor(id, translation) });
|
|
1463
|
+
entries.push({ id, scene: PROJECT_LOCALE_SCENE, source: src, translation, comments: commentsOf(id), context: withGender({ character: c.name }), stale: staleFor(id, translation) });
|
|
1450
1464
|
}
|
|
1451
1465
|
return { project: loaded.project.project.id, defaultLocale, locale: targetLocale, entries };
|
|
1452
1466
|
}
|
|
@@ -1569,6 +1583,7 @@ function catalogToPo(catalog) {
|
|
|
1569
1583
|
if (e.context?.character || e.context?.kind) {
|
|
1570
1584
|
out.push(`#. [${[e.context.kind, e.context.character].filter(Boolean).join(" ")}]`);
|
|
1571
1585
|
}
|
|
1586
|
+
if (e.context?.gender) out.push(`#. Gender: ${e.context.gender}`);
|
|
1572
1587
|
out.push(`#: ${e.scene}`);
|
|
1573
1588
|
if (e.stale) out.push("#, fuzzy");
|
|
1574
1589
|
out.push(poField("msgctxt", e.id), poField("msgid", e.source), poField("msgstr", e.translation), "");
|
|
@@ -1662,7 +1677,7 @@ function poToCatalog(text) {
|
|
|
1662
1677
|
}
|
|
1663
1678
|
|
|
1664
1679
|
// src/loc-xlsx.ts
|
|
1665
|
-
var HEADERS = ["ID", "Source", "Translation", "Comments", "Status"];
|
|
1680
|
+
var HEADERS = ["ID", "Source", "Translation", "Comments", "Status", "Gender"];
|
|
1666
1681
|
var sheetName = (scene) => scene.replace(/[:\\/?*[\]]/g, "-").slice(0, 31);
|
|
1667
1682
|
async function catalogToXlsx(catalog) {
|
|
1668
1683
|
const { default: ExcelJS } = await import("exceljs");
|
|
@@ -1677,7 +1692,8 @@ async function catalogToXlsx(catalog) {
|
|
|
1677
1692
|
{ header: "Source", key: "source", width: 40 },
|
|
1678
1693
|
{ header: "Translation", key: "translation", width: 40 },
|
|
1679
1694
|
{ header: "Comments", key: "comments", width: 30 },
|
|
1680
|
-
{ header: "Status", key: "status", width: 10 }
|
|
1695
|
+
{ header: "Status", key: "status", width: 10 },
|
|
1696
|
+
{ header: "Gender", key: "gender", width: 12 }
|
|
1681
1697
|
];
|
|
1682
1698
|
ws.getRow(1).font = { bold: true };
|
|
1683
1699
|
for (const e of entries) {
|
|
@@ -1686,7 +1702,8 @@ async function catalogToXlsx(catalog) {
|
|
|
1686
1702
|
source: e.source,
|
|
1687
1703
|
translation: e.translation,
|
|
1688
1704
|
comments: e.comments.join("\n"),
|
|
1689
|
-
status: e.stale ? "stale" : e.translation ? "translated" : ""
|
|
1705
|
+
status: e.stale ? "stale" : e.translation ? "translated" : "",
|
|
1706
|
+
gender: e.context?.gender ?? ""
|
|
1690
1707
|
});
|
|
1691
1708
|
}
|
|
1692
1709
|
}
|
|
@@ -1742,8 +1759,9 @@ function runVoiceScript(loaded, opts = {}) {
|
|
|
1742
1759
|
const stub = writingLadder[0];
|
|
1743
1760
|
const recordThreshold = ladderDecls.findIndex((s) => s.readyToRecord);
|
|
1744
1761
|
const writingIndex = new Map(writingLadder.map((name, i) => [name, i]));
|
|
1745
|
-
const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, documentation: docsOf } = mergeAuthoring(loaded);
|
|
1746
|
-
const
|
|
1762
|
+
const { writing: writingOf, recording: manualRecordingOf, cut: cutSet, rerecord: rerecordSet, documentation: docsOf } = mergeAuthoring(loaded);
|
|
1763
|
+
const recordingBase = opts.recordingOverride ?? manualRecordingOf;
|
|
1764
|
+
const recordingOf = (id) => effectiveRecording(id, recordingBase, rerecordSet, recordingLadder[0]);
|
|
1747
1765
|
const source = sourceStrings(loaded);
|
|
1748
1766
|
const actorOf = /* @__PURE__ */ new Map();
|
|
1749
1767
|
for (const c of project.cast ?? []) if (c.actor) actorOf.set(c.name, c.actor);
|
|
@@ -1774,7 +1792,7 @@ function runVoiceScript(loaded, opts = {}) {
|
|
|
1774
1792
|
text: plainVoice(source[beat.id] ?? ""),
|
|
1775
1793
|
comments: leading ? [...ancestorVo, ...own] : own,
|
|
1776
1794
|
// first line of the run gets the enclosing context
|
|
1777
|
-
recordingStatus: recordingOf
|
|
1795
|
+
recordingStatus: recordingOf(beat.id)
|
|
1778
1796
|
});
|
|
1779
1797
|
leading = false;
|
|
1780
1798
|
}
|
|
@@ -2607,6 +2625,7 @@ function runStatusBrowse(loaded, status, dimension = "writing", focus, recording
|
|
|
2607
2625
|
const statusOf = recording ? recordingOverride ?? merged.recording : merged.writing;
|
|
2608
2626
|
const ladder = recording ? loaded.project.recordingStatuses ?? DEFAULT_RECORDING_STATUSES4 : loaded.project.writingStatuses ?? DEFAULT_WRITING_STATUSES3;
|
|
2609
2627
|
const lowest = ladder[0]?.name;
|
|
2628
|
+
const statusAt = (id) => recording ? effectiveRecording(id, statusOf, merged.rerecord, lowest ?? "") : statusOf.get(id) ?? lowest;
|
|
2610
2629
|
const out = [];
|
|
2611
2630
|
for (const scene of loaded.scenes) {
|
|
2612
2631
|
const file = loaded.sceneFiles[scene.id];
|
|
@@ -2615,7 +2634,7 @@ function runStatusBrowse(loaded, status, dimension = "writing", focus, recording
|
|
|
2615
2634
|
const consider = (beat) => {
|
|
2616
2635
|
const tracked = recording ? beat.kind === "line" : beat.kind === "line" || beat.kind === "text";
|
|
2617
2636
|
if (!tracked) return;
|
|
2618
|
-
if ((
|
|
2637
|
+
if (statusAt(beat.id) !== status) return;
|
|
2619
2638
|
out.push({ id: beat.id, kind: "beat", text: strings[beat.id], location: segments, sceneId: scene.id, file });
|
|
2620
2639
|
};
|
|
2621
2640
|
walkNodes4(block.children, (node) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patterkit/ops",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The shared operations layer: every project operation (load, validate, export, format, play, ...) as a pure function, consumed by the patter CLI and Patterpad alike.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"build": "tsup src/index.ts --format esm --dts --clean"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@patterkit/compiler": "0.
|
|
31
|
-
"@patterkit/core": "0.1.
|
|
32
|
-
"@patterkit/model": "0.
|
|
33
|
-
"@patterkit/runtime": "0.2.
|
|
30
|
+
"@patterkit/compiler": "0.2.0",
|
|
31
|
+
"@patterkit/core": "0.1.3",
|
|
32
|
+
"@patterkit/model": "0.2.0",
|
|
33
|
+
"@patterkit/runtime": "0.2.1",
|
|
34
34
|
"docx": "^9.7.1",
|
|
35
35
|
"exceljs": "^4.4.0",
|
|
36
36
|
"jszip": "^3.10.1",
|