@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.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);
|
|
339
|
+
if (skip)
|
|
340
|
+
continue;
|
|
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
|
+
});
|
|
332
352
|
if (skip)
|
|
333
353
|
continue;
|
|
334
|
-
processedQueue.push(
|
|
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(), ["@", "%"]);
|
|
@@ -657,7 +674,8 @@ var novely = ({
|
|
|
657
674
|
askBeforeExit = true,
|
|
658
675
|
preloadAssets = "lazy",
|
|
659
676
|
parallelAssetsDownloadLimit = 15,
|
|
660
|
-
fetch: request = fetch
|
|
677
|
+
fetch: request = fetch,
|
|
678
|
+
saveOnUnload = true
|
|
661
679
|
}) => {
|
|
662
680
|
const languages = Object.keys(translation);
|
|
663
681
|
const limitScript = pLimit(1);
|
|
@@ -747,46 +765,57 @@ 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 storageData = 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(storageData.get());
|
|
816
|
+
}
|
|
788
817
|
}, 10);
|
|
789
|
-
|
|
818
|
+
storageData.subscribe(throttledOnStorageDataChange);
|
|
790
819
|
const getStoredData = async () => {
|
|
791
820
|
let stored = await storage.get();
|
|
792
821
|
for (const migration of migrations) {
|
|
@@ -802,9 +831,8 @@ var novely = ({
|
|
|
802
831
|
if (isEmpty(stored.data)) {
|
|
803
832
|
stored.data = defaultData;
|
|
804
833
|
}
|
|
805
|
-
$$.update((prev) => (prev.dataLoaded = true, prev));
|
|
806
834
|
dataLoaded.resolve();
|
|
807
|
-
|
|
835
|
+
storageData.set(stored);
|
|
808
836
|
};
|
|
809
837
|
storageDelay.then(getStoredData);
|
|
810
838
|
const initial = getDefaultSave(klona(defaultState));
|
|
@@ -816,13 +844,13 @@ var novely = ({
|
|
|
816
844
|
addEventListener("visibilitychange", onVisibilityChange);
|
|
817
845
|
addEventListener("beforeunload", throttledEmergencyOnStorageDataChange);
|
|
818
846
|
const save = (override = false, type = override ? "auto" : "manual") => {
|
|
819
|
-
if (
|
|
847
|
+
if (!coreData.get().dataLoaded)
|
|
820
848
|
return;
|
|
821
849
|
if (!autosaves && type === "auto")
|
|
822
850
|
return;
|
|
823
851
|
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
824
852
|
const current = klona(stack.value);
|
|
825
|
-
|
|
853
|
+
storageData.update((prev) => {
|
|
826
854
|
const isLatest = findLastIndex(prev.saves, (value) => times.has(value[2][0])) === prev.saves.length - 1;
|
|
827
855
|
current[2][0] = intime(Date.now());
|
|
828
856
|
current[2][1] = type;
|
|
@@ -840,29 +868,29 @@ var novely = ({
|
|
|
840
868
|
});
|
|
841
869
|
};
|
|
842
870
|
const newGame = () => {
|
|
843
|
-
if (
|
|
871
|
+
if (!coreData.get().dataLoaded)
|
|
844
872
|
return;
|
|
845
873
|
const save2 = getDefaultSave(klona(defaultState));
|
|
846
874
|
if (autosaves) {
|
|
847
|
-
|
|
875
|
+
storageData.update((prev) => {
|
|
848
876
|
return prev.saves.push(save2), prev;
|
|
849
877
|
});
|
|
850
878
|
}
|
|
851
879
|
restore(save2);
|
|
852
880
|
};
|
|
853
881
|
const set = (save2, ctx) => {
|
|
854
|
-
const stack = useStack(ctx ||
|
|
882
|
+
const stack = useStack(ctx || MAIN_CONTEXT_KEY);
|
|
855
883
|
stack.value = save2;
|
|
856
884
|
return restore(save2);
|
|
857
885
|
};
|
|
858
886
|
let interacted = 0;
|
|
859
887
|
const restore = async (save2) => {
|
|
860
|
-
if (
|
|
888
|
+
if (!coreData.get().dataLoaded)
|
|
861
889
|
return;
|
|
862
|
-
let latest = save2 ||
|
|
890
|
+
let latest = save2 || storageData.get().saves.at(-1);
|
|
863
891
|
if (!latest) {
|
|
864
892
|
latest = klona(initial);
|
|
865
|
-
|
|
893
|
+
storageData.update((prev) => {
|
|
866
894
|
prev.saves.push(latest);
|
|
867
895
|
return prev;
|
|
868
896
|
});
|
|
@@ -873,19 +901,22 @@ var novely = ({
|
|
|
873
901
|
const previous = stack.previous;
|
|
874
902
|
const [path] = stack.value = latest;
|
|
875
903
|
renderer.ui.showScreen("game");
|
|
876
|
-
const queue = getActionsFromPath(story, path);
|
|
877
|
-
const processor = createQueueProcessor(queue
|
|
878
|
-
|
|
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;
|
|
879
910
|
if (previous) {
|
|
880
|
-
const prevQueue = getActionsFromPath(story, previous[0],
|
|
881
|
-
|
|
882
|
-
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--) {
|
|
883
913
|
const element = prevQueue[i];
|
|
884
|
-
if (isAction(element)) {
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
914
|
+
if (!isAction(element)) {
|
|
915
|
+
continue;
|
|
916
|
+
}
|
|
917
|
+
const [action2, fn] = element;
|
|
918
|
+
if (action2 === "custom") {
|
|
919
|
+
context.clearCustom(fn);
|
|
889
920
|
}
|
|
890
921
|
}
|
|
891
922
|
}
|
|
@@ -905,9 +936,6 @@ var novely = ({
|
|
|
905
936
|
render(context);
|
|
906
937
|
};
|
|
907
938
|
const refer = (path) => {
|
|
908
|
-
if (!path) {
|
|
909
|
-
path = useStack(MAIN_CONTEXT_KEY).value[0];
|
|
910
|
-
}
|
|
911
939
|
let current = story;
|
|
912
940
|
let precurrent = story;
|
|
913
941
|
const blocks = [];
|
|
@@ -946,7 +974,7 @@ var novely = ({
|
|
|
946
974
|
ctx.audio.destroy();
|
|
947
975
|
const [time, type] = current[2];
|
|
948
976
|
if (type === "auto" && interacted <= 1 && times.has(time)) {
|
|
949
|
-
|
|
977
|
+
storageData.update((prev) => {
|
|
950
978
|
prev.saves = prev.saves.filter((save2) => save2 !== current);
|
|
951
979
|
return prev;
|
|
952
980
|
});
|
|
@@ -963,11 +991,13 @@ var novely = ({
|
|
|
963
991
|
return translation[lang].internal[key];
|
|
964
992
|
};
|
|
965
993
|
const preview = async ([path, data2], name) => {
|
|
966
|
-
const queue = getActionsFromPath(story, path);
|
|
994
|
+
const { queue } = getActionsFromPath(story, path, true);
|
|
967
995
|
const ctx = renderer.getContext(name);
|
|
968
996
|
ctx.meta.restoring = true;
|
|
969
997
|
ctx.meta.preview = true;
|
|
970
|
-
const processor = createQueueProcessor(queue
|
|
998
|
+
const processor = createQueueProcessor(queue, {
|
|
999
|
+
skip: /* @__PURE__ */ new Set()
|
|
1000
|
+
});
|
|
971
1001
|
await processor.run((action2, props) => {
|
|
972
1002
|
if (isAudioAction(action2))
|
|
973
1003
|
return;
|
|
@@ -984,6 +1014,23 @@ var novely = ({
|
|
|
984
1014
|
const removeContext = (name) => {
|
|
985
1015
|
STACK_MAP.delete(name);
|
|
986
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
|
+
};
|
|
987
1034
|
const renderer = createRenderer({
|
|
988
1035
|
mainContextKey: MAIN_CONTEXT_KEY,
|
|
989
1036
|
characters,
|
|
@@ -996,9 +1043,10 @@ var novely = ({
|
|
|
996
1043
|
t,
|
|
997
1044
|
preview,
|
|
998
1045
|
removeContext,
|
|
1046
|
+
getStateFunction,
|
|
999
1047
|
languages,
|
|
1000
|
-
|
|
1001
|
-
|
|
1048
|
+
storageData,
|
|
1049
|
+
coreData
|
|
1002
1050
|
});
|
|
1003
1051
|
const useStack = createUseStackFunction(renderer);
|
|
1004
1052
|
useStack(MAIN_CONTEXT_KEY).push(initial);
|
|
@@ -1012,15 +1060,19 @@ var novely = ({
|
|
|
1012
1060
|
stack.push(current);
|
|
1013
1061
|
save(true, "auto");
|
|
1014
1062
|
};
|
|
1015
|
-
const
|
|
1016
|
-
const stack = useStack(ctx);
|
|
1017
|
-
const path = stack.value[0];
|
|
1063
|
+
const nextPath = (path) => {
|
|
1018
1064
|
const last = path.at(-1);
|
|
1019
1065
|
if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
|
|
1020
1066
|
last[1]++;
|
|
1021
1067
|
} else {
|
|
1022
1068
|
path.push([null, 0]);
|
|
1023
1069
|
}
|
|
1070
|
+
return path;
|
|
1071
|
+
};
|
|
1072
|
+
const next = (ctx) => {
|
|
1073
|
+
const stack = useStack(ctx);
|
|
1074
|
+
const path = stack.value[0];
|
|
1075
|
+
nextPath(path);
|
|
1024
1076
|
};
|
|
1025
1077
|
const matchActionInit = {
|
|
1026
1078
|
getContext: renderer.getContext,
|
|
@@ -1042,7 +1094,7 @@ var novely = ({
|
|
|
1042
1094
|
wait({ ctx, push }, [time]) {
|
|
1043
1095
|
if (ctx.meta.restoring)
|
|
1044
1096
|
return;
|
|
1045
|
-
setTimeout(push, isFunction(time) ? time() : time);
|
|
1097
|
+
setTimeout(push, isFunction(time) ? time(getStateAtCtx(ctx)) : time);
|
|
1046
1098
|
},
|
|
1047
1099
|
showBackground({ ctx, push }, [background]) {
|
|
1048
1100
|
ctx.background(background);
|
|
@@ -1088,7 +1140,7 @@ var novely = ({
|
|
|
1088
1140
|
const name = (() => {
|
|
1089
1141
|
const c = character;
|
|
1090
1142
|
const cs = characters;
|
|
1091
|
-
const [lang] =
|
|
1143
|
+
const [lang] = storageData.get().meta;
|
|
1092
1144
|
if (c && c in cs) {
|
|
1093
1145
|
const block = cs[c].name;
|
|
1094
1146
|
if (typeof block === "string") {
|
|
@@ -1101,15 +1153,31 @@ var novely = ({
|
|
|
1101
1153
|
return c || "";
|
|
1102
1154
|
})();
|
|
1103
1155
|
ctx.dialog(
|
|
1104
|
-
|
|
1105
|
-
|
|
1156
|
+
templateReplace(content, data2),
|
|
1157
|
+
templateReplace(name, data2),
|
|
1106
1158
|
character,
|
|
1107
1159
|
emotion,
|
|
1108
1160
|
forward
|
|
1109
1161
|
);
|
|
1110
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
|
+
},
|
|
1111
1172
|
function({ ctx, push }, [fn]) {
|
|
1112
|
-
const
|
|
1173
|
+
const { restoring, goingBack, preview: preview2 } = ctx.meta;
|
|
1174
|
+
const result = fn({
|
|
1175
|
+
lang: storageData.get().meta[0],
|
|
1176
|
+
goingBack,
|
|
1177
|
+
restoring,
|
|
1178
|
+
preview: preview2,
|
|
1179
|
+
state: getStateFunction(ctx)
|
|
1180
|
+
});
|
|
1113
1181
|
if (!ctx.meta.restoring) {
|
|
1114
1182
|
result ? result.then(push) : push();
|
|
1115
1183
|
}
|
|
@@ -1121,22 +1189,26 @@ var novely = ({
|
|
|
1121
1189
|
choices.unshift(question);
|
|
1122
1190
|
question = "";
|
|
1123
1191
|
}
|
|
1124
|
-
const
|
|
1125
|
-
|
|
1192
|
+
const transformedChoices = choices.map(([content, action2, visible]) => {
|
|
1193
|
+
const shown = !visible || visible({
|
|
1194
|
+
lang: storageData.get().meta[0],
|
|
1195
|
+
state: getStateAtCtx(ctx)
|
|
1196
|
+
});
|
|
1197
|
+
if (DEV2 && action2.length === 0 && !shown) {
|
|
1126
1198
|
console.warn(`Choice children should not be empty, either add content there or make item not selectable`);
|
|
1127
1199
|
}
|
|
1128
|
-
return [
|
|
1200
|
+
return [templateReplace(content, data2), shown];
|
|
1129
1201
|
});
|
|
1130
|
-
if (DEV2 &&
|
|
1202
|
+
if (DEV2 && transformedChoices.length === 0) {
|
|
1131
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]`);
|
|
1132
1204
|
}
|
|
1133
|
-
ctx.choices(
|
|
1205
|
+
ctx.choices(templateReplace(question, data2), transformedChoices, (selected) => {
|
|
1134
1206
|
if (!ctx.meta.preview) {
|
|
1135
1207
|
enmemory(ctx);
|
|
1136
1208
|
}
|
|
1137
1209
|
const stack = useStack(ctx);
|
|
1138
1210
|
const offset = isWithoutQuestion ? 0 : 1;
|
|
1139
|
-
if (DEV2 && !
|
|
1211
|
+
if (DEV2 && !transformedChoices[selected]) {
|
|
1140
1212
|
throw new Error("Choice children is empty, either add content there or make item not selectable");
|
|
1141
1213
|
}
|
|
1142
1214
|
stack.value[0].push(["choice", selected + offset], [null, 0]);
|
|
@@ -1175,7 +1247,7 @@ var novely = ({
|
|
|
1175
1247
|
throw new Error(`Attempt to use Condition action with empty variants object`);
|
|
1176
1248
|
}
|
|
1177
1249
|
if (!ctx.meta.restoring) {
|
|
1178
|
-
const val = String(condition());
|
|
1250
|
+
const val = String(condition(getStateAtCtx(ctx)));
|
|
1179
1251
|
if (DEV2 && !variants[val]) {
|
|
1180
1252
|
throw new Error(`Attempt to go to unknown variant "${val}"`);
|
|
1181
1253
|
}
|
|
@@ -1198,7 +1270,7 @@ var novely = ({
|
|
|
1198
1270
|
},
|
|
1199
1271
|
input({ ctx, data: data2, forward }, [question, onInput, setup]) {
|
|
1200
1272
|
ctx.input(
|
|
1201
|
-
|
|
1273
|
+
templateReplace(question, data2),
|
|
1202
1274
|
onInput,
|
|
1203
1275
|
setup || noop,
|
|
1204
1276
|
forward
|
|
@@ -1236,7 +1308,7 @@ var novely = ({
|
|
|
1236
1308
|
push();
|
|
1237
1309
|
},
|
|
1238
1310
|
text({ ctx, data: data2, forward }, text) {
|
|
1239
|
-
const string = text.map((content) =>
|
|
1311
|
+
const string = text.map((content) => templateReplace(content, data2)).join(" ");
|
|
1240
1312
|
if (DEV2 && string.length === 0) {
|
|
1241
1313
|
throw new Error(`Action Text was called with empty string or array`);
|
|
1242
1314
|
}
|
|
@@ -1328,52 +1400,106 @@ var novely = ({
|
|
|
1328
1400
|
const interactivity = (value = false) => {
|
|
1329
1401
|
interacted = value ? interacted + 1 : 0;
|
|
1330
1402
|
};
|
|
1331
|
-
const
|
|
1403
|
+
const templateReplace = (content, values) => {
|
|
1332
1404
|
const {
|
|
1333
1405
|
data: data2,
|
|
1334
1406
|
meta: [lang]
|
|
1335
|
-
} =
|
|
1336
|
-
const obj = values
|
|
1337
|
-
const cnt = isFunction(content) ? content() : typeof content === "string" ? content : content[lang];
|
|
1338
|
-
const str2 =
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1407
|
+
} = storageData.get();
|
|
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
|
+
);
|
|
1344
1423
|
};
|
|
1345
|
-
|
|
1424
|
+
const data = (value) => {
|
|
1425
|
+
const _data = storageData.get().data;
|
|
1346
1426
|
if (!value)
|
|
1347
|
-
return
|
|
1348
|
-
const
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
return prev2;
|
|
1427
|
+
return _data;
|
|
1428
|
+
const val = isFunction(value) ? value(_data) : deepmerge(_data, value);
|
|
1429
|
+
storageData.update((prev) => {
|
|
1430
|
+
prev.data = val;
|
|
1431
|
+
return prev;
|
|
1353
1432
|
});
|
|
1354
|
-
|
|
1433
|
+
return void 0;
|
|
1434
|
+
};
|
|
1355
1435
|
return {
|
|
1356
1436
|
/**
|
|
1357
1437
|
* Function to set game script
|
|
1438
|
+
*
|
|
1439
|
+
* @example
|
|
1440
|
+
* ```ts
|
|
1441
|
+
* engine.script({
|
|
1442
|
+
* start: [
|
|
1443
|
+
* action.function(() => {})
|
|
1444
|
+
* ]
|
|
1445
|
+
* })
|
|
1446
|
+
* ```
|
|
1358
1447
|
*/
|
|
1359
1448
|
script,
|
|
1360
1449
|
/**
|
|
1361
|
-
*
|
|
1450
|
+
* Get actions
|
|
1451
|
+
*
|
|
1452
|
+
* @example
|
|
1453
|
+
* ```ts
|
|
1454
|
+
* engine.script({
|
|
1455
|
+
* start: [
|
|
1456
|
+
* action.function(() => {})
|
|
1457
|
+
* ]
|
|
1458
|
+
* })
|
|
1459
|
+
* ```
|
|
1362
1460
|
*/
|
|
1363
1461
|
action,
|
|
1364
1462
|
/**
|
|
1365
|
-
*
|
|
1463
|
+
* @deprecated Will be removed BUT replaced with state passed into actions as a parameter
|
|
1366
1464
|
*/
|
|
1367
|
-
state,
|
|
1465
|
+
state: getStateFunction(MAIN_CONTEXT_KEY),
|
|
1368
1466
|
/**
|
|
1369
|
-
*
|
|
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
|
+
* ```
|
|
1370
1481
|
*/
|
|
1371
1482
|
data,
|
|
1372
1483
|
/**
|
|
1373
|
-
*
|
|
1484
|
+
* @deprecated Renamed into `templateReplace`
|
|
1374
1485
|
*/
|
|
1375
1486
|
unwrap(content) {
|
|
1376
|
-
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);
|
|
1377
1503
|
},
|
|
1378
1504
|
/**
|
|
1379
1505
|
* Cancel data loading, hide UI, ignore page change events
|