@patterkit/ops 0.25.2 → 0.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +15 -2
- package/dist/index.js +184 -12
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -237,6 +237,10 @@ declare function loadProjectLanding(startPath: string, opts?: {
|
|
|
237
237
|
* Listed scenes come first in list order; unlisted scenes follow in their existing (file) order;
|
|
238
238
|
* listed ids that no longer exist are simply ignored. Presentation only - play order is untouched. */
|
|
239
239
|
declare function applySceneOrder(scenes: Scene[], order: string[] | undefined): void;
|
|
240
|
+
/** Sort scenes into the scene list's order as shown (`sceneFolderLayout`: `sceneOrder` within each folder,
|
|
241
|
+
* folders nested), in place. The same as `applySceneOrder` for a project with no folders. Presentation
|
|
242
|
+
* only - play order is untouched. */
|
|
243
|
+
declare function applySceneNavOrder(scenes: Scene[], project: Pick<ProjectFile, "sceneOrder" | "sceneFolders" | "sceneFolder">): void;
|
|
240
244
|
declare function loadProject(startPath: string): LoadedProject;
|
|
241
245
|
|
|
242
246
|
/** A raw-bytes hygiene problem in one source file (repairable by `format`). */
|
|
@@ -714,6 +718,9 @@ declare function resolveStart(loaded: LoadedProject, override?: {
|
|
|
714
718
|
scene?: string;
|
|
715
719
|
block?: string;
|
|
716
720
|
};
|
|
721
|
+
/** Each filed scene's folder path ("Act 1 / The docks"), by scene id: the Folder column the report, voice
|
|
722
|
+
* script, and loc spreadsheets carry (scene folders). Empty when the project has no folders. */
|
|
723
|
+
declare function sceneFolderPaths(loaded: Pick<LoadedProject, "project" | "scenes">): Map<string, string>;
|
|
717
724
|
|
|
718
725
|
interface FormatResult {
|
|
719
726
|
file: string;
|
|
@@ -744,6 +751,8 @@ interface VoicedCounts extends WrittenCounts {
|
|
|
744
751
|
interface SceneReport {
|
|
745
752
|
sceneId: string;
|
|
746
753
|
name: string;
|
|
754
|
+
/** The scene's folder path in Patterpad's list ("Act 1 / The docks"), when it's filed in one. */
|
|
755
|
+
folder?: string;
|
|
747
756
|
file?: string;
|
|
748
757
|
/** The scene's writing status: its LOWEST-rung status-tracked beat. Absent when it has no such beats. */
|
|
749
758
|
status?: string;
|
|
@@ -1003,7 +1012,9 @@ declare function catalogToPo(catalog: LocCatalog): string;
|
|
|
1003
1012
|
declare function poToCatalog(text: string): LocCatalog;
|
|
1004
1013
|
|
|
1005
1014
|
/** Render the catalog as an .xlsx workbook: a sheet per scene, translator-facing columns. */
|
|
1006
|
-
declare function catalogToXlsx(catalog: LocCatalog
|
|
1015
|
+
declare function catalogToXlsx(catalog: LocCatalog, opts?: {
|
|
1016
|
+
folders?: ReadonlyMap<string, string>;
|
|
1017
|
+
}): Promise<Buffer>;
|
|
1007
1018
|
/** Parse an .xlsx workbook back into a catalog. Scene = the hidden Scene column (else, for an older export,
|
|
1008
1019
|
* the sheet name); stale = Status "stale". `locale` (and the project and source language) come from the
|
|
1009
1020
|
* hidden "Patter" sheet; an older export has none, so its `locale` is undefined and the caller supplies one.
|
|
@@ -1011,6 +1022,8 @@ declare function catalogToXlsx(catalog: LocCatalog): Promise<Buffer>;
|
|
|
1011
1022
|
declare function xlsxToCatalog(buffer: Buffer): Promise<LocCatalog>;
|
|
1012
1023
|
|
|
1013
1024
|
interface VoiceLine {
|
|
1025
|
+
/** The scene's folder path in Patterpad's list ("Act 1 / The docks"), when it's filed in one. */
|
|
1026
|
+
folder?: string;
|
|
1014
1027
|
/** Readable container trail (`Scene › Block › Option`) - the scope changes signal a new run / branch. */
|
|
1015
1028
|
scope: string;
|
|
1016
1029
|
/** The line beat's id (recordings / filenames key to this). */
|
|
@@ -2193,4 +2206,4 @@ declare const REFUSE_MISSING_SHARE = 0.25;
|
|
|
2193
2206
|
/** Plan an import. Reads the handoff record named in the document from the project. */
|
|
2194
2207
|
declare function planEditableImport(loaded: LoadedProject, returned: ReturnedDoc, opts: ImportOptions): ImportPlan;
|
|
2195
2208
|
|
|
2196
|
-
export { AUDIO_MANIFEST_FILE, AUDIO_MANIFEST_SCHEMA, AUTHORING_HANDLED, type ActionTextChange, type ApplyStats, type AudioEntry, type AudioRung, type AudioSnapshot, type BuildPlan, CONFLICT_SIDECAR, type CastKeyRewrite, type CharacterReport, type ClashPathSegment, type Conflict, type ConflictKind, type ConflictSidecar, type ContentError, type CoverageAsyncHooks, type CoverageBeat, type CoverageHooks, type CoverageOptions, type CoverageOrder, type CoverageReport, type DecisionPlan, type DecisionResult, type DocumentManifest, type EditableScript, type EditableScriptOptions, type ExportOptions, type ExtensionUpgrade, type FormatResult, GAME_SCOPES_OWNER, type GameDataStageRenameResult, type GameScopes, type GameScopesIssue, HANDOFF_DIR, type HandoffIssue, type HygieneIssue, type ImportOptions, type ImportPlan, type ImportProblem, type ImportReport, type InitOptions, type InitResult, type InitVcs, type LoadedManifest, type LoadedProject, type LocCatalog, type LocEntry, type LocImportLine, type LocImportReport, type LocMismatch, type LocaleReport, type MarkedShard, type MergeFileType, type MergeResult, type MergedShard, NotAPackError, type OpenSuggestion, PATTER_SCOPES_FILE, PATTER_SCOPES_OWNER, PATTER_VCS_FORMAT, PROJECT_HANDLED, type PinPlan, type PinnedName, type PlannedWrite, type PlayEvent, type PlayOptions, type PlayOutcome, type PlayResult, type ProvenanceCheck, RARE_REACH_PCT, REFUSE_MISSING_SHARE, type Random, type ReadBox, type ReadComment, type ReadEffect, type ReadItem, type ReadMarker, type ReadParagraph, type ReadText, type ReplaceField, type ReplaceHit, type ReplaceOptions, type ReplacePlan, type ReportData, type ReportTotals, type Resolution, type ResolutionDecision, ResolutionError, type ResolveEntry, type ReturnedDoc, SCRIPT_FONT_FILES, SCRIPT_FONT_PACKAGES, SHARD_EXTENSIONS, type SceneReport, type ScriptDoc, type ScriptElement, type ScriptFace, type SearchFocus, type ShardText, type StageRenameResult, type StageTarget, type SuggestionDecision, type TrackedChange, type UnpackMergeResult, type UnpackResult, type UnresolvedClash, UnsafeEntryError, UnsupportedMergeError, type ValidateResult, type VoiceLine, type VoiceScript, type VoicedCounts, type WebExport, type WrittenCounts, actionTextChanges, actionTextIds, applyLoc, applyResolutions, applySceneOrder, applySuggestionDecisions, applyWrites, audioFolderRungs, audioFoldersActive, audioManifest, audioManifestWrite, authoringPath, besideBundlePath, bundleOutputPath, castColours, catalogToJson, catalogToPo, catalogToXlsx, characterColour, clashPathSegments, classesForChannel, cleanCastRenames, compareLocTokens, compileLoaded, cueLabel, currentBundlePosture, defaultBundlePath, defaultGameScopesDir, describeLocExport, describeLocImport, detectMergeType, discoverGameScopes, drivableScopes, effectDescriber, exportBlockers, exportEditableScript, extractLoc, findMarkedShards, findProjectFile, formatMarker, gameScopeTokens, gameScopesCatalogue, gameScopesSnapshot, handoffPath, handoffWrite, hostScopeTokens, importLocale, isSettableClash, issueMarkerCodes, jsonToCatalog, keepCastColours, leastReachedFirst, listGameDataFields, listOpenSuggestions, listProjectTags, loadProject, loadProjectLanding, loadedExtensions, locLineHomes, manifestPath, markerLength, moveCastKeys, newHandoffId, parseHandoff, patterScopesFile, patterScopesStale, patterScopesWrite, pickAudio, planBuild, planEditableImport, planExtensionUpgrades, planPins, planReturnedWorld, planShareScopes, planWorldSave, poToCatalog, previewRegistry, projectManifests, projectStringSources, proposeCoverageDrivers, qualifierNamer, reachabilityIssues, readEditableDocx, readGameScopes, readHandoff, readHandoffs, readMarker, readProjectManifest, renameCastInScene, renameCastKeys, renameCastStrings, renameGameDataStage, renameStage, renderCoverageText, renderPlay, renderReportText, resolveDocumentation, resolveStart, runCoverage, runCoverageAsync, runExport, runExportFull, runExportHtml, runExportWeb, runFieldBrowse, runFormat, runInit, runMerge, runPack, runPlay, runPropertyUsage, runReplace, runReport, runReportXlsx, runResolve, runScriptDoc, runSearch, runStatusBrowse, runTagBrowse, runUnpack, runUnpackMerge, runValidate, runVoiceScript, scanAudio, scanAudioStatus, sceneIdForShard, sceneTextIds, scriptFontLicencePath, scriptFontLicences, scriptToDocx, scriptToPdf, serialiseHandoff, setCut, sidecarIssues, soundManifest, stageDialect, stageGateCount, staleParts, unresolvedClashes, unresolvedMarkIssues, useScriptFontFiles, valueAtClashPath, vcsConfigWrites, voiceScriptToXlsx, worldSettingsScopes, xlsxToCatalog };
|
|
2209
|
+
export { AUDIO_MANIFEST_FILE, AUDIO_MANIFEST_SCHEMA, AUTHORING_HANDLED, type ActionTextChange, type ApplyStats, type AudioEntry, type AudioRung, type AudioSnapshot, type BuildPlan, CONFLICT_SIDECAR, type CastKeyRewrite, type CharacterReport, type ClashPathSegment, type Conflict, type ConflictKind, type ConflictSidecar, type ContentError, type CoverageAsyncHooks, type CoverageBeat, type CoverageHooks, type CoverageOptions, type CoverageOrder, type CoverageReport, type DecisionPlan, type DecisionResult, type DocumentManifest, type EditableScript, type EditableScriptOptions, type ExportOptions, type ExtensionUpgrade, type FormatResult, GAME_SCOPES_OWNER, type GameDataStageRenameResult, type GameScopes, type GameScopesIssue, HANDOFF_DIR, type HandoffIssue, type HygieneIssue, type ImportOptions, type ImportPlan, type ImportProblem, type ImportReport, type InitOptions, type InitResult, type InitVcs, type LoadedManifest, type LoadedProject, type LocCatalog, type LocEntry, type LocImportLine, type LocImportReport, type LocMismatch, type LocaleReport, type MarkedShard, type MergeFileType, type MergeResult, type MergedShard, NotAPackError, type OpenSuggestion, PATTER_SCOPES_FILE, PATTER_SCOPES_OWNER, PATTER_VCS_FORMAT, PROJECT_HANDLED, type PinPlan, type PinnedName, type PlannedWrite, type PlayEvent, type PlayOptions, type PlayOutcome, type PlayResult, type ProvenanceCheck, RARE_REACH_PCT, REFUSE_MISSING_SHARE, type Random, type ReadBox, type ReadComment, type ReadEffect, type ReadItem, type ReadMarker, type ReadParagraph, type ReadText, type ReplaceField, type ReplaceHit, type ReplaceOptions, type ReplacePlan, type ReportData, type ReportTotals, type Resolution, type ResolutionDecision, ResolutionError, type ResolveEntry, type ReturnedDoc, SCRIPT_FONT_FILES, SCRIPT_FONT_PACKAGES, SHARD_EXTENSIONS, type SceneReport, type ScriptDoc, type ScriptElement, type ScriptFace, type SearchFocus, type ShardText, type StageRenameResult, type StageTarget, type SuggestionDecision, type TrackedChange, type UnpackMergeResult, type UnpackResult, type UnresolvedClash, UnsafeEntryError, UnsupportedMergeError, type ValidateResult, type VoiceLine, type VoiceScript, type VoicedCounts, type WebExport, type WrittenCounts, actionTextChanges, actionTextIds, applyLoc, applyResolutions, applySceneNavOrder, applySceneOrder, applySuggestionDecisions, applyWrites, audioFolderRungs, audioFoldersActive, audioManifest, audioManifestWrite, authoringPath, besideBundlePath, bundleOutputPath, castColours, catalogToJson, catalogToPo, catalogToXlsx, characterColour, clashPathSegments, classesForChannel, cleanCastRenames, compareLocTokens, compileLoaded, cueLabel, currentBundlePosture, defaultBundlePath, defaultGameScopesDir, describeLocExport, describeLocImport, detectMergeType, discoverGameScopes, drivableScopes, effectDescriber, exportBlockers, exportEditableScript, extractLoc, findMarkedShards, findProjectFile, formatMarker, gameScopeTokens, gameScopesCatalogue, gameScopesSnapshot, handoffPath, handoffWrite, hostScopeTokens, importLocale, isSettableClash, issueMarkerCodes, jsonToCatalog, keepCastColours, leastReachedFirst, listGameDataFields, listOpenSuggestions, listProjectTags, loadProject, loadProjectLanding, loadedExtensions, locLineHomes, manifestPath, markerLength, moveCastKeys, newHandoffId, parseHandoff, patterScopesFile, patterScopesStale, patterScopesWrite, pickAudio, planBuild, planEditableImport, planExtensionUpgrades, planPins, planReturnedWorld, planShareScopes, planWorldSave, poToCatalog, previewRegistry, projectManifests, projectStringSources, proposeCoverageDrivers, qualifierNamer, reachabilityIssues, readEditableDocx, readGameScopes, readHandoff, readHandoffs, readMarker, readProjectManifest, renameCastInScene, renameCastKeys, renameCastStrings, renameGameDataStage, renameStage, renderCoverageText, renderPlay, renderReportText, resolveDocumentation, resolveStart, runCoverage, runCoverageAsync, runExport, runExportFull, runExportHtml, runExportWeb, runFieldBrowse, runFormat, runInit, runMerge, runPack, runPlay, runPropertyUsage, runReplace, runReport, runReportXlsx, runResolve, runScriptDoc, runSearch, runStatusBrowse, runTagBrowse, runUnpack, runUnpackMerge, runValidate, runVoiceScript, scanAudio, scanAudioStatus, sceneFolderPaths, sceneIdForShard, sceneTextIds, scriptFontLicencePath, scriptFontLicences, scriptToDocx, scriptToPdf, serialiseHandoff, setCut, sidecarIssues, soundManifest, stageDialect, stageGateCount, staleParts, unresolvedClashes, unresolvedMarkIssues, useScriptFontFiles, valueAtClashPath, vcsConfigWrites, voiceScriptToXlsx, worldSettingsScopes, xlsxToCatalog };
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { readdirSync as readdirSync2, readFileSync as readFileSync2, statSync, existsSync as existsSync2 } from "fs";
|
|
3
3
|
import { join as join2, dirname as dirname2, resolve as resolve2, basename } from "path";
|
|
4
4
|
import { parseSource } from "@patterkit/core";
|
|
5
|
-
import { EXTENSION_SHARD, EXTENSIONS_DIR, MANIFEST_EXTENSION, isCaseOnlyPropertyName, projectLayout } from "@patterkit/model";
|
|
5
|
+
import { EXTENSION_SHARD, EXTENSIONS_DIR, MANIFEST_EXTENSION, isCaseOnlyPropertyName, projectLayout, sceneFolderLayout } from "@patterkit/model";
|
|
6
6
|
|
|
7
7
|
// src/game-scopes.ts
|
|
8
8
|
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
@@ -490,6 +490,11 @@ function applySceneOrder(scenes, order) {
|
|
|
490
490
|
return (fileRank.get(a.id) ?? 0) - (fileRank.get(b.id) ?? 0);
|
|
491
491
|
});
|
|
492
492
|
}
|
|
493
|
+
function applySceneNavOrder(scenes, project) {
|
|
494
|
+
if (!project.sceneOrder?.length && !project.sceneFolders?.length) return;
|
|
495
|
+
const rank = new Map(sceneFolderLayout(project, scenes.map((s) => s.id)).sceneIds.map((id, i) => [id, i]));
|
|
496
|
+
scenes.sort((a, b) => (rank.get(a.id) ?? 0) - (rank.get(b.id) ?? 0));
|
|
497
|
+
}
|
|
493
498
|
function loadProject(startPath) {
|
|
494
499
|
const projectFile = findProjectFile(startPath);
|
|
495
500
|
const root = dirname2(projectFile);
|
|
@@ -506,7 +511,7 @@ function loadProject(startPath) {
|
|
|
506
511
|
scenes.push(scene);
|
|
507
512
|
sceneFiles[scene.id] = f;
|
|
508
513
|
}
|
|
509
|
-
|
|
514
|
+
applySceneNavOrder(scenes, project);
|
|
510
515
|
const locales = [];
|
|
511
516
|
const localeFiles = [];
|
|
512
517
|
for (const f of walkFiles(join2(root, layout.strings), ".patterloc")) {
|
|
@@ -737,7 +742,9 @@ function mergeDecls(strategy, base, ours, theirs, path, conflicts) {
|
|
|
737
742
|
const out = shared(strategy, asArr(base), asArr(ours), asArr(theirs), "", path, conflicts);
|
|
738
743
|
return out.map((m) => isObj(m) && isObj(m.qualifierLabels) && Object.keys(m.qualifierLabels).length === 0 ? (({ qualifierLabels: _q, ...rest }) => rest)(m) : m);
|
|
739
744
|
}
|
|
740
|
-
var PROJECT_HANDLED = /* @__PURE__ */ new Set(["properties", "cast", "gameDataFields", "qualifiers", "locales", "extensions"]);
|
|
745
|
+
var PROJECT_HANDLED = /* @__PURE__ */ new Set(["properties", "cast", "gameDataFields", "qualifiers", "locales", "extensions", "sceneOrder", "sceneFolders", "sceneFolder"]);
|
|
746
|
+
var SCENE_FOLDERS = keyedBy("id", objectOf({}), { order: "preserve" });
|
|
747
|
+
var SCENE_FOLDER = mapOf(ATOMIC);
|
|
741
748
|
function mergeProject(base, ours, theirs) {
|
|
742
749
|
const conflicts = [];
|
|
743
750
|
const merged = {};
|
|
@@ -754,6 +761,15 @@ function mergeProject(base, ours, theirs) {
|
|
|
754
761
|
} else if (k === "qualifiers") {
|
|
755
762
|
const v = mergeQualifiers(base[k], ours[k], theirs[k], conflicts);
|
|
756
763
|
if (v !== void 0) merged[k] = v;
|
|
764
|
+
} else if (k === "sceneOrder") {
|
|
765
|
+
const v = mergeOrder(asArr(base[k]), asArr(ours[k]), asArr(theirs[k]), k, conflicts);
|
|
766
|
+
if (v.length > 0) merged[k] = v;
|
|
767
|
+
} else if (k === "sceneFolders") {
|
|
768
|
+
const v = shared(SCENE_FOLDERS, asArr(base[k]), asArr(ours[k]), asArr(theirs[k]), "", k, conflicts);
|
|
769
|
+
if (v.length > 0) merged[k] = v;
|
|
770
|
+
} else if (k === "sceneFolder") {
|
|
771
|
+
const v = shared(SCENE_FOLDER, asMap(base[k]), asMap(ours[k]), asMap(theirs[k]), "", k, conflicts);
|
|
772
|
+
if (Object.keys(v).length > 0) merged[k] = v;
|
|
757
773
|
} else if (k === "locales") {
|
|
758
774
|
merged.locales = mergeLocales(asMap(base.locales), asMap(ours.locales), asMap(theirs.locales), conflicts);
|
|
759
775
|
} else if (k === "extensions" && [base[k], ours[k], theirs[k]].every((x) => x === void 0 || isObj(x))) {
|
|
@@ -766,6 +782,55 @@ function mergeProject(base, ours, theirs) {
|
|
|
766
782
|
}
|
|
767
783
|
return { type: "project", merged, conflicts, warnings: [] };
|
|
768
784
|
}
|
|
785
|
+
function mergeOrder(base, ours, theirs, path, conflicts) {
|
|
786
|
+
const b = base.filter((x) => typeof x === "string");
|
|
787
|
+
const o = ours.filter((x) => typeof x === "string");
|
|
788
|
+
const t = theirs.filter((x) => typeof x === "string");
|
|
789
|
+
if (eq(o, t) || eq(b, t)) return o;
|
|
790
|
+
if (eq(b, o)) return t;
|
|
791
|
+
const moved = (side) => {
|
|
792
|
+
const at = new Map(b.map((id, i) => [id, i]));
|
|
793
|
+
const kept = side.filter((id) => at.has(id));
|
|
794
|
+
const run = longestRun(kept.map((id) => at.get(id)));
|
|
795
|
+
const keep = new Set(run.map((i) => b[i]));
|
|
796
|
+
return new Set(kept.filter((id) => !keep.has(id)));
|
|
797
|
+
};
|
|
798
|
+
const movedO = moved(o), movedT = moved(t);
|
|
799
|
+
const inB = new Set(b), inO = new Set(o), inT = new Set(t);
|
|
800
|
+
const before = (side, id) => side[side.indexOf(id) - 1];
|
|
801
|
+
let clash = false;
|
|
802
|
+
for (const id of movedT) if (movedO.has(id) && before(o, id) !== before(t, id)) clash = true;
|
|
803
|
+
const out = o.filter((id) => !(inB.has(id) && !inT.has(id)));
|
|
804
|
+
for (let i = 0; i < t.length; i++) {
|
|
805
|
+
const id = t[i];
|
|
806
|
+
const added = !inB.has(id) && !inO.has(id);
|
|
807
|
+
if (!added && !(movedT.has(id) && !movedO.has(id))) continue;
|
|
808
|
+
const from = out.indexOf(id);
|
|
809
|
+
if (from >= 0) out.splice(from, 1);
|
|
810
|
+
let after = -1;
|
|
811
|
+
for (let j = i - 1; j >= 0 && after < 0; j--) after = out.indexOf(t[j]);
|
|
812
|
+
out.splice(after + 1, 0, id);
|
|
813
|
+
}
|
|
814
|
+
if (clash) conflicts.push({ id: "", path, base: b, ours: o, theirs: t, kind: "moved" });
|
|
815
|
+
return out;
|
|
816
|
+
}
|
|
817
|
+
function longestRun(xs) {
|
|
818
|
+
const tails = [], prev = new Array(xs.length).fill(-1), tailAt = [];
|
|
819
|
+
xs.forEach((x, i) => {
|
|
820
|
+
let lo = 0, hi = tails.length;
|
|
821
|
+
while (lo < hi) {
|
|
822
|
+
const mid = lo + hi >> 1;
|
|
823
|
+
if (tails[mid] < x) lo = mid + 1;
|
|
824
|
+
else hi = mid;
|
|
825
|
+
}
|
|
826
|
+
tails[lo] = x;
|
|
827
|
+
tailAt[lo] = i;
|
|
828
|
+
prev[i] = lo > 0 ? tailAt[lo - 1] : -1;
|
|
829
|
+
});
|
|
830
|
+
const run = [];
|
|
831
|
+
for (let i = tailAt[tails.length - 1] ?? -1; i >= 0; i = prev[i]) run.unshift(xs[i]);
|
|
832
|
+
return run;
|
|
833
|
+
}
|
|
769
834
|
function mergeProjectExtensions(b, o, t, conflicts) {
|
|
770
835
|
const out = {};
|
|
771
836
|
for (const id of /* @__PURE__ */ new Set([...Object.keys(b), ...Object.keys(o), ...Object.keys(t)])) {
|
|
@@ -1210,7 +1275,7 @@ import {
|
|
|
1210
1275
|
} from "@patterkit/model";
|
|
1211
1276
|
|
|
1212
1277
|
// src/loaded-helpers.ts
|
|
1213
|
-
import { RERECORD_STATUS, effectiveGameId } from "@patterkit/model";
|
|
1278
|
+
import { RERECORD_STATUS, effectiveGameId, sceneFolderLayout as sceneFolderLayout2, sceneFolderPath } from "@patterkit/model";
|
|
1214
1279
|
function effectiveRecording(id, base, rerecord, lowest) {
|
|
1215
1280
|
return rerecord.has(id) ? RERECORD_STATUS : base.get(id) ?? lowest;
|
|
1216
1281
|
}
|
|
@@ -1273,6 +1338,16 @@ function mergeAuthoring2(loaded) {
|
|
|
1273
1338
|
}
|
|
1274
1339
|
return { writing, recording, cut, rerecord, documentation, edits };
|
|
1275
1340
|
}
|
|
1341
|
+
function sceneFolderPaths(loaded) {
|
|
1342
|
+
const out = /* @__PURE__ */ new Map();
|
|
1343
|
+
if (!loaded.project.sceneFolders?.length) return out;
|
|
1344
|
+
const layout = sceneFolderLayout2(loaded.project, loaded.scenes.map((s) => s.id));
|
|
1345
|
+
for (const s of loaded.scenes) {
|
|
1346
|
+
const path = sceneFolderPath(loaded.project, layout, s.id);
|
|
1347
|
+
if (path) out.set(s.id, path);
|
|
1348
|
+
}
|
|
1349
|
+
return out;
|
|
1350
|
+
}
|
|
1276
1351
|
|
|
1277
1352
|
// src/documentation.ts
|
|
1278
1353
|
import { DEFAULT_DOCUMENTATION_CLASSES } from "@patterkit/model";
|
|
@@ -2255,7 +2330,7 @@ function runValidate(loaded) {
|
|
|
2255
2330
|
const unresolvedMerges = [...sidecarIssues(tree.get(CONFLICT_SIDECAR)), ...markedShards(SHARD_EXTENSIONS.flatMap((ext) => tree.get(ext)))];
|
|
2256
2331
|
const orphans = orphanShards(loaded, tree);
|
|
2257
2332
|
const extensions = [...extensionIssues(loaded), ...gitattributesHint(loaded)];
|
|
2258
|
-
const structuralErrors = structural.filter((i) => i.severity
|
|
2333
|
+
const structuralErrors = structural.filter((i) => i.severity === void 0);
|
|
2259
2334
|
const reachability = compiled && structuralErrors.length === 0 && !conditions.some(isError) ? reachabilityIssues(loaded, compiled) : [];
|
|
2260
2335
|
return {
|
|
2261
2336
|
structural,
|
|
@@ -2277,7 +2352,7 @@ function exportBlockers(loaded) {
|
|
|
2277
2352
|
const { project, scenes, locales } = loaded;
|
|
2278
2353
|
const merged = loaded.gameScopes?.merged;
|
|
2279
2354
|
const foreignScopes = hostScopesToSpec2(projectScopes2(project, merged).host);
|
|
2280
|
-
const structural = validateProject({ project, scenes, authoring: loaded.authoring }).filter((i) => i.severity
|
|
2355
|
+
const structural = validateProject({ project, scenes, authoring: loaded.authoring }).filter((i) => i.severity === void 0);
|
|
2281
2356
|
const conditions = validateConditions({ project, scenes }, { foreignScopes, gameScopes: merged }).filter(isError);
|
|
2282
2357
|
const interpolation = validateInterpolation({ project, scenes, locales }, { foreignScopes, gameScopes: merged }).filter(isError);
|
|
2283
2358
|
return [
|
|
@@ -2629,7 +2704,7 @@ import { canonicalStringify as canonicalStringify5 } from "@patterkit/core";
|
|
|
2629
2704
|
import { DEFAULT_PAD_AFTER } from "@patterkit/model";
|
|
2630
2705
|
|
|
2631
2706
|
// src/playable-runtime.ts
|
|
2632
|
-
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var de=Object.defineProperty;var xt=Object.getOwnPropertyDescriptor;var kt=Object.getOwnPropertyNames;var Et=Object.prototype.hasOwnProperty;var Dt=(r,e)=>{for(var t in e)de(r,t,{get:e[t],enumerable:!0})},Ct=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of kt(e))!Et.call(r,s)&&s!==t&&de(r,s,{get:()=>e[s],enumerable:!(n=xt(e,s))||n.enumerable});return r};var It=r=>Ct(de({},"__esModule",{value:!0}),r);var rn={};Dt(rn,{Checkpoint:()=>K,DEFAULT_PAD_AFTER:()=>O,Engine:()=>ae,Flow:()=>N,buildTagIndex:()=>ie,compareStages:()=>B,describeBundle:()=>gt,effectiveGameData:()=>ht,estimateLineDuration:()=>Ve,extensionDecls:()=>mt,extensionsOf:()=>yt,gameDataFields:()=>ft,gameDataValue:()=>ke,nextStage:()=>F,resolveSceneRef:()=>ne,sceneAddressIndex:()=>ye,stageIndex:()=>A});function M(r,e){return r!==void 0&&Object.prototype.hasOwnProperty.call(r,e)?r[e]:void 0}function I(r){switch(r[0]){case"b":return{kind:"bool",value:r[1]};case"n":return{kind:"number",value:r[1]};case"s":return{kind:"string",value:r[1]};case"sv":return{kind:"scopedvar",scope:r[1],name:r[2]};case"u":return{kind:"unary",op:r[1],operand:I(r[2])};case"bin":return{kind:"binary",op:r[1],left:I(r[2]),right:I(r[3])};case"call":{let e=r.slice(2).map(I);return{kind:"call",name:r[1],args:e}}case"fd":return{kind:"flagdelta",sign:r[1],name:r[2]}}}var u=class extends Error{constructor(e){super(e),this.name="EvalError"}};function X(r,e,t){let n=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=M(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):M(o,i.name);if(a===void 0){if(n.get(i.scope)==="throw")throw new u(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{if(i.name==="advance"&&!M(t.functions,i.name)){let a=i.args[0];if(i.args.length!==1||a===void 0)throw new u(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=pe(a,e);if(c===void 0)throw new u("advance() needs a quality reference (@scope.name of a quality property)");let l=v(s(a),c,"advance");return c[Math.min(l+1,c.length-1)]}let o=M(t.functions,i.name);if(!o)throw new u(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new u("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=s(i.operand);if(typeof a!="boolean")throw new u(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new u(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let g=s(i.left);if(typeof g!="boolean")throw new u(`\'and\' requires boolean operands, left is ${typeof g}`);if(!g)return!1;let f=s(i.right);if(typeof f!="boolean")throw new u(`\'and\' requires boolean operands, right is ${typeof f}`);return f}if(i.op==="or"){let g=s(i.left);if(typeof g!="boolean")throw new u(`\'or\' requires boolean operands, left is ${typeof g}`);if(g)return!0;let f=s(i.right);if(typeof f!="boolean")throw new u(`\'or\' requires boolean operands, right is ${typeof f}`);return f}let o=s(i.left),a=s(i.right),c=pe(i.left,e),l=pe(i.right,e),d=c??l;if(d!==void 0){if(c&&l&&!Rt(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new u(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return v(o,d,">")>v(a,d,">");case">=":return v(o,d,">=")>=v(a,d,">=");case"<":return v(o,d,"<")<v(a,d,"<");case"<=":return v(o,d,"<=")<=v(a,d,"<=");case"+":case"-":case"*":case"/":throw new u(`\'${i.op}\' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(i.op){case"==":return Pe(o,a);case"!=":return!Pe(o,a);case">":return R(o,a,">"),o>a;case">=":return R(o,a,">="),o>=a;case"<":return R(o,a,"<"),o<a;case"<=":return R(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new u(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return R(o,a,"-"),o-a;case"*":return R(o,a,"*"),o*a;case"/":if(R(o,a,"/"),a===0)throw new u("division by zero");return o/a}}}};return s(r)}function Pe(r,e){if(Array.isArray(r)||Array.isArray(e)){if(!Array.isArray(r)||!Array.isArray(e)||r.length!==e.length)return!1;let t=[...r].sort(),n=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==n[s])return!1;return!0}return r===e}function R(r,e,t){if(typeof r!="number"||typeof e!="number")throw new u(`\'${t}\' requires numeric operands, got ${typeof r} and ${typeof e}`)}function pe(r,e){if(!(r.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(r.scope,r.name)}function v(r,e,t){if(typeof r!="string")throw new u(`\'${t}\' on a quality compares stages, got ${typeof r}`);let n=e.indexOf(r);if(n<0)throw new u(`"${r}" is not a stage of this quality (stages: ${e.join(", ")})`);return n}var Rt=(r,e)=>r.length===e.length&&r.every((t,n)=>t===e[n]);function j(r){if(Number.isNaN(r)||!Number.isFinite(r))return 0;let e=Math.trunc(r)%4294967296;return e<0?e+4294967296:e}function ue(r){let e=j(r);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 fe=r=>Array.isArray(r)?r:r.stages??[],Ot=r=>Array.isArray(r)?"flags":r==null?"nothing":typeof r;function A(r,e){return typeof e=="string"?fe(r).indexOf(e):-1}function ge(r,e){if(typeof e!="string")throw new u(`a stage is a name, got ${Ot(e)}`);let t=r.indexOf(e);if(t<0)throw new u(`"${e}" is not a stage of this quality (stages: ${r.join(", ")})`);return t}function B(r,e,t){let n=fe(r),s=ge(n,e)-ge(n,t);return s<0?-1:s>0?1:0}function F(r,e){let t=fe(r);return t[Math.min(ge(t,e)+1,t.length-1)]}function $(r,e,t){switch(r.type){case"boolean":return typeof e=="boolean";case"number":return typeof e=="number";case"string":case"text":case"multiline":return typeof e=="string";case"enum":return typeof e!="string"?!1:e===""&&t?.noneChosen?!0:!Array.isArray(r.values)||r.values.includes(e);case"flags":return Array.isArray(e)&&e.every(n=>typeof n=="string"&&(!Array.isArray(r.values)||r.values.includes(n)));case"quality":return typeof e=="string"&&(!Array.isArray(r.stages)||r.stages.includes(e));default:return!0}}var Pt={name:"check_flags",count:r=>Math.max(1,r.args.length-1)},Nt=[Pt];function Ne(r,e,t){let n=t?.countingCalls??Nt;return Y(r,t?.want??!0,e,n)}function Y(r,e,t,n){if(r.kind==="binary"&&(r.op==="and"||r.op==="or")){let s=Y(r.left,e,t,n),i=Y(r.right,e,t,n);return r.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(r.kind==="unary"&&r.op==="not")return Y(r.operand,!e,t,n);if(r.kind==="call"){let s=n.find(i=>i.name===r.name);if(s){let i=s.count(r),o=t(r);return e?o?i:0:o?0:1}}return t(r)===e?1:0}var E=class r{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(n=>n.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let n=this.norm(t.name);this.decls.set(n,t),this.values[n]=structuredClone(t.default??L(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,n){let s=this.norm(e);if(!n?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:n?.silent??!1,reason:n?.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])=>Be(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new r([],{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,t){let n=new Map;for(let[i,o]of Object.entries(e)){let a=this.norm(i);this.values[a]=structuredClone(o),n.set(a,o)}let s=[];if(!t?.refit)return s;for(let[i,o]of this.decls){if(!n.has(i))continue;let a=n.get(i);$(o,a,{noneChosen:o.type==="enum"&&o.default===""})||(this.values[i]=structuredClone(L(o)),s.push({name:i,value:a}))}return s}};function Be(r,e,t,n,s=""){let i=n??r.name.toLowerCase();return{name:i,path:s+i,type:r.type,value:e,default:r.default??L(r),...r.values!==void 0?{values:r.values}:{},...r.stages!==void 0?{stages:r.stages}:{},writable:t??r.writable??!0}}var At=r=>r.toLowerCase(),he=1,Z=class{scopes=new Map;parked=new Map;parkedRefit=new Set;misfits=[];rev=0;get revision(){return this.rev}defineOwned(e,t,n){let s=typeof n=="string"?{pathPrefix:n}:n??{},i=new E(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,i,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,n){this.assertFree(e,n?.owner),this.scopes.set(e,{kind:"owned",bag:t,...n?.owner!==void 0?{owner:n.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(this.note(e,t.load(s,{refit:this.parkedRefit.has(e)})),this.parked.delete(e),this.parkedRefit.delete(e)),this}remove(e,t){let n=this.scopes.get(e);if(!n)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&n.kind==="owned"&&(this.parked.set(e,n.bag.save()),this.parkedRefit.delete(e)),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear(),this.parkedRefit.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&(this.parked.delete(t),this.parkedRefit.delete(t));return this}takeMisfits(e){if(!e){let n=this.misfits;return this.misfits=[],n}let t=this.misfits.filter(n=>e(n.scope));return this.misfits=this.misfits.filter(n=>!e(n.scope)),t}note(e,t){for(let n of t)this.misfits.push({scope:e,name:n.name,value:n.value})}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,n=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??At,a=new Map;for(let c of n)a.set(o(c.name),c);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:i.writable??!0,norm:o,...i.owner!==void 0?{owner:i.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let n=this.scopes.get(e);if(n)return n.kind==="owned"?n.bag.get(t):n.resolver.get(n.norm(t))}set(e,t,n,s){let i=this.scopes.get(e);if(!i)throw new Error(`unknown scope \'@${e}\'`);if(i.kind==="owned"){if(!s?.host&&!i.bag.writable(t))throw new Error(`\'@${e}.${t}\' is read-only`);i.bag.set(t,n,s?.host?{host:!0}:void 0);return}let o=i.norm(t);if(!i.resolver.set)throw new Error(`\'@${e}.${t}\' is read-only`);if(!s?.host&&!this.foreignWritable(i,o))throw new Error(`\'@${e}.${t}\' is read-only`);i.resolver.set(o,n)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,n]of this.scopes){let s=n.owner!==void 0?{owner:n.owner}:{};if(n.kind==="owned")for(let i of n.bag.rows())e.push({scope:t,...s,...i});else for(let[i,o]of n.decls)e.push({scope:t,...s,...Be(o,n.resolver.get(i),this.foreignWritable(n,i),i,`${t}.`)})}return e}toEvalContext(e,t){let n=this.view(t?.aliases),s={};for(let[o,a]of n)s[o]=a.kind==="owned"?a.bag.values:a.resolver;let i=this.qualityLadders(n);return i.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(o,a)=>{let c=n.get(o);return c?i.get(o)?.get(Bt(c)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[n,s]of Object.entries(e??{})){let i=this.scopes.get(s);if(!i)throw new Error(`alias \'@${n}\' names \'${s}\', which is not registered`);t.set(n,i)}return t}qualityLadders(e){let t=new Map;for(let[n,s]of e)for(let[i,o]of Ae(s)){if(o.type!=="quality"||o.stages===void 0)continue;let a=t.get(n);a||(a=new Map,t.set(n,a)),a.set(i,o.stages)}return t}toSchema(e){let t=new Map;for(let[n,s]of this.view(e?.aliases)){let i=Ae(s);if(i.length===0)continue;let o=new Map;for(let[a,c]of i)o.set(a,{type:c.type,enumValues:c.values,...c.stages!==void 0?{stages:c.stages}:{}});t.set(n,o)}return{properties:t}}save(){let e={};for(let[t,n]of this.scopes)n.kind==="owned"&&(e[t]=n.bag.save());for(let[t,n]of this.parked)e[t]=structuredClone(n);return e}load(e,t){t?.keepParked||(this.parked.clear(),this.parkedRefit.clear());let n=t?.refit===!0;for(let[s,i]of Object.entries(e)){let o=this.scopes.get(s);o?.kind==="owned"?this.note(s,o.bag.load(i,{refit:n})):o||(this.parked.set(s,structuredClone(i)),n?this.parkedRefit.add(s):this.parkedRefit.delete(s))}}saveFragment(){return{version:he,scopes:this.save()}}loadFragment(e){if(e.version!==he)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${he})`);this.load(e.scopes)}assertFree(e,t){let n=this.scopes.get(e);if(!n)return;let s=n.owner!==void 0?` by ${n.owner}`:"",i=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope \'@${e}\' is already registered${s}${i}`)}};function Ae(r){return r.kind==="foreign"?[...r.decls.entries()]:r.bag.declarations().map(e=>[r.bag.normalise(e.name),e])}function Bt(r){return r.kind==="foreign"?r.norm:e=>r.bag.normalise(e)}function L(r){if(r.default!==void 0)return r.default;switch(r.type){case"boolean":return!1;case"number":return 0;case"string":case"text":case"multiline":return"";case"enum":return r.values?.[0]??"";case"flags":return[];case"quality":return r.stages?.[0]??"";default:return!1}}var Fe=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function G(r){return r.ctx.host??{}}var me={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(r,e){if(r.length!==2)throw new u("random(a, b) requires exactly 2 arguments");let t=G(e).nextRandom;if(!t)throw new u("random() called without a PRNG in context");let n=e.evaluate(r[0]),s=e.evaluate(r[1]);if(typeof n!="number"||typeof s!="number")throw new u("random(a, b) arguments must be numbers");if(!Number.isInteger(n)||!Number.isInteger(s))throw new u("random(a, b) arguments must be integers");let i=Math.min(n,s),o=Math.max(n,s);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:Te("check_flags"),eval(r,e){let t=Le(r[0],e,"check_flags");for(let n=1;n<r.length;n++){let s=r[n];if(s.kind!=="flagdelta")throw new u("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:Te("set_flags"),eval(r,e){let t=[...Le(r[0],e,"set_flags")];for(let n=1;n<r.length;n++){let s=r[n];if(s.kind!=="flagdelta")throw new u("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let i=t.indexOf(s.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:te("visits"),eval:(r,e)=>G(e).visits?.(ee(r,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:te("seen"),eval:(r,e)=>(G(e).visits?.(ee(r,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:te("patter_visits"),eval:(r,e)=>G(e).patterVisits?.(ee(r,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:te("patter_seen"),eval:(r,e)=>(G(e).patterVisits?.(ee(r,e,"patter_seen"))??0)>0}}},An=Fe.map(r=>r.token).filter(r=>r!=="patter"&&r!=="scene");function Me(r,e){let t=r.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function ee(r,e,t){let n=e.evaluate(r[0]);if(typeof n!="string")throw new u(`${t}(id) requires a string node id`);return n}function te(r){return(e,t)=>{let n=e[0];n&&n.kind!=="string"&&t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${r}(id): the argument must be a string id literal (a scene / block / node id)`})}}function Le(r,e,t){if(!r)throw new u(`${t}() requires at least one argument (the flags variable)`);let n=e.evaluate(r);if(Array.isArray(n))return n;if(n===!1||n===null||n===void 0)return[];throw new u(`${t}() first argument must be a flags property`)}function Te(r){return(e,t)=>{if(e.length===0)return;let n=e[0];if(n.kind!=="scopedvar"){t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${r}(): first argument must be a flags property reference (@name or @scope.name)`});return}let s=t.schema.properties.get(n.scope)?.get(n.name);if(s&&s.type!=="flags"){let i=n.scope===t.defaultScope?n.name:`${n.scope}.${n.name}`;t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${r}(): \'@${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:`${r}(): 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:`${r}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var Ft=/^@[A-Za-z0-9_.]+$/;function*Lt(r){let e="",t=0;for(;t<r.length;){let n=r[t];if(n==="{"&&r[t+1]==="{"){e+="{",t+=2;continue}if(n==="}"&&r[t+1]==="}"){e+="}",t+=2;continue}if(n==="{"){let s=r.indexOf("}",t+1);if(s!==-1){let i=r.slice(t,s+1),o=r.slice(t+1,s).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:Ft.test(o)?o:void 0},t=s+1;continue}e+=i,t=s+1;continue}}e+=n,t+=1}e&&(yield{kind:"text",value:e})}function Tt(r){return Array.isArray(r)?r.join(", "):typeof r=="boolean"?r?"true":"false":String(r)}function Mt(r){return r===" "||r===" "||r===`\n`||r==="\\r"||r==="\\f"||r==="\\v"}function jt(r){let e="",t=!1;for(let n of r){if(Mt(n)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=n}return e}function je(r,e,t){if(e.length===0||r.indexOf(e)<0)return r;let n="",s=0,i=!1;for(;s<r.length;){if(r.startsWith(e,s)){let o=r.indexOf(t,s+e.length);if(o>=0){s=o+t.length,i=!0;continue}n+=r.slice(s);break}n+=r[s],s+=1}return i?jt(n):r}function $e(r,e){if(r.indexOf("{")<0)return r;let t="";for(let n of Lt(r)){if(n.kind==="text"){t+=n.value;continue}if(!n.ref){t+=n.raw;continue}let s=e(n.ref);t+=s===void 0?"":Tt(s)}return t}function Ge(r){return r.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function D(r,e){for(let t of r){e(t);let n=t.children;n&&D(n,e)}}function y(r){let e=r.gameId?.trim();return e||Ge(r.name)}function ye(r){let e=new Map;for(let[t,n]of Object.entries(r))e.set(y(n),t);return e}function ne(r,e,t){return(t??ye(r)).get(e)??(Object.hasOwn(r,e)?e:void 0)}var O=.6;var $t=14,Gt=1.5,Vt=18;function Ve(r,e){let t=0;for(let n of r??"")n!=="\\xAD"&&t++;return Math.min(Math.max(t/$t,Gt),Vt)}function re(r){return`cast:${r}`}function se(r){return`qualifier:${r}`}function qe(r,e){return`speaker:${r}:${e}`}var _e="speaker:pattern",He="{name} ({qualifier})";function Ue(r,e,t){return r.split("{name}").map(n=>n.split("{qualifier}").join(t)).join(e)}var Se={open:"[",close:"]"},We="SFX";var V=4;function q(r){let e=new Set,t=[];for(let n of r)e.has(n)||(e.add(n),t.push(n));return t}function ie(r){let e=new Map,t=(n,s)=>{let i=q([...s,...n.tags??[]]);if(e.set(n.id,i),n.type==="group"&&n.prompt&&e.set(n.prompt.id,q([...i,...n.prompt.tags??[]])),n.type==="group")for(let o of n.children)t(o,i);else for(let o of n.beats??[])e.set(o.id,q([...i,...o.tags??[]]))};for(let n of Object.values(r.scenes)){let s=q(n.tags??[]);e.set(n.id,s);for(let i of n.blocks){let o=q([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var Ke=r=>r.kind==="line"||r.kind==="text",_=r=>typeof r=="number"&&Number.isFinite(r)?r:void 0;function Je(r){let e=new Map,t=(i,o,a)=>{let c=i.kind==="gameEvent"?void 0:_(i.padAfter),l=c??o;a&&l<0&&(l=0),e.set(i.id,c!==void 0?{own:c,resolved:l}:{resolved:l})},n=(i,o)=>{let a=_(i.padAfterDefault)??o;if(i.type==="group"){i.prompt&&t(i.prompt,a,!1);for(let d of i.children)n(d,a);return}let c=i.beats??[],l=-1;c.forEach((d,g)=>{Ke(d)&&(l=g)}),c.forEach((d,g)=>{Ke(d)&&t(d,a,g===l)})},s=_(r.padAfterDefault)??O;for(let i of Object.values(r.scenes)){let o=_(i.padAfterDefault)??s;for(let a of i.blocks){let c=_(a.padAfterDefault)??o;for(let l of a.children)n(l,c)}}return e}var Qe=new WeakMap,ze=new WeakMap;function qt(r){let e=ze.get(r);return e||(e=ie(r),ze.set(r,e)),e}var Xe=new WeakMap;function _t(r){let e=Xe.get(r);return e||(e=Je(r),Xe.set(r,e)),e}var Ye=new WeakMap;function Ht(r){let e=Ye.get(r);if(e)return e;e=new Map;for(let t of Object.values(r.scenes))for(let n of t.blocks)D(n.children,s=>{if(s.type==="group"&&s.selector==="choice")for(let i of s.children)e.set(i.id,s.id)});return Ye.set(r,e),e}var Ze=new WeakMap;function Ut(r){let e=Ze.get(r);if(e)return e;e=new Map;for(let t of Object.values(r.scenes))for(let n of t.blocks)D(n.children,s=>{if(s.type==="group"){s.prompt&&e.set(s.prompt.id,s.prompt);return}for(let i of s.beats??[])e.set(i.id,i)});return Ze.set(r,e),e}function b(r){if(r===void 0)return;let e={};for(let[t,n]of Object.entries(r))e[t]=Array.isArray(n)?[...n]:n;return e}function P(r,e){return r.tagIndex.get(e)?.slice()}var U="Patter",H=r=>r.replace(/%/g,"%25").replace(/\\//g,"%2F"),Wt=r=>r.replace(/%2F/g,"/").replace(/%25/g,"%"),et=Symbol("ownsRegistry"),tt=Symbol("setupReported"),w={stage:r=>`patter/scene/${H(r)}`,flow:r=>`patter/flow/${H(r)}/`,flowGlobals:r=>`patter/flow/${H(r)}/patter`,flowScene:(r,e)=>`patter/flow/${H(r)}/scene/${H(e)}`},K=class{constructor(e){this.journal=e}journal},ae=class r{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;requestedLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;engineLog=[];engineSeq=0;engineTraceHandlers=new Set;constructor(e,t={}){this.creationOptions=t;let n=[];for(let p of Object.keys(t))Jt.has(p)||n.push({node:p,message:p==="world"?\'the engine option "world" has gone: bind the scope as hostScopes: { world }\':`"${p}" isn\'t an engine option, so it\'s ignored`});let s=e.strings;this.allStrings=s,this.requestedLocale=t.locale,this.currentLocale=we(e,t.locale),this.currentLocale.problem&&n.push({node:t.locale,message:this.currentLocale.problem});let i=ot(s,this.currentLocale),o=s[e.locales.default]??{},a=e.localisation,c=a?.mode==="ids"&&!a.sourceDebug;this.sourceDebug=a?.mode==="ids"&&!!a.sourceDebug,this.sourceDebug&&typeof console<"u"&&console.warn("[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.");let l=new Map;for(let p of e.cast??[])p.displayName&&l.set(p.name,p.displayName);let d=new Map;for(let p of e.qualifiers??[])p?.gameId&&d.set(p.gameId,p.name);let g=new Map;for(let p of e.cast??[])for(let[m,h]of Object.entries(p.qualifierLabels??{}))typeof h=="string"&&g.set(`${p.name}\\0${m}`,h);let f=e.speakerPattern??He;this.defaultSeed=(t.seed??2654435769)>>>0;let T=new Map,Ee=new Map,De=new Map;for(let[p,m]of Object.entries(e.scenes)){this.sceneGameIdToId.set(y(m),p);let h=new Map;for(let S of m.blocks)Ee.set(S.id,{sceneId:p}),De.set(S.id,S),h.set(y(S),S.id),D(S.children,k=>T.set(k.id,k));this.blockGameIdToId.set(p,h)}let Ce=e.properties??[],ce=Ce.filter(p=>p.shared??!0).map(it),St=Ce.filter(p=>!(p.shared??!0)).map(it),wt=new Set(ce.map(p=>p.name.toLowerCase())),x=t.registry??new Z,Ie=!t.registry||t[et]===!0,Re=new E(ce,{pathPrefix:"@patter."}),le=[],Q=[];try{x.mountOwned("patter",Re,{owner:U}),Q.push("patter");let p=new Set,m=e.scopeRegistry?.scopes??[];for(let[h,S]of Object.entries(t.hostScopes??{})){let k=m.find(z=>z.token===h);if(!k&&!(e.externalScopes??[]).includes(h)){let z=m.find(vt=>vt.token.toLowerCase()===h.toLowerCase());n.push({node:h,message:z?`hostScopes binds "${h}", which this bundle doesn\'t declare: it declares "${z.token}", and a scope\'s token is case-sensitive`:`hostScopes binds "${h}", which this bundle doesn\'t declare`})}let bt=(k?.declarations??[]).map(pt);x.defineForeign(h,S,bt,{writable:k?.writable??!0,owner:U}),Q.push(h),p.add(h)}for(let h of Ie?e.scopeRegistry?.scopes??[]:[]){if(p.has(h.token)||x.has(h.token))continue;let S=(h.declarations??[]).map(k=>Xt(k,h.writable));x.defineOwned(h.token,S,{owner:U}),Q.push(h.token),le.push(h.token)}}catch(p){for(let m of Q)x.remove(m,{keep:!0});throw p}let Oe=new Map;for(let[p,m]of Object.entries(e.scenes)){let h=new Set((m.sceneProps??[]).filter(S=>S.shared??!1).map(S=>S.name.toLowerCase()));Oe.set(p,h)}if(this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(p,m,h)=>this.emitEngine(p,m,h),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:d,speakerLabels:g,speakerPattern:f,nodeIndex:T,blockIndex:Ee,blockById:De,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:qt(e),padIndex:_t(e),registry:x,ownsRegistry:Ie,patterBag:Re,hostScopes:le,patterSharedDecls:ce,patterLocalDecls:St,patterSharedNames:wt,sceneSharedNames:Oe,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(p=>console.warn(p.kind==="setup"?`Patterplay: ${p.message}`:p.kind==="save"?`Patterplay: flow \'${p.flow}\': ${p.message}`:`Patterplay: ${p.kind} on ${p.node} in flow \'${p.flow}\' failed, played through: ${p.message}`)),reportRefits:()=>{let p=x.takeMisfits(m=>m==="patter"||m.startsWith("patter/")||le.includes(m));if(p.length!==0)if(t.onRefit)t.onRefit(p);else for(let m of p)console.warn(`Patterplay: the save\'s ${m.scope}.${m.name} was ${JSON.stringify(m.value)}, which no longer fits its declaration; it is back at its default`)},replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??Se).open,captionClose:(e.closedCaptions??Se).close,captionCharacter:e.closedCaptions?.character||We,refSplitCache:new Map,refSplitRevision:x.revision},this.host.reportRefits(),t[tt]!==!0)for(let p of n)this.reportSetup(p.node,p.message)}reportSetup(e,t){this.host.onError({flow:"",kind:"setup",node:e,message:t}),this.host.tracing&&this.emitEngine("",{type:"diagnostic",kind:"setup",node:e,message:t})}get locale(){return this.currentLocale.locale}get locales(){let{default:e,included:t}=this.host.bundle.locales;return{default:e,included:[...t]}}get isSourceDebug(){return this.sourceDebug}get buildId(){return this.host.bundle.content?.hash??""}setLocale(e){this.requestedLocale=e,this.useLocale(we(this.host.bundle,e)),this.currentLocale.problem&&this.reportSetup(e,this.currentLocale.problem)}useLocale(e){this.currentLocale=e,this.host.strings=ot(this.allStrings,e)}replaceStrings(e){this.allStrings=e.strings;let t=this.currentLocale.problem;this.useLocale(we(e,this.requestedLocale)),this.host.defaultStrings=this.allStrings[this.host.bundle.locales.default]??{};let n=this.currentLocale.problem;n&&n!==t&&this.reportSetup(this.requestedLocale,n)}hotSwap(e){this.refuseInCheckpoint("hotSwap");let t=this.saveGame(),n=l=>(l.setClosedCaptions(this.host.captionsOn),l),s=this.requestedLocale,i=(l=!1)=>new r(e,{...this.creationOptions,...s!==void 0?{locale:s}:{},registry:this.host.registry,[et]:this.host.ownsRegistry,[tt]:l}),o=new Set(["patter",...this.host.hostScopes]),a=this.host.ownsRegistry?void 0:Object.fromEntries(Object.entries(this.host.registry.save()).filter(([l])=>o.has(l)||l.startsWith("patter/")));this.release(!0),a&&this.host.registry.load(a,{keepParked:!0,refit:!0});let c=i();try{return c.loadGame(t),n(c)}catch{c.release(!0);let l=this.host.ownsRegistry?t.registry:a;l&&this.host.registry.load(l,{keepParked:!0,refit:!0});let d=i(!0);for(let[g,f]of Object.entries(t.flows)){let T=f.cursor.currentSceneId;try{d.openFlow(g,T!==null?{scene:T}:{})}catch{}}return n(d)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:n,blockId:s}=this.resolveOpenAddress(t.scene,t.block),i=this.flowsById.get(e),o=this.host.journal;if(o&&i&&!i.isClosed)throw new Error(`openFlow would replace the open flow \'${e}\', which can\'t be undone while a checkpoint is open`);i?.close();let a=new N(e,this.host,t.seed??this.defaultSeed);return o&&(o.flows.add(a),o.opened.add(a),o.undo.push(()=>{a.close(),i?this.flowsById.set(e,i):this.flowsById.delete(e)})),this.flowsById.set(e,a),a.begin(n,s),a}runFlow(e,t,n){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:n}).advanceToStop().played;if(!s.goto(t,n))throw new Error(`runFlow: address not found: ${t}${n===void 0?"":` / ${n}`}`);return s.advanceToStop().played}assertExternalScopes(){for(let e of this.host.bundle.externalScopes??[])if(!this.host.registry.has(e))throw new Error(`this content names @${e}, which no engine on this registry registered: give every engine the game\'s one registry`)}resolveOpenAddress(e,t){if(e!==void 0){let n=be(this.host,e);if(n===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:n};let s=ve(this.host,n,t);if(s===void 0)throw new Error(`unknown block: ${t}`);return{sceneId:n,blockId:s}}if(t!==void 0){if(!this.host.blockIndex.has(t))throw new Error(`unknown block: ${t}`);return{blockId:t}}if(Object.keys(this.host.bundle.scenes).length===0)throw new Error("no scenes in bundle");return{}}resolveSceneRef(e){if(e!=null)return be(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:ve(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?y(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?y(t):void 0}tagsForBeat(e){return P(this.host,e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?P(this.host,t):void 0)??[]}tagsForBlock(e,t){let n=this.resolveSceneRef(e),s=this.resolveBlockRef(n,t);return(s!=null?P(this.host,s):void 0)??[]}gameDataForScene(e){let t=this.resolveSceneRef(e),n=t!=null?this.host.bundle.scenes[t]:void 0;return b(n?.gameData)??{}}gameDataForBlock(e,t){let n=this.resolveBlockRef(this.resolveSceneRef(e),t),s=n!=null?this.host.blockById.get(n):void 0;return b(s?.gameData)??{}}extensionsForScene(e){let t=this.resolveSceneRef(e);return{...(t!=null?this.host.bundle.scenes[t]:void 0)?.extensions}}extensionsForBlock(e,t){let n=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(n!=null?this.host.blockById.get(n):void 0)?.extensions}}extensionsForSnippet(e){let t=this.host.nodeIndex.get(e);return t?.type==="snippet"?{...t.extensions}:{}}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),n=t!=null?this.host.bundle.scenes[t]:void 0;if(!n)return[];let s=new Set;for(let i of n.blocks)nt(i.children,s);return[...s]}castForBlock(e,t){let n=this.resolveSceneRef(e),s=this.resolveBlockRef(n,t),i=s!=null?this.host.blockById.get(s):void 0;if(!i)return[];let o=new Set;return nt(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...y(e)?{gameId:y(e)}:{},name:e.name,...rt(e.gameData),...C(e.extensions),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...y(t)?{gameId:y(t)}:{},name:t.name,...rt(t.gameData),...C(t.extensions),...this.tagsField(t.id),children:t.children.map(n=>this.outlineNode(n))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let n of t.blocks)D(n.children,s=>{if(s.type==="snippet")for(let i of s.beats??[])e.push({sceneId:t.id,blockId:n.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=P(this.host,e.id),n={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){n.character=e.character;let i=this.host.defaultStrings[re(e.character)]??this.host.castDisplay.get(e.character);i!==void 0&&(n.characterName=i)}if(e.direction!==void 0&&(n.direction=e.direction),e.qualifier!==void 0){n.qualifier=e.qualifier;let i=this.host.defaultStrings[se(e.qualifier)]??this.host.qualifierDisplay.get(e.qualifier);i!==void 0&&(n.qualifierName=i)}let s=oe(this.host,e.character,e.qualifier,!0);s!==void 0&&(n.speakerLabel=s)}if(e.kind==="line"||e.kind==="text"){let s=this.host.padIndex.get(e.id);n.padAfter=s?.resolved??O,s?.own!==void 0&&(n.ownPadAfter=s.own);let i=this.host.defaultStrings[e.id];i!==void 0&&(n.text=i)}return e.gameData&&Object.keys(e.gameData).length&&(n.gameData=b(e.gameData)),e.extensions&&Object.keys(e.extensions).length&&(n.extensions=e.extensions),t&&t.length&&(n.tags=t),n}tagsField(e){let t=P(this.host,e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}listBags(){let e=[{bag:this.host.patterBag}];for(let[t,n]of this.host.stageBags)e.push({bag:n,pathPrefix:`@scene:${t}.`});return e}checkpoint(){if(this.host.journal)throw new Error("a checkpoint is already open: roll it back or commit it first");let e={undo:[],flows:new Set,opened:new Set,selectors:new Set,flowSelectors:new Map};return this.host.journal=e,new K(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let n=t.length-1;n>=0;n--)t[n]();r.forget(e.journal)}commit(e){this.endCheckpoint(e),r.forget(e.journal)}get inCheckpoint(){return this.host.journal!==null}endCheckpoint(e){if(this.host.journal!==e.journal)throw new Error("that checkpoint is not the open one");this.host.journal=null}static forget(e){e.undo=[],e.flows.clear(),e.opened.clear(),e.selectors.clear(),e.flowSelectors.clear()}refuseInCheckpoint(e){if(this.host.journal)throw new Error(`${e} can\'t be undone, so it isn\'t allowed while a checkpoint is open`)}captureFlow(e){let t=this.flowsById.get(e);if(!t)throw new Error(`no open flow \'${e}\' to capture`);return{version:V,...t.snapshot(),...Zt(e,this.host.registry.save())}}restoreFlow(e,t){this.refuseInCheckpoint("restoreFlow");let n=t.version;if(n!==3&&n!==V)throw new Error(`unsupported flow state version: ${String(n)}`);let s=t.cursor;if(typeof s!="object"||s===null)throw new Error("malformed flow state: no cursor");if(this.flowsById.has(e))throw new Error(`a flow \'${e}\' is already open; close it before restoring another under that id`);this.assertExternalScopes();let i=w.flow(e);this.host.registry.discardParked(i),this.host.registry.load(Yt(e,t),{keepParked:!0,refit:!0});let o=new N(e,this.host,this.defaultSeed);try{o.restore(t)}catch(a){throw o.close(),this.host.registry.discardParked(i),a}return this.flowsById.set(e,o),this.host.reportRefits(),o}closeFlow(e){this.refuseInCheckpoint("closeFlow"),this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){this.refuseInCheckpoint("reset");for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.patterBag.reseed(this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear();for(let e of this.host.stageBags.keys())this.host.registry.remove(w.stage(e));this.host.stageBags.clear(),this.host.registry.discardParked("patter/")}release(e){for(let n of this.flowsById.values())n.releaseBags(e),n.close();this.flowsById.clear();let t=this.host.registry;for(let n of this.host.stageBags.keys())t.remove(w.stage(n),{keep:e});this.host.stageBags.clear();for(let n of["patter",...this.host.hostScopes])t.has(n)&&t.remove(n,{keep:e});for(let n of Object.keys(this.creationOptions.hostScopes??{}))t.has(n)&&t.remove(n)}getProperty(e){let{scope:t,name:n}=this.splitShared(e);return this.host.registry.get(t,n)}setProperty(e,t){let{scope:n,name:s}=this.splitShared(e);dt(this.host,n,s,t,null);let i=this.host.journal?this.host.registry.get(n,s):void 0;this.host.registry.set(n,s,t,{host:!0}),this.host.journal&&i!==void 0&&this.host.journal.undo.push(()=>this.host.registry.set(n,s,i,{host:!0}))}stageIndex(e,t){return A(this.ladderOf(e),t)}compareStages(e,t,n){return B(this.ladderOf(e),t,n)}nextStage(e,t){return F(this.ladderOf(e),t)}ladderOf(e){let{scope:t,name:n}=this.splitShared(e),s=zt(this.host,t,n);if(!s)throw new Error(`\'${e}\' is not a quality`);return s}log(){return this.engineLog}clearLog(){this.engineLog.length=0}onTrace(e){return this.engineTraceHandlers.add(e),this.host.tracing=!0,()=>{this.engineTraceHandlers.delete(e),this.host.tracing=this.host.logEnabled||this.engineTraceHandlers.size>0}}emitEngine(e,t,n){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineSeq++,...n?{scene:n}:{}})}getVisitCounts(){return Object.fromEntries(this.host.sharedVisits)}listProperties(){return this.host.patterSharedDecls.map(e=>({name:e.name,path:`@patter.${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:L(e),writable:e.writable??!0}))}splitShared(e){let t=ut(this.host,e);if(t.scope==="scene")throw new Error(`\'${e}\': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);return t}saveGame(){let e={};for(let[t,n]of this.flowsById)e[t]=n.snapshot();return{version:V,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:ct(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==3&&t!==V)throw new Error(`unsupported save version: ${String(t)}`);let n=e.flows;if(typeof n!="object"||n===null||Array.isArray(n))throw new Error("malformed save: no flows");this.assertExternalScopes();let s=this.host.registry;for(let[o,a]of this.flowsById)a.releaseBags(o in e.flows),a.close();this.flowsById.clear();for(let o of this.host.stageBags.keys())s.remove(w.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.registry;i&&(this.host.ownsRegistry?s.load(i,{refit:!0}):s.load(i,{keepParked:!0,refit:!0})),this.host.sharedVisits.clear();for(let[o,a]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(o,a);this.host.sharedSelectors.clear();for(let[o,a]of lt(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new N(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}this.host.reportRefits()}},N=class{id;host;local;registered=new Set;rngState;started=!1;flowEnded=!1;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPrompt=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.patterBag.get(e):this.local.get(e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.registry.set("patter",e,t):this.local.set(e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;if(t!==null)return this.sceneBagFor(t,e)?.get(e)},set:(e,t)=>{let n=this.currentSceneId;if(n===null)return;let s=this.sceneBagFor(n,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,n){this.id=e,this.host=t,this.rngState=j(n),this.local=this.newLocal(),this.evalCtx={scopes:{},host:{nextRandom:this.rng,visits:s=>this.visitCounts.get(s)??0,patterVisits:s=>this.host.sharedVisits.get(s)??0},qualities:(s,i)=>this.stagesFor(s,i)}}context(){let e=this.host.registry;if(e.revision!==this.ctxRevision){let t=e.toEvalContext(),n=this.evalCtx.scopes;for(let s of Object.keys(n))delete n[s];Object.assign(n,t.scopes),n.patter=this.patterResolver,n.scene=this.sceneResolver,this.registryQualities=t.qualities,this.ctxRevision=e.revision}return this.evalCtx}stagesFor(e,t){let n=t.toLowerCase();if(e==="patter")return W(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(n);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:W(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(n)}return this.registryQualities?.(e,t)??W(this.host,`host/${e}`,()=>this.host.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(n)}begin(e,t){let n=this.host.bundle.start;if(e===void 0&&t===void 0&&n&&this.host.bundle.scenes[n.scene]&&(e=n.scene,n.block!==void 0&&this.host.blockIndex.get(n.block)?.sceneId===n.scene&&(t=n.block)),this.host.journal&&!this.host.journal.opened.has(this))throw new Error("a flow can\'t be started or reset while a checkpoint is open");if(this.releaseBags(!1),this.host.registry.discardParked(w.flow(this.id)),this.mountLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.activeSnippet=null,this.beatIndex=0,this.clearPending(),this.started=!0,t){let s=this.host.blockIndex.get(t);if(!s)throw new Error(`unknown block: ${t}`);this.enterSceneSetup(s.sceneId),this.stack=[{sceneId:s.sceneId,containerId:t,index:0}],this.enter(t)}else{let s=e??Object.keys(this.host.bundle.scenes)[0],i=s?this.host.bundle.scenes[s]:void 0;if(!i)throw new Error(s?`unknown scene: ${s}`:"no scenes in bundle");this.enterSceneSetup(s);let o=i.blocks[0];o&&(this.stack=[{sceneId:s,containerId:o.id,index:0}],this.enter(o.id))}this.settle()}reset(e,t){this.begin(e,t)}goto(e,t){if(this.closed)return!1;if(this.touch(),e==="END")return this.started=!0,this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],!0;let n=be(this.host,e);if(n===void 0)return!1;let s;return t!==void 0&&(s=ve(this.host,n,t),s===void 0)?!1:this.started?(this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.enterTarget(s??n,"jump"),this.settle(),!0):(this.begin(n,s),!0)}close(){this.releaseBags(!1),this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.clearPending()}clearPending(){this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null}touch(){let e=this.host.journal;if(!e||e.flows.has(this))return;e.flows.add(this);let{rngState:t,started:n,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:d}=this,g=this.stack.map(f=>({...f}));e.undo.push(()=>{Object.assign(this,{rngState:t,started:n,flowEnded:s,currentSceneId:i,activeSnippet:o,beatIndex:a,pendingChoice:c,pendingPrompt:l,pendingPromptOwnerId:d,stack:g})})}get isClosed(){return this.closed}get currentScene(){return this.currentSceneId}advance(){if(this.closed)return{type:"end"};if(!this.started)throw new Error("flow has not been started");if(this.touch(),this.pendingPrompt){let e=this.pendingPrompt;return this.pendingPrompt=null,this.pendingPromptOwnerId=null,e.shown?this.promptResult(e.beat,e.shown):this.beatResult(e.beat)}return this.settle(),this.flowEnded?{type:"end"}:this.pendingChoice?{type:"choice",groupId:this.pendingChoice.groupId,options:this.offer(this.pendingChoice)}:this.activeSnippet?this.beatResult(this.activeSnippet.beats[this.beatIndex++]):(this.flowEnded=!0,{type:"end"})}advanceToStop(){let e=[];for(;;){let t=this.advance();if(t.type==="choice"||t.type==="end")return{played:e,stop:t};e.push(t)}}settle(){let e=0;for(;;){if(++e>1e4)throw new Error("flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content");if(this.flowEnded||this.pendingChoice)return;if(this.activeSnippet){if(this.beatIndex<(this.activeSnippet.beats?.length??0))return;this.runEffects(this.activeSnippet.onExit,this.activeSnippet.id);let i=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(i);continue}let t=this.stack[this.stack.length-1];if(!t){this.flowEnded=!0;return}t.sceneId!==this.currentSceneId&&(this.currentSceneId=t.sceneId);let n=this.childrenOf(t.containerId);if(!n){this.stack.pop();continue}let s=t.index;for(;t.index<n.length&&!this.eligible(n[t.index]);)t.index++;if(this.host.tracing&&t.index!==s&&this.emit({type:"select",group:t.containerId,selector:"run",children:n.slice(s,t.index+1).map((i,o)=>({id:i.id,eligible:s+o===t.index})),picked:n[t.index]?.id??null}),t.index>=n.length){this.stack.pop();continue}this.enterChild(n[t.index++])}}listBags(){let e=[{bag:this.local,pathPrefix:`${this.id}/@patter.`}];for(let[t,n]of this.sceneBags)e.push({bag:n,pathPrefix:`${this.id}/@scene:${t}.`});return e}log(){return this.flowLog}clearLog(){this.flowLog.length=0}emit(e){let t=this.currentSceneId??void 0;this.host.emitEngine(this.id,e,t),this.host.logEnabled&&this.flowLog.push({...e,seq:this.flowSeq++,...t?{scene:t}:{}})}getVisitCounts(){return Object.fromEntries(this.visitCounts)}stepFor(e){let t=Ut(this.host.bundle).get(e);return t?this.beatResult(t):void 0}getChoices(){return this.pendingChoice?this.offer(this.pendingChoice):[]}offer(e){return e.options.map(t=>{let n=e.byId.get(t.id),s=this.promptFor(n);return e.shown.set(t.id,s),{id:t.id,prompt:s,eligible:t.eligible,gameData:b(t.gameData),...C(n.extensions)}})}choose(e){let t=this.pendingChoice;if(!t)throw new Error("no choice is pending");let n=t.options.find(a=>a.id===e);if(!n)throw new Error(`unknown choice option: ${e}`);if(!n.eligible)throw new Error(`choice option is not eligible: ${e}`);this.touch();let s=t.byId.get(e);this.host.tracing&&this.emit({type:"chose",group:t.groupId,option:e}),this.pendingChoice=null;let i=this.host.replayPromptOnChoose?this.authoredPromptOf(s):void 0,o=i?t.shown.has(e)?t.shown.get(e):this.promptFor(s):void 0;this.pendingPrompt=i&&o?{beat:i,shown:{...o}}:null,this.pendingPromptOwnerId=this.pendingPrompt?s.id:null,this.enterChild(s)}isEnded(){return this.flowEnded}getProperty(e){let{scope:t,name:n}=this.splitRef(e);return t==="patter"?this.patterResolver.get(n):t==="scene"?this.sceneResolver.get(n):this.host.registry.get(t,n)}setProperty(e,t){this.writeProperty(e,t,!0)}stageIndex(e,t){return A(this.ladderOf(e),t)}compareStages(e,t,n){return B(this.ladderOf(e),t,n)}nextStage(e,t){return F(this.ladderOf(e),t)}ladderOf(e){let{scope:t,name:n}=this.splitRef(e);this.context();let s=this.stagesFor(t,n);if(!s)throw new Error(`\'${e}\' is not a quality`);return s}writeProperty(e,t,n){let{scope:s,name:i}=this.splitRef(e);if(n){if(s==="scene"&&this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);dt(this.host,s,i,t,this.currentSceneId)}let o=this.host.journal;if(s==="patter"){if(o){let a=this.host.patterSharedNames.has(i),c=a?this.host.patterBag.get(i):this.local.get(i),l=this.local;c!==void 0&&o.undo.push(a?()=>this.host.registry.set("patter",i,c):()=>l.set(i,c))}this.patterResolver.set(i,t)}else if(s==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);if(o){let a=this.sceneBagFor(this.currentSceneId,i),c=a?.get(i);a&&c!==void 0&&o.undo.push(()=>a.set(i,c))}this.sceneResolver.set(i,t)}else{let a=o?this.host.registry.get(s,i):void 0;this.host.registry.set(s,i,t,n?{host:!0}:void 0),o&&a!==void 0&&o.undo.push(()=>this.host.registry.set(s,i,a,{host:!0}))}}snapshot(){let e=Ht(this.host.bundle),t=this.activeSnippet?.beats?.[this.beatIndex]?.id;return{rngState:this.rngState,visits:Object.fromEntries(this.visitCounts),cursor:{flowEnded:this.flowEnded,currentSceneId:this.currentSceneId,stack:this.stack.map(n=>{let s=this.childrenOf(n.containerId)?.[n.index],i=e.get(n.containerId);return{...n,...s?{nextId:s.id}:{},...i!==void 0?{choiceId:i}:{}}}),activeSnippetId:this.activeSnippet?.id??null,...t!==void 0?{beatId:t}:{},pendingChoice:this.pendingChoice?{groupId:this.pendingChoice.groupId,options:this.pendingChoice.options.map(n=>({id:n.id,eligible:n.eligible,...n.gameData!==void 0?{gameData:b(n.gameData)}:{}}))}:null,pendingPromptOwnerId:this.pendingPromptOwnerId,...this.pendingPrompt?.shown?{pendingPrompt:Kt(this.pendingPrompt.shown)}:{},selectors:ct(this.selectors)}}}restore(e){if(this.host.journal)throw new Error("a flow can\'t be restored while a checkpoint is open");let t=this.restoreCursor(e);this.releaseBags(!0),this.mountLocal();for(let n of new Set([this.currentSceneId,...this.stack.map(s=>s.sceneId)]))n!==null&&this.host.bundle.scenes[n]&&this.ensureSceneBags(n);t&&(this.report({flow:this.id,kind:"save",node:t.node,message:`the save\'s place has gone from this build: ${t.instead}`}),t.reoffer&&this.setupChoice(t.reoffer))}restoreCursor(e){this.rngState=j(e.rngState),this.visitCounts=new Map(Object.entries(e.visits??{}));let t=e.cursor;this.started=!0,this.flowEnded=t.flowEnded,this.currentSceneId=t.currentSceneId;let n=null;this.stack=[];for(let s of t.stack){let{nextId:i,choiceId:o,...a}=s,c=this.childrenOf(a.containerId),l=!c&&o!==void 0?this.host.nodeIndex.get(o):void 0;if(l?.type==="group"&&l.selector==="choice"){n={node:t.activeSnippetId??t.pendingChoice?.groupId??a.containerId,instead:`option ${a.containerId} has gone, so choice ${l.id} is offered again`,reoffer:l};break}let d=i!==void 0?c?.findIndex(g=>g.id===i)??-1:-1;this.stack.push(d>=0?{...a,index:d}:{...a})}if(this.activeSnippet=null,this.beatIndex=0,!n?.reoffer&&t.activeSnippetId!==null){let s=this.host.nodeIndex.get(t.activeSnippetId);if(s&&s.type==="snippet"){let i=s.beats??[];if(this.activeSnippet=s,t.beatId!==void 0){let o=i.findIndex(a=>a.id===t.beatId);this.beatIndex=o>=0?o:i.length,o<0&&(n={node:t.activeSnippetId,instead:`beat ${t.beatId} has gone from snippet ${t.activeSnippetId}, so the snippet ends there`})}else this.beatIndex=t.beatIndex??i.length}else n={node:t.activeSnippetId,instead:`snippet ${t.activeSnippetId} has gone, so play carries on after it`}}if(this.selectors=lt(t.selectors),this.pendingChoice=null,!n?.reoffer&&t.pendingChoice!==null){let s=new Map,i=[];for(let o of t.pendingChoice.options){let a=this.host.nodeIndex.get(o.id);a&&(s.set(o.id,a),i.push({id:o.id,eligible:o.eligible,...o.gameData!==void 0?{gameData:o.gameData}:{}}))}if(i.length>0)this.pendingChoice={groupId:t.pendingChoice.groupId,options:i,byId:s,shown:new Map};else{let o=this.host.nodeIndex.get(t.pendingChoice.groupId);n=o?.type==="group"&&o.selector==="choice"?{node:t.pendingChoice.groupId,instead:`none of choice ${o.id}\'s options is left, so it\'s offered again`,reoffer:o}:{node:t.pendingChoice.groupId,instead:`choice ${t.pendingChoice.groupId} has gone, so play carries on after it`}}}if(this.pendingPrompt=null,this.pendingPromptOwnerId=n?.reoffer?null:t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let s=this.host.nodeIndex.get(this.pendingPromptOwnerId),i=s?this.authoredPromptOf(s):void 0,o=t.pendingPrompt,a=o?.kind==="line"&&o.speakerLabel===void 0?oe(this.host,o.character,o.qualifier):void 0;i?this.pendingPrompt={beat:i,...o?{shown:{...o,padAfter:o.padAfter??this.padOf(i.id),...a!==void 0?{speakerLabel:a}:{}}}:{}}:this.pendingPromptOwnerId=null}return n}enterSceneSetup(e){let t=this.host.bundle.scenes[e];if(!t)throw new Error(`unknown scene: ${e}`);this.currentSceneId=e,this.enter(e),this.seedScene(t),this.runEffects(t.onEntry,t.id)}enterChild(e){if(this.enter(e.id),e.type==="snippet"){this.beginSnippet(e);return}let t=e.selector??"run";if(t==="run"){this.stack.push({sceneId:this.currentSceneId,containerId:e.id,index:0});return}if(t==="choice"){this.setupChoice(e);return}let n=this.selectChild(e);n&&this.enterChild(n)}childrenOf(e){let t=this.host.blockById.get(e);if(t)return t.children;let n=this.host.nodeIndex.get(e);if(n&&n.type==="group")return n.children}beginSnippet(e){this.runEffects(e.onEnter,e.id),this.activeSnippet=e,this.beatIndex=0}setupChoice(e){let t=[],n=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,eligible:a,...o.gameData!==void 0?{gameData:o.gameData}:{}}),n.set(o.id,o))}if(t.some(o=>o.eligible)){this.host.tracing&&this.emit({type:"choice",group:e.id,options:t.map(o=>({id:o.id,eligible:o.eligible}))}),this.pendingChoice={groupId:e.id,options:t,byId:n,shown:new Map};return}let i=s.find(o=>this.eligible(o));if(i){this.enterChild(i);return}this.host.tracing&&this.emit({type:"dry",group:e.id}),this.host.onDryChoice?.(e.id)}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(this.host.tracing&&this.emit({type:"jump",to:e,mode:t}),e==="END"){this.flowEnded=!0,this.stack=[];return}let n,s,i=this.host.bundle.scenes[e];if(i){this.enterSceneSetup(e);let a=i.blocks[0];if(!a){t==="jump"&&(this.stack=[]);return}n=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),n=a.sceneId,s=e}this.enter(s);let o={sceneId:n,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)})),n=e.children.filter((c,l)=>t[l].eligible),s=e.options?.order??"sequential",i=e.options?.exhaust??"once",o=c=>(this.host.tracing&&this.emit({type:"select",group:e.id,selector:e.selector??"default",...e.selector==="sequence"?{order:s,exhaust:i}:{},children:t,picked:c?.id??null}),c);if(n.length===0)return o(null);let a=this.selectorState(e);switch(e.selector){case"branch":return o(n[0]);case"sequence":return o(s==="shuffle"?this.pickShuffle(n,i,a):s==="specificity"?this.pickSpecificity(n,i,a):this.pickSequential(n,i,a));default:return null}}pickSequential(e,t,n){let s=e.length,i=n.seq??0;return n.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,n){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(f=>f.id),a=new Set(e.map(f=>f.id));if(n.bag===void 0&&(n.bag=o()),!n.bag.some(f=>a.has(f))){if(t==="once")return null;if(i){let f=e[s-1];return n.last=f.id,f}if(n.bag=o(),n.bag.length===0)return null}let c=n.bag.filter(f=>a.has(f)),l=n.last!==void 0&&c.length>1?c.indexOf(n.last):-1,d=Math.floor(this.rng()*(l>=0?c.length-1:c.length));l>=0&&d>=l&&d++;let g=c[d];return n.bag.splice(n.bag.indexOf(g),1),n.last=g,e.find(f=>f.id===g)}pickSpecificity(e,t,n){let s=e;if(t!=="repeat"){n.bag===void 0&&(n.bag=e.map(d=>d.id));let l=new Set(n.bag);if(s=e.filter(d=>l.has(d.id)),s.length===0)return t==="stick"&&n.last!==void 0?e.find(d=>d.id===n.last)??null:null}let i=-1,a=s.map(l=>{let d=this.specScore(l);return d>i&&(i=d),{c:l,s:d}}).filter(l=>l.s===i).map(l=>l.c),c;if(a.length===1)c=a[0];else{let l=n.last!==void 0?a.findIndex(g=>g.id===n.last):-1,d=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&d>=l&&d++,c=a[d]}return t!=="repeat"&&(n.bag=n.bag.filter(l=>l!==c.id)),n.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0,e):0}matchedSpec(e,t,n){return Ne(e,i=>{try{return at(X(i,this.context(),me))}catch(o){return this.reportError("best-match",n.id,n.condition,o),!1}},{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,n=this.host.journal,s;if(n&&(s=e.shared?n.selectors:n.flowSelectors.get(this),s||(s=new Set,n.flowSelectors.set(this,s))),n&&s&&!s.has(e.id)){s.add(e.id);let o=t.get(e.id),a=o?{...o,...o.bag?{bag:[...o.bag]}:{}}:void 0;n.undo.push(()=>{a?t.set(e.id,a):t.delete(e.id)})}let i=t.get(e.id);return i||(i={},t.set(e.id,i)),i}runEffects(e,t){for(let n of e??[]){let s;try{s=this.evalExpr(n.value);let i=this.host.tracing?this.getProperty(n.target):void 0;this.writeProperty(n.target,s,!1),this.host.tracing&&this.emit({type:"write",target:n.target,value:s,...i!==void 0?{prev:i}:{}})}catch(i){this.reportError("effect",t,n.value,i)}}}eligible(e){if(!e.condition)return!0;try{return at(this.evalExpr(e.condition))}catch(t){return this.reportError("condition",e.id,e.condition,t),!1}}reportError(e,t,n,s){let i=s instanceof Error?s.message:String(s),o=n?.src;this.report({flow:this.id,kind:e,node:t,...o?{source:o}:{},message:i})}report(e){this.host.onError(e),this.host.tracing&&this.emit({type:"diagnostic",kind:e.kind,node:e.node,...e.source?{source:e.source}:{},message:e.message})}evalExpr(e){return X(this.conditionAst(e),this.context(),me)}conditionAst(e){let t=Qe.get(e);return t||(t=I(e.ast),Qe.set(e,t)),t}enter(e){let t=this.visitCounts,n=t.get(e);t.set(e,(n??0)+1);let s=this.host.sharedVisits,i=s.get(e);s.set(e,(i??0)+1),this.host.journal&&this.host.journal.undo.push(()=>{n===void 0?t.delete(e):t.set(e,n),i===void 0?s.delete(e):s.set(e,i)})}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=ue(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=P(this.host,e.id),n=t&&t.length?{tags:t}:{};switch(e.kind){case"gameEvent":return{type:"gameEvent",id:e.id,gameData:b(e.gameData),...C(e.extensions),...n};case"text":return{type:"text",id:e.id,text:this.interpolate(this.resolveString(e.id)),padAfter:this.padOf(e.id),...e.action?{action:!0}:{},gameData:b(e.gameData),...C(e.extensions),...n};case"line":{let s=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?s:this.interpolate(s)),c=i&&a.length===0;return{type:"line",id:e.id,text:a,character:c?void 0:e.character,characterName:c?void 0:this.resolveCharacterName(e.character),direction:c?void 0:e.direction,qualifier:c?void 0:e.qualifier,qualifierName:c?void 0:this.resolveQualifierName(e.qualifier),speakerLabel:c?void 0:oe(this.host,e.character,e.qualifier),padAfter:this.padOf(e.id),gameData:b(e.gameData),...C(e.extensions),...n}}}}interpolate(e){return $e(e,t=>this.getProperty(t))}stripCaptions(e){return je(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 n=this.interpolate(this.resolveString(t.id)),s=this.padOf(t.id);return t.kind==="line"?{kind:"line",text:this.captionLine(n),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction,qualifier:t.qualifier,qualifierName:this.resolveQualifierName(t.qualifier),speakerLabel:oe(this.host,t.character,t.qualifier),padAfter:s}:{kind:"text",text:n,padAfter:s}}authoredPromptOf(e){return e.type==="group"?e.prompt:void 0}promptResult(e,t){let n=P(this.host,e.id),s=n&&n.length?{tags:n}:{};return t.kind==="text"?{type:"text",id:e.id,text:t.text,padAfter:this.padOf(e.id),gameData:b(e.gameData),...C(e.extensions),...s}:{type:"line",id:e.id,text:t.text,character:t.character,characterName:t.characterName,direction:t.direction,qualifier:t.qualifier,qualifierName:t.qualifierName,speakerLabel:t.speakerLabel,padAfter:this.padOf(e.id),gameData:b(e.gameData),...C(e.extensions),...s}}promptBeatOf(e){return e.type==="group"&&e.prompt?e.prompt:((e.type==="snippet"?e:this.firstTextSnippetIn(e.children))?.beats??[]).find(n=>n.kind==="line"||n.kind==="text")}firstTextSnippetIn(e){let t;return D(e,n=>{!t&&n.type==="snippet"&&(n.beats??[]).some(s=>s.kind==="line"||s.kind==="text")&&(t=n)}),t}resolveString(e){if(this.host.emitIds)return e;let t=this.host.strings[e];if(t!==void 0)return t;let n=this.host.defaultStrings[e];return n!==void 0?`<Untranslated: ${e}> ${n}`:e}resolveCharacterName(e){if(e===void 0||this.host.emitIds)return;let t=re(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}padOf(e){return this.host.padIndex.get(e)?.resolved??O}resolveQualifierName(e){if(e===void 0||this.host.emitIds)return;let t=se(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.qualifierDisplay.get(e)}splitRef(e){return ut(this.host,e)}newLocal(){return new E(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=w.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:U}),this.registered.add(e),this.host.reportRefits()}releaseBags(e){for(let t of this.registered)this.host.registry.remove(t,{keep:e});this.registered.clear(),this.sceneBags.clear()}sceneBagFor(e,t){if(this.host.bundle.scenes[e])return this.ensureSceneBags(e),this.host.sceneSharedNames.get(e)?.has(t.toLowerCase())?this.host.stageBags.get(e):this.sceneBags.get(e)}ensureSceneBags(e){let t=this.host.sceneSharedNames.get(e)??new Set,n=this.host.bundle.scenes[e]?.sceneProps??[],s=this.host.journal,i=!this.sceneBags.has(e)||!this.host.stageBags.has(e);if(!this.sceneBags.has(e)){let o=new E(n.filter(l=>!t.has(l.name.toLowerCase())),{pathPrefix:"@scene."}),a=w.flowScene(this.id,e),c=st(this.host.registry,a,o,s!==null);this.registered.add(a),this.sceneBags.set(e,o),s&&s.undo.push(()=>{this.sceneBags.get(e)===o&&(this.host.registry.remove(a,c?{keep:!0}:void 0),this.registered.delete(a),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let o=new E(n.filter(c=>t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),a=st(this.host.registry,w.stage(e),o,s!==null);this.host.stageBags.set(e,o),s&&s.undo.push(()=>{this.host.stageBags.get(e)===o&&(this.host.registry.remove(w.stage(e),a?{keep:!0}:void 0),this.host.stageBags.delete(e))})}i&&this.host.reportRefits()}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;this.ensureSceneBags(e.id);for(let n of e.sceneProps??[]){if(!n.temporary)continue;let s=n.name.toLowerCase(),i=t.has(s)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);if(!i)continue;let o=this.host.journal?i.get(s):void 0;o!==void 0&&this.host.journal.undo.push(()=>i.set(s,o)),i.set(s,L(n))}}};function nt(r,e){D(r,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let n of t.beats??[])n.kind==="line"&&n.character&&e.add(n.character)})}function oe(r,e,t,n=!1){if(!e||r.emitIds)return;let s=c=>(n?void 0:r.strings[c])??r.defaultStrings[c],i=s(re(e))??r.castDisplay.get(e)??e;if(t===void 0)return i;let o=r.speakerLabels.get(`${e}\\0${t}`);if(o!==void 0)return s(qe(e,t))??o;let a=s(se(t))??r.qualifierDisplay.get(t)??t;return Ue(s(_e)??r.speakerPattern,i,a)}function rt(r){return r&&Object.keys(r).length?{gameData:b(r)}:{}}function C(r){return r&&Object.keys(r).length?{extensions:r}:{}}function Kt(r){let e={kind:r.kind,text:r.text};return r.character!==void 0&&(e.character=r.character),r.characterName!==void 0&&(e.characterName=r.characterName),r.direction!==void 0&&(e.direction=r.direction),r.qualifier!==void 0&&(e.qualifier=r.qualifier),r.qualifierName!==void 0&&(e.qualifierName=r.qualifierName),r.speakerLabel!==void 0&&(e.speakerLabel=r.speakerLabel),typeof r.padAfter=="number"&&(e.padAfter=r.padAfter),e}function ct(r){let e={};for(let[t,n]of r){let s={};n.seq!==void 0&&(s.seq=n.seq),n.bag&&(s.bag=[...n.bag]),n.last!==void 0&&(s.last=n.last),e[t]=s}return e}function lt(r){let e=new Map;for(let[t,n]of Object.entries(r??{})){let s={};n.seq!==void 0&&(s.seq=n.seq),n.bag&&(s.bag=[...n.bag]),n.last!==void 0&&(s.last=n.last),e.set(t,s)}return e}function st(r,e,t,n){let s=n?JSON.stringify(t.save()):"";return r.mountOwned(e,t,{owner:U}),n&&JSON.stringify(t.save())!==s}function it(r){return{name:r.name,type:r.type,values:r.values,stages:r.stages,default:r.default}}var Jt=new Set(["rng","seed","locale","hostScopes","registry","replayPromptOnChoose","closedCaptions","onDryChoice","onError","onRefit","log"]);function we(r,e){let t=r.locales.default;if(e===void 0)return{locale:t,table:t};if(r.localisation?.mode==="ids")return{locale:e,table:e};let n=[...new Set([t,...r.locales.included,...Object.keys(r.strings)])];if(n.includes(e))return{locale:e,table:e};let s=c=>c.toLowerCase().replace(/_/g,"-"),i=n.find(c=>s(c)===s(e));if(i!==void 0)return{locale:i,table:i,problem:`the locale "${e}" is spelled "${i}" in this bundle, so it\'s using "${i}"`};let o=s(e).split("-")[0],a=n.find(c=>s(c)===o);return a!==void 0?{locale:a,table:a,problem:`this bundle has no locale "${e}", so it\'s using its language, "${a}"`}:{locale:t,problem:`this bundle has no locale "${e}", so it\'s using the default, "${t}", with every string marked untranslated`}}function ot(r,e){return e.table!==void 0?r[e.table]??{}:{}}function dt(r,e,t,n,s){let i=c=>c?.find(l=>l.name.toLowerCase()===t),o;if(e==="patter")o=i(r.bundle.properties);else if(e==="scene")o=i(s!==null?r.bundle.scenes[s]?.sceneProps:void 0);else{let c=r.bundle.scopeRegistry?.scopes.find(l=>l.token===e);if(!c||c.declarations===void 0)return;o=i(c.declarations)}let a=`@${e}.${t}`;if(o===void 0)throw new Error(`no property at "${a}": the content declares no ${a}`);if(!$(o,n,{noneChosen:o.type==="enum"&&o.default===""}))throw new Error(`"${a}" takes ${Qt(o)}; ${JSON.stringify(n)} doesn\'t fit`)}function Qt(r){let e=t=>(t??[]).map(n=>JSON.stringify(n)).join(", ");switch(r.type){case"number":return"a number";case"boolean":return"true or false";case"enum":return`one of ${e(r.values)}`;case"flags":return r.values!==void 0&&r.values.length>0?`a list of flags from ${e(r.values)}`:"a list of flags";case"quality":return`one of its stages ${e(r.stages)}`;default:return"text"}}function W(r,e,t){let n=r.qualityLadders.get(e);if(!n){n=new Map;for(let s of t()??[]){let i=s.name.toLowerCase();s.type==="quality"&&s.stages&&!n.has(i)&&n.set(i,s.stages)}r.qualityLadders.set(e,n)}return n}function zt(r,e,t){let n=t.toLowerCase();return e==="patter"?W(r,"patter",()=>[...r.patterSharedDecls,...r.patterLocalDecls]).get(n):r.registry.toEvalContext().qualities?.(e,t)??W(r,`host/${e}`,()=>r.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(n)}function pt(r){return{name:r.name,type:r.type,values:r.values,stages:r.stages,default:r.default,writable:r.writable}}function Xt(r,e){let t=pt(r),n=r.writable??e;return n===void 0?t:{...t,writable:n}}function be(r,e){return ne(r.bundle.scenes,e,r.sceneGameIdToId)}function ve(r,e,t){return r.blockGameIdToId.get(e)?.get(t)??(r.blockIndex.get(t)?.sceneId===e?t:void 0)}function ut(r,e){r.refSplitRevision!==r.registry.revision&&(r.refSplitCache.clear(),r.refSplitRevision=r.registry.revision);let t=r.refSplitCache.get(e);return t||(t=Me(e,n=>n==="scene"||r.registry.has(n)||(r.bundle.externalScopes?.includes(n)??!1)),r.refSplitCache.set(e,t)),t}function Yt(r,e){let t={};e.scopes?.patter&&(t[w.flowGlobals(r)]=e.scopes.patter);for(let[n,s]of Object.entries(e.sceneBags??{}))t[w.flowScene(r,n)]=s;return t}function Zt(r,e){let t={scopes:{},sceneBags:{}},n=w.flow(r);for(let[s,i]of Object.entries(e)){if(!s.startsWith(n))continue;let o=s.slice(n.length);o==="patter"?t.scopes.patter=i:o.startsWith("scene/")&&(t.sceneBags[Wt(o.slice(6))]=i)}return t}function at(r){return typeof r=="boolean"?r:typeof r=="number"?r!==0:typeof r=="string"?r!=="":r.length>0}var en=(r,e)=>r.shared??e;function xe(r,e){return{name:r.name,type:r.type,hasDefault:r.default!==void 0,...r.default!==void 0?{default:r.default}:{},shared:en(r,e)}}function tn(r){return{name:r.name,type:r.type,hasDefault:r.default!==void 0,...r.values?{values:[...r.values]}:{},...r.purpose?{purpose:r.purpose}:{}}}function nn(r,e){e.blocks++;let t=[...r.children];for(;t.length;){let n=t.pop();if(n.type==="group"){e.groups++,n.prompt&&e.prompts++,t.push(...n.children);continue}e.snippets++;for(let s of n.beats??[])e.beats++,s.kind==="gameEvent"&&e.gameEvents++}}function gt(r){let e={scenes:0,blocks:0,groups:0,snippets:0,beats:0,prompts:0,gameEvents:0,cast:r.cast?.length??0},t=[],n=[];for(let o of Object.values(r.scenes)){e.scenes++;let a=y(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:y(c),name:c.name}))});for(let c of o.blocks)nn(c,e);o.sceneProps?.length&&n.push({gameId:a,properties:o.sceneProps.map(c=>xe(c,!1))})}let s=(r.scopeRegistry?.scopes??[]).map(o=>({token:o.token,writable:o.writable??!0,opaque:o.declarations===void 0,properties:(o.declarations??[]).map(a=>xe(a,!0))})),i=Object.entries(r.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(tn)}));return{identity:{schema:r.schema,project:r.content.project,...r.content.version!==void 0?{version:r.content.version}:{},...r.content.hash!==void 0?{hash:r.content.hash}:{},...r.content.structureHash!==void 0?{structureHash:r.content.structureHash}:{},voiced:r.voiced,defaultLocale:r.locales.default,locales:[...r.locales.included],localisation:r.localisation?.mode??"embedded",sourceDebug:r.localisation?.sourceDebug??!1},addresses:t,hostScopes:s,properties:{patter:(r.properties??[]).map(o=>xe(o,!0)),scene:n},gameData:i,counts:e}}var J=(r,e)=>Object.prototype.hasOwnProperty.call(r,e);function ft(r,e){return r.gameDataFields?.[e]??[]}function ke(r,e,t){return e&&J(e,t)?e[t]:r.find(n=>n.name===t)?.default}function ht(r,e){let t={};for(let n of r){let s=ke(r,e,n.name);s!==void 0&&(t[n.name]=s)}for(let[n,s]of Object.entries(e??{}))J(t,n)||(t[n]=s);return t}function mt(r,e,t){let n=r.extensions!==void 0&&J(r.extensions,e)?r.extensions[e]:void 0;return n!==void 0&&J(n.fields,t)?n.fields[t]??[]:[]}function yt(r,e,t=""){let n=o=>({...o?.extensions??{}}),s=Object.values(r.scenes??{}),i=o=>(J(r.scenes??{},o)?r.scenes[o]:void 0)??s.find(a=>y(a)===o);switch(e){case"project":{let o={};for(let[a,c]of Object.entries(r.extensions??{}))c.project!==void 0&&(o[a]=c.project);return o}case"scene":return n(i(t));case"block":{let o=t.indexOf("/");if(o<0)return{};let a=i(t.slice(0,o))?.blocks??[],c=t.slice(o+1);return n(a.find(l=>l.id===c)??a.find(l=>y(l)===c))}case"snippet":case"option":case"line":case"text":case"gameEvent":{let o,a=(c,l)=>{for(let d of c??[]){if(o!==void 0)return;if(d.type==="group"){if(l&&d.id===t&&e==="option"){o=d;return}if(l&&d.prompt?.id===t&&d.prompt.kind===e){o=d.prompt;return}a(d.children??[],d.selector==="choice");continue}if(d.id===t&&e==="snippet"){o=d;return}let g=(d.beats??[]).find(f=>f.id===t);if(g!==void 0){g.kind===e&&(o=g);return}}};for(let c of s)for(let l of c.blocks??[])o===void 0&&a(l.children??[],!1);return n(o)}default:return{}}}return It(rn);})();\n';
|
|
2707
|
+
var PLAYABLE_RUNTIME_JS = 'var Patterplay=(()=>{var pe=Object.defineProperty;var xt=Object.getOwnPropertyDescriptor;var kt=Object.getOwnPropertyNames;var Et=Object.prototype.hasOwnProperty;var Dt=(r,e)=>{for(var t in e)pe(r,t,{get:e[t],enumerable:!0})},Ct=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of kt(e))!Et.call(r,s)&&s!==t&&pe(r,s,{get:()=>e[s],enumerable:!(n=xt(e,s))||n.enumerable});return r};var It=r=>Ct(pe({},"__esModule",{value:!0}),r);var rn={};Dt(rn,{Checkpoint:()=>K,DEFAULT_PAD_AFTER:()=>O,Engine:()=>ce,Flow:()=>A,buildTagIndex:()=>oe,compareStages:()=>F,describeBundle:()=>gt,effectiveGameData:()=>ht,estimateLineDuration:()=>Ve,extensionDecls:()=>mt,extensionsOf:()=>yt,gameDataFields:()=>ft,gameDataValue:()=>Ee,nextStage:()=>L,resolveSceneRef:()=>re,sceneAddressIndex:()=>Se,stageIndex:()=>B});function M(r,e){return r!==void 0&&Object.prototype.hasOwnProperty.call(r,e)?r[e]:void 0}function R(r){switch(r[0]){case"b":return{kind:"bool",value:r[1]};case"n":return{kind:"number",value:r[1]};case"s":return{kind:"string",value:r[1]};case"sv":return{kind:"scopedvar",scope:r[1],name:r[2]};case"u":return{kind:"unary",op:r[1],operand:R(r[2])};case"bin":return{kind:"binary",op:r[1],left:R(r[2]),right:R(r[3])};case"call":{let e=r.slice(2).map(R);return{kind:"call",name:r[1],args:e}}case"fd":return{kind:"flagdelta",sign:r[1],name:r[2]}}}var u=class extends Error{constructor(e){super(e),this.name="EvalError"}};function Y(r,e,t){let n=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=M(e.scopes,i.scope);if(o===void 0)return!1;let a=typeof o.get=="function"?o.get(i.name):M(o,i.name);if(a===void 0){if(n.get(i.scope)==="throw")throw new u(`@${i.scope}.${i.name} is not declared on the current ${i.scope}.`);return!1}return a}case"call":{if(i.name==="advance"&&!M(t.functions,i.name)){let a=i.args[0];if(i.args.length!==1||a===void 0)throw new u(`advance() takes exactly 1 argument, got ${i.args.length}`);let c=ue(a,e);if(c===void 0)throw new u("advance() needs a quality reference (@scope.name of a quality property)");let l=v(s(a),c,"advance");return c[Math.min(l+1,c.length-1)]}let o=M(t.functions,i.name);if(!o)throw new u(`unknown function \'${i.name}\'`);return o.eval(i.args,{evaluate:s,ctx:e})}case"flagdelta":throw new u("flagdelta node is only valid as an argument to a flag-delta function");case"unary":{if(i.op==="not"){let a=s(i.operand);if(typeof a!="boolean")throw new u(`\'not\' requires a boolean operand, got ${typeof a}`);return!a}let o=s(i.operand);if(typeof o!="number")throw new u(`unary \'-\' requires a numeric operand, got ${typeof o}`);return-o}case"binary":{if(i.op==="and"){let g=s(i.left);if(typeof g!="boolean")throw new u(`\'and\' requires boolean operands, left is ${typeof g}`);if(!g)return!1;let f=s(i.right);if(typeof f!="boolean")throw new u(`\'and\' requires boolean operands, right is ${typeof f}`);return f}if(i.op==="or"){let g=s(i.left);if(typeof g!="boolean")throw new u(`\'or\' requires boolean operands, left is ${typeof g}`);if(g)return!0;let f=s(i.right);if(typeof f!="boolean")throw new u(`\'or\' requires boolean operands, right is ${typeof f}`);return f}let o=s(i.left),a=s(i.right),c=ue(i.left,e),l=ue(i.right,e),d=c??l;if(d!==void 0){if(c&&l&&!Rt(c,l)&&(i.op===">"||i.op===">="||i.op==="<"||i.op==="<="))throw new u(`\'${i.op}\' compares two different qualities, whose stage orders are unrelated`);switch(i.op){case">":return v(o,d,">")>v(a,d,">");case">=":return v(o,d,">=")>=v(a,d,">=");case"<":return v(o,d,"<")<v(a,d,"<");case"<=":return v(o,d,"<=")<=v(a,d,"<=");case"+":case"-":case"*":case"/":throw new u(`\'${i.op}\' cannot be applied to a quality - a stage is a position, not a number; use advance() to move it`);default:break}}switch(i.op){case"==":return Oe(o,a);case"!=":return!Oe(o,a);case">":return P(o,a,">"),o>a;case">=":return P(o,a,">="),o>=a;case"<":return P(o,a,"<"),o<a;case"<=":return P(o,a,"<="),o<=a;case"+":if(typeof o=="number"&&typeof a=="number"||typeof o=="string"&&typeof a=="string")return o+a;throw new u(`\'+\' requires two numbers or two strings, got ${typeof o} and ${typeof a}`);case"-":return P(o,a,"-"),o-a;case"*":return P(o,a,"*"),o*a;case"/":if(P(o,a,"/"),a===0)throw new u("division by zero");return o/a}}}};return s(r)}function Oe(r,e){if(Array.isArray(r)||Array.isArray(e)){if(!Array.isArray(r)||!Array.isArray(e)||r.length!==e.length)return!1;let t=[...r].sort(),n=[...e].sort();for(let s=0;s<t.length;s++)if(t[s]!==n[s])return!1;return!0}return r===e}function P(r,e,t){if(typeof r!="number"||typeof e!="number")throw new u(`\'${t}\' requires numeric operands, got ${typeof r} and ${typeof e}`)}function ue(r,e){if(!(r.kind!=="scopedvar"||e.qualities===void 0))return e.qualities(r.scope,r.name)}function v(r,e,t){if(typeof r!="string")throw new u(`\'${t}\' on a quality compares stages, got ${typeof r}`);let n=e.indexOf(r);if(n<0)throw new u(`"${r}" is not a stage of this quality (stages: ${e.join(", ")})`);return n}var Rt=(r,e)=>r.length===e.length&&r.every((t,n)=>t===e[n]);function j(r){if(Number.isNaN(r)||!Number.isFinite(r))return 0;let e=Math.trunc(r)%4294967296;return e<0?e+4294967296:e}function ge(r){let e=j(r);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 he=r=>Array.isArray(r)?r:r.stages??[],Pt=r=>Array.isArray(r)?"flags":r==null?"nothing":typeof r;function B(r,e){return typeof e=="string"?he(r).indexOf(e):-1}function fe(r,e){if(typeof e!="string")throw new u(`a stage is a name, got ${Pt(e)}`);let t=r.indexOf(e);if(t<0)throw new u(`"${e}" is not a stage of this quality (stages: ${r.join(", ")})`);return t}function F(r,e,t){let n=he(r),s=fe(n,e)-fe(n,t);return s<0?-1:s>0?1:0}function L(r,e){let t=he(r);return t[Math.min(fe(t,e)+1,t.length-1)]}function $(r,e,t){switch(r.type){case"boolean":return typeof e=="boolean";case"number":return typeof e=="number";case"string":case"text":case"multiline":return typeof e=="string";case"enum":return typeof e!="string"?!1:e===""&&t?.noneChosen?!0:!Array.isArray(r.values)||r.values.includes(e);case"flags":return Array.isArray(e)&&e.every(n=>typeof n=="string"&&(!Array.isArray(r.values)||r.values.includes(n)));case"quality":return typeof e=="string"&&(!Array.isArray(r.stages)||r.stages.includes(e));default:return!0}}var Ot={name:"check_flags",count:r=>Math.max(1,r.args.length-1)},Nt=[Ot];function Ne(r,e,t){let n=t?.countingCalls??Nt;return Z(r,t?.want??!0,e,n)}function Z(r,e,t,n){if(r.kind==="binary"&&(r.op==="and"||r.op==="or")){let s=Z(r.left,e,t,n),i=Z(r.right,e,t,n);return r.op==="and"===e?s>0&&i>0?s+i:0:Math.max(s,i)}if(r.kind==="unary"&&r.op==="not")return Z(r.operand,!e,t,n);if(r.kind==="call"){let s=n.find(i=>i.name===r.name);if(s){let i=s.count(r),o=t(r);return e?o?i:0:o?0:1}}return t(r)===e?1:0}var E=class r{values=Object.create(null);decls=new Map;subscribers=new Set;auditors=new Set;norm;pathPrefix;constructor(e=[],t){this.norm=t?.normalise??(n=>n.toLowerCase()),this.pathPrefix=t?.pathPrefix??"",this.seed(e)}seed(e){for(let t of e){let n=this.norm(t.name);this.decls.set(n,t),this.values[n]=structuredClone(t.default??T(t))}}get(e){return this.values[this.norm(e)]}normalise(e){return this.norm(e)}writable(e){return this.decls.get(this.norm(e))?.writable!==!1}set(e,t,n){let s=this.norm(e);if(!n?.host&&!this.writable(s))throw new Error(`\'${e}\' is read-only`);let i={name:s,prev:this.values[s],next:t,silent:n?.silent??!1,reason:n?.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])=>Be(t,this.get(e),void 0,e,this.pathPrefix))}declarations(){return[...this.decls.values()]}clone(){let e=new r([],{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,t){let n=new Map;for(let[i,o]of Object.entries(e)){let a=this.norm(i);this.values[a]=structuredClone(o),n.set(a,o)}let s=[];if(!t?.refit)return s;for(let[i,o]of this.decls){if(!n.has(i))continue;let a=n.get(i);$(o,a,{noneChosen:o.type==="enum"&&o.default===""})||(this.values[i]=structuredClone(T(o)),s.push({name:i,value:a}))}return s}};function Be(r,e,t,n,s=""){let i=n??r.name.toLowerCase();return{name:i,path:s+i,type:r.type,value:e,default:r.default??T(r),...r.values!==void 0?{values:r.values}:{},...r.stages!==void 0?{stages:r.stages}:{},writable:t??r.writable??!0}}var At=r=>r.toLowerCase(),me=1,ee=class{scopes=new Map;parked=new Map;parkedRefit=new Set;misfits=[];rev=0;get revision(){return this.rev}defineOwned(e,t,n){let s=typeof n=="string"?{pathPrefix:n}:n??{},i=new E(t,{pathPrefix:s.pathPrefix??`${e}.`,...s.normalise?{normalise:s.normalise}:{}});return this.mountOwned(e,i,s.owner!==void 0?{owner:s.owner}:void 0)}mountOwned(e,t,n){this.assertFree(e,n?.owner),this.scopes.set(e,{kind:"owned",bag:t,...n?.owner!==void 0?{owner:n.owner}:{}}),this.rev++;let s=this.parked.get(e);return s&&(this.note(e,t.load(s,{refit:this.parkedRefit.has(e)})),this.parked.delete(e),this.parkedRefit.delete(e)),this}remove(e,t){let n=this.scopes.get(e);if(!n)throw new Error(`unknown scope \'@${e}\'`);return t?.keep&&n.kind==="owned"&&(this.parked.set(e,n.bag.save()),this.parkedRefit.delete(e)),this.scopes.delete(e),this.rev++,this}discardParked(e){if(e===void 0)this.parked.clear(),this.parkedRefit.clear();else for(let t of[...this.parked.keys()])t.startsWith(e)&&(this.parked.delete(t),this.parkedRefit.delete(t));return this}takeMisfits(e){if(!e){let n=this.misfits;return this.misfits=[],n}let t=this.misfits.filter(n=>e(n.scope));return this.misfits=this.misfits.filter(n=>!e(n.scope)),t}note(e,t){for(let n of t)this.misfits.push({scope:e,name:n.name,value:n.value})}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,n=[],s=!0){let i=typeof s=="boolean"?{writable:s}:s;this.assertFree(e,i.owner);let o=i.normalise??At,a=new Map;for(let c of n)a.set(o(c.name),c);return this.scopes.set(e,{kind:"foreign",resolver:t,decls:a,scopeWritable:i.writable??!0,norm:o,...i.owner!==void 0?{owner:i.owner}:{}}),this.rev++,this}has(e){return this.scopes.has(e)}get(e,t){let n=this.scopes.get(e);if(n)return n.kind==="owned"?n.bag.get(t):n.resolver.get(n.norm(t))}set(e,t,n,s){let i=this.scopes.get(e);if(!i)throw new Error(`unknown scope \'@${e}\'`);if(i.kind==="owned"){if(!s?.host&&!i.bag.writable(t))throw new Error(`\'@${e}.${t}\' is read-only`);i.bag.set(t,n,s?.host?{host:!0}:void 0);return}let o=i.norm(t);if(!i.resolver.set)throw new Error(`\'@${e}.${t}\' is read-only`);if(!s?.host&&!this.foreignWritable(i,o))throw new Error(`\'@${e}.${t}\' is read-only`);i.resolver.set(o,n)}foreignWritable(e,t){return e.resolver.set?e.decls.get(t)?.writable??e.scopeWritable:!1}listProperties(){let e=[];for(let[t,n]of this.scopes){let s=n.owner!==void 0?{owner:n.owner}:{};if(n.kind==="owned")for(let i of n.bag.rows())e.push({scope:t,...s,...i});else for(let[i,o]of n.decls)e.push({scope:t,...s,...Be(o,n.resolver.get(i),this.foreignWritable(n,i),i,`${t}.`)})}return e}toEvalContext(e,t){let n=this.view(t?.aliases),s={};for(let[o,a]of n)s[o]=a.kind==="owned"?a.bag.values:a.resolver;let i=this.qualityLadders(n);return i.size===0?{scopes:s,host:e}:{scopes:s,host:e,qualities:(o,a)=>{let c=n.get(o);return c?i.get(o)?.get(Bt(c)(a)):void 0}}}view(e){let t=new Map(this.scopes);for(let[n,s]of Object.entries(e??{})){let i=this.scopes.get(s);if(!i)throw new Error(`alias \'@${n}\' names \'${s}\', which is not registered`);t.set(n,i)}return t}qualityLadders(e){let t=new Map;for(let[n,s]of e)for(let[i,o]of Ae(s)){if(o.type!=="quality"||o.stages===void 0)continue;let a=t.get(n);a||(a=new Map,t.set(n,a)),a.set(i,o.stages)}return t}toSchema(e){let t=new Map;for(let[n,s]of this.view(e?.aliases)){let i=Ae(s);if(i.length===0)continue;let o=new Map;for(let[a,c]of i)o.set(a,{type:c.type,enumValues:c.values,...c.stages!==void 0?{stages:c.stages}:{}});t.set(n,o)}return{properties:t}}save(){let e={};for(let[t,n]of this.scopes)n.kind==="owned"&&(e[t]=n.bag.save());for(let[t,n]of this.parked)e[t]=structuredClone(n);return e}load(e,t){t?.keepParked||(this.parked.clear(),this.parkedRefit.clear());let n=t?.refit===!0;for(let[s,i]of Object.entries(e)){let o=this.scopes.get(s);o?.kind==="owned"?this.note(s,o.bag.load(i,{refit:n})):o||(this.parked.set(s,structuredClone(i)),n?this.parkedRefit.add(s):this.parkedRefit.delete(s))}}saveFragment(){return{version:me,scopes:this.save()}}loadFragment(e){if(e.version!==me)throw new Error(`unsupported owned-state fragment version ${e.version} (supported: ${me})`);this.load(e.scopes)}assertFree(e,t){let n=this.scopes.get(e);if(!n)return;let s=n.owner!==void 0?` by ${n.owner}`:"",i=t!==void 0?` (wanted by ${t})`:"";throw new Error(`scope \'@${e}\' is already registered${s}${i}`)}};function Ae(r){return r.kind==="foreign"?[...r.decls.entries()]:r.bag.declarations().map(e=>[r.bag.normalise(e.name),e])}function Bt(r){return r.kind==="foreign"?r.norm:e=>r.bag.normalise(e)}function T(r){if(r.default!==void 0)return r.default;switch(r.type){case"boolean":return!1;case"number":return 0;case"string":case"text":case"multiline":return"";case"enum":return r.values?.[0]??"";case"flags":return[];case"quality":return r.stages?.[0]??"";default:return!1}}var Fe=[{token:"patter",engine:"Patterplay",means:"Patter\'s shared globals"},{token:"story",engine:"Storylet Engine",means:"the Storylet Engine\'s shared @story properties"}];function G(r){return r.ctx.host??{}}var ye={defaultScope:"patter",scopes:[{token:"patter"},{token:"scene"}],functions:{random:{minArgs:2,maxArgs:2,returnType:"number",eval(r,e){if(r.length!==2)throw new u("random(a, b) requires exactly 2 arguments");let t=G(e).nextRandom;if(!t)throw new u("random() called without a PRNG in context");let n=e.evaluate(r[0]),s=e.evaluate(r[1]);if(typeof n!="number"||typeof s!="number")throw new u("random(a, b) arguments must be numbers");if(!Number.isInteger(n)||!Number.isInteger(s))throw new u("random(a, b) arguments must be integers");let i=Math.min(n,s),o=Math.max(n,s);return Math.floor(t()*(o-i+1))+i}},check_flags:{minArgs:1,returnType:"boolean",flagDeltaArgs:!0,validate:Te("check_flags"),eval(r,e){let t=Le(r[0],e,"check_flags");for(let n=1;n<r.length;n++){let s=r[n];if(s.kind!=="flagdelta")throw new u("check_flags() flag args must be +flagName or -flagName");if(s.sign==="+"?!t.includes(s.name):t.includes(s.name))return!1}return!0}},set_flags:{minArgs:1,returnType:"flags",flagDeltaArgs:!0,validate:Te("set_flags"),eval(r,e){let t=[...Le(r[0],e,"set_flags")];for(let n=1;n<r.length;n++){let s=r[n];if(s.kind!=="flagdelta")throw new u("set_flags() flag args must be +flagName or -flagName");if(s.sign==="+")t.includes(s.name)||t.push(s.name);else{let i=t.indexOf(s.name);i>=0&&t.splice(i,1)}}return t}},visits:{minArgs:1,maxArgs:1,returnType:"number",validate:ne("visits"),eval:(r,e)=>G(e).visits?.(te(r,e,"visits"))??0},seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:ne("seen"),eval:(r,e)=>(G(e).visits?.(te(r,e,"seen"))??0)>0},patter_visits:{minArgs:1,maxArgs:1,returnType:"number",validate:ne("patter_visits"),eval:(r,e)=>G(e).patterVisits?.(te(r,e,"patter_visits"))??0},patter_seen:{minArgs:1,maxArgs:1,returnType:"boolean",validate:ne("patter_seen"),eval:(r,e)=>(G(e).patterVisits?.(te(r,e,"patter_seen"))??0)>0}}},An=Fe.map(r=>r.token).filter(r=>r!=="patter"&&r!=="scene");function Me(r,e){let t=r.replace(/^@/,"").split(".");return t.length===2&&e(t[0])?{scope:t[0],name:t[1].toLowerCase()}:{scope:"patter",name:t.join(".").toLowerCase()}}function te(r,e,t){let n=e.evaluate(r[0]);if(typeof n!="string")throw new u(`${t}(id) requires a string node id`);return n}function ne(r){return(e,t)=>{let n=e[0];n&&n.kind!=="string"&&t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${r}(id): the argument must be a string id literal (a scene / block / node id)`})}}function Le(r,e,t){if(!r)throw new u(`${t}() requires at least one argument (the flags variable)`);let n=e.evaluate(r);if(Array.isArray(n))return n;if(n===!1||n===null||n===void 0)return[];throw new u(`${t}() first argument must be a flags property`)}function Te(r){return(e,t)=>{if(e.length===0)return;let n=e[0];if(n.kind!=="scopedvar"){t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${r}(): first argument must be a flags property reference (@name or @scope.name)`});return}let s=t.schema.properties.get(n.scope)?.get(n.name);if(s&&s.type!=="flags"){let i=n.scope===t.defaultScope?n.name:`${n.scope}.${n.name}`;t.report({path:[...t.path,"args",0],kind:"wrong-arg-type",severity:"error",message:`${r}(): \'@${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:`${r}(): 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:`${r}(): unknown flag \'${o.name}\'`,reference:o.name})}}}var Ft=/^@[A-Za-z0-9_.]+$/;function*Lt(r){let e="",t=0;for(;t<r.length;){let n=r[t];if(n==="{"&&r[t+1]==="{"){e+="{",t+=2;continue}if(n==="}"&&r[t+1]==="}"){e+="}",t+=2;continue}if(n==="{"){let s=r.indexOf("}",t+1);if(s!==-1){let i=r.slice(t,s+1),o=r.slice(t+1,s).trim();if(o.startsWith("@")){e&&(yield{kind:"text",value:e},e=""),yield{kind:"slot",raw:i,inner:o,ref:Ft.test(o)?o:void 0},t=s+1;continue}e+=i,t=s+1;continue}}e+=n,t+=1}e&&(yield{kind:"text",value:e})}function Tt(r){return Array.isArray(r)?r.join(", "):typeof r=="boolean"?r?"true":"false":String(r)}function Mt(r){return r===" "||r===" "||r===`\n`||r==="\\r"||r==="\\f"||r==="\\v"}function jt(r){let e="",t=!1;for(let n of r){if(Mt(n)){t=!0;continue}t&&e.length>0&&(e+=" "),t=!1,e+=n}return e}function je(r,e,t){if(e.length===0||r.indexOf(e)<0)return r;let n="",s=0,i=!1;for(;s<r.length;){if(r.startsWith(e,s)){let o=r.indexOf(t,s+e.length);if(o>=0){s=o+t.length,i=!0;continue}n+=r.slice(s);break}n+=r[s],s+=1}return i?jt(n):r}function $e(r,e){if(r.indexOf("{")<0)return r;let t="";for(let n of Lt(r)){if(n.kind==="text"){t+=n.value;continue}if(!n.ref){t+=n.raw;continue}let s=e(n.ref);t+=s===void 0?"":Tt(s)}return t}function Ge(r){return r.toLowerCase().replace(/[\'\u2019]/g,"").replace(/[^a-z0-9-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")}function D(r,e){for(let t of r){e(t);let n=t.children;n&&D(n,e)}}function y(r){let e=r.gameId?.trim();return e||Ge(r.name)}function Se(r){let e=new Map;for(let[t,n]of Object.entries(r))e.set(y(n),t);return e}function re(r,e,t){return(t??Se(r)).get(e)??(Object.hasOwn(r,e)?e:void 0)}var O=.6;var $t=14,Gt=1.5,Vt=18;function Ve(r,e){let t=0;for(let n of r??"")n!=="\\xAD"&&t++;return Math.min(Math.max(t/$t,Gt),Vt)}function se(r){return`cast:${r}`}function ie(r){return`qualifier:${r}`}function qe(r,e){return`speaker:${r}:${e}`}var _e="speaker:pattern",He="{name} ({qualifier})";function Ue(r,e,t){return r.split("{name}").map(n=>n.split("{qualifier}").join(t)).join(e)}var we={open:"[",close:"]"},We="SFX";var V=4;function q(r){let e=new Set,t=[];for(let n of r)e.has(n)||(e.add(n),t.push(n));return t}function oe(r){let e=new Map,t=(n,s)=>{let i=q([...s,...n.tags??[]]);if(e.set(n.id,i),n.type==="group"&&n.prompt&&e.set(n.prompt.id,q([...i,...n.prompt.tags??[]])),n.type==="group")for(let o of n.children)t(o,i);else for(let o of n.beats??[])e.set(o.id,q([...i,...o.tags??[]]))};for(let n of Object.values(r.scenes)){let s=q(n.tags??[]);e.set(n.id,s);for(let i of n.blocks){let o=q([...s,...i.tags??[]]);e.set(i.id,o);for(let a of i.children)t(a,o)}}return e}var Ke=r=>r.kind==="line"||r.kind==="text",_=r=>typeof r=="number"&&Number.isFinite(r)?r:void 0;function Je(r){let e=new Map,t=(i,o,a)=>{let c=i.kind==="gameEvent"?void 0:_(i.padAfter),l=c??o;a&&l<0&&(l=0),e.set(i.id,c!==void 0?{own:c,resolved:l}:{resolved:l})},n=(i,o)=>{let a=_(i.padAfterDefault)??o;if(i.type==="group"){i.prompt&&t(i.prompt,a,!1);for(let d of i.children)n(d,a);return}let c=i.beats??[],l=-1;c.forEach((d,g)=>{Ke(d)&&(l=g)}),c.forEach((d,g)=>{Ke(d)&&t(d,a,g===l)})},s=_(r.padAfterDefault)??O;for(let i of Object.values(r.scenes)){let o=_(i.padAfterDefault)??s;for(let a of i.blocks){let c=_(a.padAfterDefault)??o;for(let l of a.children)n(l,c)}}return e}var Qe=new WeakMap,ze=new WeakMap;function qt(r){let e=ze.get(r);return e||(e=oe(r),ze.set(r,e)),e}var Xe=new WeakMap;function _t(r){let e=Xe.get(r);return e||(e=Je(r),Xe.set(r,e)),e}var Ye=new WeakMap;function Ht(r){let e=Ye.get(r);if(e)return e;e=new Map;for(let t of Object.values(r.scenes))for(let n of t.blocks)D(n.children,s=>{if(s.type==="group"&&s.selector==="choice")for(let i of s.children)e.set(i.id,s.id)});return Ye.set(r,e),e}var Ze=new WeakMap;function Ut(r){let e=Ze.get(r);if(e)return e;e=new Map;for(let t of Object.values(r.scenes))for(let n of t.blocks)D(n.children,s=>{if(s.type==="group"){s.prompt&&e.set(s.prompt.id,s.prompt);return}for(let i of s.beats??[])e.set(i.id,i)});return Ze.set(r,e),e}function b(r){if(r===void 0)return;let e={};for(let[t,n]of Object.entries(r))e[t]=Array.isArray(n)?[...n]:n;return e}function N(r,e){return r.tagIndex.get(e)?.slice()}var U="Patter",H=r=>r.replace(/%/g,"%25").replace(/\\//g,"%2F"),Wt=r=>r.replace(/%2F/g,"/").replace(/%25/g,"%"),et=Symbol("ownsRegistry"),tt=Symbol("setupReported"),w={stage:r=>`patter/scene/${H(r)}`,flow:r=>`patter/flow/${H(r)}/`,flowGlobals:r=>`patter/flow/${H(r)}/patter`,flowScene:(r,e)=>`patter/flow/${H(r)}/scene/${H(e)}`},K=class{constructor(e){this.journal=e}journal},ce=class r{host;defaultSeed;flowsById=new Map;allStrings;currentLocale;requestedLocale;sourceDebug;sceneGameIdToId=new Map;blockGameIdToId=new Map;creationOptions;engineLog=[];engineSeq=0;engineTraceHandlers=new Set;constructor(e,t={}){this.creationOptions=t;let n=[];for(let p of Object.keys(t))Jt.has(p)||n.push({node:p,message:p==="world"?\'the engine option "world" has gone: bind the scope as hostScopes: { world }\':`"${p}" isn\'t an engine option, so it\'s ignored`});let s=e.strings;this.allStrings=s,this.requestedLocale=t.locale,this.currentLocale=be(e,t.locale),this.currentLocale.problem&&n.push({node:t.locale,message:this.currentLocale.problem});let i=ot(s,this.currentLocale),o=s[e.locales.default]??{},a=e.localisation,c=a?.mode==="ids"&&!a.sourceDebug;this.sourceDebug=a?.mode==="ids"&&!!a.sourceDebug,this.sourceDebug&&typeof console<"u"&&console.warn("[Patterplay] source-only DEBUG build: strings are the source language for debugging, not a shippable localised build.");let l=new Map;for(let p of e.cast??[])p.displayName&&l.set(p.name,p.displayName);let d=new Map;for(let p of e.qualifiers??[])p?.gameId&&d.set(p.gameId,p.name);let g=new Map;for(let p of e.cast??[])for(let[m,h]of Object.entries(p.qualifierLabels??{}))typeof h=="string"&&g.set(`${p.name}\\0${m}`,h);let f=e.speakerPattern??He;this.defaultSeed=(t.seed??2654435769)>>>0;let I=new Map,Q=new Map,De=new Map;for(let[p,m]of Object.entries(e.scenes)){this.sceneGameIdToId.set(y(m),p);let h=new Map;for(let S of m.blocks)Q.set(S.id,{sceneId:p}),De.set(S.id,S),h.set(y(S),S.id),D(S.children,k=>I.set(k.id,k));this.blockGameIdToId.set(p,h)}let Ce=e.properties??[],le=Ce.filter(p=>p.shared??!0).map(it),St=Ce.filter(p=>!(p.shared??!0)).map(it),wt=new Set(le.map(p=>p.name.toLowerCase())),x=t.registry??new ee,Ie=!t.registry||t[et]===!0,Re=new E(le,{pathPrefix:"@patter."}),de=[],z=[];try{x.mountOwned("patter",Re,{owner:U}),z.push("patter");let p=new Set,m=e.scopeRegistry?.scopes??[];for(let[h,S]of Object.entries(t.hostScopes??{})){let k=m.find(X=>X.token===h);if(!k&&!(e.externalScopes??[]).includes(h)){let X=m.find(vt=>vt.token.toLowerCase()===h.toLowerCase());n.push({node:h,message:X?`hostScopes binds "${h}", which this bundle doesn\'t declare: it declares "${X.token}", and a scope\'s token is case-sensitive`:`hostScopes binds "${h}", which this bundle doesn\'t declare`})}let bt=(k?.declarations??[]).map(pt);x.defineForeign(h,S,bt,{writable:k?.writable??!0,owner:U}),z.push(h),p.add(h)}for(let h of Ie?e.scopeRegistry?.scopes??[]:[]){if(p.has(h.token)||x.has(h.token))continue;let S=(h.declarations??[]).map(k=>Xt(k,h.writable));x.defineOwned(h.token,S,{owner:U}),z.push(h.token),de.push(h.token)}}catch(p){for(let m of z)x.remove(m,{keep:!0});throw p}let Pe=new Map;for(let[p,m]of Object.entries(e.scenes)){let h=new Set((m.sceneProps??[]).filter(S=>S.shared??!1).map(S=>S.name.toLowerCase()));Pe.set(p,h)}if(this.host={logEnabled:t.log??!1,tracing:t.log??!1,emitEngine:(p,m,h)=>this.emitEngine(p,m,h),bundle:e,emitIds:c,strings:i,defaultStrings:o,castDisplay:l,qualifierDisplay:d,speakerLabels:g,speakerPattern:f,nodeIndex:I,blockIndex:Q,blockById:De,sceneGameIdToId:this.sceneGameIdToId,blockGameIdToId:this.blockGameIdToId,tagIndex:qt(e),padIndex:_t(e),registry:x,ownsRegistry:Ie,patterBag:Re,hostScopes:de,patterSharedDecls:le,patterLocalDecls:St,patterSharedNames:wt,sceneSharedNames:Pe,sharedVisits:new Map,qualityLadders:new Map,sharedSelectors:new Map,stageBags:new Map,journal:null,customRng:t.rng,onDryChoice:t.onDryChoice,onError:t.onError??(p=>console.warn(p.kind==="setup"?`Patterplay: ${p.message}`:p.kind==="save"?`Patterplay: flow \'${p.flow}\': ${p.message}`:`Patterplay: ${p.kind} on ${p.node} in flow \'${p.flow}\' failed, played through: ${p.message}`)),reportRefits:()=>{let p=x.takeMisfits(m=>m==="patter"||m.startsWith("patter/")||de.includes(m));if(p.length!==0)if(t.onRefit)t.onRefit(p);else for(let m of p)console.warn(`Patterplay: the save\'s ${m.scope}.${m.name} was ${JSON.stringify(m.value)}, which no longer fits its declaration; it is back at its default`)},replayPromptOnChoose:t.replayPromptOnChoose??!1,captionsOn:t.closedCaptions??!0,captionOpen:(e.closedCaptions??we).open,captionClose:(e.closedCaptions??we).close,captionCharacter:e.closedCaptions?.character||We,refSplitCache:new Map,refSplitRevision:x.revision},this.host.reportRefits(),t[tt]!==!0)for(let p of n)this.reportSetup(p.node,p.message)}reportSetup(e,t){this.host.onError({flow:"",kind:"setup",node:e,message:t}),this.host.tracing&&this.emitEngine("",{type:"diagnostic",kind:"setup",node:e,message:t})}get locale(){return this.currentLocale.locale}get locales(){let{default:e,included:t}=this.host.bundle.locales;return{default:e,included:[...t]}}get isSourceDebug(){return this.sourceDebug}get buildId(){return this.host.bundle.content?.hash??""}setLocale(e){this.requestedLocale=e,this.useLocale(be(this.host.bundle,e)),this.currentLocale.problem&&this.reportSetup(e,this.currentLocale.problem)}useLocale(e){this.currentLocale=e,this.host.strings=ot(this.allStrings,e)}replaceStrings(e){this.allStrings=e.strings;let t=this.currentLocale.problem;this.useLocale(be(e,this.requestedLocale)),this.host.defaultStrings=this.allStrings[this.host.bundle.locales.default]??{};let n=this.currentLocale.problem;n&&n!==t&&this.reportSetup(this.requestedLocale,n)}hotSwap(e){this.refuseInCheckpoint("hotSwap");let t=this.saveGame(),n=l=>(l.setClosedCaptions(this.host.captionsOn),l),s=this.requestedLocale,i=(l=!1)=>new r(e,{...this.creationOptions,...s!==void 0?{locale:s}:{},registry:this.host.registry,[et]:this.host.ownsRegistry,[tt]:l}),o=new Set(["patter",...this.host.hostScopes]),a=this.host.ownsRegistry?void 0:Object.fromEntries(Object.entries(this.host.registry.save()).filter(([l])=>o.has(l)||l.startsWith("patter/")));this.release(!0),a&&this.host.registry.load(a,{keepParked:!0,refit:!0});let c=i();try{return c.loadGame(t),n(c)}catch{c.release(!0);let l=this.host.ownsRegistry?t.registry:a;l&&this.host.registry.load(l,{keepParked:!0,refit:!0});let d=i(!0);for(let[g,f]of Object.entries(t.flows)){let I=f.cursor.currentSceneId;try{d.openFlow(g,I!==null?{scene:I}:{})}catch{}}return n(d)}}get closedCaptions(){return this.host.captionsOn}setClosedCaptions(e){this.host.captionsOn=e}openFlow(e,t={}){this.assertExternalScopes();let{sceneId:n,blockId:s}=this.resolveOpenAddress(t.scene,t.block),i=this.flowsById.get(e),o=this.host.journal;if(o&&i&&!i.isClosed)throw new Error(`openFlow would replace the open flow \'${e}\', which can\'t be undone while a checkpoint is open`);i?.close();let a=new A(e,this.host,t.seed??this.defaultSeed);return o&&(o.flows.add(a),o.opened.add(a),o.undo.push(()=>{a.close(),i?this.flowsById.set(e,i):this.flowsById.delete(e)})),this.flowsById.set(e,a),a.begin(n,s),a}runFlow(e,t,n){let s=this.flowsById.get(e);if(!s)return this.openFlow(e,{scene:t,block:n}).advanceToStop().played;if(!s.goto(t,n))throw new Error(`runFlow: address not found: ${t}${n===void 0?"":` / ${n}`}`);return s.advanceToStop().played}assertExternalScopes(){for(let e of this.host.bundle.externalScopes??[])if(!this.host.registry.has(e))throw new Error(`this content names @${e}, which no engine on this registry registered: give every engine the game\'s one registry`)}resolveOpenAddress(e,t){if(e!==void 0){let n=ve(this.host,e);if(n===void 0)throw new Error(`unknown scene: ${e}`);if(t===void 0)return{sceneId:n};let s=xe(this.host,n,t);if(s===void 0)throw new Error(`unknown block: ${t}`);return{sceneId:n,blockId:s}}if(t!==void 0){if(!this.host.blockIndex.has(t))throw new Error(`unknown block: ${t}`);return{blockId:t}}if(Object.keys(this.host.bundle.scenes).length===0)throw new Error("no scenes in bundle");return{}}resolveSceneRef(e){if(e!=null)return ve(this.host,e)??e}resolveBlockRef(e,t){if(t!=null)return e==null?t:xe(this.host,e,t)}sceneAddress(e){let t=this.host.bundle.scenes[e];return t?y(t):void 0}blockAddress(e){let t=this.host.blockById.get(e);return t?y(t):void 0}tagsForBeat(e){return N(this.host,e)??[]}tagsForScene(e){let t=this.resolveSceneRef(e);return(t!=null?N(this.host,t):void 0)??[]}tagsForBlock(e,t){let n=this.resolveSceneRef(e),s=this.resolveBlockRef(n,t);return(s!=null?N(this.host,s):void 0)??[]}gameDataForScene(e){let t=this.resolveSceneRef(e),n=t!=null?this.host.bundle.scenes[t]:void 0;return b(n?.gameData)??{}}gameDataForBlock(e,t){let n=this.resolveBlockRef(this.resolveSceneRef(e),t),s=n!=null?this.host.blockById.get(n):void 0;return b(s?.gameData)??{}}extensionsForScene(e){let t=this.resolveSceneRef(e);return{...(t!=null?this.host.bundle.scenes[t]:void 0)?.extensions}}extensionsForBlock(e,t){let n=this.resolveBlockRef(this.resolveSceneRef(e),t);return{...(n!=null?this.host.blockById.get(n):void 0)?.extensions}}extensionsForSnippet(e){let t=this.host.nodeIndex.get(e);return t?.type==="snippet"?{...t.extensions}:{}}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),n=t!=null?this.host.bundle.scenes[t]:void 0;if(!n)return[];let s=new Set;for(let i of n.blocks)nt(i.children,s);return[...s]}castForBlock(e,t){let n=this.resolveSceneRef(e),s=this.resolveBlockRef(n,t),i=s!=null?this.host.blockById.get(s):void 0;if(!i)return[];let o=new Set;return nt(i.children,o),[...o]}getOutline(){return Object.values(this.host.bundle.scenes).map(e=>({id:e.id,...y(e)?{gameId:y(e)}:{},name:e.name,...rt(e.gameData),...C(e.extensions),...this.tagsField(e.id),blocks:e.blocks.map(t=>({id:t.id,...y(t)?{gameId:y(t)}:{},name:t.name,...rt(t.gameData),...C(t.extensions),...this.tagsField(t.id),children:t.children.map(n=>this.outlineNode(n))}))}))}getBeatSequence(){let e=[];for(let t of Object.values(this.host.bundle.scenes))for(let n of t.blocks)D(n.children,s=>{if(s.type==="snippet")for(let i of s.beats??[])e.push({sceneId:t.id,blockId:n.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=N(this.host,e.id),n={id:e.id,kind:e.kind};if(e.kind==="line"){if(e.character!==void 0){n.character=e.character;let i=this.host.defaultStrings[se(e.character)]??this.host.castDisplay.get(e.character);i!==void 0&&(n.characterName=i)}if(e.direction!==void 0&&(n.direction=e.direction),e.qualifier!==void 0){n.qualifier=e.qualifier;let i=this.host.defaultStrings[ie(e.qualifier)]??this.host.qualifierDisplay.get(e.qualifier);i!==void 0&&(n.qualifierName=i)}let s=ae(this.host,e.character,e.qualifier,!0);s!==void 0&&(n.speakerLabel=s)}if(e.kind==="line"||e.kind==="text"){let s=this.host.padIndex.get(e.id);n.padAfter=s?.resolved??O,s?.own!==void 0&&(n.ownPadAfter=s.own);let i=this.host.defaultStrings[e.id];i!==void 0&&(n.text=i)}return e.gameData&&Object.keys(e.gameData).length&&(n.gameData=b(e.gameData)),e.extensions&&Object.keys(e.extensions).length&&(n.extensions=e.extensions),t&&t.length&&(n.tags=t),n}tagsField(e){let t=N(this.host,e);return t&&t.length?{tags:t}:{}}getFlow(e){return this.flowsById.get(e)}flows(){return[...this.flowsById.values()]}listBags(){let e=[{bag:this.host.patterBag}];for(let[t,n]of this.host.stageBags)e.push({bag:n,pathPrefix:`@scene:${t}.`});return e}checkpoint(){if(this.host.journal)throw new Error("a checkpoint is already open: roll it back or commit it first");let e={undo:[],flows:new Set,opened:new Set,selectors:new Set,flowSelectors:new Map};return this.host.journal=e,new K(e)}rollback(e){this.endCheckpoint(e);let t=e.journal.undo;for(let n=t.length-1;n>=0;n--)t[n]();r.forget(e.journal)}commit(e){this.endCheckpoint(e),r.forget(e.journal)}get inCheckpoint(){return this.host.journal!==null}endCheckpoint(e){if(this.host.journal!==e.journal)throw new Error("that checkpoint is not the open one");this.host.journal=null}static forget(e){e.undo=[],e.flows.clear(),e.opened.clear(),e.selectors.clear(),e.flowSelectors.clear()}refuseInCheckpoint(e){if(this.host.journal)throw new Error(`${e} can\'t be undone, so it isn\'t allowed while a checkpoint is open`)}captureFlow(e){let t=this.flowsById.get(e);if(!t)throw new Error(`no open flow \'${e}\' to capture`);return{version:V,...t.snapshot(),...Zt(e,this.host.registry.save())}}restoreFlow(e,t){this.refuseInCheckpoint("restoreFlow");let n=t.version;if(n!==3&&n!==V)throw new Error(`unsupported flow state version: ${String(n)}`);let s=t.cursor;if(typeof s!="object"||s===null)throw new Error("malformed flow state: no cursor");if(this.flowsById.has(e))throw new Error(`a flow \'${e}\' is already open; close it before restoring another under that id`);this.assertExternalScopes();let i=w.flow(e);this.host.registry.discardParked(i),this.host.registry.load(Yt(e,t),{keepParked:!0,refit:!0});let o=new A(e,this.host,this.defaultSeed);try{o.restore(t)}catch(a){throw o.close(),this.host.registry.discardParked(i),a}return this.flowsById.set(e,o),this.host.reportRefits(),o}closeFlow(e){this.refuseInCheckpoint("closeFlow"),this.flowsById.get(e)?.close(),this.flowsById.delete(e)}reset(){this.refuseInCheckpoint("reset");for(let e of this.flowsById.values())e.close();this.flowsById.clear(),this.host.patterBag.reseed(this.host.patterSharedDecls),this.host.sharedVisits.clear(),this.host.sharedSelectors.clear();for(let e of this.host.stageBags.keys())this.host.registry.remove(w.stage(e));this.host.stageBags.clear(),this.host.registry.discardParked("patter/")}release(e){for(let n of this.flowsById.values())n.releaseBags(e),n.close();this.flowsById.clear();let t=this.host.registry;for(let n of this.host.stageBags.keys())t.remove(w.stage(n),{keep:e});this.host.stageBags.clear();for(let n of["patter",...this.host.hostScopes])t.has(n)&&t.remove(n,{keep:e});for(let n of Object.keys(this.creationOptions.hostScopes??{}))t.has(n)&&t.remove(n)}getProperty(e){let{scope:t,name:n}=this.splitShared(e);return this.host.registry.get(t,n)}setProperty(e,t){let{scope:n,name:s}=this.splitShared(e);dt(this.host,n,s,t,null);let i=this.host.journal?this.host.registry.get(n,s):void 0;this.host.registry.set(n,s,t,{host:!0}),this.host.journal&&i!==void 0&&this.host.journal.undo.push(()=>this.host.registry.set(n,s,i,{host:!0}))}stageIndex(e,t){return B(this.ladderOf(e),t)}compareStages(e,t,n){return F(this.ladderOf(e),t,n)}nextStage(e,t){return L(this.ladderOf(e),t)}ladderOf(e){let{scope:t,name:n}=this.splitShared(e),s=zt(this.host,t,n);if(!s)throw new Error(`\'${e}\' is not a quality`);return s}log(){return this.engineLog}clearLog(){this.engineLog.length=0}onTrace(e){return this.engineTraceHandlers.add(e),this.host.tracing=!0,()=>{this.engineTraceHandlers.delete(e),this.host.tracing=this.host.logEnabled||this.engineTraceHandlers.size>0}}emitEngine(e,t,n){for(let s of this.engineTraceHandlers)s(e,t);this.host.logEnabled&&this.engineLog.push({...t,flow:e,seq:this.engineSeq++,...n?{scene:n}:{}})}getVisitCounts(){return Object.fromEntries(this.host.sharedVisits)}listProperties(){return this.host.patterSharedDecls.map(e=>({name:e.name,path:`@patter.${e.name}`,type:e.type,values:e.values,stages:e.stages,value:this.getProperty(`@${e.name}`),default:T(e),writable:e.writable??!0}))}splitShared(e){let t=ut(this.host,e);if(t.scope==="scene")throw new Error(`\'${e}\': @scene properties are scene-scoped - read/write them on a Flow, not the Engine`);return t}saveGame(){let e={};for(let[t,n]of this.flowsById)e[t]=n.snapshot();return{version:V,...this.host.ownsRegistry?{registry:this.host.registry.save()}:{},sharedVisits:Object.fromEntries(this.host.sharedVisits),sharedSelectors:ct(this.host.sharedSelectors),flows:e}}loadGame(e){this.refuseInCheckpoint("loadGame");let t=e.version;if(t!==3&&t!==V)throw new Error(`unsupported save version: ${String(t)}`);let n=e.flows;if(typeof n!="object"||n===null||Array.isArray(n))throw new Error("malformed save: no flows");this.assertExternalScopes();let s=this.host.registry;for(let[o,a]of this.flowsById)a.releaseBags(o in e.flows),a.close();this.flowsById.clear();for(let o of this.host.stageBags.keys())s.remove(w.stage(o),{keep:!0});this.host.stageBags.clear();let i=e.registry;i&&(this.host.ownsRegistry?s.load(i,{refit:!0}):s.load(i,{keepParked:!0,refit:!0})),this.host.sharedVisits.clear();for(let[o,a]of Object.entries(e.sharedVisits??{}))this.host.sharedVisits.set(o,a);this.host.sharedSelectors.clear();for(let[o,a]of lt(e.sharedSelectors))this.host.sharedSelectors.set(o,a);for(let[o,a]of Object.entries(e.flows)){let c=new A(o,this.host,this.defaultSeed);c.restore(a),this.flowsById.set(o,c)}this.host.reportRefits()}},A=class{id;host;local;registered=new Set;rngState;started=!1;flowEnded=!1;dryChoice=null;ranDry=null;closed=!1;currentSceneId=null;stack=[];activeSnippet=null;beatIndex=0;pendingChoice=null;pendingPrompt=null;pendingPromptOwnerId=null;selectors=new Map;visitCounts=new Map;sceneBags=new Map;patterResolver={get:e=>this.host.patterSharedNames.has(e)?this.host.patterBag.get(e):this.local.get(e),set:(e,t)=>{this.host.patterSharedNames.has(e)?this.host.registry.set("patter",e,t):this.local.set(e,t)}};sceneResolver={get:e=>{let t=this.currentSceneId;if(t!==null)return this.sceneBagFor(t,e)?.get(e)},set:(e,t)=>{let n=this.currentSceneId;if(n===null)return;let s=this.sceneBagFor(n,e);s&&s.set(e,t)}};evalCtx;ctxRevision=-1;registryQualities;flowLog=[];flowSeq=0;constructor(e,t,n){this.id=e,this.host=t,this.rngState=j(n),this.local=this.newLocal(),this.evalCtx={scopes:{},host:{nextRandom:this.rng,visits:s=>this.visitCounts.get(s)??0,patterVisits:s=>this.host.sharedVisits.get(s)??0},qualities:(s,i)=>this.stagesFor(s,i)}}context(){let e=this.host.registry;if(e.revision!==this.ctxRevision){let t=e.toEvalContext(),n=this.evalCtx.scopes;for(let s of Object.keys(n))delete n[s];Object.assign(n,t.scopes),n.patter=this.patterResolver,n.scene=this.sceneResolver,this.registryQualities=t.qualities,this.ctxRevision=e.revision}return this.evalCtx}stagesFor(e,t){let n=t.toLowerCase();if(e==="patter")return W(this.host,"patter",()=>[...this.host.patterSharedDecls,...this.host.patterLocalDecls]).get(n);if(e==="scene"){let s=this.currentSceneId;return s==null?void 0:W(this.host,`scene/${s}`,()=>this.host.bundle.scenes[s]?.sceneProps).get(n)}return this.registryQualities?.(e,t)??W(this.host,`host/${e}`,()=>this.host.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(n)}begin(e,t){let n=this.host.bundle.start;if(e===void 0&&t===void 0&&n&&this.host.bundle.scenes[n.scene]&&(e=n.scene,n.block!==void 0&&this.host.blockIndex.get(n.block)?.sceneId===n.scene&&(t=n.block)),this.host.journal&&!this.host.journal.opened.has(this))throw new Error("a flow can\'t be started or reset while a checkpoint is open");if(this.releaseBags(!1),this.host.registry.discardParked(w.flow(this.id)),this.mountLocal(),this.selectors.clear(),this.visitCounts.clear(),this.stack=[],this.currentSceneId=null,this.flowEnded=!1,this.dryChoice=null,this.ranDry=null,this.activeSnippet=null,this.beatIndex=0,this.clearPending(),this.started=!0,t){let s=this.host.blockIndex.get(t);if(!s)throw new Error(`unknown block: ${t}`);this.enterSceneSetup(s.sceneId),this.stack=[{sceneId:s.sceneId,containerId:t,index:0}],this.enter(t)}else{let s=e??Object.keys(this.host.bundle.scenes)[0],i=s?this.host.bundle.scenes[s]:void 0;if(!i)throw new Error(s?`unknown scene: ${s}`:"no scenes in bundle");this.enterSceneSetup(s);let o=i.blocks[0];o&&(this.stack=[{sceneId:s,containerId:o.id,index:0}],this.enter(o.id))}this.settle()}reset(e,t){this.begin(e,t)}goto(e,t){if(this.closed)return!1;if(this.touch(),e==="END")return this.started=!0,this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!0,this.stack=[],this.dryChoice=null,this.ranDry=null,!0;let n=ve(this.host,e);if(n===void 0)return!1;let s;return t!==void 0&&(s=xe(this.host,n,t),s===void 0)?!1:this.started?(this.clearPending(),this.activeSnippet=null,this.beatIndex=0,this.flowEnded=!1,this.dryChoice=null,this.ranDry=null,this.enterTarget(s??n,"jump"),this.settle(),!0):(this.begin(n,s),!0)}close(){this.releaseBags(!1),this.closed=!0,this.flowEnded=!0,this.stack=[],this.activeSnippet=null,this.beatIndex=0,this.clearPending()}clearPending(){this.pendingChoice=null,this.pendingPrompt=null,this.pendingPromptOwnerId=null}touch(){let e=this.host.journal;if(!e||e.flows.has(this))return;e.flows.add(this);let{rngState:t,started:n,flowEnded:s,dryChoice:i,ranDry:o,currentSceneId:a,activeSnippet:c,beatIndex:l,pendingChoice:d,pendingPrompt:g,pendingPromptOwnerId:f}=this,I=this.stack.map(Q=>({...Q}));e.undo.push(()=>{Object.assign(this,{rngState:t,started:n,flowEnded:s,dryChoice:i,ranDry:o,currentSceneId:a,activeSnippet:c,beatIndex:l,pendingChoice:d,pendingPrompt:g,pendingPromptOwnerId:f,stack:I})})}get isClosed(){return this.closed}get currentScene(){return this.currentSceneId}advance(){if(this.closed)return{type:"end",reason:"ended"};if(!this.started)throw new Error("flow has not been started");if(this.touch(),this.pendingPrompt){let e=this.pendingPrompt;return this.pendingPrompt=null,this.pendingPromptOwnerId=null,e.shown?this.promptResult(e.beat,e.shown):this.beatResult(e.beat)}return this.settle(),!this.flowEnded&&!this.activeSnippet&&!this.pendingChoice&&(this.flowEnded=!0),this.flowEnded?this.endResult():(this.dryChoice=null,this.pendingChoice?{type:"choice",groupId:this.pendingChoice.groupId,options:this.offer(this.pendingChoice)}:this.beatResult(this.activeSnippet.beats[this.beatIndex++]))}endResult(){if(this.dryChoice!==null){let e=this.dryChoice;this.dryChoice=null,this.ranDry=e,this.report({flow:this.id,kind:"dry-choice",node:e,message:`choice \'${e}\' ran dry (no option left to take, and no fallback) with nothing after it, so the flow ended there`})}return{type:"end",reason:this.ranDry!==null?"ran-dry":"ended"}}advanceToStop(){let e=[];for(;;){let t=this.advance();if(t.type==="choice"||t.type==="end")return{played:e,stop:t};e.push(t)}}settle(){let e=0;for(;;){if(++e>1e4)throw new Error("flow did not settle after 10000 transitions - likely a jump cycle with no deliverable content");if(this.flowEnded||this.pendingChoice)return;if(this.activeSnippet){if(this.beatIndex<(this.activeSnippet.beats?.length??0))return;this.runEffects(this.activeSnippet.onExit,this.activeSnippet.id);let i=this.activeSnippet.jump;this.activeSnippet=null,this.beatIndex=0,this.resolveJump(i);continue}let t=this.stack[this.stack.length-1];if(!t){this.flowEnded=!0;return}t.sceneId!==this.currentSceneId&&(this.currentSceneId=t.sceneId);let n=this.childrenOf(t.containerId);if(!n){this.stack.pop();continue}let s=t.index;for(;t.index<n.length&&!this.eligible(n[t.index]);)t.index++;if(this.host.tracing&&t.index!==s&&this.emit({type:"select",group:t.containerId,selector:"run",children:n.slice(s,t.index+1).map((i,o)=>({id:i.id,eligible:s+o===t.index})),picked:n[t.index]?.id??null}),t.index>=n.length){this.stack.pop();continue}this.enterChild(n[t.index++])}}listBags(){let e=[{bag:this.local,pathPrefix:`${this.id}/@patter.`}];for(let[t,n]of this.sceneBags)e.push({bag:n,pathPrefix:`${this.id}/@scene:${t}.`});return e}log(){return this.flowLog}clearLog(){this.flowLog.length=0}emit(e){let t=this.currentSceneId??void 0;this.host.emitEngine(this.id,e,t),this.host.logEnabled&&this.flowLog.push({...e,seq:this.flowSeq++,...t?{scene:t}:{}})}getVisitCounts(){return Object.fromEntries(this.visitCounts)}stepFor(e){let t=Ut(this.host.bundle).get(e);return t?this.beatResult(t):void 0}getChoices(){return this.pendingChoice?this.offer(this.pendingChoice):[]}offer(e){return e.options.map(t=>{let n=e.byId.get(t.id),s=this.promptFor(n);return e.shown.set(t.id,s),{id:t.id,prompt:s,eligible:t.eligible,gameData:b(t.gameData),...C(n.extensions)}})}choose(e){let t=this.pendingChoice;if(!t)throw new Error("no choice is pending");let n=t.options.find(a=>a.id===e);if(!n)throw new Error(`unknown choice option: ${e}`);if(!n.eligible)throw new Error(`choice option is not eligible: ${e}`);this.touch();let s=t.byId.get(e);this.host.tracing&&this.emit({type:"chose",group:t.groupId,option:e}),this.pendingChoice=null;let i=this.host.replayPromptOnChoose?this.authoredPromptOf(s):void 0,o=i?t.shown.has(e)?t.shown.get(e):this.promptFor(s):void 0;this.pendingPrompt=i&&o?{beat:i,shown:{...o}}:null,this.pendingPromptOwnerId=this.pendingPrompt?s.id:null,this.enterChild(s)}isEnded(){return this.flowEnded}getProperty(e){let{scope:t,name:n}=this.splitRef(e);return t==="patter"?this.patterResolver.get(n):t==="scene"?this.sceneResolver.get(n):this.host.registry.get(t,n)}setProperty(e,t){this.writeProperty(e,t,!0)}stageIndex(e,t){return B(this.ladderOf(e),t)}compareStages(e,t,n){return F(this.ladderOf(e),t,n)}nextStage(e,t){return L(this.ladderOf(e),t)}ladderOf(e){let{scope:t,name:n}=this.splitRef(e);this.context();let s=this.stagesFor(t,n);if(!s)throw new Error(`\'${e}\' is not a quality`);return s}writeProperty(e,t,n){let{scope:s,name:i}=this.splitRef(e);if(n){if(s==="scene"&&this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);dt(this.host,s,i,t,this.currentSceneId)}let o=this.host.journal;if(s==="patter"){if(o){let a=this.host.patterSharedNames.has(i),c=a?this.host.patterBag.get(i):this.local.get(i),l=this.local;c!==void 0&&o.undo.push(a?()=>this.host.registry.set("patter",i,c):()=>l.set(i,c))}this.patterResolver.set(i,t)}else if(s==="scene"){if(this.currentSceneId===null)throw new Error(`\'${e}\': the flow has not entered a scene yet`);if(o){let a=this.sceneBagFor(this.currentSceneId,i),c=a?.get(i);a&&c!==void 0&&o.undo.push(()=>a.set(i,c))}this.sceneResolver.set(i,t)}else{let a=o?this.host.registry.get(s,i):void 0;this.host.registry.set(s,i,t,n?{host:!0}:void 0),o&&a!==void 0&&o.undo.push(()=>this.host.registry.set(s,i,a,{host:!0}))}}snapshot(){let e=Ht(this.host.bundle),t=this.activeSnippet?.beats?.[this.beatIndex]?.id;return{rngState:this.rngState,visits:Object.fromEntries(this.visitCounts),cursor:{flowEnded:this.flowEnded,...this.ranDry!==null?{ranDry:this.ranDry}:{},currentSceneId:this.currentSceneId,stack:this.stack.map(n=>{let s=this.childrenOf(n.containerId)?.[n.index],i=e.get(n.containerId);return{...n,...s?{nextId:s.id}:{},...i!==void 0?{choiceId:i}:{}}}),activeSnippetId:this.activeSnippet?.id??null,...t!==void 0?{beatId:t}:{},pendingChoice:this.pendingChoice?{groupId:this.pendingChoice.groupId,options:this.pendingChoice.options.map(n=>({id:n.id,eligible:n.eligible,...n.gameData!==void 0?{gameData:b(n.gameData)}:{}}))}:null,pendingPromptOwnerId:this.pendingPromptOwnerId,...this.pendingPrompt?.shown?{pendingPrompt:Kt(this.pendingPrompt.shown)}:{},selectors:ct(this.selectors)}}}restore(e){if(this.host.journal)throw new Error("a flow can\'t be restored while a checkpoint is open");let t=this.restoreCursor(e);this.releaseBags(!0),this.mountLocal();for(let n of new Set([this.currentSceneId,...this.stack.map(s=>s.sceneId)]))n!==null&&this.host.bundle.scenes[n]&&this.ensureSceneBags(n);t&&(this.report({flow:this.id,kind:"save",node:t.node,message:`the save\'s place has gone from this build: ${t.instead}`}),t.reoffer&&this.setupChoice(t.reoffer))}restoreCursor(e){this.rngState=j(e.rngState),this.visitCounts=new Map(Object.entries(e.visits??{}));let t=e.cursor;this.started=!0,this.flowEnded=t.flowEnded,this.dryChoice=null,this.ranDry=t.flowEnded&&typeof t.ranDry=="string"?t.ranDry:null,this.currentSceneId=t.currentSceneId;let n=null;this.stack=[];for(let s of t.stack){let{nextId:i,choiceId:o,...a}=s,c=this.childrenOf(a.containerId),l=!c&&o!==void 0?this.host.nodeIndex.get(o):void 0;if(l?.type==="group"&&l.selector==="choice"){n={node:t.activeSnippetId??t.pendingChoice?.groupId??a.containerId,instead:`option ${a.containerId} has gone, so choice ${l.id} is offered again`,reoffer:l};break}let d=i!==void 0?c?.findIndex(g=>g.id===i)??-1:-1;this.stack.push(d>=0?{...a,index:d}:{...a})}if(this.activeSnippet=null,this.beatIndex=0,!n?.reoffer&&t.activeSnippetId!==null){let s=this.host.nodeIndex.get(t.activeSnippetId);if(s&&s.type==="snippet"){let i=s.beats??[];if(this.activeSnippet=s,t.beatId!==void 0){let o=i.findIndex(a=>a.id===t.beatId);this.beatIndex=o>=0?o:i.length,o<0&&(n={node:t.activeSnippetId,instead:`beat ${t.beatId} has gone from snippet ${t.activeSnippetId}, so the snippet ends there`})}else this.beatIndex=t.beatIndex??i.length}else n={node:t.activeSnippetId,instead:`snippet ${t.activeSnippetId} has gone, so play carries on after it`}}if(this.selectors=lt(t.selectors),this.pendingChoice=null,!n?.reoffer&&t.pendingChoice!==null){let s=new Map,i=[];for(let o of t.pendingChoice.options){let a=this.host.nodeIndex.get(o.id);a&&(s.set(o.id,a),i.push({id:o.id,eligible:o.eligible,...o.gameData!==void 0?{gameData:o.gameData}:{}}))}if(i.length>0)this.pendingChoice={groupId:t.pendingChoice.groupId,options:i,byId:s,shown:new Map};else{let o=this.host.nodeIndex.get(t.pendingChoice.groupId);n=o?.type==="group"&&o.selector==="choice"?{node:t.pendingChoice.groupId,instead:`none of choice ${o.id}\'s options is left, so it\'s offered again`,reoffer:o}:{node:t.pendingChoice.groupId,instead:`choice ${t.pendingChoice.groupId} has gone, so play carries on after it`}}}if(this.pendingPrompt=null,this.pendingPromptOwnerId=n?.reoffer?null:t.pendingPromptOwnerId??null,this.pendingPromptOwnerId){let s=this.host.nodeIndex.get(this.pendingPromptOwnerId),i=s?this.authoredPromptOf(s):void 0,o=t.pendingPrompt,a=o?.kind==="line"&&o.speakerLabel===void 0?ae(this.host,o.character,o.qualifier):void 0;i?this.pendingPrompt={beat:i,...o?{shown:{...o,padAfter:o.padAfter??this.padOf(i.id),...a!==void 0?{speakerLabel:a}:{}}}:{}}:this.pendingPromptOwnerId=null}return n}enterSceneSetup(e){let t=this.host.bundle.scenes[e];if(!t)throw new Error(`unknown scene: ${e}`);this.currentSceneId=e,this.enter(e),this.seedScene(t),this.runEffects(t.onEntry,t.id)}enterChild(e){if(this.enter(e.id),e.type==="snippet"){this.beginSnippet(e);return}let t=e.selector??"run";if(t==="run"){this.stack.push({sceneId:this.currentSceneId,containerId:e.id,index:0});return}if(t==="choice"){this.setupChoice(e);return}let n=this.selectChild(e);n&&this.enterChild(n)}childrenOf(e){let t=this.host.blockById.get(e);if(t)return t.children;let n=this.host.nodeIndex.get(e);if(n&&n.type==="group")return n.children}beginSnippet(e){this.runEffects(e.onEnter,e.id),this.activeSnippet=e,this.beatIndex=0}setupChoice(e){let t=[],n=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,eligible:a,...o.gameData!==void 0?{gameData:o.gameData}:{}}),n.set(o.id,o))}if(t.some(o=>o.eligible)){this.host.tracing&&this.emit({type:"choice",group:e.id,options:t.map(o=>({id:o.id,eligible:o.eligible}))}),this.pendingChoice={groupId:e.id,options:t,byId:n,shown:new Map};return}let i=s.find(o=>this.eligible(o));if(i){this.enterChild(i);return}this.host.tracing&&this.emit({type:"dry",group:e.id}),this.host.onDryChoice?.(e.id),this.dryChoice=e.id}resolveJump(e){e&&this.enterTarget(e.to,e.mode==="call"?"call":"jump")}enterTarget(e,t){if(this.host.tracing&&this.emit({type:"jump",to:e,mode:t}),e==="END"){this.flowEnded=!0,this.stack=[];return}let n,s,i=this.host.bundle.scenes[e];if(i){this.enterSceneSetup(e);let a=i.blocks[0];if(!a){t==="jump"&&(this.stack=[]);return}n=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),n=a.sceneId,s=e}this.enter(s);let o={sceneId:n,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)})),n=e.children.filter((c,l)=>t[l].eligible),s=e.options?.order??"sequential",i=e.options?.exhaust??"once",o=c=>(this.host.tracing&&this.emit({type:"select",group:e.id,selector:e.selector??"default",...e.selector==="sequence"?{order:s,exhaust:i}:{},children:t,picked:c?.id??null}),c);if(n.length===0)return o(null);let a=this.selectorState(e);switch(e.selector){case"branch":return o(n[0]);case"sequence":return o(s==="shuffle"?this.pickShuffle(n,i,a):s==="specificity"?this.pickSpecificity(n,i,a):this.pickSequential(n,i,a));default:return null}}pickSequential(e,t,n){let s=e.length,i=n.seq??0;return n.seq=i+1,t==="repeat"?e[i%s]:i<s?e[i]:t==="stick"?e[s-1]:null}pickShuffle(e,t,n){let s=e.length,i=t==="stick",o=()=>(i?e.slice(0,s-1):e).map(f=>f.id),a=new Set(e.map(f=>f.id));if(n.bag===void 0&&(n.bag=o()),!n.bag.some(f=>a.has(f))){if(t==="once")return null;if(i){let f=e[s-1];return n.last=f.id,f}if(n.bag=o(),n.bag.length===0)return null}let c=n.bag.filter(f=>a.has(f)),l=n.last!==void 0&&c.length>1?c.indexOf(n.last):-1,d=Math.floor(this.rng()*(l>=0?c.length-1:c.length));l>=0&&d>=l&&d++;let g=c[d];return n.bag.splice(n.bag.indexOf(g),1),n.last=g,e.find(f=>f.id===g)}pickSpecificity(e,t,n){let s=e;if(t!=="repeat"){n.bag===void 0&&(n.bag=e.map(d=>d.id));let l=new Set(n.bag);if(s=e.filter(d=>l.has(d.id)),s.length===0)return t==="stick"&&n.last!==void 0?e.find(d=>d.id===n.last)??null:null}let i=-1,a=s.map(l=>{let d=this.specScore(l);return d>i&&(i=d),{c:l,s:d}}).filter(l=>l.s===i).map(l=>l.c),c;if(a.length===1)c=a[0];else{let l=n.last!==void 0?a.findIndex(g=>g.id===n.last):-1,d=Math.floor(this.rng()*(l>=0?a.length-1:a.length));l>=0&&d>=l&&d++,c=a[d]}return t!=="repeat"&&(n.bag=n.bag.filter(l=>l!==c.id)),n.last=c.id,c}specScore(e){return e.condition?this.matchedSpec(this.conditionAst(e.condition),!0,e):0}matchedSpec(e,t,n){return Ne(e,i=>{try{return at(Y(i,this.context(),ye))}catch(o){return this.reportError("best-match",n.id,n.condition,o),!1}},{want:t})}selectorState(e){let t=e.shared?this.host.sharedSelectors:this.selectors,n=this.host.journal,s;if(n&&(s=e.shared?n.selectors:n.flowSelectors.get(this),s||(s=new Set,n.flowSelectors.set(this,s))),n&&s&&!s.has(e.id)){s.add(e.id);let o=t.get(e.id),a=o?{...o,...o.bag?{bag:[...o.bag]}:{}}:void 0;n.undo.push(()=>{a?t.set(e.id,a):t.delete(e.id)})}let i=t.get(e.id);return i||(i={},t.set(e.id,i)),i}runEffects(e,t){for(let n of e??[]){let s;try{s=this.evalExpr(n.value);let i=this.host.tracing?this.getProperty(n.target):void 0;this.writeProperty(n.target,s,!1),this.host.tracing&&this.emit({type:"write",target:n.target,value:s,...i!==void 0?{prev:i}:{}})}catch(i){this.reportError("effect",t,n.value,i)}}}eligible(e){if(!e.condition)return!0;try{return at(this.evalExpr(e.condition))}catch(t){return this.reportError("condition",e.id,e.condition,t),!1}}reportError(e,t,n,s){let i=s instanceof Error?s.message:String(s),o=n?.src;this.report({flow:this.id,kind:e,node:t,...o?{source:o}:{},message:i})}report(e){this.host.onError(e),this.host.tracing&&this.emit({type:"diagnostic",kind:e.kind,node:e.node,...e.source?{source:e.source}:{},message:e.message})}evalExpr(e){return Y(this.conditionAst(e),this.context(),ye)}conditionAst(e){let t=Qe.get(e);return t||(t=R(e.ast),Qe.set(e,t)),t}enter(e){let t=this.visitCounts,n=t.get(e);t.set(e,(n??0)+1);let s=this.host.sharedVisits,i=s.get(e);s.set(e,(i??0)+1),this.host.journal&&this.host.journal.undo.push(()=>{n===void 0?t.delete(e):t.set(e,n),i===void 0?s.delete(e):s.set(e,i)})}rng=()=>{if(this.host.customRng)return this.host.customRng();let e=ge(this.rngState),t=e.next();return this.rngState=e.state(),t};beatResult(e){let t=N(this.host,e.id),n=t&&t.length?{tags:t}:{};switch(e.kind){case"gameEvent":return{type:"gameEvent",id:e.id,gameData:b(e.gameData),...C(e.extensions),...n};case"text":return{type:"text",id:e.id,text:this.interpolate(this.resolveString(e.id)),padAfter:this.padOf(e.id),...e.action?{action:!0}:{},gameData:b(e.gameData),...C(e.extensions),...n};case"line":{let s=this.resolveString(e.id),i=!this.host.captionsOn,a=i&&e.character===this.host.captionCharacter?"":this.captionLine(this.host.bundle.voiced?s:this.interpolate(s)),c=i&&a.length===0;return{type:"line",id:e.id,text:a,character:c?void 0:e.character,characterName:c?void 0:this.resolveCharacterName(e.character),direction:c?void 0:e.direction,qualifier:c?void 0:e.qualifier,qualifierName:c?void 0:this.resolveQualifierName(e.qualifier),speakerLabel:c?void 0:ae(this.host,e.character,e.qualifier),padAfter:this.padOf(e.id),gameData:b(e.gameData),...C(e.extensions),...n}}}}interpolate(e){return $e(e,t=>this.getProperty(t))}stripCaptions(e){return je(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 n=this.interpolate(this.resolveString(t.id)),s=this.padOf(t.id);return t.kind==="line"?{kind:"line",text:this.captionLine(n),character:t.character,characterName:this.resolveCharacterName(t.character),direction:t.direction,qualifier:t.qualifier,qualifierName:this.resolveQualifierName(t.qualifier),speakerLabel:ae(this.host,t.character,t.qualifier),padAfter:s}:{kind:"text",text:n,padAfter:s}}authoredPromptOf(e){return e.type==="group"?e.prompt:void 0}promptResult(e,t){let n=N(this.host,e.id),s=n&&n.length?{tags:n}:{};return t.kind==="text"?{type:"text",id:e.id,text:t.text,padAfter:this.padOf(e.id),gameData:b(e.gameData),...C(e.extensions),...s}:{type:"line",id:e.id,text:t.text,character:t.character,characterName:t.characterName,direction:t.direction,qualifier:t.qualifier,qualifierName:t.qualifierName,speakerLabel:t.speakerLabel,padAfter:this.padOf(e.id),gameData:b(e.gameData),...C(e.extensions),...s}}promptBeatOf(e){return e.type==="group"&&e.prompt?e.prompt:((e.type==="snippet"?e:this.firstTextSnippetIn(e.children))?.beats??[]).find(n=>n.kind==="line"||n.kind==="text")}firstTextSnippetIn(e){let t;return D(e,n=>{!t&&n.type==="snippet"&&(n.beats??[]).some(s=>s.kind==="line"||s.kind==="text")&&(t=n)}),t}resolveString(e){if(this.host.emitIds)return e;let t=this.host.strings[e];if(t!==void 0)return t;let n=this.host.defaultStrings[e];return n!==void 0?`<Untranslated: ${e}> ${n}`:e}resolveCharacterName(e){if(e===void 0||this.host.emitIds)return;let t=se(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.castDisplay.get(e)}padOf(e){return this.host.padIndex.get(e)?.resolved??O}resolveQualifierName(e){if(e===void 0||this.host.emitIds)return;let t=ie(e);return this.host.strings[t]??this.host.defaultStrings[t]??this.host.qualifierDisplay.get(e)}splitRef(e){return ut(this.host,e)}newLocal(){return new E(this.host.patterLocalDecls,{pathPrefix:"@patter."})}mountLocal(){this.local=this.newLocal();let e=w.flowGlobals(this.id);this.host.registry.mountOwned(e,this.local,{owner:U}),this.registered.add(e),this.host.reportRefits()}releaseBags(e){for(let t of this.registered)this.host.registry.remove(t,{keep:e});this.registered.clear(),this.sceneBags.clear()}sceneBagFor(e,t){if(this.host.bundle.scenes[e])return this.ensureSceneBags(e),this.host.sceneSharedNames.get(e)?.has(t.toLowerCase())?this.host.stageBags.get(e):this.sceneBags.get(e)}ensureSceneBags(e){let t=this.host.sceneSharedNames.get(e)??new Set,n=this.host.bundle.scenes[e]?.sceneProps??[],s=this.host.journal,i=!this.sceneBags.has(e)||!this.host.stageBags.has(e);if(!this.sceneBags.has(e)){let o=new E(n.filter(l=>!t.has(l.name.toLowerCase())),{pathPrefix:"@scene."}),a=w.flowScene(this.id,e),c=st(this.host.registry,a,o,s!==null);this.registered.add(a),this.sceneBags.set(e,o),s&&s.undo.push(()=>{this.sceneBags.get(e)===o&&(this.host.registry.remove(a,c?{keep:!0}:void 0),this.registered.delete(a),this.sceneBags.delete(e))})}if(!this.host.stageBags.has(e)){let o=new E(n.filter(c=>t.has(c.name.toLowerCase())),{pathPrefix:"@scene."}),a=st(this.host.registry,w.stage(e),o,s!==null);this.host.stageBags.set(e,o),s&&s.undo.push(()=>{this.host.stageBags.get(e)===o&&(this.host.registry.remove(w.stage(e),a?{keep:!0}:void 0),this.host.stageBags.delete(e))})}i&&this.host.reportRefits()}seedScene(e){let t=this.host.sceneSharedNames.get(e.id)??new Set;this.ensureSceneBags(e.id);for(let n of e.sceneProps??[]){if(!n.temporary)continue;let s=n.name.toLowerCase(),i=t.has(s)?this.host.stageBags.get(e.id):this.sceneBags.get(e.id);if(!i)continue;let o=this.host.journal?i.get(s):void 0;o!==void 0&&this.host.journal.undo.push(()=>i.set(s,o)),i.set(s,T(n))}}};function nt(r,e){D(r,t=>{if(t.type==="group"){t.prompt?.kind==="line"&&t.prompt.character&&e.add(t.prompt.character);return}for(let n of t.beats??[])n.kind==="line"&&n.character&&e.add(n.character)})}function ae(r,e,t,n=!1){if(!e||r.emitIds)return;let s=c=>(n?void 0:r.strings[c])??r.defaultStrings[c],i=s(se(e))??r.castDisplay.get(e)??e;if(t===void 0)return i;let o=r.speakerLabels.get(`${e}\\0${t}`);if(o!==void 0)return s(qe(e,t))??o;let a=s(ie(t))??r.qualifierDisplay.get(t)??t;return Ue(s(_e)??r.speakerPattern,i,a)}function rt(r){return r&&Object.keys(r).length?{gameData:b(r)}:{}}function C(r){return r&&Object.keys(r).length?{extensions:r}:{}}function Kt(r){let e={kind:r.kind,text:r.text};return r.character!==void 0&&(e.character=r.character),r.characterName!==void 0&&(e.characterName=r.characterName),r.direction!==void 0&&(e.direction=r.direction),r.qualifier!==void 0&&(e.qualifier=r.qualifier),r.qualifierName!==void 0&&(e.qualifierName=r.qualifierName),r.speakerLabel!==void 0&&(e.speakerLabel=r.speakerLabel),typeof r.padAfter=="number"&&(e.padAfter=r.padAfter),e}function ct(r){let e={};for(let[t,n]of r){let s={};n.seq!==void 0&&(s.seq=n.seq),n.bag&&(s.bag=[...n.bag]),n.last!==void 0&&(s.last=n.last),e[t]=s}return e}function lt(r){let e=new Map;for(let[t,n]of Object.entries(r??{})){let s={};n.seq!==void 0&&(s.seq=n.seq),n.bag&&(s.bag=[...n.bag]),n.last!==void 0&&(s.last=n.last),e.set(t,s)}return e}function st(r,e,t,n){let s=n?JSON.stringify(t.save()):"";return r.mountOwned(e,t,{owner:U}),n&&JSON.stringify(t.save())!==s}function it(r){return{name:r.name,type:r.type,values:r.values,stages:r.stages,default:r.default}}var Jt=new Set(["rng","seed","locale","hostScopes","registry","replayPromptOnChoose","closedCaptions","onDryChoice","onError","onRefit","log"]);function be(r,e){let t=r.locales.default;if(e===void 0)return{locale:t,table:t};if(r.localisation?.mode==="ids")return{locale:e,table:e};let n=[...new Set([t,...r.locales.included,...Object.keys(r.strings)])];if(n.includes(e))return{locale:e,table:e};let s=c=>c.toLowerCase().replace(/_/g,"-"),i=n.find(c=>s(c)===s(e));if(i!==void 0)return{locale:i,table:i,problem:`the locale "${e}" is spelled "${i}" in this bundle, so it\'s using "${i}"`};let o=s(e).split("-")[0],a=n.find(c=>s(c)===o);return a!==void 0?{locale:a,table:a,problem:`this bundle has no locale "${e}", so it\'s using its language, "${a}"`}:{locale:t,problem:`this bundle has no locale "${e}", so it\'s using the default, "${t}", with every string marked untranslated`}}function ot(r,e){return e.table!==void 0?r[e.table]??{}:{}}function dt(r,e,t,n,s){let i=c=>c?.find(l=>l.name.toLowerCase()===t),o;if(e==="patter")o=i(r.bundle.properties);else if(e==="scene")o=i(s!==null?r.bundle.scenes[s]?.sceneProps:void 0);else{let c=r.bundle.scopeRegistry?.scopes.find(l=>l.token===e);if(!c||c.declarations===void 0)return;o=i(c.declarations)}let a=`@${e}.${t}`;if(o===void 0)throw new Error(`no property at "${a}": the content declares no ${a}`);if(!$(o,n,{noneChosen:o.type==="enum"&&o.default===""}))throw new Error(`"${a}" takes ${Qt(o)}; ${JSON.stringify(n)} doesn\'t fit`)}function Qt(r){let e=t=>(t??[]).map(n=>JSON.stringify(n)).join(", ");switch(r.type){case"number":return"a number";case"boolean":return"true or false";case"enum":return`one of ${e(r.values)}`;case"flags":return r.values!==void 0&&r.values.length>0?`a list of flags from ${e(r.values)}`:"a list of flags";case"quality":return`one of its stages ${e(r.stages)}`;default:return"text"}}function W(r,e,t){let n=r.qualityLadders.get(e);if(!n){n=new Map;for(let s of t()??[]){let i=s.name.toLowerCase();s.type==="quality"&&s.stages&&!n.has(i)&&n.set(i,s.stages)}r.qualityLadders.set(e,n)}return n}function zt(r,e,t){let n=t.toLowerCase();return e==="patter"?W(r,"patter",()=>[...r.patterSharedDecls,...r.patterLocalDecls]).get(n):r.registry.toEvalContext().qualities?.(e,t)??W(r,`host/${e}`,()=>r.bundle.scopeRegistry?.scopes.find(s=>s.token===e)?.declarations).get(n)}function pt(r){return{name:r.name,type:r.type,values:r.values,stages:r.stages,default:r.default,writable:r.writable}}function Xt(r,e){let t=pt(r),n=r.writable??e;return n===void 0?t:{...t,writable:n}}function ve(r,e){return re(r.bundle.scenes,e,r.sceneGameIdToId)}function xe(r,e,t){return r.blockGameIdToId.get(e)?.get(t)??(r.blockIndex.get(t)?.sceneId===e?t:void 0)}function ut(r,e){r.refSplitRevision!==r.registry.revision&&(r.refSplitCache.clear(),r.refSplitRevision=r.registry.revision);let t=r.refSplitCache.get(e);return t||(t=Me(e,n=>n==="scene"||r.registry.has(n)||(r.bundle.externalScopes?.includes(n)??!1)),r.refSplitCache.set(e,t)),t}function Yt(r,e){let t={};e.scopes?.patter&&(t[w.flowGlobals(r)]=e.scopes.patter);for(let[n,s]of Object.entries(e.sceneBags??{}))t[w.flowScene(r,n)]=s;return t}function Zt(r,e){let t={scopes:{},sceneBags:{}},n=w.flow(r);for(let[s,i]of Object.entries(e)){if(!s.startsWith(n))continue;let o=s.slice(n.length);o==="patter"?t.scopes.patter=i:o.startsWith("scene/")&&(t.sceneBags[Wt(o.slice(6))]=i)}return t}function at(r){return typeof r=="boolean"?r:typeof r=="number"?r!==0:typeof r=="string"?r!=="":r.length>0}var en=(r,e)=>r.shared??e;function ke(r,e){return{name:r.name,type:r.type,hasDefault:r.default!==void 0,...r.default!==void 0?{default:r.default}:{},shared:en(r,e)}}function tn(r){return{name:r.name,type:r.type,hasDefault:r.default!==void 0,...r.values?{values:[...r.values]}:{},...r.purpose?{purpose:r.purpose}:{}}}function nn(r,e){e.blocks++;let t=[...r.children];for(;t.length;){let n=t.pop();if(n.type==="group"){e.groups++,n.prompt&&e.prompts++,t.push(...n.children);continue}e.snippets++;for(let s of n.beats??[])e.beats++,s.kind==="gameEvent"&&e.gameEvents++}}function gt(r){let e={scenes:0,blocks:0,groups:0,snippets:0,beats:0,prompts:0,gameEvents:0,cast:r.cast?.length??0},t=[],n=[];for(let o of Object.values(r.scenes)){e.scenes++;let a=y(o);t.push({gameId:a,name:o.name,blocks:o.blocks.map(c=>({gameId:y(c),name:c.name}))});for(let c of o.blocks)nn(c,e);o.sceneProps?.length&&n.push({gameId:a,properties:o.sceneProps.map(c=>ke(c,!1))})}let s=(r.scopeRegistry?.scopes??[]).map(o=>({token:o.token,writable:o.writable??!0,opaque:o.declarations===void 0,properties:(o.declarations??[]).map(a=>ke(a,!0))})),i=Object.entries(r.gameDataFields??{}).filter(([,o])=>(o?.length??0)>0).map(([o,a])=>({kind:o,fields:(a??[]).map(tn)}));return{identity:{schema:r.schema,project:r.content.project,...r.content.version!==void 0?{version:r.content.version}:{},...r.content.hash!==void 0?{hash:r.content.hash}:{},...r.content.structureHash!==void 0?{structureHash:r.content.structureHash}:{},voiced:r.voiced,defaultLocale:r.locales.default,locales:[...r.locales.included],localisation:r.localisation?.mode??"embedded",sourceDebug:r.localisation?.sourceDebug??!1},addresses:t,hostScopes:s,properties:{patter:(r.properties??[]).map(o=>ke(o,!0)),scene:n},gameData:i,counts:e}}var J=(r,e)=>Object.prototype.hasOwnProperty.call(r,e);function ft(r,e){return r.gameDataFields?.[e]??[]}function Ee(r,e,t){return e&&J(e,t)?e[t]:r.find(n=>n.name===t)?.default}function ht(r,e){let t={};for(let n of r){let s=Ee(r,e,n.name);s!==void 0&&(t[n.name]=s)}for(let[n,s]of Object.entries(e??{}))J(t,n)||(t[n]=s);return t}function mt(r,e,t){let n=r.extensions!==void 0&&J(r.extensions,e)?r.extensions[e]:void 0;return n!==void 0&&J(n.fields,t)?n.fields[t]??[]:[]}function yt(r,e,t=""){let n=o=>({...o?.extensions??{}}),s=Object.values(r.scenes??{}),i=o=>(J(r.scenes??{},o)?r.scenes[o]:void 0)??s.find(a=>y(a)===o);switch(e){case"project":{let o={};for(let[a,c]of Object.entries(r.extensions??{}))c.project!==void 0&&(o[a]=c.project);return o}case"scene":return n(i(t));case"block":{let o=t.indexOf("/");if(o<0)return{};let a=i(t.slice(0,o))?.blocks??[],c=t.slice(o+1);return n(a.find(l=>l.id===c)??a.find(l=>y(l)===c))}case"snippet":case"option":case"line":case"text":case"gameEvent":{let o,a=(c,l)=>{for(let d of c??[]){if(o!==void 0)return;if(d.type==="group"){if(l&&d.id===t&&e==="option"){o=d;return}if(l&&d.prompt?.id===t&&d.prompt.kind===e){o=d.prompt;return}a(d.children??[],d.selector==="choice");continue}if(d.id===t&&e==="snippet"){o=d;return}let g=(d.beats??[]).find(f=>f.id===t);if(g!==void 0){g.kind===e&&(o=g);return}}};for(let c of s)for(let l of c.blocks??[])o===void 0&&a(l.children??[],!1);return n(o)}default:return{}}}return It(rn);})();\n';
|
|
2633
2708
|
|
|
2634
2709
|
// src/play-timeline.ts
|
|
2635
2710
|
function createTimeline(defaultPad) {
|
|
@@ -2707,6 +2782,10 @@ var PLAYER_JS = String.raw`
|
|
|
2707
2782
|
// is stored under the key Patterpad's Play window uses, so the two keep one choice; the old reading paces
|
|
2708
2783
|
// carry over (slow is half speed, fast is double).
|
|
2709
2784
|
// A run token cancels an in-flight wait the moment the player restarts, loads, or picks a choice.
|
|
2785
|
+
// Continue, as in Patterpad's Play window and under its stored key: on, the story plays through at the chosen
|
|
2786
|
+
// speed; off, each beat waits for the reader (a click or tap on the page, Space or Enter, or the Next button).
|
|
2787
|
+
// A reader's own choice holds; with none, an unvoiced story starts with it off, since nothing in it is meant to
|
|
2788
|
+
// be heard at speaking pace, and a voiced one starts with it on.
|
|
2710
2789
|
var createTimeline = (${createTimeline.toString()});
|
|
2711
2790
|
var createClock = (${createClock.toString()});
|
|
2712
2791
|
var DEFAULT_PAD = ${DEFAULT_PAD_AFTER}; // the built-in pause, for a bundle from before padding
|
|
@@ -2716,17 +2795,78 @@ var PLAYER_JS = String.raw`
|
|
|
2716
2795
|
if (own(OLD_SPEED, speedKey)) speedKey = OLD_SPEED[speedKey];
|
|
2717
2796
|
if (!own(SPEED_RATE, speedKey)) speedKey = "normal";
|
|
2718
2797
|
var estimate = window.Patterplay.estimateLineDuration;
|
|
2719
|
-
var clock = null, wake = null; // the run's clock, and a hook that re-times the wait in flight
|
|
2798
|
+
var clock = null, wake = null, timeline = null; // the run's clock and timeline, and a hook that re-times the wait in flight
|
|
2799
|
+
var continueMode = (function () { try { var v = localStorage.getItem("patter.playContinue"); if (v !== null) return v !== "0"; } catch (e) {} return !!BUNDLE.voiced; })();
|
|
2800
|
+
var pendingStep = null; // with Continue off: shows the beat the reader is waiting on
|
|
2720
2801
|
// Call then() when script time "at" comes on the run's clock. A change of speed wakes the wait, which
|
|
2721
2802
|
// works out the time again; a superseded run's wait just stops.
|
|
2722
2803
|
function waitUntil(at, token, then) {
|
|
2723
2804
|
if (token !== runToken) return;
|
|
2805
|
+
if (!continueMode) { wake = null; waitForReader(token, then); return; }
|
|
2724
2806
|
var ms = clock.at(at) - Date.now();
|
|
2725
2807
|
if (ms <= 0) { wake = null; then(); return; }
|
|
2726
2808
|
var t = setTimeout(function () { waitUntil(at, token, then); }, ms);
|
|
2727
2809
|
wake = function () { clearTimeout(t); waitUntil(at, token, then); };
|
|
2728
2810
|
}
|
|
2729
2811
|
function scrollDown() { window.scrollTo(0, document.body.scrollHeight); }
|
|
2812
|
+
|
|
2813
|
+
// With Continue off, the next beat waits behind a Next button (and any click or key that means "go on").
|
|
2814
|
+
function waitForReader(token, then) {
|
|
2815
|
+
clearNext();
|
|
2816
|
+
var b = document.createElement("button");
|
|
2817
|
+
b.className = "next"; b.id = "next"; b.textContent = "Next";
|
|
2818
|
+
pendingStep = function () { if (token !== runToken) return; clearNext(); then(); };
|
|
2819
|
+
b.onclick = function (e) { e.stopPropagation(); if (pendingStep) pendingStep(); };
|
|
2820
|
+
controls.appendChild(b);
|
|
2821
|
+
scrollDown();
|
|
2822
|
+
}
|
|
2823
|
+
function clearNext() { pendingStep = null; var b = document.getElementById("next"); if (b) b.remove(); }
|
|
2824
|
+
document.addEventListener("click", function (e) {
|
|
2825
|
+
if (!pendingStep) return;
|
|
2826
|
+
var t = e.target;
|
|
2827
|
+
if (t && t.closest && t.closest("button, a, select, input, label, .bar")) return; // the bar and other controls keep their own clicks
|
|
2828
|
+
pendingStep();
|
|
2829
|
+
});
|
|
2830
|
+
document.addEventListener("keydown", function (e) {
|
|
2831
|
+
if (!pendingStep || (e.key !== " " && e.key !== "Enter")) return;
|
|
2832
|
+
var t = e.target;
|
|
2833
|
+
if (t && t.closest && t.closest("button, select, input, textarea")) return; // a focused control answers its own key
|
|
2834
|
+
e.preventDefault();
|
|
2835
|
+
pendingStep();
|
|
2836
|
+
});
|
|
2837
|
+
|
|
2838
|
+
// The Continue toggle. A page published before it existed has no such button, so it's added here.
|
|
2839
|
+
var continueBtn = document.getElementById("continue");
|
|
2840
|
+
if (!continueBtn) {
|
|
2841
|
+
var inner = document.querySelector(".bar .inner");
|
|
2842
|
+
if (inner) {
|
|
2843
|
+
continueBtn = document.createElement("button");
|
|
2844
|
+
continueBtn.id = "continue";
|
|
2845
|
+
continueBtn.textContent = "Continue";
|
|
2846
|
+
inner.insertBefore(continueBtn, document.querySelector(".bar .speedlabel") || document.getElementById("flash"));
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
function reflectContinue() {
|
|
2850
|
+
if (!continueBtn) return;
|
|
2851
|
+
continueBtn.setAttribute("aria-pressed", String(continueMode));
|
|
2852
|
+
continueBtn.title = continueMode
|
|
2853
|
+
? "Continue is on: the story plays through at the chosen speed. Click to step through it yourself."
|
|
2854
|
+
: "Continue is off: click the page, or press Space, for each line. Click to play it through instead.";
|
|
2855
|
+
}
|
|
2856
|
+
if (continueBtn) {
|
|
2857
|
+
reflectContinue();
|
|
2858
|
+
continueBtn.onclick = function () {
|
|
2859
|
+
continueMode = !continueMode;
|
|
2860
|
+
try { localStorage.setItem("patter.playContinue", continueMode ? "1" : "0"); } catch (e) {}
|
|
2861
|
+
reflectContinue();
|
|
2862
|
+
if (continueMode && pendingStep) {
|
|
2863
|
+
// Playing on from a beat the reader was waiting on: the timing starts again from now.
|
|
2864
|
+
timeline = createTimeline(DEFAULT_PAD);
|
|
2865
|
+
clock = createClock(Date.now(), SPEED_RATE[speedKey]);
|
|
2866
|
+
pendingStep();
|
|
2867
|
+
} else if (!continueMode && wake) wake(); // the wait in flight becomes a wait for the reader
|
|
2868
|
+
};
|
|
2869
|
+
}
|
|
2730
2870
|
var speedSel = document.getElementById("speed");
|
|
2731
2871
|
if (speedSel) {
|
|
2732
2872
|
speedSel.value = speedKey;
|
|
@@ -2756,11 +2896,18 @@ var PLAYER_JS = String.raw`
|
|
|
2756
2896
|
function run() {
|
|
2757
2897
|
controls.innerHTML = "";
|
|
2758
2898
|
var token = ++runToken;
|
|
2759
|
-
|
|
2899
|
+
clearNext();
|
|
2900
|
+
timeline = createTimeline(DEFAULT_PAD);
|
|
2760
2901
|
clock = createClock(Date.now(), SPEED_RATE[speedKey]);
|
|
2902
|
+
var first = true;
|
|
2761
2903
|
// A step read ahead appears when its time comes: a line or text when its pause is over, the choices
|
|
2762
2904
|
// as the last line starts, the end as the last line still playing ends.
|
|
2763
2905
|
function next(s) {
|
|
2906
|
+
// The first beat (of a run, or the reply to a choice) starts at once. With Continue off, so do the
|
|
2907
|
+
// choices and the end: the reader has nothing to wait for once the last line is up.
|
|
2908
|
+
var atOnce = first || (!continueMode && (!s || s.type === "end" || s.type === "choice"));
|
|
2909
|
+
first = false;
|
|
2910
|
+
if (atOnce && !continueMode) { show(s); return; }
|
|
2764
2911
|
var at = !s || s.type === "end" ? timeline.endAt() : s.type === "choice" ? timeline.choicesAt() : timeline.startOf();
|
|
2765
2912
|
waitUntil(at, token, function () { show(s); });
|
|
2766
2913
|
}
|
|
@@ -2842,6 +2989,10 @@ var STYLE = String.raw`
|
|
|
2842
2989
|
.bar .speedlabel { font: 0.8rem ui-sans-serif, system-ui, sans-serif; color: #9a8f78; display: flex; align-items: center; gap: 0.3rem; }
|
|
2843
2990
|
.bar #speed { font: 0.85rem ui-sans-serif, system-ui, sans-serif; background: #fff; border: 1px solid #d9cfbb; border-radius: 6px; padding: 0.3rem 0.4rem; cursor: pointer; color: #2b2620; }
|
|
2844
2991
|
.bar #speed:hover { border-color: #b8956a; }
|
|
2992
|
+
.bar #continue[aria-pressed="true"] { background: #efe4cf; border-color: #b8956a; }
|
|
2993
|
+
.next { align-self: flex-start; font: 0.9rem ui-sans-serif, system-ui, sans-serif; background: #fff; border: 1px solid #d9cfbb; border-radius: 999px; padding: 0.35rem 1rem; cursor: pointer; color: #6b5f48; animation: fade 0.3s ease-out both; }
|
|
2994
|
+
.next::after { content: " \25B8"; }
|
|
2995
|
+
.next:hover { border-color: #b8956a; background: #fffaf0; }
|
|
2845
2996
|
#flash { margin-left: auto; color: #9a8f78; font: 0.8rem ui-sans-serif, system-ui, sans-serif; opacity: 0; transition: opacity .2s; }
|
|
2846
2997
|
`;
|
|
2847
2998
|
function sourceStory(loaded) {
|
|
@@ -2874,6 +3025,7 @@ ${head}
|
|
|
2874
3025
|
<button id="restart">\u21BA Restart</button>
|
|
2875
3026
|
<button id="save">Save</button>
|
|
2876
3027
|
<button id="load">Load</button>
|
|
3028
|
+
<button id="continue" aria-pressed="false">Continue</button>
|
|
2877
3029
|
<label class="speedlabel">Speed
|
|
2878
3030
|
<select id="speed" title="How fast the scene plays: its timing at half speed, as written, at double speed, or with no waits">
|
|
2879
3031
|
<option value="half">Half</option>
|
|
@@ -3321,6 +3473,7 @@ function* sweep(loaded, options = {}, hooks = {}) {
|
|
|
3321
3473
|
seed: Math.floor(rng() * 4294967296),
|
|
3322
3474
|
onDryChoice: (groupId) => dryThisRun.add(groupId),
|
|
3323
3475
|
onError: (e) => {
|
|
3476
|
+
if (e.kind === "dry-choice") return;
|
|
3324
3477
|
const key = `${e.kind}|${e.node}|${e.message}`;
|
|
3325
3478
|
if (!errorsThisRun.has(key)) errorsThisRun.set(key, { kind: e.kind, node: e.node, ...e.source ? { source: e.source } : {}, message: e.message });
|
|
3326
3479
|
},
|
|
@@ -3788,6 +3941,7 @@ function runReport(loaded, recordingOverride) {
|
|
|
3788
3941
|
return w !== void 0 && writingIndex.has(w) ? w : stub;
|
|
3789
3942
|
};
|
|
3790
3943
|
const rungIdxOf = (id) => writingIndex.get(wsOf(id)) ?? 0;
|
|
3944
|
+
const folders = sceneFolderPaths(loaded);
|
|
3791
3945
|
for (const scene of loaded.scenes) {
|
|
3792
3946
|
const sceneCut = cutSet.has(scene.id);
|
|
3793
3947
|
const sceneAction = textIsAction2(project, scene);
|
|
@@ -3861,6 +4015,7 @@ function runReport(loaded, recordingOverride) {
|
|
|
3861
4015
|
const report = {
|
|
3862
4016
|
sceneId: scene.id,
|
|
3863
4017
|
name: scene.name,
|
|
4018
|
+
...folders.has(scene.id) ? { folder: folders.get(scene.id) } : {},
|
|
3864
4019
|
file: loaded.sceneFiles[scene.id],
|
|
3865
4020
|
status,
|
|
3866
4021
|
estimated: true,
|
|
@@ -3914,6 +4069,7 @@ function runReport(loaded, recordingOverride) {
|
|
|
3914
4069
|
reports.push({
|
|
3915
4070
|
sceneId: scene.id,
|
|
3916
4071
|
name: scene.name,
|
|
4072
|
+
...folders.has(scene.id) ? { folder: folders.get(scene.id) } : {},
|
|
3917
4073
|
file: loaded.sceneFiles[scene.id],
|
|
3918
4074
|
status,
|
|
3919
4075
|
estimated: false,
|
|
@@ -4053,7 +4209,9 @@ async function runReportXlsx(data) {
|
|
|
4053
4209
|
const rec = data.recordingTracked;
|
|
4054
4210
|
const scenes = wb.addWorksheet("Scenes");
|
|
4055
4211
|
const action = data.totals.action.count > 0;
|
|
4212
|
+
const filed = data.scenes.some((s) => s.folder);
|
|
4056
4213
|
scenes.columns = [
|
|
4214
|
+
...filed ? [{ header: "Folder", key: "folder", width: 22 }] : [],
|
|
4057
4215
|
{ header: "Scene", key: "name", width: 24 },
|
|
4058
4216
|
{ header: "Status", key: "status", width: 10 },
|
|
4059
4217
|
...v ? [{ header: "Voiced", key: "vCount", width: 8 }] : [],
|
|
@@ -4071,6 +4229,7 @@ async function runReportXlsx(data) {
|
|
|
4071
4229
|
];
|
|
4072
4230
|
const sceneRow = (name, s) => ({
|
|
4073
4231
|
name,
|
|
4232
|
+
folder: s.folder ?? "",
|
|
4074
4233
|
status: s.status,
|
|
4075
4234
|
vCount: s.voiced.count,
|
|
4076
4235
|
wCount: s.written.count,
|
|
@@ -4320,7 +4479,7 @@ function cellText(v) {
|
|
|
4320
4479
|
if ("result" in o) return cellText(o.result);
|
|
4321
4480
|
return "";
|
|
4322
4481
|
}
|
|
4323
|
-
async function catalogToXlsx(catalog) {
|
|
4482
|
+
async function catalogToXlsx(catalog, opts = {}) {
|
|
4324
4483
|
const { default: ExcelJS } = await import("exceljs");
|
|
4325
4484
|
const wb = new ExcelJS.Workbook();
|
|
4326
4485
|
wb.creator = "patter";
|
|
@@ -4339,7 +4498,9 @@ async function catalogToXlsx(catalog) {
|
|
|
4339
4498
|
// Hidden: the translator has no use for it, and it is the import's only faithful record of the scene.
|
|
4340
4499
|
{ header: "Scene", key: "scene", width: 22, hidden: true },
|
|
4341
4500
|
// After the hidden Scene column, so the columns an older reader finds by position stay where they were.
|
|
4342
|
-
{ header: "Qualifier", key: "qualifier", width: 11 }
|
|
4501
|
+
{ header: "Qualifier", key: "qualifier", width: 11 },
|
|
4502
|
+
// Scene folders: the scene's folder, last of all, when the project has any. Never read back.
|
|
4503
|
+
...opts.folders?.size ? [{ header: "Folder", key: "folder", width: 22 }] : []
|
|
4343
4504
|
];
|
|
4344
4505
|
ws.getRow(1).font = { bold: true };
|
|
4345
4506
|
ws.views = [{ state: "frozen", ySplit: 1 }];
|
|
@@ -4352,7 +4513,8 @@ async function catalogToXlsx(catalog) {
|
|
|
4352
4513
|
status: e.stale ? "stale" : e.translation ? "translated" : "",
|
|
4353
4514
|
gender: e.context?.gender ?? "",
|
|
4354
4515
|
scene,
|
|
4355
|
-
qualifier: e.context?.qualifier ?? ""
|
|
4516
|
+
qualifier: e.context?.qualifier ?? "",
|
|
4517
|
+
folder: opts.folders?.get(scene) ?? ""
|
|
4356
4518
|
});
|
|
4357
4519
|
}
|
|
4358
4520
|
}
|
|
@@ -4438,12 +4600,16 @@ function runVoiceScript(loaded, opts = {}) {
|
|
|
4438
4600
|
const voClasses = classesForChannel(project.documentationClasses ?? DEFAULT_DOCUMENTATION_CLASSES3, "vo");
|
|
4439
4601
|
const ownVo = (id) => (docsOf.get(id) ?? []).filter((d) => d.type !== void 0 && voClasses.has(d.type)).map((d) => d.text);
|
|
4440
4602
|
const lines = [];
|
|
4603
|
+
const folders = sceneFolderPaths(loaded);
|
|
4604
|
+
let sceneNow = "";
|
|
4441
4605
|
const emitLine = (beat, scope, leadingVo) => {
|
|
4442
4606
|
if (beat.kind !== "line" || cutSet.has(beat.id)) return false;
|
|
4443
4607
|
const ws = writingOf.get(beat.id) ?? stub;
|
|
4444
4608
|
if (!everything && recordThreshold !== -1 && (writingIndex.get(ws) ?? 0) < recordThreshold) return false;
|
|
4445
4609
|
const own = ownVo(beat.id);
|
|
4610
|
+
const folder = folders.get(sceneNow);
|
|
4446
4611
|
lines.push({
|
|
4612
|
+
...folder ? { folder } : {},
|
|
4447
4613
|
scope: scope.join(" \u203A "),
|
|
4448
4614
|
id: beat.id,
|
|
4449
4615
|
character: beat.character ?? "",
|
|
@@ -4475,6 +4641,7 @@ function runVoiceScript(loaded, opts = {}) {
|
|
|
4475
4641
|
};
|
|
4476
4642
|
for (const scene of loaded.scenes) {
|
|
4477
4643
|
if (cutSet.has(scene.id)) continue;
|
|
4644
|
+
sceneNow = scene.id;
|
|
4478
4645
|
const sceneVo = ownVo(scene.id);
|
|
4479
4646
|
for (const block of scene.blocks) {
|
|
4480
4647
|
if (cutSet.has(block.id)) continue;
|
|
@@ -4492,7 +4659,9 @@ async function voiceScriptToXlsx(data) {
|
|
|
4492
4659
|
const wb = new ExcelJS.Workbook();
|
|
4493
4660
|
wb.creator = "patter";
|
|
4494
4661
|
const ws = wb.addWorksheet("Voice Script");
|
|
4662
|
+
const filed = data.lines.some((l) => l.folder);
|
|
4495
4663
|
ws.columns = [
|
|
4664
|
+
...filed ? [{ header: "Folder", key: "folder", width: 22 }] : [],
|
|
4496
4665
|
{ header: "Scope", key: "scope", width: 28 },
|
|
4497
4666
|
{ header: "Line ID", key: "id", width: 18 },
|
|
4498
4667
|
{ header: "Character", key: "character", width: 16 },
|
|
@@ -4507,6 +4676,7 @@ async function voiceScriptToXlsx(data) {
|
|
|
4507
4676
|
ws.views = [{ state: "frozen", ySplit: 1 }];
|
|
4508
4677
|
for (const l of data.lines) {
|
|
4509
4678
|
const row = ws.addRow({
|
|
4679
|
+
folder: l.folder ?? "",
|
|
4510
4680
|
scope: l.scope,
|
|
4511
4681
|
id: l.id,
|
|
4512
4682
|
character: l.character,
|
|
@@ -7155,6 +7325,7 @@ export {
|
|
|
7155
7325
|
actionTextIds,
|
|
7156
7326
|
applyLoc,
|
|
7157
7327
|
applyResolutions,
|
|
7328
|
+
applySceneNavOrder,
|
|
7158
7329
|
applySceneOrder,
|
|
7159
7330
|
applySuggestionDecisions,
|
|
7160
7331
|
applyWrites,
|
|
@@ -7278,6 +7449,7 @@ export {
|
|
|
7278
7449
|
runVoiceScript,
|
|
7279
7450
|
scanAudio,
|
|
7280
7451
|
scanAudioStatus,
|
|
7452
|
+
sceneFolderPaths,
|
|
7281
7453
|
sceneIdForShard,
|
|
7282
7454
|
sceneTextIds,
|
|
7283
7455
|
scriptFontLicencePath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patterkit/ops",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
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",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"@fontsource/noto-sans": "^5.3.0",
|
|
34
34
|
"@fontsource/noto-sans-math": "^5.3.0",
|
|
35
35
|
"@ibm/plex-mono": "1.0.0",
|
|
36
|
-
"@patterkit/compiler": "0.15.
|
|
37
|
-
"@patterkit/core": "0.
|
|
38
|
-
"@patterkit/dialect": "0.2.
|
|
39
|
-
"@patterkit/model": "0.
|
|
40
|
-
"@patterkit/runtime": "0.
|
|
36
|
+
"@patterkit/compiler": "0.15.2",
|
|
37
|
+
"@patterkit/core": "0.11.0",
|
|
38
|
+
"@patterkit/dialect": "0.2.20",
|
|
39
|
+
"@patterkit/model": "0.23.0",
|
|
40
|
+
"@patterkit/runtime": "0.38.0",
|
|
41
41
|
"@wildwinter/expr": "^0.6.0",
|
|
42
42
|
"@wildwinter/toolkit": "^0.4.0",
|
|
43
43
|
"@xmldom/xmldom": "^0.8.15",
|