@novely/core 0.19.5 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +68 -47
- package/dist/index.global.js +430 -247
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +430 -247
- package/dist/index.js.map +1 -1
- package/package.json +62 -62
package/dist/index.js
CHANGED
|
@@ -2,13 +2,29 @@
|
|
|
2
2
|
var SKIPPED_DURING_RESTORE = /* @__PURE__ */ new Set(["dialog", "choice", "input", "vibrate", "text"]);
|
|
3
3
|
var BLOCK_EXIT_STATEMENTS = /* @__PURE__ */ new Set(["choice:exit", "condition:exit", "block:exit"]);
|
|
4
4
|
var BLOCK_STATEMENTS = /* @__PURE__ */ new Set(["choice", "condition", "block"]);
|
|
5
|
+
var AUDIO_ACTIONS = /* @__PURE__ */ new Set([
|
|
6
|
+
"playMusic",
|
|
7
|
+
"stopMusic",
|
|
8
|
+
"playSound",
|
|
9
|
+
"stopSound",
|
|
10
|
+
"voice",
|
|
11
|
+
"stopVoice"
|
|
12
|
+
]);
|
|
5
13
|
var EMPTY_SET = /* @__PURE__ */ new Set();
|
|
6
14
|
var DEFAULT_TYPEWRITER_SPEED = "Medium";
|
|
15
|
+
var MAIN_CONTEXT_KEY = "$MAIN";
|
|
16
|
+
|
|
17
|
+
// src/shared.ts
|
|
18
|
+
var STACK_MAP = /* @__PURE__ */ new Map();
|
|
7
19
|
|
|
8
20
|
// src/utils.ts
|
|
9
|
-
var matchAction = (values) => {
|
|
10
|
-
return (action, props) => {
|
|
11
|
-
|
|
21
|
+
var matchAction = (getContext, values) => {
|
|
22
|
+
return (action, props, { ctx, data }) => {
|
|
23
|
+
const context = typeof ctx === "string" ? getContext(ctx) : ctx;
|
|
24
|
+
return values[action]({
|
|
25
|
+
ctx: context,
|
|
26
|
+
data
|
|
27
|
+
}, props);
|
|
12
28
|
};
|
|
13
29
|
};
|
|
14
30
|
var isNumber = (val) => {
|
|
@@ -152,6 +168,189 @@ var getOppositeAction = (action) => {
|
|
|
152
168
|
};
|
|
153
169
|
return MAP[action];
|
|
154
170
|
};
|
|
171
|
+
var getActionsFromPath = (story, path, raw = false) => {
|
|
172
|
+
let current = story;
|
|
173
|
+
let precurrent;
|
|
174
|
+
let ignoreNested = false;
|
|
175
|
+
let index = 0;
|
|
176
|
+
const max = path.reduce((acc, [type, val]) => {
|
|
177
|
+
if (isNull(type) && isNumber(val)) {
|
|
178
|
+
return acc + 1;
|
|
179
|
+
}
|
|
180
|
+
return acc;
|
|
181
|
+
}, 0);
|
|
182
|
+
const queue = [];
|
|
183
|
+
const blocks = [];
|
|
184
|
+
for (const [type, val] of path) {
|
|
185
|
+
if (type === "jump") {
|
|
186
|
+
precurrent = story;
|
|
187
|
+
current = current[val];
|
|
188
|
+
} else if (type === null) {
|
|
189
|
+
precurrent = current;
|
|
190
|
+
if (isNumber(val)) {
|
|
191
|
+
index++;
|
|
192
|
+
let startIndex = 0;
|
|
193
|
+
if (ignoreNested) {
|
|
194
|
+
const prev = findLastPathItemBeforeItemOfType(path.slice(0, index), "block");
|
|
195
|
+
if (prev) {
|
|
196
|
+
startIndex = prev[1];
|
|
197
|
+
ignoreNested = false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
for (let i = startIndex; i <= val; i++) {
|
|
201
|
+
const item = current[i];
|
|
202
|
+
if (!isAction(item))
|
|
203
|
+
continue;
|
|
204
|
+
const [action, ...meta] = item;
|
|
205
|
+
const push = () => {
|
|
206
|
+
queue.push([action, meta]);
|
|
207
|
+
};
|
|
208
|
+
if (raw) {
|
|
209
|
+
push();
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (isSkippedDuringRestore(action) || isUserRequiredAction(action, meta)) {
|
|
213
|
+
if (index === max && i === val) {
|
|
214
|
+
push();
|
|
215
|
+
}
|
|
216
|
+
continue;
|
|
217
|
+
} else {
|
|
218
|
+
push();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
current = current[val];
|
|
223
|
+
} else if (type === "choice") {
|
|
224
|
+
blocks.push(precurrent);
|
|
225
|
+
current = current[val + 1][1];
|
|
226
|
+
} else if (type === "condition") {
|
|
227
|
+
blocks.push(precurrent);
|
|
228
|
+
current = current[2][val];
|
|
229
|
+
} else if (type === "block") {
|
|
230
|
+
blocks.push(precurrent);
|
|
231
|
+
current = story[val];
|
|
232
|
+
} else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
|
|
233
|
+
current = blocks.pop();
|
|
234
|
+
ignoreNested = true;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return queue;
|
|
238
|
+
};
|
|
239
|
+
var createQueueProcessor = (queue) => {
|
|
240
|
+
const processedQueue = [];
|
|
241
|
+
const keep = /* @__PURE__ */ new Set();
|
|
242
|
+
const characters = /* @__PURE__ */ new Set();
|
|
243
|
+
const audio = {
|
|
244
|
+
music: /* @__PURE__ */ new Set(),
|
|
245
|
+
sound: /* @__PURE__ */ new Set()
|
|
246
|
+
};
|
|
247
|
+
const next = (i) => queue.slice(i + 1);
|
|
248
|
+
for (const [i, [action, meta]] of queue.entries()) {
|
|
249
|
+
keep.add(action);
|
|
250
|
+
if (action === "function" || action === "custom") {
|
|
251
|
+
if (action === "custom" && meta[0].callOnlyLatest) {
|
|
252
|
+
const notLatest = next(i).some(([, _meta]) => {
|
|
253
|
+
if (!_meta || !meta)
|
|
254
|
+
return false;
|
|
255
|
+
const c0 = _meta[0];
|
|
256
|
+
const c1 = meta[0];
|
|
257
|
+
const isIdenticalID = c0.id && c1.id && c0.id === c1.id;
|
|
258
|
+
const isIdenticalByReference = c0 === c1;
|
|
259
|
+
return isIdenticalID || isIdenticalByReference || str(c0) === str(c1);
|
|
260
|
+
});
|
|
261
|
+
if (notLatest)
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
processedQueue.push([action, meta]);
|
|
265
|
+
} else if (action === "showCharacter" || action === "playSound" || action === "playMusic" || action === "voice") {
|
|
266
|
+
const closing = getOppositeAction(action);
|
|
267
|
+
const skip = next(i).some(([_action, _meta]) => {
|
|
268
|
+
if (!_meta || !meta)
|
|
269
|
+
return false;
|
|
270
|
+
if (_meta[0] !== meta[0])
|
|
271
|
+
return false;
|
|
272
|
+
return _action === closing || _action === action;
|
|
273
|
+
});
|
|
274
|
+
if (skip)
|
|
275
|
+
continue;
|
|
276
|
+
if (action === "showCharacter") {
|
|
277
|
+
characters.add(meta[0]);
|
|
278
|
+
} else if (action === "playMusic") {
|
|
279
|
+
audio.music.add(meta[0]);
|
|
280
|
+
} else if (action === "playSound") {
|
|
281
|
+
audio.sound.add(meta[0]);
|
|
282
|
+
}
|
|
283
|
+
processedQueue.push([action, meta]);
|
|
284
|
+
} else if (action === "showBackground" || action === "animateCharacter" || action === "preload") {
|
|
285
|
+
const skip = next(i).some(([_action], i2, array) => action === _action);
|
|
286
|
+
if (skip)
|
|
287
|
+
continue;
|
|
288
|
+
processedQueue.push([action, meta]);
|
|
289
|
+
} else {
|
|
290
|
+
processedQueue.push([action, meta]);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const run = async (match) => {
|
|
294
|
+
for await (const [action, meta] of processedQueue) {
|
|
295
|
+
const result = match(action, meta);
|
|
296
|
+
if (isPromise(result)) {
|
|
297
|
+
await result;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
processedQueue.length = 0;
|
|
301
|
+
};
|
|
302
|
+
const getKeep = () => {
|
|
303
|
+
return {
|
|
304
|
+
keep,
|
|
305
|
+
characters,
|
|
306
|
+
audio
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
return {
|
|
310
|
+
run,
|
|
311
|
+
getKeep
|
|
312
|
+
};
|
|
313
|
+
};
|
|
314
|
+
var getStack = (ctx) => {
|
|
315
|
+
const { id } = ctx;
|
|
316
|
+
const cached = STACK_MAP.get(id);
|
|
317
|
+
if (cached)
|
|
318
|
+
return cached;
|
|
319
|
+
const stack = [];
|
|
320
|
+
STACK_MAP.set(id, stack);
|
|
321
|
+
return stack;
|
|
322
|
+
};
|
|
323
|
+
var createUseStackFunction = (renderer) => {
|
|
324
|
+
const useStack = (context) => {
|
|
325
|
+
const ctx = typeof context === "string" ? renderer.getContext(context) : context;
|
|
326
|
+
const stack = getStack(ctx);
|
|
327
|
+
return {
|
|
328
|
+
get previous() {
|
|
329
|
+
return stack.previous;
|
|
330
|
+
},
|
|
331
|
+
get value() {
|
|
332
|
+
return stack.at(-1);
|
|
333
|
+
},
|
|
334
|
+
set value(value) {
|
|
335
|
+
stack[stack.length - 1] = value;
|
|
336
|
+
},
|
|
337
|
+
back() {
|
|
338
|
+
if (stack.length > 1) {
|
|
339
|
+
stack.previous = stack.pop();
|
|
340
|
+
ctx.meta.goingBack = true;
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
push(value) {
|
|
344
|
+
stack.push(value);
|
|
345
|
+
},
|
|
346
|
+
clear() {
|
|
347
|
+
stack.length = 0;
|
|
348
|
+
stack.length = 1;
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
};
|
|
352
|
+
return useStack;
|
|
353
|
+
};
|
|
155
354
|
|
|
156
355
|
// src/global.ts
|
|
157
356
|
var PRELOADED_ASSETS = /* @__PURE__ */ new Set();
|
|
@@ -171,10 +370,13 @@ var store = (current, subscribers = /* @__PURE__ */ new Set()) => {
|
|
|
171
370
|
const update = (fn) => {
|
|
172
371
|
push(current = fn(current));
|
|
173
372
|
};
|
|
373
|
+
const set = (val) => {
|
|
374
|
+
update(() => val);
|
|
375
|
+
};
|
|
174
376
|
const get = () => {
|
|
175
377
|
return current;
|
|
176
378
|
};
|
|
177
|
-
return { subscribe, update, get };
|
|
379
|
+
return { subscribe, update, set, get };
|
|
178
380
|
};
|
|
179
381
|
|
|
180
382
|
// ../deepmerge/dist/index.js
|
|
@@ -422,6 +624,7 @@ var novely = ({
|
|
|
422
624
|
}
|
|
423
625
|
});
|
|
424
626
|
function state(value) {
|
|
627
|
+
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
425
628
|
if (!value)
|
|
426
629
|
return stack.value[1];
|
|
427
630
|
const prev = stack.value[1];
|
|
@@ -438,26 +641,6 @@ var novely = ({
|
|
|
438
641
|
[intime(Date.now()), "auto"]
|
|
439
642
|
];
|
|
440
643
|
};
|
|
441
|
-
const createStack = (current, stack2 = [current]) => {
|
|
442
|
-
return {
|
|
443
|
-
get value() {
|
|
444
|
-
return stack2.at(-1);
|
|
445
|
-
},
|
|
446
|
-
set value(value) {
|
|
447
|
-
stack2[stack2.length - 1] = value;
|
|
448
|
-
},
|
|
449
|
-
back() {
|
|
450
|
-
if (stack2.length > 1)
|
|
451
|
-
stack2.pop(), goingBack = true;
|
|
452
|
-
},
|
|
453
|
-
push(value) {
|
|
454
|
-
stack2.push(value);
|
|
455
|
-
},
|
|
456
|
-
clear() {
|
|
457
|
-
stack2 = [getDefaultSave(klona(defaultState))];
|
|
458
|
-
}
|
|
459
|
-
};
|
|
460
|
-
};
|
|
461
644
|
const getLanguageWithoutParameters = () => {
|
|
462
645
|
return getLanguage2(languages, getLanguage);
|
|
463
646
|
};
|
|
@@ -499,11 +682,10 @@ var novely = ({
|
|
|
499
682
|
}
|
|
500
683
|
$$.update((prev) => (prev.dataLoaded = true, prev));
|
|
501
684
|
dataLoaded.resolve();
|
|
502
|
-
$.
|
|
685
|
+
$.set(stored);
|
|
503
686
|
};
|
|
504
687
|
storageDelay.then(getStoredData);
|
|
505
688
|
const initial = getDefaultSave(klona(defaultState));
|
|
506
|
-
const stack = createStack(initial);
|
|
507
689
|
addEventListener("visibilitychange", () => {
|
|
508
690
|
if (document.visibilityState === "hidden") {
|
|
509
691
|
throttledEmergencyOnStorageDataChange();
|
|
@@ -515,6 +697,7 @@ var novely = ({
|
|
|
515
697
|
return;
|
|
516
698
|
if (!autosaves && type === "auto")
|
|
517
699
|
return;
|
|
700
|
+
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
518
701
|
const current = klona(stack.value);
|
|
519
702
|
$.update((prev) => {
|
|
520
703
|
const isLatest = findLastIndex(prev.saves, (value) => times.has(value[2][0])) === prev.saves.length - 1;
|
|
@@ -544,139 +727,65 @@ var novely = ({
|
|
|
544
727
|
}
|
|
545
728
|
restore(save2);
|
|
546
729
|
};
|
|
547
|
-
const set = (save2) => {
|
|
730
|
+
const set = (save2, ctx) => {
|
|
731
|
+
const stack = useStack(ctx || renderer.getContext(MAIN_CONTEXT_KEY));
|
|
548
732
|
stack.value = save2;
|
|
549
733
|
return restore(save2);
|
|
550
734
|
};
|
|
551
|
-
let restoring = false;
|
|
552
|
-
let goingBack = false;
|
|
553
735
|
let interacted = 0;
|
|
554
736
|
const restore = async (save2) => {
|
|
555
737
|
if (!$$.get().dataLoaded)
|
|
556
738
|
return;
|
|
557
739
|
let latest = save2 || $.get().saves.at(-1);
|
|
558
740
|
if (!latest) {
|
|
559
|
-
$.
|
|
741
|
+
$.set({
|
|
560
742
|
saves: [initial],
|
|
561
743
|
data: klona(defaultData),
|
|
562
744
|
meta: [getLanguageWithoutParameters(), DEFAULT_TYPEWRITER_SPEED, 1, 1, 1]
|
|
563
|
-
})
|
|
745
|
+
});
|
|
564
746
|
latest = klona(initial);
|
|
565
747
|
}
|
|
566
|
-
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
precurrent = story;
|
|
585
|
-
current = current[val];
|
|
586
|
-
} else if (type === null) {
|
|
587
|
-
precurrent = current;
|
|
588
|
-
if (isNumber(val)) {
|
|
589
|
-
index++;
|
|
590
|
-
let startIndex = 0;
|
|
591
|
-
if (ignoreNested) {
|
|
592
|
-
const prev = findLastPathItemBeforeItemOfType(path.slice(0, index), "block");
|
|
593
|
-
if (prev) {
|
|
594
|
-
startIndex = prev[1];
|
|
595
|
-
ignoreNested = false;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
for (let i = startIndex; i <= val; i++) {
|
|
599
|
-
const item = current[i];
|
|
600
|
-
if (!isAction(item))
|
|
601
|
-
continue;
|
|
602
|
-
const [action2, ...meta] = item;
|
|
603
|
-
const push2 = () => {
|
|
604
|
-
keep.add(action2);
|
|
605
|
-
queue.push([action2, meta]);
|
|
606
|
-
};
|
|
607
|
-
if (action2 === "showCharacter")
|
|
608
|
-
characters2.add(meta[0]);
|
|
609
|
-
if (isSkippedDuringRestore(action2) || isUserRequiredAction(action2, meta)) {
|
|
610
|
-
if (index === max && i === val) {
|
|
611
|
-
push2();
|
|
612
|
-
} else {
|
|
613
|
-
continue;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
push2();
|
|
748
|
+
const context = renderer.getContext(MAIN_CONTEXT_KEY);
|
|
749
|
+
const stack = useStack(context);
|
|
750
|
+
context.meta.restoring = true;
|
|
751
|
+
const previous = stack.previous;
|
|
752
|
+
const [path] = stack.value = latest;
|
|
753
|
+
renderer.ui.showScreen("game");
|
|
754
|
+
const queue = getActionsFromPath(story, path);
|
|
755
|
+
const processor = createQueueProcessor(queue);
|
|
756
|
+
const { keep, characters: characters2, audio } = processor.getKeep();
|
|
757
|
+
if (previous) {
|
|
758
|
+
const prevQueue = getActionsFromPath(story, previous[0], true);
|
|
759
|
+
const currQueue = getActionsFromPath(story, path, true);
|
|
760
|
+
for (let i = prevQueue.length - 1; i > currQueue.length; i--) {
|
|
761
|
+
const element = prevQueue[i];
|
|
762
|
+
if (isAction(element)) {
|
|
763
|
+
const [action2, props] = element;
|
|
764
|
+
if (action2 === "custom") {
|
|
765
|
+
context.clearCustom(props[0]);
|
|
617
766
|
}
|
|
618
767
|
}
|
|
619
|
-
current = current[val];
|
|
620
|
-
} else if (type === "choice") {
|
|
621
|
-
blocks.push(precurrent);
|
|
622
|
-
current = current[val + 1][1];
|
|
623
|
-
} else if (type === "condition") {
|
|
624
|
-
blocks.push(precurrent);
|
|
625
|
-
current = current[2][val];
|
|
626
|
-
} else if (type === "block") {
|
|
627
|
-
blocks.push(precurrent);
|
|
628
|
-
current = story[val];
|
|
629
|
-
} else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
|
|
630
|
-
current = blocks.pop();
|
|
631
|
-
ignoreNested = true;
|
|
632
768
|
}
|
|
633
769
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
});
|
|
649
|
-
if (notLatest)
|
|
650
|
-
continue;
|
|
651
|
-
}
|
|
652
|
-
const result = match(action2, meta);
|
|
653
|
-
if (isPromise(result)) {
|
|
654
|
-
await result;
|
|
655
|
-
}
|
|
656
|
-
} else if (action2 === "showCharacter" || action2 === "playSound" || action2 === "playMusic" || action2 === "voice") {
|
|
657
|
-
const closing = getOppositeAction(action2);
|
|
658
|
-
const skip = next2(i).some(([_action, _meta]) => {
|
|
659
|
-
if (!_meta || !meta)
|
|
660
|
-
return false;
|
|
661
|
-
if (_meta[0] !== meta[0])
|
|
662
|
-
return false;
|
|
663
|
-
return _action === closing || _action === action2;
|
|
664
|
-
});
|
|
665
|
-
if (skip)
|
|
666
|
-
continue;
|
|
667
|
-
match(action2, meta);
|
|
668
|
-
} else if (action2 === "showBackground" || action2 === "animateCharacter" || action2 === "preload") {
|
|
669
|
-
const notLatest = next2(i).some(([_action]) => action2 === _action);
|
|
670
|
-
if (notLatest)
|
|
671
|
-
continue;
|
|
672
|
-
match(action2, meta);
|
|
673
|
-
} else {
|
|
674
|
-
match(action2, meta);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
restoring = goingBack = false, render();
|
|
770
|
+
match("clear", [keep, characters2, audio], {
|
|
771
|
+
ctx: context,
|
|
772
|
+
data: latest[1]
|
|
773
|
+
});
|
|
774
|
+
await processor.run((action2, props) => {
|
|
775
|
+
if (!latest)
|
|
776
|
+
return;
|
|
777
|
+
return match(action2, props, {
|
|
778
|
+
ctx: context,
|
|
779
|
+
data: latest[1]
|
|
780
|
+
});
|
|
781
|
+
});
|
|
782
|
+
context.meta.restoring = context.meta.restoring = false;
|
|
783
|
+
render(context);
|
|
678
784
|
};
|
|
679
|
-
const refer = (path
|
|
785
|
+
const refer = (path) => {
|
|
786
|
+
if (!path) {
|
|
787
|
+
path = useStack(MAIN_CONTEXT_KEY).value[0];
|
|
788
|
+
}
|
|
680
789
|
let current = story;
|
|
681
790
|
let precurrent = story;
|
|
682
791
|
const blocks = [];
|
|
@@ -707,10 +816,12 @@ var novely = ({
|
|
|
707
816
|
renderer.ui.showExitPrompt();
|
|
708
817
|
return;
|
|
709
818
|
}
|
|
819
|
+
const ctx = renderer.getContext(MAIN_CONTEXT_KEY);
|
|
820
|
+
const stack = useStack(ctx);
|
|
710
821
|
const current = stack.value;
|
|
711
822
|
stack.clear();
|
|
712
823
|
renderer.ui.showScreen("mainmenu");
|
|
713
|
-
|
|
824
|
+
ctx.audio.destroy();
|
|
714
825
|
const [time, type] = current[2];
|
|
715
826
|
if (type === "auto" && interacted <= 1 && times.has(time)) {
|
|
716
827
|
$.update((prev) => {
|
|
@@ -721,13 +832,38 @@ var novely = ({
|
|
|
721
832
|
interactivity(false);
|
|
722
833
|
times.clear();
|
|
723
834
|
};
|
|
724
|
-
const back = () => {
|
|
725
|
-
|
|
835
|
+
const back = async () => {
|
|
836
|
+
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
837
|
+
stack.back();
|
|
838
|
+
await restore(stack.value);
|
|
726
839
|
};
|
|
727
840
|
const t = (key, lang) => {
|
|
728
841
|
return translation[lang].internal[key];
|
|
729
842
|
};
|
|
843
|
+
const preview = async ([path, data2], name) => {
|
|
844
|
+
const queue = getActionsFromPath(story, path);
|
|
845
|
+
const ctx = renderer.getContext(name);
|
|
846
|
+
ctx.meta.restoring = true;
|
|
847
|
+
ctx.meta.preview = true;
|
|
848
|
+
const processor = createQueueProcessor(queue);
|
|
849
|
+
await processor.run((action2, props) => {
|
|
850
|
+
if (AUDIO_ACTIONS.has(action2))
|
|
851
|
+
return;
|
|
852
|
+
if (action2 === "vibrate")
|
|
853
|
+
return;
|
|
854
|
+
if (action2 === "end")
|
|
855
|
+
return;
|
|
856
|
+
return match(action2, props, {
|
|
857
|
+
ctx,
|
|
858
|
+
data: data2
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
};
|
|
862
|
+
const removeContext = (name) => {
|
|
863
|
+
STACK_MAP.delete(name);
|
|
864
|
+
};
|
|
730
865
|
const renderer = createRenderer({
|
|
866
|
+
mainContextKey: MAIN_CONTEXT_KEY,
|
|
731
867
|
characters,
|
|
732
868
|
set,
|
|
733
869
|
restore,
|
|
@@ -736,62 +872,67 @@ var novely = ({
|
|
|
736
872
|
exit,
|
|
737
873
|
back,
|
|
738
874
|
t,
|
|
739
|
-
|
|
875
|
+
preview,
|
|
876
|
+
removeContext,
|
|
740
877
|
languages,
|
|
741
878
|
$,
|
|
742
879
|
$$
|
|
743
880
|
});
|
|
881
|
+
const useStack = createUseStackFunction(renderer);
|
|
882
|
+
useStack(MAIN_CONTEXT_KEY).push(initial);
|
|
744
883
|
renderer.ui.start();
|
|
745
|
-
const match = matchAction({
|
|
746
|
-
wait([time]) {
|
|
747
|
-
if (!restoring)
|
|
884
|
+
const match = matchAction(renderer.getContext, {
|
|
885
|
+
wait({ ctx }, [time]) {
|
|
886
|
+
if (!ctx.meta.restoring)
|
|
748
887
|
setTimeout(push, isFunction(time) ? time() : time);
|
|
749
888
|
},
|
|
750
|
-
showBackground([background]) {
|
|
751
|
-
|
|
752
|
-
push();
|
|
889
|
+
showBackground({ ctx }, [background]) {
|
|
890
|
+
ctx.background(background);
|
|
891
|
+
push(ctx);
|
|
753
892
|
},
|
|
754
|
-
playMusic([source]) {
|
|
755
|
-
|
|
756
|
-
push();
|
|
893
|
+
playMusic({ ctx }, [source]) {
|
|
894
|
+
ctx.audio.music(source, "music", true).play();
|
|
895
|
+
push(ctx);
|
|
757
896
|
},
|
|
758
|
-
stopMusic([source]) {
|
|
759
|
-
|
|
760
|
-
push();
|
|
897
|
+
stopMusic({ ctx }, [source]) {
|
|
898
|
+
ctx.audio.music(source, "music").stop();
|
|
899
|
+
push(ctx);
|
|
761
900
|
},
|
|
762
|
-
playSound([source, loop]) {
|
|
763
|
-
|
|
764
|
-
push();
|
|
901
|
+
playSound({ ctx }, [source, loop]) {
|
|
902
|
+
ctx.audio.music(source, "sound", loop || false).play();
|
|
903
|
+
push(ctx);
|
|
765
904
|
},
|
|
766
|
-
stopSound([source]) {
|
|
767
|
-
|
|
768
|
-
push();
|
|
905
|
+
stopSound({ ctx }, [source]) {
|
|
906
|
+
ctx.audio.music(source, "sound").stop();
|
|
907
|
+
push(ctx);
|
|
769
908
|
},
|
|
770
|
-
voice([source]) {
|
|
771
|
-
|
|
772
|
-
push();
|
|
909
|
+
voice({ ctx }, [source]) {
|
|
910
|
+
ctx.audio.voice(source);
|
|
911
|
+
push(ctx);
|
|
773
912
|
},
|
|
774
|
-
stopVoice() {
|
|
775
|
-
|
|
776
|
-
push();
|
|
913
|
+
stopVoice({ ctx }) {
|
|
914
|
+
ctx.audio.voiceStop();
|
|
915
|
+
push(ctx);
|
|
777
916
|
},
|
|
778
|
-
showCharacter([character, emotion, className, style]) {
|
|
917
|
+
showCharacter({ ctx }, [character, emotion, className, style]) {
|
|
779
918
|
if (DEV && !characters[character].emotions[emotion]) {
|
|
780
919
|
throw new Error(`Attempt to show character "${character}" with unknown emotion "${emotion}"`);
|
|
781
920
|
}
|
|
782
|
-
const handle =
|
|
783
|
-
handle.append(className, style, restoring);
|
|
784
|
-
handle.
|
|
785
|
-
push();
|
|
921
|
+
const handle = ctx.character(character);
|
|
922
|
+
handle.append(className, style, ctx.meta.restoring);
|
|
923
|
+
handle.emotion(emotion, true);
|
|
924
|
+
push(ctx);
|
|
786
925
|
},
|
|
787
|
-
hideCharacter([character, className, style, duration]) {
|
|
788
|
-
|
|
926
|
+
hideCharacter({ ctx }, [character, className, style, duration]) {
|
|
927
|
+
ctx.character(character).remove(className, style, duration, ctx.meta.restoring).then(() => {
|
|
928
|
+
push(ctx);
|
|
929
|
+
});
|
|
789
930
|
},
|
|
790
|
-
dialog([character, content, emotion]) {
|
|
931
|
+
dialog({ ctx, data: data2 }, [character, content, emotion]) {
|
|
791
932
|
const name = (() => {
|
|
792
933
|
const c = character;
|
|
793
934
|
const cs = characters;
|
|
794
|
-
const lang = $.get().meta
|
|
935
|
+
const [lang] = $.get().meta;
|
|
795
936
|
if (c && c in cs) {
|
|
796
937
|
const block = cs[c].name;
|
|
797
938
|
if (typeof block === "string") {
|
|
@@ -803,16 +944,17 @@ var novely = ({
|
|
|
803
944
|
}
|
|
804
945
|
return c || "";
|
|
805
946
|
})();
|
|
806
|
-
const run =
|
|
807
|
-
run(forward
|
|
947
|
+
const run = ctx.dialog(unwrap2(content, data2), unwrap2(name, data2), character, emotion);
|
|
948
|
+
run(() => forward(ctx));
|
|
808
949
|
},
|
|
809
|
-
function([fn]) {
|
|
810
|
-
const result = fn(restoring, goingBack);
|
|
811
|
-
if (!restoring)
|
|
812
|
-
result ? result.then(push) : push();
|
|
950
|
+
function({ ctx }, [fn]) {
|
|
951
|
+
const result = fn(ctx.meta.restoring, ctx.meta.goingBack, ctx.meta.preview);
|
|
952
|
+
if (!ctx.meta.restoring) {
|
|
953
|
+
result ? result.then(() => push(ctx)) : push(ctx);
|
|
954
|
+
}
|
|
813
955
|
return result;
|
|
814
956
|
},
|
|
815
|
-
choice([question, ...choices]) {
|
|
957
|
+
choice({ ctx, data: data2 }, [question, ...choices]) {
|
|
816
958
|
const isWithoutQuestion = Array.isArray(question);
|
|
817
959
|
if (isWithoutQuestion) {
|
|
818
960
|
choices.unshift(question);
|
|
@@ -822,46 +964,57 @@ var novely = ({
|
|
|
822
964
|
if (DEV && action2.length === 0 && (!visible || visible())) {
|
|
823
965
|
console.warn(`Choice children should not be empty, either add content there or make item not selectable`);
|
|
824
966
|
}
|
|
825
|
-
return [unwrap2(content), action2, visible];
|
|
967
|
+
return [unwrap2(content, data2), action2, visible];
|
|
826
968
|
});
|
|
827
969
|
if (DEV && unwrapped.length === 0) {
|
|
828
970
|
throw new Error(`Running choice without variants to choose from, look at how to use Choice action properly [https://novely.pages.dev/guide/actions/choice#usage]`);
|
|
829
971
|
}
|
|
830
|
-
const run =
|
|
972
|
+
const run = ctx.choices(unwrap2(question, data2), unwrapped);
|
|
831
973
|
run((selected) => {
|
|
832
|
-
|
|
974
|
+
if (!ctx.meta.preview) {
|
|
975
|
+
enmemory(ctx);
|
|
976
|
+
}
|
|
977
|
+
const stack = useStack(ctx);
|
|
833
978
|
const offset = isWithoutQuestion ? 0 : 1;
|
|
834
979
|
if (DEV && !unwrapped[selected + offset]) {
|
|
835
980
|
throw new Error("Choice children is empty, either add content there or make item not selectable");
|
|
836
981
|
}
|
|
837
982
|
stack.value[0].push(["choice", selected + offset], [null, 0]);
|
|
838
|
-
render();
|
|
983
|
+
render(ctx);
|
|
839
984
|
interactivity(true);
|
|
840
985
|
});
|
|
841
986
|
},
|
|
842
|
-
jump([scene]) {
|
|
987
|
+
jump({ ctx, data: data2 }, [scene]) {
|
|
843
988
|
if (DEV && !story[scene]) {
|
|
844
989
|
throw new Error(`Attempt to jump to unknown scene "${scene}"`);
|
|
845
990
|
}
|
|
846
991
|
if (DEV && story[scene].length === 0) {
|
|
847
992
|
throw new Error(`Attempt to jump to empty scene "${scene}"`);
|
|
848
993
|
}
|
|
994
|
+
const stack = useStack(ctx);
|
|
849
995
|
stack.value[0] = [
|
|
850
996
|
["jump", scene],
|
|
851
997
|
[null, -1]
|
|
852
998
|
];
|
|
853
|
-
match("clear", []
|
|
999
|
+
match("clear", [], {
|
|
1000
|
+
ctx,
|
|
1001
|
+
data: data2
|
|
1002
|
+
});
|
|
854
1003
|
},
|
|
855
|
-
clear([keep, characters2]) {
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
1004
|
+
clear({ ctx }, [keep, characters2, audio]) {
|
|
1005
|
+
ctx.vibrate(0);
|
|
1006
|
+
const run = ctx.clear(
|
|
1007
|
+
keep || EMPTY_SET,
|
|
1008
|
+
characters2 || EMPTY_SET,
|
|
1009
|
+
audio || { music: EMPTY_SET, sounds: EMPTY_SET }
|
|
1010
|
+
);
|
|
1011
|
+
run(() => push(ctx));
|
|
859
1012
|
},
|
|
860
|
-
condition([condition, variants]) {
|
|
1013
|
+
condition({ ctx }, [condition, variants]) {
|
|
861
1014
|
if (DEV && Object.values(variants).length === 0) {
|
|
862
1015
|
throw new Error(`Attempt to use Condition action with empty variants object`);
|
|
863
1016
|
}
|
|
864
|
-
if (!restoring) {
|
|
1017
|
+
if (!ctx.meta.restoring) {
|
|
865
1018
|
const val = String(condition());
|
|
866
1019
|
if (DEV && !variants[val]) {
|
|
867
1020
|
throw new Error(`Attempt to go to unknown variant "${val}"`);
|
|
@@ -869,46 +1022,56 @@ var novely = ({
|
|
|
869
1022
|
if (DEV && variants[val].length === 0) {
|
|
870
1023
|
throw new Error(`Attempt to go to empty variant "${val}"`);
|
|
871
1024
|
}
|
|
1025
|
+
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
872
1026
|
stack.value[0].push(["condition", val], [null, 0]);
|
|
873
|
-
render();
|
|
1027
|
+
render(ctx);
|
|
874
1028
|
}
|
|
875
1029
|
},
|
|
876
|
-
end() {
|
|
877
|
-
|
|
878
|
-
|
|
1030
|
+
end({ ctx }) {
|
|
1031
|
+
if (ctx.meta.preview)
|
|
1032
|
+
return;
|
|
1033
|
+
ctx.vibrate(0);
|
|
1034
|
+
ctx.clear(EMPTY_SET, EMPTY_SET, { music: EMPTY_SET, sounds: EMPTY_SET })(noop);
|
|
879
1035
|
renderer.ui.showScreen("mainmenu");
|
|
880
1036
|
interactivity(false);
|
|
881
1037
|
times.clear();
|
|
882
1038
|
},
|
|
883
|
-
input([question, onInput, setup]) {
|
|
884
|
-
|
|
1039
|
+
input({ ctx, data: data2 }, [question, onInput, setup]) {
|
|
1040
|
+
ctx.input(unwrap2(question, data2), onInput, setup)(() => {
|
|
1041
|
+
forward(ctx);
|
|
1042
|
+
});
|
|
885
1043
|
},
|
|
886
|
-
custom([handler]) {
|
|
887
|
-
const result =
|
|
888
|
-
if (
|
|
889
|
-
|
|
890
|
-
if (!
|
|
891
|
-
|
|
1044
|
+
custom({ ctx }, [handler]) {
|
|
1045
|
+
const result = ctx.custom(handler, () => {
|
|
1046
|
+
if (ctx.meta.restoring)
|
|
1047
|
+
return;
|
|
1048
|
+
if (handler.requireUserAction && !ctx.meta.preview) {
|
|
1049
|
+
enmemory(ctx);
|
|
1050
|
+
interactivity(true);
|
|
1051
|
+
}
|
|
1052
|
+
push(ctx);
|
|
892
1053
|
});
|
|
893
1054
|
return result;
|
|
894
1055
|
},
|
|
895
|
-
vibrate(pattern) {
|
|
896
|
-
|
|
897
|
-
push();
|
|
1056
|
+
vibrate({ ctx }, pattern) {
|
|
1057
|
+
ctx.vibrate(pattern);
|
|
1058
|
+
push(ctx);
|
|
898
1059
|
},
|
|
899
|
-
next() {
|
|
900
|
-
push();
|
|
1060
|
+
next({ ctx }) {
|
|
1061
|
+
push(ctx);
|
|
901
1062
|
},
|
|
902
|
-
animateCharacter([character, timeout, ...classes]) {
|
|
1063
|
+
animateCharacter({ ctx, data: data2 }, [character, timeout, ...classes]) {
|
|
903
1064
|
if (DEV && classes.length === 0) {
|
|
904
1065
|
throw new Error("Attempt to use AnimateCharacter without classes. Classes should be provided [https://novely.pages.dev/guide/actions/animateCharacter.html]");
|
|
905
1066
|
}
|
|
906
1067
|
if (DEV && (timeout <= 0 || !Number.isFinite(timeout) || Number.isNaN(timeout))) {
|
|
907
1068
|
throw new Error("Attempt to use AnimateCharacter with unacceptable timeout. It should be finite and greater than zero");
|
|
908
1069
|
}
|
|
1070
|
+
if (ctx.meta.preview)
|
|
1071
|
+
return;
|
|
909
1072
|
const handler = (get) => {
|
|
910
|
-
const { clear } = get(
|
|
911
|
-
const char =
|
|
1073
|
+
const { clear } = get(false);
|
|
1074
|
+
const char = ctx.getCharacter(character);
|
|
912
1075
|
if (!char)
|
|
913
1076
|
return;
|
|
914
1077
|
const target = char.canvas;
|
|
@@ -924,18 +1087,23 @@ var novely = ({
|
|
|
924
1087
|
clearTimeout(timeoutId);
|
|
925
1088
|
});
|
|
926
1089
|
};
|
|
927
|
-
|
|
1090
|
+
handler.key = "@@internal-animate-character";
|
|
1091
|
+
match("custom", [handler], {
|
|
1092
|
+
ctx,
|
|
1093
|
+
data: data2
|
|
1094
|
+
});
|
|
928
1095
|
},
|
|
929
|
-
text(text) {
|
|
930
|
-
const string = text.map((content) => unwrap2(content)).join(" ");
|
|
1096
|
+
text({ ctx, data: data2 }, text) {
|
|
1097
|
+
const string = text.map((content) => unwrap2(content, data2)).join(" ");
|
|
931
1098
|
if (DEV && string.length === 0) {
|
|
932
1099
|
throw new Error(`Action Text was called with empty string or array`);
|
|
933
1100
|
}
|
|
934
|
-
|
|
1101
|
+
ctx.text(string, () => forward(ctx));
|
|
935
1102
|
},
|
|
936
|
-
exit() {
|
|
937
|
-
if (restoring)
|
|
1103
|
+
exit({ ctx, data: data2 }) {
|
|
1104
|
+
if (ctx.meta.restoring)
|
|
938
1105
|
return;
|
|
1106
|
+
const stack = useStack(ctx);
|
|
939
1107
|
const path = stack.value[0];
|
|
940
1108
|
const last = path.at(-1);
|
|
941
1109
|
const ignore = [];
|
|
@@ -949,7 +1117,10 @@ var novely = ({
|
|
|
949
1117
|
if (isExitImpossible(path)) {
|
|
950
1118
|
const referred = refer(path);
|
|
951
1119
|
if (isAction(referred) && isSkippedDuringRestore(referred[0])) {
|
|
952
|
-
match("end", []
|
|
1120
|
+
match("end", [], {
|
|
1121
|
+
ctx,
|
|
1122
|
+
data: data2
|
|
1123
|
+
});
|
|
953
1124
|
}
|
|
954
1125
|
return;
|
|
955
1126
|
}
|
|
@@ -974,36 +1145,39 @@ var novely = ({
|
|
|
974
1145
|
}
|
|
975
1146
|
break;
|
|
976
1147
|
}
|
|
977
|
-
render();
|
|
1148
|
+
render(ctx);
|
|
978
1149
|
},
|
|
979
|
-
preload([source]) {
|
|
980
|
-
if (!goingBack && !restoring && !PRELOADED_ASSETS.has(source)) {
|
|
1150
|
+
preload({ ctx }, [source]) {
|
|
1151
|
+
if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
|
|
981
1152
|
PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
|
|
982
1153
|
}
|
|
983
|
-
push();
|
|
1154
|
+
push(ctx);
|
|
984
1155
|
},
|
|
985
|
-
block([scene]) {
|
|
1156
|
+
block({ ctx }, [scene]) {
|
|
986
1157
|
if (DEV && !story[scene]) {
|
|
987
1158
|
throw new Error(`Attempt to call Block action with unknown scene "${scene}"`);
|
|
988
1159
|
}
|
|
989
1160
|
if (DEV && story[scene].length === 0) {
|
|
990
1161
|
throw new Error(`Attempt to call Block action with empty scene "${scene}"`);
|
|
991
1162
|
}
|
|
992
|
-
if (!restoring) {
|
|
1163
|
+
if (!ctx.meta.restoring) {
|
|
1164
|
+
const stack = useStack(ctx);
|
|
993
1165
|
stack.value[0].push(["block", scene], [null, 0]);
|
|
994
|
-
render();
|
|
1166
|
+
render(ctx);
|
|
995
1167
|
}
|
|
996
1168
|
}
|
|
997
1169
|
});
|
|
998
|
-
const enmemory = () => {
|
|
999
|
-
if (restoring)
|
|
1170
|
+
const enmemory = (ctx) => {
|
|
1171
|
+
if (ctx.meta.restoring)
|
|
1000
1172
|
return;
|
|
1173
|
+
const stack = useStack(ctx);
|
|
1001
1174
|
const current = klona(stack.value);
|
|
1002
1175
|
current[2][1] = "auto";
|
|
1003
1176
|
stack.push(current);
|
|
1004
1177
|
save(true, "auto");
|
|
1005
1178
|
};
|
|
1006
|
-
const next = () => {
|
|
1179
|
+
const next = (ctx) => {
|
|
1180
|
+
const stack = useStack(ctx);
|
|
1007
1181
|
const path = stack.value[0];
|
|
1008
1182
|
const last = path.at(-1);
|
|
1009
1183
|
if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
|
|
@@ -1012,33 +1186,42 @@ var novely = ({
|
|
|
1012
1186
|
path.push([null, 0]);
|
|
1013
1187
|
}
|
|
1014
1188
|
};
|
|
1015
|
-
const render = () => {
|
|
1016
|
-
const
|
|
1189
|
+
const render = (ctx) => {
|
|
1190
|
+
const stack = useStack(ctx);
|
|
1191
|
+
const referred = refer(stack.value[0]);
|
|
1017
1192
|
if (isAction(referred)) {
|
|
1018
1193
|
const [action2, ...props] = referred;
|
|
1019
|
-
match(action2, props
|
|
1194
|
+
match(action2, props, {
|
|
1195
|
+
ctx,
|
|
1196
|
+
data: stack.value[1]
|
|
1197
|
+
});
|
|
1020
1198
|
} else {
|
|
1021
|
-
match("exit", []
|
|
1199
|
+
match("exit", [], {
|
|
1200
|
+
ctx,
|
|
1201
|
+
data: stack.value[1]
|
|
1202
|
+
});
|
|
1022
1203
|
}
|
|
1023
1204
|
};
|
|
1024
|
-
const push = () => {
|
|
1025
|
-
if (!restoring)
|
|
1026
|
-
next(), render();
|
|
1205
|
+
const push = (ctx) => {
|
|
1206
|
+
if (!ctx.meta.restoring)
|
|
1207
|
+
next(ctx), render(ctx);
|
|
1027
1208
|
};
|
|
1028
|
-
const forward = () => {
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1209
|
+
const forward = (ctx) => {
|
|
1210
|
+
if (!ctx.meta.preview)
|
|
1211
|
+
enmemory(ctx);
|
|
1212
|
+
push(ctx);
|
|
1213
|
+
if (!ctx.meta.preview)
|
|
1214
|
+
interactivity(true);
|
|
1032
1215
|
};
|
|
1033
1216
|
const interactivity = (value = false) => {
|
|
1034
1217
|
interacted = value ? interacted + 1 : 0;
|
|
1035
1218
|
};
|
|
1036
|
-
const unwrap2 = (content,
|
|
1219
|
+
const unwrap2 = (content, values) => {
|
|
1037
1220
|
const {
|
|
1038
1221
|
data: data2,
|
|
1039
1222
|
meta: [lang]
|
|
1040
1223
|
} = $.get();
|
|
1041
|
-
const obj =
|
|
1224
|
+
const obj = values ? values : data2;
|
|
1042
1225
|
const cnt = isFunction(content) ? content() : typeof content === "string" ? content : content[lang];
|
|
1043
1226
|
const str2 = isFunction(cnt) ? cnt() : cnt;
|
|
1044
1227
|
const trans = translation[lang];
|
|
@@ -1078,7 +1261,7 @@ var novely = ({
|
|
|
1078
1261
|
* Unwraps translatable content to a string value
|
|
1079
1262
|
*/
|
|
1080
1263
|
unwrap(content) {
|
|
1081
|
-
return unwrap2(content,
|
|
1264
|
+
return unwrap2(content, $.get().data);
|
|
1082
1265
|
}
|
|
1083
1266
|
};
|
|
1084
1267
|
};
|