@novely/core 0.19.4 → 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 +439 -255
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +439 -255
- 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) => {
|
|
@@ -141,15 +157,199 @@ var isExitImpossible = (path) => {
|
|
|
141
157
|
if (blockStatements.length > blockExitStatements.length) {
|
|
142
158
|
return false;
|
|
143
159
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
160
|
+
return !blockExitStatements.every(([name], i) => name && name.startsWith(blockStatements[i][0]));
|
|
161
|
+
};
|
|
162
|
+
var getOppositeAction = (action) => {
|
|
163
|
+
const MAP = {
|
|
164
|
+
"showCharacter": "hideCharacter",
|
|
165
|
+
"playSound": "stopSound",
|
|
166
|
+
"playMusic": "stopMusic",
|
|
167
|
+
"voice": "stopVoice"
|
|
168
|
+
};
|
|
169
|
+
return MAP[action];
|
|
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
|
+
}
|
|
151
292
|
}
|
|
152
|
-
|
|
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;
|
|
153
353
|
};
|
|
154
354
|
|
|
155
355
|
// src/global.ts
|
|
@@ -170,10 +370,13 @@ var store = (current, subscribers = /* @__PURE__ */ new Set()) => {
|
|
|
170
370
|
const update = (fn) => {
|
|
171
371
|
push(current = fn(current));
|
|
172
372
|
};
|
|
373
|
+
const set = (val) => {
|
|
374
|
+
update(() => val);
|
|
375
|
+
};
|
|
173
376
|
const get = () => {
|
|
174
377
|
return current;
|
|
175
378
|
};
|
|
176
|
-
return { subscribe, update, get };
|
|
379
|
+
return { subscribe, update, set, get };
|
|
177
380
|
};
|
|
178
381
|
|
|
179
382
|
// ../deepmerge/dist/index.js
|
|
@@ -421,6 +624,7 @@ var novely = ({
|
|
|
421
624
|
}
|
|
422
625
|
});
|
|
423
626
|
function state(value) {
|
|
627
|
+
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
424
628
|
if (!value)
|
|
425
629
|
return stack.value[1];
|
|
426
630
|
const prev = stack.value[1];
|
|
@@ -437,26 +641,6 @@ var novely = ({
|
|
|
437
641
|
[intime(Date.now()), "auto"]
|
|
438
642
|
];
|
|
439
643
|
};
|
|
440
|
-
const createStack = (current, stack2 = [current]) => {
|
|
441
|
-
return {
|
|
442
|
-
get value() {
|
|
443
|
-
return stack2.at(-1);
|
|
444
|
-
},
|
|
445
|
-
set value(value) {
|
|
446
|
-
stack2[stack2.length - 1] = value;
|
|
447
|
-
},
|
|
448
|
-
back() {
|
|
449
|
-
if (stack2.length > 1)
|
|
450
|
-
stack2.pop(), goingBack = true;
|
|
451
|
-
},
|
|
452
|
-
push(value) {
|
|
453
|
-
stack2.push(value);
|
|
454
|
-
},
|
|
455
|
-
clear() {
|
|
456
|
-
stack2 = [getDefaultSave(klona(defaultState))];
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
};
|
|
460
644
|
const getLanguageWithoutParameters = () => {
|
|
461
645
|
return getLanguage2(languages, getLanguage);
|
|
462
646
|
};
|
|
@@ -498,11 +682,10 @@ var novely = ({
|
|
|
498
682
|
}
|
|
499
683
|
$$.update((prev) => (prev.dataLoaded = true, prev));
|
|
500
684
|
dataLoaded.resolve();
|
|
501
|
-
$.
|
|
685
|
+
$.set(stored);
|
|
502
686
|
};
|
|
503
687
|
storageDelay.then(getStoredData);
|
|
504
688
|
const initial = getDefaultSave(klona(defaultState));
|
|
505
|
-
const stack = createStack(initial);
|
|
506
689
|
addEventListener("visibilitychange", () => {
|
|
507
690
|
if (document.visibilityState === "hidden") {
|
|
508
691
|
throttledEmergencyOnStorageDataChange();
|
|
@@ -514,6 +697,7 @@ var novely = ({
|
|
|
514
697
|
return;
|
|
515
698
|
if (!autosaves && type === "auto")
|
|
516
699
|
return;
|
|
700
|
+
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
517
701
|
const current = klona(stack.value);
|
|
518
702
|
$.update((prev) => {
|
|
519
703
|
const isLatest = findLastIndex(prev.saves, (value) => times.has(value[2][0])) === prev.saves.length - 1;
|
|
@@ -543,139 +727,65 @@ var novely = ({
|
|
|
543
727
|
}
|
|
544
728
|
restore(save2);
|
|
545
729
|
};
|
|
546
|
-
const set = (save2) => {
|
|
730
|
+
const set = (save2, ctx) => {
|
|
731
|
+
const stack = useStack(ctx || renderer.getContext(MAIN_CONTEXT_KEY));
|
|
547
732
|
stack.value = save2;
|
|
548
733
|
return restore(save2);
|
|
549
734
|
};
|
|
550
|
-
let restoring = false;
|
|
551
|
-
let goingBack = false;
|
|
552
735
|
let interacted = 0;
|
|
553
736
|
const restore = async (save2) => {
|
|
554
737
|
if (!$$.get().dataLoaded)
|
|
555
738
|
return;
|
|
556
739
|
let latest = save2 || $.get().saves.at(-1);
|
|
557
740
|
if (!latest) {
|
|
558
|
-
$.
|
|
741
|
+
$.set({
|
|
559
742
|
saves: [initial],
|
|
560
743
|
data: klona(defaultData),
|
|
561
744
|
meta: [getLanguageWithoutParameters(), DEFAULT_TYPEWRITER_SPEED, 1, 1, 1]
|
|
562
|
-
})
|
|
745
|
+
});
|
|
563
746
|
latest = klona(initial);
|
|
564
747
|
}
|
|
565
|
-
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
let index = 0;
|
|
571
|
-
const max = stack.value[0].reduce((acc, [type, val]) => {
|
|
572
|
-
if (isNull(type) && isNumber(val)) {
|
|
573
|
-
return acc + 1;
|
|
574
|
-
}
|
|
575
|
-
return acc;
|
|
576
|
-
}, 0);
|
|
577
|
-
const queue = [];
|
|
578
|
-
const keep = /* @__PURE__ */ new Set();
|
|
579
|
-
const characters2 = /* @__PURE__ */ new Set();
|
|
580
|
-
const blocks = [];
|
|
581
|
-
for (const [type, val] of path) {
|
|
582
|
-
if (type === "jump") {
|
|
583
|
-
precurrent = story;
|
|
584
|
-
current = current[val];
|
|
585
|
-
} else if (type === null) {
|
|
586
|
-
precurrent = current;
|
|
587
|
-
if (isNumber(val)) {
|
|
588
|
-
index++;
|
|
589
|
-
let startIndex = 0;
|
|
590
|
-
if (ignoreNested) {
|
|
591
|
-
const prev = findLastPathItemBeforeItemOfType(path.slice(0, index), "block");
|
|
592
|
-
if (prev) {
|
|
593
|
-
startIndex = prev[1];
|
|
594
|
-
ignoreNested = false;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
for (let i = startIndex; i <= val; i++) {
|
|
598
|
-
const item = current[i];
|
|
599
|
-
if (!isAction(item))
|
|
600
|
-
continue;
|
|
601
|
-
const [action2, ...meta] = item;
|
|
602
|
-
const push2 = () => {
|
|
603
|
-
keep.add(action2);
|
|
604
|
-
queue.push([action2, meta]);
|
|
605
|
-
};
|
|
606
|
-
if (action2 === "showCharacter")
|
|
607
|
-
characters2.add(meta[0]);
|
|
608
|
-
if (isSkippedDuringRestore(action2) || isUserRequiredAction(action2, meta)) {
|
|
609
|
-
if (index === max && i === val) {
|
|
610
|
-
push2();
|
|
611
|
-
} else {
|
|
612
|
-
continue;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
push2();
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
current = current[val];
|
|
619
|
-
} else if (type === "choice") {
|
|
620
|
-
blocks.push(precurrent);
|
|
621
|
-
current = current[val + 1][1];
|
|
622
|
-
} else if (type === "condition") {
|
|
623
|
-
blocks.push(precurrent);
|
|
624
|
-
current = current[2][val];
|
|
625
|
-
} else if (type === "block") {
|
|
626
|
-
blocks.push(precurrent);
|
|
627
|
-
current = story[val];
|
|
628
|
-
} else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
|
|
629
|
-
current = blocks.pop();
|
|
630
|
-
ignoreNested = true;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
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;
|
|
633
753
|
renderer.ui.showScreen("game");
|
|
634
|
-
|
|
635
|
-
const
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
});
|
|
648
|
-
if (notLatest)
|
|
649
|
-
continue;
|
|
650
|
-
}
|
|
651
|
-
const result = match(action2, meta);
|
|
652
|
-
if (isPromise(result)) {
|
|
653
|
-
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
|
+
}
|
|
654
767
|
}
|
|
655
|
-
} else if (action2 === "showCharacter" || action2 === "playSound" || action2 === "playMusic") {
|
|
656
|
-
const closing = action2 === "showCharacter" ? "hideCharacter" : action2 === "playSound" ? "stopSound" : "stopMusic";
|
|
657
|
-
const skip = next2(i).some(([_action, _meta]) => {
|
|
658
|
-
if (!_meta || !meta)
|
|
659
|
-
return false;
|
|
660
|
-
const hidden = _action === closing && _meta[0] === meta[0];
|
|
661
|
-
const notLatest = _action === action2 && _meta[0] === meta[0];
|
|
662
|
-
return hidden || notLatest;
|
|
663
|
-
});
|
|
664
|
-
if (skip)
|
|
665
|
-
continue;
|
|
666
|
-
match(action2, meta);
|
|
667
|
-
} else if (action2 === "showBackground" || action2 === "animateCharacter" || action2 === "preload" || action2 === "voice") {
|
|
668
|
-
const notLatest = next2(i).some(([_action]) => action2 === _action);
|
|
669
|
-
if (notLatest)
|
|
670
|
-
continue;
|
|
671
|
-
match(action2, meta);
|
|
672
|
-
} else {
|
|
673
|
-
match(action2, meta);
|
|
674
768
|
}
|
|
675
769
|
}
|
|
676
|
-
|
|
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);
|
|
677
784
|
};
|
|
678
|
-
const refer = (path
|
|
785
|
+
const refer = (path) => {
|
|
786
|
+
if (!path) {
|
|
787
|
+
path = useStack(MAIN_CONTEXT_KEY).value[0];
|
|
788
|
+
}
|
|
679
789
|
let current = story;
|
|
680
790
|
let precurrent = story;
|
|
681
791
|
const blocks = [];
|
|
@@ -706,10 +816,12 @@ var novely = ({
|
|
|
706
816
|
renderer.ui.showExitPrompt();
|
|
707
817
|
return;
|
|
708
818
|
}
|
|
819
|
+
const ctx = renderer.getContext(MAIN_CONTEXT_KEY);
|
|
820
|
+
const stack = useStack(ctx);
|
|
709
821
|
const current = stack.value;
|
|
710
822
|
stack.clear();
|
|
711
823
|
renderer.ui.showScreen("mainmenu");
|
|
712
|
-
|
|
824
|
+
ctx.audio.destroy();
|
|
713
825
|
const [time, type] = current[2];
|
|
714
826
|
if (type === "auto" && interacted <= 1 && times.has(time)) {
|
|
715
827
|
$.update((prev) => {
|
|
@@ -720,13 +832,38 @@ var novely = ({
|
|
|
720
832
|
interactivity(false);
|
|
721
833
|
times.clear();
|
|
722
834
|
};
|
|
723
|
-
const back = () => {
|
|
724
|
-
|
|
835
|
+
const back = async () => {
|
|
836
|
+
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
837
|
+
stack.back();
|
|
838
|
+
await restore(stack.value);
|
|
725
839
|
};
|
|
726
840
|
const t = (key, lang) => {
|
|
727
841
|
return translation[lang].internal[key];
|
|
728
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
|
+
};
|
|
729
865
|
const renderer = createRenderer({
|
|
866
|
+
mainContextKey: MAIN_CONTEXT_KEY,
|
|
730
867
|
characters,
|
|
731
868
|
set,
|
|
732
869
|
restore,
|
|
@@ -735,62 +872,67 @@ var novely = ({
|
|
|
735
872
|
exit,
|
|
736
873
|
back,
|
|
737
874
|
t,
|
|
738
|
-
|
|
875
|
+
preview,
|
|
876
|
+
removeContext,
|
|
739
877
|
languages,
|
|
740
878
|
$,
|
|
741
879
|
$$
|
|
742
880
|
});
|
|
881
|
+
const useStack = createUseStackFunction(renderer);
|
|
882
|
+
useStack(MAIN_CONTEXT_KEY).push(initial);
|
|
743
883
|
renderer.ui.start();
|
|
744
|
-
const match = matchAction({
|
|
745
|
-
wait([time]) {
|
|
746
|
-
if (!restoring)
|
|
884
|
+
const match = matchAction(renderer.getContext, {
|
|
885
|
+
wait({ ctx }, [time]) {
|
|
886
|
+
if (!ctx.meta.restoring)
|
|
747
887
|
setTimeout(push, isFunction(time) ? time() : time);
|
|
748
888
|
},
|
|
749
|
-
showBackground([background]) {
|
|
750
|
-
|
|
751
|
-
push();
|
|
889
|
+
showBackground({ ctx }, [background]) {
|
|
890
|
+
ctx.background(background);
|
|
891
|
+
push(ctx);
|
|
752
892
|
},
|
|
753
|
-
playMusic([source]) {
|
|
754
|
-
|
|
755
|
-
push();
|
|
893
|
+
playMusic({ ctx }, [source]) {
|
|
894
|
+
ctx.audio.music(source, "music", true).play();
|
|
895
|
+
push(ctx);
|
|
756
896
|
},
|
|
757
|
-
stopMusic([source]) {
|
|
758
|
-
|
|
759
|
-
push();
|
|
897
|
+
stopMusic({ ctx }, [source]) {
|
|
898
|
+
ctx.audio.music(source, "music").stop();
|
|
899
|
+
push(ctx);
|
|
760
900
|
},
|
|
761
|
-
playSound([source, loop]) {
|
|
762
|
-
|
|
763
|
-
push();
|
|
901
|
+
playSound({ ctx }, [source, loop]) {
|
|
902
|
+
ctx.audio.music(source, "sound", loop || false).play();
|
|
903
|
+
push(ctx);
|
|
764
904
|
},
|
|
765
|
-
stopSound([source]) {
|
|
766
|
-
|
|
767
|
-
push();
|
|
905
|
+
stopSound({ ctx }, [source]) {
|
|
906
|
+
ctx.audio.music(source, "sound").stop();
|
|
907
|
+
push(ctx);
|
|
768
908
|
},
|
|
769
|
-
voice([source]) {
|
|
770
|
-
|
|
771
|
-
push();
|
|
909
|
+
voice({ ctx }, [source]) {
|
|
910
|
+
ctx.audio.voice(source);
|
|
911
|
+
push(ctx);
|
|
772
912
|
},
|
|
773
|
-
stopVoice() {
|
|
774
|
-
|
|
775
|
-
push();
|
|
913
|
+
stopVoice({ ctx }) {
|
|
914
|
+
ctx.audio.voiceStop();
|
|
915
|
+
push(ctx);
|
|
776
916
|
},
|
|
777
|
-
showCharacter([character, emotion, className, style]) {
|
|
917
|
+
showCharacter({ ctx }, [character, emotion, className, style]) {
|
|
778
918
|
if (DEV && !characters[character].emotions[emotion]) {
|
|
779
919
|
throw new Error(`Attempt to show character "${character}" with unknown emotion "${emotion}"`);
|
|
780
920
|
}
|
|
781
|
-
const handle =
|
|
782
|
-
handle.append(className, style, restoring);
|
|
783
|
-
handle.
|
|
784
|
-
push();
|
|
921
|
+
const handle = ctx.character(character);
|
|
922
|
+
handle.append(className, style, ctx.meta.restoring);
|
|
923
|
+
handle.emotion(emotion, true);
|
|
924
|
+
push(ctx);
|
|
785
925
|
},
|
|
786
|
-
hideCharacter([character, className, style, duration]) {
|
|
787
|
-
|
|
926
|
+
hideCharacter({ ctx }, [character, className, style, duration]) {
|
|
927
|
+
ctx.character(character).remove(className, style, duration, ctx.meta.restoring).then(() => {
|
|
928
|
+
push(ctx);
|
|
929
|
+
});
|
|
788
930
|
},
|
|
789
|
-
dialog([character, content, emotion]) {
|
|
931
|
+
dialog({ ctx, data: data2 }, [character, content, emotion]) {
|
|
790
932
|
const name = (() => {
|
|
791
933
|
const c = character;
|
|
792
934
|
const cs = characters;
|
|
793
|
-
const lang = $.get().meta
|
|
935
|
+
const [lang] = $.get().meta;
|
|
794
936
|
if (c && c in cs) {
|
|
795
937
|
const block = cs[c].name;
|
|
796
938
|
if (typeof block === "string") {
|
|
@@ -802,16 +944,17 @@ var novely = ({
|
|
|
802
944
|
}
|
|
803
945
|
return c || "";
|
|
804
946
|
})();
|
|
805
|
-
const run =
|
|
806
|
-
run(forward
|
|
947
|
+
const run = ctx.dialog(unwrap2(content, data2), unwrap2(name, data2), character, emotion);
|
|
948
|
+
run(() => forward(ctx));
|
|
807
949
|
},
|
|
808
|
-
function([fn]) {
|
|
809
|
-
const result = fn(restoring, goingBack);
|
|
810
|
-
if (!restoring)
|
|
811
|
-
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
|
+
}
|
|
812
955
|
return result;
|
|
813
956
|
},
|
|
814
|
-
choice([question, ...choices]) {
|
|
957
|
+
choice({ ctx, data: data2 }, [question, ...choices]) {
|
|
815
958
|
const isWithoutQuestion = Array.isArray(question);
|
|
816
959
|
if (isWithoutQuestion) {
|
|
817
960
|
choices.unshift(question);
|
|
@@ -821,46 +964,57 @@ var novely = ({
|
|
|
821
964
|
if (DEV && action2.length === 0 && (!visible || visible())) {
|
|
822
965
|
console.warn(`Choice children should not be empty, either add content there or make item not selectable`);
|
|
823
966
|
}
|
|
824
|
-
return [unwrap2(content), action2, visible];
|
|
967
|
+
return [unwrap2(content, data2), action2, visible];
|
|
825
968
|
});
|
|
826
969
|
if (DEV && unwrapped.length === 0) {
|
|
827
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]`);
|
|
828
971
|
}
|
|
829
|
-
const run =
|
|
972
|
+
const run = ctx.choices(unwrap2(question, data2), unwrapped);
|
|
830
973
|
run((selected) => {
|
|
831
|
-
|
|
974
|
+
if (!ctx.meta.preview) {
|
|
975
|
+
enmemory(ctx);
|
|
976
|
+
}
|
|
977
|
+
const stack = useStack(ctx);
|
|
832
978
|
const offset = isWithoutQuestion ? 0 : 1;
|
|
833
979
|
if (DEV && !unwrapped[selected + offset]) {
|
|
834
980
|
throw new Error("Choice children is empty, either add content there or make item not selectable");
|
|
835
981
|
}
|
|
836
982
|
stack.value[0].push(["choice", selected + offset], [null, 0]);
|
|
837
|
-
render();
|
|
983
|
+
render(ctx);
|
|
838
984
|
interactivity(true);
|
|
839
985
|
});
|
|
840
986
|
},
|
|
841
|
-
jump([scene]) {
|
|
987
|
+
jump({ ctx, data: data2 }, [scene]) {
|
|
842
988
|
if (DEV && !story[scene]) {
|
|
843
989
|
throw new Error(`Attempt to jump to unknown scene "${scene}"`);
|
|
844
990
|
}
|
|
845
991
|
if (DEV && story[scene].length === 0) {
|
|
846
992
|
throw new Error(`Attempt to jump to empty scene "${scene}"`);
|
|
847
993
|
}
|
|
994
|
+
const stack = useStack(ctx);
|
|
848
995
|
stack.value[0] = [
|
|
849
996
|
["jump", scene],
|
|
850
997
|
[null, -1]
|
|
851
998
|
];
|
|
852
|
-
match("clear", []
|
|
999
|
+
match("clear", [], {
|
|
1000
|
+
ctx,
|
|
1001
|
+
data: data2
|
|
1002
|
+
});
|
|
853
1003
|
},
|
|
854
|
-
clear([keep, characters2]) {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
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));
|
|
858
1012
|
},
|
|
859
|
-
condition([condition, variants]) {
|
|
1013
|
+
condition({ ctx }, [condition, variants]) {
|
|
860
1014
|
if (DEV && Object.values(variants).length === 0) {
|
|
861
1015
|
throw new Error(`Attempt to use Condition action with empty variants object`);
|
|
862
1016
|
}
|
|
863
|
-
if (!restoring) {
|
|
1017
|
+
if (!ctx.meta.restoring) {
|
|
864
1018
|
const val = String(condition());
|
|
865
1019
|
if (DEV && !variants[val]) {
|
|
866
1020
|
throw new Error(`Attempt to go to unknown variant "${val}"`);
|
|
@@ -868,46 +1022,56 @@ var novely = ({
|
|
|
868
1022
|
if (DEV && variants[val].length === 0) {
|
|
869
1023
|
throw new Error(`Attempt to go to empty variant "${val}"`);
|
|
870
1024
|
}
|
|
1025
|
+
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
871
1026
|
stack.value[0].push(["condition", val], [null, 0]);
|
|
872
|
-
render();
|
|
1027
|
+
render(ctx);
|
|
873
1028
|
}
|
|
874
1029
|
},
|
|
875
|
-
end() {
|
|
876
|
-
|
|
877
|
-
|
|
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);
|
|
878
1035
|
renderer.ui.showScreen("mainmenu");
|
|
879
1036
|
interactivity(false);
|
|
880
1037
|
times.clear();
|
|
881
1038
|
},
|
|
882
|
-
input([question, onInput, setup]) {
|
|
883
|
-
|
|
1039
|
+
input({ ctx, data: data2 }, [question, onInput, setup]) {
|
|
1040
|
+
ctx.input(unwrap2(question, data2), onInput, setup)(() => {
|
|
1041
|
+
forward(ctx);
|
|
1042
|
+
});
|
|
884
1043
|
},
|
|
885
|
-
custom([handler]) {
|
|
886
|
-
const result =
|
|
887
|
-
if (
|
|
888
|
-
|
|
889
|
-
if (!
|
|
890
|
-
|
|
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);
|
|
891
1053
|
});
|
|
892
1054
|
return result;
|
|
893
1055
|
},
|
|
894
|
-
vibrate(pattern) {
|
|
895
|
-
|
|
896
|
-
push();
|
|
1056
|
+
vibrate({ ctx }, pattern) {
|
|
1057
|
+
ctx.vibrate(pattern);
|
|
1058
|
+
push(ctx);
|
|
897
1059
|
},
|
|
898
|
-
next() {
|
|
899
|
-
push();
|
|
1060
|
+
next({ ctx }) {
|
|
1061
|
+
push(ctx);
|
|
900
1062
|
},
|
|
901
|
-
animateCharacter([character, timeout, ...classes]) {
|
|
1063
|
+
animateCharacter({ ctx, data: data2 }, [character, timeout, ...classes]) {
|
|
902
1064
|
if (DEV && classes.length === 0) {
|
|
903
1065
|
throw new Error("Attempt to use AnimateCharacter without classes. Classes should be provided [https://novely.pages.dev/guide/actions/animateCharacter.html]");
|
|
904
1066
|
}
|
|
905
1067
|
if (DEV && (timeout <= 0 || !Number.isFinite(timeout) || Number.isNaN(timeout))) {
|
|
906
1068
|
throw new Error("Attempt to use AnimateCharacter with unacceptable timeout. It should be finite and greater than zero");
|
|
907
1069
|
}
|
|
1070
|
+
if (ctx.meta.preview)
|
|
1071
|
+
return;
|
|
908
1072
|
const handler = (get) => {
|
|
909
|
-
const { clear } = get(
|
|
910
|
-
const char =
|
|
1073
|
+
const { clear } = get(false);
|
|
1074
|
+
const char = ctx.getCharacter(character);
|
|
911
1075
|
if (!char)
|
|
912
1076
|
return;
|
|
913
1077
|
const target = char.canvas;
|
|
@@ -923,18 +1087,23 @@ var novely = ({
|
|
|
923
1087
|
clearTimeout(timeoutId);
|
|
924
1088
|
});
|
|
925
1089
|
};
|
|
926
|
-
|
|
1090
|
+
handler.key = "@@internal-animate-character";
|
|
1091
|
+
match("custom", [handler], {
|
|
1092
|
+
ctx,
|
|
1093
|
+
data: data2
|
|
1094
|
+
});
|
|
927
1095
|
},
|
|
928
|
-
text(text) {
|
|
929
|
-
const string = text.map((content) => unwrap2(content)).join(" ");
|
|
1096
|
+
text({ ctx, data: data2 }, text) {
|
|
1097
|
+
const string = text.map((content) => unwrap2(content, data2)).join(" ");
|
|
930
1098
|
if (DEV && string.length === 0) {
|
|
931
1099
|
throw new Error(`Action Text was called with empty string or array`);
|
|
932
1100
|
}
|
|
933
|
-
|
|
1101
|
+
ctx.text(string, () => forward(ctx));
|
|
934
1102
|
},
|
|
935
|
-
exit() {
|
|
936
|
-
if (restoring)
|
|
1103
|
+
exit({ ctx, data: data2 }) {
|
|
1104
|
+
if (ctx.meta.restoring)
|
|
937
1105
|
return;
|
|
1106
|
+
const stack = useStack(ctx);
|
|
938
1107
|
const path = stack.value[0];
|
|
939
1108
|
const last = path.at(-1);
|
|
940
1109
|
const ignore = [];
|
|
@@ -948,7 +1117,10 @@ var novely = ({
|
|
|
948
1117
|
if (isExitImpossible(path)) {
|
|
949
1118
|
const referred = refer(path);
|
|
950
1119
|
if (isAction(referred) && isSkippedDuringRestore(referred[0])) {
|
|
951
|
-
match("end", []
|
|
1120
|
+
match("end", [], {
|
|
1121
|
+
ctx,
|
|
1122
|
+
data: data2
|
|
1123
|
+
});
|
|
952
1124
|
}
|
|
953
1125
|
return;
|
|
954
1126
|
}
|
|
@@ -973,36 +1145,39 @@ var novely = ({
|
|
|
973
1145
|
}
|
|
974
1146
|
break;
|
|
975
1147
|
}
|
|
976
|
-
render();
|
|
1148
|
+
render(ctx);
|
|
977
1149
|
},
|
|
978
|
-
preload([source]) {
|
|
979
|
-
if (!goingBack && !restoring && !PRELOADED_ASSETS.has(source)) {
|
|
1150
|
+
preload({ ctx }, [source]) {
|
|
1151
|
+
if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
|
|
980
1152
|
PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
|
|
981
1153
|
}
|
|
982
|
-
push();
|
|
1154
|
+
push(ctx);
|
|
983
1155
|
},
|
|
984
|
-
block([scene]) {
|
|
1156
|
+
block({ ctx }, [scene]) {
|
|
985
1157
|
if (DEV && !story[scene]) {
|
|
986
1158
|
throw new Error(`Attempt to call Block action with unknown scene "${scene}"`);
|
|
987
1159
|
}
|
|
988
1160
|
if (DEV && story[scene].length === 0) {
|
|
989
1161
|
throw new Error(`Attempt to call Block action with empty scene "${scene}"`);
|
|
990
1162
|
}
|
|
991
|
-
if (!restoring) {
|
|
1163
|
+
if (!ctx.meta.restoring) {
|
|
1164
|
+
const stack = useStack(ctx);
|
|
992
1165
|
stack.value[0].push(["block", scene], [null, 0]);
|
|
993
|
-
render();
|
|
1166
|
+
render(ctx);
|
|
994
1167
|
}
|
|
995
1168
|
}
|
|
996
1169
|
});
|
|
997
|
-
const enmemory = () => {
|
|
998
|
-
if (restoring)
|
|
1170
|
+
const enmemory = (ctx) => {
|
|
1171
|
+
if (ctx.meta.restoring)
|
|
999
1172
|
return;
|
|
1173
|
+
const stack = useStack(ctx);
|
|
1000
1174
|
const current = klona(stack.value);
|
|
1001
1175
|
current[2][1] = "auto";
|
|
1002
1176
|
stack.push(current);
|
|
1003
1177
|
save(true, "auto");
|
|
1004
1178
|
};
|
|
1005
|
-
const next = () => {
|
|
1179
|
+
const next = (ctx) => {
|
|
1180
|
+
const stack = useStack(ctx);
|
|
1006
1181
|
const path = stack.value[0];
|
|
1007
1182
|
const last = path.at(-1);
|
|
1008
1183
|
if (last && (isNull(last[0]) || last[0] === "jump") && isNumber(last[1])) {
|
|
@@ -1011,33 +1186,42 @@ var novely = ({
|
|
|
1011
1186
|
path.push([null, 0]);
|
|
1012
1187
|
}
|
|
1013
1188
|
};
|
|
1014
|
-
const render = () => {
|
|
1015
|
-
const
|
|
1189
|
+
const render = (ctx) => {
|
|
1190
|
+
const stack = useStack(ctx);
|
|
1191
|
+
const referred = refer(stack.value[0]);
|
|
1016
1192
|
if (isAction(referred)) {
|
|
1017
1193
|
const [action2, ...props] = referred;
|
|
1018
|
-
match(action2, props
|
|
1194
|
+
match(action2, props, {
|
|
1195
|
+
ctx,
|
|
1196
|
+
data: stack.value[1]
|
|
1197
|
+
});
|
|
1019
1198
|
} else {
|
|
1020
|
-
match("exit", []
|
|
1199
|
+
match("exit", [], {
|
|
1200
|
+
ctx,
|
|
1201
|
+
data: stack.value[1]
|
|
1202
|
+
});
|
|
1021
1203
|
}
|
|
1022
1204
|
};
|
|
1023
|
-
const push = () => {
|
|
1024
|
-
if (!restoring)
|
|
1025
|
-
next(), render();
|
|
1205
|
+
const push = (ctx) => {
|
|
1206
|
+
if (!ctx.meta.restoring)
|
|
1207
|
+
next(ctx), render(ctx);
|
|
1026
1208
|
};
|
|
1027
|
-
const forward = () => {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1209
|
+
const forward = (ctx) => {
|
|
1210
|
+
if (!ctx.meta.preview)
|
|
1211
|
+
enmemory(ctx);
|
|
1212
|
+
push(ctx);
|
|
1213
|
+
if (!ctx.meta.preview)
|
|
1214
|
+
interactivity(true);
|
|
1031
1215
|
};
|
|
1032
1216
|
const interactivity = (value = false) => {
|
|
1033
1217
|
interacted = value ? interacted + 1 : 0;
|
|
1034
1218
|
};
|
|
1035
|
-
const unwrap2 = (content,
|
|
1219
|
+
const unwrap2 = (content, values) => {
|
|
1036
1220
|
const {
|
|
1037
1221
|
data: data2,
|
|
1038
1222
|
meta: [lang]
|
|
1039
1223
|
} = $.get();
|
|
1040
|
-
const obj =
|
|
1224
|
+
const obj = values ? values : data2;
|
|
1041
1225
|
const cnt = isFunction(content) ? content() : typeof content === "string" ? content : content[lang];
|
|
1042
1226
|
const str2 = isFunction(cnt) ? cnt() : cnt;
|
|
1043
1227
|
const trans = translation[lang];
|
|
@@ -1077,7 +1261,7 @@ var novely = ({
|
|
|
1077
1261
|
* Unwraps translatable content to a string value
|
|
1078
1262
|
*/
|
|
1079
1263
|
unwrap(content) {
|
|
1080
|
-
return unwrap2(content,
|
|
1264
|
+
return unwrap2(content, $.get().data);
|
|
1081
1265
|
}
|
|
1082
1266
|
};
|
|
1083
1267
|
};
|