@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.global.js
CHANGED
|
@@ -161,7 +161,7 @@ var Novely = (() => {
|
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
// src/constants.ts
|
|
164
|
-
var SKIPPED_DURING_RESTORE = /* @__PURE__ */ new Set(["dialog", "choice", "input", "vibrate", "text"]);
|
|
164
|
+
var SKIPPED_DURING_RESTORE = /* @__PURE__ */ new Set(["dialog", "say", "choice", "input", "vibrate", "text"]);
|
|
165
165
|
var BLOCK_EXIT_STATEMENTS = /* @__PURE__ */ new Set(["choice:exit", "condition:exit", "block:exit"]);
|
|
166
166
|
var BLOCK_STATEMENTS = /* @__PURE__ */ new Set(["choice", "condition", "block"]);
|
|
167
167
|
var AUDIO_ACTIONS = /* @__PURE__ */ new Set([
|
|
@@ -256,8 +256,8 @@ var Novely = (() => {
|
|
|
256
256
|
return startsWith("http") || startsWith("/") || startsWith(".") || startsWith("data");
|
|
257
257
|
};
|
|
258
258
|
var str = String;
|
|
259
|
-
var isUserRequiredAction = (action, meta) => {
|
|
260
|
-
return action === "custom" && meta[0] && meta[0].requireUserAction;
|
|
259
|
+
var isUserRequiredAction = ([action, ...meta]) => {
|
|
260
|
+
return Boolean(action === "custom" && meta[0] && meta[0].requireUserAction);
|
|
261
261
|
};
|
|
262
262
|
var getLanguage = (languages) => {
|
|
263
263
|
let { language } = navigator;
|
|
@@ -378,11 +378,13 @@ var Novely = (() => {
|
|
|
378
378
|
};
|
|
379
379
|
return MAP[action];
|
|
380
380
|
};
|
|
381
|
-
var getActionsFromPath = (story, path,
|
|
381
|
+
var getActionsFromPath = (story, path, filter) => {
|
|
382
382
|
let current = story;
|
|
383
383
|
let precurrent;
|
|
384
384
|
let ignoreNested = false;
|
|
385
385
|
let index = 0;
|
|
386
|
+
let skipPreserve = void 0;
|
|
387
|
+
const skip = /* @__PURE__ */ new Set();
|
|
386
388
|
const max = path.reduce((acc, [type, val]) => {
|
|
387
389
|
if (isNull(type) && isNumber(val)) {
|
|
388
390
|
return acc + 1;
|
|
@@ -411,21 +413,19 @@ var Novely = (() => {
|
|
|
411
413
|
const item = current[i];
|
|
412
414
|
if (!isAction(item))
|
|
413
415
|
continue;
|
|
414
|
-
const [action
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
push();
|
|
420
|
-
continue;
|
|
416
|
+
const [action] = item;
|
|
417
|
+
const last = index === max && i === val;
|
|
418
|
+
const shouldSkip = isSkippedDuringRestore(action) || isUserRequiredAction(item);
|
|
419
|
+
if (shouldSkip) {
|
|
420
|
+
skip.add(item);
|
|
421
421
|
}
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
422
|
+
if (shouldSkip && last) {
|
|
423
|
+
skipPreserve = item;
|
|
424
|
+
}
|
|
425
|
+
if (filter && shouldSkip && !last) {
|
|
426
426
|
continue;
|
|
427
427
|
} else {
|
|
428
|
-
push();
|
|
428
|
+
queue.push(item);
|
|
429
429
|
}
|
|
430
430
|
}
|
|
431
431
|
}
|
|
@@ -444,9 +444,13 @@ var Novely = (() => {
|
|
|
444
444
|
ignoreNested = true;
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
|
-
return
|
|
447
|
+
return {
|
|
448
|
+
queue,
|
|
449
|
+
skip,
|
|
450
|
+
skipPreserve
|
|
451
|
+
};
|
|
448
452
|
};
|
|
449
|
-
var createQueueProcessor = (queue) => {
|
|
453
|
+
var createQueueProcessor = (queue, options) => {
|
|
450
454
|
const processedQueue = [];
|
|
451
455
|
const keep = /* @__PURE__ */ new Set();
|
|
452
456
|
const characters = /* @__PURE__ */ new Set();
|
|
@@ -455,70 +459,83 @@ var Novely = (() => {
|
|
|
455
459
|
sound: /* @__PURE__ */ new Set()
|
|
456
460
|
};
|
|
457
461
|
const next = (i) => queue.slice(i + 1);
|
|
458
|
-
for (const [i,
|
|
462
|
+
for (const [i, item] of queue.entries()) {
|
|
463
|
+
const [action, ...params] = item;
|
|
459
464
|
keep.add(action);
|
|
465
|
+
if (options.skip.has(item) && item !== options.skipPreserve) {
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
460
468
|
if (action === "function" || action === "custom") {
|
|
461
|
-
if (action === "custom" &&
|
|
462
|
-
const notLatest = next(i).some(([,
|
|
463
|
-
if (!
|
|
469
|
+
if (action === "custom" && params[0].callOnlyLatest) {
|
|
470
|
+
const notLatest = next(i).some(([, func]) => {
|
|
471
|
+
if (!isFunction(func))
|
|
464
472
|
return false;
|
|
465
|
-
const c0 =
|
|
466
|
-
const c1 =
|
|
467
|
-
const isIdenticalID = c0.id && c1.id && c0.id === c1.id;
|
|
473
|
+
const c0 = func;
|
|
474
|
+
const c1 = params[0];
|
|
475
|
+
const isIdenticalID = Boolean(c0.id && c1.id && c0.id === c1.id);
|
|
468
476
|
const isIdenticalByReference = c0 === c1;
|
|
469
477
|
return isIdenticalID || isIdenticalByReference || str(c0) === str(c1);
|
|
470
478
|
});
|
|
471
479
|
if (notLatest)
|
|
472
480
|
continue;
|
|
473
481
|
}
|
|
474
|
-
processedQueue.push(
|
|
482
|
+
processedQueue.push(item);
|
|
475
483
|
} else if (action === "showCharacter" || action === "playSound" || action === "playMusic" || action === "voice") {
|
|
476
484
|
const closing = getOppositeAction(action);
|
|
477
|
-
const skip = next(i).some(([_action,
|
|
478
|
-
if (
|
|
479
|
-
return false;
|
|
480
|
-
if (_meta[0] !== meta[0])
|
|
485
|
+
const skip = next(i).some(([_action, target]) => {
|
|
486
|
+
if (target !== params[0]) {
|
|
481
487
|
return false;
|
|
488
|
+
}
|
|
482
489
|
return _action === closing || _action === action;
|
|
483
490
|
});
|
|
484
491
|
if (skip)
|
|
485
492
|
continue;
|
|
486
493
|
if (action === "showCharacter") {
|
|
487
|
-
characters.add(
|
|
494
|
+
characters.add(params[0]);
|
|
488
495
|
} else if (action === "playMusic") {
|
|
489
|
-
audio.music.add(
|
|
496
|
+
audio.music.add(params[0]);
|
|
490
497
|
} else if (action === "playSound") {
|
|
491
|
-
audio.sound.add(
|
|
498
|
+
audio.sound.add(params[0]);
|
|
492
499
|
}
|
|
493
|
-
processedQueue.push(
|
|
494
|
-
} else if (action === "showBackground" || action === "
|
|
495
|
-
const skip = next(i).some(([_action]
|
|
500
|
+
processedQueue.push(item);
|
|
501
|
+
} else if (action === "showBackground" || action === "preload") {
|
|
502
|
+
const skip = next(i).some(([_action]) => action === _action);
|
|
496
503
|
if (skip)
|
|
497
504
|
continue;
|
|
498
|
-
processedQueue.push(
|
|
505
|
+
processedQueue.push(item);
|
|
506
|
+
} else if (action === "animateCharacter") {
|
|
507
|
+
const skip = next(i).some(([_action, character], j, array) => {
|
|
508
|
+
if (action === _action && character === params[0]) {
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
const next2 = array.slice(j);
|
|
512
|
+
const characterWillAnimate = next2.some(([__action, __character]) => action === __action);
|
|
513
|
+
const hasBlockingActions = next2.some((item2) => options.skip.has(item2));
|
|
514
|
+
return characterWillAnimate && hasBlockingActions;
|
|
515
|
+
});
|
|
516
|
+
if (skip)
|
|
517
|
+
continue;
|
|
518
|
+
processedQueue.push(item);
|
|
499
519
|
} else {
|
|
500
|
-
processedQueue.push(
|
|
520
|
+
processedQueue.push(item);
|
|
501
521
|
}
|
|
502
522
|
}
|
|
503
523
|
const run = async (match) => {
|
|
504
|
-
for await (const [action,
|
|
505
|
-
const result = match(action,
|
|
524
|
+
for await (const [action, ...params] of processedQueue) {
|
|
525
|
+
const result = match(action, params);
|
|
506
526
|
if (isPromise(result)) {
|
|
507
527
|
await result;
|
|
508
528
|
}
|
|
509
529
|
}
|
|
510
530
|
processedQueue.length = 0;
|
|
511
531
|
};
|
|
512
|
-
|
|
513
|
-
|
|
532
|
+
return {
|
|
533
|
+
run,
|
|
534
|
+
keep: {
|
|
514
535
|
keep,
|
|
515
536
|
characters,
|
|
516
537
|
audio
|
|
517
|
-
}
|
|
518
|
-
};
|
|
519
|
-
return {
|
|
520
|
-
run,
|
|
521
|
-
getKeep
|
|
538
|
+
}
|
|
522
539
|
};
|
|
523
540
|
};
|
|
524
541
|
var getStack = (ctx) => {
|
|
@@ -750,17 +767,17 @@ var Novely = (() => {
|
|
|
750
767
|
output.push(input);
|
|
751
768
|
return output;
|
|
752
769
|
};
|
|
753
|
-
var
|
|
770
|
+
var flattenAllowedContent = (c, state) => {
|
|
754
771
|
if (Array.isArray(c)) {
|
|
755
|
-
return c.map((item) =>
|
|
772
|
+
return c.map((item) => flattenAllowedContent(item, state)).join("<br>");
|
|
756
773
|
}
|
|
757
774
|
if (typeof c === "function") {
|
|
758
|
-
return
|
|
775
|
+
return flattenAllowedContent(c(state), state);
|
|
759
776
|
}
|
|
760
777
|
return c;
|
|
761
778
|
};
|
|
762
779
|
var replace = (str2, obj, pluralization, actions, pr) => {
|
|
763
|
-
return
|
|
780
|
+
return str2.replaceAll(RGX, (x, key, y) => {
|
|
764
781
|
x = 0;
|
|
765
782
|
y = obj;
|
|
766
783
|
const [pathstr, plural, action] = split(key.trim(), ["@", "%"]);
|
|
@@ -810,7 +827,6 @@ var Novely = (() => {
|
|
|
810
827
|
renderer: createRenderer,
|
|
811
828
|
initialScreen = "mainmenu",
|
|
812
829
|
translation,
|
|
813
|
-
languages,
|
|
814
830
|
state: defaultState,
|
|
815
831
|
data: defaultData,
|
|
816
832
|
autosaves = true,
|
|
@@ -821,8 +837,10 @@ var Novely = (() => {
|
|
|
821
837
|
askBeforeExit = true,
|
|
822
838
|
preloadAssets = "lazy",
|
|
823
839
|
parallelAssetsDownloadLimit = 15,
|
|
824
|
-
fetch: request = fetch
|
|
840
|
+
fetch: request = fetch,
|
|
841
|
+
saveOnUnload = true
|
|
825
842
|
}) => {
|
|
843
|
+
const languages = Object.keys(translation);
|
|
826
844
|
const limitScript = (0, import_p_limit.default)(1);
|
|
827
845
|
const limitAssetsDownload = (0, import_p_limit.default)(parallelAssetsDownloadLimit);
|
|
828
846
|
const story = {};
|
|
@@ -910,44 +928,55 @@ var Novely = (() => {
|
|
|
910
928
|
};
|
|
911
929
|
}
|
|
912
930
|
});
|
|
913
|
-
|
|
914
|
-
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
915
|
-
if (!value)
|
|
916
|
-
return stack.value[1];
|
|
917
|
-
const prev = stack.value[1];
|
|
918
|
-
const val = isFunction(value) ? value(prev) : deepmerge(prev, value);
|
|
919
|
-
stack.value[1] = val;
|
|
920
|
-
}
|
|
921
|
-
const getDefaultSave = (state2 = {}) => {
|
|
931
|
+
const getDefaultSave = (state = {}) => {
|
|
922
932
|
return [
|
|
923
933
|
[
|
|
924
934
|
["jump", "start"],
|
|
925
935
|
[null, 0]
|
|
926
936
|
],
|
|
927
|
-
|
|
937
|
+
state,
|
|
928
938
|
[intime(Date.now()), "auto"]
|
|
929
939
|
];
|
|
930
940
|
};
|
|
931
941
|
const getLanguageWithoutParameters = () => {
|
|
932
|
-
|
|
942
|
+
const language = getLanguage2(languages, getLanguage);
|
|
943
|
+
if (languages.includes(language)) {
|
|
944
|
+
return language;
|
|
945
|
+
}
|
|
946
|
+
if (DEV) {
|
|
947
|
+
throw new Error(`Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`);
|
|
948
|
+
}
|
|
949
|
+
throw 0;
|
|
933
950
|
};
|
|
934
951
|
const initialData = {
|
|
935
952
|
saves: [],
|
|
936
953
|
data: klona(defaultData),
|
|
937
954
|
meta: [getLanguageWithoutParameters(), DEFAULT_TYPEWRITER_SPEED, 1, 1, 1]
|
|
938
955
|
};
|
|
939
|
-
const
|
|
956
|
+
const $ = store(initialData);
|
|
957
|
+
const coreData = store({
|
|
940
958
|
dataLoaded: false
|
|
959
|
+
});
|
|
960
|
+
const onDataLoadedPromise = ({ cancelled }) => {
|
|
961
|
+
if (cancelled) {
|
|
962
|
+
dataLoaded.promise.then(onDataLoadedPromise);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
coreData.update((data2) => {
|
|
966
|
+
data2.dataLoaded = true;
|
|
967
|
+
return data2;
|
|
968
|
+
});
|
|
941
969
|
};
|
|
942
|
-
|
|
943
|
-
const $$ = store(coreData);
|
|
970
|
+
dataLoaded.promise.then(onDataLoadedPromise);
|
|
944
971
|
const onStorageDataChange = (value) => {
|
|
945
|
-
if (
|
|
972
|
+
if (coreData.get().dataLoaded)
|
|
946
973
|
storage.set(value);
|
|
947
974
|
};
|
|
948
975
|
const throttledOnStorageDataChange = throttle(onStorageDataChange, throttleTimeout);
|
|
949
976
|
const throttledEmergencyOnStorageDataChange = throttle(() => {
|
|
950
|
-
|
|
977
|
+
if (saveOnUnload === true || saveOnUnload === "prod" && !DEV) {
|
|
978
|
+
onStorageDataChange($.get());
|
|
979
|
+
}
|
|
951
980
|
}, 10);
|
|
952
981
|
$.subscribe(throttledOnStorageDataChange);
|
|
953
982
|
const getStoredData = async () => {
|
|
@@ -955,19 +984,16 @@ var Novely = (() => {
|
|
|
955
984
|
for (const migration of migrations) {
|
|
956
985
|
stored = migration(stored);
|
|
957
986
|
}
|
|
958
|
-
stored.meta[
|
|
959
|
-
if (overrideLanguage) {
|
|
987
|
+
if (overrideLanguage || !stored.meta[0]) {
|
|
960
988
|
stored.meta[0] = getLanguageWithoutParameters();
|
|
961
|
-
} else {
|
|
962
|
-
stored.meta[0] ||= getLanguageWithoutParameters();
|
|
963
989
|
}
|
|
990
|
+
stored.meta[1] ||= DEFAULT_TYPEWRITER_SPEED;
|
|
964
991
|
stored.meta[2] ??= 1;
|
|
965
992
|
stored.meta[3] ??= 1;
|
|
966
993
|
stored.meta[4] ??= 1;
|
|
967
994
|
if (isEmpty(stored.data)) {
|
|
968
995
|
stored.data = defaultData;
|
|
969
996
|
}
|
|
970
|
-
$$.update((prev) => (prev.dataLoaded = true, prev));
|
|
971
997
|
dataLoaded.resolve();
|
|
972
998
|
$.set(stored);
|
|
973
999
|
};
|
|
@@ -981,7 +1007,7 @@ var Novely = (() => {
|
|
|
981
1007
|
addEventListener("visibilitychange", onVisibilityChange);
|
|
982
1008
|
addEventListener("beforeunload", throttledEmergencyOnStorageDataChange);
|
|
983
1009
|
const save = (override = false, type = override ? "auto" : "manual") => {
|
|
984
|
-
if (
|
|
1010
|
+
if (!coreData.get().dataLoaded)
|
|
985
1011
|
return;
|
|
986
1012
|
if (!autosaves && type === "auto")
|
|
987
1013
|
return;
|
|
@@ -1005,7 +1031,7 @@ var Novely = (() => {
|
|
|
1005
1031
|
});
|
|
1006
1032
|
};
|
|
1007
1033
|
const newGame = () => {
|
|
1008
|
-
if (
|
|
1034
|
+
if (!coreData.get().dataLoaded)
|
|
1009
1035
|
return;
|
|
1010
1036
|
const save2 = getDefaultSave(klona(defaultState));
|
|
1011
1037
|
if (autosaves) {
|
|
@@ -1016,13 +1042,13 @@ var Novely = (() => {
|
|
|
1016
1042
|
restore(save2);
|
|
1017
1043
|
};
|
|
1018
1044
|
const set = (save2, ctx) => {
|
|
1019
|
-
const stack = useStack(ctx ||
|
|
1045
|
+
const stack = useStack(ctx || MAIN_CONTEXT_KEY);
|
|
1020
1046
|
stack.value = save2;
|
|
1021
1047
|
return restore(save2);
|
|
1022
1048
|
};
|
|
1023
1049
|
let interacted = 0;
|
|
1024
1050
|
const restore = async (save2) => {
|
|
1025
|
-
if (
|
|
1051
|
+
if (!coreData.get().dataLoaded)
|
|
1026
1052
|
return;
|
|
1027
1053
|
let latest = save2 || $.get().saves.at(-1);
|
|
1028
1054
|
if (!latest) {
|
|
@@ -1038,19 +1064,22 @@ var Novely = (() => {
|
|
|
1038
1064
|
const previous = stack.previous;
|
|
1039
1065
|
const [path] = stack.value = latest;
|
|
1040
1066
|
renderer.ui.showScreen("game");
|
|
1041
|
-
const queue = getActionsFromPath(story, path);
|
|
1042
|
-
const processor = createQueueProcessor(queue
|
|
1043
|
-
|
|
1067
|
+
const { queue, skip, skipPreserve } = getActionsFromPath(story, path, false);
|
|
1068
|
+
const processor = createQueueProcessor(queue, {
|
|
1069
|
+
skip,
|
|
1070
|
+
skipPreserve
|
|
1071
|
+
});
|
|
1072
|
+
const { keep, characters: characters2, audio } = processor.keep;
|
|
1044
1073
|
if (previous) {
|
|
1045
|
-
const prevQueue = getActionsFromPath(story, previous[0],
|
|
1046
|
-
|
|
1047
|
-
for (let i = prevQueue.length - 1; i > currQueue.length; i--) {
|
|
1074
|
+
const { queue: prevQueue } = getActionsFromPath(story, previous[0], false);
|
|
1075
|
+
for (let i = prevQueue.length - 1; i > queue.length; i--) {
|
|
1048
1076
|
const element = prevQueue[i];
|
|
1049
|
-
if (isAction(element)) {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1077
|
+
if (!isAction(element)) {
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
const [action2, fn] = element;
|
|
1081
|
+
if (action2 === "custom") {
|
|
1082
|
+
context.clearCustom(fn);
|
|
1054
1083
|
}
|
|
1055
1084
|
}
|
|
1056
1085
|
}
|
|
@@ -1070,9 +1099,6 @@ var Novely = (() => {
|
|
|
1070
1099
|
render(context);
|
|
1071
1100
|
};
|
|
1072
1101
|
const refer = (path) => {
|
|
1073
|
-
if (!path) {
|
|
1074
|
-
path = useStack(MAIN_CONTEXT_KEY).value[0];
|
|
1075
|
-
}
|
|
1076
1102
|
let current = story;
|
|
1077
1103
|
let precurrent = story;
|
|
1078
1104
|
const blocks = [];
|
|
@@ -1128,11 +1154,13 @@ var Novely = (() => {
|
|
|
1128
1154
|
return translation[lang].internal[key];
|
|
1129
1155
|
};
|
|
1130
1156
|
const preview = async ([path, data2], name) => {
|
|
1131
|
-
const queue = getActionsFromPath(story, path);
|
|
1157
|
+
const { queue } = getActionsFromPath(story, path, true);
|
|
1132
1158
|
const ctx = renderer.getContext(name);
|
|
1133
1159
|
ctx.meta.restoring = true;
|
|
1134
1160
|
ctx.meta.preview = true;
|
|
1135
|
-
const processor = createQueueProcessor(queue
|
|
1161
|
+
const processor = createQueueProcessor(queue, {
|
|
1162
|
+
skip: /* @__PURE__ */ new Set()
|
|
1163
|
+
});
|
|
1136
1164
|
await processor.run((action2, props) => {
|
|
1137
1165
|
if (isAudioAction(action2))
|
|
1138
1166
|
return;
|
|
@@ -1149,6 +1177,23 @@ var Novely = (() => {
|
|
|
1149
1177
|
const removeContext = (name) => {
|
|
1150
1178
|
STACK_MAP.delete(name);
|
|
1151
1179
|
};
|
|
1180
|
+
const getStateAtCtx = (context) => {
|
|
1181
|
+
return useStack(context).value[1];
|
|
1182
|
+
};
|
|
1183
|
+
const getStateFunction = (context) => {
|
|
1184
|
+
const stack = useStack(context);
|
|
1185
|
+
const state = (value) => {
|
|
1186
|
+
const _state = getStateAtCtx(context);
|
|
1187
|
+
if (!value) {
|
|
1188
|
+
return _state;
|
|
1189
|
+
}
|
|
1190
|
+
const prev = _state;
|
|
1191
|
+
const val = isFunction(value) ? value(prev) : deepmerge(prev, value);
|
|
1192
|
+
stack.value[1] = val;
|
|
1193
|
+
return void 0;
|
|
1194
|
+
};
|
|
1195
|
+
return state;
|
|
1196
|
+
};
|
|
1152
1197
|
const renderer = createRenderer({
|
|
1153
1198
|
mainContextKey: MAIN_CONTEXT_KEY,
|
|
1154
1199
|
characters,
|
|
@@ -1161,9 +1206,10 @@ var Novely = (() => {
|
|
|
1161
1206
|
t,
|
|
1162
1207
|
preview,
|
|
1163
1208
|
removeContext,
|
|
1209
|
+
getStateFunction,
|
|
1164
1210
|
languages,
|
|
1165
1211
|
$,
|
|
1166
|
-
|
|
1212
|
+
$$: coreData
|
|
1167
1213
|
});
|
|
1168
1214
|
const useStack = createUseStackFunction(renderer);
|
|
1169
1215
|
useStack(MAIN_CONTEXT_KEY).push(initial);
|
|
@@ -1177,15 +1223,19 @@ var Novely = (() => {
|
|
|
1177
1223
|
stack.push(current);
|
|
1178
1224
|
save(true, "auto");
|
|
1179
1225
|
};
|
|
1180
|
-
const
|
|
1181
|
-
const stack = useStack(ctx);
|
|
1182
|
-
const path = stack.value[0];
|
|
1226
|
+
const nextPath = (path) => {
|
|
1183
1227
|
const last = path.at(-1);
|
|
1184
1228
|
if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
|
|
1185
1229
|
last[1]++;
|
|
1186
1230
|
} else {
|
|
1187
1231
|
path.push([null, 0]);
|
|
1188
1232
|
}
|
|
1233
|
+
return path;
|
|
1234
|
+
};
|
|
1235
|
+
const next = (ctx) => {
|
|
1236
|
+
const stack = useStack(ctx);
|
|
1237
|
+
const path = stack.value[0];
|
|
1238
|
+
nextPath(path);
|
|
1189
1239
|
};
|
|
1190
1240
|
const matchActionInit = {
|
|
1191
1241
|
getContext: renderer.getContext,
|
|
@@ -1207,7 +1257,7 @@ var Novely = (() => {
|
|
|
1207
1257
|
wait({ ctx, push }, [time]) {
|
|
1208
1258
|
if (ctx.meta.restoring)
|
|
1209
1259
|
return;
|
|
1210
|
-
setTimeout(push, isFunction(time) ? time() : time);
|
|
1260
|
+
setTimeout(push, isFunction(time) ? time(getStateAtCtx(ctx)) : time);
|
|
1211
1261
|
},
|
|
1212
1262
|
showBackground({ ctx, push }, [background]) {
|
|
1213
1263
|
ctx.background(background);
|
|
@@ -1266,15 +1316,31 @@ var Novely = (() => {
|
|
|
1266
1316
|
return c || "";
|
|
1267
1317
|
})();
|
|
1268
1318
|
ctx.dialog(
|
|
1269
|
-
|
|
1270
|
-
|
|
1319
|
+
templateReplace(content, data2),
|
|
1320
|
+
templateReplace(name, data2),
|
|
1271
1321
|
character,
|
|
1272
1322
|
emotion,
|
|
1273
1323
|
forward
|
|
1274
1324
|
);
|
|
1275
1325
|
},
|
|
1326
|
+
say({ ctx, data: data2 }, [character, content]) {
|
|
1327
|
+
if (DEV && !characters[character]) {
|
|
1328
|
+
throw new Error(`Attempt to call Say action with unknown character "${character}"`);
|
|
1329
|
+
}
|
|
1330
|
+
match("dialog", [character, content], {
|
|
1331
|
+
ctx,
|
|
1332
|
+
data: data2
|
|
1333
|
+
});
|
|
1334
|
+
},
|
|
1276
1335
|
function({ ctx, push }, [fn]) {
|
|
1277
|
-
const
|
|
1336
|
+
const { restoring, goingBack, preview: preview2 } = ctx.meta;
|
|
1337
|
+
const result = fn({
|
|
1338
|
+
lang: $.get().meta[0],
|
|
1339
|
+
goingBack,
|
|
1340
|
+
restoring,
|
|
1341
|
+
preview: preview2,
|
|
1342
|
+
state: getStateFunction(ctx)
|
|
1343
|
+
});
|
|
1278
1344
|
if (!ctx.meta.restoring) {
|
|
1279
1345
|
result ? result.then(push) : push();
|
|
1280
1346
|
}
|
|
@@ -1286,22 +1352,26 @@ var Novely = (() => {
|
|
|
1286
1352
|
choices.unshift(question);
|
|
1287
1353
|
question = "";
|
|
1288
1354
|
}
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1355
|
+
const transformedChoices = choices.map(([content, action2, visible]) => {
|
|
1356
|
+
const shown = !visible || visible({
|
|
1357
|
+
lang: $.get().meta[0],
|
|
1358
|
+
state: getStateAtCtx(ctx)
|
|
1359
|
+
});
|
|
1360
|
+
if (DEV && action2.length === 0 && !shown) {
|
|
1291
1361
|
console.warn(`Choice children should not be empty, either add content there or make item not selectable`);
|
|
1292
1362
|
}
|
|
1293
|
-
return [
|
|
1363
|
+
return [templateReplace(content, data2), action2, shown];
|
|
1294
1364
|
});
|
|
1295
|
-
if (DEV &&
|
|
1365
|
+
if (DEV && transformedChoices.length === 0) {
|
|
1296
1366
|
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]`);
|
|
1297
1367
|
}
|
|
1298
|
-
ctx.choices(
|
|
1368
|
+
ctx.choices(templateReplace(question, data2), transformedChoices, (selected) => {
|
|
1299
1369
|
if (!ctx.meta.preview) {
|
|
1300
1370
|
enmemory(ctx);
|
|
1301
1371
|
}
|
|
1302
1372
|
const stack = useStack(ctx);
|
|
1303
1373
|
const offset = isWithoutQuestion ? 0 : 1;
|
|
1304
|
-
if (DEV && !
|
|
1374
|
+
if (DEV && !transformedChoices[selected]) {
|
|
1305
1375
|
throw new Error("Choice children is empty, either add content there or make item not selectable");
|
|
1306
1376
|
}
|
|
1307
1377
|
stack.value[0].push(["choice", selected + offset], [null, 0]);
|
|
@@ -1340,7 +1410,7 @@ var Novely = (() => {
|
|
|
1340
1410
|
throw new Error(`Attempt to use Condition action with empty variants object`);
|
|
1341
1411
|
}
|
|
1342
1412
|
if (!ctx.meta.restoring) {
|
|
1343
|
-
const val = String(condition());
|
|
1413
|
+
const val = String(condition(getStateAtCtx(ctx)));
|
|
1344
1414
|
if (DEV && !variants[val]) {
|
|
1345
1415
|
throw new Error(`Attempt to go to unknown variant "${val}"`);
|
|
1346
1416
|
}
|
|
@@ -1363,7 +1433,7 @@ var Novely = (() => {
|
|
|
1363
1433
|
},
|
|
1364
1434
|
input({ ctx, data: data2, forward }, [question, onInput, setup]) {
|
|
1365
1435
|
ctx.input(
|
|
1366
|
-
|
|
1436
|
+
templateReplace(question, data2),
|
|
1367
1437
|
onInput,
|
|
1368
1438
|
setup || noop,
|
|
1369
1439
|
forward
|
|
@@ -1401,7 +1471,7 @@ var Novely = (() => {
|
|
|
1401
1471
|
push();
|
|
1402
1472
|
},
|
|
1403
1473
|
text({ ctx, data: data2, forward }, text) {
|
|
1404
|
-
const string = text.map((content) =>
|
|
1474
|
+
const string = text.map((content) => templateReplace(content, data2)).join(" ");
|
|
1405
1475
|
if (DEV && string.length === 0) {
|
|
1406
1476
|
throw new Error(`Action Text was called with empty string or array`);
|
|
1407
1477
|
}
|
|
@@ -1493,52 +1563,106 @@ var Novely = (() => {
|
|
|
1493
1563
|
const interactivity = (value = false) => {
|
|
1494
1564
|
interacted = value ? interacted + 1 : 0;
|
|
1495
1565
|
};
|
|
1496
|
-
const
|
|
1566
|
+
const templateReplace = (content, values) => {
|
|
1497
1567
|
const {
|
|
1498
1568
|
data: data2,
|
|
1499
1569
|
meta: [lang]
|
|
1500
1570
|
} = $.get();
|
|
1501
|
-
const obj = values
|
|
1502
|
-
const cnt = isFunction(content) ? content() : typeof content === "string" ? content : content[lang];
|
|
1503
|
-
const str2 =
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1571
|
+
const obj = values || data2;
|
|
1572
|
+
const cnt = isFunction(content) ? content(obj) : typeof content === "string" ? content : content[lang];
|
|
1573
|
+
const str2 = flattenAllowedContent(
|
|
1574
|
+
isFunction(cnt) ? cnt(obj) : cnt,
|
|
1575
|
+
obj
|
|
1576
|
+
);
|
|
1577
|
+
const t2 = translation[lang];
|
|
1578
|
+
const pluralRules = (t2.plural || t2.actions) && new Intl.PluralRules(t2.tag || lang);
|
|
1579
|
+
return replace(
|
|
1580
|
+
str2,
|
|
1581
|
+
obj,
|
|
1582
|
+
t2.plural,
|
|
1583
|
+
t2.actions,
|
|
1584
|
+
pluralRules
|
|
1585
|
+
);
|
|
1509
1586
|
};
|
|
1510
|
-
|
|
1587
|
+
const data = (value) => {
|
|
1588
|
+
const _data = $.get().data;
|
|
1511
1589
|
if (!value)
|
|
1512
|
-
return
|
|
1513
|
-
const
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
return prev2;
|
|
1590
|
+
return _data;
|
|
1591
|
+
const val = isFunction(value) ? value(_data) : deepmerge(_data, value);
|
|
1592
|
+
$.update((prev) => {
|
|
1593
|
+
prev.data = val;
|
|
1594
|
+
return prev;
|
|
1518
1595
|
});
|
|
1519
|
-
|
|
1596
|
+
return void 0;
|
|
1597
|
+
};
|
|
1520
1598
|
return {
|
|
1521
1599
|
/**
|
|
1522
1600
|
* Function to set game script
|
|
1601
|
+
*
|
|
1602
|
+
* @example
|
|
1603
|
+
* ```ts
|
|
1604
|
+
* engine.script({
|
|
1605
|
+
* start: [
|
|
1606
|
+
* action.function(() => {})
|
|
1607
|
+
* ]
|
|
1608
|
+
* })
|
|
1609
|
+
* ```
|
|
1523
1610
|
*/
|
|
1524
1611
|
script,
|
|
1525
1612
|
/**
|
|
1526
|
-
*
|
|
1613
|
+
* Get actions
|
|
1614
|
+
*
|
|
1615
|
+
* @example
|
|
1616
|
+
* ```ts
|
|
1617
|
+
* engine.script({
|
|
1618
|
+
* start: [
|
|
1619
|
+
* action.function(() => {})
|
|
1620
|
+
* ]
|
|
1621
|
+
* })
|
|
1622
|
+
* ```
|
|
1527
1623
|
*/
|
|
1528
1624
|
action,
|
|
1529
1625
|
/**
|
|
1530
|
-
*
|
|
1626
|
+
* @deprecated Will be removed BUT replaced with state passed into actions as a parameter
|
|
1531
1627
|
*/
|
|
1532
|
-
state,
|
|
1628
|
+
state: getStateFunction(MAIN_CONTEXT_KEY),
|
|
1533
1629
|
/**
|
|
1534
|
-
*
|
|
1630
|
+
* Store data between games
|
|
1631
|
+
*
|
|
1632
|
+
* @example
|
|
1633
|
+
* ```ts
|
|
1634
|
+
* engine.script({
|
|
1635
|
+
* start: [
|
|
1636
|
+
* action.function(() => {
|
|
1637
|
+
* // Paid content should be purchased only once
|
|
1638
|
+
* // So it will be available in any save
|
|
1639
|
+
* data({ paid_content_purchased: true })
|
|
1640
|
+
* })
|
|
1641
|
+
* ]
|
|
1642
|
+
* })
|
|
1643
|
+
* ```
|
|
1535
1644
|
*/
|
|
1536
1645
|
data,
|
|
1537
1646
|
/**
|
|
1538
|
-
*
|
|
1647
|
+
* @deprecated Renamed into `templateReplace`
|
|
1539
1648
|
*/
|
|
1540
1649
|
unwrap(content) {
|
|
1541
|
-
return
|
|
1650
|
+
return templateReplace(content);
|
|
1651
|
+
},
|
|
1652
|
+
/**
|
|
1653
|
+
* Replaces content inside {{braces}} with using global data
|
|
1654
|
+
* @example
|
|
1655
|
+
* ```ts
|
|
1656
|
+
* data({ name: 'Alexei' })
|
|
1657
|
+
*
|
|
1658
|
+
* templateReplace('{{name}} is our hero')
|
|
1659
|
+
* templateReplace({
|
|
1660
|
+
* en: (data) => 'Hello, ' + data.name
|
|
1661
|
+
* })
|
|
1662
|
+
* ```
|
|
1663
|
+
*/
|
|
1664
|
+
templateReplace(content) {
|
|
1665
|
+
return templateReplace(content);
|
|
1542
1666
|
},
|
|
1543
1667
|
/**
|
|
1544
1668
|
* Cancel data loading, hide UI, ignore page change events
|