@patterkit/runtime 0.23.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +4 -0
- package/dist/index.cjs +83 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -2
- package/dist/index.d.ts +26 -2
- package/dist/index.js +84 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ScalarValue } from '@wildwinter/expr';
|
|
2
2
|
import { ScopeRegistry, PropertyBag, ScopeDeclaration, LogMount, ScopeResolver, PropertyRow } from '@wildwinter/scoperegistry';
|
|
3
3
|
export { PropertyRow } from '@wildwinter/scoperegistry';
|
|
4
|
-
import { GameData, Bundle, CompiledGroup, CompiledSnippet, CompiledBlock, FlowSnapshot, EngineSave, SaveGame, SaveGameV2, PropertyType, ScalarValue as ScalarValue$1, GameDataNodeKind, GameDataField } from '@patterkit/model';
|
|
5
|
-
export { Bundle, DEFAULT_PAD_AFTER, EngineSave, FlowCursor, FlowSnapshot, SaveEnvelope, SaveGame, SavedChoice, SavedChoiceOption, SavedChoicePrompt, SelectorSnapshot, StackFrame } from '@patterkit/model';
|
|
4
|
+
import { GameData, Bundle, CompiledGroup, CompiledSnippet, CompiledBlock, FlowSnapshot, FlowState, EngineSave, SaveGame, SaveGameV2, PropertyType, ScalarValue as ScalarValue$1, GameDataNodeKind, GameDataField } from '@patterkit/model';
|
|
5
|
+
export { Bundle, DEFAULT_PAD_AFTER, EngineSave, FlowCursor, FlowSnapshot, FlowState, FlowValues, SaveEnvelope, SaveGame, SavedChoice, SavedChoiceOption, SavedChoicePrompt, SelectorSnapshot, StackFrame } from '@patterkit/model';
|
|
6
6
|
|
|
7
7
|
/** A beat's own `padAfter` (when it sets one) and its resolved one (always). */
|
|
8
8
|
interface BeatPad {
|
|
@@ -22,6 +22,7 @@ type StepResult = {
|
|
|
22
22
|
direction?: string;
|
|
23
23
|
qualifier?: string;
|
|
24
24
|
qualifierName?: string;
|
|
25
|
+
speakerLabel?: string;
|
|
25
26
|
padAfter: number;
|
|
26
27
|
gameData?: GameData;
|
|
27
28
|
tags?: string[];
|
|
@@ -58,6 +59,8 @@ interface BeatInfo {
|
|
|
58
59
|
qualifier?: string;
|
|
59
60
|
/** The qualifier's shown name (source locale), e.g. `V.O.`. */
|
|
60
61
|
qualifierName?: string;
|
|
62
|
+
/** The speaker's label (source locale): `You (V.O.)`, a pair's own label, or with no qualifier the name. */
|
|
63
|
+
speakerLabel?: string;
|
|
61
64
|
/** The pause after this beat, in seconds, resolved from its own value and the defaults above it (line and
|
|
62
65
|
* text only; see line padding). What a delivered step carries. */
|
|
63
66
|
padAfter?: number;
|
|
@@ -133,6 +136,8 @@ interface ChoicePrompt {
|
|
|
133
136
|
/** The speaker qualifier's `gameId` (`vo`), and its resolved shown name (locale-aware). */
|
|
134
137
|
qualifier?: string;
|
|
135
138
|
qualifierName?: string;
|
|
139
|
+
/** The name to show for the speaker (locale-aware): see {@link speakerLabelOf}. */
|
|
140
|
+
speakerLabel?: string;
|
|
136
141
|
/** The prompt's resolved pause, in seconds: when it is spoken (by `replayPromptOnChoose`, or by a game that
|
|
137
142
|
* voices it), the wait before the reply, negative to cut in on the question (line padding). */
|
|
138
143
|
padAfter: number;
|
|
@@ -379,6 +384,11 @@ interface FlowHost {
|
|
|
379
384
|
castDisplay: Map<string, string>;
|
|
380
385
|
/** Speaker qualifier gameId -> authored name (the unlocalised fallback when no loc string exists). */
|
|
381
386
|
qualifierDisplay: Map<string, string>;
|
|
387
|
+
/** `<name>\0<gameId>` -> a character's own label with that qualifier, for the overridden pairs only (the
|
|
388
|
+
* unlocalised fallback). A pair not here follows the pattern, whatever a loc file holds for it. */
|
|
389
|
+
speakerLabels: Map<string, string>;
|
|
390
|
+
/** The project's speaker pattern (the unlocalised fallback for the `speaker:pattern` string). */
|
|
391
|
+
speakerPattern: string;
|
|
382
392
|
nodeIndex: Map<string, SelectableNode>;
|
|
383
393
|
blockIndex: Map<string, {
|
|
384
394
|
sceneId: string;
|
|
@@ -686,6 +696,20 @@ declare class Engine {
|
|
|
686
696
|
/** Let go of what a closed checkpoint recorded, so a handle the game keeps holds nothing. */
|
|
687
697
|
private static forget;
|
|
688
698
|
private refuseInCheckpoint;
|
|
699
|
+
/**
|
|
700
|
+
* Take flow `id` out of the engine as plain data, to be put back later with {@link restoreFlow}: its snapshot as
|
|
701
|
+
* `saveGame` writes it, and its own property values without its id (so it can come back under another). Shared
|
|
702
|
+
* state is never in it. Doesn't close the flow; close it with {@link closeFlow} AFTER capturing, since closing
|
|
703
|
+
* drops its values. Only reads, so it is allowed while a checkpoint is open.
|
|
704
|
+
*/
|
|
705
|
+
captureFlow(id: string): FlowState;
|
|
706
|
+
/**
|
|
707
|
+
* Open flow `id` from a {@link captureFlow} state, the per-flow half of `loadGame`: its values go into the
|
|
708
|
+
* registry as a load's do, and the flow is restored as a load restores each flow. `id` needn't be the id it was
|
|
709
|
+
* captured from. Against an edited bundle it re-finds its place by id, as a load does. Refused if a flow of
|
|
710
|
+
* that id is open, or while a checkpoint is.
|
|
711
|
+
*/
|
|
712
|
+
restoreFlow(id: string, state: FlowState): Flow;
|
|
689
713
|
/** Close (remove) a flow. The flow object is FINISHED, not merely unregistered, so a host still
|
|
690
714
|
* holding it cannot keep advancing it into the shared world (see {@link Flow.close}). */
|
|
691
715
|
closeFlow(id: string): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ScalarValue } from '@wildwinter/expr';
|
|
2
2
|
import { ScopeRegistry, PropertyBag, ScopeDeclaration, LogMount, ScopeResolver, PropertyRow } from '@wildwinter/scoperegistry';
|
|
3
3
|
export { PropertyRow } from '@wildwinter/scoperegistry';
|
|
4
|
-
import { GameData, Bundle, CompiledGroup, CompiledSnippet, CompiledBlock, FlowSnapshot, EngineSave, SaveGame, SaveGameV2, PropertyType, ScalarValue as ScalarValue$1, GameDataNodeKind, GameDataField } from '@patterkit/model';
|
|
5
|
-
export { Bundle, DEFAULT_PAD_AFTER, EngineSave, FlowCursor, FlowSnapshot, SaveEnvelope, SaveGame, SavedChoice, SavedChoiceOption, SavedChoicePrompt, SelectorSnapshot, StackFrame } from '@patterkit/model';
|
|
4
|
+
import { GameData, Bundle, CompiledGroup, CompiledSnippet, CompiledBlock, FlowSnapshot, FlowState, EngineSave, SaveGame, SaveGameV2, PropertyType, ScalarValue as ScalarValue$1, GameDataNodeKind, GameDataField } from '@patterkit/model';
|
|
5
|
+
export { Bundle, DEFAULT_PAD_AFTER, EngineSave, FlowCursor, FlowSnapshot, FlowState, FlowValues, SaveEnvelope, SaveGame, SavedChoice, SavedChoiceOption, SavedChoicePrompt, SelectorSnapshot, StackFrame } from '@patterkit/model';
|
|
6
6
|
|
|
7
7
|
/** A beat's own `padAfter` (when it sets one) and its resolved one (always). */
|
|
8
8
|
interface BeatPad {
|
|
@@ -22,6 +22,7 @@ type StepResult = {
|
|
|
22
22
|
direction?: string;
|
|
23
23
|
qualifier?: string;
|
|
24
24
|
qualifierName?: string;
|
|
25
|
+
speakerLabel?: string;
|
|
25
26
|
padAfter: number;
|
|
26
27
|
gameData?: GameData;
|
|
27
28
|
tags?: string[];
|
|
@@ -58,6 +59,8 @@ interface BeatInfo {
|
|
|
58
59
|
qualifier?: string;
|
|
59
60
|
/** The qualifier's shown name (source locale), e.g. `V.O.`. */
|
|
60
61
|
qualifierName?: string;
|
|
62
|
+
/** The speaker's label (source locale): `You (V.O.)`, a pair's own label, or with no qualifier the name. */
|
|
63
|
+
speakerLabel?: string;
|
|
61
64
|
/** The pause after this beat, in seconds, resolved from its own value and the defaults above it (line and
|
|
62
65
|
* text only; see line padding). What a delivered step carries. */
|
|
63
66
|
padAfter?: number;
|
|
@@ -133,6 +136,8 @@ interface ChoicePrompt {
|
|
|
133
136
|
/** The speaker qualifier's `gameId` (`vo`), and its resolved shown name (locale-aware). */
|
|
134
137
|
qualifier?: string;
|
|
135
138
|
qualifierName?: string;
|
|
139
|
+
/** The name to show for the speaker (locale-aware): see {@link speakerLabelOf}. */
|
|
140
|
+
speakerLabel?: string;
|
|
136
141
|
/** The prompt's resolved pause, in seconds: when it is spoken (by `replayPromptOnChoose`, or by a game that
|
|
137
142
|
* voices it), the wait before the reply, negative to cut in on the question (line padding). */
|
|
138
143
|
padAfter: number;
|
|
@@ -379,6 +384,11 @@ interface FlowHost {
|
|
|
379
384
|
castDisplay: Map<string, string>;
|
|
380
385
|
/** Speaker qualifier gameId -> authored name (the unlocalised fallback when no loc string exists). */
|
|
381
386
|
qualifierDisplay: Map<string, string>;
|
|
387
|
+
/** `<name>\0<gameId>` -> a character's own label with that qualifier, for the overridden pairs only (the
|
|
388
|
+
* unlocalised fallback). A pair not here follows the pattern, whatever a loc file holds for it. */
|
|
389
|
+
speakerLabels: Map<string, string>;
|
|
390
|
+
/** The project's speaker pattern (the unlocalised fallback for the `speaker:pattern` string). */
|
|
391
|
+
speakerPattern: string;
|
|
382
392
|
nodeIndex: Map<string, SelectableNode>;
|
|
383
393
|
blockIndex: Map<string, {
|
|
384
394
|
sceneId: string;
|
|
@@ -686,6 +696,20 @@ declare class Engine {
|
|
|
686
696
|
/** Let go of what a closed checkpoint recorded, so a handle the game keeps holds nothing. */
|
|
687
697
|
private static forget;
|
|
688
698
|
private refuseInCheckpoint;
|
|
699
|
+
/**
|
|
700
|
+
* Take flow `id` out of the engine as plain data, to be put back later with {@link restoreFlow}: its snapshot as
|
|
701
|
+
* `saveGame` writes it, and its own property values without its id (so it can come back under another). Shared
|
|
702
|
+
* state is never in it. Doesn't close the flow; close it with {@link closeFlow} AFTER capturing, since closing
|
|
703
|
+
* drops its values. Only reads, so it is allowed while a checkpoint is open.
|
|
704
|
+
*/
|
|
705
|
+
captureFlow(id: string): FlowState;
|
|
706
|
+
/**
|
|
707
|
+
* Open flow `id` from a {@link captureFlow} state, the per-flow half of `loadGame`: its values go into the
|
|
708
|
+
* registry as a load's do, and the flow is restored as a load restores each flow. `id` needn't be the id it was
|
|
709
|
+
* captured from. Against an edited bundle it re-finds its place by id, as a load does. Refused if a flow of
|
|
710
|
+
* that id is open, or while a checkpoint is.
|
|
711
|
+
*/
|
|
712
|
+
restoreFlow(id: string, state: FlowState): Flow;
|
|
689
713
|
/** Close (remove) a flow. The flow object is FINISHED, not merely unregistered, so a host still
|
|
690
714
|
* holding it cannot keep advancing it into the shared world (see {@link Flow.close}). */
|
|
691
715
|
closeFlow(id: string): void;
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { matchedSpecificity as scoreSpecificity } from "@wildwinter/expr-specifi
|
|
|
4
4
|
import { ScopeRegistry } from "@wildwinter/scoperegistry";
|
|
5
5
|
import { defaultFor, PropertyBag } from "@wildwinter/scoperegistry";
|
|
6
6
|
import { patterDialect, interpolate, splitRef, stripCaptions } from "@patterkit/dialect";
|
|
7
|
-
import { walkNodes, effectiveGameId, castStringKey, qualifierStringKey, DEFAULT_PAD_AFTER as DEFAULT_PAD_AFTER2, DEFAULT_CAPTION_DELIMITERS, DEFAULT_CAPTION_CHARACTER } from "@patterkit/model";
|
|
7
|
+
import { walkNodes, effectiveGameId, castStringKey, qualifierStringKey, speakerLabelKey, formatSpeakerLabel, SPEAKER_PATTERN_KEY, DEFAULT_SPEAKER_PATTERN, DEFAULT_PAD_AFTER as DEFAULT_PAD_AFTER2, DEFAULT_CAPTION_DELIMITERS, DEFAULT_CAPTION_CHARACTER } from "@patterkit/model";
|
|
8
8
|
|
|
9
9
|
// src/tags.ts
|
|
10
10
|
function dedupe(tags) {
|
|
@@ -105,6 +105,7 @@ function tagsOf(host, id) {
|
|
|
105
105
|
}
|
|
106
106
|
var OWNER = "Patter";
|
|
107
107
|
var esc = (id) => id.replace(/%/g, "%25").replace(/\//g, "%2F");
|
|
108
|
+
var unesc = (key) => key.replace(/%2F/g, "/").replace(/%25/g, "%");
|
|
108
109
|
var OWNS_REGISTRY = /* @__PURE__ */ Symbol("ownsRegistry");
|
|
109
110
|
var keys = {
|
|
110
111
|
/** A scene's SHARED `@scene` props (one bag per scene, every flow's). */
|
|
@@ -167,6 +168,9 @@ var Engine = class _Engine {
|
|
|
167
168
|
for (const c of bundle.cast ?? []) if (c.displayName) castDisplay.set(c.name, c.displayName);
|
|
168
169
|
const qualifierDisplay = /* @__PURE__ */ new Map();
|
|
169
170
|
for (const q of bundle.qualifiers ?? []) if (q?.gameId) qualifierDisplay.set(q.gameId, q.name);
|
|
171
|
+
const speakerLabels = /* @__PURE__ */ new Map();
|
|
172
|
+
for (const c of bundle.cast ?? []) for (const [q, label] of Object.entries(c.qualifierLabels ?? {})) if (typeof label === "string") speakerLabels.set(`${c.name}\0${q}`, label);
|
|
173
|
+
const speakerPattern = bundle.speakerPattern ?? DEFAULT_SPEAKER_PATTERN;
|
|
170
174
|
this.defaultSeed = (options.seed ?? 2654435769) >>> 0;
|
|
171
175
|
const nodeIndex = /* @__PURE__ */ new Map();
|
|
172
176
|
const blockIndex = /* @__PURE__ */ new Map();
|
|
@@ -228,6 +232,8 @@ var Engine = class _Engine {
|
|
|
228
232
|
defaultStrings,
|
|
229
233
|
castDisplay,
|
|
230
234
|
qualifierDisplay,
|
|
235
|
+
speakerLabels,
|
|
236
|
+
speakerPattern,
|
|
231
237
|
nodeIndex,
|
|
232
238
|
blockIndex,
|
|
233
239
|
blockById,
|
|
@@ -638,6 +644,8 @@ var Engine = class _Engine {
|
|
|
638
644
|
const name = this.host.defaultStrings[qualifierStringKey(beat.qualifier)] ?? this.host.qualifierDisplay.get(beat.qualifier);
|
|
639
645
|
if (name !== void 0) info.qualifierName = name;
|
|
640
646
|
}
|
|
647
|
+
const label = speakerLabelOf(this.host, beat.character, beat.qualifier, true);
|
|
648
|
+
if (label !== void 0) info.speakerLabel = label;
|
|
641
649
|
}
|
|
642
650
|
if (beat.kind === "line" || beat.kind === "text") {
|
|
643
651
|
const pad = this.host.padIndex.get(beat.id);
|
|
@@ -733,6 +741,45 @@ var Engine = class _Engine {
|
|
|
733
741
|
refuseInCheckpoint(what) {
|
|
734
742
|
if (this.host.journal) throw new Error(`${what} can't be undone, so it isn't allowed while a checkpoint is open`);
|
|
735
743
|
}
|
|
744
|
+
/**
|
|
745
|
+
* Take flow `id` out of the engine as plain data, to be put back later with {@link restoreFlow}: its snapshot as
|
|
746
|
+
* `saveGame` writes it, and its own property values without its id (so it can come back under another). Shared
|
|
747
|
+
* state is never in it. Doesn't close the flow; close it with {@link closeFlow} AFTER capturing, since closing
|
|
748
|
+
* drops its values. Only reads, so it is allowed while a checkpoint is open.
|
|
749
|
+
*/
|
|
750
|
+
captureFlow(id) {
|
|
751
|
+
const flow = this.flowsById.get(id);
|
|
752
|
+
if (!flow) throw new Error(`no open flow '${id}' to capture`);
|
|
753
|
+
return { version: SAVE_VERSION, ...flow.snapshot(), ...flowValuesFrom(id, this.host.registry.save()) };
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Open flow `id` from a {@link captureFlow} state, the per-flow half of `loadGame`: its values go into the
|
|
757
|
+
* registry as a load's do, and the flow is restored as a load restores each flow. `id` needn't be the id it was
|
|
758
|
+
* captured from. Against an edited bundle it re-finds its place by id, as a load does. Refused if a flow of
|
|
759
|
+
* that id is open, or while a checkpoint is.
|
|
760
|
+
*/
|
|
761
|
+
restoreFlow(id, state) {
|
|
762
|
+
this.refuseInCheckpoint("restoreFlow");
|
|
763
|
+
const version = state.version;
|
|
764
|
+
if (version !== 2 && version !== SAVE_VERSION) throw new Error(`unsupported flow state version: ${String(version)}`);
|
|
765
|
+
const cursor = state.cursor;
|
|
766
|
+
if (typeof cursor !== "object" || cursor === null) throw new Error("malformed flow state: no cursor");
|
|
767
|
+
if (this.flowsById.has(id)) throw new Error(`a flow '${id}' is already open; close it before restoring another under that id`);
|
|
768
|
+
this.assertExternalScopes();
|
|
769
|
+
const parked = keys.flow(id);
|
|
770
|
+
this.host.registry.discardParked(parked);
|
|
771
|
+
this.host.registry.load(flowSections(id, state), { keepParked: true });
|
|
772
|
+
const flow = new Flow(id, this.host, this.defaultSeed);
|
|
773
|
+
try {
|
|
774
|
+
flow.restore(state);
|
|
775
|
+
} catch (e) {
|
|
776
|
+
flow.close();
|
|
777
|
+
this.host.registry.discardParked(parked);
|
|
778
|
+
throw e;
|
|
779
|
+
}
|
|
780
|
+
this.flowsById.set(id, flow);
|
|
781
|
+
return flow;
|
|
782
|
+
}
|
|
736
783
|
/** Close (remove) a flow. The flow object is FINISHED, not merely unregistered, so a host still
|
|
737
784
|
* holding it cannot keep advancing it into the shared world (see {@link Flow.close}). */
|
|
738
785
|
closeFlow(id) {
|
|
@@ -1437,7 +1484,8 @@ var Flow = class {
|
|
|
1437
1484
|
if (!node) continue;
|
|
1438
1485
|
byId.set(o.id, node);
|
|
1439
1486
|
const { prompt: saved, ...rest } = o;
|
|
1440
|
-
|
|
1487
|
+
const label = saved?.kind === "line" && saved.speakerLabel === void 0 ? speakerLabelOf(this.host, saved.character, saved.qualifier) : void 0;
|
|
1488
|
+
options.push({ ...rest, ...saved ? { prompt: { ...saved, padAfter: saved.padAfter ?? this.promptPadOf(node), ...label !== void 0 ? { speakerLabel: label } : {} } } : {} });
|
|
1441
1489
|
}
|
|
1442
1490
|
if (options.length > 0) this.pendingChoice = { groupId: c.pendingChoice.groupId, options, byId };
|
|
1443
1491
|
}
|
|
@@ -1446,7 +1494,9 @@ var Flow = class {
|
|
|
1446
1494
|
if (this.pendingPromptOwnerId) {
|
|
1447
1495
|
const owner = this.host.nodeIndex.get(this.pendingPromptOwnerId);
|
|
1448
1496
|
const beat = owner ? this.authoredPromptOf(owner) : void 0;
|
|
1449
|
-
|
|
1497
|
+
const saved = c.pendingPrompt;
|
|
1498
|
+
const label = saved?.kind === "line" && saved.speakerLabel === void 0 ? speakerLabelOf(this.host, saved.character, saved.qualifier) : void 0;
|
|
1499
|
+
if (beat) this.pendingPrompt = { beat, ...saved ? { shown: { ...saved, padAfter: saved.padAfter ?? this.padOf(beat.id), ...label !== void 0 ? { speakerLabel: label } : {} } } : {} };
|
|
1450
1500
|
else this.pendingPromptOwnerId = null;
|
|
1451
1501
|
}
|
|
1452
1502
|
}
|
|
@@ -1832,6 +1882,7 @@ var Flow = class {
|
|
|
1832
1882
|
direction: silent ? void 0 : beat.direction,
|
|
1833
1883
|
qualifier: silent ? void 0 : beat.qualifier,
|
|
1834
1884
|
qualifierName: silent ? void 0 : this.resolveQualifierName(beat.qualifier),
|
|
1885
|
+
speakerLabel: silent ? void 0 : speakerLabelOf(this.host, beat.character, beat.qualifier),
|
|
1835
1886
|
padAfter: this.padOf(beat.id),
|
|
1836
1887
|
// a silent line still fires, so it still carries its pause
|
|
1837
1888
|
gameData: beat.gameData,
|
|
@@ -1881,6 +1932,7 @@ var Flow = class {
|
|
|
1881
1932
|
direction: beat.direction,
|
|
1882
1933
|
qualifier: beat.qualifier,
|
|
1883
1934
|
qualifierName: this.resolveQualifierName(beat.qualifier),
|
|
1935
|
+
speakerLabel: speakerLabelOf(this.host, beat.character, beat.qualifier),
|
|
1884
1936
|
padAfter
|
|
1885
1937
|
} : { kind: "text", text, padAfter };
|
|
1886
1938
|
}
|
|
@@ -1903,6 +1955,7 @@ var Flow = class {
|
|
|
1903
1955
|
direction: shown.direction,
|
|
1904
1956
|
qualifier: shown.qualifier,
|
|
1905
1957
|
qualifierName: shown.qualifierName,
|
|
1958
|
+
speakerLabel: shown.speakerLabel,
|
|
1906
1959
|
padAfter: this.padOf(beat.id),
|
|
1907
1960
|
gameData: beat.gameData,
|
|
1908
1961
|
...withTags
|
|
@@ -2048,6 +2101,16 @@ function collectCast(nodes, out) {
|
|
|
2048
2101
|
for (const beat of n.beats ?? []) if (beat.kind === "line" && beat.character) out.add(beat.character);
|
|
2049
2102
|
});
|
|
2050
2103
|
}
|
|
2104
|
+
function speakerLabelOf(host, character, qualifier, source = false) {
|
|
2105
|
+
if (!character || host.emitIds) return void 0;
|
|
2106
|
+
const lookup = (key) => (source ? void 0 : host.strings[key]) ?? host.defaultStrings[key];
|
|
2107
|
+
const name = lookup(castStringKey(character)) ?? host.castDisplay.get(character) ?? character;
|
|
2108
|
+
if (qualifier === void 0) return name;
|
|
2109
|
+
const own = host.speakerLabels.get(`${character}\0${qualifier}`);
|
|
2110
|
+
if (own !== void 0) return lookup(speakerLabelKey(character, qualifier)) ?? own;
|
|
2111
|
+
const shown = lookup(qualifierStringKey(qualifier)) ?? host.qualifierDisplay.get(qualifier) ?? qualifier;
|
|
2112
|
+
return formatSpeakerLabel(lookup(SPEAKER_PATTERN_KEY) ?? host.speakerPattern, name, shown);
|
|
2113
|
+
}
|
|
2051
2114
|
function gameDataField(gameData) {
|
|
2052
2115
|
return gameData && Object.keys(gameData).length ? { gameData } : {};
|
|
2053
2116
|
}
|
|
@@ -2058,6 +2121,7 @@ function savedPrompt(p) {
|
|
|
2058
2121
|
if (p.direction !== void 0) out.direction = p.direction;
|
|
2059
2122
|
if (p.qualifier !== void 0) out.qualifier = p.qualifier;
|
|
2060
2123
|
if (p.qualifierName !== void 0) out.qualifierName = p.qualifierName;
|
|
2124
|
+
if (p.speakerLabel !== void 0) out.speakerLabel = p.speakerLabel;
|
|
2061
2125
|
if (typeof p.padAfter === "number") out.padAfter = p.padAfter;
|
|
2062
2126
|
return out;
|
|
2063
2127
|
}
|
|
@@ -2136,9 +2200,23 @@ function sectionsFromV2(save) {
|
|
|
2136
2200
|
const out = {};
|
|
2137
2201
|
if (save.shared?.patter) out["patter"] = save.shared.patter;
|
|
2138
2202
|
for (const [s, v] of Object.entries(save.stageBags ?? {})) out[keys.stage(s)] = v;
|
|
2139
|
-
for (const [id, f] of Object.entries(save.flows ?? {}))
|
|
2140
|
-
|
|
2141
|
-
|
|
2203
|
+
for (const [id, f] of Object.entries(save.flows ?? {})) Object.assign(out, flowSections(id, f));
|
|
2204
|
+
return out;
|
|
2205
|
+
}
|
|
2206
|
+
function flowSections(id, f) {
|
|
2207
|
+
const out = {};
|
|
2208
|
+
if (f.scopes?.patter) out[keys.flowGlobals(id)] = f.scopes.patter;
|
|
2209
|
+
for (const [s, v] of Object.entries(f.sceneBags ?? {})) out[keys.flowScene(id, s)] = v;
|
|
2210
|
+
return out;
|
|
2211
|
+
}
|
|
2212
|
+
function flowValuesFrom(id, saved) {
|
|
2213
|
+
const out = { scopes: {}, sceneBags: {} };
|
|
2214
|
+
const prefix = keys.flow(id);
|
|
2215
|
+
for (const [key, values] of Object.entries(saved)) {
|
|
2216
|
+
if (!key.startsWith(prefix)) continue;
|
|
2217
|
+
const rest = key.slice(prefix.length);
|
|
2218
|
+
if (rest === "patter") out.scopes.patter = values;
|
|
2219
|
+
else if (rest.startsWith("scene/")) out.sceneBags[unesc(rest.slice("scene/".length))] = values;
|
|
2142
2220
|
}
|
|
2143
2221
|
return out;
|
|
2144
2222
|
}
|