@novely/core 0.22.1 → 0.23.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 +24 -10
- package/dist/index.global.js +232 -119
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +245 -129
- package/dist/index.js.map +1 -1
- package/package.json +62 -62
package/dist/index.d.ts
CHANGED
|
@@ -214,6 +214,7 @@ interface CharacterHandle {
|
|
|
214
214
|
emotion: (emotion: string, render: boolean) => void;
|
|
215
215
|
append: (className?: string, style?: string, restoring?: boolean) => void;
|
|
216
216
|
remove: (className?: string, style?: string, duration?: number, restoring?: boolean) => Promise<void>;
|
|
217
|
+
animate: (timeout: number, classes: string[]) => void;
|
|
217
218
|
emotions: Record<string, HTMLImageElement[]>;
|
|
218
219
|
}
|
|
219
220
|
type AudioHandle = {
|
|
@@ -223,23 +224,23 @@ type AudioHandle = {
|
|
|
223
224
|
};
|
|
224
225
|
type Renderer = {
|
|
225
226
|
misc: {
|
|
226
|
-
/**
|
|
227
|
-
* Function to preload images async and await for all images to load or fail
|
|
228
|
-
* @param images Set of images to load
|
|
229
|
-
*/
|
|
230
|
-
preloadImagesBlocking: (images: Set<string>) => Promise<PromiseSettledResult<unknown>[]>;
|
|
231
227
|
/**
|
|
232
228
|
* Function to preload image sync
|
|
233
229
|
* @param image Image URL
|
|
234
230
|
* @returns Image URL
|
|
235
231
|
*/
|
|
236
232
|
preloadImage: <T extends string>(image: T) => T;
|
|
233
|
+
/**
|
|
234
|
+
* Function to preload image async
|
|
235
|
+
* @param image Image URL
|
|
236
|
+
* @returns Promise
|
|
237
|
+
*/
|
|
238
|
+
preloadImageBlocking: (image: string) => Promise<void>;
|
|
237
239
|
/**
|
|
238
240
|
* Function to preload audio
|
|
239
|
-
* @param type kind of audio
|
|
240
241
|
* @param source <url> pointing to the audio
|
|
241
242
|
*/
|
|
242
|
-
preloadAudioBlocking: (
|
|
243
|
+
preloadAudioBlocking: (source: string) => Promise<void>;
|
|
243
244
|
};
|
|
244
245
|
ui: {
|
|
245
246
|
/**
|
|
@@ -308,7 +309,6 @@ type Renderer = {
|
|
|
308
309
|
};
|
|
309
310
|
store: unknown;
|
|
310
311
|
setStore: unknown;
|
|
311
|
-
getCharacter: (character: string) => CharacterHandle | undefined;
|
|
312
312
|
};
|
|
313
313
|
removeContext: (context: string) => void;
|
|
314
314
|
};
|
|
@@ -418,6 +418,11 @@ interface NovelyInit<Languages extends string, Characters extends Record<string,
|
|
|
418
418
|
* @default 799
|
|
419
419
|
*/
|
|
420
420
|
throttleTimeout?: number;
|
|
421
|
+
/**
|
|
422
|
+
* Limits how many assets can be downloaded parallelly
|
|
423
|
+
* @default 15
|
|
424
|
+
*/
|
|
425
|
+
parallelAssetsDownloadLimit?: number;
|
|
421
426
|
/**
|
|
422
427
|
* Custom language detector
|
|
423
428
|
* @param languages Supported languages aka `languages: []` in the config
|
|
@@ -445,8 +450,12 @@ interface NovelyInit<Languages extends string, Characters extends Record<string,
|
|
|
445
450
|
* @default "lazy"
|
|
446
451
|
*/
|
|
447
452
|
preloadAssets?: 'lazy' | 'blocking';
|
|
453
|
+
/**
|
|
454
|
+
* Fetching function
|
|
455
|
+
*/
|
|
456
|
+
fetch?: typeof fetch;
|
|
448
457
|
}
|
|
449
|
-
declare const novely: <Languages extends string, Characters extends Record<string, Character<Languages>>, StateScheme extends State, DataScheme extends Data>({ characters, storage, storageDelay, renderer: createRenderer, initialScreen, translation, languages, state: defaultState, data: defaultData, autosaves, migrations, throttleTimeout, getLanguage, overrideLanguage, askBeforeExit, preloadAssets, }: NovelyInit<Languages, Characters, StateScheme, DataScheme>) => {
|
|
458
|
+
declare const novely: <Languages extends string, Characters extends Record<string, Character<Languages>>, StateScheme extends State, DataScheme extends Data>({ characters, storage, storageDelay, renderer: createRenderer, initialScreen, translation, languages, state: defaultState, data: defaultData, autosaves, migrations, throttleTimeout, getLanguage, overrideLanguage, askBeforeExit, preloadAssets, parallelAssetsDownloadLimit, fetch: request }: NovelyInit<Languages, Characters, StateScheme, DataScheme>) => {
|
|
450
459
|
/**
|
|
451
460
|
* Function to set game script
|
|
452
461
|
*/
|
|
@@ -472,7 +481,12 @@ declare const novely: <Languages extends string, Characters extends Record<strin
|
|
|
472
481
|
/**
|
|
473
482
|
* Unwraps translatable content to a string value
|
|
474
483
|
*/
|
|
475
|
-
unwrap(content: string | (() => string) |
|
|
484
|
+
unwrap(content: string | (() => string) | Exclude<Record<Languages, string | (() => string)>, Record<string, string>> | Record<Languages, string>): string;
|
|
485
|
+
/**
|
|
486
|
+
* Cancel data loading, hide UI, ignore page change events
|
|
487
|
+
* Data updates still will work in case Novely already was loaded
|
|
488
|
+
*/
|
|
489
|
+
destroy(): void;
|
|
476
490
|
};
|
|
477
491
|
|
|
478
492
|
export { type ActionProxyProvider, type AllowedContent, type AudioHandle, type BaseTranslationStrings, type Character, type CharacterHandle, type Context, type CoreData, type CustomHandler, type CustomHandlerFunctionGetFn, type CustomHandlerFunctionParameters, type CustomHandlerGetResult, type CustomHandlerGetResultDataFunction, type DefaultActionProxyProvider, EN, type Emotions, type FunctionableValue, type GetActionParameters, JP, KK, type Lang, type NovelyScreen, type Path, type PluralType, type Pluralization, RU, type Renderer, type RendererInit, type Save, type Storage, type StorageData, type StorageMeta, type Stored, type Story, type Thenable, type TranslationActions, type TypewriterSpeed, type Unwrappable, type ValidAction, localStorageStorage, novely };
|
package/dist/index.global.js
CHANGED
|
@@ -174,18 +174,62 @@ var Novely = (() => {
|
|
|
174
174
|
]);
|
|
175
175
|
var EMPTY_SET = /* @__PURE__ */ new Set();
|
|
176
176
|
var DEFAULT_TYPEWRITER_SPEED = "Medium";
|
|
177
|
+
var HOWLER_SUPPORTED_FILE_FORMATS = /* @__PURE__ */ new Set([
|
|
178
|
+
"mp3",
|
|
179
|
+
"mpeg",
|
|
180
|
+
"opus",
|
|
181
|
+
"ogg",
|
|
182
|
+
"oga",
|
|
183
|
+
"wav",
|
|
184
|
+
"aac",
|
|
185
|
+
"caf",
|
|
186
|
+
"m4a",
|
|
187
|
+
"m4b",
|
|
188
|
+
"mp4",
|
|
189
|
+
"weba",
|
|
190
|
+
"webm",
|
|
191
|
+
"dolby",
|
|
192
|
+
"flac"
|
|
193
|
+
]);
|
|
194
|
+
var SUPPORTED_IMAGE_FILE_FORMATS = /* @__PURE__ */ new Set([
|
|
195
|
+
"apng",
|
|
196
|
+
"avif",
|
|
197
|
+
"gif",
|
|
198
|
+
"jpg",
|
|
199
|
+
"jpeg",
|
|
200
|
+
"jfif",
|
|
201
|
+
"pjpeg",
|
|
202
|
+
"pjp",
|
|
203
|
+
"png",
|
|
204
|
+
"svg",
|
|
205
|
+
"webp",
|
|
206
|
+
"bmp"
|
|
207
|
+
]);
|
|
177
208
|
var MAIN_CONTEXT_KEY = "$MAIN";
|
|
178
209
|
|
|
179
210
|
// src/shared.ts
|
|
180
211
|
var STACK_MAP = /* @__PURE__ */ new Map();
|
|
181
212
|
|
|
213
|
+
// ../../node_modules/.pnpm/esm-env@1.0.0/node_modules/esm-env/prod-ssr.js
|
|
214
|
+
var DEV = false;
|
|
215
|
+
|
|
182
216
|
// src/utils.ts
|
|
183
|
-
var matchAction = (getContext, values) => {
|
|
217
|
+
var matchAction = ({ getContext, push, forward }, values) => {
|
|
184
218
|
return (action, props, { ctx, data }) => {
|
|
185
219
|
const context = typeof ctx === "string" ? getContext(ctx) : ctx;
|
|
186
220
|
return values[action]({
|
|
187
221
|
ctx: context,
|
|
188
|
-
data
|
|
222
|
+
data,
|
|
223
|
+
push() {
|
|
224
|
+
if (context.meta.preview)
|
|
225
|
+
return;
|
|
226
|
+
push(context);
|
|
227
|
+
},
|
|
228
|
+
forward() {
|
|
229
|
+
if (context.meta.preview)
|
|
230
|
+
return;
|
|
231
|
+
forward(context);
|
|
232
|
+
}
|
|
189
233
|
}, props);
|
|
190
234
|
};
|
|
191
235
|
};
|
|
@@ -292,6 +336,9 @@ var Novely = (() => {
|
|
|
292
336
|
var isSkippedDuringRestore = (item) => {
|
|
293
337
|
return SKIPPED_DURING_RESTORE.has(item);
|
|
294
338
|
};
|
|
339
|
+
var isAudioAction = (action) => {
|
|
340
|
+
return AUDIO_ACTIONS.has(action);
|
|
341
|
+
};
|
|
295
342
|
var noop = () => {
|
|
296
343
|
};
|
|
297
344
|
var isAction = (element) => {
|
|
@@ -514,6 +561,53 @@ var Novely = (() => {
|
|
|
514
561
|
};
|
|
515
562
|
return useStack;
|
|
516
563
|
};
|
|
564
|
+
var mapSet = (set, fn) => {
|
|
565
|
+
return [...set].map(fn);
|
|
566
|
+
};
|
|
567
|
+
var isImageAsset = (asset) => {
|
|
568
|
+
return isString(asset) && isCSSImage(asset);
|
|
569
|
+
};
|
|
570
|
+
var getUrlFileExtension = (address) => {
|
|
571
|
+
try {
|
|
572
|
+
const { pathname } = new URL(address, location.href);
|
|
573
|
+
return pathname.split(".").at(-1).split("!")[0].split(":")[0];
|
|
574
|
+
} catch (error) {
|
|
575
|
+
if (DEV) {
|
|
576
|
+
console.error(new Error(`Could not construct URL "${address}".`, { cause: error }));
|
|
577
|
+
}
|
|
578
|
+
return "";
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
var fetchContentType = async (request, url) => {
|
|
582
|
+
try {
|
|
583
|
+
const response = await request(url, {
|
|
584
|
+
method: "HEAD"
|
|
585
|
+
});
|
|
586
|
+
return response.headers.get("Content-Type") || "";
|
|
587
|
+
} catch (error) {
|
|
588
|
+
if (DEV) {
|
|
589
|
+
console.error(new Error(`Failed to fetch file at "${url}"`, { cause: error }));
|
|
590
|
+
}
|
|
591
|
+
return "";
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
var getResourseType = async (request, url) => {
|
|
595
|
+
const extension = getUrlFileExtension(url);
|
|
596
|
+
if (HOWLER_SUPPORTED_FILE_FORMATS.has(extension)) {
|
|
597
|
+
return "audio";
|
|
598
|
+
}
|
|
599
|
+
if (SUPPORTED_IMAGE_FILE_FORMATS.has(extension)) {
|
|
600
|
+
return "image";
|
|
601
|
+
}
|
|
602
|
+
const contentType = await fetchContentType(request, url);
|
|
603
|
+
if (contentType.includes("audio")) {
|
|
604
|
+
return "audio";
|
|
605
|
+
}
|
|
606
|
+
if (contentType.includes("image")) {
|
|
607
|
+
return "image";
|
|
608
|
+
}
|
|
609
|
+
return "other";
|
|
610
|
+
};
|
|
517
611
|
|
|
518
612
|
// src/global.ts
|
|
519
613
|
var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
|
|
@@ -709,11 +803,6 @@ var Novely = (() => {
|
|
|
709
803
|
|
|
710
804
|
// src/novely.ts
|
|
711
805
|
var import_p_limit = __toESM(require_p_limit(), 1);
|
|
712
|
-
|
|
713
|
-
// ../../node_modules/.pnpm/esm-env@1.0.0/node_modules/esm-env/prod-ssr.js
|
|
714
|
-
var DEV = false;
|
|
715
|
-
|
|
716
|
-
// src/novely.ts
|
|
717
806
|
var novely = ({
|
|
718
807
|
characters,
|
|
719
808
|
storage = localStorageStorage({ key: "novely-game-storage" }),
|
|
@@ -730,9 +819,12 @@ var Novely = (() => {
|
|
|
730
819
|
getLanguage: getLanguage2 = getLanguage,
|
|
731
820
|
overrideLanguage = false,
|
|
732
821
|
askBeforeExit = true,
|
|
733
|
-
preloadAssets = "lazy"
|
|
822
|
+
preloadAssets = "lazy",
|
|
823
|
+
parallelAssetsDownloadLimit = 15,
|
|
824
|
+
fetch: request = fetch
|
|
734
825
|
}) => {
|
|
735
826
|
const limitScript = (0, import_p_limit.default)(1);
|
|
827
|
+
const limitAssetsDownload = (0, import_p_limit.default)(parallelAssetsDownloadLimit);
|
|
736
828
|
const story = {};
|
|
737
829
|
const times = /* @__PURE__ */ new Set();
|
|
738
830
|
const ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
|
|
@@ -748,7 +840,23 @@ var Novely = (() => {
|
|
|
748
840
|
Object.assign(story, flattenStory(part));
|
|
749
841
|
if (preloadAssets === "blocking" && ASSETS_TO_PRELOAD.size > 0) {
|
|
750
842
|
renderer.ui.showScreen("loading");
|
|
751
|
-
|
|
843
|
+
const { preloadAudioBlocking, preloadImageBlocking } = renderer.misc;
|
|
844
|
+
const list = mapSet(ASSETS_TO_PRELOAD, (asset) => {
|
|
845
|
+
return limitAssetsDownload(async () => {
|
|
846
|
+
const type = await getResourseType(request, asset);
|
|
847
|
+
switch (type) {
|
|
848
|
+
case "audio": {
|
|
849
|
+
await preloadAudioBlocking(asset);
|
|
850
|
+
break;
|
|
851
|
+
}
|
|
852
|
+
case "image": {
|
|
853
|
+
await preloadImageBlocking(asset);
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
});
|
|
859
|
+
await Promise.allSettled(list);
|
|
752
860
|
}
|
|
753
861
|
const screen = renderer.ui.getScreen();
|
|
754
862
|
const nextScreen = scriptCalled ? screen : initialScreen;
|
|
@@ -769,13 +877,25 @@ var Novely = (() => {
|
|
|
769
877
|
return limitScript(() => scriptBase(part));
|
|
770
878
|
};
|
|
771
879
|
const action = new Proxy({}, {
|
|
772
|
-
get(_,
|
|
880
|
+
get(_, action2) {
|
|
773
881
|
return (...props) => {
|
|
774
882
|
if (preloadAssets === "blocking") {
|
|
775
|
-
if (
|
|
883
|
+
if (action2 === "showBackground") {
|
|
884
|
+
if (isImageAsset(props[0])) {
|
|
885
|
+
ASSETS_TO_PRELOAD.add(props[0]);
|
|
886
|
+
}
|
|
887
|
+
if (props[0] && typeof props[0] === "object") {
|
|
888
|
+
for (const value of Object.values(props[0])) {
|
|
889
|
+
if (!isImageAsset(value))
|
|
890
|
+
continue;
|
|
891
|
+
ASSETS_TO_PRELOAD.add(value);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
if (isAudioAction(action2) && isString(props[0])) {
|
|
776
896
|
ASSETS_TO_PRELOAD.add(props[0]);
|
|
777
897
|
}
|
|
778
|
-
if (
|
|
898
|
+
if (action2 === "showCharacter" && isString(props[0]) && isString(props[1])) {
|
|
779
899
|
const images = characters[props[0]].emotions[props[1]];
|
|
780
900
|
if (Array.isArray(images)) {
|
|
781
901
|
for (const asset of images) {
|
|
@@ -786,7 +906,7 @@ var Novely = (() => {
|
|
|
786
906
|
}
|
|
787
907
|
}
|
|
788
908
|
}
|
|
789
|
-
return [
|
|
909
|
+
return [action2, ...props];
|
|
790
910
|
};
|
|
791
911
|
}
|
|
792
912
|
});
|
|
@@ -853,11 +973,12 @@ var Novely = (() => {
|
|
|
853
973
|
};
|
|
854
974
|
storageDelay.then(getStoredData);
|
|
855
975
|
const initial = getDefaultSave(klona(defaultState));
|
|
856
|
-
|
|
976
|
+
const onVisibilityChange = () => {
|
|
857
977
|
if (document.visibilityState === "hidden") {
|
|
858
978
|
throttledEmergencyOnStorageDataChange();
|
|
859
979
|
}
|
|
860
|
-
}
|
|
980
|
+
};
|
|
981
|
+
addEventListener("visibilitychange", onVisibilityChange);
|
|
861
982
|
addEventListener("beforeunload", throttledEmergencyOnStorageDataChange);
|
|
862
983
|
const save = (override = false, type = override ? "auto" : "manual") => {
|
|
863
984
|
if (!$$.get().dataLoaded)
|
|
@@ -905,12 +1026,11 @@ var Novely = (() => {
|
|
|
905
1026
|
return;
|
|
906
1027
|
let latest = save2 || $.get().saves.at(-1);
|
|
907
1028
|
if (!latest) {
|
|
908
|
-
$.set({
|
|
909
|
-
saves: [initial],
|
|
910
|
-
data: klona(defaultData),
|
|
911
|
-
meta: [getLanguageWithoutParameters(), DEFAULT_TYPEWRITER_SPEED, 1, 1, 1]
|
|
912
|
-
});
|
|
913
1029
|
latest = klona(initial);
|
|
1030
|
+
$.update((prev) => {
|
|
1031
|
+
prev.saves.push(latest);
|
|
1032
|
+
return prev;
|
|
1033
|
+
});
|
|
914
1034
|
}
|
|
915
1035
|
const context = renderer.getContext(MAIN_CONTEXT_KEY);
|
|
916
1036
|
const stack = useStack(context);
|
|
@@ -946,7 +1066,7 @@ var Novely = (() => {
|
|
|
946
1066
|
data: latest[1]
|
|
947
1067
|
});
|
|
948
1068
|
});
|
|
949
|
-
context.meta.restoring = context.meta.
|
|
1069
|
+
context.meta.restoring = context.meta.goingBack = false;
|
|
950
1070
|
render(context);
|
|
951
1071
|
};
|
|
952
1072
|
const refer = (path) => {
|
|
@@ -1014,7 +1134,7 @@ var Novely = (() => {
|
|
|
1014
1134
|
ctx.meta.preview = true;
|
|
1015
1135
|
const processor = createQueueProcessor(queue);
|
|
1016
1136
|
await processor.run((action2, props) => {
|
|
1017
|
-
if (
|
|
1137
|
+
if (isAudioAction(action2))
|
|
1018
1138
|
return;
|
|
1019
1139
|
if (action2 === "vibrate")
|
|
1020
1140
|
return;
|
|
@@ -1047,55 +1167,89 @@ var Novely = (() => {
|
|
|
1047
1167
|
});
|
|
1048
1168
|
const useStack = createUseStackFunction(renderer);
|
|
1049
1169
|
useStack(MAIN_CONTEXT_KEY).push(initial);
|
|
1050
|
-
renderer.ui.start();
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1170
|
+
const UIInstance = renderer.ui.start();
|
|
1171
|
+
const enmemory = (ctx) => {
|
|
1172
|
+
if (ctx.meta.restoring)
|
|
1173
|
+
return;
|
|
1174
|
+
const stack = useStack(ctx);
|
|
1175
|
+
const current = klona(stack.value);
|
|
1176
|
+
current[2][1] = "auto";
|
|
1177
|
+
stack.push(current);
|
|
1178
|
+
save(true, "auto");
|
|
1179
|
+
};
|
|
1180
|
+
const next = (ctx) => {
|
|
1181
|
+
const stack = useStack(ctx);
|
|
1182
|
+
const path = stack.value[0];
|
|
1183
|
+
const last = path.at(-1);
|
|
1184
|
+
if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
|
|
1185
|
+
last[1]++;
|
|
1186
|
+
} else {
|
|
1187
|
+
path.push([null, 0]);
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
1190
|
+
const matchActionInit = {
|
|
1191
|
+
getContext: renderer.getContext,
|
|
1192
|
+
push(ctx) {
|
|
1193
|
+
if (ctx.meta.restoring)
|
|
1194
|
+
return;
|
|
1195
|
+
next(ctx);
|
|
1196
|
+
render(ctx);
|
|
1197
|
+
},
|
|
1198
|
+
forward(ctx) {
|
|
1199
|
+
if (!ctx.meta.preview)
|
|
1200
|
+
enmemory(ctx);
|
|
1201
|
+
matchActionInit.push(ctx);
|
|
1202
|
+
if (!ctx.meta.preview)
|
|
1203
|
+
interactivity(true);
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
const match = matchAction(matchActionInit, {
|
|
1207
|
+
wait({ ctx, push }, [time]) {
|
|
1208
|
+
if (ctx.meta.restoring)
|
|
1209
|
+
return;
|
|
1210
|
+
setTimeout(push, isFunction(time) ? time() : time);
|
|
1055
1211
|
},
|
|
1056
|
-
showBackground({ ctx }, [background]) {
|
|
1212
|
+
showBackground({ ctx, push }, [background]) {
|
|
1057
1213
|
ctx.background(background);
|
|
1058
|
-
push(
|
|
1214
|
+
push();
|
|
1059
1215
|
},
|
|
1060
|
-
playMusic({ ctx }, [source]) {
|
|
1216
|
+
playMusic({ ctx, push }, [source]) {
|
|
1061
1217
|
ctx.audio.music(source, "music", true).play();
|
|
1062
|
-
push(
|
|
1218
|
+
push();
|
|
1063
1219
|
},
|
|
1064
|
-
stopMusic({ ctx }, [source]) {
|
|
1220
|
+
stopMusic({ ctx, push }, [source]) {
|
|
1065
1221
|
ctx.audio.music(source, "music").stop();
|
|
1066
|
-
push(
|
|
1222
|
+
push();
|
|
1067
1223
|
},
|
|
1068
|
-
playSound({ ctx }, [source, loop]) {
|
|
1224
|
+
playSound({ ctx, push }, [source, loop]) {
|
|
1069
1225
|
ctx.audio.music(source, "sound", loop || false).play();
|
|
1070
|
-
push(
|
|
1226
|
+
push();
|
|
1071
1227
|
},
|
|
1072
|
-
stopSound({ ctx }, [source]) {
|
|
1228
|
+
stopSound({ ctx, push }, [source]) {
|
|
1073
1229
|
ctx.audio.music(source, "sound").stop();
|
|
1074
|
-
push(
|
|
1230
|
+
push();
|
|
1075
1231
|
},
|
|
1076
|
-
voice({ ctx }, [source]) {
|
|
1232
|
+
voice({ ctx, push }, [source]) {
|
|
1077
1233
|
ctx.audio.voice(source);
|
|
1078
|
-
push(
|
|
1234
|
+
push();
|
|
1079
1235
|
},
|
|
1080
|
-
stopVoice({ ctx }) {
|
|
1236
|
+
stopVoice({ ctx, push }) {
|
|
1081
1237
|
ctx.audio.voiceStop();
|
|
1082
|
-
push(
|
|
1238
|
+
push();
|
|
1083
1239
|
},
|
|
1084
|
-
showCharacter({ ctx }, [character, emotion, className, style]) {
|
|
1240
|
+
showCharacter({ ctx, push }, [character, emotion, className, style]) {
|
|
1085
1241
|
if (DEV && !characters[character].emotions[emotion]) {
|
|
1086
1242
|
throw new Error(`Attempt to show character "${character}" with unknown emotion "${emotion}"`);
|
|
1087
1243
|
}
|
|
1088
1244
|
const handle = ctx.character(character);
|
|
1089
1245
|
handle.append(className, style, ctx.meta.restoring);
|
|
1090
1246
|
handle.emotion(emotion, true);
|
|
1091
|
-
push(
|
|
1247
|
+
push();
|
|
1092
1248
|
},
|
|
1093
|
-
hideCharacter({ ctx }, [character, className, style, duration]) {
|
|
1094
|
-
ctx.character(character).remove(className, style, duration, ctx.meta.restoring).then(
|
|
1095
|
-
push(ctx);
|
|
1096
|
-
});
|
|
1249
|
+
hideCharacter({ ctx, push }, [character, className, style, duration]) {
|
|
1250
|
+
ctx.character(character).remove(className, style, duration, ctx.meta.restoring).then(push);
|
|
1097
1251
|
},
|
|
1098
|
-
dialog({ ctx, data: data2 }, [character, content, emotion]) {
|
|
1252
|
+
dialog({ ctx, data: data2, forward }, [character, content, emotion]) {
|
|
1099
1253
|
const name = (() => {
|
|
1100
1254
|
const c = character;
|
|
1101
1255
|
const cs = characters;
|
|
@@ -1116,13 +1270,13 @@ var Novely = (() => {
|
|
|
1116
1270
|
unwrap2(name, data2),
|
|
1117
1271
|
character,
|
|
1118
1272
|
emotion,
|
|
1119
|
-
|
|
1273
|
+
forward
|
|
1120
1274
|
);
|
|
1121
1275
|
},
|
|
1122
|
-
function({ ctx }, [fn]) {
|
|
1276
|
+
function({ ctx, push }, [fn]) {
|
|
1123
1277
|
const result = fn(ctx.meta.restoring, ctx.meta.goingBack, ctx.meta.preview);
|
|
1124
1278
|
if (!ctx.meta.restoring) {
|
|
1125
|
-
result ? result.then(
|
|
1279
|
+
result ? result.then(push) : push();
|
|
1126
1280
|
}
|
|
1127
1281
|
return result;
|
|
1128
1282
|
},
|
|
@@ -1172,13 +1326,13 @@ var Novely = (() => {
|
|
|
1172
1326
|
data: data2
|
|
1173
1327
|
});
|
|
1174
1328
|
},
|
|
1175
|
-
clear({ ctx }, [keep, characters2, audio]) {
|
|
1329
|
+
clear({ ctx, push }, [keep, characters2, audio]) {
|
|
1176
1330
|
ctx.vibrate(0);
|
|
1177
1331
|
ctx.clear(
|
|
1178
1332
|
keep || EMPTY_SET,
|
|
1179
1333
|
characters2 || EMPTY_SET,
|
|
1180
1334
|
audio || { music: EMPTY_SET, sounds: EMPTY_SET },
|
|
1181
|
-
|
|
1335
|
+
push
|
|
1182
1336
|
);
|
|
1183
1337
|
},
|
|
1184
1338
|
condition({ ctx }, [condition, variants]) {
|
|
@@ -1207,15 +1361,15 @@ var Novely = (() => {
|
|
|
1207
1361
|
interactivity(false);
|
|
1208
1362
|
times.clear();
|
|
1209
1363
|
},
|
|
1210
|
-
input({ ctx, data: data2 }, [question, onInput, setup]) {
|
|
1364
|
+
input({ ctx, data: data2, forward }, [question, onInput, setup]) {
|
|
1211
1365
|
ctx.input(
|
|
1212
1366
|
unwrap2(question, data2),
|
|
1213
1367
|
onInput,
|
|
1214
1368
|
setup || noop,
|
|
1215
|
-
|
|
1369
|
+
forward
|
|
1216
1370
|
);
|
|
1217
1371
|
},
|
|
1218
|
-
custom({ ctx }, [handler]) {
|
|
1372
|
+
custom({ ctx, push }, [handler]) {
|
|
1219
1373
|
const result = ctx.custom(handler, () => {
|
|
1220
1374
|
if (ctx.meta.restoring)
|
|
1221
1375
|
return;
|
|
@@ -1223,18 +1377,18 @@ var Novely = (() => {
|
|
|
1223
1377
|
enmemory(ctx);
|
|
1224
1378
|
interactivity(true);
|
|
1225
1379
|
}
|
|
1226
|
-
push(
|
|
1380
|
+
push();
|
|
1227
1381
|
});
|
|
1228
1382
|
return result;
|
|
1229
1383
|
},
|
|
1230
|
-
vibrate({ ctx }, pattern) {
|
|
1384
|
+
vibrate({ ctx, push }, pattern) {
|
|
1231
1385
|
ctx.vibrate(pattern);
|
|
1232
|
-
push(
|
|
1386
|
+
push();
|
|
1233
1387
|
},
|
|
1234
|
-
next({
|
|
1235
|
-
push(
|
|
1388
|
+
next({ push }) {
|
|
1389
|
+
push();
|
|
1236
1390
|
},
|
|
1237
|
-
animateCharacter({ ctx,
|
|
1391
|
+
animateCharacter({ ctx, push }, [character, timeout, ...classes]) {
|
|
1238
1392
|
if (DEV && classes.length === 0) {
|
|
1239
1393
|
throw new Error("Attempt to use AnimateCharacter without classes. Classes should be provided [https://novely.pages.dev/guide/actions/animateCharacter.html]");
|
|
1240
1394
|
}
|
|
@@ -1243,36 +1397,15 @@ var Novely = (() => {
|
|
|
1243
1397
|
}
|
|
1244
1398
|
if (ctx.meta.preview)
|
|
1245
1399
|
return;
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
const char = ctx.getCharacter(character);
|
|
1249
|
-
if (!char)
|
|
1250
|
-
return;
|
|
1251
|
-
const target = char.canvas;
|
|
1252
|
-
if (!target)
|
|
1253
|
-
return;
|
|
1254
|
-
const classNames = classes.filter((className) => !target.classList.contains(className));
|
|
1255
|
-
target.classList.add(...classNames);
|
|
1256
|
-
const timeoutId = setTimeout(() => {
|
|
1257
|
-
target.classList.remove(...classNames);
|
|
1258
|
-
}, timeout);
|
|
1259
|
-
clear(() => {
|
|
1260
|
-
target.classList.remove(...classNames);
|
|
1261
|
-
clearTimeout(timeoutId);
|
|
1262
|
-
});
|
|
1263
|
-
};
|
|
1264
|
-
handler.key = "@@internal-animate-character";
|
|
1265
|
-
match("custom", [handler], {
|
|
1266
|
-
ctx,
|
|
1267
|
-
data: data2
|
|
1268
|
-
});
|
|
1400
|
+
ctx.character(character).animate(timeout, classes);
|
|
1401
|
+
push();
|
|
1269
1402
|
},
|
|
1270
|
-
text({ ctx, data: data2 }, text) {
|
|
1403
|
+
text({ ctx, data: data2, forward }, text) {
|
|
1271
1404
|
const string = text.map((content) => unwrap2(content, data2)).join(" ");
|
|
1272
1405
|
if (DEV && string.length === 0) {
|
|
1273
1406
|
throw new Error(`Action Text was called with empty string or array`);
|
|
1274
1407
|
}
|
|
1275
|
-
ctx.text(string,
|
|
1408
|
+
ctx.text(string, forward);
|
|
1276
1409
|
},
|
|
1277
1410
|
exit({ ctx, data: data2 }) {
|
|
1278
1411
|
if (ctx.meta.restoring)
|
|
@@ -1321,11 +1454,11 @@ var Novely = (() => {
|
|
|
1321
1454
|
}
|
|
1322
1455
|
render(ctx);
|
|
1323
1456
|
},
|
|
1324
|
-
preload({ ctx }, [source]) {
|
|
1457
|
+
preload({ ctx, push }, [source]) {
|
|
1325
1458
|
if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
|
|
1326
1459
|
PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
|
|
1327
1460
|
}
|
|
1328
|
-
push(
|
|
1461
|
+
push();
|
|
1329
1462
|
},
|
|
1330
1463
|
block({ ctx }, [scene]) {
|
|
1331
1464
|
if (DEV && !story[scene]) {
|
|
@@ -1341,25 +1474,6 @@ var Novely = (() => {
|
|
|
1341
1474
|
}
|
|
1342
1475
|
}
|
|
1343
1476
|
});
|
|
1344
|
-
const enmemory = (ctx) => {
|
|
1345
|
-
if (ctx.meta.restoring)
|
|
1346
|
-
return;
|
|
1347
|
-
const stack = useStack(ctx);
|
|
1348
|
-
const current = klona(stack.value);
|
|
1349
|
-
current[2][1] = "auto";
|
|
1350
|
-
stack.push(current);
|
|
1351
|
-
save(true, "auto");
|
|
1352
|
-
};
|
|
1353
|
-
const next = (ctx) => {
|
|
1354
|
-
const stack = useStack(ctx);
|
|
1355
|
-
const path = stack.value[0];
|
|
1356
|
-
const last = path.at(-1);
|
|
1357
|
-
if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
|
|
1358
|
-
last[1]++;
|
|
1359
|
-
} else {
|
|
1360
|
-
path.push([null, 0]);
|
|
1361
|
-
}
|
|
1362
|
-
};
|
|
1363
1477
|
const render = (ctx) => {
|
|
1364
1478
|
const stack = useStack(ctx);
|
|
1365
1479
|
const referred = refer(stack.value[0]);
|
|
@@ -1376,17 +1490,6 @@ var Novely = (() => {
|
|
|
1376
1490
|
});
|
|
1377
1491
|
}
|
|
1378
1492
|
};
|
|
1379
|
-
const push = (ctx) => {
|
|
1380
|
-
if (!ctx.meta.restoring)
|
|
1381
|
-
next(ctx), render(ctx);
|
|
1382
|
-
};
|
|
1383
|
-
const forward = (ctx) => {
|
|
1384
|
-
if (!ctx.meta.preview)
|
|
1385
|
-
enmemory(ctx);
|
|
1386
|
-
push(ctx);
|
|
1387
|
-
if (!ctx.meta.preview)
|
|
1388
|
-
interactivity(true);
|
|
1389
|
-
};
|
|
1390
1493
|
const interactivity = (value = false) => {
|
|
1391
1494
|
interacted = value ? interacted + 1 : 0;
|
|
1392
1495
|
};
|
|
@@ -1436,6 +1539,16 @@ var Novely = (() => {
|
|
|
1436
1539
|
*/
|
|
1437
1540
|
unwrap(content) {
|
|
1438
1541
|
return unwrap2(content, $.get().data);
|
|
1542
|
+
},
|
|
1543
|
+
/**
|
|
1544
|
+
* Cancel data loading, hide UI, ignore page change events
|
|
1545
|
+
* Data updates still will work in case Novely already was loaded
|
|
1546
|
+
*/
|
|
1547
|
+
destroy() {
|
|
1548
|
+
dataLoaded.cancel();
|
|
1549
|
+
UIInstance.unmount();
|
|
1550
|
+
removeEventListener("visibilitychange", onVisibilityChange);
|
|
1551
|
+
removeEventListener("beforeunload", throttledEmergencyOnStorageDataChange);
|
|
1439
1552
|
}
|
|
1440
1553
|
};
|
|
1441
1554
|
};
|