@novely/core 0.22.2 → 0.24.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 +245 -189
- package/dist/index.global.js +137 -48
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +150 -58
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.global.js
CHANGED
|
@@ -174,11 +174,45 @@ 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
217
|
var matchAction = ({ getContext, push, forward }, values) => {
|
|
184
218
|
return (action, props, { ctx, data }) => {
|
|
@@ -187,9 +221,13 @@ var Novely = (() => {
|
|
|
187
221
|
ctx: context,
|
|
188
222
|
data,
|
|
189
223
|
push() {
|
|
224
|
+
if (context.meta.preview)
|
|
225
|
+
return;
|
|
190
226
|
push(context);
|
|
191
227
|
},
|
|
192
228
|
forward() {
|
|
229
|
+
if (context.meta.preview)
|
|
230
|
+
return;
|
|
193
231
|
forward(context);
|
|
194
232
|
}
|
|
195
233
|
}, props);
|
|
@@ -298,6 +336,9 @@ var Novely = (() => {
|
|
|
298
336
|
var isSkippedDuringRestore = (item) => {
|
|
299
337
|
return SKIPPED_DURING_RESTORE.has(item);
|
|
300
338
|
};
|
|
339
|
+
var isAudioAction = (action) => {
|
|
340
|
+
return AUDIO_ACTIONS.has(action);
|
|
341
|
+
};
|
|
301
342
|
var noop = () => {
|
|
302
343
|
};
|
|
303
344
|
var isAction = (element) => {
|
|
@@ -520,6 +561,53 @@ var Novely = (() => {
|
|
|
520
561
|
};
|
|
521
562
|
return useStack;
|
|
522
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
|
+
};
|
|
523
611
|
|
|
524
612
|
// src/global.ts
|
|
525
613
|
var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
|
|
@@ -715,11 +803,6 @@ var Novely = (() => {
|
|
|
715
803
|
|
|
716
804
|
// src/novely.ts
|
|
717
805
|
var import_p_limit = __toESM(require_p_limit(), 1);
|
|
718
|
-
|
|
719
|
-
// ../../node_modules/.pnpm/esm-env@1.0.0/node_modules/esm-env/prod-ssr.js
|
|
720
|
-
var DEV = false;
|
|
721
|
-
|
|
722
|
-
// src/novely.ts
|
|
723
806
|
var novely = ({
|
|
724
807
|
characters,
|
|
725
808
|
storage = localStorageStorage({ key: "novely-game-storage" }),
|
|
@@ -727,7 +810,6 @@ var Novely = (() => {
|
|
|
727
810
|
renderer: createRenderer,
|
|
728
811
|
initialScreen = "mainmenu",
|
|
729
812
|
translation,
|
|
730
|
-
languages,
|
|
731
813
|
state: defaultState,
|
|
732
814
|
data: defaultData,
|
|
733
815
|
autosaves = true,
|
|
@@ -736,9 +818,13 @@ var Novely = (() => {
|
|
|
736
818
|
getLanguage: getLanguage2 = getLanguage,
|
|
737
819
|
overrideLanguage = false,
|
|
738
820
|
askBeforeExit = true,
|
|
739
|
-
preloadAssets = "lazy"
|
|
821
|
+
preloadAssets = "lazy",
|
|
822
|
+
parallelAssetsDownloadLimit = 15,
|
|
823
|
+
fetch: request = fetch
|
|
740
824
|
}) => {
|
|
825
|
+
const languages = Object.keys(translation);
|
|
741
826
|
const limitScript = (0, import_p_limit.default)(1);
|
|
827
|
+
const limitAssetsDownload = (0, import_p_limit.default)(parallelAssetsDownloadLimit);
|
|
742
828
|
const story = {};
|
|
743
829
|
const times = /* @__PURE__ */ new Set();
|
|
744
830
|
const ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
|
|
@@ -754,7 +840,23 @@ var Novely = (() => {
|
|
|
754
840
|
Object.assign(story, flattenStory(part));
|
|
755
841
|
if (preloadAssets === "blocking" && ASSETS_TO_PRELOAD.size > 0) {
|
|
756
842
|
renderer.ui.showScreen("loading");
|
|
757
|
-
|
|
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);
|
|
758
860
|
}
|
|
759
861
|
const screen = renderer.ui.getScreen();
|
|
760
862
|
const nextScreen = scriptCalled ? screen : initialScreen;
|
|
@@ -775,13 +877,25 @@ var Novely = (() => {
|
|
|
775
877
|
return limitScript(() => scriptBase(part));
|
|
776
878
|
};
|
|
777
879
|
const action = new Proxy({}, {
|
|
778
|
-
get(_,
|
|
880
|
+
get(_, action2) {
|
|
779
881
|
return (...props) => {
|
|
780
882
|
if (preloadAssets === "blocking") {
|
|
781
|
-
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])) {
|
|
782
896
|
ASSETS_TO_PRELOAD.add(props[0]);
|
|
783
897
|
}
|
|
784
|
-
if (
|
|
898
|
+
if (action2 === "showCharacter" && isString(props[0]) && isString(props[1])) {
|
|
785
899
|
const images = characters[props[0]].emotions[props[1]];
|
|
786
900
|
if (Array.isArray(images)) {
|
|
787
901
|
for (const asset of images) {
|
|
@@ -792,7 +906,7 @@ var Novely = (() => {
|
|
|
792
906
|
}
|
|
793
907
|
}
|
|
794
908
|
}
|
|
795
|
-
return [
|
|
909
|
+
return [action2, ...props];
|
|
796
910
|
};
|
|
797
911
|
}
|
|
798
912
|
});
|
|
@@ -841,12 +955,10 @@ var Novely = (() => {
|
|
|
841
955
|
for (const migration of migrations) {
|
|
842
956
|
stored = migration(stored);
|
|
843
957
|
}
|
|
844
|
-
stored.meta[
|
|
845
|
-
if (overrideLanguage) {
|
|
958
|
+
if (overrideLanguage || !stored.meta[0]) {
|
|
846
959
|
stored.meta[0] = getLanguageWithoutParameters();
|
|
847
|
-
} else {
|
|
848
|
-
stored.meta[0] ||= getLanguageWithoutParameters();
|
|
849
960
|
}
|
|
961
|
+
stored.meta[1] ||= DEFAULT_TYPEWRITER_SPEED;
|
|
850
962
|
stored.meta[2] ??= 1;
|
|
851
963
|
stored.meta[3] ??= 1;
|
|
852
964
|
stored.meta[4] ??= 1;
|
|
@@ -912,12 +1024,11 @@ var Novely = (() => {
|
|
|
912
1024
|
return;
|
|
913
1025
|
let latest = save2 || $.get().saves.at(-1);
|
|
914
1026
|
if (!latest) {
|
|
915
|
-
$.set({
|
|
916
|
-
saves: [initial],
|
|
917
|
-
data: klona(defaultData),
|
|
918
|
-
meta: [getLanguageWithoutParameters(), DEFAULT_TYPEWRITER_SPEED, 1, 1, 1]
|
|
919
|
-
});
|
|
920
1027
|
latest = klona(initial);
|
|
1028
|
+
$.update((prev) => {
|
|
1029
|
+
prev.saves.push(latest);
|
|
1030
|
+
return prev;
|
|
1031
|
+
});
|
|
921
1032
|
}
|
|
922
1033
|
const context = renderer.getContext(MAIN_CONTEXT_KEY);
|
|
923
1034
|
const stack = useStack(context);
|
|
@@ -1021,7 +1132,7 @@ var Novely = (() => {
|
|
|
1021
1132
|
ctx.meta.preview = true;
|
|
1022
1133
|
const processor = createQueueProcessor(queue);
|
|
1023
1134
|
await processor.run((action2, props) => {
|
|
1024
|
-
if (
|
|
1135
|
+
if (isAudioAction(action2))
|
|
1025
1136
|
return;
|
|
1026
1137
|
if (action2 === "vibrate")
|
|
1027
1138
|
return;
|
|
@@ -1272,10 +1383,10 @@ var Novely = (() => {
|
|
|
1272
1383
|
ctx.vibrate(pattern);
|
|
1273
1384
|
push();
|
|
1274
1385
|
},
|
|
1275
|
-
next({
|
|
1386
|
+
next({ push }) {
|
|
1276
1387
|
push();
|
|
1277
1388
|
},
|
|
1278
|
-
animateCharacter({ ctx,
|
|
1389
|
+
animateCharacter({ ctx, push }, [character, timeout, ...classes]) {
|
|
1279
1390
|
if (DEV && classes.length === 0) {
|
|
1280
1391
|
throw new Error("Attempt to use AnimateCharacter without classes. Classes should be provided [https://novely.pages.dev/guide/actions/animateCharacter.html]");
|
|
1281
1392
|
}
|
|
@@ -1284,30 +1395,8 @@ var Novely = (() => {
|
|
|
1284
1395
|
}
|
|
1285
1396
|
if (ctx.meta.preview)
|
|
1286
1397
|
return;
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
const char = ctx.getCharacter(character);
|
|
1290
|
-
if (!char)
|
|
1291
|
-
return;
|
|
1292
|
-
const target = char.canvas;
|
|
1293
|
-
if (!target)
|
|
1294
|
-
return;
|
|
1295
|
-
const classNames = classes.filter((className) => !target.classList.contains(className));
|
|
1296
|
-
target.classList.add(...classNames);
|
|
1297
|
-
const removeClassNames = () => {
|
|
1298
|
-
target.classList.remove(...classNames);
|
|
1299
|
-
};
|
|
1300
|
-
const timeoutId = setTimeout(removeClassNames, timeout);
|
|
1301
|
-
clear(() => {
|
|
1302
|
-
removeClassNames();
|
|
1303
|
-
clearTimeout(timeoutId);
|
|
1304
|
-
});
|
|
1305
|
-
};
|
|
1306
|
-
handler.key = "@@internal-animate-character";
|
|
1307
|
-
match("custom", [handler], {
|
|
1308
|
-
ctx,
|
|
1309
|
-
data: data2
|
|
1310
|
-
});
|
|
1398
|
+
ctx.character(character).animate(timeout, classes);
|
|
1399
|
+
push();
|
|
1311
1400
|
},
|
|
1312
1401
|
text({ ctx, data: data2, forward }, text) {
|
|
1313
1402
|
const string = text.map((content) => unwrap2(content, data2)).join(" ");
|