@novely/core 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/dist/index.d.ts +341 -199
- package/dist/index.global.js +258 -134
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +258 -134
- package/dist/index.js.map +1 -1
- package/package.json +62 -62
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/constants.ts
|
|
2
|
-
var SKIPPED_DURING_RESTORE = /* @__PURE__ */ new Set(["dialog", "choice", "input", "vibrate", "text"]);
|
|
2
|
+
var SKIPPED_DURING_RESTORE = /* @__PURE__ */ new Set(["dialog", "say", "choice", "input", "vibrate", "text"]);
|
|
3
3
|
var BLOCK_EXIT_STATEMENTS = /* @__PURE__ */ new Set(["choice:exit", "condition:exit", "block:exit"]);
|
|
4
4
|
var BLOCK_STATEMENTS = /* @__PURE__ */ new Set(["choice", "condition", "block"]);
|
|
5
5
|
var AUDIO_ACTIONS = /* @__PURE__ */ new Set([
|
|
@@ -92,8 +92,8 @@ var isCSSImage = (str2) => {
|
|
|
92
92
|
return startsWith("http") || startsWith("/") || startsWith(".") || startsWith("data");
|
|
93
93
|
};
|
|
94
94
|
var str = String;
|
|
95
|
-
var isUserRequiredAction = (action, meta) => {
|
|
96
|
-
return action === "custom" && meta[0] && meta[0].requireUserAction;
|
|
95
|
+
var isUserRequiredAction = ([action, ...meta]) => {
|
|
96
|
+
return Boolean(action === "custom" && meta[0] && meta[0].requireUserAction);
|
|
97
97
|
};
|
|
98
98
|
var getLanguage = (languages) => {
|
|
99
99
|
let { language } = navigator;
|
|
@@ -214,11 +214,13 @@ var getOppositeAction = (action) => {
|
|
|
214
214
|
};
|
|
215
215
|
return MAP[action];
|
|
216
216
|
};
|
|
217
|
-
var getActionsFromPath = (story, path,
|
|
217
|
+
var getActionsFromPath = (story, path, filter) => {
|
|
218
218
|
let current = story;
|
|
219
219
|
let precurrent;
|
|
220
220
|
let ignoreNested = false;
|
|
221
221
|
let index = 0;
|
|
222
|
+
let skipPreserve = void 0;
|
|
223
|
+
const skip = /* @__PURE__ */ new Set();
|
|
222
224
|
const max = path.reduce((acc, [type, val]) => {
|
|
223
225
|
if (isNull(type) && isNumber(val)) {
|
|
224
226
|
return acc + 1;
|
|
@@ -247,21 +249,19 @@ var getActionsFromPath = (story, path, raw = false) => {
|
|
|
247
249
|
const item = current[i];
|
|
248
250
|
if (!isAction(item))
|
|
249
251
|
continue;
|
|
250
|
-
const [action
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
push();
|
|
256
|
-
continue;
|
|
252
|
+
const [action] = item;
|
|
253
|
+
const last = index === max && i === val;
|
|
254
|
+
const shouldSkip = isSkippedDuringRestore(action) || isUserRequiredAction(item);
|
|
255
|
+
if (shouldSkip) {
|
|
256
|
+
skip.add(item);
|
|
257
257
|
}
|
|
258
|
-
if (
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
258
|
+
if (shouldSkip && last) {
|
|
259
|
+
skipPreserve = item;
|
|
260
|
+
}
|
|
261
|
+
if (filter && shouldSkip && !last) {
|
|
262
262
|
continue;
|
|
263
263
|
} else {
|
|
264
|
-
push();
|
|
264
|
+
queue.push(item);
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
267
|
}
|
|
@@ -280,9 +280,13 @@ var getActionsFromPath = (story, path, raw = false) => {
|
|
|
280
280
|
ignoreNested = true;
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
|
-
return
|
|
283
|
+
return {
|
|
284
|
+
queue,
|
|
285
|
+
skip,
|
|
286
|
+
skipPreserve
|
|
287
|
+
};
|
|
284
288
|
};
|
|
285
|
-
var createQueueProcessor = (queue) => {
|
|
289
|
+
var createQueueProcessor = (queue, options) => {
|
|
286
290
|
const processedQueue = [];
|
|
287
291
|
const keep = /* @__PURE__ */ new Set();
|
|
288
292
|
const characters = /* @__PURE__ */ new Set();
|
|
@@ -291,70 +295,83 @@ var createQueueProcessor = (queue) => {
|
|
|
291
295
|
sound: /* @__PURE__ */ new Set()
|
|
292
296
|
};
|
|
293
297
|
const next = (i) => queue.slice(i + 1);
|
|
294
|
-
for (const [i,
|
|
298
|
+
for (const [i, item] of queue.entries()) {
|
|
299
|
+
const [action, ...params] = item;
|
|
295
300
|
keep.add(action);
|
|
301
|
+
if (options.skip.has(item) && item !== options.skipPreserve) {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
296
304
|
if (action === "function" || action === "custom") {
|
|
297
|
-
if (action === "custom" &&
|
|
298
|
-
const notLatest = next(i).some(([,
|
|
299
|
-
if (!
|
|
305
|
+
if (action === "custom" && params[0].callOnlyLatest) {
|
|
306
|
+
const notLatest = next(i).some(([, func]) => {
|
|
307
|
+
if (!isFunction(func))
|
|
300
308
|
return false;
|
|
301
|
-
const c0 =
|
|
302
|
-
const c1 =
|
|
303
|
-
const isIdenticalID = c0.id && c1.id && c0.id === c1.id;
|
|
309
|
+
const c0 = func;
|
|
310
|
+
const c1 = params[0];
|
|
311
|
+
const isIdenticalID = Boolean(c0.id && c1.id && c0.id === c1.id);
|
|
304
312
|
const isIdenticalByReference = c0 === c1;
|
|
305
313
|
return isIdenticalID || isIdenticalByReference || str(c0) === str(c1);
|
|
306
314
|
});
|
|
307
315
|
if (notLatest)
|
|
308
316
|
continue;
|
|
309
317
|
}
|
|
310
|
-
processedQueue.push(
|
|
318
|
+
processedQueue.push(item);
|
|
311
319
|
} else if (action === "showCharacter" || action === "playSound" || action === "playMusic" || action === "voice") {
|
|
312
320
|
const closing = getOppositeAction(action);
|
|
313
|
-
const skip = next(i).some(([_action,
|
|
314
|
-
if (
|
|
315
|
-
return false;
|
|
316
|
-
if (_meta[0] !== meta[0])
|
|
321
|
+
const skip = next(i).some(([_action, target]) => {
|
|
322
|
+
if (target !== params[0]) {
|
|
317
323
|
return false;
|
|
324
|
+
}
|
|
318
325
|
return _action === closing || _action === action;
|
|
319
326
|
});
|
|
320
327
|
if (skip)
|
|
321
328
|
continue;
|
|
322
329
|
if (action === "showCharacter") {
|
|
323
|
-
characters.add(
|
|
330
|
+
characters.add(params[0]);
|
|
324
331
|
} else if (action === "playMusic") {
|
|
325
|
-
audio.music.add(
|
|
332
|
+
audio.music.add(params[0]);
|
|
326
333
|
} else if (action === "playSound") {
|
|
327
|
-
audio.sound.add(
|
|
334
|
+
audio.sound.add(params[0]);
|
|
328
335
|
}
|
|
329
|
-
processedQueue.push(
|
|
330
|
-
} else if (action === "showBackground" || action === "
|
|
331
|
-
const skip = next(i).some(([_action]
|
|
336
|
+
processedQueue.push(item);
|
|
337
|
+
} else if (action === "showBackground" || action === "preload") {
|
|
338
|
+
const skip = next(i).some(([_action]) => action === _action);
|
|
332
339
|
if (skip)
|
|
333
340
|
continue;
|
|
334
|
-
processedQueue.push(
|
|
341
|
+
processedQueue.push(item);
|
|
342
|
+
} else if (action === "animateCharacter") {
|
|
343
|
+
const skip = next(i).some(([_action, character], j, array) => {
|
|
344
|
+
if (action === _action && character === params[0]) {
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
const next2 = array.slice(j);
|
|
348
|
+
const characterWillAnimate = next2.some(([__action, __character]) => action === __action);
|
|
349
|
+
const hasBlockingActions = next2.some((item2) => options.skip.has(item2));
|
|
350
|
+
return characterWillAnimate && hasBlockingActions;
|
|
351
|
+
});
|
|
352
|
+
if (skip)
|
|
353
|
+
continue;
|
|
354
|
+
processedQueue.push(item);
|
|
335
355
|
} else {
|
|
336
|
-
processedQueue.push(
|
|
356
|
+
processedQueue.push(item);
|
|
337
357
|
}
|
|
338
358
|
}
|
|
339
359
|
const run = async (match) => {
|
|
340
|
-
for await (const [action,
|
|
341
|
-
const result = match(action,
|
|
360
|
+
for await (const [action, ...params] of processedQueue) {
|
|
361
|
+
const result = match(action, params);
|
|
342
362
|
if (isPromise(result)) {
|
|
343
363
|
await result;
|
|
344
364
|
}
|
|
345
365
|
}
|
|
346
366
|
processedQueue.length = 0;
|
|
347
367
|
};
|
|
348
|
-
|
|
349
|
-
|
|
368
|
+
return {
|
|
369
|
+
run,
|
|
370
|
+
keep: {
|
|
350
371
|
keep,
|
|
351
372
|
characters,
|
|
352
373
|
audio
|
|
353
|
-
}
|
|
354
|
-
};
|
|
355
|
-
return {
|
|
356
|
-
run,
|
|
357
|
-
getKeep
|
|
374
|
+
}
|
|
358
375
|
};
|
|
359
376
|
};
|
|
360
377
|
var getStack = (ctx) => {
|
|
@@ -586,17 +603,17 @@ var split = (input, delimeters) => {
|
|
|
586
603
|
output.push(input);
|
|
587
604
|
return output;
|
|
588
605
|
};
|
|
589
|
-
var
|
|
606
|
+
var flattenAllowedContent = (c, state) => {
|
|
590
607
|
if (Array.isArray(c)) {
|
|
591
|
-
return c.map((item) =>
|
|
608
|
+
return c.map((item) => flattenAllowedContent(item, state)).join("<br>");
|
|
592
609
|
}
|
|
593
610
|
if (typeof c === "function") {
|
|
594
|
-
return
|
|
611
|
+
return flattenAllowedContent(c(state), state);
|
|
595
612
|
}
|
|
596
613
|
return c;
|
|
597
614
|
};
|
|
598
615
|
var replace = (str2, obj, pluralization, actions, pr) => {
|
|
599
|
-
return
|
|
616
|
+
return str2.replaceAll(RGX, (x, key, y) => {
|
|
600
617
|
x = 0;
|
|
601
618
|
y = obj;
|
|
602
619
|
const [pathstr, plural, action] = split(key.trim(), ["@", "%"]);
|
|
@@ -647,7 +664,6 @@ var novely = ({
|
|
|
647
664
|
renderer: createRenderer,
|
|
648
665
|
initialScreen = "mainmenu",
|
|
649
666
|
translation,
|
|
650
|
-
languages,
|
|
651
667
|
state: defaultState,
|
|
652
668
|
data: defaultData,
|
|
653
669
|
autosaves = true,
|
|
@@ -658,8 +674,10 @@ var novely = ({
|
|
|
658
674
|
askBeforeExit = true,
|
|
659
675
|
preloadAssets = "lazy",
|
|
660
676
|
parallelAssetsDownloadLimit = 15,
|
|
661
|
-
fetch: request = fetch
|
|
677
|
+
fetch: request = fetch,
|
|
678
|
+
saveOnUnload = true
|
|
662
679
|
}) => {
|
|
680
|
+
const languages = Object.keys(translation);
|
|
663
681
|
const limitScript = pLimit(1);
|
|
664
682
|
const limitAssetsDownload = pLimit(parallelAssetsDownloadLimit);
|
|
665
683
|
const story = {};
|
|
@@ -747,44 +765,55 @@ var novely = ({
|
|
|
747
765
|
};
|
|
748
766
|
}
|
|
749
767
|
});
|
|
750
|
-
|
|
751
|
-
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
752
|
-
if (!value)
|
|
753
|
-
return stack.value[1];
|
|
754
|
-
const prev = stack.value[1];
|
|
755
|
-
const val = isFunction(value) ? value(prev) : deepmerge(prev, value);
|
|
756
|
-
stack.value[1] = val;
|
|
757
|
-
}
|
|
758
|
-
const getDefaultSave = (state2 = {}) => {
|
|
768
|
+
const getDefaultSave = (state = {}) => {
|
|
759
769
|
return [
|
|
760
770
|
[
|
|
761
771
|
["jump", "start"],
|
|
762
772
|
[null, 0]
|
|
763
773
|
],
|
|
764
|
-
|
|
774
|
+
state,
|
|
765
775
|
[intime(Date.now()), "auto"]
|
|
766
776
|
];
|
|
767
777
|
};
|
|
768
778
|
const getLanguageWithoutParameters = () => {
|
|
769
|
-
|
|
779
|
+
const language = getLanguage2(languages, getLanguage);
|
|
780
|
+
if (languages.includes(language)) {
|
|
781
|
+
return language;
|
|
782
|
+
}
|
|
783
|
+
if (DEV2) {
|
|
784
|
+
throw new Error(`Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`);
|
|
785
|
+
}
|
|
786
|
+
throw 0;
|
|
770
787
|
};
|
|
771
788
|
const initialData = {
|
|
772
789
|
saves: [],
|
|
773
790
|
data: klona(defaultData),
|
|
774
791
|
meta: [getLanguageWithoutParameters(), DEFAULT_TYPEWRITER_SPEED, 1, 1, 1]
|
|
775
792
|
};
|
|
776
|
-
const
|
|
793
|
+
const $ = store(initialData);
|
|
794
|
+
const coreData = store({
|
|
777
795
|
dataLoaded: false
|
|
796
|
+
});
|
|
797
|
+
const onDataLoadedPromise = ({ cancelled }) => {
|
|
798
|
+
if (cancelled) {
|
|
799
|
+
dataLoaded.promise.then(onDataLoadedPromise);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
coreData.update((data2) => {
|
|
803
|
+
data2.dataLoaded = true;
|
|
804
|
+
return data2;
|
|
805
|
+
});
|
|
778
806
|
};
|
|
779
|
-
|
|
780
|
-
const $$ = store(coreData);
|
|
807
|
+
dataLoaded.promise.then(onDataLoadedPromise);
|
|
781
808
|
const onStorageDataChange = (value) => {
|
|
782
|
-
if (
|
|
809
|
+
if (coreData.get().dataLoaded)
|
|
783
810
|
storage.set(value);
|
|
784
811
|
};
|
|
785
812
|
const throttledOnStorageDataChange = throttle(onStorageDataChange, throttleTimeout);
|
|
786
813
|
const throttledEmergencyOnStorageDataChange = throttle(() => {
|
|
787
|
-
|
|
814
|
+
if (saveOnUnload === true || saveOnUnload === "prod" && !DEV2) {
|
|
815
|
+
onStorageDataChange($.get());
|
|
816
|
+
}
|
|
788
817
|
}, 10);
|
|
789
818
|
$.subscribe(throttledOnStorageDataChange);
|
|
790
819
|
const getStoredData = async () => {
|
|
@@ -792,19 +821,16 @@ var novely = ({
|
|
|
792
821
|
for (const migration of migrations) {
|
|
793
822
|
stored = migration(stored);
|
|
794
823
|
}
|
|
795
|
-
stored.meta[
|
|
796
|
-
if (overrideLanguage) {
|
|
824
|
+
if (overrideLanguage || !stored.meta[0]) {
|
|
797
825
|
stored.meta[0] = getLanguageWithoutParameters();
|
|
798
|
-
} else {
|
|
799
|
-
stored.meta[0] ||= getLanguageWithoutParameters();
|
|
800
826
|
}
|
|
827
|
+
stored.meta[1] ||= DEFAULT_TYPEWRITER_SPEED;
|
|
801
828
|
stored.meta[2] ??= 1;
|
|
802
829
|
stored.meta[3] ??= 1;
|
|
803
830
|
stored.meta[4] ??= 1;
|
|
804
831
|
if (isEmpty(stored.data)) {
|
|
805
832
|
stored.data = defaultData;
|
|
806
833
|
}
|
|
807
|
-
$$.update((prev) => (prev.dataLoaded = true, prev));
|
|
808
834
|
dataLoaded.resolve();
|
|
809
835
|
$.set(stored);
|
|
810
836
|
};
|
|
@@ -818,7 +844,7 @@ var novely = ({
|
|
|
818
844
|
addEventListener("visibilitychange", onVisibilityChange);
|
|
819
845
|
addEventListener("beforeunload", throttledEmergencyOnStorageDataChange);
|
|
820
846
|
const save = (override = false, type = override ? "auto" : "manual") => {
|
|
821
|
-
if (
|
|
847
|
+
if (!coreData.get().dataLoaded)
|
|
822
848
|
return;
|
|
823
849
|
if (!autosaves && type === "auto")
|
|
824
850
|
return;
|
|
@@ -842,7 +868,7 @@ var novely = ({
|
|
|
842
868
|
});
|
|
843
869
|
};
|
|
844
870
|
const newGame = () => {
|
|
845
|
-
if (
|
|
871
|
+
if (!coreData.get().dataLoaded)
|
|
846
872
|
return;
|
|
847
873
|
const save2 = getDefaultSave(klona(defaultState));
|
|
848
874
|
if (autosaves) {
|
|
@@ -853,13 +879,13 @@ var novely = ({
|
|
|
853
879
|
restore(save2);
|
|
854
880
|
};
|
|
855
881
|
const set = (save2, ctx) => {
|
|
856
|
-
const stack = useStack(ctx ||
|
|
882
|
+
const stack = useStack(ctx || MAIN_CONTEXT_KEY);
|
|
857
883
|
stack.value = save2;
|
|
858
884
|
return restore(save2);
|
|
859
885
|
};
|
|
860
886
|
let interacted = 0;
|
|
861
887
|
const restore = async (save2) => {
|
|
862
|
-
if (
|
|
888
|
+
if (!coreData.get().dataLoaded)
|
|
863
889
|
return;
|
|
864
890
|
let latest = save2 || $.get().saves.at(-1);
|
|
865
891
|
if (!latest) {
|
|
@@ -875,19 +901,22 @@ var novely = ({
|
|
|
875
901
|
const previous = stack.previous;
|
|
876
902
|
const [path] = stack.value = latest;
|
|
877
903
|
renderer.ui.showScreen("game");
|
|
878
|
-
const queue = getActionsFromPath(story, path);
|
|
879
|
-
const processor = createQueueProcessor(queue
|
|
880
|
-
|
|
904
|
+
const { queue, skip, skipPreserve } = getActionsFromPath(story, path, false);
|
|
905
|
+
const processor = createQueueProcessor(queue, {
|
|
906
|
+
skip,
|
|
907
|
+
skipPreserve
|
|
908
|
+
});
|
|
909
|
+
const { keep, characters: characters2, audio } = processor.keep;
|
|
881
910
|
if (previous) {
|
|
882
|
-
const prevQueue = getActionsFromPath(story, previous[0],
|
|
883
|
-
|
|
884
|
-
for (let i = prevQueue.length - 1; i > currQueue.length; i--) {
|
|
911
|
+
const { queue: prevQueue } = getActionsFromPath(story, previous[0], false);
|
|
912
|
+
for (let i = prevQueue.length - 1; i > queue.length; i--) {
|
|
885
913
|
const element = prevQueue[i];
|
|
886
|
-
if (isAction(element)) {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
914
|
+
if (!isAction(element)) {
|
|
915
|
+
continue;
|
|
916
|
+
}
|
|
917
|
+
const [action2, fn] = element;
|
|
918
|
+
if (action2 === "custom") {
|
|
919
|
+
context.clearCustom(fn);
|
|
891
920
|
}
|
|
892
921
|
}
|
|
893
922
|
}
|
|
@@ -907,9 +936,6 @@ var novely = ({
|
|
|
907
936
|
render(context);
|
|
908
937
|
};
|
|
909
938
|
const refer = (path) => {
|
|
910
|
-
if (!path) {
|
|
911
|
-
path = useStack(MAIN_CONTEXT_KEY).value[0];
|
|
912
|
-
}
|
|
913
939
|
let current = story;
|
|
914
940
|
let precurrent = story;
|
|
915
941
|
const blocks = [];
|
|
@@ -965,11 +991,13 @@ var novely = ({
|
|
|
965
991
|
return translation[lang].internal[key];
|
|
966
992
|
};
|
|
967
993
|
const preview = async ([path, data2], name) => {
|
|
968
|
-
const queue = getActionsFromPath(story, path);
|
|
994
|
+
const { queue } = getActionsFromPath(story, path, true);
|
|
969
995
|
const ctx = renderer.getContext(name);
|
|
970
996
|
ctx.meta.restoring = true;
|
|
971
997
|
ctx.meta.preview = true;
|
|
972
|
-
const processor = createQueueProcessor(queue
|
|
998
|
+
const processor = createQueueProcessor(queue, {
|
|
999
|
+
skip: /* @__PURE__ */ new Set()
|
|
1000
|
+
});
|
|
973
1001
|
await processor.run((action2, props) => {
|
|
974
1002
|
if (isAudioAction(action2))
|
|
975
1003
|
return;
|
|
@@ -986,6 +1014,23 @@ var novely = ({
|
|
|
986
1014
|
const removeContext = (name) => {
|
|
987
1015
|
STACK_MAP.delete(name);
|
|
988
1016
|
};
|
|
1017
|
+
const getStateAtCtx = (context) => {
|
|
1018
|
+
return useStack(context).value[1];
|
|
1019
|
+
};
|
|
1020
|
+
const getStateFunction = (context) => {
|
|
1021
|
+
const stack = useStack(context);
|
|
1022
|
+
const state = (value) => {
|
|
1023
|
+
const _state = getStateAtCtx(context);
|
|
1024
|
+
if (!value) {
|
|
1025
|
+
return _state;
|
|
1026
|
+
}
|
|
1027
|
+
const prev = _state;
|
|
1028
|
+
const val = isFunction(value) ? value(prev) : deepmerge(prev, value);
|
|
1029
|
+
stack.value[1] = val;
|
|
1030
|
+
return void 0;
|
|
1031
|
+
};
|
|
1032
|
+
return state;
|
|
1033
|
+
};
|
|
989
1034
|
const renderer = createRenderer({
|
|
990
1035
|
mainContextKey: MAIN_CONTEXT_KEY,
|
|
991
1036
|
characters,
|
|
@@ -998,9 +1043,10 @@ var novely = ({
|
|
|
998
1043
|
t,
|
|
999
1044
|
preview,
|
|
1000
1045
|
removeContext,
|
|
1046
|
+
getStateFunction,
|
|
1001
1047
|
languages,
|
|
1002
1048
|
$,
|
|
1003
|
-
|
|
1049
|
+
$$: coreData
|
|
1004
1050
|
});
|
|
1005
1051
|
const useStack = createUseStackFunction(renderer);
|
|
1006
1052
|
useStack(MAIN_CONTEXT_KEY).push(initial);
|
|
@@ -1014,15 +1060,19 @@ var novely = ({
|
|
|
1014
1060
|
stack.push(current);
|
|
1015
1061
|
save(true, "auto");
|
|
1016
1062
|
};
|
|
1017
|
-
const
|
|
1018
|
-
const stack = useStack(ctx);
|
|
1019
|
-
const path = stack.value[0];
|
|
1063
|
+
const nextPath = (path) => {
|
|
1020
1064
|
const last = path.at(-1);
|
|
1021
1065
|
if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
|
|
1022
1066
|
last[1]++;
|
|
1023
1067
|
} else {
|
|
1024
1068
|
path.push([null, 0]);
|
|
1025
1069
|
}
|
|
1070
|
+
return path;
|
|
1071
|
+
};
|
|
1072
|
+
const next = (ctx) => {
|
|
1073
|
+
const stack = useStack(ctx);
|
|
1074
|
+
const path = stack.value[0];
|
|
1075
|
+
nextPath(path);
|
|
1026
1076
|
};
|
|
1027
1077
|
const matchActionInit = {
|
|
1028
1078
|
getContext: renderer.getContext,
|
|
@@ -1044,7 +1094,7 @@ var novely = ({
|
|
|
1044
1094
|
wait({ ctx, push }, [time]) {
|
|
1045
1095
|
if (ctx.meta.restoring)
|
|
1046
1096
|
return;
|
|
1047
|
-
setTimeout(push, isFunction(time) ? time() : time);
|
|
1097
|
+
setTimeout(push, isFunction(time) ? time(getStateAtCtx(ctx)) : time);
|
|
1048
1098
|
},
|
|
1049
1099
|
showBackground({ ctx, push }, [background]) {
|
|
1050
1100
|
ctx.background(background);
|
|
@@ -1103,15 +1153,31 @@ var novely = ({
|
|
|
1103
1153
|
return c || "";
|
|
1104
1154
|
})();
|
|
1105
1155
|
ctx.dialog(
|
|
1106
|
-
|
|
1107
|
-
|
|
1156
|
+
templateReplace(content, data2),
|
|
1157
|
+
templateReplace(name, data2),
|
|
1108
1158
|
character,
|
|
1109
1159
|
emotion,
|
|
1110
1160
|
forward
|
|
1111
1161
|
);
|
|
1112
1162
|
},
|
|
1163
|
+
say({ ctx, data: data2 }, [character, content]) {
|
|
1164
|
+
if (DEV2 && !characters[character]) {
|
|
1165
|
+
throw new Error(`Attempt to call Say action with unknown character "${character}"`);
|
|
1166
|
+
}
|
|
1167
|
+
match("dialog", [character, content], {
|
|
1168
|
+
ctx,
|
|
1169
|
+
data: data2
|
|
1170
|
+
});
|
|
1171
|
+
},
|
|
1113
1172
|
function({ ctx, push }, [fn]) {
|
|
1114
|
-
const
|
|
1173
|
+
const { restoring, goingBack, preview: preview2 } = ctx.meta;
|
|
1174
|
+
const result = fn({
|
|
1175
|
+
lang: $.get().meta[0],
|
|
1176
|
+
goingBack,
|
|
1177
|
+
restoring,
|
|
1178
|
+
preview: preview2,
|
|
1179
|
+
state: getStateFunction(ctx)
|
|
1180
|
+
});
|
|
1115
1181
|
if (!ctx.meta.restoring) {
|
|
1116
1182
|
result ? result.then(push) : push();
|
|
1117
1183
|
}
|
|
@@ -1123,22 +1189,26 @@ var novely = ({
|
|
|
1123
1189
|
choices.unshift(question);
|
|
1124
1190
|
question = "";
|
|
1125
1191
|
}
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1192
|
+
const transformedChoices = choices.map(([content, action2, visible]) => {
|
|
1193
|
+
const shown = !visible || visible({
|
|
1194
|
+
lang: $.get().meta[0],
|
|
1195
|
+
state: getStateAtCtx(ctx)
|
|
1196
|
+
});
|
|
1197
|
+
if (DEV2 && action2.length === 0 && !shown) {
|
|
1128
1198
|
console.warn(`Choice children should not be empty, either add content there or make item not selectable`);
|
|
1129
1199
|
}
|
|
1130
|
-
return [
|
|
1200
|
+
return [templateReplace(content, data2), action2, shown];
|
|
1131
1201
|
});
|
|
1132
|
-
if (DEV2 &&
|
|
1202
|
+
if (DEV2 && transformedChoices.length === 0) {
|
|
1133
1203
|
throw new Error(`Running choice without variants to choose from, look at how to use Choice action properly [https://novely.pages.dev/guide/actions/choice#usage]`);
|
|
1134
1204
|
}
|
|
1135
|
-
ctx.choices(
|
|
1205
|
+
ctx.choices(templateReplace(question, data2), transformedChoices, (selected) => {
|
|
1136
1206
|
if (!ctx.meta.preview) {
|
|
1137
1207
|
enmemory(ctx);
|
|
1138
1208
|
}
|
|
1139
1209
|
const stack = useStack(ctx);
|
|
1140
1210
|
const offset = isWithoutQuestion ? 0 : 1;
|
|
1141
|
-
if (DEV2 && !
|
|
1211
|
+
if (DEV2 && !transformedChoices[selected]) {
|
|
1142
1212
|
throw new Error("Choice children is empty, either add content there or make item not selectable");
|
|
1143
1213
|
}
|
|
1144
1214
|
stack.value[0].push(["choice", selected + offset], [null, 0]);
|
|
@@ -1177,7 +1247,7 @@ var novely = ({
|
|
|
1177
1247
|
throw new Error(`Attempt to use Condition action with empty variants object`);
|
|
1178
1248
|
}
|
|
1179
1249
|
if (!ctx.meta.restoring) {
|
|
1180
|
-
const val = String(condition());
|
|
1250
|
+
const val = String(condition(getStateAtCtx(ctx)));
|
|
1181
1251
|
if (DEV2 && !variants[val]) {
|
|
1182
1252
|
throw new Error(`Attempt to go to unknown variant "${val}"`);
|
|
1183
1253
|
}
|
|
@@ -1200,7 +1270,7 @@ var novely = ({
|
|
|
1200
1270
|
},
|
|
1201
1271
|
input({ ctx, data: data2, forward }, [question, onInput, setup]) {
|
|
1202
1272
|
ctx.input(
|
|
1203
|
-
|
|
1273
|
+
templateReplace(question, data2),
|
|
1204
1274
|
onInput,
|
|
1205
1275
|
setup || noop,
|
|
1206
1276
|
forward
|
|
@@ -1238,7 +1308,7 @@ var novely = ({
|
|
|
1238
1308
|
push();
|
|
1239
1309
|
},
|
|
1240
1310
|
text({ ctx, data: data2, forward }, text) {
|
|
1241
|
-
const string = text.map((content) =>
|
|
1311
|
+
const string = text.map((content) => templateReplace(content, data2)).join(" ");
|
|
1242
1312
|
if (DEV2 && string.length === 0) {
|
|
1243
1313
|
throw new Error(`Action Text was called with empty string or array`);
|
|
1244
1314
|
}
|
|
@@ -1330,52 +1400,106 @@ var novely = ({
|
|
|
1330
1400
|
const interactivity = (value = false) => {
|
|
1331
1401
|
interacted = value ? interacted + 1 : 0;
|
|
1332
1402
|
};
|
|
1333
|
-
const
|
|
1403
|
+
const templateReplace = (content, values) => {
|
|
1334
1404
|
const {
|
|
1335
1405
|
data: data2,
|
|
1336
1406
|
meta: [lang]
|
|
1337
1407
|
} = $.get();
|
|
1338
|
-
const obj = values
|
|
1339
|
-
const cnt = isFunction(content) ? content() : typeof content === "string" ? content : content[lang];
|
|
1340
|
-
const str2 =
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1408
|
+
const obj = values || data2;
|
|
1409
|
+
const cnt = isFunction(content) ? content(obj) : typeof content === "string" ? content : content[lang];
|
|
1410
|
+
const str2 = flattenAllowedContent(
|
|
1411
|
+
isFunction(cnt) ? cnt(obj) : cnt,
|
|
1412
|
+
obj
|
|
1413
|
+
);
|
|
1414
|
+
const t2 = translation[lang];
|
|
1415
|
+
const pluralRules = (t2.plural || t2.actions) && new Intl.PluralRules(t2.tag || lang);
|
|
1416
|
+
return replace(
|
|
1417
|
+
str2,
|
|
1418
|
+
obj,
|
|
1419
|
+
t2.plural,
|
|
1420
|
+
t2.actions,
|
|
1421
|
+
pluralRules
|
|
1422
|
+
);
|
|
1346
1423
|
};
|
|
1347
|
-
|
|
1424
|
+
const data = (value) => {
|
|
1425
|
+
const _data = $.get().data;
|
|
1348
1426
|
if (!value)
|
|
1349
|
-
return
|
|
1350
|
-
const
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
return prev2;
|
|
1427
|
+
return _data;
|
|
1428
|
+
const val = isFunction(value) ? value(_data) : deepmerge(_data, value);
|
|
1429
|
+
$.update((prev) => {
|
|
1430
|
+
prev.data = val;
|
|
1431
|
+
return prev;
|
|
1355
1432
|
});
|
|
1356
|
-
|
|
1433
|
+
return void 0;
|
|
1434
|
+
};
|
|
1357
1435
|
return {
|
|
1358
1436
|
/**
|
|
1359
1437
|
* Function to set game script
|
|
1438
|
+
*
|
|
1439
|
+
* @example
|
|
1440
|
+
* ```ts
|
|
1441
|
+
* engine.script({
|
|
1442
|
+
* start: [
|
|
1443
|
+
* action.function(() => {})
|
|
1444
|
+
* ]
|
|
1445
|
+
* })
|
|
1446
|
+
* ```
|
|
1360
1447
|
*/
|
|
1361
1448
|
script,
|
|
1362
1449
|
/**
|
|
1363
|
-
*
|
|
1450
|
+
* Get actions
|
|
1451
|
+
*
|
|
1452
|
+
* @example
|
|
1453
|
+
* ```ts
|
|
1454
|
+
* engine.script({
|
|
1455
|
+
* start: [
|
|
1456
|
+
* action.function(() => {})
|
|
1457
|
+
* ]
|
|
1458
|
+
* })
|
|
1459
|
+
* ```
|
|
1364
1460
|
*/
|
|
1365
1461
|
action,
|
|
1366
1462
|
/**
|
|
1367
|
-
*
|
|
1463
|
+
* @deprecated Will be removed BUT replaced with state passed into actions as a parameter
|
|
1368
1464
|
*/
|
|
1369
|
-
state,
|
|
1465
|
+
state: getStateFunction(MAIN_CONTEXT_KEY),
|
|
1370
1466
|
/**
|
|
1371
|
-
*
|
|
1467
|
+
* Store data between games
|
|
1468
|
+
*
|
|
1469
|
+
* @example
|
|
1470
|
+
* ```ts
|
|
1471
|
+
* engine.script({
|
|
1472
|
+
* start: [
|
|
1473
|
+
* action.function(() => {
|
|
1474
|
+
* // Paid content should be purchased only once
|
|
1475
|
+
* // So it will be available in any save
|
|
1476
|
+
* data({ paid_content_purchased: true })
|
|
1477
|
+
* })
|
|
1478
|
+
* ]
|
|
1479
|
+
* })
|
|
1480
|
+
* ```
|
|
1372
1481
|
*/
|
|
1373
1482
|
data,
|
|
1374
1483
|
/**
|
|
1375
|
-
*
|
|
1484
|
+
* @deprecated Renamed into `templateReplace`
|
|
1376
1485
|
*/
|
|
1377
1486
|
unwrap(content) {
|
|
1378
|
-
return
|
|
1487
|
+
return templateReplace(content);
|
|
1488
|
+
},
|
|
1489
|
+
/**
|
|
1490
|
+
* Replaces content inside {{braces}} with using global data
|
|
1491
|
+
* @example
|
|
1492
|
+
* ```ts
|
|
1493
|
+
* data({ name: 'Alexei' })
|
|
1494
|
+
*
|
|
1495
|
+
* templateReplace('{{name}} is our hero')
|
|
1496
|
+
* templateReplace({
|
|
1497
|
+
* en: (data) => 'Hello, ' + data.name
|
|
1498
|
+
* })
|
|
1499
|
+
* ```
|
|
1500
|
+
*/
|
|
1501
|
+
templateReplace(content) {
|
|
1502
|
+
return templateReplace(content);
|
|
1379
1503
|
},
|
|
1380
1504
|
/**
|
|
1381
1505
|
* Cancel data loading, hide UI, ignore page change events
|