@novely/core 0.49.0 → 0.51.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.
@@ -1,2867 +0,0 @@
1
- "use strict";
2
- var Novely = (() => {
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
-
21
- // src/index.ts
22
- var src_exports = {};
23
- __export(src_exports, {
24
- EN: () => EN,
25
- JP: () => JP,
26
- KK: () => KK,
27
- RU: () => RU,
28
- asset: () => asset,
29
- extendAction: () => extendAction,
30
- localStorageStorage: () => localStorageStorage,
31
- novely: () => novely
32
- });
33
-
34
- // ../../node_modules/.pnpm/dequal@2.0.3/node_modules/dequal/lite/index.mjs
35
- var has = Object.prototype.hasOwnProperty;
36
- function dequal(foo, bar) {
37
- var ctor, len;
38
- if (foo === bar) return true;
39
- if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
40
- if (ctor === Date) return foo.getTime() === bar.getTime();
41
- if (ctor === RegExp) return foo.toString() === bar.toString();
42
- if (ctor === Array) {
43
- if ((len = foo.length) === bar.length) {
44
- while (len-- && dequal(foo[len], bar[len])) ;
45
- }
46
- return len === -1;
47
- }
48
- if (!ctor || typeof foo === "object") {
49
- len = 0;
50
- for (ctor in foo) {
51
- if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
52
- if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor])) return false;
53
- }
54
- return Object.keys(bar).length === len;
55
- }
56
- }
57
- return foo !== foo && bar !== bar;
58
- }
59
-
60
- // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/function/debounce.mjs
61
- function debounce(func, debounceMs, { signal, edges } = {}) {
62
- let pendingThis = void 0;
63
- let pendingArgs = null;
64
- const leading = edges != null && edges.includes("leading");
65
- const trailing = edges == null || edges.includes("trailing");
66
- const invoke = () => {
67
- if (pendingArgs !== null) {
68
- func.apply(pendingThis, pendingArgs);
69
- pendingThis = void 0;
70
- pendingArgs = null;
71
- }
72
- };
73
- const onTimerEnd = () => {
74
- if (trailing) {
75
- invoke();
76
- }
77
- cancel();
78
- };
79
- let timeoutId = null;
80
- const schedule = () => {
81
- if (timeoutId != null) {
82
- clearTimeout(timeoutId);
83
- }
84
- timeoutId = setTimeout(() => {
85
- timeoutId = null;
86
- onTimerEnd();
87
- }, debounceMs);
88
- };
89
- const cancelTimer = () => {
90
- if (timeoutId !== null) {
91
- clearTimeout(timeoutId);
92
- timeoutId = null;
93
- }
94
- };
95
- const cancel = () => {
96
- cancelTimer();
97
- pendingThis = void 0;
98
- pendingArgs = null;
99
- };
100
- const flush = () => {
101
- cancelTimer();
102
- invoke();
103
- };
104
- const debounced = function(...args) {
105
- if (signal?.aborted) {
106
- return;
107
- }
108
- pendingThis = this;
109
- pendingArgs = args;
110
- const isFirstCall = timeoutId == null;
111
- schedule();
112
- if (leading && isFirstCall) {
113
- invoke();
114
- }
115
- };
116
- debounced.schedule = schedule;
117
- debounced.cancel = cancel;
118
- debounced.flush = flush;
119
- signal?.addEventListener("abort", cancel, { once: true });
120
- return debounced;
121
- }
122
-
123
- // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/function/once.mjs
124
- function once(func) {
125
- let called = false;
126
- let cache;
127
- return function() {
128
- if (called) {
129
- return cache;
130
- }
131
- const result = func();
132
- called = true;
133
- cache = result;
134
- return result;
135
- };
136
- }
137
-
138
- // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/function/throttle.mjs
139
- function throttle(func, throttleMs, { signal, edges = ["leading", "trailing"] } = {}) {
140
- let pendingAt = null;
141
- const debounced = debounce(func, throttleMs, { signal, edges });
142
- const throttled = function(...args) {
143
- if (pendingAt == null) {
144
- pendingAt = Date.now();
145
- } else {
146
- if (Date.now() - pendingAt >= throttleMs) {
147
- pendingAt = Date.now();
148
- debounced.cancel();
149
- debounced(...args);
150
- }
151
- }
152
- debounced(...args);
153
- };
154
- throttled.cancel = debounced.cancel;
155
- throttled.flush = debounced.flush;
156
- return throttled;
157
- }
158
-
159
- // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/function/memoize.mjs
160
- function memoize(fn, options = {}) {
161
- const { cache = /* @__PURE__ */ new Map(), getCacheKey } = options;
162
- const memoizedFn = function(arg) {
163
- const key = getCacheKey ? getCacheKey(arg) : arg;
164
- if (cache.has(key)) {
165
- return cache.get(key);
166
- }
167
- const result = fn.call(this, arg);
168
- cache.set(key, result);
169
- return result;
170
- };
171
- memoizedFn.cache = cache;
172
- return memoizedFn;
173
- }
174
-
175
- // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs
176
- function isPlainObject(value) {
177
- if (typeof value !== "object") {
178
- return false;
179
- }
180
- if (value == null) {
181
- return false;
182
- }
183
- if (Object.getPrototypeOf(value) === null) {
184
- return true;
185
- }
186
- if (value.toString() !== "[object Object]") {
187
- return false;
188
- }
189
- let proto = value;
190
- while (Object.getPrototypeOf(proto) !== null) {
191
- proto = Object.getPrototypeOf(proto);
192
- }
193
- return Object.getPrototypeOf(value) === proto;
194
- }
195
-
196
- // ../../node_modules/.pnpm/es-toolkit@1.23.0/node_modules/es-toolkit/dist/object/merge.mjs
197
- function merge(target, source) {
198
- const sourceKeys = Object.keys(source);
199
- for (let i = 0; i < sourceKeys.length; i++) {
200
- const key = sourceKeys[i];
201
- const sourceValue = source[key];
202
- const targetValue = target[key];
203
- if (Array.isArray(sourceValue)) {
204
- if (Array.isArray(targetValue)) {
205
- target[key] = merge(targetValue, sourceValue);
206
- } else {
207
- target[key] = merge([], sourceValue);
208
- }
209
- } else if (isPlainObject(sourceValue)) {
210
- if (isPlainObject(targetValue)) {
211
- target[key] = merge(targetValue, sourceValue);
212
- } else {
213
- target[key] = merge({}, sourceValue);
214
- }
215
- } else if (targetValue === void 0 || sourceValue !== void 0) {
216
- target[key] = sourceValue;
217
- }
218
- }
219
- return target;
220
- }
221
-
222
- // ../../node_modules/.pnpm/esm-env@1.0.0/node_modules/esm-env/prod-ssr.js
223
- var DEV = false;
224
-
225
- // ../../node_modules/.pnpm/klona@2.0.6/node_modules/klona/full/index.mjs
226
- function set(obj, key, val) {
227
- if (typeof val.value === "object") val.value = klona(val.value);
228
- if (!val.enumerable || val.get || val.set || !val.configurable || !val.writable || key === "__proto__") {
229
- Object.defineProperty(obj, key, val);
230
- } else obj[key] = val.value;
231
- }
232
- function klona(x) {
233
- if (typeof x !== "object") return x;
234
- var i = 0, k, list, tmp, str = Object.prototype.toString.call(x);
235
- if (str === "[object Object]") {
236
- tmp = Object.create(x.__proto__ || null);
237
- } else if (str === "[object Array]") {
238
- tmp = Array(x.length);
239
- } else if (str === "[object Set]") {
240
- tmp = /* @__PURE__ */ new Set();
241
- x.forEach(function(val) {
242
- tmp.add(klona(val));
243
- });
244
- } else if (str === "[object Map]") {
245
- tmp = /* @__PURE__ */ new Map();
246
- x.forEach(function(val, key) {
247
- tmp.set(klona(key), klona(val));
248
- });
249
- } else if (str === "[object Date]") {
250
- tmp = /* @__PURE__ */ new Date(+x);
251
- } else if (str === "[object RegExp]") {
252
- tmp = new RegExp(x.source, x.flags);
253
- } else if (str === "[object DataView]") {
254
- tmp = new x.constructor(klona(x.buffer));
255
- } else if (str === "[object ArrayBuffer]") {
256
- tmp = x.slice(0);
257
- } else if (str.slice(-6) === "Array]") {
258
- tmp = new x.constructor(x);
259
- }
260
- if (tmp) {
261
- for (list = Object.getOwnPropertySymbols(x); i < list.length; i++) {
262
- set(tmp, list[i], Object.getOwnPropertyDescriptor(x, list[i]));
263
- }
264
- for (i = 0, list = Object.getOwnPropertyNames(x); i < list.length; i++) {
265
- if (Object.hasOwnProperty.call(tmp, k = list[i]) && tmp[k] === x[k]) continue;
266
- set(tmp, k, Object.getOwnPropertyDescriptor(x, k));
267
- }
268
- }
269
- return tmp || x;
270
- }
271
-
272
- // ../../node_modules/.pnpm/yocto-queue@1.1.1/node_modules/yocto-queue/index.js
273
- var Node = class {
274
- value;
275
- next;
276
- constructor(value) {
277
- this.value = value;
278
- }
279
- };
280
- var Queue = class {
281
- #head;
282
- #tail;
283
- #size;
284
- constructor() {
285
- this.clear();
286
- }
287
- enqueue(value) {
288
- const node = new Node(value);
289
- if (this.#head) {
290
- this.#tail.next = node;
291
- this.#tail = node;
292
- } else {
293
- this.#head = node;
294
- this.#tail = node;
295
- }
296
- this.#size++;
297
- }
298
- dequeue() {
299
- const current = this.#head;
300
- if (!current) {
301
- return;
302
- }
303
- this.#head = this.#head.next;
304
- this.#size--;
305
- return current.value;
306
- }
307
- peek() {
308
- if (!this.#head) {
309
- return;
310
- }
311
- return this.#head.value;
312
- }
313
- clear() {
314
- this.#head = void 0;
315
- this.#tail = void 0;
316
- this.#size = 0;
317
- }
318
- get size() {
319
- return this.#size;
320
- }
321
- *[Symbol.iterator]() {
322
- let current = this.#head;
323
- while (current) {
324
- yield current.value;
325
- current = current.next;
326
- }
327
- }
328
- };
329
-
330
- // ../../node_modules/.pnpm/p-limit@6.1.0/node_modules/p-limit/index.js
331
- function pLimit(concurrency) {
332
- validateConcurrency(concurrency);
333
- const queue = new Queue();
334
- let activeCount = 0;
335
- const resumeNext = () => {
336
- if (activeCount < concurrency && queue.size > 0) {
337
- queue.dequeue()();
338
- activeCount++;
339
- }
340
- };
341
- const next = () => {
342
- activeCount--;
343
- resumeNext();
344
- };
345
- const run = async (function_, resolve, arguments_) => {
346
- const result = (async () => function_(...arguments_))();
347
- resolve(result);
348
- try {
349
- await result;
350
- } catch {
351
- }
352
- next();
353
- };
354
- const enqueue = (function_, resolve, arguments_) => {
355
- new Promise((internalResolve) => {
356
- queue.enqueue(internalResolve);
357
- }).then(
358
- run.bind(void 0, function_, resolve, arguments_)
359
- );
360
- (async () => {
361
- await Promise.resolve();
362
- if (activeCount < concurrency) {
363
- resumeNext();
364
- }
365
- })();
366
- };
367
- const generator = (function_, ...arguments_) => new Promise((resolve) => {
368
- enqueue(function_, resolve, arguments_);
369
- });
370
- Object.defineProperties(generator, {
371
- activeCount: {
372
- get: () => activeCount
373
- },
374
- pendingCount: {
375
- get: () => queue.size
376
- },
377
- clearQueue: {
378
- value() {
379
- queue.clear();
380
- }
381
- },
382
- concurrency: {
383
- get: () => concurrency,
384
- set(newConcurrency) {
385
- validateConcurrency(newConcurrency);
386
- concurrency = newConcurrency;
387
- queueMicrotask(() => {
388
- while (activeCount < concurrency && queue.size > 0) {
389
- resumeNext();
390
- }
391
- });
392
- }
393
- }
394
- });
395
- return generator;
396
- }
397
- function validateConcurrency(concurrency) {
398
- if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
399
- throw new TypeError("Expected `concurrency` to be a number from 1 and up");
400
- }
401
- }
402
-
403
- // src/constants.ts
404
- var SKIPPED_DURING_RESTORE = /* @__PURE__ */ new Set(["dialog", "choice", "input", "vibrate", "text"]);
405
- var BLOCK_EXIT_STATEMENTS = /* @__PURE__ */ new Set(["choice:exit", "condition:exit", "block:exit"]);
406
- var BLOCK_STATEMENTS = /* @__PURE__ */ new Set(["choice", "condition", "block"]);
407
- var AUDIO_ACTIONS = /* @__PURE__ */ new Set(["playMusic", "stopMusic", "playSound", "stopSound", "voice", "stopVoice"]);
408
- var EMPTY_SET = /* @__PURE__ */ new Set();
409
- var DEFAULT_TYPEWRITER_SPEED = "Medium";
410
- var HOWLER_SUPPORTED_FILE_FORMATS = /* @__PURE__ */ new Set([
411
- "mp3",
412
- "mpeg",
413
- "opus",
414
- "ogg",
415
- "oga",
416
- "wav",
417
- "aac",
418
- "caf",
419
- "m4a",
420
- "m4b",
421
- "mp4",
422
- "weba",
423
- "webm",
424
- "dolby",
425
- "flac"
426
- ]);
427
- var SUPPORTED_IMAGE_FILE_FORMATS = /* @__PURE__ */ new Set([
428
- "apng",
429
- "avif",
430
- "gif",
431
- "jpg",
432
- "jpeg",
433
- "jfif",
434
- "pjpeg",
435
- "pjp",
436
- "png",
437
- "svg",
438
- "webp",
439
- "bmp"
440
- ]);
441
- var MAIN_CONTEXT_KEY = "$MAIN";
442
-
443
- // src/utilities/assertions.ts
444
- var isNumber = (val) => {
445
- return typeof val === "number";
446
- };
447
- var isNull = (val) => {
448
- return val === null;
449
- };
450
- var isString = (val) => {
451
- return typeof val === "string";
452
- };
453
- var isFunction = (val) => {
454
- return typeof val === "function";
455
- };
456
- var isPromise = (val) => {
457
- return Boolean(val) && (typeof val === "object" || isFunction(val)) && isFunction(val.then);
458
- };
459
- var isEmpty = (val) => {
460
- return typeof val === "object" && !isNull(val) && Object.keys(val).length === 0;
461
- };
462
- var isCSSImageURL = (url) => {
463
- const startsWith = String.prototype.startsWith.bind(url);
464
- return startsWith("http") || startsWith("/") || startsWith(".") || startsWith("data");
465
- };
466
- var isUserRequiredAction = ([action, ...meta]) => {
467
- return Boolean(action === "custom" && meta[0] && meta[0].requireUserAction);
468
- };
469
- var isBlockStatement = (statement) => {
470
- return BLOCK_STATEMENTS.has(statement);
471
- };
472
- var isBlockExitStatement = (statement) => {
473
- return BLOCK_EXIT_STATEMENTS.has(statement);
474
- };
475
- var isSkippedDuringRestore = (item) => {
476
- return SKIPPED_DURING_RESTORE.has(item);
477
- };
478
- var isAudioAction = (action) => {
479
- return AUDIO_ACTIONS.has(action);
480
- };
481
- var isAction = (element) => {
482
- return Array.isArray(element) && isString(element[0]);
483
- };
484
- var isImageAsset = (asset2) => {
485
- return isString(asset2) && isCSSImageURL(asset2);
486
- };
487
- var isBlockingAction = (action) => {
488
- return isUserRequiredAction(action) || isSkippedDuringRestore(action[0]) && action[0] !== "vibrate";
489
- };
490
- var isAsset = (suspect) => {
491
- return suspect !== null && typeof suspect === "object" && "source" in suspect && "type" in suspect;
492
- };
493
-
494
- // src/utilities/match-action.ts
495
- var matchAction = (callbacks, values) => {
496
- const { getContext, onBeforeActionCall, push, forward } = callbacks;
497
- const match = (action, props, { ctx, data }) => {
498
- const context = typeof ctx === "string" ? getContext(ctx) : ctx;
499
- onBeforeActionCall({
500
- action,
501
- props,
502
- ctx: context
503
- });
504
- return values[action](
505
- {
506
- ctx: context,
507
- data,
508
- push() {
509
- if (context.meta.preview) return;
510
- push(context);
511
- },
512
- forward() {
513
- if (context.meta.preview) return;
514
- forward(context);
515
- }
516
- },
517
- props
518
- );
519
- };
520
- return {
521
- match,
522
- nativeActions: Object.keys(values)
523
- };
524
- };
525
-
526
- // src/utilities/ungrupped.ts
527
- var getLanguage = (languages) => {
528
- let { language } = navigator;
529
- if (languages.includes(language)) {
530
- return language;
531
- } else if (languages.includes(language = language.slice(0, 2))) {
532
- return language;
533
- } else if (language = languages.find((value) => navigator.languages.includes(value))) {
534
- return language;
535
- }
536
- return languages[0];
537
- };
538
- var noop = () => {
539
- };
540
- var mapSet = (set2, fn) => {
541
- return [...set2].map(fn);
542
- };
543
- var capitalize = (str) => {
544
- return str[0].toUpperCase() + str.slice(1);
545
- };
546
- var getIntlLanguageDisplayName = memoize((lang) => {
547
- try {
548
- const intl = new Intl.DisplayNames([lang], {
549
- type: "language"
550
- });
551
- return intl.of(lang) || lang;
552
- } catch {
553
- return lang;
554
- }
555
- });
556
- var unwrapAsset = (asset2) => {
557
- return isAsset(asset2) ? asset2.source : asset2;
558
- };
559
- var handleAudioAsset = (asset2) => {
560
- if (DEV && isAsset(asset2) && asset2.type !== "audio") {
561
- throw new Error("Attempt to use non-audio asset in audio action", { cause: asset2 });
562
- }
563
- return unwrapAsset(asset2);
564
- };
565
- var handleImageAsset = (asset2) => {
566
- if (DEV && isAsset(asset2) && asset2.type !== "image") {
567
- throw new Error("Attempt to use non-image asset in action that requires image assets", { cause: asset2 });
568
- }
569
- return unwrapAsset(asset2);
570
- };
571
- var getCharactersData = (characters) => {
572
- const entries = Object.entries(characters);
573
- const mapped = entries.map(([key, value]) => [key, { name: value.name, emotions: Object.keys(value.emotions) }]);
574
- return Object.fromEntries(mapped);
575
- };
576
- var toArray = (target) => {
577
- return Array.isArray(target) ? target : [target];
578
- };
579
- var getLanguageFromStore = (store2) => {
580
- return store2.get().meta[0];
581
- };
582
- var getVolumeFromStore = (store2) => {
583
- const { meta } = store2.get();
584
- return {
585
- music: meta[2],
586
- sound: meta[3],
587
- voice: meta[4]
588
- };
589
- };
590
-
591
- // src/utilities/actions-processing.ts
592
- var isExitImpossible = (path) => {
593
- const blockStatements = path.filter(([item]) => isBlockStatement(item));
594
- const blockExitStatements = path.filter(([item]) => isBlockExitStatement(item));
595
- if (blockStatements.length === 0 && blockExitStatements.length === 0) {
596
- return true;
597
- }
598
- if (blockStatements.length > blockExitStatements.length) {
599
- return false;
600
- }
601
- return !blockExitStatements.every(([name], i) => name && name.startsWith(blockStatements[i][0]));
602
- };
603
- var createReferFunction = ({ story, onUnknownSceneHit }) => {
604
- const refer = async (path) => {
605
- const { promise: ready, resolve: setReady } = Promise.withResolvers();
606
- let current = story;
607
- let precurrent = story;
608
- const blocks = [];
609
- const refer2 = async () => {
610
- for (const [type, val] of path) {
611
- if (type === "jump") {
612
- if (!current[val]) {
613
- setReady(true);
614
- await onUnknownSceneHit(val);
615
- }
616
- if (DEV && !story[val]) {
617
- throw new Error(`Attempt to jump to unknown scene "${val}"`);
618
- }
619
- if (DEV && story[val].length === 0) {
620
- throw new Error(`Attempt to jump to empty scene "${val}"`);
621
- }
622
- precurrent = story;
623
- current = current[val];
624
- } else if (type === null) {
625
- precurrent = current;
626
- current = current[val];
627
- } else if (type === "choice") {
628
- blocks.push(precurrent);
629
- current = current[val + 1][1];
630
- } else if (type === "condition") {
631
- blocks.push(precurrent);
632
- current = current[2][val];
633
- } else if (type === "block") {
634
- blocks.push(precurrent);
635
- current = story[val];
636
- } else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
637
- current = blocks.pop();
638
- }
639
- }
640
- setReady(false);
641
- return current;
642
- };
643
- const value = refer2();
644
- const found = await ready;
645
- return {
646
- found,
647
- value
648
- };
649
- };
650
- const referGuarded = async (path) => {
651
- return await (await refer(path)).value;
652
- };
653
- return {
654
- refer,
655
- referGuarded
656
- };
657
- };
658
- var exitPath = async ({ path, refer, onExitImpossible }) => {
659
- const last = path.at(-1);
660
- const ignore = [];
661
- let wasExitImpossible = false;
662
- if (!isAction(await refer(path))) {
663
- if (last && isNull(last[0]) && isNumber(last[1])) {
664
- last[1]--;
665
- } else {
666
- path.pop();
667
- }
668
- }
669
- if (isExitImpossible(path)) {
670
- const referred = await refer(path);
671
- if (isAction(referred) && isSkippedDuringRestore(referred[0])) {
672
- onExitImpossible?.();
673
- }
674
- wasExitImpossible = true;
675
- return {
676
- exitImpossible: wasExitImpossible
677
- };
678
- }
679
- for (let i = path.length - 1; i > 0; i--) {
680
- const [name] = path[i];
681
- if (isBlockExitStatement(name)) {
682
- ignore.push(name);
683
- }
684
- if (!isBlockStatement(name)) continue;
685
- if (ignore.at(-1)?.startsWith(name)) {
686
- ignore.pop();
687
- continue;
688
- }
689
- path.push([`${name}:exit`]);
690
- const prev = findLastPathItemBeforeItemOfType(path.slice(0, i + 1), name);
691
- if (prev) path.push([null, prev[1] + 1]);
692
- if (!isAction(await refer(path))) {
693
- path.pop();
694
- continue;
695
- }
696
- break;
697
- }
698
- return {
699
- exitImpossible: wasExitImpossible
700
- };
701
- };
702
- var nextPath = (path) => {
703
- const last = path.at(-1);
704
- if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
705
- last[1]++;
706
- } else {
707
- path.push([null, 0]);
708
- }
709
- return path;
710
- };
711
- var collectActionsBeforeBlockingAction = async ({
712
- path,
713
- refer,
714
- clone
715
- }) => {
716
- const collection = [];
717
- let action = await refer(path);
718
- while (true) {
719
- if (action == void 0) {
720
- const { exitImpossible } = await exitPath({
721
- path,
722
- refer
723
- });
724
- if (exitImpossible) {
725
- break;
726
- }
727
- }
728
- if (!action) {
729
- break;
730
- }
731
- if (isBlockingAction(action)) {
732
- const [name, ...props] = action;
733
- if (name === "choice") {
734
- const choiceProps = props;
735
- for (let i = 0; i < choiceProps.length; i++) {
736
- const branchContent = choiceProps[i];
737
- if (!Array.isArray(branchContent)) continue;
738
- const virtualPath = clone(path);
739
- virtualPath.push(["choice", i], [null, 0]);
740
- const innerActions = await collectActionsBeforeBlockingAction({
741
- path: virtualPath,
742
- refer,
743
- clone
744
- });
745
- collection.push(...innerActions);
746
- }
747
- } else if (name === "condition") {
748
- const conditionProps = props;
749
- const conditions = Object.keys(conditionProps[1]);
750
- for (const condition of conditions) {
751
- const virtualPath = clone(path);
752
- virtualPath.push(["condition", condition], [null, 0]);
753
- const innerActions = await collectActionsBeforeBlockingAction({
754
- path: virtualPath,
755
- refer,
756
- clone
757
- });
758
- collection.push(...innerActions);
759
- }
760
- }
761
- break;
762
- }
763
- collection.push(action);
764
- if (action[0] === "jump") {
765
- path = [
766
- ["jump", action[1]],
767
- [null, 0]
768
- ];
769
- } else if (action[0] == "block") {
770
- path.push(["block", action[1]], [null, 0]);
771
- } else {
772
- nextPath(path);
773
- }
774
- action = await refer(path);
775
- }
776
- return collection;
777
- };
778
- var findLastPathItemBeforeItemOfType = (path, name) => {
779
- const item = path.findLast(([_name, _value], i, array) => {
780
- const next = array[i + 1];
781
- return isNull(_name) && isNumber(_value) && next != null && next[0] === name;
782
- });
783
- return item;
784
- };
785
- var getOppositeAction = (action) => {
786
- const MAP = {
787
- showCharacter: "hideCharacter",
788
- playSound: "stopSound",
789
- playMusic: "stopMusic",
790
- voice: "stopVoice"
791
- };
792
- return MAP[action];
793
- };
794
- var getActionsFromPath = async ({ story, path, filter, referGuarded }) => {
795
- let current = story;
796
- let precurrent;
797
- let ignoreNestedBefore = null;
798
- let index = 0;
799
- let skipPreserve = void 0;
800
- const skip = /* @__PURE__ */ new Set();
801
- const max = path.reduce((acc, [type, val]) => {
802
- if (isNull(type) && isNumber(val)) {
803
- return acc + 1;
804
- }
805
- return acc;
806
- }, 0);
807
- const queue = [];
808
- const blocks = [];
809
- await referGuarded(path);
810
- for (const [type, val] of path) {
811
- if (type === "jump") {
812
- precurrent = story;
813
- current = current[val];
814
- } else if (type === null) {
815
- precurrent = current;
816
- if (isNumber(val)) {
817
- index++;
818
- let startIndex = 0;
819
- if (ignoreNestedBefore) {
820
- const prev = findLastPathItemBeforeItemOfType(path.slice(0, index), ignoreNestedBefore);
821
- if (prev) {
822
- startIndex = prev[1];
823
- ignoreNestedBefore = null;
824
- }
825
- }
826
- for (let i = startIndex; i <= val; i++) {
827
- const item = current[i];
828
- if (!isAction(item)) continue;
829
- const [action] = item;
830
- const last = index === max && i === val;
831
- const shouldSkip = isSkippedDuringRestore(action) || isUserRequiredAction(item);
832
- if (shouldSkip) {
833
- skip.add(item);
834
- }
835
- if (shouldSkip && last) {
836
- skipPreserve = item;
837
- }
838
- if (filter && shouldSkip && !last) {
839
- continue;
840
- } else {
841
- queue.push(item);
842
- }
843
- }
844
- }
845
- current = current[val];
846
- } else if (type === "choice") {
847
- blocks.push(precurrent);
848
- current = current[val + 1][1];
849
- } else if (type === "condition") {
850
- blocks.push(precurrent);
851
- current = current[2][val];
852
- } else if (type === "block") {
853
- blocks.push(precurrent);
854
- current = story[val];
855
- } else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
856
- current = blocks.pop();
857
- ignoreNestedBefore = type.slice(0, -5);
858
- }
859
- }
860
- return {
861
- queue,
862
- skip,
863
- skipPreserve
864
- };
865
- };
866
- var createQueueProcessor = (queue, options) => {
867
- const processedQueue = [];
868
- const keep = /* @__PURE__ */ new Set();
869
- const characters = /* @__PURE__ */ new Set();
870
- const audio2 = {
871
- music: /* @__PURE__ */ new Set(),
872
- sounds: /* @__PURE__ */ new Set()
873
- };
874
- const next = (i) => queue.slice(i + 1);
875
- for (const [i, item] of queue.entries()) {
876
- const [action, ...params] = item;
877
- if (options.skip.has(item) && item !== options.skipPreserve) {
878
- continue;
879
- }
880
- keep.add(action);
881
- if (action === "function" || action === "custom") {
882
- if (action === "custom") {
883
- const fn = params[0];
884
- if ("callOnlyLatest" in fn && fn.callOnlyLatest) {
885
- const notLatest = next(i).some(([name, func]) => {
886
- if (name !== "custom") return;
887
- const isIdenticalId = Boolean(func.id && fn.id && func.id === fn.id);
888
- const isIdenticalByReference = func === fn;
889
- const isIdenticalByCode = String(func) === String(fn);
890
- return isIdenticalId || isIdenticalByReference || isIdenticalByCode;
891
- });
892
- if (notLatest) continue;
893
- } else if ("skipOnRestore" in fn && fn.skipOnRestore) {
894
- if (fn.skipOnRestore(next(i))) {
895
- continue;
896
- }
897
- }
898
- }
899
- processedQueue.push(item);
900
- } else if (action === "playSound") {
901
- const closing = getOppositeAction(action);
902
- const skip = next(i).some((item2) => {
903
- if (isUserRequiredAction(item2) || isSkippedDuringRestore(item2[0])) {
904
- return true;
905
- }
906
- const [_action, target] = item2;
907
- if (target !== params[0]) {
908
- return false;
909
- }
910
- return _action === closing || _action === action;
911
- });
912
- if (skip) continue;
913
- audio2.sounds.add(unwrapAsset(params[0]));
914
- processedQueue.push(item);
915
- } else if (action === "showCharacter" || action === "playMusic" || action === "voice") {
916
- const closing = getOppositeAction(action);
917
- const skip = next(i).some(([_action, target]) => {
918
- if (target !== params[0] && action !== "voice") {
919
- return false;
920
- }
921
- const musicWillBePaused = action === "playMusic" && _action === "pauseMusic";
922
- return musicWillBePaused || _action === closing || _action === action;
923
- });
924
- if (skip) continue;
925
- if (action === "showCharacter") {
926
- characters.add(params[0]);
927
- } else if (action === "playMusic") {
928
- audio2.music.add(unwrapAsset(params[0]));
929
- }
930
- processedQueue.push(item);
931
- } else if (action === "showBackground" || action === "preload") {
932
- const skip = next(i).some(([_action]) => action === _action);
933
- if (skip) continue;
934
- processedQueue.push(item);
935
- } else if (action === "animateCharacter") {
936
- const skip = next(i).some(([_action, character], j, array) => {
937
- if (action === _action && character === params[0]) {
938
- return true;
939
- }
940
- const next2 = array.slice(j);
941
- const characterWillAnimate = next2.some(([__action, __character]) => action === __action);
942
- const hasBlockingActions = next2.some((item2) => options.skip.has(item2));
943
- const differentCharacterWillAnimate = !hasBlockingActions && next2.some(([__action, __character]) => __action === action && __character !== params[0]);
944
- return characterWillAnimate && hasBlockingActions || differentCharacterWillAnimate;
945
- });
946
- if (skip) continue;
947
- processedQueue.push(item);
948
- } else {
949
- processedQueue.push(item);
950
- }
951
- }
952
- const run = async (match) => {
953
- for (const item of processedQueue) {
954
- const result = match(item);
955
- if (isPromise(result)) {
956
- await result;
957
- }
958
- }
959
- processedQueue.length = 0;
960
- };
961
- return {
962
- run,
963
- keep: {
964
- keep,
965
- characters,
966
- audio: audio2
967
- }
968
- };
969
- };
970
-
971
- // src/utilities/controlled-promise.ts
972
- var createControlledPromise = () => {
973
- const object = {
974
- resolve: null,
975
- reject: null,
976
- promise: null,
977
- cancel: null
978
- };
979
- const init = () => {
980
- const promise = new Promise((resolve, reject) => {
981
- object.reject = reject;
982
- object.resolve = (value) => {
983
- resolve({ cancelled: false, value });
984
- };
985
- object.cancel = () => {
986
- resolve({ cancelled: true, value: null });
987
- init();
988
- };
989
- });
990
- object.promise = promise;
991
- };
992
- return init(), object;
993
- };
994
-
995
- // src/utilities/resources.ts
996
- var getUrlFileExtension = (address) => {
997
- try {
998
- const { pathname } = new URL(address, location.href);
999
- return pathname.split(".").at(-1).split("!")[0].split(":")[0];
1000
- } catch (error) {
1001
- if (DEV) {
1002
- console.error(new Error(`Could not construct URL "${address}".`, { cause: error }));
1003
- }
1004
- return "";
1005
- }
1006
- };
1007
- var fetchContentType = async (url, request) => {
1008
- try {
1009
- const response = await request(url, {
1010
- method: "HEAD"
1011
- });
1012
- return response.headers.get("Content-Type") || "";
1013
- } catch (error) {
1014
- if (DEV) {
1015
- console.error(new Error(`Failed to fetch file at "${url}"`, { cause: error }));
1016
- }
1017
- return "";
1018
- }
1019
- };
1020
- var getResourseType = memoize(
1021
- async ({ url, request }) => {
1022
- if (!isCSSImageURL(url)) {
1023
- return "other";
1024
- }
1025
- const extension = getUrlFileExtension(url);
1026
- if (HOWLER_SUPPORTED_FILE_FORMATS.has(extension)) {
1027
- return "audio";
1028
- }
1029
- if (SUPPORTED_IMAGE_FILE_FORMATS.has(extension)) {
1030
- return "image";
1031
- }
1032
- const contentType = await fetchContentType(url, request);
1033
- if (contentType.includes("audio")) {
1034
- return "audio";
1035
- }
1036
- if (contentType.includes("image")) {
1037
- return "image";
1038
- }
1039
- return "other";
1040
- },
1041
- {
1042
- getCacheKey: ({ url }) => url
1043
- }
1044
- );
1045
-
1046
- // src/shared.ts
1047
- var STACK_MAP = /* @__PURE__ */ new Map();
1048
- var CUSTOM_ACTION_MAP = /* @__PURE__ */ new Map();
1049
- var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
1050
- var ASSETS_TO_PRELOAD = /* @__PURE__ */ new Set();
1051
-
1052
- // src/utilities/stack.ts
1053
- var getStack = memoize(
1054
- (_) => {
1055
- return [];
1056
- },
1057
- {
1058
- cache: STACK_MAP,
1059
- getCacheKey: (ctx) => ctx.id
1060
- }
1061
- );
1062
- var createUseStackFunction = (renderer) => {
1063
- const useStack = (context) => {
1064
- const ctx = typeof context === "string" ? renderer.getContext(context) : context;
1065
- const stack = getStack(ctx);
1066
- return {
1067
- get previous() {
1068
- return stack.previous;
1069
- },
1070
- get value() {
1071
- return stack.at(-1);
1072
- },
1073
- set value(value) {
1074
- stack[stack.length - 1] = value;
1075
- },
1076
- back() {
1077
- stack.previous = stack.length > 1 ? stack.pop() : this.value;
1078
- ctx.meta.goingBack = true;
1079
- },
1080
- push(value) {
1081
- stack.push(value);
1082
- },
1083
- clear() {
1084
- stack.previous = void 0;
1085
- stack.length = 0;
1086
- stack.length = 1;
1087
- }
1088
- };
1089
- };
1090
- return useStack;
1091
- };
1092
-
1093
- // src/utilities/story.ts
1094
- var flatActions = (item) => {
1095
- return item.flatMap((data) => {
1096
- const type = data[0];
1097
- if (Array.isArray(type)) return flatActions(data);
1098
- return [data];
1099
- });
1100
- };
1101
- var flatStory = (story) => {
1102
- for (const key in story) {
1103
- story[key] = flatActions(story[key]);
1104
- }
1105
- return story;
1106
- };
1107
-
1108
- // src/browser.ts
1109
- var setupBrowserVisibilityChangeListeners = ({ onChange }) => {
1110
- if (typeof document === "undefined") return noop;
1111
- const onVisibilityChange = () => {
1112
- if (document.visibilityState === "hidden") {
1113
- onChange();
1114
- }
1115
- };
1116
- addEventListener("visibilitychange", onVisibilityChange);
1117
- addEventListener("beforeunload", onChange);
1118
- return () => {
1119
- removeEventListener("visibilitychange", onVisibilityChange);
1120
- removeEventListener("beforeunload", onChange);
1121
- };
1122
- };
1123
-
1124
- // src/custom-action.ts
1125
- var createCustomActionNode = (id) => {
1126
- const div = document.createElement("div");
1127
- div.setAttribute("data-id", id);
1128
- return div;
1129
- };
1130
- var getCustomActionHolder = (ctx, fn) => {
1131
- const cached = CUSTOM_ACTION_MAP.get(ctx.id + fn.key);
1132
- if (cached) {
1133
- return cached;
1134
- }
1135
- const holder = {
1136
- cleanup: noop,
1137
- node: null,
1138
- fn,
1139
- localData: {}
1140
- };
1141
- CUSTOM_ACTION_MAP.set(ctx.id + fn.key, holder);
1142
- return holder;
1143
- };
1144
- var handleCustomAction = (ctx, fn, {
1145
- lang,
1146
- state,
1147
- setMountElement,
1148
- setClear,
1149
- remove: renderersRemove,
1150
- getStack: getStack2,
1151
- templateReplace
1152
- }) => {
1153
- const holder = getCustomActionHolder(ctx, fn);
1154
- const flags = {
1155
- ...ctx.meta
1156
- };
1157
- const getDomNodes = (insert = true) => {
1158
- if (holder.node || !insert) {
1159
- return {
1160
- element: holder.node,
1161
- root: ctx.root
1162
- };
1163
- }
1164
- holder.node = insert ? createCustomActionNode(fn.key) : null;
1165
- setMountElement(holder.node);
1166
- return {
1167
- element: holder.node,
1168
- root: ctx.root
1169
- };
1170
- };
1171
- const clear = (func) => {
1172
- setClear(
1173
- holder.cleanup = () => {
1174
- func();
1175
- holder.node = null;
1176
- holder.cleanup = noop;
1177
- setMountElement(null);
1178
- setClear(noop);
1179
- }
1180
- );
1181
- };
1182
- const data = (updatedData) => {
1183
- if (updatedData) {
1184
- return holder.localData = updatedData;
1185
- }
1186
- return holder.localData;
1187
- };
1188
- const remove = () => {
1189
- holder.cleanup();
1190
- renderersRemove();
1191
- };
1192
- const stack = getStack2(ctx);
1193
- const getPath = () => {
1194
- return stack.value[0];
1195
- };
1196
- return fn({
1197
- flags,
1198
- lang,
1199
- state,
1200
- data,
1201
- templateReplace,
1202
- clear,
1203
- remove,
1204
- rendererContext: ctx,
1205
- getDomNodes,
1206
- getPath,
1207
- contextKey: ctx.id
1208
- });
1209
- };
1210
-
1211
- // src/preloading.ts
1212
- var ACTION_NAME_TO_VOLUME_MAP = {
1213
- playMusic: "music",
1214
- playSound: "sound",
1215
- voice: "voice"
1216
- };
1217
- var enqueueAssetForPreloading = (asset2) => {
1218
- if (!PRELOADED_ASSETS.has(asset2)) {
1219
- ASSETS_TO_PRELOAD.add(asset2);
1220
- }
1221
- };
1222
- var handleAssetsPreloading = async ({
1223
- request,
1224
- limiter,
1225
- preloadAudioBlocking,
1226
- preloadImageBlocking
1227
- }) => {
1228
- const list = mapSet(ASSETS_TO_PRELOAD, (asset2) => {
1229
- return limiter(async () => {
1230
- const type = await getResourseType({
1231
- url: asset2,
1232
- request
1233
- });
1234
- switch (type) {
1235
- case "audio": {
1236
- await preloadAudioBlocking(asset2);
1237
- break;
1238
- }
1239
- case "image": {
1240
- await preloadImageBlocking(asset2);
1241
- break;
1242
- }
1243
- }
1244
- ASSETS_TO_PRELOAD.delete(asset2);
1245
- PRELOADED_ASSETS.add(asset2);
1246
- });
1247
- });
1248
- await Promise.allSettled(list);
1249
- ASSETS_TO_PRELOAD.clear();
1250
- };
1251
- var huntAssets = ({ volume, lang, mode, characters, action, props, handle }) => {
1252
- if (action === "showBackground") {
1253
- if (isString(props[0])) {
1254
- handle(handleAudioAsset(props[0]));
1255
- }
1256
- if (props[0] && typeof props[0] === "object") {
1257
- for (const value of Object.values(props[0])) {
1258
- if (isImageAsset(value)) {
1259
- handle(value);
1260
- } else if (isAsset(value)) {
1261
- const unwrapped = handleImageAsset(value);
1262
- if (isImageAsset(unwrapped)) {
1263
- handle(unwrapped);
1264
- }
1265
- }
1266
- }
1267
- }
1268
- return;
1269
- }
1270
- const getVolumeFor = (action2) => {
1271
- if (action2 in ACTION_NAME_TO_VOLUME_MAP) {
1272
- return volume[ACTION_NAME_TO_VOLUME_MAP[action2]];
1273
- }
1274
- return 0;
1275
- };
1276
- if (isAudioAction(action) && isString(props[0])) {
1277
- if (getVolumeFor(action) > 0) {
1278
- handle(handleAudioAsset(props[0]));
1279
- }
1280
- return;
1281
- }
1282
- if (action === "voice" && typeof props[0] === "object") {
1283
- if (getVolumeFor("voice") == 0) {
1284
- return;
1285
- }
1286
- for (const [language, value] of Object.entries(props[0])) {
1287
- if (mode === "blocking") {
1288
- value && handle(handleAudioAsset(value));
1289
- } else if (language === lang) {
1290
- value && handle(handleAudioAsset(value));
1291
- }
1292
- }
1293
- return;
1294
- }
1295
- if (action === "showCharacter" && isString(props[0]) && isString(props[1])) {
1296
- const images = toArray(characters[props[0]].emotions[props[1]]);
1297
- for (const asset2 of images) {
1298
- handle(handleImageAsset(asset2));
1299
- }
1300
- return;
1301
- }
1302
- if (action === "custom" && props[0].assets) {
1303
- for (const asset2 of props[0].assets) {
1304
- isAsset(asset2) ? handle(asset2.source) : handle(asset2);
1305
- }
1306
- return;
1307
- }
1308
- if (action === "choice") {
1309
- for (let i = 1; i < props.length; i++) {
1310
- const data = props[i];
1311
- if (Array.isArray(data)) {
1312
- handle(handleImageAsset(data[5]));
1313
- }
1314
- }
1315
- }
1316
- };
1317
-
1318
- // src/storage.ts
1319
- var localStorageStorage = (options) => {
1320
- return {
1321
- async get() {
1322
- const fallback = { saves: [], data: {}, meta: [] };
1323
- try {
1324
- const value = localStorage.getItem(options.key);
1325
- return value ? JSON.parse(value) : fallback;
1326
- } catch {
1327
- return fallback;
1328
- }
1329
- },
1330
- async set(data) {
1331
- try {
1332
- localStorage.setItem(options.key, JSON.stringify(data));
1333
- } catch {
1334
- }
1335
- }
1336
- };
1337
- };
1338
-
1339
- // src/store.ts
1340
- var store = (current, subscribers = /* @__PURE__ */ new Set()) => {
1341
- const subscribe = (cb) => {
1342
- subscribers.add(cb), cb(current);
1343
- return () => {
1344
- subscribers.delete(cb);
1345
- };
1346
- };
1347
- const push = (value) => {
1348
- for (const cb of subscribers) cb(value);
1349
- };
1350
- const update = (fn) => {
1351
- push(current = fn(current));
1352
- };
1353
- const set2 = (val) => {
1354
- update(() => val);
1355
- };
1356
- const get = () => {
1357
- return current;
1358
- };
1359
- return { subscribe, update, set: set2, get };
1360
- };
1361
-
1362
- // src/translation.ts
1363
- var RGX = /{{(.*?)}}/g;
1364
- var split = (input, delimeters) => {
1365
- const output = [];
1366
- for (const delimeter of delimeters) {
1367
- if (!input) break;
1368
- const [start, end] = input.split(delimeter, 2);
1369
- output.push(start);
1370
- input = end;
1371
- }
1372
- output.push(input);
1373
- return output;
1374
- };
1375
- var flattenAllowedContent = (c, state) => {
1376
- if (Array.isArray(c)) {
1377
- return c.map((item) => flattenAllowedContent(item, state)).join("<br>");
1378
- }
1379
- if (typeof c === "function") {
1380
- return flattenAllowedContent(c(state), state);
1381
- }
1382
- return c;
1383
- };
1384
- var replace = (input, data, pluralization, actions, pr) => {
1385
- return input.replaceAll(RGX, (x, key, y) => {
1386
- x = 0;
1387
- y = data;
1388
- const [pathstr, plural, action] = split(key.trim(), ["@", "%"]);
1389
- if (!pathstr) {
1390
- return "";
1391
- }
1392
- const path = pathstr.split(".");
1393
- while (y && x < path.length) y = y[path[x++]];
1394
- if (plural && pluralization && y && pr) {
1395
- y = pluralization[plural][pr.select(y)];
1396
- }
1397
- const actionHandler = actions && action ? actions[action] : void 0;
1398
- if (actionHandler) y = actionHandler(y);
1399
- return y == null ? "" : y;
1400
- });
1401
- };
1402
-
1403
- // src/utilities/actions.ts
1404
- var VIRTUAL_ACTIONS = ["say"];
1405
- var buildActionObject = ({
1406
- rendererActions,
1407
- nativeActions,
1408
- characters,
1409
- preloadAssets,
1410
- storageData
1411
- }) => {
1412
- const allActions = [...nativeActions, ...VIRTUAL_ACTIONS];
1413
- const object = { ...rendererActions };
1414
- for (let action of allActions) {
1415
- object[action] = (...props) => {
1416
- if (action === "say") {
1417
- action = "dialog";
1418
- const [character] = props;
1419
- if (DEV && !characters[character]) {
1420
- throw new Error(`Attempt to call Say action with unknown character "${character}"`);
1421
- }
1422
- } else if (action === "choice") {
1423
- if (props.slice(1).every((choice) => !Array.isArray(choice))) {
1424
- for (let i = 1; i < props.length; i++) {
1425
- const choice = props[i];
1426
- props[i] = [
1427
- choice.title,
1428
- flatActions(choice.children),
1429
- choice.active,
1430
- choice.visible,
1431
- choice.onSelect,
1432
- choice.image
1433
- ];
1434
- }
1435
- } else {
1436
- for (let i = 1; i < props.length; i++) {
1437
- const choice = props[i];
1438
- if (Array.isArray(choice)) {
1439
- choice[1] = flatActions(choice[1]);
1440
- }
1441
- }
1442
- }
1443
- } else if (action === "condition") {
1444
- const actions = props[1];
1445
- for (const key in actions) {
1446
- actions[key] = flatActions(actions[key]);
1447
- }
1448
- }
1449
- if (preloadAssets === "blocking") {
1450
- huntAssets({
1451
- action,
1452
- props,
1453
- mode: preloadAssets,
1454
- characters,
1455
- lang: getLanguageFromStore(storageData),
1456
- volume: getVolumeFromStore(storageData),
1457
- handle: enqueueAssetForPreloading
1458
- });
1459
- }
1460
- return [action, ...props];
1461
- };
1462
- }
1463
- return object;
1464
- };
1465
-
1466
- // src/novely.ts
1467
- var novely = ({
1468
- characters,
1469
- characterAssetSizes = {},
1470
- defaultEmotions = {},
1471
- storage = localStorageStorage({ key: "novely-game-storage" }),
1472
- storageDelay = Promise.resolve(),
1473
- renderer: createRenderer,
1474
- initialScreen = "mainmenu",
1475
- translation,
1476
- state: defaultState = {},
1477
- data: defaultData = {},
1478
- autosaves = true,
1479
- migrations = [],
1480
- throttleTimeout = 799,
1481
- getLanguage: getLanguage2 = getLanguage,
1482
- overrideLanguage = false,
1483
- askBeforeExit = true,
1484
- preloadAssets = "lazy",
1485
- parallelAssetsDownloadLimit = 15,
1486
- fetch: request = fetch,
1487
- cloneFunction: clone = klona,
1488
- saveOnUnload = true,
1489
- startKey = "start",
1490
- defaultTypewriterSpeed = DEFAULT_TYPEWRITER_SPEED,
1491
- storyOptions = { mode: "static" }
1492
- }) => {
1493
- const languages = Object.keys(translation);
1494
- const limitScript = pLimit(1);
1495
- const limitAssetsDownload = pLimit(parallelAssetsDownloadLimit);
1496
- const story = {};
1497
- const times = /* @__PURE__ */ new Set();
1498
- const dataLoaded = createControlledPromise();
1499
- let initialScreenWasShown = false;
1500
- let destroyed = false;
1501
- if (storyOptions.mode === "dynamic") {
1502
- storyOptions.preloadSaves ??= 4;
1503
- }
1504
- const storyLoad = storyOptions.mode === "static" ? noop : storyOptions.load;
1505
- const onUnknownSceneHit = memoize(async (scene) => {
1506
- const part = await storyLoad(scene);
1507
- if (part) {
1508
- await script(part);
1509
- }
1510
- });
1511
- const intime = (value) => {
1512
- return times.add(value), value;
1513
- };
1514
- const scriptBase = async (part) => {
1515
- if (destroyed) return;
1516
- Object.assign(story, flatStory(part));
1517
- if (!initialScreenWasShown) {
1518
- renderer.ui.showLoading();
1519
- }
1520
- if (preloadAssets === "blocking" && ASSETS_TO_PRELOAD.size > 0) {
1521
- renderer.ui.showLoading();
1522
- await handleAssetsPreloading({
1523
- ...renderer.misc,
1524
- limiter: limitAssetsDownload,
1525
- request
1526
- });
1527
- }
1528
- await dataLoaded.promise;
1529
- renderer.ui.hideLoading();
1530
- if (!initialScreenWasShown) {
1531
- initialScreenWasShown = true;
1532
- if (initialScreen === "game") {
1533
- restore(void 0);
1534
- } else {
1535
- renderer.ui.showScreen(initialScreen);
1536
- }
1537
- }
1538
- };
1539
- const script = (part) => {
1540
- return limitScript(() => scriptBase(part));
1541
- };
1542
- const getDefaultSave = (state) => {
1543
- return [
1544
- [
1545
- ["jump", startKey],
1546
- [null, 0]
1547
- ],
1548
- state,
1549
- [intime(Date.now()), "auto"],
1550
- []
1551
- ];
1552
- };
1553
- const getLanguageWithoutParameters = () => {
1554
- const language = getLanguage2(languages, getLanguage);
1555
- if (languages.includes(language)) {
1556
- return language;
1557
- }
1558
- if (DEV) {
1559
- throw new Error(
1560
- `Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`
1561
- );
1562
- }
1563
- throw 0;
1564
- };
1565
- const initialData = {
1566
- saves: [],
1567
- data: clone(defaultData),
1568
- meta: [getLanguageWithoutParameters(), DEFAULT_TYPEWRITER_SPEED, 1, 1, 1]
1569
- };
1570
- const storageData = store(initialData);
1571
- const coreData = store({
1572
- dataLoaded: false
1573
- });
1574
- const onDataLoadedPromise = async ({ cancelled }) => {
1575
- if (cancelled) {
1576
- dataLoaded.promise.then(onDataLoadedPromise);
1577
- return;
1578
- }
1579
- const preload = () => {
1580
- const saves = [...storageData.get().saves].reverse();
1581
- const sliced = saves.slice(0, storyOptions.mode === "dynamic" ? storyOptions.preloadSaves : 0);
1582
- for (const [path] of sliced) {
1583
- referGuarded(path);
1584
- }
1585
- };
1586
- if (preloadAssets === "blocking") {
1587
- await preload();
1588
- } else {
1589
- void preload();
1590
- }
1591
- coreData.update((data2) => {
1592
- data2.dataLoaded = true;
1593
- return data2;
1594
- });
1595
- };
1596
- dataLoaded.promise.then(onDataLoadedPromise);
1597
- const onStorageDataChange = (value) => {
1598
- if (!coreData.get().dataLoaded) return;
1599
- const data2 = clone(value);
1600
- for (const save2 of data2.saves) {
1601
- save2[3] = [];
1602
- }
1603
- storage.set(data2);
1604
- };
1605
- const throttledOnStorageDataChange = throttle(onStorageDataChange, throttleTimeout);
1606
- const throttledEmergencyOnStorageDataChange = throttle(() => {
1607
- if (saveOnUnload === true || saveOnUnload === "prod" && !DEV) {
1608
- onStorageDataChange(storageData.get());
1609
- }
1610
- }, 10);
1611
- storageData.subscribe(throttledOnStorageDataChange);
1612
- const getStoredData = async () => {
1613
- let stored = await storage.get();
1614
- for (const migration of migrations) {
1615
- stored = migration(stored);
1616
- if (DEV && !stored) {
1617
- throw new Error("Migrations should return a value.");
1618
- }
1619
- }
1620
- if (overrideLanguage || !stored.meta[0]) {
1621
- stored.meta[0] = getLanguageWithoutParameters();
1622
- }
1623
- stored.meta[1] ||= defaultTypewriterSpeed;
1624
- stored.meta[2] ??= 1;
1625
- stored.meta[3] ??= 1;
1626
- stored.meta[4] ??= 1;
1627
- if (isEmpty(stored.data)) {
1628
- stored.data = defaultData;
1629
- }
1630
- dataLoaded.resolve();
1631
- storageData.set(stored);
1632
- };
1633
- storageDelay.then(getStoredData);
1634
- const initial = getDefaultSave(clone(defaultState));
1635
- const unsubscribeFromBrowserVisibilityChange = setupBrowserVisibilityChangeListeners({
1636
- onChange: throttledEmergencyOnStorageDataChange
1637
- });
1638
- const save = (type) => {
1639
- if (!coreData.get().dataLoaded) return;
1640
- if (!autosaves && type === "auto") return;
1641
- const stack = useStack(MAIN_CONTEXT_KEY);
1642
- const current = clone(stack.value);
1643
- storageData.update((prev) => {
1644
- const replace2 = () => {
1645
- prev.saves[prev.saves.length - 1] = current;
1646
- return prev;
1647
- };
1648
- const add = () => {
1649
- prev.saves.push(current);
1650
- return prev;
1651
- };
1652
- const last = prev.saves.at(-1);
1653
- if (!last) return add();
1654
- current[2][0] = intime(Date.now());
1655
- current[2][1] = type;
1656
- current[3] = [];
1657
- const isIdentical = dequal(last[0], current[0]) && dequal(last[1], current[1]);
1658
- const isLastMadeInCurrentSession = times.has(last[2][0]);
1659
- if (isLastMadeInCurrentSession && last[2][1] === "auto" && type === "manual") {
1660
- return replace2();
1661
- }
1662
- if (last[2][1] === "manual" && type === "auto" && isIdentical) {
1663
- return prev;
1664
- }
1665
- if (isLastMadeInCurrentSession && last[2][1] === "auto" && type === "auto") {
1666
- return replace2();
1667
- }
1668
- return add();
1669
- });
1670
- };
1671
- const newGame = () => {
1672
- if (!coreData.get().dataLoaded) return;
1673
- const save2 = getDefaultSave(clone(defaultState));
1674
- if (autosaves) {
1675
- storageData.update((prev) => {
1676
- return prev.saves.push(save2), prev;
1677
- });
1678
- }
1679
- const context = renderer.getContext(MAIN_CONTEXT_KEY);
1680
- const stack = useStack(context);
1681
- stack.value = save2;
1682
- context.meta.restoring = context.meta.goingBack = false;
1683
- renderer.ui.showScreen("game");
1684
- render(context);
1685
- };
1686
- const set2 = (save2, ctx) => {
1687
- const stack = useStack(ctx || MAIN_CONTEXT_KEY);
1688
- stack.value = save2;
1689
- return restore(save2);
1690
- };
1691
- let interacted = 0;
1692
- const restore = async (save2) => {
1693
- if (isEmpty(story)) {
1694
- if (DEV) {
1695
- throw new Error(
1696
- "Story is empty. You should call an `enine.script` function [https://novely.pages.dev/guide/story.html]"
1697
- );
1698
- }
1699
- return;
1700
- }
1701
- if (!coreData.get().dataLoaded) return;
1702
- let latest = save2 || storageData.get().saves.at(-1);
1703
- if (!latest) {
1704
- latest = clone(initial);
1705
- storageData.update((prev) => {
1706
- prev.saves.push(latest);
1707
- return prev;
1708
- });
1709
- }
1710
- const context = renderer.getContext(MAIN_CONTEXT_KEY);
1711
- const stack = useStack(context);
1712
- context.meta.restoring = true;
1713
- const previous = stack.previous;
1714
- const [path] = stack.value = latest;
1715
- renderer.ui.showScreen("game");
1716
- const { found } = await refer(path);
1717
- if (found) context.loading(true);
1718
- const { queue, skip, skipPreserve } = await getActionsFromPath({
1719
- story,
1720
- path,
1721
- filter: false,
1722
- referGuarded
1723
- });
1724
- const {
1725
- run,
1726
- keep: { keep, characters: characters2, audio: audio2 }
1727
- } = createQueueProcessor(queue, {
1728
- skip,
1729
- skipPreserve
1730
- });
1731
- if (previous) {
1732
- const { queue: prevQueue } = await getActionsFromPath({
1733
- story,
1734
- path: previous[0],
1735
- filter: false,
1736
- referGuarded
1737
- });
1738
- for (let i = prevQueue.length - 1; i > queue.length - 1; i--) {
1739
- const element = prevQueue[i];
1740
- if (!isAction(element)) {
1741
- continue;
1742
- }
1743
- const [action2, fn] = element;
1744
- if (action2 === "custom") {
1745
- getCustomActionHolder(context, fn).cleanup();
1746
- }
1747
- }
1748
- }
1749
- if (context.meta.goingBack) {
1750
- match("clear", [keep, characters2, audio2], {
1751
- ctx: context,
1752
- data: latest[1]
1753
- });
1754
- }
1755
- context.loading(false);
1756
- const lastQueueItem = queue.at(-1);
1757
- const lastQueueItemRequiresUserAction = lastQueueItem && isBlockingAction(lastQueueItem);
1758
- await run((item) => {
1759
- if (!latest) return;
1760
- if (lastQueueItem === item && lastQueueItemRequiresUserAction) {
1761
- return;
1762
- }
1763
- const [action2, ...props] = item;
1764
- return match(action2, props, {
1765
- ctx: context,
1766
- data: latest[1]
1767
- });
1768
- });
1769
- if (!context.meta.goingBack) {
1770
- context.meta.restoring = false;
1771
- }
1772
- await render(context);
1773
- context.meta.restoring = context.meta.goingBack = false;
1774
- };
1775
- const { refer, referGuarded } = createReferFunction({
1776
- story,
1777
- onUnknownSceneHit
1778
- });
1779
- const exit = (force = false, saving = true) => {
1780
- const ctx = renderer.getContext(MAIN_CONTEXT_KEY);
1781
- const stack = useStack(ctx);
1782
- const current = stack.value;
1783
- const isSaved = () => {
1784
- const { saves } = storageData.get();
1785
- const [currentPath, currentData] = stack.value;
1786
- return saves.some(
1787
- ([path, data2, [date, type2]]) => type2 === "manual" && times.has(date) && dequal(path, currentPath) && dequal(data2, currentData)
1788
- );
1789
- };
1790
- if (interacted > 1 && !force && askBeforeExit && !isSaved()) {
1791
- renderer.ui.showExitPrompt();
1792
- return;
1793
- }
1794
- if (interacted > 0 && saving) {
1795
- save("auto");
1796
- }
1797
- stack.clear();
1798
- ctx.clear(EMPTY_SET, EMPTY_SET, { music: EMPTY_SET, sounds: EMPTY_SET }, noop);
1799
- renderer.ui.showScreen("mainmenu");
1800
- ctx.audio.destroy();
1801
- const [time, type] = current[2];
1802
- if (type === "auto" && interacted <= 1 && times.has(time)) {
1803
- storageData.update((prev) => {
1804
- prev.saves = prev.saves.filter((save2) => save2 !== current);
1805
- return prev;
1806
- });
1807
- }
1808
- interactivity(false);
1809
- times.clear();
1810
- };
1811
- const back = async () => {
1812
- const stack = useStack(MAIN_CONTEXT_KEY);
1813
- const valueBeforeBack = stack.value;
1814
- stack.back();
1815
- if (dequal(valueBeforeBack, stack.value) && !stack.previous) {
1816
- return;
1817
- }
1818
- await restore(stack.value);
1819
- };
1820
- const t = (key, lang) => {
1821
- return translation[lang].internal[key];
1822
- };
1823
- const preview = async (save2, name) => {
1824
- if (isEmpty(story)) {
1825
- return Promise.resolve({
1826
- assets: []
1827
- });
1828
- }
1829
- const [path, data2] = save2;
1830
- const ctx = renderer.getContext(name);
1831
- const { found } = await refer(path);
1832
- if (found) ctx.loading(true);
1833
- const { queue } = await getActionsFromPath({
1834
- story,
1835
- path,
1836
- filter: true,
1837
- referGuarded
1838
- });
1839
- ctx.loading(false);
1840
- ctx.meta.restoring = true;
1841
- ctx.meta.preview = true;
1842
- const processor = createQueueProcessor(queue, {
1843
- skip: EMPTY_SET
1844
- });
1845
- useStack(ctx).push(clone(save2));
1846
- const assets = [];
1847
- await processor.run(([action2, ...props]) => {
1848
- if (isAudioAction(action2)) return;
1849
- if (action2 === "vibrate") return;
1850
- if (action2 === "end") return;
1851
- huntAssets({
1852
- action: action2,
1853
- props,
1854
- mode: preloadAssets,
1855
- characters,
1856
- lang: getLanguageFromStore(storageData),
1857
- volume: getVolumeFromStore(storageData),
1858
- handle: assets.push.bind(assets)
1859
- });
1860
- return match(action2, props, {
1861
- ctx,
1862
- data: data2
1863
- });
1864
- });
1865
- return {
1866
- assets
1867
- };
1868
- };
1869
- const removeContext = (name) => {
1870
- STACK_MAP.delete(name);
1871
- };
1872
- const getStateAtCtx = (context) => {
1873
- return useStack(context).value[1];
1874
- };
1875
- const getStateFunction = (context) => {
1876
- const stack = useStack(context);
1877
- const state = (value) => {
1878
- const _state = getStateAtCtx(context);
1879
- if (!value) {
1880
- return _state;
1881
- }
1882
- const prev = _state;
1883
- const val = isFunction(value) ? value(prev) : merge(prev, value);
1884
- stack.value[1] = val;
1885
- return void 0;
1886
- };
1887
- return state;
1888
- };
1889
- const getLanguageDisplayName = (lang) => {
1890
- const language = translation[lang];
1891
- if (DEV && !language) {
1892
- throw new Error(
1893
- `Attempt to use unsupported language "${language}". Supported languages: ${languages.join(", ")}.`
1894
- );
1895
- }
1896
- return capitalize(language.nameOverride || getIntlLanguageDisplayName(lang));
1897
- };
1898
- const clearCustomAction = (ctx, fn) => {
1899
- getCustomActionHolder(ctx, fn).cleanup();
1900
- };
1901
- const getResourseTypeForRenderer = (url) => {
1902
- return getResourseType({
1903
- url,
1904
- request
1905
- });
1906
- };
1907
- const getCharacterColor = (c) => {
1908
- return c in characters ? characters[c].color : "#000000";
1909
- };
1910
- const getCharacterAssets = (character, emotion) => {
1911
- return toArray(characters[character].emotions[emotion]).map(handleImageAsset);
1912
- };
1913
- const getCharacterName = (character) => {
1914
- const c = character;
1915
- const cs = characters;
1916
- const [lang] = storageData.get().meta;
1917
- if (c && c in cs) {
1918
- const block = cs[c].name;
1919
- if (typeof block === "string") {
1920
- return block;
1921
- }
1922
- if (lang in block) {
1923
- return block[lang];
1924
- }
1925
- }
1926
- return String(c) || "";
1927
- };
1928
- const getDialogOverview = async () => {
1929
- const { value: save2 } = useStack(MAIN_CONTEXT_KEY);
1930
- const stateSnapshots = save2[3];
1931
- if (stateSnapshots.length == 0) {
1932
- return [];
1933
- }
1934
- const { queue } = await getActionsFromPath({
1935
- story,
1936
- path: save2[0],
1937
- filter: false,
1938
- referGuarded
1939
- });
1940
- const [lang] = storageData.get().meta;
1941
- const dialogItems = [];
1942
- for (let p = 0, a = stateSnapshots.length, i = queue.length - 1; a > 0 && i > 0; i--) {
1943
- const action2 = queue[i];
1944
- if (action2[0] === "dialog") {
1945
- const [_, name, text] = action2;
1946
- let voice = void 0;
1947
- for (let j = i - 1; j > p && j > 0; j--) {
1948
- const action3 = queue[j];
1949
- if (isUserRequiredAction(action3) || isSkippedDuringRestore(action3[0])) break;
1950
- if (action3[0] === "stopVoice") break;
1951
- if (action3[0] === "voice") {
1952
- voice = action3[1];
1953
- break;
1954
- }
1955
- }
1956
- dialogItems.push({
1957
- name,
1958
- text,
1959
- voice
1960
- });
1961
- p = i;
1962
- a--;
1963
- }
1964
- }
1965
- const entries = dialogItems.reverse().map(({ name, text, voice }, i) => {
1966
- const state = stateSnapshots[i];
1967
- const audioSource = isString(voice) ? voice : isAsset(voice) ? voice : voice == void 0 ? voice : voice[lang];
1968
- name = name ? getCharacterName(name) : "";
1969
- return {
1970
- name: templateReplace(name, state),
1971
- text: templateReplace(text, state),
1972
- voice: audioSource ? handleAudioAsset(audioSource) : ""
1973
- };
1974
- });
1975
- return entries;
1976
- };
1977
- const renderer = createRenderer({
1978
- mainContextKey: MAIN_CONTEXT_KEY,
1979
- characters: getCharactersData(characters),
1980
- characterAssetSizes,
1981
- set: set2,
1982
- restore,
1983
- save,
1984
- newGame,
1985
- exit,
1986
- back,
1987
- t,
1988
- preview,
1989
- removeContext,
1990
- getStateFunction,
1991
- clearCustomAction,
1992
- languages,
1993
- storageData,
1994
- coreData,
1995
- getLanguageDisplayName,
1996
- getCharacterColor,
1997
- getCharacterAssets,
1998
- getDialogOverview,
1999
- getResourseType: getResourseTypeForRenderer
2000
- });
2001
- const useStack = createUseStackFunction(renderer);
2002
- useStack(MAIN_CONTEXT_KEY).push(initial);
2003
- const UIInstance = renderer.ui.start();
2004
- const enmemory = (ctx) => {
2005
- if (ctx.meta.restoring) return;
2006
- const stack = useStack(ctx);
2007
- const current = clone(stack.value);
2008
- current[2][1] = "auto";
2009
- stack.push(current);
2010
- save("auto");
2011
- };
2012
- const next = (ctx) => {
2013
- const stack = useStack(ctx);
2014
- const path = stack.value[0];
2015
- nextPath(path);
2016
- };
2017
- const matchActionOptions = {
2018
- getContext: renderer.getContext,
2019
- push(ctx) {
2020
- if (ctx.meta.restoring) return;
2021
- next(ctx);
2022
- render(ctx);
2023
- },
2024
- forward(ctx) {
2025
- if (!ctx.meta.preview) enmemory(ctx);
2026
- matchActionOptions.push(ctx);
2027
- if (!ctx.meta.preview) interactivity(true);
2028
- },
2029
- async onBeforeActionCall({ action: action2, props, ctx }) {
2030
- if (preloadAssets !== "automatic") return;
2031
- if (ctx.meta.preview || ctx.meta.restoring) return;
2032
- if (!isBlockingAction([action2, ...props])) return;
2033
- try {
2034
- const collection = await collectActionsBeforeBlockingAction({
2035
- path: nextPath(clone(useStack(ctx).value[0])),
2036
- refer: referGuarded,
2037
- clone
2038
- });
2039
- for (const [action3, ...props2] of collection) {
2040
- huntAssets({
2041
- action: action3,
2042
- props: props2,
2043
- mode: preloadAssets,
2044
- characters,
2045
- lang: getLanguageFromStore(storageData),
2046
- volume: getVolumeFromStore(storageData),
2047
- handle: enqueueAssetForPreloading
2048
- });
2049
- }
2050
- handleAssetsPreloading({
2051
- ...renderer.misc,
2052
- request,
2053
- limiter: limitAssetsDownload
2054
- });
2055
- } catch (cause) {
2056
- console.error(cause);
2057
- }
2058
- }
2059
- };
2060
- const { match, nativeActions } = matchAction(matchActionOptions, {
2061
- wait({ ctx, data: data2, push }, [time]) {
2062
- if (ctx.meta.restoring) return;
2063
- setTimeout(push, isFunction(time) ? time(data2) : time);
2064
- },
2065
- showBackground({ ctx, push }, [background]) {
2066
- if (isString(background) || isAsset(background)) {
2067
- ctx.background({
2068
- all: handleImageAsset(background)
2069
- });
2070
- } else {
2071
- ctx.background(
2072
- Object.fromEntries(Object.entries(background).map(([media, asset2]) => [media, handleImageAsset(asset2)]))
2073
- );
2074
- }
2075
- push();
2076
- },
2077
- playMusic({ ctx, push }, [source]) {
2078
- ctx.audio.music(handleAudioAsset(source), "music").play(true);
2079
- push();
2080
- },
2081
- pauseMusic({ ctx, push }, [source]) {
2082
- ctx.audio.music(handleAudioAsset(source), "music").pause();
2083
- push();
2084
- },
2085
- stopMusic({ ctx, push }, [source]) {
2086
- ctx.audio.music(handleAudioAsset(source), "music").stop();
2087
- push();
2088
- },
2089
- playSound({ ctx, push }, [source, loop]) {
2090
- ctx.audio.music(handleAudioAsset(source), "sound").play(loop || false);
2091
- push();
2092
- },
2093
- pauseSound({ ctx, push }, [source]) {
2094
- ctx.audio.music(handleAudioAsset(source), "sound").pause();
2095
- push();
2096
- },
2097
- stopSound({ ctx, push }, [source]) {
2098
- ctx.audio.music(handleAudioAsset(source), "sound").stop();
2099
- push();
2100
- },
2101
- voice({ ctx, push }, [source]) {
2102
- const [lang] = storageData.get().meta;
2103
- const audioSource = isString(source) ? source : isAsset(source) ? source : source[lang];
2104
- if (!audioSource) {
2105
- push();
2106
- return;
2107
- }
2108
- ctx.audio.voice(handleAudioAsset(audioSource));
2109
- push();
2110
- },
2111
- stopVoice({ ctx, push }) {
2112
- ctx.audio.voiceStop();
2113
- push();
2114
- },
2115
- showCharacter({ ctx, push }, [character, emotion, className, style]) {
2116
- emotion ??= defaultEmotions[character];
2117
- if (DEV && !emotion) {
2118
- throw new Error(`Attemp to show character "${character}" without emotion provided.`);
2119
- }
2120
- if (!emotion) return;
2121
- if (DEV && !characters[character].emotions[emotion]) {
2122
- throw new Error(`Attempt to show character "${character}" with unknown emotion "${emotion}"`);
2123
- }
2124
- const handle = ctx.character(character);
2125
- handle.append(className, style, ctx.meta.restoring);
2126
- handle.emotion(emotion, true);
2127
- push();
2128
- },
2129
- hideCharacter({ ctx, push }, [character, className, style, duration]) {
2130
- ctx.character(character).remove(className, style, duration, ctx.meta.restoring).then(push);
2131
- },
2132
- dialog({ ctx, data: data2, forward }, [character, content, emotion]) {
2133
- const name = getCharacterName(character);
2134
- const stack = useStack(ctx);
2135
- if (!ctx.meta.restoring && !ctx.meta.goingBack) {
2136
- stack.value[3].push(clone(data2));
2137
- }
2138
- ctx.clearBlockingActions("dialog");
2139
- ctx.dialog(templateReplace(content, data2), templateReplace(name, data2), character, emotion, forward);
2140
- },
2141
- function({ ctx, push }, [fn]) {
2142
- const { restoring, goingBack, preview: preview2 } = ctx.meta;
2143
- const result = fn({
2144
- lang: getLanguageFromStore(storageData),
2145
- goingBack,
2146
- restoring,
2147
- preview: preview2,
2148
- state: getStateFunction(ctx)
2149
- });
2150
- if (!ctx.meta.restoring) {
2151
- result ? result.then(push) : push();
2152
- }
2153
- return result;
2154
- },
2155
- choice({ ctx, data: data2 }, [question, ...choices]) {
2156
- const isWithoutQuestion = Array.isArray(question);
2157
- if (isWithoutQuestion) {
2158
- choices.unshift(question);
2159
- question = "";
2160
- }
2161
- const transformedChoices = choices.map(([content, _children, active, visible, onSelect, image]) => {
2162
- const active$ = store(false);
2163
- const visible$ = store(false);
2164
- const lang = getLanguageFromStore(storageData);
2165
- const getCheckValue = (fn) => {
2166
- if (!fn) {
2167
- return true;
2168
- }
2169
- return fn({
2170
- lang,
2171
- state: getStateAtCtx(ctx)
2172
- });
2173
- };
2174
- const update = () => {
2175
- active$.set(getCheckValue(active));
2176
- visible$.set(getCheckValue(visible));
2177
- };
2178
- update();
2179
- const onSelectGuarded = onSelect || noop;
2180
- const onSelectWrapped = () => {
2181
- onSelectGuarded({
2182
- recompute: update
2183
- });
2184
- };
2185
- const imageValue = image ? handleImageAsset(image) : "";
2186
- return [templateReplace(content, data2), active$, visible$, onSelectWrapped, imageValue];
2187
- });
2188
- if (DEV && transformedChoices.length === 0) {
2189
- throw new Error(
2190
- `Running choice without variants to choose from, look at how to use Choice action properly [https://novely.pages.dev/guide/actions/choice#usage]`
2191
- );
2192
- }
2193
- ctx.clearBlockingActions("choice");
2194
- ctx.choices(templateReplace(question, data2), transformedChoices, (selected) => {
2195
- if (!ctx.meta.preview) {
2196
- enmemory(ctx);
2197
- }
2198
- const stack = useStack(ctx);
2199
- const offset = isWithoutQuestion ? 0 : 1;
2200
- if (DEV && !transformedChoices[selected]) {
2201
- throw new Error("Choice children is empty, either add content there or make item not selectable");
2202
- }
2203
- stack.value[0].push(["choice", selected + offset], [null, 0]);
2204
- render(ctx);
2205
- interactivity(true);
2206
- });
2207
- },
2208
- jump({ ctx, data: data2 }, [scene]) {
2209
- const stack = useStack(ctx);
2210
- stack.value[0] = [
2211
- ["jump", scene],
2212
- [null, -1]
2213
- ];
2214
- stack.value[3] = [];
2215
- match("clear", [], {
2216
- ctx,
2217
- data: data2
2218
- });
2219
- },
2220
- clear({ ctx, push }, [keep, characters2, audio2]) {
2221
- ctx.vibrate(0);
2222
- ctx.clear(
2223
- keep || EMPTY_SET,
2224
- characters2 || EMPTY_SET,
2225
- audio2 || { music: EMPTY_SET, sounds: EMPTY_SET },
2226
- push
2227
- );
2228
- },
2229
- condition({ ctx, data: data2 }, [condition, variants]) {
2230
- if (DEV && Object.values(variants).length === 0) {
2231
- throw new Error(`Attempt to use Condition action with empty variants object`);
2232
- }
2233
- if (!ctx.meta.restoring) {
2234
- const val = String(condition(data2));
2235
- if (DEV && !variants[val]) {
2236
- throw new Error(`Attempt to go to unknown variant "${val}"`);
2237
- }
2238
- if (DEV && variants[val].length === 0) {
2239
- throw new Error(`Attempt to go to empty variant "${val}"`);
2240
- }
2241
- const stack = useStack(ctx);
2242
- stack.value[0].push(["condition", val], [null, 0]);
2243
- render(ctx);
2244
- }
2245
- },
2246
- end({ ctx }) {
2247
- if (ctx.meta.preview) return;
2248
- exit(true, false);
2249
- },
2250
- input({ ctx, data: data2, forward }, [question, onInput, setup]) {
2251
- ctx.clearBlockingActions("input");
2252
- ctx.input(templateReplace(question, data2), onInput, setup || noop, forward);
2253
- },
2254
- custom({ ctx, push }, [fn]) {
2255
- if (fn.requireUserAction) {
2256
- ctx.clearBlockingActions(void 0);
2257
- }
2258
- const state = getStateFunction(ctx);
2259
- const lang = getLanguageFromStore(storageData);
2260
- const result = handleCustomAction(ctx, fn, {
2261
- ...ctx.custom(fn),
2262
- state,
2263
- lang,
2264
- getStack: useStack,
2265
- templateReplace
2266
- });
2267
- const next2 = () => {
2268
- if (fn.requireUserAction && !ctx.meta.preview) {
2269
- enmemory(ctx);
2270
- interactivity(true);
2271
- }
2272
- push();
2273
- };
2274
- if (!ctx.meta.restoring || ctx.meta.goingBack) {
2275
- if (isPromise(result)) {
2276
- result.then(next2);
2277
- } else {
2278
- next2();
2279
- }
2280
- }
2281
- return result;
2282
- },
2283
- vibrate({ ctx, push }, pattern) {
2284
- ctx.vibrate(pattern);
2285
- push();
2286
- },
2287
- next({ push }) {
2288
- push();
2289
- },
2290
- animateCharacter({ ctx, push }, [character, className]) {
2291
- const classes = className.split(" ");
2292
- if (DEV && classes.length === 0) {
2293
- throw new Error(
2294
- "Attempt to use AnimateCharacter without classes. Classes should be provided [https://novely.pages.dev/guide/actions/animateCharacter.html]"
2295
- );
2296
- }
2297
- if (ctx.meta.preview) return;
2298
- ctx.character(character).animate(classes);
2299
- push();
2300
- },
2301
- text({ ctx, data: data2, forward }, text) {
2302
- const string = text.map((content) => templateReplace(content, data2)).join(" ");
2303
- if (DEV && string.length === 0) {
2304
- throw new Error(`Action Text was called with empty string or array`);
2305
- }
2306
- ctx.clearBlockingActions("text");
2307
- ctx.text(string, forward);
2308
- },
2309
- async exit({ ctx, data: data2 }) {
2310
- if (ctx.meta.restoring) return;
2311
- const { exitImpossible } = await exitPath({
2312
- path: useStack(ctx).value[0],
2313
- refer: referGuarded,
2314
- onExitImpossible: () => {
2315
- match("end", [], {
2316
- ctx,
2317
- data: data2
2318
- });
2319
- }
2320
- });
2321
- if (exitImpossible) {
2322
- ctx.clearBlockingActions(void 0);
2323
- return;
2324
- }
2325
- render(ctx);
2326
- },
2327
- preload({ ctx, push }, [source]) {
2328
- if (DEV && preloadAssets !== "lazy") {
2329
- console.error(
2330
- `You do not need a preload action becase "preloadAssets" strategy was set to "${preloadAssets}"`
2331
- );
2332
- }
2333
- if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
2334
- PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
2335
- }
2336
- push();
2337
- },
2338
- block({ ctx }, [scene]) {
2339
- if (DEV && !story[scene]) {
2340
- throw new Error(`Attempt to call Block action with unknown scene "${scene}"`);
2341
- }
2342
- if (DEV && story[scene].length === 0) {
2343
- throw new Error(`Attempt to call Block action with empty scene "${scene}"`);
2344
- }
2345
- if (!ctx.meta.restoring) {
2346
- const stack = useStack(ctx);
2347
- stack.value[0].push(["block", scene], [null, 0]);
2348
- render(ctx);
2349
- }
2350
- }
2351
- });
2352
- const action = buildActionObject({
2353
- rendererActions: renderer.actions,
2354
- nativeActions,
2355
- characters,
2356
- preloadAssets,
2357
- storageData
2358
- });
2359
- const render = async (ctx) => {
2360
- const stack = useStack(ctx);
2361
- const [path, state] = stack.value;
2362
- const { found, value } = await refer(path);
2363
- if (found) {
2364
- ctx.loading(true);
2365
- }
2366
- const referred = await value;
2367
- if (found) {
2368
- ctx.loading(false);
2369
- }
2370
- if (isAction(referred)) {
2371
- const [action2, ...props] = referred;
2372
- match(action2, props, {
2373
- ctx,
2374
- data: state
2375
- });
2376
- } else if (Object.values(story).some((branch) => branch === referred)) {
2377
- match("end", [], {
2378
- ctx,
2379
- data: state
2380
- });
2381
- } else {
2382
- match("exit", [], {
2383
- ctx,
2384
- data: state
2385
- });
2386
- }
2387
- };
2388
- const interactivity = (value = false) => {
2389
- interacted = value ? interacted + 1 : 0;
2390
- };
2391
- const templateReplace = (content, values) => {
2392
- const {
2393
- data: data2,
2394
- meta: [lang]
2395
- } = storageData.get();
2396
- const obj = values || data2;
2397
- const str = flattenAllowedContent(!isFunction(content) && !isString(content) ? content[lang] : content, obj);
2398
- const t2 = translation[lang];
2399
- const pluralRules = (t2.plural || t2.actions) && new Intl.PluralRules(t2.tag || lang);
2400
- return replace(str, obj, t2.plural, t2.actions, pluralRules);
2401
- };
2402
- const data = (value) => {
2403
- const _data = storageData.get().data;
2404
- if (!value) return _data;
2405
- const val = isFunction(value) ? value(_data) : merge(_data, value);
2406
- storageData.update((prev) => {
2407
- prev.data = val;
2408
- return prev;
2409
- });
2410
- return void 0;
2411
- };
2412
- const typeEssentials = {
2413
- l: null,
2414
- s: null,
2415
- d: null,
2416
- c: null
2417
- };
2418
- const getCurrentStorageData = () => {
2419
- return coreData.get().dataLoaded ? clone(storageData.get()) : null;
2420
- };
2421
- const setStorageData = (data2) => {
2422
- if (destroyed) {
2423
- if (DEV) {
2424
- throw new Error(
2425
- `function \`setStorageData\` was called after novely instance was destroyed. Data is not updater nor synced after destroy.`
2426
- );
2427
- }
2428
- return;
2429
- }
2430
- storageData.set(data2);
2431
- };
2432
- return {
2433
- /**
2434
- * Function to set game script
2435
- *
2436
- * @example
2437
- * ```ts
2438
- * engine.script({
2439
- * start: [
2440
- * action.function(() => {})
2441
- * ]
2442
- * })
2443
- * ```
2444
- */
2445
- script,
2446
- /**
2447
- * Get actions
2448
- *
2449
- * @example
2450
- * ```ts
2451
- * engine.script({
2452
- * start: [
2453
- * action.function(() => {})
2454
- * ]
2455
- * })
2456
- * ```
2457
- */
2458
- action,
2459
- /**
2460
- * State bound to `$MAIN` game context
2461
- * @deprecated Use `state` function provided from action arguments
2462
- */
2463
- state: getStateFunction(MAIN_CONTEXT_KEY),
2464
- /**
2465
- * Store data between games
2466
- *
2467
- * @example
2468
- * ```ts
2469
- * engine.script({
2470
- * start: [
2471
- * action.function(() => {
2472
- * // Paid content should be purchased only once
2473
- * // So it will be available in any save
2474
- * data({ paid_content_purchased: true })
2475
- * })
2476
- * ]
2477
- * })
2478
- * ```
2479
- */
2480
- data,
2481
- /**
2482
- * Used in combination with type utilities
2483
- *
2484
- * @example
2485
- * ```ts
2486
- * import type { ConditionParams, StateFunction } from '@novely/core';
2487
- *
2488
- * const conditionCheck = (state: StateFunction<ConditionParams<typeof engine.typeEssintials>>) => {
2489
- * return state.age >= 18;
2490
- * }
2491
- * ```
2492
- */
2493
- typeEssentials,
2494
- /**
2495
- * Replaces content inside {{braces}} using global data
2496
- * @example
2497
- * ```ts
2498
- * data({ name: 'Alexei' })
2499
- *
2500
- * templateReplace('{{name}} is our hero')
2501
- * templateReplace({
2502
- * en: (data) => 'Hello, ' + data.name
2503
- * })
2504
- * ```
2505
- */
2506
- templateReplace(content) {
2507
- return templateReplace(content);
2508
- },
2509
- /**
2510
- * Same as `templateReplace` but uses state and requires explicitly providing it
2511
- */
2512
- templateReplaceState(content, state) {
2513
- return templateReplace(content, state);
2514
- },
2515
- /**
2516
- * Cancel data loading, hide UI, ignore page change events
2517
- * Data updates still will work in case Novely already was loaded
2518
- */
2519
- destroy() {
2520
- dataLoaded.cancel();
2521
- UIInstance.unmount();
2522
- unsubscribeFromBrowserVisibilityChange();
2523
- destroyed = true;
2524
- },
2525
- /**
2526
- * Funtion to get current storage data
2527
- *
2528
- * @example
2529
- * ```ts
2530
- * const currentStorageData = engine.getCurrentStorageData();
2531
- * ```
2532
- */
2533
- getCurrentStorageData,
2534
- /**
2535
- * Function to set storage data. Using this function is not recommended.
2536
- *
2537
- * @deprecated
2538
- * @example
2539
- * ```ts
2540
- * const currentStorageData = engine.getCurrentStorageData();
2541
- *
2542
- * if (currentStorageData) {
2543
- * // update music volume
2544
- * currentStorageData.meta[2] = 1;
2545
- *
2546
- * setStorageData(currentStorageData)
2547
- * }
2548
- * ```
2549
- */
2550
- setStorageData
2551
- };
2552
- };
2553
-
2554
- // src/extend-actions.ts
2555
- var extendAction = (base, extension) => {
2556
- return {
2557
- ...extension,
2558
- ...base
2559
- };
2560
- };
2561
-
2562
- // src/translations.ts
2563
- var RU = {
2564
- NewGame: "\u041D\u043E\u0432\u0430\u044F \u0438\u0433\u0440\u0430",
2565
- HomeScreen: "\u0413\u043B\u0430\u0432\u043D\u044B\u0439 \u044D\u043A\u0440\u0430\u043D",
2566
- ToTheGame: "\u041A \u0438\u0433\u0440\u0435",
2567
- Language: "\u042F\u0437\u044B\u043A",
2568
- NoSaves: "\u0421\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0439 \u043D\u0435\u0442",
2569
- LoadSave: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C",
2570
- Saves: "\u0421\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u044F",
2571
- Settings: "\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438",
2572
- Sumbit: "\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C",
2573
- GoBack: "\u041D\u0430\u0437\u0430\u0434",
2574
- DoSave: "\u0421\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435",
2575
- Auto: "\u0410\u0432\u0442\u043E",
2576
- Stop: "\u0421\u0442\u043E\u043F",
2577
- Exit: "\u0412\u044B\u0445\u043E\u0434",
2578
- Automatic: "\u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u043E\u0435",
2579
- Manual: "\u0420\u0443\u0447\u043D\u043E\u0435",
2580
- Remove: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C",
2581
- LoadASaveFrom: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u043E\u0442",
2582
- DeleteASaveFrom: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u043E\u0442",
2583
- TextSpeed: "\u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C \u0442\u0435\u043A\u0441\u0442\u0430",
2584
- TextSpeedSlow: "\u041C\u0435\u0434\u043B\u0435\u043D\u043D\u0430\u044F",
2585
- TextSpeedMedium: "\u0421\u0440\u0435\u0434\u043D\u044F\u044F",
2586
- TextSpeedFast: "\u0411\u044B\u0441\u0442\u0440\u0430\u044F",
2587
- TextSpeedAuto: "\u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0430\u044F",
2588
- CompleteText: "\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0442\u0435\u043A\u0441\u0442",
2589
- GoForward: "\u041F\u0435\u0440\u0435\u0439\u0442\u0438 \u0432\u043F\u0435\u0440\u0451\u0434",
2590
- ExitDialogWarning: "\u0412\u044B \u0443\u0432\u0435\u0440\u0435\u043D\u044B, \u0447\u0442\u043E \u0445\u043E\u0442\u0438\u0442\u0435 \u0432\u044B\u0439\u0442\u0438? \u041F\u0440\u043E\u0433\u0440\u0435\u0441\u0441 \u0431\u0443\u0434\u0435\u0442 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D.",
2591
- ExitDialogExit: "\u0412\u044B\u0439\u0442\u0438",
2592
- ExitDialogBack: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F \u0432 \u0438\u0433\u0440\u0443",
2593
- OpenMenu: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u043C\u0435\u043D\u044E",
2594
- CloseMenu: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C \u043C\u0435\u043D\u044E",
2595
- MusicVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u043C\u0443\u0437\u044B\u043A\u0438",
2596
- SoundVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0437\u0432\u0443\u043A\u043E\u0432",
2597
- VoiceVolume: "\u0413\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u044C \u0440\u0435\u0447\u0438",
2598
- Close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C",
2599
- DialogOverview: "\u041E\u0431\u0437\u043E\u0440 \u0434\u0438\u0430\u043B\u043E\u0433\u0430"
2600
- };
2601
- var EN = {
2602
- NewGame: "New Game",
2603
- HomeScreen: "Home Screen",
2604
- ToTheGame: "To the Game",
2605
- Language: "Language",
2606
- NoSaves: "No saves",
2607
- LoadSave: "Load",
2608
- Saves: "Saves",
2609
- Settings: "Settings",
2610
- Sumbit: "Submit",
2611
- GoBack: "Go back",
2612
- DoSave: "Save",
2613
- Auto: "Auto",
2614
- Stop: "Stop",
2615
- Exit: "Exit",
2616
- Automatic: "Automatic",
2617
- Manual: "Manual",
2618
- Remove: "Remove",
2619
- LoadASaveFrom: "Load a save from",
2620
- DeleteASaveFrom: "Delete a save from",
2621
- TextSpeed: "Text Speed",
2622
- TextSpeedSlow: "Slow",
2623
- TextSpeedMedium: "Medium",
2624
- TextSpeedFast: "Fast",
2625
- TextSpeedAuto: "Auto",
2626
- CompleteText: "Complete text",
2627
- GoForward: "Go forward",
2628
- ExitDialogWarning: "Are you sure you want to exit? Progress will be saved.",
2629
- ExitDialogExit: "Exit",
2630
- ExitDialogBack: "Return to game",
2631
- OpenMenu: "Open menu",
2632
- CloseMenu: "Close menu",
2633
- MusicVolume: "Music volume",
2634
- SoundVolume: "Sound volume",
2635
- VoiceVolume: "Voice volume",
2636
- Close: "Close",
2637
- DialogOverview: "Dialog Overview"
2638
- };
2639
- var KK = {
2640
- NewGame: "\u0416\u0430\u04A3\u0430 \u043E\u0439\u044B\u043D",
2641
- HomeScreen: "\u041D\u0435\u0433\u0456\u0437\u0433\u0456 \u044D\u043A\u0440\u0430\u043D",
2642
- ToTheGame: "\u041E\u0439\u044B\u043D\u0493\u0430",
2643
- Language: "\u0422\u0456\u043B",
2644
- NoSaves: "\u0421\u0430\u049B\u0442\u0430\u0443 \u0436\u043E\u049B",
2645
- LoadSave: "\u0416\u04AF\u043A\u0442\u0435\u0443",
2646
- Saves: "\u0421\u0430\u049B\u0442\u0430\u0443",
2647
- Settings: "\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043B\u0435\u0440",
2648
- Sumbit: "\u0420\u0430\u0441\u0442\u0430\u0443",
2649
- GoBack: "\u0410\u0440\u0442\u049B\u0430",
2650
- DoSave: "\u0421\u0430\u049B\u0442\u0430\u0443",
2651
- Auto: "\u0410\u0432\u0442\u043E",
2652
- Stop: "\u0422\u043E\u049B\u0442\u0430",
2653
- Exit: "\u0428\u044B\u0493\u0443",
2654
- Automatic: "\u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0442\u044B",
2655
- Manual: "\u049A\u043E\u043B\u043C\u0435\u043D",
2656
- Remove: "\u0416\u043E\u044E",
2657
- LoadASaveFrom: "\u0421\u0430\u049B\u0442\u0430\u0443\u0434\u044B \u0436\u04AF\u043A\u0442\u0435\u0443",
2658
- DeleteASaveFrom: "\u0421\u0430\u049B\u0442\u0430\u0443\u0434\u044B \u0436\u043E\u044E",
2659
- TextSpeed: "\u041C\u04D9\u0442\u0456\u043D \u0416\u044B\u043B\u0434\u0430\u043C\u0434\u044B\u0493\u044B",
2660
- TextSpeedSlow: "\u0411\u0430\u044F\u0443",
2661
- TextSpeedMedium: "\u041E\u0440\u0442\u0430\u0448\u0430",
2662
- TextSpeedFast: "\u0416\u044B\u043B\u0434\u0430\u043C",
2663
- TextSpeedAuto: "\u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0442\u044B",
2664
- CompleteText: "\u041C\u04D9\u0442\u0456\u043D\u0434\u0456 \u0430\u044F\u049B\u0442\u0430\u0443",
2665
- GoForward: "\u0410\u043B\u0493\u0430 \u0436\u044B\u043B\u0436\u0443",
2666
- ExitDialogWarning: "\u0421\u0456\u0437 \u0448\u044B\u049B\u049B\u044B\u04A3\u044B\u0437 \u043A\u0435\u043B\u0435\u0442\u0456\u043D\u0456\u043D\u0435 \u0441\u0435\u043D\u0456\u043C\u0434\u0456\u0441\u0456\u0437 \u0431\u0435? \u041F\u0440\u043E\u0433\u0440\u0435\u0441\u0441 \u0441\u0430\u049B\u0442\u0430\u043B\u0430\u0434\u044B",
2667
- ExitDialogExit: "\u0428\u044B\u0493\u0443",
2668
- ExitDialogBack: "\u041E\u0439\u044B\u043D\u0493\u0430 \u043E\u0440\u0430\u043B\u0443",
2669
- OpenMenu: "\u041C\u04D9\u0437\u0456\u0440\u0434\u0456 \u0430\u0448\u044B\u04A3\u044B\u0437",
2670
- CloseMenu: "\u041C\u04D9\u0437\u0456\u0440\u0434\u0456 \u0436\u0430\u0431\u0443",
2671
- MusicVolume: "\u041C\u0443\u0437\u044B\u043A\u0430\u043D\u044B\u04A3 \u043A\u04E9\u043B\u0435\u043C\u0456",
2672
- SoundVolume: "\u0414\u044B\u0431\u044B\u0441\u0442\u0430\u0440\u0434\u044B\u04A3 \u043A\u04E9\u043B\u0435\u043C\u0456",
2673
- VoiceVolume: "\u0421\u04E9\u0439\u043B\u0435\u0443 \u043A\u04E9\u043B\u0435\u043C\u0456",
2674
- Close: "\u0416\u0430\u0431\u0443",
2675
- DialogOverview: "\u0414\u0438\u0430\u043B\u043E\u0433\u049B\u0430 \u0428\u043E\u043B\u0443"
2676
- };
2677
- var JP = {
2678
- NewGame: "\u300C\u65B0\u3057\u3044\u30B2\u30FC\u30E0\u300D",
2679
- HomeScreen: "\u30DB\u30FC\u30E0\u753B\u9762",
2680
- ToTheGame: "\u300C\u30B2\u30FC\u30E0\u306B\u623B\u308B\u300D",
2681
- Language: "\u8A00\u8A9E",
2682
- NoSaves: "\u30CE\u30FC\u30BB\u30FC\u30D6",
2683
- LoadSave: "\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9",
2684
- Saves: "\u4FDD\u5B58",
2685
- Settings: "\u8A2D\u5B9A",
2686
- Sumbit: "\u78BA\u8A8D",
2687
- GoBack: "\u300C\u623B\u308B\u300D",
2688
- DoSave: "\u4FDD\u5B58",
2689
- Auto: "\u30AA\u30FC\u30C8",
2690
- Stop: "\u6B62\u307E\u308C",
2691
- Exit: "\u51FA\u53E3",
2692
- Automatic: "\u81EA\u52D5",
2693
- Manual: "\u30DE\u30CB\u30E5\u30A2\u30EB",
2694
- Remove: "\u524A\u9664",
2695
- LoadASaveFrom: "\u30ED\u30FC\u30C9\u30BB\u30FC\u30D6\u304B\u3089",
2696
- DeleteASaveFrom: "\u304B\u3089\u4FDD\u5B58\u3092\u524A\u9664",
2697
- TextSpeed: "\u30C6\u30AD\u30B9\u30C8\u30B9\u30D4\u30FC\u30C9",
2698
- TextSpeedSlow: "\u300C\u9045\u3044\u300D",
2699
- TextSpeedMedium: "\u30DF\u30C7\u30A3\u30A2\u30E0",
2700
- TextSpeedFast: "\u300C\u901F\u3044\u300D",
2701
- TextSpeedAuto: "\u81EA\u52D5",
2702
- CompleteText: "\u30C6\u30AD\u30B9\u30C8\u3092\u5B8C\u6210\u3055\u305B\u308B",
2703
- GoForward: "\u5148\u306B\u884C\u304F",
2704
- ExitDialogWarning: "\u672C\u5F53\u306B\u7D42\u4E86\u3057\u307E\u3059\u304B\uFF1F\u9032\u884C\u72B6\u6CC1\u306F\u4FDD\u5B58\u3055\u308C\u307E\u3059",
2705
- ExitDialogExit: "\u7D42\u4E86",
2706
- ExitDialogBack: "\u30B2\u30FC\u30E0\u306B\u623B\u308B",
2707
- OpenMenu: "\u30E1\u30CB\u30E5\u30FC\u3092\u958B\u304F",
2708
- CloseMenu: "\u30E1\u30CB\u30E5\u30FC\u3092\u9589\u3058\u308B",
2709
- MusicVolume: "\u97F3\u697D\u306E\u30DC\u30EA\u30E5\u30FC\u30E0",
2710
- SoundVolume: "\u97F3\u91CF",
2711
- VoiceVolume: "\u30B9\u30D4\u30FC\u30C1\u306E\u91CF",
2712
- Close: "\u9589\u3058\u308B",
2713
- DialogOverview: "\u30C0\u30A4\u30A2\u30ED\u30B0\u306E\u6982\u8981"
2714
- };
2715
-
2716
- // src/audio-codecs.ts
2717
- var cut = (str) => str.replace(/^no$/, "");
2718
- var audio = new Audio();
2719
- var canPlay = (type) => !!cut(audio.canPlayType(type));
2720
- var canPlayMultiple = (...types) => types.some((type) => canPlay(type));
2721
- var supportsMap = {
2722
- mp3: canPlayMultiple("audio/mpeg;", "audio/mp3;"),
2723
- mpeg: canPlay("audio/mpeg;"),
2724
- opus: canPlay('audio/ogg; codecs="opus"'),
2725
- ogg: canPlay('audio/ogg; codecs="vorbis"'),
2726
- oga: canPlay('audio/ogg; codecs="vorbis"'),
2727
- wav: canPlayMultiple('audio/wav; codecs="1"', "audio/wav;"),
2728
- aac: canPlay("audio/aac;"),
2729
- caf: canPlay("audio/x-caf;"),
2730
- m4a: canPlayMultiple("audio/x-m4a;", "audio/m4a;", "audio/aac;"),
2731
- m4b: canPlayMultiple("audio/x-m4b;", "audio/m4b;", "audio/aac;"),
2732
- mp4: canPlayMultiple("audio/x-mp4;", "audio/mp4;", "audio/aac;"),
2733
- weba: canPlay('audio/webm; codecs="vorbis"'),
2734
- webm: canPlay('audio/webm; codecs="vorbis"'),
2735
- dolby: canPlay('audio/mp4; codecs="ec-3"'),
2736
- flac: canPlayMultiple("audio/x-flac;", "audio/flac;")
2737
- };
2738
-
2739
- // src/image-formats.ts
2740
- var avif = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=";
2741
- var jxl = "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgAAFSqjjBu8nOv58kOHxbSN6wxttW1hSaLIODZJJ3BIEkkaoCUzGM6qJAE=";
2742
- var webp = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
2743
- var supportsFormat = (source) => {
2744
- const { promise, resolve } = Promise.withResolvers();
2745
- const img = Object.assign(document.createElement("img"), {
2746
- src: source
2747
- });
2748
- img.onload = img.onerror = () => {
2749
- resolve(img.height === 2);
2750
- };
2751
- return promise;
2752
- };
2753
- var supportsMap2 = {
2754
- avif: false,
2755
- jxl: false,
2756
- webp: false
2757
- };
2758
- var formatsMap = {
2759
- avif,
2760
- jxl,
2761
- webp
2762
- };
2763
- var loadImageFormatsSupport = async () => {
2764
- const promises = [];
2765
- for (const [format, source] of Object.entries(formatsMap)) {
2766
- const promise = supportsFormat(source).then((supported) => {
2767
- supportsMap2[format] = supported;
2768
- });
2769
- promises.push(promise);
2770
- }
2771
- await Promise.all(promises);
2772
- };
2773
- loadImageFormatsSupport();
2774
-
2775
- // src/asset.ts
2776
- var generateRandomId = () => Math.random().toString(36);
2777
- var getType = memoize(
2778
- (extensions) => {
2779
- if (extensions.every((extension) => HOWLER_SUPPORTED_FILE_FORMATS.has(extension))) {
2780
- return "audio";
2781
- }
2782
- if (extensions.every((extension) => SUPPORTED_IMAGE_FILE_FORMATS.has(extension))) {
2783
- return "image";
2784
- }
2785
- throw extensions;
2786
- },
2787
- {
2788
- getCacheKey: (extensions) => extensions.join("~")
2789
- }
2790
- );
2791
- var SUPPORT_MAPS = {
2792
- image: supportsMap2,
2793
- audio: supportsMap
2794
- };
2795
- var assetPrivate = memoize(
2796
- (variants) => {
2797
- if (DEV && variants.length === 0) {
2798
- throw new Error(`Attempt to use "asset" function without arguments`);
2799
- }
2800
- const map = {};
2801
- const extensions = [];
2802
- for (const v of variants) {
2803
- const e = getUrlFileExtension(v);
2804
- map[e] = v;
2805
- extensions.push(e);
2806
- }
2807
- const type = getType(extensions);
2808
- const getSource = once(() => {
2809
- const support = SUPPORT_MAPS[type];
2810
- for (const extension of extensions) {
2811
- if (extension in support) {
2812
- if (support[extension]) {
2813
- return map[extension];
2814
- }
2815
- } else {
2816
- return map[extension];
2817
- }
2818
- }
2819
- if (DEV) {
2820
- throw new Error(`No matching asset was found for ${variants.map((v) => `"${v}"`).join(", ")}`);
2821
- }
2822
- return "";
2823
- });
2824
- return {
2825
- get source() {
2826
- return getSource();
2827
- },
2828
- get type() {
2829
- return type;
2830
- },
2831
- id: generateRandomId()
2832
- };
2833
- },
2834
- {
2835
- getCacheKey: (variants) => variants.join("~")
2836
- }
2837
- );
2838
- var asset = (...variants) => {
2839
- return assetPrivate(variants);
2840
- };
2841
- asset.image = (source) => {
2842
- if (assetPrivate.cache.has(source)) {
2843
- return assetPrivate.cache.get(source);
2844
- }
2845
- const asset2 = {
2846
- type: "image",
2847
- source,
2848
- id: generateRandomId()
2849
- };
2850
- assetPrivate.cache.set(source, asset2);
2851
- return asset2;
2852
- };
2853
- asset.audio = (source) => {
2854
- if (assetPrivate.cache.has(source)) {
2855
- return assetPrivate.cache.get(source);
2856
- }
2857
- const asset2 = {
2858
- type: "audio",
2859
- source,
2860
- id: generateRandomId()
2861
- };
2862
- assetPrivate.cache.set(source, asset2);
2863
- return asset2;
2864
- };
2865
- return __toCommonJS(src_exports);
2866
- })();
2867
- //# sourceMappingURL=index.global.js.map