@novely/core 0.19.5 → 0.21.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 +67 -46
- package/dist/index.global.js +441 -251
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +441 -251
- 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
|
-
let index = 0;
|
|
572
|
-
const max = stack.value[0].reduce((acc, [type, val]) => {
|
|
573
|
-
if (isNull(type) && isNumber(val)) {
|
|
574
|
-
return acc + 1;
|
|
575
|
-
}
|
|
576
|
-
return acc;
|
|
577
|
-
}, 0);
|
|
578
|
-
const queue = [];
|
|
579
|
-
const keep = /* @__PURE__ */ new Set();
|
|
580
|
-
const characters2 = /* @__PURE__ */ new Set();
|
|
581
|
-
const blocks = [];
|
|
582
|
-
for (const [type, val] of path) {
|
|
583
|
-
if (type === "jump") {
|
|
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();
|
|
617
|
-
}
|
|
618
|
-
}
|
|
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
|
-
}
|
|
633
|
-
}
|
|
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;
|
|
634
753
|
renderer.ui.showScreen("game");
|
|
635
|
-
|
|
636
|
-
const
|
|
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;
|
|
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]);
|
|
766
|
+
}
|
|
655
767
|
}
|
|
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
768
|
}
|
|
676
769
|
}
|
|
677
|
-
|
|
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,65 +944,81 @@ var novely = ({
|
|
|
803
944
|
}
|
|
804
945
|
return c || "";
|
|
805
946
|
})();
|
|
806
|
-
|
|
807
|
-
|
|
947
|
+
ctx.dialog(
|
|
948
|
+
unwrap2(content, data2),
|
|
949
|
+
unwrap2(name, data2),
|
|
950
|
+
character,
|
|
951
|
+
emotion,
|
|
952
|
+
() => forward(ctx)
|
|
953
|
+
);
|
|
808
954
|
},
|
|
809
|
-
function([fn]) {
|
|
810
|
-
const result = fn(restoring, goingBack);
|
|
811
|
-
if (!restoring)
|
|
812
|
-
result ? result.then(push) : push();
|
|
955
|
+
function({ ctx }, [fn]) {
|
|
956
|
+
const result = fn(ctx.meta.restoring, ctx.meta.goingBack, ctx.meta.preview);
|
|
957
|
+
if (!ctx.meta.restoring) {
|
|
958
|
+
result ? result.then(() => push(ctx)) : push(ctx);
|
|
959
|
+
}
|
|
813
960
|
return result;
|
|
814
961
|
},
|
|
815
|
-
choice([question, ...choices]) {
|
|
962
|
+
choice({ ctx, data: data2 }, [question, ...choices]) {
|
|
816
963
|
const isWithoutQuestion = Array.isArray(question);
|
|
817
964
|
if (isWithoutQuestion) {
|
|
818
965
|
choices.unshift(question);
|
|
819
966
|
question = "";
|
|
820
967
|
}
|
|
821
|
-
const
|
|
968
|
+
const unwrappedChoices = choices.map(([content, action2, visible]) => {
|
|
822
969
|
if (DEV && action2.length === 0 && (!visible || visible())) {
|
|
823
970
|
console.warn(`Choice children should not be empty, either add content there or make item not selectable`);
|
|
824
971
|
}
|
|
825
|
-
return [unwrap2(content), action2, visible];
|
|
972
|
+
return [unwrap2(content, data2), action2, visible];
|
|
826
973
|
});
|
|
827
|
-
if (DEV &&
|
|
974
|
+
if (DEV && unwrappedChoices.length === 0) {
|
|
828
975
|
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
976
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
977
|
+
ctx.choices(unwrap2(question, data2), unwrappedChoices, (selected) => {
|
|
978
|
+
if (!ctx.meta.preview) {
|
|
979
|
+
enmemory(ctx);
|
|
980
|
+
}
|
|
981
|
+
const stack = useStack(ctx);
|
|
833
982
|
const offset = isWithoutQuestion ? 0 : 1;
|
|
834
|
-
if (DEV && !
|
|
983
|
+
if (DEV && !unwrappedChoices[selected + offset]) {
|
|
835
984
|
throw new Error("Choice children is empty, either add content there or make item not selectable");
|
|
836
985
|
}
|
|
837
986
|
stack.value[0].push(["choice", selected + offset], [null, 0]);
|
|
838
|
-
render();
|
|
987
|
+
render(ctx);
|
|
839
988
|
interactivity(true);
|
|
840
989
|
});
|
|
841
990
|
},
|
|
842
|
-
jump([scene]) {
|
|
991
|
+
jump({ ctx, data: data2 }, [scene]) {
|
|
843
992
|
if (DEV && !story[scene]) {
|
|
844
993
|
throw new Error(`Attempt to jump to unknown scene "${scene}"`);
|
|
845
994
|
}
|
|
846
995
|
if (DEV && story[scene].length === 0) {
|
|
847
996
|
throw new Error(`Attempt to jump to empty scene "${scene}"`);
|
|
848
997
|
}
|
|
998
|
+
const stack = useStack(ctx);
|
|
849
999
|
stack.value[0] = [
|
|
850
1000
|
["jump", scene],
|
|
851
1001
|
[null, -1]
|
|
852
1002
|
];
|
|
853
|
-
match("clear", []
|
|
1003
|
+
match("clear", [], {
|
|
1004
|
+
ctx,
|
|
1005
|
+
data: data2
|
|
1006
|
+
});
|
|
854
1007
|
},
|
|
855
|
-
clear([keep, characters2]) {
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
1008
|
+
clear({ ctx }, [keep, characters2, audio]) {
|
|
1009
|
+
ctx.vibrate(0);
|
|
1010
|
+
ctx.clear(
|
|
1011
|
+
keep || EMPTY_SET,
|
|
1012
|
+
characters2 || EMPTY_SET,
|
|
1013
|
+
audio || { music: EMPTY_SET, sounds: EMPTY_SET },
|
|
1014
|
+
() => push(ctx)
|
|
1015
|
+
);
|
|
859
1016
|
},
|
|
860
|
-
condition([condition, variants]) {
|
|
1017
|
+
condition({ ctx }, [condition, variants]) {
|
|
861
1018
|
if (DEV && Object.values(variants).length === 0) {
|
|
862
1019
|
throw new Error(`Attempt to use Condition action with empty variants object`);
|
|
863
1020
|
}
|
|
864
|
-
if (!restoring) {
|
|
1021
|
+
if (!ctx.meta.restoring) {
|
|
865
1022
|
const val = String(condition());
|
|
866
1023
|
if (DEV && !variants[val]) {
|
|
867
1024
|
throw new Error(`Attempt to go to unknown variant "${val}"`);
|
|
@@ -869,46 +1026,59 @@ var novely = ({
|
|
|
869
1026
|
if (DEV && variants[val].length === 0) {
|
|
870
1027
|
throw new Error(`Attempt to go to empty variant "${val}"`);
|
|
871
1028
|
}
|
|
1029
|
+
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
872
1030
|
stack.value[0].push(["condition", val], [null, 0]);
|
|
873
|
-
render();
|
|
1031
|
+
render(ctx);
|
|
874
1032
|
}
|
|
875
1033
|
},
|
|
876
|
-
end() {
|
|
877
|
-
|
|
878
|
-
|
|
1034
|
+
end({ ctx }) {
|
|
1035
|
+
if (ctx.meta.preview)
|
|
1036
|
+
return;
|
|
1037
|
+
ctx.vibrate(0);
|
|
1038
|
+
ctx.clear(EMPTY_SET, EMPTY_SET, { music: EMPTY_SET, sounds: EMPTY_SET }, noop);
|
|
879
1039
|
renderer.ui.showScreen("mainmenu");
|
|
880
1040
|
interactivity(false);
|
|
881
1041
|
times.clear();
|
|
882
1042
|
},
|
|
883
|
-
input([question, onInput, setup]) {
|
|
884
|
-
|
|
1043
|
+
input({ ctx, data: data2 }, [question, onInput, setup]) {
|
|
1044
|
+
ctx.input(
|
|
1045
|
+
unwrap2(question, data2),
|
|
1046
|
+
onInput,
|
|
1047
|
+
setup || noop,
|
|
1048
|
+
() => forward(ctx)
|
|
1049
|
+
);
|
|
885
1050
|
},
|
|
886
|
-
custom([handler]) {
|
|
887
|
-
const result =
|
|
888
|
-
if (
|
|
889
|
-
|
|
890
|
-
if (!
|
|
891
|
-
|
|
1051
|
+
custom({ ctx }, [handler]) {
|
|
1052
|
+
const result = ctx.custom(handler, () => {
|
|
1053
|
+
if (ctx.meta.restoring)
|
|
1054
|
+
return;
|
|
1055
|
+
if (handler.requireUserAction && !ctx.meta.preview) {
|
|
1056
|
+
enmemory(ctx);
|
|
1057
|
+
interactivity(true);
|
|
1058
|
+
}
|
|
1059
|
+
push(ctx);
|
|
892
1060
|
});
|
|
893
1061
|
return result;
|
|
894
1062
|
},
|
|
895
|
-
vibrate(pattern) {
|
|
896
|
-
|
|
897
|
-
push();
|
|
1063
|
+
vibrate({ ctx }, pattern) {
|
|
1064
|
+
ctx.vibrate(pattern);
|
|
1065
|
+
push(ctx);
|
|
898
1066
|
},
|
|
899
|
-
next() {
|
|
900
|
-
push();
|
|
1067
|
+
next({ ctx }) {
|
|
1068
|
+
push(ctx);
|
|
901
1069
|
},
|
|
902
|
-
animateCharacter([character, timeout, ...classes]) {
|
|
1070
|
+
animateCharacter({ ctx, data: data2 }, [character, timeout, ...classes]) {
|
|
903
1071
|
if (DEV && classes.length === 0) {
|
|
904
1072
|
throw new Error("Attempt to use AnimateCharacter without classes. Classes should be provided [https://novely.pages.dev/guide/actions/animateCharacter.html]");
|
|
905
1073
|
}
|
|
906
1074
|
if (DEV && (timeout <= 0 || !Number.isFinite(timeout) || Number.isNaN(timeout))) {
|
|
907
1075
|
throw new Error("Attempt to use AnimateCharacter with unacceptable timeout. It should be finite and greater than zero");
|
|
908
1076
|
}
|
|
1077
|
+
if (ctx.meta.preview)
|
|
1078
|
+
return;
|
|
909
1079
|
const handler = (get) => {
|
|
910
|
-
const { clear } = get(
|
|
911
|
-
const char =
|
|
1080
|
+
const { clear } = get(false);
|
|
1081
|
+
const char = ctx.getCharacter(character);
|
|
912
1082
|
if (!char)
|
|
913
1083
|
return;
|
|
914
1084
|
const target = char.canvas;
|
|
@@ -924,18 +1094,23 @@ var novely = ({
|
|
|
924
1094
|
clearTimeout(timeoutId);
|
|
925
1095
|
});
|
|
926
1096
|
};
|
|
927
|
-
|
|
1097
|
+
handler.key = "@@internal-animate-character";
|
|
1098
|
+
match("custom", [handler], {
|
|
1099
|
+
ctx,
|
|
1100
|
+
data: data2
|
|
1101
|
+
});
|
|
928
1102
|
},
|
|
929
|
-
text(text) {
|
|
930
|
-
const string = text.map((content) => unwrap2(content)).join(" ");
|
|
1103
|
+
text({ ctx, data: data2 }, text) {
|
|
1104
|
+
const string = text.map((content) => unwrap2(content, data2)).join(" ");
|
|
931
1105
|
if (DEV && string.length === 0) {
|
|
932
1106
|
throw new Error(`Action Text was called with empty string or array`);
|
|
933
1107
|
}
|
|
934
|
-
|
|
1108
|
+
ctx.text(string, () => forward(ctx));
|
|
935
1109
|
},
|
|
936
|
-
exit() {
|
|
937
|
-
if (restoring)
|
|
1110
|
+
exit({ ctx, data: data2 }) {
|
|
1111
|
+
if (ctx.meta.restoring)
|
|
938
1112
|
return;
|
|
1113
|
+
const stack = useStack(ctx);
|
|
939
1114
|
const path = stack.value[0];
|
|
940
1115
|
const last = path.at(-1);
|
|
941
1116
|
const ignore = [];
|
|
@@ -949,7 +1124,10 @@ var novely = ({
|
|
|
949
1124
|
if (isExitImpossible(path)) {
|
|
950
1125
|
const referred = refer(path);
|
|
951
1126
|
if (isAction(referred) && isSkippedDuringRestore(referred[0])) {
|
|
952
|
-
match("end", []
|
|
1127
|
+
match("end", [], {
|
|
1128
|
+
ctx,
|
|
1129
|
+
data: data2
|
|
1130
|
+
});
|
|
953
1131
|
}
|
|
954
1132
|
return;
|
|
955
1133
|
}
|
|
@@ -974,36 +1152,39 @@ var novely = ({
|
|
|
974
1152
|
}
|
|
975
1153
|
break;
|
|
976
1154
|
}
|
|
977
|
-
render();
|
|
1155
|
+
render(ctx);
|
|
978
1156
|
},
|
|
979
|
-
preload([source]) {
|
|
980
|
-
if (!goingBack && !restoring && !PRELOADED_ASSETS.has(source)) {
|
|
1157
|
+
preload({ ctx }, [source]) {
|
|
1158
|
+
if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
|
|
981
1159
|
PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
|
|
982
1160
|
}
|
|
983
|
-
push();
|
|
1161
|
+
push(ctx);
|
|
984
1162
|
},
|
|
985
|
-
block([scene]) {
|
|
1163
|
+
block({ ctx }, [scene]) {
|
|
986
1164
|
if (DEV && !story[scene]) {
|
|
987
1165
|
throw new Error(`Attempt to call Block action with unknown scene "${scene}"`);
|
|
988
1166
|
}
|
|
989
1167
|
if (DEV && story[scene].length === 0) {
|
|
990
1168
|
throw new Error(`Attempt to call Block action with empty scene "${scene}"`);
|
|
991
1169
|
}
|
|
992
|
-
if (!restoring) {
|
|
1170
|
+
if (!ctx.meta.restoring) {
|
|
1171
|
+
const stack = useStack(ctx);
|
|
993
1172
|
stack.value[0].push(["block", scene], [null, 0]);
|
|
994
|
-
render();
|
|
1173
|
+
render(ctx);
|
|
995
1174
|
}
|
|
996
1175
|
}
|
|
997
1176
|
});
|
|
998
|
-
const enmemory = () => {
|
|
999
|
-
if (restoring)
|
|
1177
|
+
const enmemory = (ctx) => {
|
|
1178
|
+
if (ctx.meta.restoring)
|
|
1000
1179
|
return;
|
|
1180
|
+
const stack = useStack(ctx);
|
|
1001
1181
|
const current = klona(stack.value);
|
|
1002
1182
|
current[2][1] = "auto";
|
|
1003
1183
|
stack.push(current);
|
|
1004
1184
|
save(true, "auto");
|
|
1005
1185
|
};
|
|
1006
|
-
const next = () => {
|
|
1186
|
+
const next = (ctx) => {
|
|
1187
|
+
const stack = useStack(ctx);
|
|
1007
1188
|
const path = stack.value[0];
|
|
1008
1189
|
const last = path.at(-1);
|
|
1009
1190
|
if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
|
|
@@ -1012,33 +1193,42 @@ var novely = ({
|
|
|
1012
1193
|
path.push([null, 0]);
|
|
1013
1194
|
}
|
|
1014
1195
|
};
|
|
1015
|
-
const render = () => {
|
|
1016
|
-
const
|
|
1196
|
+
const render = (ctx) => {
|
|
1197
|
+
const stack = useStack(ctx);
|
|
1198
|
+
const referred = refer(stack.value[0]);
|
|
1017
1199
|
if (isAction(referred)) {
|
|
1018
1200
|
const [action2, ...props] = referred;
|
|
1019
|
-
match(action2, props
|
|
1201
|
+
match(action2, props, {
|
|
1202
|
+
ctx,
|
|
1203
|
+
data: stack.value[1]
|
|
1204
|
+
});
|
|
1020
1205
|
} else {
|
|
1021
|
-
match("exit", []
|
|
1206
|
+
match("exit", [], {
|
|
1207
|
+
ctx,
|
|
1208
|
+
data: stack.value[1]
|
|
1209
|
+
});
|
|
1022
1210
|
}
|
|
1023
1211
|
};
|
|
1024
|
-
const push = () => {
|
|
1025
|
-
if (!restoring)
|
|
1026
|
-
next(), render();
|
|
1212
|
+
const push = (ctx) => {
|
|
1213
|
+
if (!ctx.meta.restoring)
|
|
1214
|
+
next(ctx), render(ctx);
|
|
1027
1215
|
};
|
|
1028
|
-
const forward = () => {
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1216
|
+
const forward = (ctx) => {
|
|
1217
|
+
if (!ctx.meta.preview)
|
|
1218
|
+
enmemory(ctx);
|
|
1219
|
+
push(ctx);
|
|
1220
|
+
if (!ctx.meta.preview)
|
|
1221
|
+
interactivity(true);
|
|
1032
1222
|
};
|
|
1033
1223
|
const interactivity = (value = false) => {
|
|
1034
1224
|
interacted = value ? interacted + 1 : 0;
|
|
1035
1225
|
};
|
|
1036
|
-
const unwrap2 = (content,
|
|
1226
|
+
const unwrap2 = (content, values) => {
|
|
1037
1227
|
const {
|
|
1038
1228
|
data: data2,
|
|
1039
1229
|
meta: [lang]
|
|
1040
1230
|
} = $.get();
|
|
1041
|
-
const obj =
|
|
1231
|
+
const obj = values ? values : data2;
|
|
1042
1232
|
const cnt = isFunction(content) ? content() : typeof content === "string" ? content : content[lang];
|
|
1043
1233
|
const str2 = isFunction(cnt) ? cnt() : cnt;
|
|
1044
1234
|
const trans = translation[lang];
|
|
@@ -1078,7 +1268,7 @@ var novely = ({
|
|
|
1078
1268
|
* Unwraps translatable content to a string value
|
|
1079
1269
|
*/
|
|
1080
1270
|
unwrap(content) {
|
|
1081
|
-
return unwrap2(content,
|
|
1271
|
+
return unwrap2(content, $.get().data);
|
|
1082
1272
|
}
|
|
1083
1273
|
};
|
|
1084
1274
|
};
|