@novely/core 0.38.2 → 0.39.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 +57 -14
- package/dist/index.global.js +559 -140
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +310 -166
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -49,10 +49,10 @@ var MAIN_CONTEXT_KEY = "$MAIN";
|
|
|
49
49
|
var STACK_MAP = /* @__PURE__ */ new Map();
|
|
50
50
|
var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
|
|
51
51
|
var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
|
|
52
|
-
var
|
|
52
|
+
var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
|
|
53
53
|
|
|
54
54
|
// src/utils.ts
|
|
55
|
-
import { DEV } from "esm-env";
|
|
55
|
+
import { DEV as DEV2 } from "esm-env";
|
|
56
56
|
|
|
57
57
|
// ../../node_modules/.pnpm/klona@2.0.6/node_modules/klona/json/index.mjs
|
|
58
58
|
function klona(val) {
|
|
@@ -81,6 +81,127 @@ function klona(val) {
|
|
|
81
81
|
return val;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
// src/utils.ts
|
|
85
|
+
import { default as memoize2 } from "micro-memoize";
|
|
86
|
+
|
|
87
|
+
// src/asset.ts
|
|
88
|
+
import { DEV } from "esm-env";
|
|
89
|
+
|
|
90
|
+
// src/audio-codecs.ts
|
|
91
|
+
var cut = (str2) => str2.replace(/^no$/, "");
|
|
92
|
+
var audio = new Audio();
|
|
93
|
+
var canPlay = (type) => !!cut(audio.canPlayType(type));
|
|
94
|
+
var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
|
|
95
|
+
var supportsMap = {
|
|
96
|
+
mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
|
|
97
|
+
mpeg: canPlay("audio/mpeg;"),
|
|
98
|
+
opus: canPlay('audio/ogg; codecs="opus"'),
|
|
99
|
+
ogg: canPlay('audio/ogg; codecs="vorbis"'),
|
|
100
|
+
oga: canPlay('audio/ogg; codecs="vorbis"'),
|
|
101
|
+
wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
|
|
102
|
+
aac: canPlay("audio/aac;"),
|
|
103
|
+
caf: canPlay("audio/x-caf;"),
|
|
104
|
+
m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
|
|
105
|
+
m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
|
|
106
|
+
mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
|
|
107
|
+
weba: canPlay('audio/webm; codecs="vorbis"'),
|
|
108
|
+
webm: canPlay('audio/webm; codecs="vorbis"'),
|
|
109
|
+
dolby: canPlay('audio/mp4; codecs="ec-3"'),
|
|
110
|
+
flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// src/image-formats.ts
|
|
114
|
+
var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
|
|
115
|
+
var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
|
|
116
|
+
var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
|
|
117
|
+
var supportsFormat = (source) => {
|
|
118
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
119
|
+
const img = Object.assign(document.createElement("img"), {
|
|
120
|
+
src: source
|
|
121
|
+
});
|
|
122
|
+
img.onload = img.onerror = () => {
|
|
123
|
+
resolve(img.height === 2);
|
|
124
|
+
};
|
|
125
|
+
return promise;
|
|
126
|
+
};
|
|
127
|
+
var supportsMap2 = {
|
|
128
|
+
avif: false,
|
|
129
|
+
jxl: false,
|
|
130
|
+
webp: false
|
|
131
|
+
};
|
|
132
|
+
var formatsMap = {
|
|
133
|
+
avif,
|
|
134
|
+
jxl,
|
|
135
|
+
webp
|
|
136
|
+
};
|
|
137
|
+
var loadImageFormatsSupport = async () => {
|
|
138
|
+
const promises = [];
|
|
139
|
+
for (const [format, source] of Object.entries(formatsMap)) {
|
|
140
|
+
const promise = supportsFormat(source).then((supported) => {
|
|
141
|
+
supportsMap2[format] = supported;
|
|
142
|
+
});
|
|
143
|
+
promises.push(promise);
|
|
144
|
+
}
|
|
145
|
+
await Promise.all(promises);
|
|
146
|
+
};
|
|
147
|
+
loadImageFormatsSupport();
|
|
148
|
+
|
|
149
|
+
// src/asset.ts
|
|
150
|
+
import { default as memoize } from "micro-memoize";
|
|
151
|
+
var getType = memoize((extensions) => {
|
|
152
|
+
if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
|
|
153
|
+
return "audio";
|
|
154
|
+
}
|
|
155
|
+
if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
|
|
156
|
+
return "image";
|
|
157
|
+
}
|
|
158
|
+
throw extensions;
|
|
159
|
+
});
|
|
160
|
+
var SUPPORT_MAPS = {
|
|
161
|
+
"image": supportsMap2,
|
|
162
|
+
"audio": supportsMap
|
|
163
|
+
};
|
|
164
|
+
var asset = memoize((...variants) => {
|
|
165
|
+
if (DEV && variants.length === 0) {
|
|
166
|
+
throw new Error(`Attempt to use "asset" function without arguments`);
|
|
167
|
+
}
|
|
168
|
+
const map = {};
|
|
169
|
+
const extensions = [];
|
|
170
|
+
for (const v of variants) {
|
|
171
|
+
const e = getUrlFileExtension(v);
|
|
172
|
+
map[e] = v;
|
|
173
|
+
extensions.push(e);
|
|
174
|
+
}
|
|
175
|
+
const type = getType(extensions);
|
|
176
|
+
const getSource = memoize(() => {
|
|
177
|
+
const support = SUPPORT_MAPS[type];
|
|
178
|
+
for (const extension of extensions) {
|
|
179
|
+
if (extension in support) {
|
|
180
|
+
if (support[extension]) {
|
|
181
|
+
return map[extension];
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
return map[extension];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (DEV) {
|
|
188
|
+
throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
|
|
189
|
+
}
|
|
190
|
+
return "";
|
|
191
|
+
});
|
|
192
|
+
return {
|
|
193
|
+
get source() {
|
|
194
|
+
return getSource();
|
|
195
|
+
},
|
|
196
|
+
get type() {
|
|
197
|
+
return type;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
var isAsset = (suspect) => {
|
|
202
|
+
return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
|
|
203
|
+
};
|
|
204
|
+
|
|
84
205
|
// src/utils.ts
|
|
85
206
|
var matchAction = ({ getContext, onBeforeActionCall, push, forward }, values) => {
|
|
86
207
|
return (action, props, { ctx, data }) => {
|
|
@@ -325,7 +446,7 @@ var createQueueProcessor = (queue, options) => {
|
|
|
325
446
|
const processedQueue = [];
|
|
326
447
|
const keep = /* @__PURE__ */ new Set();
|
|
327
448
|
const characters = /* @__PURE__ */ new Set();
|
|
328
|
-
const
|
|
449
|
+
const audio2 = {
|
|
329
450
|
music: /* @__PURE__ */ new Set(),
|
|
330
451
|
sound: /* @__PURE__ */ new Set()
|
|
331
452
|
};
|
|
@@ -350,14 +471,9 @@ var createQueueProcessor = (queue, options) => {
|
|
|
350
471
|
});
|
|
351
472
|
if (notLatest) continue;
|
|
352
473
|
} else if ("skipOnRestore" in fn && fn.skipOnRestore) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
return nextActions;
|
|
357
|
-
};
|
|
358
|
-
return nextActions;
|
|
359
|
-
};
|
|
360
|
-
if (fn.skipOnRestore(getNext)) continue;
|
|
474
|
+
if (fn.skipOnRestore(() => next(i))) {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
361
477
|
}
|
|
362
478
|
}
|
|
363
479
|
processedQueue.push(item);
|
|
@@ -375,9 +491,9 @@ var createQueueProcessor = (queue, options) => {
|
|
|
375
491
|
if (action === "showCharacter") {
|
|
376
492
|
characters.add(params[0]);
|
|
377
493
|
} else if (action === "playMusic") {
|
|
378
|
-
|
|
494
|
+
audio2.music.add(unwrapAsset(params[0]));
|
|
379
495
|
} else if (action === "playSound") {
|
|
380
|
-
|
|
496
|
+
audio2.sound.add(unwrapAsset(params[0]));
|
|
381
497
|
}
|
|
382
498
|
processedQueue.push(item);
|
|
383
499
|
} else if (action === "showBackground" || action === "preload") {
|
|
@@ -414,7 +530,7 @@ var createQueueProcessor = (queue, options) => {
|
|
|
414
530
|
keep: {
|
|
415
531
|
keep,
|
|
416
532
|
characters,
|
|
417
|
-
audio
|
|
533
|
+
audio: audio2
|
|
418
534
|
}
|
|
419
535
|
};
|
|
420
536
|
};
|
|
@@ -461,15 +577,15 @@ var createUseStackFunction = (renderer) => {
|
|
|
461
577
|
var mapSet = (set, fn) => {
|
|
462
578
|
return [...set].map(fn);
|
|
463
579
|
};
|
|
464
|
-
var isImageAsset = (
|
|
465
|
-
return isString(
|
|
580
|
+
var isImageAsset = (asset2) => {
|
|
581
|
+
return isString(asset2) && isCSSImage(asset2);
|
|
466
582
|
};
|
|
467
583
|
var getUrlFileExtension = (address) => {
|
|
468
584
|
try {
|
|
469
585
|
const { pathname } = new URL(address, location.href);
|
|
470
586
|
return pathname.split(".").at(-1).split("!")[0].split(":")[0];
|
|
471
587
|
} catch (error) {
|
|
472
|
-
if (
|
|
588
|
+
if (DEV2) {
|
|
473
589
|
console.error(new Error(`Could not construct URL "${address}".`, { cause: error }));
|
|
474
590
|
}
|
|
475
591
|
return "";
|
|
@@ -482,43 +598,41 @@ var fetchContentType = async (request, url) => {
|
|
|
482
598
|
});
|
|
483
599
|
return response.headers.get("Content-Type") || "";
|
|
484
600
|
} catch (error) {
|
|
485
|
-
if (
|
|
601
|
+
if (DEV2) {
|
|
486
602
|
console.error(new Error(`Failed to fetch file at "${url}"`, { cause: error }));
|
|
487
603
|
}
|
|
488
604
|
return "";
|
|
489
605
|
}
|
|
490
606
|
};
|
|
491
|
-
var getResourseType =
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
return encache("image");
|
|
607
|
+
var getResourseType = memoize2(
|
|
608
|
+
async (request, url) => {
|
|
609
|
+
if (!isCSSImage(url)) {
|
|
610
|
+
return "other";
|
|
611
|
+
}
|
|
612
|
+
const extension = getUrlFileExtension(url);
|
|
613
|
+
if (HOWLER_SUPPORTED_FILE_FORMATS.has(extension)) {
|
|
614
|
+
return "audio";
|
|
615
|
+
}
|
|
616
|
+
if (SUPPORTED_IMAGE_FILE_FORMATS.has(extension)) {
|
|
617
|
+
return "image";
|
|
618
|
+
}
|
|
619
|
+
const contentType = await fetchContentType(request, url);
|
|
620
|
+
if (contentType.includes("audio")) {
|
|
621
|
+
return "audio";
|
|
622
|
+
}
|
|
623
|
+
if (contentType.includes("image")) {
|
|
624
|
+
return "image";
|
|
625
|
+
}
|
|
626
|
+
return "other";
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
isPromise: true
|
|
515
630
|
}
|
|
516
|
-
|
|
517
|
-
};
|
|
631
|
+
);
|
|
518
632
|
var capitalize = (str2) => {
|
|
519
633
|
return str2[0].toUpperCase() + str2.slice(1);
|
|
520
634
|
};
|
|
521
|
-
var getIntlLanguageDisplayName = (lang) => {
|
|
635
|
+
var getIntlLanguageDisplayName = memoize2((lang) => {
|
|
522
636
|
try {
|
|
523
637
|
const intl = new Intl.DisplayNames([lang], {
|
|
524
638
|
type: "language"
|
|
@@ -527,7 +641,7 @@ var getIntlLanguageDisplayName = (lang) => {
|
|
|
527
641
|
} catch {
|
|
528
642
|
return lang;
|
|
529
643
|
}
|
|
530
|
-
};
|
|
644
|
+
});
|
|
531
645
|
var createReferFunction = (story) => {
|
|
532
646
|
const refer = (path) => {
|
|
533
647
|
let current = story;
|
|
@@ -671,6 +785,29 @@ var collectActionsBeforeBlockingAction = ({ path, refer }) => {
|
|
|
671
785
|
}
|
|
672
786
|
return collection;
|
|
673
787
|
};
|
|
788
|
+
var unwrapAsset = (asset2) => {
|
|
789
|
+
return isAsset(asset2) ? asset2.source : asset2;
|
|
790
|
+
};
|
|
791
|
+
var handleAudioAsset = (asset2) => {
|
|
792
|
+
if (DEV2 && isAsset(asset2) && asset2.type !== "audio") {
|
|
793
|
+
throw new Error("Attempt to use non-audio asset in audio action", { cause: asset2 });
|
|
794
|
+
}
|
|
795
|
+
return unwrapAsset(asset2);
|
|
796
|
+
};
|
|
797
|
+
var handleImageAsset = (asset2) => {
|
|
798
|
+
if (DEV2 && isAsset(asset2) && asset2.type !== "image") {
|
|
799
|
+
throw new Error("Attempt to use non-image asset in action that requires image assets", { cause: asset2 });
|
|
800
|
+
}
|
|
801
|
+
return unwrapAsset(asset2);
|
|
802
|
+
};
|
|
803
|
+
var getCharactersData = (characters) => {
|
|
804
|
+
const entries = Object.entries(characters);
|
|
805
|
+
const mapped = entries.map(([key, value]) => [key, { name: value.name, emotions: Object.keys(value.emotions) }]);
|
|
806
|
+
return Object.fromEntries(mapped);
|
|
807
|
+
};
|
|
808
|
+
var toArray = (target) => {
|
|
809
|
+
return Array.isArray(target) ? target : [target];
|
|
810
|
+
};
|
|
674
811
|
|
|
675
812
|
// src/novely.ts
|
|
676
813
|
import { dequal } from "dequal/lite";
|
|
@@ -921,7 +1058,69 @@ var setupBrowserVisibilityChangeListeners = ({ onChange }) => {
|
|
|
921
1058
|
|
|
922
1059
|
// src/novely.ts
|
|
923
1060
|
import pLimit from "p-limit";
|
|
924
|
-
import { DEV as
|
|
1061
|
+
import { DEV as DEV3 } from "esm-env";
|
|
1062
|
+
|
|
1063
|
+
// src/preloading.ts
|
|
1064
|
+
var enqueueAssetForPreloading = (asset2) => {
|
|
1065
|
+
if (!PRELOADED_ASSETS.has(asset2)) {
|
|
1066
|
+
ASSETS_TO_PRELOAD.add(asset2);
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
var handleAssetsPreloading = async ({ request, limiter, preloadAudioBlocking, preloadImageBlocking }) => {
|
|
1070
|
+
const list = mapSet(ASSETS_TO_PRELOAD, (asset2) => {
|
|
1071
|
+
return limiter(async () => {
|
|
1072
|
+
const type = await getResourseType(request, asset2);
|
|
1073
|
+
switch (type) {
|
|
1074
|
+
case "audio": {
|
|
1075
|
+
await preloadAudioBlocking(asset2);
|
|
1076
|
+
break;
|
|
1077
|
+
}
|
|
1078
|
+
case "image": {
|
|
1079
|
+
await preloadImageBlocking(asset2);
|
|
1080
|
+
break;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
ASSETS_TO_PRELOAD.delete(asset2);
|
|
1084
|
+
PRELOADED_ASSETS.add(asset2);
|
|
1085
|
+
});
|
|
1086
|
+
});
|
|
1087
|
+
await Promise.allSettled(list);
|
|
1088
|
+
ASSETS_TO_PRELOAD.clear();
|
|
1089
|
+
};
|
|
1090
|
+
var huntAssets = ({ characters, action, props, handle }) => {
|
|
1091
|
+
if (action === "showBackground") {
|
|
1092
|
+
if (isString(props[0])) {
|
|
1093
|
+
handle(handleAudioAsset(props[0]));
|
|
1094
|
+
}
|
|
1095
|
+
if (props[0] && typeof props[0] === "object") {
|
|
1096
|
+
for (const value of Object.values(props[0])) {
|
|
1097
|
+
if (isImageAsset(value)) {
|
|
1098
|
+
handle(value);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
if (isAudioAction(action) && isString(props[0])) {
|
|
1105
|
+
handle(handleAudioAsset(props[0]));
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
if (action === "showCharacter" && isString(props[0]) && isString(props[1])) {
|
|
1109
|
+
const images = toArray(characters[props[0]].emotions[props[1]]);
|
|
1110
|
+
for (const asset2 of images) {
|
|
1111
|
+
handle(handleImageAsset(asset2));
|
|
1112
|
+
}
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
if (action === "custom" && props[0].assets && props[0].assets.length > 0) {
|
|
1116
|
+
for (const asset2 of props[0].assets) {
|
|
1117
|
+
handle(asset2);
|
|
1118
|
+
}
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
|
|
1123
|
+
// src/novely.ts
|
|
925
1124
|
var novely = ({
|
|
926
1125
|
characters,
|
|
927
1126
|
characterAssetSizes = {},
|
|
@@ -950,38 +1149,12 @@ var novely = ({
|
|
|
950
1149
|
const limitAssetsDownload = pLimit(parallelAssetsDownloadLimit);
|
|
951
1150
|
const story = {};
|
|
952
1151
|
const times = /* @__PURE__ */ new Set();
|
|
953
|
-
const ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
|
|
954
1152
|
const dataLoaded = createControlledPromise();
|
|
955
1153
|
let initialScreenWasShown = false;
|
|
956
1154
|
let destroyed = false;
|
|
957
1155
|
const intime = (value) => {
|
|
958
1156
|
return times.add(value), value;
|
|
959
1157
|
};
|
|
960
|
-
const handleAssetsPreloading = async () => {
|
|
961
|
-
const { preloadAudioBlocking, preloadImageBlocking } = renderer.misc;
|
|
962
|
-
const list = mapSet(ASSETS_TO_PRELOAD, (asset) => {
|
|
963
|
-
return limitAssetsDownload(async () => {
|
|
964
|
-
const type = await getResourseType(request, asset);
|
|
965
|
-
switch (type) {
|
|
966
|
-
case "audio": {
|
|
967
|
-
await preloadAudioBlocking(asset);
|
|
968
|
-
break;
|
|
969
|
-
}
|
|
970
|
-
case "image": {
|
|
971
|
-
await preloadImageBlocking(asset);
|
|
972
|
-
break;
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
});
|
|
976
|
-
});
|
|
977
|
-
await Promise.allSettled(list);
|
|
978
|
-
ASSETS_TO_PRELOAD.clear();
|
|
979
|
-
};
|
|
980
|
-
const assetsToPreloadAdd = (asset) => {
|
|
981
|
-
if (!PRELOADED_ASSETS.has(asset)) {
|
|
982
|
-
ASSETS_TO_PRELOAD.add(asset);
|
|
983
|
-
}
|
|
984
|
-
};
|
|
985
1158
|
const scriptBase = async (part) => {
|
|
986
1159
|
if (destroyed) return;
|
|
987
1160
|
Object.assign(story, flattenStory(part));
|
|
@@ -994,7 +1167,11 @@ var novely = ({
|
|
|
994
1167
|
if (!loadingIsShown) {
|
|
995
1168
|
renderer.ui.showLoading();
|
|
996
1169
|
}
|
|
997
|
-
await handleAssetsPreloading(
|
|
1170
|
+
await handleAssetsPreloading({
|
|
1171
|
+
...renderer.misc,
|
|
1172
|
+
limiter: limitAssetsDownload,
|
|
1173
|
+
request
|
|
1174
|
+
});
|
|
998
1175
|
}
|
|
999
1176
|
await dataLoaded.promise;
|
|
1000
1177
|
renderer.ui.hideLoading();
|
|
@@ -1010,40 +1187,6 @@ var novely = ({
|
|
|
1010
1187
|
const script = (part) => {
|
|
1011
1188
|
return limitScript(() => scriptBase(part));
|
|
1012
1189
|
};
|
|
1013
|
-
const assetPreloadingCheck = (action2, props, doAction = assetsToPreloadAdd) => {
|
|
1014
|
-
if (action2 === "showBackground") {
|
|
1015
|
-
if (isImageAsset(props[0])) {
|
|
1016
|
-
doAction(props[0]);
|
|
1017
|
-
}
|
|
1018
|
-
if (props[0] && typeof props[0] === "object") {
|
|
1019
|
-
for (const value of Object.values(props[0])) {
|
|
1020
|
-
if (!isImageAsset(value)) continue;
|
|
1021
|
-
doAction(value);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
return;
|
|
1025
|
-
}
|
|
1026
|
-
if (isAudioAction(action2) && isString(props[0])) {
|
|
1027
|
-
doAction(props[0]);
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
if (action2 === "showCharacter" && isString(props[0]) && isString(props[1])) {
|
|
1031
|
-
const images = characters[props[0]].emotions[props[1]];
|
|
1032
|
-
if (Array.isArray(images)) {
|
|
1033
|
-
for (const asset of images) {
|
|
1034
|
-
doAction(asset);
|
|
1035
|
-
}
|
|
1036
|
-
} else {
|
|
1037
|
-
doAction(images);
|
|
1038
|
-
}
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
if (action2 === "custom" && props[0].assets && props[0].assets.length > 0) {
|
|
1042
|
-
for (const asset of props[0].assets) {
|
|
1043
|
-
doAction(asset);
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
};
|
|
1047
1190
|
const action = new Proxy({}, {
|
|
1048
1191
|
get(_, action2) {
|
|
1049
1192
|
if (action2 in renderer.actions) {
|
|
@@ -1051,7 +1194,7 @@ var novely = ({
|
|
|
1051
1194
|
}
|
|
1052
1195
|
return (...props) => {
|
|
1053
1196
|
if (preloadAssets === "blocking") {
|
|
1054
|
-
|
|
1197
|
+
huntAssets({ characters, action: action2, props, handle: enqueueAssetForPreloading });
|
|
1055
1198
|
}
|
|
1056
1199
|
return [action2, ...props];
|
|
1057
1200
|
};
|
|
@@ -1072,7 +1215,7 @@ var novely = ({
|
|
|
1072
1215
|
if (languages.includes(language)) {
|
|
1073
1216
|
return language;
|
|
1074
1217
|
}
|
|
1075
|
-
if (
|
|
1218
|
+
if (DEV3) {
|
|
1076
1219
|
throw new Error(`Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`);
|
|
1077
1220
|
}
|
|
1078
1221
|
throw 0;
|
|
@@ -1102,7 +1245,7 @@ var novely = ({
|
|
|
1102
1245
|
};
|
|
1103
1246
|
const throttledOnStorageDataChange = throttle(onStorageDataChange, throttleTimeout);
|
|
1104
1247
|
const throttledEmergencyOnStorageDataChange = throttle(() => {
|
|
1105
|
-
if (saveOnUnload === true || saveOnUnload === "prod" && !
|
|
1248
|
+
if (saveOnUnload === true || saveOnUnload === "prod" && !DEV3) {
|
|
1106
1249
|
onStorageDataChange(storageData.get());
|
|
1107
1250
|
}
|
|
1108
1251
|
}, 10);
|
|
@@ -1111,7 +1254,7 @@ var novely = ({
|
|
|
1111
1254
|
let stored = await storage.get();
|
|
1112
1255
|
for (const migration of migrations) {
|
|
1113
1256
|
stored = migration(stored);
|
|
1114
|
-
if (
|
|
1257
|
+
if (DEV3 && !stored) {
|
|
1115
1258
|
throw new Error("Migrations should return a value.");
|
|
1116
1259
|
}
|
|
1117
1260
|
}
|
|
@@ -1188,7 +1331,7 @@ var novely = ({
|
|
|
1188
1331
|
let interacted = 0;
|
|
1189
1332
|
const restore = async (save2) => {
|
|
1190
1333
|
if (isEmpty(story)) {
|
|
1191
|
-
if (
|
|
1334
|
+
if (DEV3) {
|
|
1192
1335
|
throw new Error("Story is empty. You should call an `enine.script` function [https://novely.pages.dev/guide/story.html]");
|
|
1193
1336
|
}
|
|
1194
1337
|
return;
|
|
@@ -1209,7 +1352,7 @@ var novely = ({
|
|
|
1209
1352
|
const [path] = stack.value = latest;
|
|
1210
1353
|
renderer.ui.showScreen("game");
|
|
1211
1354
|
const { queue, skip, skipPreserve } = getActionsFromPath(story, path, false);
|
|
1212
|
-
const { run, keep: { keep, characters: characters2, audio } } = createQueueProcessor(queue, {
|
|
1355
|
+
const { run, keep: { keep, characters: characters2, audio: audio2 } } = createQueueProcessor(queue, {
|
|
1213
1356
|
skip,
|
|
1214
1357
|
skipPreserve
|
|
1215
1358
|
});
|
|
@@ -1227,7 +1370,7 @@ var novely = ({
|
|
|
1227
1370
|
}
|
|
1228
1371
|
}
|
|
1229
1372
|
if (context.meta.goingBack) {
|
|
1230
|
-
match("clear", [keep, characters2,
|
|
1373
|
+
match("clear", [keep, characters2, audio2], {
|
|
1231
1374
|
ctx: context,
|
|
1232
1375
|
data: latest[1]
|
|
1233
1376
|
});
|
|
@@ -1314,7 +1457,7 @@ var novely = ({
|
|
|
1314
1457
|
if (isAudioAction(action2)) return;
|
|
1315
1458
|
if (action2 === "vibrate") return;
|
|
1316
1459
|
if (action2 === "end") return;
|
|
1317
|
-
|
|
1460
|
+
huntAssets({ characters, action: action2, props, handle: assets.push.bind(assets) });
|
|
1318
1461
|
return match(action2, props, {
|
|
1319
1462
|
ctx,
|
|
1320
1463
|
data: data2
|
|
@@ -1346,7 +1489,7 @@ var novely = ({
|
|
|
1346
1489
|
};
|
|
1347
1490
|
const getLanguageDisplayName = (lang) => {
|
|
1348
1491
|
const language = translation[lang];
|
|
1349
|
-
if (
|
|
1492
|
+
if (DEV3 && !language) {
|
|
1350
1493
|
throw new Error(`Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`);
|
|
1351
1494
|
}
|
|
1352
1495
|
return capitalize(language.nameOverride || getIntlLanguageDisplayName(lang));
|
|
@@ -1357,9 +1500,15 @@ var novely = ({
|
|
|
1357
1500
|
const getResourseTypeForRenderer = (url) => {
|
|
1358
1501
|
return getResourseType(request, url);
|
|
1359
1502
|
};
|
|
1503
|
+
const getCharacterColor = (c) => {
|
|
1504
|
+
return c in characters ? characters[c].color : "#000000";
|
|
1505
|
+
};
|
|
1506
|
+
const getCharacterAssets = (character, emotion) => {
|
|
1507
|
+
return toArray(characters[character].emotions[emotion]).map(handleImageAsset);
|
|
1508
|
+
};
|
|
1360
1509
|
const renderer = createRenderer({
|
|
1361
1510
|
mainContextKey: MAIN_CONTEXT_KEY,
|
|
1362
|
-
characters,
|
|
1511
|
+
characters: getCharactersData(characters),
|
|
1363
1512
|
characterAssetSizes,
|
|
1364
1513
|
set,
|
|
1365
1514
|
restore,
|
|
@@ -1376,6 +1525,8 @@ var novely = ({
|
|
|
1376
1525
|
storageData,
|
|
1377
1526
|
coreData,
|
|
1378
1527
|
getLanguageDisplayName,
|
|
1528
|
+
getCharacterColor,
|
|
1529
|
+
getCharacterAssets,
|
|
1379
1530
|
getResourseType: getResourseTypeForRenderer
|
|
1380
1531
|
});
|
|
1381
1532
|
const useStack = createUseStackFunction(renderer);
|
|
@@ -1416,26 +1567,12 @@ var novely = ({
|
|
|
1416
1567
|
refer
|
|
1417
1568
|
});
|
|
1418
1569
|
for (const [action3, ...props2] of collection) {
|
|
1419
|
-
|
|
1570
|
+
huntAssets({ characters, action: action3, props: props2, handle: enqueueAssetForPreloading });
|
|
1420
1571
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
switch (type) {
|
|
1426
|
-
case "audio": {
|
|
1427
|
-
preloadAudioBlocking(asset).then(() => {
|
|
1428
|
-
PRELOADED_ASSETS.add(asset);
|
|
1429
|
-
});
|
|
1430
|
-
break;
|
|
1431
|
-
}
|
|
1432
|
-
case "image": {
|
|
1433
|
-
preloadImageBlocking(asset).then(() => {
|
|
1434
|
-
PRELOADED_ASSETS.add(asset);
|
|
1435
|
-
});
|
|
1436
|
-
break;
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1572
|
+
handleAssetsPreloading({
|
|
1573
|
+
...renderer.misc,
|
|
1574
|
+
request,
|
|
1575
|
+
limiter: limitAssetsDownload
|
|
1439
1576
|
});
|
|
1440
1577
|
} catch (cause) {
|
|
1441
1578
|
console.error(cause);
|
|
@@ -1448,41 +1585,47 @@ var novely = ({
|
|
|
1448
1585
|
setTimeout(push, isFunction(time) ? time(getStateAtCtx(ctx)) : time);
|
|
1449
1586
|
},
|
|
1450
1587
|
showBackground({ ctx, push }, [background]) {
|
|
1451
|
-
|
|
1588
|
+
if (isString(background) || isAsset(background)) {
|
|
1589
|
+
ctx.background({
|
|
1590
|
+
"all": handleImageAsset(background)
|
|
1591
|
+
});
|
|
1592
|
+
} else {
|
|
1593
|
+
ctx.background(Object.fromEntries(Object.entries(background).map(([media, asset2]) => [media, handleImageAsset(asset2)])));
|
|
1594
|
+
}
|
|
1452
1595
|
push();
|
|
1453
1596
|
},
|
|
1454
1597
|
playMusic({ ctx, push }, [source]) {
|
|
1455
|
-
ctx.audio.music(source, "music").play(true);
|
|
1598
|
+
ctx.audio.music(handleAudioAsset(source), "music").play(true);
|
|
1456
1599
|
push();
|
|
1457
1600
|
},
|
|
1458
1601
|
pauseMusic({ ctx, push }, [source]) {
|
|
1459
|
-
ctx.audio.music(source, "music").pause();
|
|
1602
|
+
ctx.audio.music(handleAudioAsset(source), "music").pause();
|
|
1460
1603
|
push();
|
|
1461
1604
|
},
|
|
1462
1605
|
stopMusic({ ctx, push }, [source]) {
|
|
1463
|
-
ctx.audio.music(source, "music").stop();
|
|
1606
|
+
ctx.audio.music(handleAudioAsset(source), "music").stop();
|
|
1464
1607
|
push();
|
|
1465
1608
|
},
|
|
1466
1609
|
playSound({ ctx, push }, [source, loop]) {
|
|
1467
|
-
ctx.audio.music(source, "sound").play(loop || false);
|
|
1610
|
+
ctx.audio.music(handleAudioAsset(source), "sound").play(loop || false);
|
|
1468
1611
|
push();
|
|
1469
1612
|
},
|
|
1470
1613
|
pauseSound({ ctx, push }, [source]) {
|
|
1471
|
-
ctx.audio.music(source, "sound").pause();
|
|
1614
|
+
ctx.audio.music(handleAudioAsset(source), "sound").pause();
|
|
1472
1615
|
push();
|
|
1473
1616
|
},
|
|
1474
1617
|
stopSound({ ctx, push }, [source]) {
|
|
1475
|
-
ctx.audio.music(source, "sound").stop();
|
|
1618
|
+
ctx.audio.music(handleAudioAsset(source), "sound").stop();
|
|
1476
1619
|
push();
|
|
1477
1620
|
},
|
|
1478
1621
|
voice({ ctx, push }, [source]) {
|
|
1479
1622
|
const [lang] = storageData.get().meta;
|
|
1480
|
-
const audioSource = isString(source) ? source : source[lang];
|
|
1623
|
+
const audioSource = isString(source) ? source : isAsset(source) ? source : source[lang];
|
|
1481
1624
|
if (!audioSource) {
|
|
1482
1625
|
push();
|
|
1483
1626
|
return;
|
|
1484
1627
|
}
|
|
1485
|
-
ctx.audio.voice(audioSource);
|
|
1628
|
+
ctx.audio.voice(handleAudioAsset(audioSource));
|
|
1486
1629
|
push();
|
|
1487
1630
|
},
|
|
1488
1631
|
stopVoice({ ctx, push }) {
|
|
@@ -1491,11 +1634,11 @@ var novely = ({
|
|
|
1491
1634
|
},
|
|
1492
1635
|
showCharacter({ ctx, push }, [character, emotion, className, style]) {
|
|
1493
1636
|
emotion ??= defaultEmotions[character];
|
|
1494
|
-
if (
|
|
1637
|
+
if (DEV3 && !emotion) {
|
|
1495
1638
|
throw new Error(`Attemp to show character "${character}" without emotion provided.`);
|
|
1496
1639
|
}
|
|
1497
1640
|
if (!emotion) return;
|
|
1498
|
-
if (
|
|
1641
|
+
if (DEV3 && !characters[character].emotions[emotion]) {
|
|
1499
1642
|
throw new Error(`Attempt to show character "${character}" with unknown emotion "${emotion}"`);
|
|
1500
1643
|
}
|
|
1501
1644
|
const handle = ctx.character(character);
|
|
@@ -1532,7 +1675,7 @@ var novely = ({
|
|
|
1532
1675
|
);
|
|
1533
1676
|
},
|
|
1534
1677
|
say({ ctx, data: data2 }, [character, content]) {
|
|
1535
|
-
if (
|
|
1678
|
+
if (DEV3 && !characters[character]) {
|
|
1536
1679
|
throw new Error(`Attempt to call Say action with unknown character "${character}"`);
|
|
1537
1680
|
}
|
|
1538
1681
|
match("dialog", [character, content], {
|
|
@@ -1565,12 +1708,12 @@ var novely = ({
|
|
|
1565
1708
|
lang: storageData.get().meta[0],
|
|
1566
1709
|
state: getStateAtCtx(ctx)
|
|
1567
1710
|
});
|
|
1568
|
-
if (
|
|
1711
|
+
if (DEV3 && action2.length === 0 && !shown) {
|
|
1569
1712
|
console.warn(`Choice children should not be empty, either add content there or make item not selectable`);
|
|
1570
1713
|
}
|
|
1571
1714
|
return [templateReplace(content, data2), shown];
|
|
1572
1715
|
});
|
|
1573
|
-
if (
|
|
1716
|
+
if (DEV3 && transformedChoices.length === 0) {
|
|
1574
1717
|
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]`);
|
|
1575
1718
|
}
|
|
1576
1719
|
ctx.clearBlockingActions("choice");
|
|
@@ -1580,7 +1723,7 @@ var novely = ({
|
|
|
1580
1723
|
}
|
|
1581
1724
|
const stack = useStack(ctx);
|
|
1582
1725
|
const offset = isWithoutQuestion ? 0 : 1;
|
|
1583
|
-
if (
|
|
1726
|
+
if (DEV3 && !transformedChoices[selected]) {
|
|
1584
1727
|
throw new Error("Choice children is empty, either add content there or make item not selectable");
|
|
1585
1728
|
}
|
|
1586
1729
|
stack.value[0].push(["choice", selected + offset], [null, 0]);
|
|
@@ -1589,10 +1732,10 @@ var novely = ({
|
|
|
1589
1732
|
});
|
|
1590
1733
|
},
|
|
1591
1734
|
jump({ ctx, data: data2 }, [scene]) {
|
|
1592
|
-
if (
|
|
1735
|
+
if (DEV3 && !story[scene]) {
|
|
1593
1736
|
throw new Error(`Attempt to jump to unknown scene "${scene}"`);
|
|
1594
1737
|
}
|
|
1595
|
-
if (
|
|
1738
|
+
if (DEV3 && story[scene].length === 0) {
|
|
1596
1739
|
throw new Error(`Attempt to jump to empty scene "${scene}"`);
|
|
1597
1740
|
}
|
|
1598
1741
|
const stack = useStack(ctx);
|
|
@@ -1605,25 +1748,25 @@ var novely = ({
|
|
|
1605
1748
|
data: data2
|
|
1606
1749
|
});
|
|
1607
1750
|
},
|
|
1608
|
-
clear({ ctx, push }, [keep, characters2,
|
|
1751
|
+
clear({ ctx, push }, [keep, characters2, audio2]) {
|
|
1609
1752
|
ctx.vibrate(0);
|
|
1610
1753
|
ctx.clear(
|
|
1611
1754
|
keep || EMPTY_SET,
|
|
1612
1755
|
characters2 || EMPTY_SET,
|
|
1613
|
-
|
|
1756
|
+
audio2 || { music: EMPTY_SET, sounds: EMPTY_SET },
|
|
1614
1757
|
push
|
|
1615
1758
|
);
|
|
1616
1759
|
},
|
|
1617
1760
|
condition({ ctx }, [condition, variants]) {
|
|
1618
|
-
if (
|
|
1761
|
+
if (DEV3 && Object.values(variants).length === 0) {
|
|
1619
1762
|
throw new Error(`Attempt to use Condition action with empty variants object`);
|
|
1620
1763
|
}
|
|
1621
1764
|
if (!ctx.meta.restoring) {
|
|
1622
1765
|
const val = String(condition(getStateAtCtx(ctx)));
|
|
1623
|
-
if (
|
|
1766
|
+
if (DEV3 && !variants[val]) {
|
|
1624
1767
|
throw new Error(`Attempt to go to unknown variant "${val}"`);
|
|
1625
1768
|
}
|
|
1626
|
-
if (
|
|
1769
|
+
if (DEV3 && variants[val].length === 0) {
|
|
1627
1770
|
throw new Error(`Attempt to go to empty variant "${val}"`);
|
|
1628
1771
|
}
|
|
1629
1772
|
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
@@ -1680,7 +1823,7 @@ var novely = ({
|
|
|
1680
1823
|
},
|
|
1681
1824
|
animateCharacter({ ctx, push }, [character, className]) {
|
|
1682
1825
|
const classes = className.split(" ");
|
|
1683
|
-
if (
|
|
1826
|
+
if (DEV3 && classes.length === 0) {
|
|
1684
1827
|
throw new Error("Attempt to use AnimateCharacter without classes. Classes should be provided [https://novely.pages.dev/guide/actions/animateCharacter.html]");
|
|
1685
1828
|
}
|
|
1686
1829
|
if (ctx.meta.preview) return;
|
|
@@ -1689,7 +1832,7 @@ var novely = ({
|
|
|
1689
1832
|
},
|
|
1690
1833
|
text({ ctx, data: data2, forward }, text) {
|
|
1691
1834
|
const string = text.map((content) => templateReplace(content, data2)).join(" ");
|
|
1692
|
-
if (
|
|
1835
|
+
if (DEV3 && string.length === 0) {
|
|
1693
1836
|
throw new Error(`Action Text was called with empty string or array`);
|
|
1694
1837
|
}
|
|
1695
1838
|
ctx.clearBlockingActions("text");
|
|
@@ -1714,7 +1857,7 @@ var novely = ({
|
|
|
1714
1857
|
render(ctx);
|
|
1715
1858
|
},
|
|
1716
1859
|
preload({ ctx, push }, [source]) {
|
|
1717
|
-
if (
|
|
1860
|
+
if (DEV3 && preloadAssets !== "lazy") {
|
|
1718
1861
|
console.error(`You do not need a preload action becase "preloadAssets" strategy was set to "${preloadAssets}"`);
|
|
1719
1862
|
}
|
|
1720
1863
|
if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
|
|
@@ -1723,10 +1866,10 @@ var novely = ({
|
|
|
1723
1866
|
push();
|
|
1724
1867
|
},
|
|
1725
1868
|
block({ ctx }, [scene]) {
|
|
1726
|
-
if (
|
|
1869
|
+
if (DEV3 && !story[scene]) {
|
|
1727
1870
|
throw new Error(`Attempt to call Block action with unknown scene "${scene}"`);
|
|
1728
1871
|
}
|
|
1729
|
-
if (
|
|
1872
|
+
if (DEV3 && story[scene].length === 0) {
|
|
1730
1873
|
throw new Error(`Attempt to call Block action with empty scene "${scene}"`);
|
|
1731
1874
|
}
|
|
1732
1875
|
if (!ctx.meta.restoring) {
|
|
@@ -1798,8 +1941,8 @@ var novely = ({
|
|
|
1798
1941
|
};
|
|
1799
1942
|
const setStorageData = (data2) => {
|
|
1800
1943
|
if (destroyed) {
|
|
1801
|
-
if (
|
|
1802
|
-
throw new Error(`function \`setStorageData\` was called after novely instance was destroyed.`);
|
|
1944
|
+
if (DEV3) {
|
|
1945
|
+
throw new Error(`function \`setStorageData\` was called after novely instance was destroyed. Data is not updater nor synced after destroy.`);
|
|
1803
1946
|
}
|
|
1804
1947
|
return;
|
|
1805
1948
|
}
|
|
@@ -2079,6 +2222,7 @@ export {
|
|
|
2079
2222
|
JP,
|
|
2080
2223
|
KK,
|
|
2081
2224
|
RU,
|
|
2225
|
+
asset,
|
|
2082
2226
|
extendAction,
|
|
2083
2227
|
localStorageStorage,
|
|
2084
2228
|
novely
|