@novely/core 0.24.0 → 0.26.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 +201 -107
- package/dist/index.global.js +265 -139
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +265 -139
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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);
|
|
503
|
+
if (skip)
|
|
504
|
+
continue;
|
|
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
|
+
});
|
|
496
516
|
if (skip)
|
|
497
517
|
continue;
|
|
498
|
-
processedQueue.push(
|
|
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(), ["@", "%"]);
|
|
@@ -820,7 +837,8 @@ var Novely = (() => {
|
|
|
820
837
|
askBeforeExit = true,
|
|
821
838
|
preloadAssets = "lazy",
|
|
822
839
|
parallelAssetsDownloadLimit = 15,
|
|
823
|
-
fetch: request = fetch
|
|
840
|
+
fetch: request = fetch,
|
|
841
|
+
saveOnUnload = true
|
|
824
842
|
}) => {
|
|
825
843
|
const languages = Object.keys(translation);
|
|
826
844
|
const limitScript = (0, import_p_limit.default)(1);
|
|
@@ -910,46 +928,57 @@ 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 storageData = 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(storageData.get());
|
|
979
|
+
}
|
|
951
980
|
}, 10);
|
|
952
|
-
|
|
981
|
+
storageData.subscribe(throttledOnStorageDataChange);
|
|
953
982
|
const getStoredData = async () => {
|
|
954
983
|
let stored = await storage.get();
|
|
955
984
|
for (const migration of migrations) {
|
|
@@ -965,9 +994,8 @@ var Novely = (() => {
|
|
|
965
994
|
if (isEmpty(stored.data)) {
|
|
966
995
|
stored.data = defaultData;
|
|
967
996
|
}
|
|
968
|
-
$$.update((prev) => (prev.dataLoaded = true, prev));
|
|
969
997
|
dataLoaded.resolve();
|
|
970
|
-
|
|
998
|
+
storageData.set(stored);
|
|
971
999
|
};
|
|
972
1000
|
storageDelay.then(getStoredData);
|
|
973
1001
|
const initial = getDefaultSave(klona(defaultState));
|
|
@@ -979,13 +1007,13 @@ var Novely = (() => {
|
|
|
979
1007
|
addEventListener("visibilitychange", onVisibilityChange);
|
|
980
1008
|
addEventListener("beforeunload", throttledEmergencyOnStorageDataChange);
|
|
981
1009
|
const save = (override = false, type = override ? "auto" : "manual") => {
|
|
982
|
-
if (
|
|
1010
|
+
if (!coreData.get().dataLoaded)
|
|
983
1011
|
return;
|
|
984
1012
|
if (!autosaves && type === "auto")
|
|
985
1013
|
return;
|
|
986
1014
|
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
987
1015
|
const current = klona(stack.value);
|
|
988
|
-
|
|
1016
|
+
storageData.update((prev) => {
|
|
989
1017
|
const isLatest = findLastIndex(prev.saves, (value) => times.has(value[2][0])) === prev.saves.length - 1;
|
|
990
1018
|
current[2][0] = intime(Date.now());
|
|
991
1019
|
current[2][1] = type;
|
|
@@ -1003,29 +1031,29 @@ var Novely = (() => {
|
|
|
1003
1031
|
});
|
|
1004
1032
|
};
|
|
1005
1033
|
const newGame = () => {
|
|
1006
|
-
if (
|
|
1034
|
+
if (!coreData.get().dataLoaded)
|
|
1007
1035
|
return;
|
|
1008
1036
|
const save2 = getDefaultSave(klona(defaultState));
|
|
1009
1037
|
if (autosaves) {
|
|
1010
|
-
|
|
1038
|
+
storageData.update((prev) => {
|
|
1011
1039
|
return prev.saves.push(save2), prev;
|
|
1012
1040
|
});
|
|
1013
1041
|
}
|
|
1014
1042
|
restore(save2);
|
|
1015
1043
|
};
|
|
1016
1044
|
const set = (save2, ctx) => {
|
|
1017
|
-
const stack = useStack(ctx ||
|
|
1045
|
+
const stack = useStack(ctx || MAIN_CONTEXT_KEY);
|
|
1018
1046
|
stack.value = save2;
|
|
1019
1047
|
return restore(save2);
|
|
1020
1048
|
};
|
|
1021
1049
|
let interacted = 0;
|
|
1022
1050
|
const restore = async (save2) => {
|
|
1023
|
-
if (
|
|
1051
|
+
if (!coreData.get().dataLoaded)
|
|
1024
1052
|
return;
|
|
1025
|
-
let latest = save2 ||
|
|
1053
|
+
let latest = save2 || storageData.get().saves.at(-1);
|
|
1026
1054
|
if (!latest) {
|
|
1027
1055
|
latest = klona(initial);
|
|
1028
|
-
|
|
1056
|
+
storageData.update((prev) => {
|
|
1029
1057
|
prev.saves.push(latest);
|
|
1030
1058
|
return prev;
|
|
1031
1059
|
});
|
|
@@ -1036,19 +1064,22 @@ var Novely = (() => {
|
|
|
1036
1064
|
const previous = stack.previous;
|
|
1037
1065
|
const [path] = stack.value = latest;
|
|
1038
1066
|
renderer.ui.showScreen("game");
|
|
1039
|
-
const queue = getActionsFromPath(story, path);
|
|
1040
|
-
const processor = createQueueProcessor(queue
|
|
1041
|
-
|
|
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;
|
|
1042
1073
|
if (previous) {
|
|
1043
|
-
const prevQueue = getActionsFromPath(story, previous[0],
|
|
1044
|
-
|
|
1045
|
-
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--) {
|
|
1046
1076
|
const element = prevQueue[i];
|
|
1047
|
-
if (isAction(element)) {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1077
|
+
if (!isAction(element)) {
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
const [action2, fn] = element;
|
|
1081
|
+
if (action2 === "custom") {
|
|
1082
|
+
context.clearCustom(fn);
|
|
1052
1083
|
}
|
|
1053
1084
|
}
|
|
1054
1085
|
}
|
|
@@ -1068,9 +1099,6 @@ var Novely = (() => {
|
|
|
1068
1099
|
render(context);
|
|
1069
1100
|
};
|
|
1070
1101
|
const refer = (path) => {
|
|
1071
|
-
if (!path) {
|
|
1072
|
-
path = useStack(MAIN_CONTEXT_KEY).value[0];
|
|
1073
|
-
}
|
|
1074
1102
|
let current = story;
|
|
1075
1103
|
let precurrent = story;
|
|
1076
1104
|
const blocks = [];
|
|
@@ -1109,7 +1137,7 @@ var Novely = (() => {
|
|
|
1109
1137
|
ctx.audio.destroy();
|
|
1110
1138
|
const [time, type] = current[2];
|
|
1111
1139
|
if (type === "auto" && interacted <= 1 && times.has(time)) {
|
|
1112
|
-
|
|
1140
|
+
storageData.update((prev) => {
|
|
1113
1141
|
prev.saves = prev.saves.filter((save2) => save2 !== current);
|
|
1114
1142
|
return prev;
|
|
1115
1143
|
});
|
|
@@ -1126,11 +1154,13 @@ var Novely = (() => {
|
|
|
1126
1154
|
return translation[lang].internal[key];
|
|
1127
1155
|
};
|
|
1128
1156
|
const preview = async ([path, data2], name) => {
|
|
1129
|
-
const queue = getActionsFromPath(story, path);
|
|
1157
|
+
const { queue } = getActionsFromPath(story, path, true);
|
|
1130
1158
|
const ctx = renderer.getContext(name);
|
|
1131
1159
|
ctx.meta.restoring = true;
|
|
1132
1160
|
ctx.meta.preview = true;
|
|
1133
|
-
const processor = createQueueProcessor(queue
|
|
1161
|
+
const processor = createQueueProcessor(queue, {
|
|
1162
|
+
skip: /* @__PURE__ */ new Set()
|
|
1163
|
+
});
|
|
1134
1164
|
await processor.run((action2, props) => {
|
|
1135
1165
|
if (isAudioAction(action2))
|
|
1136
1166
|
return;
|
|
@@ -1147,6 +1177,23 @@ var Novely = (() => {
|
|
|
1147
1177
|
const removeContext = (name) => {
|
|
1148
1178
|
STACK_MAP.delete(name);
|
|
1149
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
|
+
};
|
|
1150
1197
|
const renderer = createRenderer({
|
|
1151
1198
|
mainContextKey: MAIN_CONTEXT_KEY,
|
|
1152
1199
|
characters,
|
|
@@ -1159,9 +1206,10 @@ var Novely = (() => {
|
|
|
1159
1206
|
t,
|
|
1160
1207
|
preview,
|
|
1161
1208
|
removeContext,
|
|
1209
|
+
getStateFunction,
|
|
1162
1210
|
languages,
|
|
1163
|
-
|
|
1164
|
-
|
|
1211
|
+
storageData,
|
|
1212
|
+
coreData
|
|
1165
1213
|
});
|
|
1166
1214
|
const useStack = createUseStackFunction(renderer);
|
|
1167
1215
|
useStack(MAIN_CONTEXT_KEY).push(initial);
|
|
@@ -1175,15 +1223,19 @@ var Novely = (() => {
|
|
|
1175
1223
|
stack.push(current);
|
|
1176
1224
|
save(true, "auto");
|
|
1177
1225
|
};
|
|
1178
|
-
const
|
|
1179
|
-
const stack = useStack(ctx);
|
|
1180
|
-
const path = stack.value[0];
|
|
1226
|
+
const nextPath = (path) => {
|
|
1181
1227
|
const last = path.at(-1);
|
|
1182
1228
|
if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
|
|
1183
1229
|
last[1]++;
|
|
1184
1230
|
} else {
|
|
1185
1231
|
path.push([null, 0]);
|
|
1186
1232
|
}
|
|
1233
|
+
return path;
|
|
1234
|
+
};
|
|
1235
|
+
const next = (ctx) => {
|
|
1236
|
+
const stack = useStack(ctx);
|
|
1237
|
+
const path = stack.value[0];
|
|
1238
|
+
nextPath(path);
|
|
1187
1239
|
};
|
|
1188
1240
|
const matchActionInit = {
|
|
1189
1241
|
getContext: renderer.getContext,
|
|
@@ -1205,7 +1257,7 @@ var Novely = (() => {
|
|
|
1205
1257
|
wait({ ctx, push }, [time]) {
|
|
1206
1258
|
if (ctx.meta.restoring)
|
|
1207
1259
|
return;
|
|
1208
|
-
setTimeout(push, isFunction(time) ? time() : time);
|
|
1260
|
+
setTimeout(push, isFunction(time) ? time(getStateAtCtx(ctx)) : time);
|
|
1209
1261
|
},
|
|
1210
1262
|
showBackground({ ctx, push }, [background]) {
|
|
1211
1263
|
ctx.background(background);
|
|
@@ -1251,7 +1303,7 @@ var Novely = (() => {
|
|
|
1251
1303
|
const name = (() => {
|
|
1252
1304
|
const c = character;
|
|
1253
1305
|
const cs = characters;
|
|
1254
|
-
const [lang] =
|
|
1306
|
+
const [lang] = storageData.get().meta;
|
|
1255
1307
|
if (c && c in cs) {
|
|
1256
1308
|
const block = cs[c].name;
|
|
1257
1309
|
if (typeof block === "string") {
|
|
@@ -1264,15 +1316,31 @@ var Novely = (() => {
|
|
|
1264
1316
|
return c || "";
|
|
1265
1317
|
})();
|
|
1266
1318
|
ctx.dialog(
|
|
1267
|
-
|
|
1268
|
-
|
|
1319
|
+
templateReplace(content, data2),
|
|
1320
|
+
templateReplace(name, data2),
|
|
1269
1321
|
character,
|
|
1270
1322
|
emotion,
|
|
1271
1323
|
forward
|
|
1272
1324
|
);
|
|
1273
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
|
+
},
|
|
1274
1335
|
function({ ctx, push }, [fn]) {
|
|
1275
|
-
const
|
|
1336
|
+
const { restoring, goingBack, preview: preview2 } = ctx.meta;
|
|
1337
|
+
const result = fn({
|
|
1338
|
+
lang: storageData.get().meta[0],
|
|
1339
|
+
goingBack,
|
|
1340
|
+
restoring,
|
|
1341
|
+
preview: preview2,
|
|
1342
|
+
state: getStateFunction(ctx)
|
|
1343
|
+
});
|
|
1276
1344
|
if (!ctx.meta.restoring) {
|
|
1277
1345
|
result ? result.then(push) : push();
|
|
1278
1346
|
}
|
|
@@ -1284,22 +1352,26 @@ var Novely = (() => {
|
|
|
1284
1352
|
choices.unshift(question);
|
|
1285
1353
|
question = "";
|
|
1286
1354
|
}
|
|
1287
|
-
const
|
|
1288
|
-
|
|
1355
|
+
const transformedChoices = choices.map(([content, action2, visible]) => {
|
|
1356
|
+
const shown = !visible || visible({
|
|
1357
|
+
lang: storageData.get().meta[0],
|
|
1358
|
+
state: getStateAtCtx(ctx)
|
|
1359
|
+
});
|
|
1360
|
+
if (DEV && action2.length === 0 && !shown) {
|
|
1289
1361
|
console.warn(`Choice children should not be empty, either add content there or make item not selectable`);
|
|
1290
1362
|
}
|
|
1291
|
-
return [
|
|
1363
|
+
return [templateReplace(content, data2), shown];
|
|
1292
1364
|
});
|
|
1293
|
-
if (DEV &&
|
|
1365
|
+
if (DEV && transformedChoices.length === 0) {
|
|
1294
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]`);
|
|
1295
1367
|
}
|
|
1296
|
-
ctx.choices(
|
|
1368
|
+
ctx.choices(templateReplace(question, data2), transformedChoices, (selected) => {
|
|
1297
1369
|
if (!ctx.meta.preview) {
|
|
1298
1370
|
enmemory(ctx);
|
|
1299
1371
|
}
|
|
1300
1372
|
const stack = useStack(ctx);
|
|
1301
1373
|
const offset = isWithoutQuestion ? 0 : 1;
|
|
1302
|
-
if (DEV && !
|
|
1374
|
+
if (DEV && !transformedChoices[selected]) {
|
|
1303
1375
|
throw new Error("Choice children is empty, either add content there or make item not selectable");
|
|
1304
1376
|
}
|
|
1305
1377
|
stack.value[0].push(["choice", selected + offset], [null, 0]);
|
|
@@ -1338,7 +1410,7 @@ var Novely = (() => {
|
|
|
1338
1410
|
throw new Error(`Attempt to use Condition action with empty variants object`);
|
|
1339
1411
|
}
|
|
1340
1412
|
if (!ctx.meta.restoring) {
|
|
1341
|
-
const val = String(condition());
|
|
1413
|
+
const val = String(condition(getStateAtCtx(ctx)));
|
|
1342
1414
|
if (DEV && !variants[val]) {
|
|
1343
1415
|
throw new Error(`Attempt to go to unknown variant "${val}"`);
|
|
1344
1416
|
}
|
|
@@ -1361,7 +1433,7 @@ var Novely = (() => {
|
|
|
1361
1433
|
},
|
|
1362
1434
|
input({ ctx, data: data2, forward }, [question, onInput, setup]) {
|
|
1363
1435
|
ctx.input(
|
|
1364
|
-
|
|
1436
|
+
templateReplace(question, data2),
|
|
1365
1437
|
onInput,
|
|
1366
1438
|
setup || noop,
|
|
1367
1439
|
forward
|
|
@@ -1399,7 +1471,7 @@ var Novely = (() => {
|
|
|
1399
1471
|
push();
|
|
1400
1472
|
},
|
|
1401
1473
|
text({ ctx, data: data2, forward }, text) {
|
|
1402
|
-
const string = text.map((content) =>
|
|
1474
|
+
const string = text.map((content) => templateReplace(content, data2)).join(" ");
|
|
1403
1475
|
if (DEV && string.length === 0) {
|
|
1404
1476
|
throw new Error(`Action Text was called with empty string or array`);
|
|
1405
1477
|
}
|
|
@@ -1491,52 +1563,106 @@ var Novely = (() => {
|
|
|
1491
1563
|
const interactivity = (value = false) => {
|
|
1492
1564
|
interacted = value ? interacted + 1 : 0;
|
|
1493
1565
|
};
|
|
1494
|
-
const
|
|
1566
|
+
const templateReplace = (content, values) => {
|
|
1495
1567
|
const {
|
|
1496
1568
|
data: data2,
|
|
1497
1569
|
meta: [lang]
|
|
1498
|
-
} =
|
|
1499
|
-
const obj = values
|
|
1500
|
-
const cnt = isFunction(content) ? content() : typeof content === "string" ? content : content[lang];
|
|
1501
|
-
const str2 =
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1570
|
+
} = storageData.get();
|
|
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
|
+
);
|
|
1507
1586
|
};
|
|
1508
|
-
|
|
1587
|
+
const data = (value) => {
|
|
1588
|
+
const _data = storageData.get().data;
|
|
1509
1589
|
if (!value)
|
|
1510
|
-
return
|
|
1511
|
-
const
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
return prev2;
|
|
1590
|
+
return _data;
|
|
1591
|
+
const val = isFunction(value) ? value(_data) : deepmerge(_data, value);
|
|
1592
|
+
storageData.update((prev) => {
|
|
1593
|
+
prev.data = val;
|
|
1594
|
+
return prev;
|
|
1516
1595
|
});
|
|
1517
|
-
|
|
1596
|
+
return void 0;
|
|
1597
|
+
};
|
|
1518
1598
|
return {
|
|
1519
1599
|
/**
|
|
1520
1600
|
* Function to set game script
|
|
1601
|
+
*
|
|
1602
|
+
* @example
|
|
1603
|
+
* ```ts
|
|
1604
|
+
* engine.script({
|
|
1605
|
+
* start: [
|
|
1606
|
+
* action.function(() => {})
|
|
1607
|
+
* ]
|
|
1608
|
+
* })
|
|
1609
|
+
* ```
|
|
1521
1610
|
*/
|
|
1522
1611
|
script,
|
|
1523
1612
|
/**
|
|
1524
|
-
*
|
|
1613
|
+
* Get actions
|
|
1614
|
+
*
|
|
1615
|
+
* @example
|
|
1616
|
+
* ```ts
|
|
1617
|
+
* engine.script({
|
|
1618
|
+
* start: [
|
|
1619
|
+
* action.function(() => {})
|
|
1620
|
+
* ]
|
|
1621
|
+
* })
|
|
1622
|
+
* ```
|
|
1525
1623
|
*/
|
|
1526
1624
|
action,
|
|
1527
1625
|
/**
|
|
1528
|
-
*
|
|
1626
|
+
* @deprecated Will be removed BUT replaced with state passed into actions as a parameter
|
|
1529
1627
|
*/
|
|
1530
|
-
state,
|
|
1628
|
+
state: getStateFunction(MAIN_CONTEXT_KEY),
|
|
1531
1629
|
/**
|
|
1532
|
-
*
|
|
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
|
+
* ```
|
|
1533
1644
|
*/
|
|
1534
1645
|
data,
|
|
1535
1646
|
/**
|
|
1536
|
-
*
|
|
1647
|
+
* @deprecated Renamed into `templateReplace`
|
|
1537
1648
|
*/
|
|
1538
1649
|
unwrap(content) {
|
|
1539
|
-
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);
|
|
1540
1666
|
},
|
|
1541
1667
|
/**
|
|
1542
1668
|
* Cancel data loading, hide UI, ignore page change events
|